diff --git a/assemblies/nexus-base-feature/pom.xml b/assemblies/nexus-base-feature/pom.xml index 000334a9a4b6a17f32627ae2b30542da5597d34f..7c56fb62207edfa2605815a6a0107f64ccd2274b 100644 --- a/assemblies/nexus-base-feature/pom.xml +++ b/assemblies/nexus-base-feature/pom.xml @@ -19,7 +19,7 @@ org.sonatype.nexus.assemblies nexus-assemblies - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-base-feature diff --git a/assemblies/nexus-base-template/pom.xml b/assemblies/nexus-base-template/pom.xml index 3c7b39395f06801f54462f12cbf260cb880df0d6..a5f96a624b0a06bd9c07e0e09b3eab062ae3e284 100644 --- a/assemblies/nexus-base-template/pom.xml +++ b/assemblies/nexus-base-template/pom.xml @@ -21,7 +21,7 @@ org.sonatype.nexus.assemblies nexus-assemblies - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-base-template diff --git a/assemblies/nexus-boot-feature/pom.xml b/assemblies/nexus-boot-feature/pom.xml index 9a604cdafdb9ada5b93578769c9959f0d72ffd8c..1578a10015b8912c97d2856bc2d05f55572df010 100644 --- a/assemblies/nexus-boot-feature/pom.xml +++ b/assemblies/nexus-boot-feature/pom.xml @@ -19,7 +19,7 @@ org.sonatype.nexus.assemblies nexus-assemblies - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-boot-feature diff --git a/assemblies/nexus-core-feature/pom.xml b/assemblies/nexus-core-feature/pom.xml index 50c0a784ea48b48e8b193f752af9370dcf1ea6cc..7e07b099e4b54ab04185328a6fc59ae78975f4ed 100644 --- a/assemblies/nexus-core-feature/pom.xml +++ b/assemblies/nexus-core-feature/pom.xml @@ -19,7 +19,7 @@ org.sonatype.nexus.assemblies nexus-assemblies - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-core-feature diff --git a/assemblies/nexus-startup-feature/pom.xml b/assemblies/nexus-startup-feature/pom.xml index 0f6c070f7707ec6cd91833b78fe1edc4c859915d..fc41711ab3ce3e3807d1aa11b727100a07a6d495 100644 --- a/assemblies/nexus-startup-feature/pom.xml +++ b/assemblies/nexus-startup-feature/pom.xml @@ -19,7 +19,7 @@ org.sonatype.nexus.assemblies nexus-assemblies - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-startup-feature diff --git a/assemblies/pom.xml b/assemblies/pom.xml index f5ac4172d71124e38304f0fd6190a91c4acc3c50..77ecee1b67714db14dda05ca8b8685bbe6c36d56 100644 --- a/assemblies/pom.xml +++ b/assemblies/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-parent - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.assemblies @@ -45,7 +45,7 @@ org.sonatype.nexus nexus-components pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -53,7 +53,7 @@ org.sonatype.nexus.plugins nexus-plugins pom - 3.8.0-SNAPSHOT + 3.8.0-02 import diff --git a/buildsupport/all/pom.xml b/buildsupport/all/pom.xml index 2fe4f9ba06a9aa6883afd15f406b592e8ee798f3..9624c0e79d53eddf0149959a8e6d47c3cbb24d5a 100644 --- a/buildsupport/all/pom.xml +++ b/buildsupport/all/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-all @@ -35,7 +35,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-commons pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -43,7 +43,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-db pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -51,7 +51,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-goodies pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -59,7 +59,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-groovy pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -67,7 +67,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-guice pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -75,7 +75,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-httpclient pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -83,7 +83,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-internal pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -91,7 +91,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-jetty pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -99,7 +99,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-jruby pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -107,7 +107,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-logging pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -115,7 +115,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-maven pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -123,7 +123,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-metrics pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -131,7 +131,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-osgi pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -139,7 +139,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-other pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -147,7 +147,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-rest pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -155,7 +155,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-security pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -163,7 +163,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-testing pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -171,7 +171,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-ui pom - 3.8.0-SNAPSHOT + 3.8.0-02 import diff --git a/buildsupport/commons/pom.xml b/buildsupport/commons/pom.xml index 320d396475eed403e3080ed05f1f7183244f8d0e..9b550483c43443847e8ebf992e9d6f6f710d9463 100644 --- a/buildsupport/commons/pom.xml +++ b/buildsupport/commons/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-commons diff --git a/buildsupport/db/pom.xml b/buildsupport/db/pom.xml index e03f193580c5a36e317273c5b6c90a246a3e9d83..2da5306f8a6bf2e517e9e6195dc19778c1392657 100644 --- a/buildsupport/db/pom.xml +++ b/buildsupport/db/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-db diff --git a/buildsupport/extjs-maven-plugin/pom.xml b/buildsupport/extjs-maven-plugin/pom.xml index 9a2977296b1251d801307b8bfe3c47ab1c814c12..8c88df260b70845db9e2cb0fc707c84b025c9ffa 100644 --- a/buildsupport/extjs-maven-plugin/pom.xml +++ b/buildsupport/extjs-maven-plugin/pom.xml @@ -21,7 +21,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 extjs-maven-plugin @@ -37,7 +37,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-all pom - 3.8.0-SNAPSHOT + 3.8.0-02 import diff --git a/buildsupport/goodies/pom.xml b/buildsupport/goodies/pom.xml index 82a006c71995bd41c9944582af9670f6e7aa3047..a88985ab316c2b3e96613c51a6265d7dc4e9187f 100644 --- a/buildsupport/goodies/pom.xml +++ b/buildsupport/goodies/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-goodies diff --git a/buildsupport/groovy/pom.xml b/buildsupport/groovy/pom.xml index 6451b5b4bed8ff926344d4e44c65989fcf34904d..de5e555b30a9a03d490392774aba5eaa58a62c28 100644 --- a/buildsupport/groovy/pom.xml +++ b/buildsupport/groovy/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-groovy diff --git a/buildsupport/guice/pom.xml b/buildsupport/guice/pom.xml index b93505a653b2900f0ab0dd3d669334c8cef44fe1..0f2cc3802dd3e2095c83f653330ba27219cc017e 100644 --- a/buildsupport/guice/pom.xml +++ b/buildsupport/guice/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-guice diff --git a/buildsupport/httpclient/pom.xml b/buildsupport/httpclient/pom.xml index 2de33633d6aca6cd3c085e3a2f43e146536a97e7..f70f051bf5a00939e62b6d100bf2ed4503f0f8fb 100644 --- a/buildsupport/httpclient/pom.xml +++ b/buildsupport/httpclient/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-httpclient diff --git a/buildsupport/internal/pom.xml b/buildsupport/internal/pom.xml index b1267753a5130114ea07b1a5ec6411b573ef4a95..3a7be49bd90435757c0cf483df23fcb5fbd4d7bf 100644 --- a/buildsupport/internal/pom.xml +++ b/buildsupport/internal/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-internal diff --git a/buildsupport/jetty/pom.xml b/buildsupport/jetty/pom.xml index c12698cebde581804916b2d3f9dcec6805c94385..053cc0a170788446debf0577dd1172a63ef0f4ae 100644 --- a/buildsupport/jetty/pom.xml +++ b/buildsupport/jetty/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-jetty diff --git a/buildsupport/jruby/pom.xml b/buildsupport/jruby/pom.xml index 9ac4275b5fc6b968d07ef6227b0b1d61d5c7fec7..68ed0995225e125c835fa8b9d27d810247b3ac4f 100644 --- a/buildsupport/jruby/pom.xml +++ b/buildsupport/jruby/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-jruby diff --git a/buildsupport/logging/pom.xml b/buildsupport/logging/pom.xml index e86eba2325b8322651c1c71dde01ba6b5fdb9f81..2cbd8d15cdd9cd0541885520aac7259ed0005785 100644 --- a/buildsupport/logging/pom.xml +++ b/buildsupport/logging/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-logging diff --git a/buildsupport/maven/pom.xml b/buildsupport/maven/pom.xml index 4033d9fbc77b1773cf32b145df4f5eb00ef49deb..87e651d475518dc7c072393ecb6ec0369c675216 100644 --- a/buildsupport/maven/pom.xml +++ b/buildsupport/maven/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-maven diff --git a/buildsupport/metrics/pom.xml b/buildsupport/metrics/pom.xml index 1d6db02785273c924f64edd555fa336e5d5c598c..3c4c193350b6f4278460a7d0ad416d0a0b226c0f 100644 --- a/buildsupport/metrics/pom.xml +++ b/buildsupport/metrics/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-metrics diff --git a/buildsupport/osgi/pom.xml b/buildsupport/osgi/pom.xml index a8f09d7c2794795a75c7ec905943327e7800eb56..6d1de71c0734f19f6927df134ca1bb874241fda6 100644 --- a/buildsupport/osgi/pom.xml +++ b/buildsupport/osgi/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-osgi diff --git a/buildsupport/other/pom.xml b/buildsupport/other/pom.xml index b206b287aca67ee4f8b60a31c10b9d928b2782bf..ecd6ae71d8873ec2ed7d4bd416d80c5cc0de81ed 100644 --- a/buildsupport/other/pom.xml +++ b/buildsupport/other/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-other diff --git a/buildsupport/pom.xml b/buildsupport/pom.xml index 9ad5da578e04e905fc4c08a61bc9bd72a382455b..51727ffdb698ef6fe7484f7b3e1ef48d1765ceb2 100644 --- a/buildsupport/pom.xml +++ b/buildsupport/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-parent - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.buildsupport diff --git a/buildsupport/rest/pom.xml b/buildsupport/rest/pom.xml index c64930ac13ba6532eb0869992bc9b038d1cc55bb..320f2d00af966c62520d2c71276d643cfa5ba5f3 100644 --- a/buildsupport/rest/pom.xml +++ b/buildsupport/rest/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-rest diff --git a/buildsupport/scripts/pom.xml b/buildsupport/scripts/pom.xml index 8053db090bf7aeb770c57e39181d8cee3e874047..9de48bb83590bdb47aff0b4e5b141b152d83caf9 100644 --- a/buildsupport/scripts/pom.xml +++ b/buildsupport/scripts/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-scripts @@ -36,7 +36,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-all pom - 3.8.0-SNAPSHOT + 3.8.0-02 import diff --git a/buildsupport/security/pom.xml b/buildsupport/security/pom.xml index df356a9b9d63124fef4b1d0262740007bd81b82e..288e9370665a4491ed99da360dfc6fbc7eaddbd5 100644 --- a/buildsupport/security/pom.xml +++ b/buildsupport/security/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-security diff --git a/buildsupport/testing/pom.xml b/buildsupport/testing/pom.xml index 46dc952ce6e32ad439cb72942d51c8bea6534f66..995a4991487050357db9e3fc74917fd51b9c54ee 100644 --- a/buildsupport/testing/pom.xml +++ b/buildsupport/testing/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-testing diff --git a/buildsupport/ui/pom.xml b/buildsupport/ui/pom.xml index a7780bb7653bd6c9098024dbb396d6e6eb519e9f..e1e768d2b3f8149f2bf18560c110888940865cfb 100644 --- a/buildsupport/ui/pom.xml +++ b/buildsupport/ui/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-buildsupport-ui diff --git a/components/nexus-analytics-api/pom.xml b/components/nexus-analytics-api/pom.xml index 7c452ad0115054172d5df059544a277750f92c55..1b2b96a9ffc4a3ecee8ac0230f07df63d0c86bc0 100644 --- a/components/nexus-analytics-api/pom.xml +++ b/components/nexus-analytics-api/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-analytics-api diff --git a/components/nexus-audit/pom.xml b/components/nexus-audit/pom.xml index 89ab52a5383ca9eac90555258c510559b62bbf1a..e10530e6b13b698a779d2ce7ab6e501bf69992fe 100644 --- a/components/nexus-audit/pom.xml +++ b/components/nexus-audit/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-audit diff --git a/components/nexus-base/pom.xml b/components/nexus-base/pom.xml index 604ea66f6a9b6a73898f86889f099865a7c81821..79b96670fdafcf15d3d51c3aa0f86d63fbc032e3 100644 --- a/components/nexus-base/pom.xml +++ b/components/nexus-base/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-base diff --git a/components/nexus-blobstore-api/pom.xml b/components/nexus-blobstore-api/pom.xml index 1ff4694bc2c8787b80275eb37cf2f840f415899a..ac08635bfb67760cb91e6fd16322731f9f8395fa 100644 --- a/components/nexus-blobstore-api/pom.xml +++ b/components/nexus-blobstore-api/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-blobstore-api diff --git a/components/nexus-blobstore-file/pom.xml b/components/nexus-blobstore-file/pom.xml index 8354485274a2c68d5ed967f12b6a5df6b138cb3f..ab9e4bc0f074a55b36dd832703df3e4080494f2b 100644 --- a/components/nexus-blobstore-file/pom.xml +++ b/components/nexus-blobstore-file/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-blobstore-file diff --git a/components/nexus-blobstore/pom.xml b/components/nexus-blobstore/pom.xml index 1e387b136c675a5a806cefbf8091d3785a33863a..89d974b34d6e1d129df75fa75f13d53aa9e0dee4 100644 --- a/components/nexus-blobstore/pom.xml +++ b/components/nexus-blobstore/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-blobstore diff --git a/components/nexus-bootstrap/pom.xml b/components/nexus-bootstrap/pom.xml index 1c7a644885ff9bdcb8e218fb17cb9a2b2e74b026..af37fd77ee16d247b83f63d5afc6043f7494af03 100644 --- a/components/nexus-bootstrap/pom.xml +++ b/components/nexus-bootstrap/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-bootstrap diff --git a/components/nexus-cache/pom.xml b/components/nexus-cache/pom.xml index 3b5f06c93ae997b4561b4d0f74c745162c829239..6d12a2a2a8885c711b552b2e36ec07d738a47df6 100644 --- a/components/nexus-cache/pom.xml +++ b/components/nexus-cache/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-cache diff --git a/components/nexus-capability/pom.xml b/components/nexus-capability/pom.xml index 1314f9d48c5e0952b3350269e39ecf08269d327b..d8b47019496b5fd386fbd05ea1d7bc72062a5090 100644 --- a/components/nexus-capability/pom.xml +++ b/components/nexus-capability/pom.xml @@ -21,7 +21,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-capability diff --git a/components/nexus-commands/pom.xml b/components/nexus-commands/pom.xml index ca231510a71e319790e8101e10b4dce96648f01f..37970b10662c0679fdc25ab467f429ea37a66589 100644 --- a/components/nexus-commands/pom.xml +++ b/components/nexus-commands/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-commands diff --git a/components/nexus-common/pom.xml b/components/nexus-common/pom.xml index 1adba73a85518078747ab3771ce52d0793d38e12..25c60b982c329bb12723f939ffafcd8a3707980e 100644 --- a/components/nexus-common/pom.xml +++ b/components/nexus-common/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-common diff --git a/components/nexus-core/pom.xml b/components/nexus-core/pom.xml index a5c2ea2701bee23088c9d03b9a4221f7d0351619..c63b27b1412d949fc669d8f8c0c3c92d1fcfbb8f 100644 --- a/components/nexus-core/pom.xml +++ b/components/nexus-core/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-core diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/backup/DatabaseBackupTaskDescriptor.java b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/backup/DatabaseBackupTaskDescriptor.java index 4ba1fa4a7e019b28205bbfb3cc67ef74a0b53b20..b1254a1b26ae4a36ae1488b432f62e485e9bf13a 100644 --- a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/backup/DatabaseBackupTaskDescriptor.java +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/backup/DatabaseBackupTaskDescriptor.java @@ -35,7 +35,7 @@ import static org.sonatype.nexus.formfields.FormField.MANDATORY; public class DatabaseBackupTaskDescriptor extends TaskDescriptorSupport { - public static final String MSG = "Admin - Export databases for backup"; + public static final String MSG = "Export configuration & metadata for backup"; public static final String TYPE_ID = "db.backup"; diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/script/ScriptTaskDescriptor.java b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/script/ScriptTaskDescriptor.java index aab13259907de50974efbf008f859fabf32bdaad..410458a9f759a8a338691cc821d9a2750435a8ee 100644 --- a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/script/ScriptTaskDescriptor.java +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/script/ScriptTaskDescriptor.java @@ -43,7 +43,7 @@ public class ScriptTaskDescriptor private interface Messages extends MessageBundle { - @DefaultMessage("Admin - Execute script") + @DefaultMessage("Execute script") String name(); @DefaultMessage("Language") diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/security/apikey/ApiKeyStoreImpl.java b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/security/apikey/ApiKeyStoreImpl.java index 6db3ea0bf361c3fc7b0c692f018c33afa8d1e9cf..a63c1769b232ca50340f947e3e8f6f5947a24baf 100644 --- a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/security/apikey/ApiKeyStoreImpl.java +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/security/apikey/ApiKeyStoreImpl.java @@ -46,7 +46,6 @@ import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SCHEMAS; import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTx; import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; -import static org.sonatype.nexus.scheduling.CancelableHelper.checkCancellation; /** * OrientDB impl of {@link ApiKeyStore}. @@ -177,11 +176,9 @@ public class ApiKeyStoreImpl @Override @Guarded(by = STARTED) public void purgeApiKeys() { - checkCancellation(); inTxRetry(databaseInstance).run(db -> { List delete = new ArrayList<>(); for (ApiKey entity : entityAdapter.browse(db)) { - checkCancellation(); try { principalsHelper.getUserStatus(entity.getPrincipals()); } @@ -191,7 +188,6 @@ public class ApiKeyStoreImpl } } for (ApiKey entity : delete) { - checkCancellation(); entityAdapter.deleteEntity(db, entity); } }); diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/security/apikey/PurgeApiKeysTask.java b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/security/apikey/PurgeApiKeysTask.java index 65f59f5dc87ffa922eec97149972e074648f6071..9d66608149f6cfc881a6c27c2de73b055038b7ff 100644 --- a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/security/apikey/PurgeApiKeysTask.java +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/security/apikey/PurgeApiKeysTask.java @@ -15,7 +15,6 @@ package org.sonatype.nexus.internal.security.apikey; import javax.inject.Inject; import javax.inject.Named; -import org.sonatype.nexus.scheduling.Cancelable; import org.sonatype.nexus.scheduling.TaskSupport; import org.sonatype.nexus.security.authc.apikey.ApiKeyStore; @@ -30,7 +29,6 @@ import static com.google.common.base.Preconditions.checkNotNull; @Named public class PurgeApiKeysTask extends TaskSupport - implements Cancelable { private final ApiKeyStore store; @@ -47,6 +45,6 @@ public class PurgeApiKeysTask @Override public String getMessage() { - return "Deleting orphaned API keys"; + return "Purging orphaned API keys"; } } diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/security/apikey/PurgeApiKeysTaskDescriptor.java b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/security/apikey/PurgeApiKeysTaskDescriptor.java index 10ad3496850bd6798bca7cd124b57df0f8a0939b..e1b2d2b518508b04a2526886127ab604c9147207 100644 --- a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/security/apikey/PurgeApiKeysTaskDescriptor.java +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/security/apikey/PurgeApiKeysTaskDescriptor.java @@ -32,7 +32,7 @@ public class PurgeApiKeysTaskDescriptor public PurgeApiKeysTaskDescriptor() { super(TYPE_ID, PurgeApiKeysTask.class, - "Admin - Delete orphaned API keys", + "Purge orphaned API keys", VISIBLE, EXPOSED ); diff --git a/components/nexus-core/src/test/java/org/sonatype/nexus/internal/security/apikey/ApiKeyStoreImplTest.groovy b/components/nexus-core/src/test/java/org/sonatype/nexus/internal/security/apikey/ApiKeyStoreImplTest.groovy index 8050b47f113be23c3408b5951212025304789845..9fa780861db5bef09be378b58c085d920881e456 100644 --- a/components/nexus-core/src/test/java/org/sonatype/nexus/internal/security/apikey/ApiKeyStoreImplTest.groovy +++ b/components/nexus-core/src/test/java/org/sonatype/nexus/internal/security/apikey/ApiKeyStoreImplTest.groovy @@ -12,32 +12,25 @@ */ package org.sonatype.nexus.internal.security.apikey -import java.util.concurrent.atomic.AtomicBoolean - import org.sonatype.goodies.testsupport.TestSupport import org.sonatype.nexus.crypto.internal.CryptoHelperImpl import org.sonatype.nexus.crypto.internal.RandomBytesGeneratorImpl import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule -import org.sonatype.nexus.scheduling.CancelableHelper -import org.sonatype.nexus.scheduling.TaskInterruptedException import org.sonatype.nexus.security.UserPrincipalsHelper -import org.sonatype.nexus.security.user.UserNotFoundException -import org.sonatype.nexus.security.user.UserStatus import com.google.common.collect.Maps import org.apache.shiro.subject.PrincipalCollection import org.apache.shiro.subject.SimplePrincipalCollection +import org.hamcrest.MatcherAssert import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test -import org.mockito.Mock -import static org.hamcrest.MatcherAssert.assertThat +import static MatcherAssert.assertThat import static org.hamcrest.Matchers.equalTo import static org.hamcrest.Matchers.nullValue -import static org.junit.Assert.fail -import static org.mockito.Mockito.when +import static org.mockito.Mockito.mock /** * Tests {@link ApiKeyStoreImpl} @@ -45,35 +38,17 @@ import static org.mockito.Mockito.when class ApiKeyStoreImplTest extends TestSupport { - private static final String PRINCIPAL_A_NAME = 'name-a' - - private static final String PRINCIPAL_A_DOMAIN = 'foo' - - private static final String PRINCIPAL_B_NAME = 'name-b' - - private static final String PRINCIPAL_B_DOMAIN = 'bar' - - private static final UserStatus USER_STATUS = UserStatus.disabled - - private static final boolean CANCELLED = true - @Rule public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory('test') - @Mock - private UserPrincipalsHelper principalsHelper - - private AtomicBoolean cancelled = new AtomicBoolean(!CANCELLED) - private ApiKeyStoreImpl underTest @Before void setup() { - CancelableHelper.set(cancelled) underTest = new ApiKeyStoreImpl( database.instanceProvider, new ApiKeyEntityAdapter(ClassLoader.getSystemClassLoader()), - principalsHelper, + mock(UserPrincipalsHelper.class), Maps.newHashMap(), new DefaultApiKeyFactory(new RandomBytesGeneratorImpl(new CryptoHelperImpl())) ) @@ -86,7 +61,6 @@ class ApiKeyStoreImplTest underTest.stop() underTest = null } - CancelableHelper.remove() } @Test @@ -172,47 +146,6 @@ class ApiKeyStoreImplTest assertThat(key2, equalTo(key)); } - @Test - void 'Can purge orphaned API keys'() { - PrincipalCollection principalA = makePrincipals(PRINCIPAL_A_NAME) - PrincipalCollection principalB = makePrincipals(PRINCIPAL_B_NAME) - char[] apiKeyForPrincipalA = underTest.createApiKey(PRINCIPAL_A_DOMAIN, principalA) - underTest.createApiKey(PRINCIPAL_B_NAME, principalB) - when(principalsHelper.getUserStatus(principalA)).thenReturn(USER_STATUS) - when(principalsHelper.getUserStatus(principalB)). - thenThrow(new UserNotFoundException(principalB.getPrimaryPrincipal())) - - underTest.purgeApiKeys() - - //Verify that api keys that belong to non-existent users are purged - assertThat(underTest.getApiKey(PRINCIPAL_A_DOMAIN, principalA), equalTo(apiKeyForPrincipalA)) - assertThat(underTest.getApiKey(PRINCIPAL_B_DOMAIN, principalB), nullValue()) - } - - @Test - void 'Purge orphaned API keys is cancelable'() { - PrincipalCollection principalA = makePrincipals(PRINCIPAL_A_NAME) - PrincipalCollection principalB = makePrincipals(PRINCIPAL_B_NAME) - char[] apiKeyForPrincipalA = underTest.createApiKey(PRINCIPAL_A_DOMAIN, principalA) - char[] apiKeyForPrincipalB = underTest.createApiKey(PRINCIPAL_B_DOMAIN, principalB) - when(principalsHelper.getUserStatus(principalA)). - thenThrow(new UserNotFoundException(principalA.getPrimaryPrincipal())) - when(principalsHelper.getUserStatus(principalB)). - thenThrow(new UserNotFoundException(principalB.getPrimaryPrincipal())) - cancelled.set(CANCELLED) - - try { - underTest.purgeApiKeys() - fail("Expected exception to be thrown") - } - catch (TaskInterruptedException expected) { - } - - //Verify that no api keys were purged even though they belong to non-existent users - assertThat(underTest.getApiKey(PRINCIPAL_A_DOMAIN, principalA), equalTo(apiKeyForPrincipalA)) - assertThat(underTest.getApiKey(PRINCIPAL_B_DOMAIN, principalB), equalTo(apiKeyForPrincipalB)) - } - private PrincipalCollection makePrincipals(String name) { return new SimplePrincipalCollection(name, "foo") } diff --git a/components/nexus-crypto/pom.xml b/components/nexus-crypto/pom.xml index 858149ce2a3aa7c173e23531b6111720f8040c52..e20839d8167fefa4faea8cb8a877a1ce8ab89d7d 100644 --- a/components/nexus-crypto/pom.xml +++ b/components/nexus-crypto/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-crypto diff --git a/components/nexus-elasticsearch/pom.xml b/components/nexus-elasticsearch/pom.xml index d34031a4a0f2cba04c7a67eb66fe1f3f9b374435..1f2a0714813404806d57181a857d4e9e3d27d02b 100644 --- a/components/nexus-elasticsearch/pom.xml +++ b/components/nexus-elasticsearch/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-elasticsearch diff --git a/components/nexus-email/pom.xml b/components/nexus-email/pom.xml index df4441965ac40e795cf3ea6b8dc45f017a8b04dc..bd858e1f010b1a65f335bc8d5551bc62de19f290 100644 --- a/components/nexus-email/pom.xml +++ b/components/nexus-email/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-email diff --git a/components/nexus-extdirect/pom.xml b/components/nexus-extdirect/pom.xml index 8d6f6ab7e340bb8961d5073f8e5d43cdea0427e9..d4dc6312dd4404653adaa4b550160106593e3e4b 100644 --- a/components/nexus-extdirect/pom.xml +++ b/components/nexus-extdirect/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-extdirect diff --git a/components/nexus-extender/pom.xml b/components/nexus-extender/pom.xml index 9231e7e84db34f4b5bdd18c699a7ad01cd605b8f..5f4abbe7c27c9a1f7dd1985a483d7ca4cb4533a3 100644 --- a/components/nexus-extender/pom.xml +++ b/components/nexus-extender/pom.xml @@ -19,7 +19,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-extender diff --git a/components/nexus-formfields/pom.xml b/components/nexus-formfields/pom.xml index b6043f7ddf471ed8f3f9bfdb851d706127105a4c..14ec43f52749e78628abd38b69c0a778b07ba37a 100644 --- a/components/nexus-formfields/pom.xml +++ b/components/nexus-formfields/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-formfields diff --git a/components/nexus-guice-servlet/pom.xml b/components/nexus-guice-servlet/pom.xml index 8ba77896d0ae80769009bca395051aadeeb3d97b..a8e64195a05da65d0276e31a583d841371149f48 100644 --- a/components/nexus-guice-servlet/pom.xml +++ b/components/nexus-guice-servlet/pom.xml @@ -19,7 +19,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-guice-servlet diff --git a/components/nexus-httpclient/pom.xml b/components/nexus-httpclient/pom.xml index 13c6a5406404a5d049ba6e621af3daf02dbee750..9fe9ee5b8b1401d434cde06be83fc838e43d3c7b 100644 --- a/components/nexus-httpclient/pom.xml +++ b/components/nexus-httpclient/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-httpclient diff --git a/components/nexus-jmx/pom.xml b/components/nexus-jmx/pom.xml index 03fd6a5d41f2ea0da8908efb20c36c1a1caca05c..f12ce0deb7ba8975f595276e5e410fb93836c744 100644 --- a/components/nexus-jmx/pom.xml +++ b/components/nexus-jmx/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-jmx diff --git a/components/nexus-main/pom.xml b/components/nexus-main/pom.xml index d75d24839859f2117a5770c378ccb2d9ce55c3c4..b059f468d9c2f05c4211eee7e8b46576fb7612c7 100644 --- a/components/nexus-main/pom.xml +++ b/components/nexus-main/pom.xml @@ -21,7 +21,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-main diff --git a/components/nexus-mime/pom.xml b/components/nexus-mime/pom.xml index a8cd3c4984b563dc331ca2d0ee1316c3e6562b9d..27652b362e7e9591a366a78184a613c9d66d95c9 100644 --- a/components/nexus-mime/pom.xml +++ b/components/nexus-mime/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-mime diff --git a/components/nexus-orient-console/pom.xml b/components/nexus-orient-console/pom.xml index b6efc4808fdfda3e5ef0baed72715f5c90cc4ece..2411237ab467148f421d87e57cce84c8accef50e 100644 --- a/components/nexus-orient-console/pom.xml +++ b/components/nexus-orient-console/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-orient-console diff --git a/components/nexus-orient/pom.xml b/components/nexus-orient/pom.xml index 8044e35c1ae7170f2965e1255271b4eada196fda..48683880a46e9ca42aabbe85853d0f2ce99d6a1b 100644 --- a/components/nexus-orient/pom.xml +++ b/components/nexus-orient/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-orient diff --git a/components/nexus-oss-edition/pom.xml b/components/nexus-oss-edition/pom.xml index d6bdfa79bae1f326eaa0546e4a1dd0f12db430f7..4c134f96f3875fca6ce8443b2154a95b3bfea81f 100644 --- a/components/nexus-oss-edition/pom.xml +++ b/components/nexus-oss-edition/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-oss-edition diff --git a/components/nexus-pax-exam/pom.xml b/components/nexus-pax-exam/pom.xml index 501b8d2691b5f78ea71a84b91fd017584ecd8e87..f10c49c15966c5805ae5d51088e5997b32103fa8 100644 --- a/components/nexus-pax-exam/pom.xml +++ b/components/nexus-pax-exam/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-pax-exam diff --git a/components/nexus-pax-logging/pom.xml b/components/nexus-pax-logging/pom.xml index c53de9f23730282c356e2069f8c0d223af29c3fa..eb75be0f5a60b029a495144d907d3609480ca818 100644 --- a/components/nexus-pax-logging/pom.xml +++ b/components/nexus-pax-logging/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-pax-logging diff --git a/components/nexus-plugin-api/pom.xml b/components/nexus-plugin-api/pom.xml index 92a8c9a8f5ff96974e9fec084e4ae4507a1bf00b..b26b79d5ad212c996fdf3ecf74ca32316bc1d86f 100644 --- a/components/nexus-plugin-api/pom.xml +++ b/components/nexus-plugin-api/pom.xml @@ -19,7 +19,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-plugin-api diff --git a/components/nexus-quartz/pom.xml b/components/nexus-quartz/pom.xml index 08fb27133d7ca012234139999d3aafbcd297562e..ea28810cee239a9cfbfa114ed60036f412b4c432 100644 --- a/components/nexus-quartz/pom.xml +++ b/components/nexus-quartz/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-quartz diff --git a/components/nexus-rapture/pom.xml b/components/nexus-rapture/pom.xml index 4cd08228e0401ae51c13ea6a3750e562af7bc40b..b4a99531e09fec92f0b265b2d9e5fd92a075db09 100644 --- a/components/nexus-rapture/pom.xml +++ b/components/nexus-rapture/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-rapture diff --git a/components/nexus-repository/pom.xml b/components/nexus-repository/pom.xml index ae2ef24017433a73324cfdfb71ee5a13b29b79de..a4a6d23384aaf6b430ce2703eb390085be56abfa 100644 --- a/components/nexus-repository/pom.xml +++ b/components/nexus-repository/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-repository diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesTaskDescriptor.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesTaskDescriptor.java index e7f2af7b5784cd923a0632d33a6210abd372e9e8..f4f89da2f5706ab7df7b8ccb47f75fd53dcf874d 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesTaskDescriptor.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesTaskDescriptor.java @@ -38,7 +38,7 @@ public class RebuildBrowseNodesTaskDescriptor @Inject public RebuildBrowseNodesTaskDescriptor(final NodeAccess nodeAccess, BrowseNodeConfiguration configuration) { - super(TYPE_ID, RebuildBrowseNodesTask.class, "Repair - Rebuild repository browse", + super(TYPE_ID, RebuildBrowseNodesTask.class, "Rebuild repository browse tree", configuration.isEnabled() ? VISIBLE : NOT_VISIBLE, configuration.isEnabled() ? EXPOSED : NOT_EXPOSED, new RepositoryCombobox(REPOSITORY_NAME_FIELD_ID, "Repository", "Select the repository to rebuild browse tree", true).excludingAnyOfTypes(GroupType.NAME).includeAnEntryForAllRepositories(), diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedTaskDescriptor.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedTaskDescriptor.java index 96ddc9bd26d70eaa31163a54af7781ad4f890ee4..4f22e7dbf027b9eaa3e2447bf9b56bf39a3b1e0f 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedTaskDescriptor.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedTaskDescriptor.java @@ -33,7 +33,7 @@ import static org.sonatype.nexus.repository.purge.PurgeUnusedTask.REPOSITORY_NAM public class PurgeUnusedTaskDescriptor extends TaskDescriptorSupport { - public static final String TASK_NAME = "Repository - Delete unused components"; + public static final String TASK_NAME = "Purge unused components and assets"; public static final String TYPE_ID = "repository.purge-unused"; diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentResponseUtils.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentResponseUtils.java deleted file mode 100644 index 2ca23372c48c6b79a37da21120468df8e44df5f6..0000000000000000000000000000000000000000 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentResponseUtils.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Sonatype Nexus (TM) Open Source Version - * Copyright (c) 2008-present Sonatype, Inc. - * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. - * - * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, - * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. - * - * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks - * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the - * Eclipse Foundation. All other trademarks are the property of their respective owners. - */ -package org.sonatype.nexus.repository.rest.api; - -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; - -import org.sonatype.nexus.repository.storage.Component; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap.Builder; - -/** - * Utils for working with {@link Component} responses in the REST API. - * - * @since 3.next - */ -public class ComponentResponseUtils -{ - private static final String NAME = "name"; - - private static final String GROUP = "group"; - - private static final String VERSION = "version"; - - private ComponentResponseUtils() { - // empty - } - - /** - * Create a simple map to use in a {@link org.sonatype.nexus.rest.SimpleApiResponse} from the given {@link Component}. - * - * @param component the {@link Component} to produce the map from - * @return Map of component attributes. Will always contain the name, and optionally the group and version if they exist. - */ - public static Map mapFor(Component component) { - Builder builder = ImmutableMap.builder().put(NAME, component.name()); - maybePut(builder, component, Component::group, GROUP); - maybePut(builder, component, Component::version, VERSION); - return builder.build(); - } - - public static void maybePut(final Builder builder, - final T t, - final Function f, - final String key) - { - Optional.of(t).map(f).ifPresent(v -> builder.put(key, v)); - } -} diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/ComponentUploadParameterContributor.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/ComponentUploadParameterContributor.java index 15c4debce11639cf95864d564c950bf2ce55f906..4ce193fba5752084a5fe9ae7468c877070c52c12 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/ComponentUploadParameterContributor.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/ComponentUploadParameterContributor.java @@ -26,7 +26,6 @@ import org.sonatype.nexus.repository.upload.UploadManager; import org.sonatype.nexus.swagger.ParameterContributor; import com.google.common.collect.ImmutableList; -import io.swagger.models.HttpMethod; import io.swagger.models.parameters.FormParameter; import static io.swagger.models.HttpMethod.POST; @@ -39,13 +38,11 @@ import static io.swagger.models.HttpMethod.POST; public class ComponentUploadParameterContributor extends ParameterContributor { - private static final List HTTP_METHODS = ImmutableList.of(POST); - private static final List PATHS = ImmutableList.of(ComponentsResource.RESOURCE_URI); @Inject public ComponentUploadParameterContributor(final UploadManager uploadManager) { - super(HTTP_METHODS, PATHS, transformUploadDefinitions(uploadManager.getAvailableDefinitions())); + super(POST, PATHS, transformUploadDefinitions(uploadManager.getAvailableDefinitions())); } private static Collection transformUploadDefinitions(final Collection uploadDefinitions) { diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/SearchParameterContributor.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/SearchParameterContributor.java index 30a673700eb24c9ce8e65f674d61bf60e1cbcb91..019947e11dc3366159b739ef801cafe3d0219443 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/SearchParameterContributor.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/SearchParameterContributor.java @@ -26,7 +26,6 @@ import org.sonatype.nexus.repository.rest.internal.resources.SearchResource; import org.sonatype.nexus.swagger.ParameterContributor; import com.google.common.collect.ImmutableList; -import io.swagger.models.HttpMethod; import io.swagger.models.parameters.QueryParameter; import static io.swagger.models.HttpMethod.GET; @@ -40,8 +39,6 @@ import static java.util.stream.Collectors.toList; public class SearchParameterContributor extends ParameterContributor { - private static final List HTTP_METHODS = ImmutableList.of(GET); - private static final List PATHS = ImmutableList.of( SearchResource.RESOURCE_URI, SearchResource.RESOURCE_URI + SearchResource.SEARCH_ASSET_URI, @@ -50,7 +47,7 @@ public class SearchParameterContributor @Inject public SearchParameterContributor(final SearchMappingsService searchMappings) { - super(HTTP_METHODS, PATHS, transformMappings(searchMappings.getAllMappings())); + super(GET, PATHS, transformMappings(searchMappings.getAllMappings())); } private static Collection transformMappings(final Iterable searchMappings) { // NOSONAR diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoriesResource.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoriesResource.java deleted file mode 100644 index ba62728a147da482ec282b0f2c5d764f4a6d8074..0000000000000000000000000000000000000000 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoriesResource.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Sonatype Nexus (TM) Open Source Version - * Copyright (c) 2008-present Sonatype, Inc. - * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. - * - * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, - * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. - * - * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks - * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the - * Eclipse Foundation. All other trademarks are the property of their respective owners. - */ -package org.sonatype.nexus.repository.rest.internal.resources; - -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; - -import org.sonatype.nexus.repository.rest.api.RepositoryXO; -import org.sonatype.nexus.repository.rest.internal.resources.doc.RepositoriesResourceDoc; -import org.sonatype.nexus.rest.Resource; - -import static com.google.common.base.Preconditions.checkNotNull; -import static java.util.stream.Collectors.toList; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; -import static org.sonatype.nexus.repository.rest.internal.resources.RepositoriesResource.RESOURCE_URI; -import static org.sonatype.nexus.rest.APIConstants.BETA_API_PREFIX; - -/** - * @since 3.next - */ -@Named -@Singleton -@Path(RESOURCE_URI) -@Produces(APPLICATION_JSON) -@Consumes(APPLICATION_JSON) -public class RepositoriesResource - implements Resource, RepositoriesResourceDoc -{ - public static final String RESOURCE_URI = BETA_API_PREFIX + "/repositories"; - - private final RepositoryManagerRESTAdapter repositoryManagerRESTAdapter; - - @Inject - public RepositoriesResource(final RepositoryManagerRESTAdapter repositoryManagerRESTAdapter) { - this.repositoryManagerRESTAdapter = checkNotNull(repositoryManagerRESTAdapter); - } - - @GET - public List getRepositories() { - return repositoryManagerRESTAdapter.getRepositories() - .stream() - .map(RepositoryXO::fromRepository) - .collect(toList()); - } -} diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapter.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapter.java index 55079005a077d29a3c37c0c7eb9bead05e65e74a..85f13278e4d6e575db7aeea708ff26f2280fa017 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapter.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapter.java @@ -12,8 +12,6 @@ */ package org.sonatype.nexus.repository.rest.internal.resources; -import java.util.List; - import org.sonatype.nexus.repository.Repository; /** @@ -31,9 +29,4 @@ public interface RepositoryManagerRESTAdapter * supplied id exists. */ Repository getRepository(String repositoryId); - - /** - * Retrieve all repositories that the user access to. - */ - List getRepositories(); } diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImpl.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImpl.java index 063a3603cc64565d1e985c44a0f08c1577065dce..164ae02f402e54718cf90ce3cade7561a34eed2b 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImpl.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImpl.java @@ -12,9 +12,6 @@ */ package org.sonatype.nexus.repository.rest.internal.resources; -import java.util.List; -import java.util.stream.Collectors; - import javax.inject.Inject; import javax.inject.Named; import javax.ws.rs.NotFoundException; @@ -27,8 +24,6 @@ import org.sonatype.nexus.repository.security.RepositoryViewPermission; import org.sonatype.nexus.security.SecurityHelper; import org.sonatype.nexus.selector.SelectorManager; -import com.google.common.collect.Streams; - import static com.google.common.base.Preconditions.checkNotNull; import static java.util.Collections.singletonList; import static java.util.Optional.ofNullable; @@ -84,13 +79,6 @@ public class RepositoryManagerRESTAdapterImpl } } - @Override - public List getRepositories() { - return Streams.stream(repositoryManager.browse()) - .filter(this::userCanBrowseRepository) - .collect(Collectors.toList()); - } - private boolean userCanViewRepository(final Repository repository) { return userHasReadPermission(repository) || userHasAnyContentSelectorAccess(repository); } diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTask.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTask.java index bad273347952628139b486a9ba40ecfc85c55c7e..7d5aa8db738ef82295517e399f14edf31fd3627c 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTask.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTask.java @@ -39,7 +39,7 @@ public class RebuildIndexTask @Override public String getMessage() { - return "Rebuilding search index of " + getRepositoryField(); + return "Rebuilding index of " + getRepositoryField(); } } diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTaskDescriptor.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTaskDescriptor.java index 5ea114a04360219b047a1201f819db76e15ab167..f2bf54c2bbab209de7d5c90e7b96406dcb59f712 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTaskDescriptor.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTaskDescriptor.java @@ -38,7 +38,7 @@ public class RebuildIndexTaskDescriptor public RebuildIndexTaskDescriptor(final NodeAccess nodeAccess) { super(TYPE_ID, RebuildIndexTask.class, - "Repair - Rebuild repository search", + "Rebuild repository index", VISIBLE, EXPOSED, new RepositoryCombobox( diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentMaintenance.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentMaintenance.java index 1b39a6cc4b3d7b64a066ee9145f007476809bf93..59e0799d24b0495a0cd65ace7cbf9612cd15e76f 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentMaintenance.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentMaintenance.java @@ -29,28 +29,8 @@ public interface ComponentMaintenance */ void deleteComponent(EntityId componentId); - /** - * Deletes a component and maybe the associated blobs. - * - * @param componentId entity id of the component to delete - * @param deleteBlobs should blob deletion be requested - * - * @since 3.next - */ - void deleteComponent(EntityId componentId, boolean deleteBlobs); - /** * Deletes an asset from storage. */ void deleteAsset(EntityId assetId); - - /** - * Deletes an asset and maybe the associated blob. - * - * @param assetId entity id of the asset to delete - * @param deleteBlob should blob deletion be requested - * - * @since 3.next - */ - void deleteAsset(EntityId assetId, boolean deleteBlob); } diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DefaultComponentMaintenanceImpl.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DefaultComponentMaintenanceImpl.java index d1fa38cf23989cc0f1e1d23220f383ab06e5fc53..bca4881c70c6fd6299adba447b524a5ae1880000 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DefaultComponentMaintenanceImpl.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DefaultComponentMaintenanceImpl.java @@ -39,18 +39,10 @@ public class DefaultComponentMaintenanceImpl */ @Override public void deleteComponent(final EntityId componentId) { - deleteComponent(componentId, true); - } - - /** - * Deletes the component directly, with no additional bookkeeping. - */ - @Override - public void deleteComponent(final EntityId componentId, final boolean deleteBlobs) { checkNotNull(componentId); UnitOfWork.begin(getRepository().facet(StorageFacet.class).txSupplier()); try { - deleteComponentTx(componentId, deleteBlobs); + deleteComponentTx(componentId); } finally { UnitOfWork.end(); @@ -58,14 +50,14 @@ public class DefaultComponentMaintenanceImpl } @TransactionalDeleteBlob - protected void deleteComponentTx(final EntityId componentId, final boolean deleteBlobs) { + protected void deleteComponentTx(final EntityId componentId) { StorageTx tx = UnitOfWork.currentTx(); Component component = tx.findComponentInBucket(componentId, tx.findBucket(getRepository())); if (component == null) { return; } log.info("Deleting component: {}", component); - tx.deleteComponent(component, deleteBlobs); + tx.deleteComponent(component); } /** @@ -74,16 +66,10 @@ public class DefaultComponentMaintenanceImpl @Override @Guarded(by = STARTED) public void deleteAsset(final EntityId assetId) { - deleteAsset(assetId, true); - } - - @Override - @Guarded(by = STARTED) - public void deleteAsset(final EntityId assetId, final boolean deleteBlob) { checkNotNull(assetId); UnitOfWork.begin(getRepository().facet(StorageFacet.class).txSupplier()); try { - deleteAssetTx(assetId, deleteBlob); + deleteAssetTx(assetId); } finally { UnitOfWork.end(); @@ -91,13 +77,13 @@ public class DefaultComponentMaintenanceImpl } @TransactionalDeleteBlob - protected void deleteAssetTx(final EntityId assetId, final boolean deleteBlob) { + protected void deleteAssetTx(final EntityId assetId) { StorageTx tx = UnitOfWork.currentTx(); Asset asset = tx.findAsset(assetId, tx.findBucket(getRepository())); if (asset == null) { return; } log.info("Deleting asset: {}", asset); - tx.deleteAsset(asset, deleteBlob); + tx.deleteAsset(asset); } } diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskDescriptor.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskDescriptor.java index 1c9dcab79dcb0220db864416837ad8fc1cc3f627..0e1be50be6d8913e4968925b8451cca9f3687642 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskDescriptor.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskDescriptor.java @@ -37,7 +37,7 @@ public class RebuildAssetUploadMetadataTaskDescriptor super( TYPE_ID, RebuildAssetUploadMetadataTask.class, - "Repair - Reconcile date metadata from blob store", + "Rebuild asset upload metadata", configuration.isEnabled() ? VISIBLE : NOT_VISIBLE, configuration.isEnabled() ? EXPOSED : NOT_EXPOSED, nodeAccess.isClustered() ? newLimitNodeFormField() : null); diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/SingleAssetComponentMaintenance.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/SingleAssetComponentMaintenance.java index 97ad7b7f2547ddd2a7df05245476dad6935f5036..4702f6620fdce8e2d10332cb1303bbee33692116 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/SingleAssetComponentMaintenance.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/SingleAssetComponentMaintenance.java @@ -32,7 +32,7 @@ public class SingleAssetComponentMaintenance * Deletes both the asset and its component. */ @TransactionalDeleteBlob - protected void deleteAssetTx(final EntityId assetId, final boolean deleteBlob) { + protected void deleteAssetTx(final EntityId assetId) { StorageTx tx = UnitOfWork.currentTx(); final Asset asset = tx.findAsset(assetId, tx.findBucket(getRepository())); if (asset == null) { @@ -41,11 +41,11 @@ public class SingleAssetComponentMaintenance final EntityId componentId = asset.componentId(); if (componentId == null) { // Assets without components should be deleted on their own - super.deleteAssetTx(assetId, deleteBlob); + super.deleteAssetTx(assetId); } else { // Otherwise, delete the component, which in turn cascades down to the asset - deleteComponentTx(componentId, deleteBlob); + deleteComponentTx(componentId); } } } diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTx.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTx.java index 92f288d3b5dc5b1244b965e24fcc4ea26077ec9d..83eee92d515fa97507cbe47380c16fc81f7a5660 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTx.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTx.java @@ -332,31 +332,11 @@ public interface StorageTx */ void deleteComponent(Component component); - /** - * Deletes an existing component, all constituent assets, and maybe requests deletion of the asset blobs. - * - * @param component to be deleted - * @param deleteBlobs should asset blob deletion be requested - * - * @since 3.next - */ - void deleteComponent(Component component, boolean deleteBlobs); - /** * Deletes an existing asset and requests the blob to be deleted. */ void deleteAsset(Asset asset); - /** - * Deletes an existing asset and maybe requests the blob be deleted. - * - * @param asset to be deleted - * @param deleteBlob should blob deletion be requested - * - * @since 3.next - */ - void deleteAsset(Asset asset, boolean deleteBlob); - /** * Creates a new Blob and updates the given asset with a reference to it, hash metadata, size, and content type. * The old blob, if any, will be deleted. diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTxImpl.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTxImpl.java index f8119d58327ff2c232ee1a6a6c71ff3f0eb448c5..0891515fc16b02b8ee6dd96d4c2d2b1d2db39e56 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTxImpl.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTxImpl.java @@ -530,48 +530,31 @@ public class StorageTxImpl @Override @Guarded(by = ACTIVE) - public void deleteComponent(final Component component) { + public void deleteComponent(Component component) { deleteComponent(component, true); } - @Override - @Guarded(by = ACTIVE) - public void deleteComponent(final Component component, final boolean deleteBlobs) { - deleteComponent(component, true, deleteBlobs); - } - - private void deleteComponent(final Component component, final boolean checkWritePolicy, final boolean deleteBlobs) { + private void deleteComponent(final Component component, final boolean checkWritePolicy) { checkNotNull(component); for (Asset asset : browseAssets(component)) { - deleteAsset(asset, checkWritePolicy ? writePolicySelector.select(asset, writePolicy) : null, deleteBlobs); + deleteAsset(asset, checkWritePolicy ? writePolicySelector.select(asset, writePolicy) : null); } componentEntityAdapter.deleteEntity(db, component); } @Override @Guarded(by = ACTIVE) - public void deleteAsset(final Asset asset) { - deleteAsset(asset, true); - } - - @Override - @Guarded(by = ACTIVE) - public void deleteAsset(final Asset asset, final boolean deleteBlob) { - deleteAsset(asset, writePolicySelector.select(asset, writePolicy), deleteBlob); + public void deleteAsset(Asset asset) { + deleteAsset(asset, writePolicySelector.select(asset, writePolicy)); } - private void deleteAsset(final Asset asset, - @Nullable final WritePolicy effectiveWritePolicy, - final boolean deleteBlob) - { + private void deleteAsset(final Asset asset, @Nullable final WritePolicy effectiveWritePolicy) { checkNotNull(asset); - if (deleteBlob) { - BlobRef blobRef = asset.blobRef(); - if (blobRef != null) { - deleteBlob(blobRef, effectiveWritePolicy, format("Deleting asset %s", EntityHelper.id(asset))); - } + BlobRef blobRef = asset.blobRef(); + if (blobRef != null) { + deleteBlob(blobRef, effectiveWritePolicy, format("Deleting asset %s", EntityHelper.id(asset))); } assetEntityAdapter.deleteEntity(db, asset); } diff --git a/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/api/ComponentResponseUtilsTest.groovy b/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/api/ComponentResponseUtilsTest.groovy deleted file mode 100644 index a5bdc31f8552d267854951902afb23cb8c337f32..0000000000000000000000000000000000000000 --- a/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/api/ComponentResponseUtilsTest.groovy +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Sonatype Nexus (TM) Open Source Version - * Copyright (c) 2008-present Sonatype, Inc. - * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. - * - * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, - * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. - * - * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks - * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the - * Eclipse Foundation. All other trademarks are the property of their respective owners. - */ -package org.sonatype.nexus.repository.rest.api - -import org.sonatype.nexus.repository.storage.DefaultComponent - -import spock.lang.Specification - -class ComponentResponseUtilsTest - extends Specification -{ - def "a component with only a name has only that name in the return map"() { - given: 'component only has name' - def component = new DefaultComponent() - component.name("name") - - when: 'method called' - def result = ComponentResponseUtils.mapFor(component) - - then: 'map will only contain name' - result.size() == 1 - result.containsKey("name") - } - - def "a component with all attributes has all three entries in map"() { - given: 'component is fully populated' - def component = new DefaultComponent() - component.name("name") - component.group("group") - component.version("1.0.0") - - when: 'method called' - def result = ComponentResponseUtils.mapFor(component) - - then: 'map will contain all three entries' - result.size() == 3 - result.containsKey("name") - result.containsKey("group") - result.containsKey("version") - } -} diff --git a/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoriesResourceTest.groovy b/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoriesResourceTest.groovy deleted file mode 100644 index 335a3c437f585f5b9748928a7e6291eb031df17f..0000000000000000000000000000000000000000 --- a/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoriesResourceTest.groovy +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Sonatype Nexus (TM) Open Source Version - * Copyright (c) 2008-present Sonatype, Inc. - * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. - * - * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, - * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. - * - * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks - * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the - * Eclipse Foundation. All other trademarks are the property of their respective owners. - */ -package org.sonatype.nexus.repository.rest.internal.resources - -import org.sonatype.goodies.testsupport.TestSupport -import org.sonatype.nexus.repository.Format -import org.sonatype.nexus.repository.Repository -import org.sonatype.nexus.repository.Type -import org.sonatype.nexus.repository.rest.api.RepositoryXO -import org.sonatype.nexus.repository.types.HostedType -import org.sonatype.nexus.repository.types.ProxyType - -import org.junit.Before -import org.junit.Test -import org.mockito.Mock - -import static org.hamcrest.Matchers.is -import static org.junit.Assert.assertThat -import static org.mockito.Mockito.mock -import static org.mockito.Mockito.when - -class RepositoriesResourceTest - extends TestSupport -{ - private static final String REPOSITORY_1_NAME = 'repoOneName' - - private static final Format REPOSITORY_1_FORMAT = new Format('repoOneFormat') {} - - private static final Type REPOSITORY_1_TYPE = new ProxyType() - - private static final String REPOSITORY_1_URL = 'repoOneUrl' - - private static final RepositoryXO REPOSITORY_XO_1 = - new RepositoryXO(name: REPOSITORY_1_NAME, format: REPOSITORY_1_FORMAT.getValue(), - type: REPOSITORY_1_TYPE.getValue(), url: REPOSITORY_1_URL) - - private static final String REPOSITORY_2_NAME = 'repoTwoName' - - private static final Format REPOSITORY_2_FORMAT = new Format('repoTwoFormat') {} - - private static final Type REPOSITORY_2_TYPE = new HostedType() - - private static final String REPOSITORY_2_URL = 'repoTwoUrl' - - private static final RepositoryXO REPOSITORY_XO_2 = - new RepositoryXO(name: REPOSITORY_2_NAME, format: REPOSITORY_2_FORMAT.getValue(), - type: REPOSITORY_2_TYPE.getValue(), url: REPOSITORY_2_URL) - - @Mock - private RepositoryManagerRESTAdapter repositoryManagerRESTAdapter - - private RepositoriesResource underTest - - @Before - void setup() { - Repository repository1 = - createMockRepository(REPOSITORY_1_NAME, REPOSITORY_1_FORMAT, REPOSITORY_1_TYPE, REPOSITORY_1_URL) - Repository repository2 = - createMockRepository(REPOSITORY_2_NAME, REPOSITORY_2_FORMAT, REPOSITORY_2_TYPE, REPOSITORY_2_URL) - - when(repositoryManagerRESTAdapter.getRepositories()).thenReturn([repository1, repository2]) - - underTest = new RepositoriesResource(repositoryManagerRESTAdapter) - } - - @Test - void testGetRepositories() { - assertThat(underTest.getRepositories(), is([REPOSITORY_XO_1, REPOSITORY_XO_2])) - } - - private static Repository createMockRepository(final String name, final Format format, final Type type, - final String url) - { - Repository repository = mock(Repository.class) - when(repository.getName()).thenReturn(name) - when(repository.getFormat()).thenReturn(format) - when(repository.getType()).thenReturn(type) - when(repository.getUrl()).thenReturn(url) - return repository - } -} diff --git a/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImplTest.java b/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImplTest.java index 21cf2ceac223d1ca81fc2cdfd2ce248bbaf1bbff..b0aac50dc5736b83cd3fe43e4792a8358c22336b 100644 --- a/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImplTest.java +++ b/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImplTest.java @@ -12,8 +12,6 @@ */ package org.sonatype.nexus.repository.rest.internal.resources; -import java.util.Arrays; - import javax.ws.rs.NotFoundException; import javax.ws.rs.WebApplicationException; @@ -44,38 +42,12 @@ import static org.sonatype.nexus.security.BreadActions.READ; public class RepositoryManagerRESTAdapterImplTest extends TestSupport { - private static final String REPOSITORY_NAME = "repoName"; - - private static final String REPOSITORY_NAME_2 = "repoNameTwo"; - - private static final String REPOSITORY_NAME_3 = "repoNameThree"; - - private static final String REPOSITORY_FORMAT = "repoFormat"; - - private static final String REPOSITORY_FORMAT_2 = "repoFormatTwo"; - - private static final String REPOSITORY_FORMAT_3 = "repoFormatThree"; - - private static final boolean PERMIT_BROWSE = true; - - private static final boolean PERMIT_READ = true; - - private static final boolean IGNORED_PERMIT_READ = false; - - private static final boolean PERMIT_VIA_CONTENT_SELECTOR = true; - @Mock RepositoryManager repositoryManager; @Mock Repository repository; - @Mock - Repository repository2; - - @Mock - Repository repository3; - @Mock SecurityHelper securityHelper; @@ -88,64 +60,53 @@ public class RepositoryManagerRESTAdapterImplTest @Mock Format repositoryFormat; - @Mock - Format repositoryFormat2; - - @Mock - Format repositoryFormat3; - @Mock SelectorConfiguration selectorConfiguration; @Mock SelectorManager selectorManager; + final String REPOSITORY_NAME = "test"; + RepositoryManagerRESTAdapterImpl underTest; @Before public void setUp() throws Exception { when(repositoryManager.get(REPOSITORY_NAME)).thenReturn(repository); - when(repositoryManager.browse()).thenReturn(Arrays.asList(repository, repository2, repository3)); when(repository.getFormat()).thenReturn(repositoryFormat); - when(repository2.getFormat()).thenReturn(repositoryFormat2); - when(repository3.getFormat()).thenReturn(repositoryFormat3); when(selectorManager.browse()).thenReturn(singletonList(selectorConfiguration)); when(selectorConfiguration.getName()).thenReturn("selector"); when(repository.getName()).thenReturn(REPOSITORY_NAME); - when(repository2.getName()).thenReturn(REPOSITORY_NAME_2); - when(repository3.getName()).thenReturn(REPOSITORY_NAME_3); - when(repositoryFormat.getValue()).thenReturn(REPOSITORY_FORMAT); - when(repositoryFormat2.getValue()).thenReturn(REPOSITORY_FORMAT_2); - when(repositoryFormat3.getValue()).thenReturn(REPOSITORY_FORMAT_3); + when(repositoryFormat.getValue()).thenReturn("m2"); underTest = new RepositoryManagerRESTAdapterImpl(repositoryManager, securityHelper, selectorManager); } @Test public void getRepository_allPermissions() throws Exception { - configurePermissions(repository, PERMIT_BROWSE, PERMIT_READ, PERMIT_VIA_CONTENT_SELECTOR); + configurePermissions(true, true, true); assertThat(underTest.getRepository(REPOSITORY_NAME), is(repository)); } @Test public void getRepository_browseOnly() throws Exception { - configurePermissions(repository, PERMIT_BROWSE, !PERMIT_READ, !PERMIT_VIA_CONTENT_SELECTOR); + configurePermissions(true, false, false); assertThat(underTest.getRepository(REPOSITORY_NAME), is(repository)); } @Test public void getRepository_contentSelectorOnly() throws Exception { - configurePermissions(repository, !PERMIT_BROWSE, !PERMIT_READ, PERMIT_VIA_CONTENT_SELECTOR); + configurePermissions(false, false, true); assertThat(underTest.getRepository(REPOSITORY_NAME), is(repository)); } @Test public void getRepository_readOnlyReturnsForbidden() throws Exception { - configurePermissions(repository, !PERMIT_BROWSE, PERMIT_READ, !PERMIT_VIA_CONTENT_SELECTOR); + configurePermissions(false, true, false); try { underTest.getRepository(REPOSITORY_NAME); @@ -158,12 +119,11 @@ public class RepositoryManagerRESTAdapterImplTest @Test(expected = NotFoundException.class) public void getRepository_notFoundWithNoPermissions() { - configurePermissions(repository, !PERMIT_BROWSE, !PERMIT_READ, !PERMIT_VIA_CONTENT_SELECTOR); + configurePermissions(false, false, false); underTest.getRepository(REPOSITORY_NAME); } - private void configurePermissions(final Repository repository, - final boolean permitBrowse, + private void configurePermissions(final boolean permitBrowse, final boolean permitRead, final boolean permitViaContentSelector) { @@ -198,13 +158,4 @@ public class RepositoryManagerRESTAdapterImplTest assertThat(e.getResponse().getStatus(), is(422)); } } - - @Test - public void getRepositories() { - configurePermissions(repository, !PERMIT_BROWSE, IGNORED_PERMIT_READ, PERMIT_VIA_CONTENT_SELECTOR); - configurePermissions(repository2, PERMIT_BROWSE, IGNORED_PERMIT_READ, !PERMIT_VIA_CONTENT_SELECTOR); - configurePermissions(repository3, !PERMIT_BROWSE, IGNORED_PERMIT_READ, !PERMIT_VIA_CONTENT_SELECTOR); - - assertThat(underTest.getRepositories(), is(Arrays.asList(repository, repository2))); - } } diff --git a/components/nexus-rest-client/pom.xml b/components/nexus-rest-client/pom.xml index 2b5d889c8edd679ff0021d24c877913630b31f29..bd17b42c95713bf6d98a9b366237c68b8a5a954b 100644 --- a/components/nexus-rest-client/pom.xml +++ b/components/nexus-rest-client/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-rest-client diff --git a/components/nexus-rest-jackson2/pom.xml b/components/nexus-rest-jackson2/pom.xml index 964217f8f98a487a22b47196a834ba4f38f937fb..d2d8130558c3256c571169347ab9281699699488 100644 --- a/components/nexus-rest-jackson2/pom.xml +++ b/components/nexus-rest-jackson2/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-rest-jackson2 diff --git a/components/nexus-rest/pom.xml b/components/nexus-rest/pom.xml index d27b8c0c826975b597c9ddc6cd47f6d32bc0d592..3e79f303160a98c4ec289a13e3bf6c9f377c2b84 100644 --- a/components/nexus-rest/pom.xml +++ b/components/nexus-rest/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-rest diff --git a/components/nexus-scheduling/pom.xml b/components/nexus-scheduling/pom.xml index 453da94f4597d0bc457ceb39d6a6c52aa89964d9..4ea0ac8399a2e293481bfdfd304b821d725312ed 100644 --- a/components/nexus-scheduling/pom.xml +++ b/components/nexus-scheduling/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-scheduling diff --git a/components/nexus-script/pom.xml b/components/nexus-script/pom.xml index ac52a847a27d9123b7363ed364c1ee7b01a9b6e0..5b0f66ba3f8325d5dfb827fb302c48547a2ab940 100644 --- a/components/nexus-script/pom.xml +++ b/components/nexus-script/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-script diff --git a/components/nexus-security/pom.xml b/components/nexus-security/pom.xml index a40c7a107ce6056118b35b8043360e96342a0c27..db9a6be6c851718f752b78f9b0545f59b44b48d0 100644 --- a/components/nexus-security/pom.xml +++ b/components/nexus-security/pom.xml @@ -21,7 +21,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-security diff --git a/components/nexus-selector/pom.xml b/components/nexus-selector/pom.xml index 05f138ae1b6884d7492b32105338a90fb4ed8784..edab56b3d18d218c5ebe43bb6ac425ea1409e38e 100644 --- a/components/nexus-selector/pom.xml +++ b/components/nexus-selector/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-selector diff --git a/components/nexus-servlet/pom.xml b/components/nexus-servlet/pom.xml index 233754cad7fbf43ee79824f156e5a1d95c4066e0..2504353803c288a07475d0ed5bf3778fbce53578 100644 --- a/components/nexus-servlet/pom.xml +++ b/components/nexus-servlet/pom.xml @@ -19,7 +19,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-servlet diff --git a/components/nexus-siesta/pom.xml b/components/nexus-siesta/pom.xml index 4a4295b8648596f0fab7a1594f15414160ac691a..25a43b6f88fdf1d15bd920a6e6f68e0fb4de37c1 100644 --- a/components/nexus-siesta/pom.xml +++ b/components/nexus-siesta/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-siesta diff --git a/components/nexus-ssl/pom.xml b/components/nexus-ssl/pom.xml index 678a71e8b2ad9979a8c7c60e052c3d3960eb321b..381a0033aadf377d32f86cf5f23f2ba8be50a86b 100644 --- a/components/nexus-ssl/pom.xml +++ b/components/nexus-ssl/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-ssl diff --git a/components/nexus-supportzip-api/pom.xml b/components/nexus-supportzip-api/pom.xml index 2104d8a86656432a72d0f5d0fa67240e3198c33c..7db87e4c8b202871f3cc7a3d07d8958835fca7ea 100644 --- a/components/nexus-supportzip-api/pom.xml +++ b/components/nexus-supportzip-api/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-supportzip-api diff --git a/components/nexus-swagger/pom.xml b/components/nexus-swagger/pom.xml index 89b2d09014cb494c9819ab9f21de78c3c2df8da6..012847a17c9fb229d8d112e402f02609f77fc97d 100644 --- a/components/nexus-swagger/pom.xml +++ b/components/nexus-swagger/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-swagger diff --git a/components/nexus-swagger/src/main/java/org/sonatype/nexus/swagger/ParameterContributor.java b/components/nexus-swagger/src/main/java/org/sonatype/nexus/swagger/ParameterContributor.java index dcc18cb0fffaf5aa3edd44ba7cf2e3408cd9cb0c..655d07fba1c41f29f5fa856083751aa30624ba03 100644 --- a/components/nexus-swagger/src/main/java/org/sonatype/nexus/swagger/ParameterContributor.java +++ b/components/nexus-swagger/src/main/java/org/sonatype/nexus/swagger/ParameterContributor.java @@ -39,7 +39,7 @@ public abstract class ParameterContributor httpMethods; + private final HttpMethod httpMethod; private final Collection paths; @@ -49,11 +49,11 @@ public abstract class ParameterContributor httpMethods, + public ParameterContributor(final HttpMethod httpMethod, final Collection paths, final Collection params) { - this.httpMethods = checkNotNull(httpMethods); + this.httpMethod = checkNotNull(httpMethod); this.paths = checkNotNull(paths); this.params = checkNotNull(params); this.contributed = paths.stream().collect(toMap(p -> p, p -> false)); @@ -65,12 +65,10 @@ public abstract class ParameterContributor { - paths.forEach(p -> { - if (!contributed.get(p) && contributeGetParameters(swagger, httpMethod, p, params)) { - contributed.put(p, true); - } - }); + paths.forEach(p -> { + if (!contributed.get(p) && contributeGetParameters(swagger, httpMethod, p, params)) { + contributed.put(p, true); + } }); allContributed = contributed.entrySet().stream().allMatch(Entry::getValue); diff --git a/components/nexus-task-logging/pom.xml b/components/nexus-task-logging/pom.xml index 4a65f32455f38622fb9dc0826ecfbd4b496e402b..aa3c9319bd203ceebd6755e0ac04045eabd2c3aa 100644 --- a/components/nexus-task-logging/pom.xml +++ b/components/nexus-task-logging/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-task-logging diff --git a/components/nexus-test-common/pom.xml b/components/nexus-test-common/pom.xml index 756c824d1c9d74f50f5ca95dd8720f8bf2ea0561..106cc1d76251c549a948c85ff80d3026d27c2366 100644 --- a/components/nexus-test-common/pom.xml +++ b/components/nexus-test-common/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-test-common diff --git a/components/nexus-thread/pom.xml b/components/nexus-thread/pom.xml index ec4307bd2d73fa6afe48d7ce6f486304f1bd60f4..67b24d5a4678a8b097c714d7024c5098ce85d2ac 100644 --- a/components/nexus-thread/pom.xml +++ b/components/nexus-thread/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-thread diff --git a/components/nexus-transaction/pom.xml b/components/nexus-transaction/pom.xml index 1e4efae82b4c4732b2b389a1b31a81590d332f3c..787e184ff92a691cb904fd7ce95c1c58f1520af0 100644 --- a/components/nexus-transaction/pom.xml +++ b/components/nexus-transaction/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-transaction diff --git a/components/nexus-upgrade/pom.xml b/components/nexus-upgrade/pom.xml index 987decbc9a72fd50bb6e18215f8faa56d9cafab5..df806ba31d6eed65d688717bd414439416ca756f 100644 --- a/components/nexus-upgrade/pom.xml +++ b/components/nexus-upgrade/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-upgrade diff --git a/components/nexus-validation/pom.xml b/components/nexus-validation/pom.xml index b1b60c0b06c77b56d700d00f3ab0fc8bf228d223..445ed8cbcd263c71017666f832a537d6aa729560 100644 --- a/components/nexus-validation/pom.xml +++ b/components/nexus-validation/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-validation diff --git a/components/nexus-webhooks/pom.xml b/components/nexus-webhooks/pom.xml index 7e5a1598a1dc3dc9c2395a4cf9e2c07aa97010f8..6e8e656179be34ad366268252d8db253010f4f46 100644 --- a/components/nexus-webhooks/pom.xml +++ b/components/nexus-webhooks/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-webhooks diff --git a/components/nexus-webresources-api/pom.xml b/components/nexus-webresources-api/pom.xml index 5ef6de5f62a5139d7648ea5f313dadcd88f7d4f2..469e7a615301aa27e22ae3ba7dc1831903c9fd79 100644 --- a/components/nexus-webresources-api/pom.xml +++ b/components/nexus-webresources-api/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-components - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-webresources-api diff --git a/components/pom.xml b/components/pom.xml index 1d417bc3c89b6fa3f28fef4d5638d8f40fccfd89..f56513fbfd6e1255b9a82b1743e620e077126392 100644 --- a/components/pom.xml +++ b/components/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-parent - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-components @@ -91,7 +91,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-all pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -99,7 +99,7 @@ org.sonatype.nexus.bundles nexus-thirdparty-bundles pom - 3.8.0-SNAPSHOT + 3.8.0-02 import diff --git a/mvnw b/mvnw old mode 100755 new mode 100644 diff --git a/plugins/nexus-audit-plugin/pom.xml b/plugins/nexus-audit-plugin/pom.xml index 09b72318a4a6e7e107ac175e1d6c5f7d3c3a5da7..23abb73bfcc0eb251c2d3c672c6430b8e099b5d1 100644 --- a/plugins/nexus-audit-plugin/pom.xml +++ b/plugins/nexus-audit-plugin/pom.xml @@ -21,7 +21,7 @@ org.sonatype.nexus.plugins nexus-plugins - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-audit-plugin diff --git a/plugins/nexus-blobstore-s3/pom.xml b/plugins/nexus-blobstore-s3/pom.xml index 7616bcd28f00fc148bf3a32ed43a7ffae4f766a6..c39db25cc30f2b07deb55138582de866272e3d0e 100644 --- a/plugins/nexus-blobstore-s3/pom.xml +++ b/plugins/nexus-blobstore-s3/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.plugins nexus-plugins - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-blobstore-s3 diff --git a/plugins/nexus-blobstore-tasks/pom.xml b/plugins/nexus-blobstore-tasks/pom.xml index 7f3f82191946fd9f34871263960de15ddbf3a49f..67351ee918e56d2ac09abc5c4f215c1f56b93944 100644 --- a/plugins/nexus-blobstore-tasks/pom.xml +++ b/plugins/nexus-blobstore-tasks/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.plugins nexus-plugins - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-blobstore-tasks diff --git a/plugins/nexus-blobstore-tasks/src/main/java/org/sonatype/nexus/blobstore/compact/internal/CompactBlobStoreTaskDescriptor.java b/plugins/nexus-blobstore-tasks/src/main/java/org/sonatype/nexus/blobstore/compact/internal/CompactBlobStoreTaskDescriptor.java index 2950b0a31baa6d121baa861367ba5f308e1555b0..190e8e7cc3de4ff54e5a77b20711165c95ce43f6 100644 --- a/plugins/nexus-blobstore-tasks/src/main/java/org/sonatype/nexus/blobstore/compact/internal/CompactBlobStoreTaskDescriptor.java +++ b/plugins/nexus-blobstore-tasks/src/main/java/org/sonatype/nexus/blobstore/compact/internal/CompactBlobStoreTaskDescriptor.java @@ -40,7 +40,7 @@ public class CompactBlobStoreTaskDescriptor public CompactBlobStoreTaskDescriptor() { super(TYPE_ID, CompactBlobStoreTask.class, - "Admin - Compact blob store", + "Compact blob store", VISIBLE, EXPOSED, new ComboboxFormField( diff --git a/plugins/nexus-blobstore-tasks/src/main/java/org/sonatype/nexus/blobstore/restore/RestoreMetadataTaskDescriptor.java b/plugins/nexus-blobstore-tasks/src/main/java/org/sonatype/nexus/blobstore/restore/RestoreMetadataTaskDescriptor.java index 3b9440752a5a145b13cee2e13d07d69c4c6ad7ce..8431b783be4610c4361f23573fd40e7c8a9c4552 100644 --- a/plugins/nexus-blobstore-tasks/src/main/java/org/sonatype/nexus/blobstore/restore/RestoreMetadataTaskDescriptor.java +++ b/plugins/nexus-blobstore-tasks/src/main/java/org/sonatype/nexus/blobstore/restore/RestoreMetadataTaskDescriptor.java @@ -45,7 +45,7 @@ public class RestoreMetadataTaskDescriptor static final String DRY_RUN = "dryRun"; private interface Messages extends MessageBundle { - @DefaultMessage("Repair - Reconcile component database from blob store") + @DefaultMessage("Restore Asset/Component metadata from Blob Store") String name(); @DefaultMessage("Blob store") diff --git a/plugins/nexus-coreui-plugin/pom.xml b/plugins/nexus-coreui-plugin/pom.xml index e9b843255f633f305c213b922dc8cf41b781cb40..8683636d09471dc396a2943d9607b8bbf313794e 100644 --- a/plugins/nexus-coreui-plugin/pom.xml +++ b/plugins/nexus-coreui-plugin/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.plugins nexus-plugins - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-coreui-plugin diff --git a/plugins/nexus-repository-httpbridge/pom.xml b/plugins/nexus-repository-httpbridge/pom.xml index 4eee779008bb2f4f8141acc420cda749cb16453e..55e360fc0f41666507506c36a3a8fb9cb50e7674 100644 --- a/plugins/nexus-repository-httpbridge/pom.xml +++ b/plugins/nexus-repository-httpbridge/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.plugins nexus-plugins - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-repository-httpbridge diff --git a/plugins/nexus-repository-maven/pom.xml b/plugins/nexus-repository-maven/pom.xml index 5d198d8cbcccf53fd09d4bfaf388b8eda5101edd..8a2d72497e33d62559f750cbedc61a8cb672e88e 100644 --- a/plugins/nexus-repository-maven/pom.xml +++ b/plugins/nexus-repository-maven/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.plugins nexus-plugins - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-repository-maven diff --git a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/hosted/MavenHostedComponentMaintenanceFacet.java b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/hosted/MavenHostedComponentMaintenanceFacet.java index b2838e0cccd83ad70521422e76b0c4f0b538d477..1ccb5af69c23e8531e3885daf7b91a21c50aeaae 100644 --- a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/hosted/MavenHostedComponentMaintenanceFacet.java +++ b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/hosted/MavenHostedComponentMaintenanceFacet.java @@ -38,7 +38,7 @@ public class MavenHostedComponentMaintenanceFacet extends DefaultComponentMaintenanceImpl { @Override - public void deleteComponent(final EntityId componentId, final boolean deleteBlobs) { + public void deleteComponent(final EntityId componentId) { String[] coordinates = Transactional.operation .withDb(getRepository().facet(StorageFacet.class).txSupplier()) .call(() -> { @@ -53,7 +53,7 @@ public class MavenHostedComponentMaintenanceFacet } return null; }); - super.deleteComponent(componentId, deleteBlobs); + super.deleteComponent(componentId); if (coordinates != null) { getRepository().facet(MavenHostedFacet.class) .deleteMetadata(coordinates[0], coordinates[1], coordinates[2]); diff --git a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/PublishMavenIndexTaskDescriptor.java b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/PublishMavenIndexTaskDescriptor.java index 6534d46efc90d6d41dfd774661a44ccab7bbb4fd..47ca83814ef64b17e0ce35d3fbea4fd8aab4603d 100644 --- a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/PublishMavenIndexTaskDescriptor.java +++ b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/PublishMavenIndexTaskDescriptor.java @@ -36,13 +36,13 @@ public class PublishMavenIndexTaskDescriptor public PublishMavenIndexTaskDescriptor() { super(TYPE_ID, PublishMavenIndexTask.class, - "Maven - Publish Maven Indexer files", + "Publish Maven indexes", VISIBLE, EXPOSED, new RepositoryCombobox( REPOSITORY_NAME_FIELD_ID, "Repository", - "Select the Maven repository to publish indexer files for", + "Select the Maven repository to publish indexes for", true ).includingAnyOfFormats(Maven2Format.NAME).includeAnEntryForAllRepositories() ); diff --git a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/PurgeMavenUnusedSnapshotsTaskDescriptor.java b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/PurgeMavenUnusedSnapshotsTaskDescriptor.java index 81c7af10dfac058dc54f7c961d0afbff86794db8..149efbe35e6421bc605d43e19b9a798cc3fb47a9 100644 --- a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/PurgeMavenUnusedSnapshotsTaskDescriptor.java +++ b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/PurgeMavenUnusedSnapshotsTaskDescriptor.java @@ -34,7 +34,7 @@ import static org.sonatype.nexus.repository.maven.tasks.PurgeMavenUnusedSnapshot public class PurgeMavenUnusedSnapshotsTaskDescriptor extends TaskDescriptorSupport { - public static final String TASK_NAME = "Maven - Delete unused SNAPSHOT"; + public static final String TASK_NAME = "Purge unused Maven snapshot versions"; public static final String TYPE_ID = "repository.maven.purge-unused-snapshots"; @@ -51,13 +51,13 @@ public class PurgeMavenUnusedSnapshotsTaskDescriptor new RepositoryCombobox( REPOSITORY_NAME_FIELD_ID, "Repository", - "Select the repository to delete unused snapshot versions from", + "Select the repository to purge unused snapshot versions from", FormField.MANDATORY ).includingAnyOfFacets(PurgeUnusedSnapshotsFacet.class).includeAnEntryForAllRepositories(), new NumberTextFormField( LAST_USED_FIELD_ID, "Last used in days", - "Delete all snapshots that were last used before given number of days", + "Purge all snapshots that were last used before given number of days", FormField.MANDATORY ).withInitialValue(LAST_USED_INIT_VALUE).withMinimumValue(LAST_USED_MIN_VALUE) ); diff --git a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/RebuildMaven2MetadataTaskDescriptor.java b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/RebuildMaven2MetadataTaskDescriptor.java index 91b49c3a8d40ef0d9880284d47b300ce01b30980..be9acfc34b1797a0bc527c362e9de64a6595393d 100644 --- a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/RebuildMaven2MetadataTaskDescriptor.java +++ b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/RebuildMaven2MetadataTaskDescriptor.java @@ -49,7 +49,7 @@ public class RebuildMaven2MetadataTaskDescriptor public RebuildMaven2MetadataTaskDescriptor() { super(TYPE_ID, RebuildMaven2MetadataTask.class, - "Repair - Rebuild Maven repository metadata (maven-metadata.xml)", + "Rebuild Maven repository metadata", VISIBLE, EXPOSED, new RepositoryCombobox( diff --git a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/RemoveSnapshotsTaskDescriptor.java b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/RemoveSnapshotsTaskDescriptor.java index 76f0647c82df775472211e25b99a6a6196384708..5e5589a24e6dc2a6141c0297eb4e1c17d5bba6c2 100644 --- a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/RemoveSnapshotsTaskDescriptor.java +++ b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/RemoveSnapshotsTaskDescriptor.java @@ -47,7 +47,7 @@ public class RemoveSnapshotsTaskDescriptor { super(TYPE_ID, RemoveSnapshotsTask.class, - "Maven - Delete SNAPSHOT", + "Remove snapshots from Maven repository", VISIBLE, EXPOSED, new RepositoryCombobox(REPOSITORY_NAME_FIELD_ID, @@ -62,14 +62,14 @@ public class RemoveSnapshotsTaskDescriptor true).withInitialValue(1).withMinimumValue(-1), new NumberTextFormField(SNAPSHOT_RETENTION_DAYS, "Snapshot retention (days)", - "Delete all snapshots older than this, provided we still keep the minimum number specified.", + "Purge all snapshots older than this, provided we still keep the minimum number specified.", true).withInitialValue(30).withMinimumValue(0), new CheckboxFormField(REMOVE_IF_RELEASED, "Remove if released", - "Delete all snapshots that have a corresponding release", false), + "Purge all snapshots that have a corresponding release", false), new NumberTextFormField(GRACE_PERIOD, "Grace period after release (days)", - "The grace period during which snapshots with an associated release will not be deleted.", + "The grace period during which snapshots with an associated release will not be purged.", false).withMinimumValue(0)); } } diff --git a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/UnpublishMavenIndexTaskDescriptor.java b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/UnpublishMavenIndexTaskDescriptor.java index 9b6a9e6d3c082e88b55d3d9725a013197a253cd9..719b037595c3c333a42fea7327c7b2d20a1fd86d 100644 --- a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/UnpublishMavenIndexTaskDescriptor.java +++ b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/tasks/UnpublishMavenIndexTaskDescriptor.java @@ -36,7 +36,7 @@ public class UnpublishMavenIndexTaskDescriptor public UnpublishMavenIndexTaskDescriptor() { super(TYPE_ID, UnpublishMavenIndexTask.class, - "Maven - Unpublish Maven Indexer files", + "Remove Maven indexes", VISIBLE, EXPOSED, new RepositoryCombobox( diff --git a/plugins/nexus-repository-raw/pom.xml b/plugins/nexus-repository-raw/pom.xml index a34c532171e255b1e9cff1ffbc0868a0829bd5b1..14721670bed37ae0ac94c31ffda47c9ba2e73b32 100644 --- a/plugins/nexus-repository-raw/pom.xml +++ b/plugins/nexus-repository-raw/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.plugins nexus-plugins - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-repository-raw diff --git a/plugins/nexus-restore-maven/pom.xml b/plugins/nexus-restore-maven/pom.xml index cb815f076b0d56e45e68e100758217fe5edf4d10..412a4cd8de475a3233289e51a8938bb329856b6f 100644 --- a/plugins/nexus-restore-maven/pom.xml +++ b/plugins/nexus-restore-maven/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.plugins nexus-plugins - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-restore-maven diff --git a/plugins/nexus-script-plugin/pom.xml b/plugins/nexus-script-plugin/pom.xml index 7f257588c604fceb07b8fcf6eb4282e3427c62a3..0e60186080b4b0af4b1e1ffa6605fb77c046f9d9 100644 --- a/plugins/nexus-script-plugin/pom.xml +++ b/plugins/nexus-script-plugin/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.plugins nexus-plugins - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-script-plugin diff --git a/plugins/nexus-ssl-plugin/pom.xml b/plugins/nexus-ssl-plugin/pom.xml index d81be741c87b08cf991bac0f3f93e62446181e50..f878f9803702b3df68a452f5eef6cfe8acd83a07 100644 --- a/plugins/nexus-ssl-plugin/pom.xml +++ b/plugins/nexus-ssl-plugin/pom.xml @@ -21,7 +21,7 @@ org.sonatype.nexus.plugins nexus-plugins - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-ssl-plugin diff --git a/plugins/nexus-task-log-cleanup/pom.xml b/plugins/nexus-task-log-cleanup/pom.xml index 5e87799522539ee09628433795106a094cfac6c1..1365085e7dff53badf9fc7d75cfd8e38e16bef3d 100644 --- a/plugins/nexus-task-log-cleanup/pom.xml +++ b/plugins/nexus-task-log-cleanup/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus.plugins nexus-plugins - 3.8.0-SNAPSHOT + 3.8.0-02 nexus-task-log-cleanup diff --git a/plugins/pom.xml b/plugins/pom.xml index e7abf92962f8fd485294585d34a0a346059b3c23..35ff4b3cd0bc1cacee40860a26f1bf3aadb38cd7 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -20,7 +20,7 @@ org.sonatype.nexus nexus-parent - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.plugins @@ -51,7 +51,7 @@ org.sonatype.nexus.buildsupport nexus-buildsupport-all pom - 3.8.0-SNAPSHOT + 3.8.0-02 import @@ -62,13 +62,13 @@ org.sonatype.nexus.plugins nexus-audit-plugin - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.plugins nexus-audit-plugin - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -78,13 +78,13 @@ org.sonatype.nexus.plugins nexus-blobstore-tasks - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.plugins nexus-blobstore-tasks - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -94,13 +94,13 @@ org.sonatype.nexus.plugins nexus-coreui-plugin - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.plugins nexus-coreui-plugin - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -109,7 +109,7 @@ org.sonatype.nexus.plugins nexus-coreui-plugin sources - 3.8.0-SNAPSHOT + 3.8.0-02 @@ -117,13 +117,13 @@ org.sonatype.nexus.plugins nexus-repository-httpbridge - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.plugins nexus-repository-httpbridge - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -133,13 +133,13 @@ org.sonatype.nexus.plugins nexus-repository-maven - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.plugins nexus-repository-maven - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -147,13 +147,13 @@ org.sonatype.nexus.plugins nexus-restore-maven - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.plugins nexus-restore-maven - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -163,13 +163,13 @@ org.sonatype.nexus.plugins nexus-repository-raw - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.plugins nexus-repository-raw - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -179,13 +179,13 @@ org.sonatype.nexus.plugins nexus-ssl-plugin - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.plugins nexus-ssl-plugin - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -195,13 +195,13 @@ org.sonatype.nexus.plugins nexus-script-plugin - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.plugins nexus-script-plugin - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -211,13 +211,13 @@ org.sonatype.nexus.plugins nexus-task-log-cleanup - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.plugins nexus-task-log-cleanup - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -227,13 +227,13 @@ org.sonatype.nexus.plugins nexus-blobstore-s3 - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus.plugins nexus-blobstore-s3 - 3.8.0-SNAPSHOT + 3.8.0-02 features xml diff --git a/pom.xml b/pom.xml index 8f8c29e47cf65796caecf65d2cb54c4876d04eda..6181d56d2e99d5c5c7b004871d5ee773fe399aec 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ ${project.groupId}:${project.artifactId} pom - 3.8.0-SNAPSHOT + 3.8.0-02 2008 http://nexus.sonatype.org/ @@ -88,7 +88,7 @@ Define nexus versions. The 'nexus.version' property always refers to the version of the current project. These values must always be constants; 'nexus.version' will get update automatically by set-version. --> - 3.8.0-SNAPSHOT + 3.8.0-02 2.14.6-02 @@ -142,169 +142,169 @@ org.sonatype.nexus nexus-analytics-api - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-audit - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-base - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-blobstore - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-blobstore-api - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-blobstore-file - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-bootstrap - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-capability - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-commands - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-common - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-core - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-crypto - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-orient - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-orient-console - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-cache - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-elasticsearch - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-email - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-extdirect - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-extender - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-httpclient - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-jmx - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-scheduling - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-formfields - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-guice-servlet - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-main - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-mime - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-oss-edition - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-oss-edition - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -312,44 +312,44 @@ org.sonatype.nexus nexus-pax-logging - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-plugin-api - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-quartz - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-rapture - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-rapture - 3.8.0-SNAPSHOT + 3.8.0-02 sources org.sonatype.nexus nexus-repository - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-repository - 3.8.0-SNAPSHOT + 3.8.0-02 tests test @@ -357,109 +357,109 @@ org.sonatype.nexus nexus-rest - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-rest-client - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-rest-jackson2 - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-script - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-security - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-selector - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-servlet - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-siesta - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-ssl - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-supportzip-api - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-swagger - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-task-logging - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-thread - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-transaction - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-upgrade - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-validation - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-webhooks - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-webresources-api - 3.8.0-SNAPSHOT + 3.8.0-02 @@ -467,13 +467,13 @@ org.sonatype.nexus nexus-pax-exam - 3.8.0-SNAPSHOT + 3.8.0-02 org.sonatype.nexus nexus-test-common - 3.8.0-SNAPSHOT + 3.8.0-02 @@ -481,7 +481,7 @@ org.sonatype.nexus.assemblies nexus-startup-feature - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -489,7 +489,7 @@ org.sonatype.nexus.assemblies nexus-boot-feature - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -497,7 +497,7 @@ org.sonatype.nexus.assemblies nexus-base-feature - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -505,7 +505,7 @@ org.sonatype.nexus.assemblies nexus-core-feature - 3.8.0-SNAPSHOT + 3.8.0-02 features xml @@ -513,7 +513,7 @@ org.sonatype.nexus.assemblies nexus-base-template - 3.8.0-SNAPSHOT + 3.8.0-02 zip @@ -793,7 +793,7 @@ org.sonatype.nexus.buildsupport extjs-maven-plugin - 3.8.0-SNAPSHOT + 3.8.0-02 diff --git a/revision.txt b/revision.txt index a64e5cd392c910395bb0862620be526be472d364..6c19d2f35a74e1e3af106e95768a67d75e5e954e 100644 --- a/revision.txt +++ b/revision.txt @@ -1 +1 @@ -b=master,r=efff85647125f90a57bacf46f29e76cf1b3a31d2,t=2018-02-02-1826-12385 \ No newline at end of file +b=release-3.8.0,r=4900ae4d4f3bd55c34d43678225019bbb8d2e303,t=2018-02-01-1948-2081 \ No newline at end of file diff --git a/thirdparty-bundles/LICENSE.txt b/thirdparty-bundles/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..d6b3020fc7e5fcc7971e2df653af622de77ca06d --- /dev/null +++ b/thirdparty-bundles/LICENSE.txt @@ -0,0 +1,87 @@ +Eclipse Public License -v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and + +b) in the case of each subsequent Contributor: + +i) changes to the Program, and + +ii) additions to the Program; + +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. + +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. + +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. + +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and + +b) its license agreement: + +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; + +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; + +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and + +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and + +b) a copy of this Agreement must be included with each copy of the Program. + +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. diff --git a/thirdparty-bundles/README.md b/thirdparty-bundles/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a49d8cb6b7ee7271a8784d63e5fdb8646329ee88 --- /dev/null +++ b/thirdparty-bundles/README.md @@ -0,0 +1,47 @@ + +# Sonatype Nexus Repository Open Source Codebase + +[![Join the chat at https://gitter.im/sonatype/nexus-developers](https://badges.gitter.im/sonatype/nexus-developers.svg)](https://gitter.im/sonatype/nexus-developers?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +## Requirements + +* Apache Maven 3.3.3+ +* Java 8+ +* Network access to https://repository.sonatype.org/content/groups/sonatype-public-grid + +## Building + +To build the project and generate the template assembly use the included Maven wrapper: + + ./mvnw clean install + +## Running + +To run Nexus Repository, after building, unzip the assembly and start the server: + + unzip -d target assemblies/nexus-base-template/target/nexus-base-template-*.zip + ./target/nexus-base-template-*/bin/nexus console + +The `nexus-base-template` assembly is used as the basis for the official Sonatype Nexus distributions. + +## Getting help + +Looking to contribute to our code but need some help? There's a few ways to get information or our attention: + +* File an issue in [our public JIRA](https://issues.sonatype.org/browse/NEXUS) +* Check out the [Nexus3](http://stackoverflow.com/questions/tagged/nexus3) tag on Stack Overflow +* Check out the [Nexus Repository User List](https://groups.google.com/a/glists.sonatype.com/forum/?hl=en#!forum/nexus-users) +* Connect with [@sonatypeDev](https://twitter.com/sonatypeDev) on Twitter diff --git a/thirdparty-bundles/assemblies/nexus-base-feature/pom.xml b/thirdparty-bundles/assemblies/nexus-base-feature/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..7c56fb62207edfa2605815a6a0107f64ccd2274b --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-feature/pom.xml @@ -0,0 +1,111 @@ + + + 4.0.0 + + + org.sonatype.nexus.assemblies + nexus-assemblies + 3.8.0-02 + + + nexus-base-feature + ${project.groupId}:${project.artifactId} + feature + + + + org.apache.karaf.shell + org.apache.karaf.shell.core + provided + + + + org.apache.karaf.shell + org.apache.karaf.shell.commands + provided + + + + org.sonatype.nexus + nexus-extender + + + + org.sonatype.nexus + nexus-plugin-api + + + + org.sonatype.nexus + nexus-base + + + + org.sonatype.nexus + nexus-extdirect + + + + org.sonatype.nexus + nexus-siesta + + + + org.sonatype.nexus + nexus-swagger + + + + org.sonatype.nexus + nexus-rapture + + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + mvn-coordinates + initialize + + execute + + + + + + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + + + + + diff --git a/thirdparty-bundles/assemblies/nexus-base-feature/src/main/feature/feature.xml b/thirdparty-bundles/assemblies/nexus-base-feature/src/main/feature/feature.xml new file mode 100644 index 0000000000000000000000000000000000000000..0d8220e5599b8ea6332b6007fabbeacd3fcd029a --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-feature/src/main/feature/feature.xml @@ -0,0 +1,46 @@ + + + + + wrap:${mvn:javax.el}$overwrite=merge&Fragment-Host=org.hibernate.validator + wrap:${mvn:validation-api}$overwrite=merge&Fragment-Host=org.hibernate.validator + wrap:${mvn:paranamer}$overwrite=merge&Fragment-Host=org.hibernate.validator + wrap:${mvn:httpcore}$Bundle-SymbolicName=httpcore + wrap:${mvn:httpclient}$Bundle-SymbolicName=httpclient&Fragment-Host=httpcore + wrap:${mvn:jaxb-core}$Bundle-SymbolicName=jaxb-core + wrap:${mvn:jaxb-impl}$Bundle-SymbolicName=jaxb-impl&Fragment-Host=jaxb-core + wrap:${mvn:resteasy-jaxrs}$Bundle-SymbolicName=resteasy-jaxrs&Fragment-Host=org.sonatype.nexus.siesta + wrap:${mvn:resteasy-atom-provider}$Bundle-SymbolicName=resteasy-atom-provider&Fragment-Host=org.sonatype.nexus.siesta + wrap:${mvn:resteasy-jackson2-provider}$Bundle-SymbolicName=resteasy-jackson2-provider&Fragment-Host=org.sonatype.nexus.siesta + wrap:${mvn:resteasy-jaxb-provider}$Bundle-SymbolicName=resteasy-jaxb-provider&Fragment-Host=org.sonatype.nexus.siesta + wrap:${mvn:resteasy-multipart-provider}$Bundle-SymbolicName=resteasy-multipart-provider&Fragment-Host=org.sonatype.nexus.siesta + wrap:${mvn:resteasy-validator-provider-11}$Bundle-SymbolicName=resteasy-validator-provider&Fragment-Host=org.sonatype.nexus.siesta + wrap:${mvn:resteasy-client}$Bundle-SymbolicName=resteasy-client&Fragment-Host=org.sonatype.nexus.siesta + + + wrap:${mvn:swagger-core}$overwrite=merge&Import-Package=com.google.common.*,* + wrap:${mvn:swagger-jaxrs}$overwrite=merge&Import-Package=com.google.common.*,* + + + ${mvn:nexus-extender} + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/pom.xml b/thirdparty-bundles/assemblies/nexus-base-template/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..a5f96a624b0a06bd9c07e0e09b3eab062ae3e284 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/pom.xml @@ -0,0 +1,394 @@ + + + + + 4.0.0 + + + org.sonatype.nexus.assemblies + nexus-assemblies + 3.8.0-02 + + + nexus-base-template + ${project.groupId}:${project.artifactId} + karaf-assembly + + + + + org.sonatype.nexus + nexus-main + provided + + + + + org.apache.felix + org.apache.felix.framework + provided + + + + + org.apache.karaf.features + framework + kar + + + + org.apache.karaf.features + standard + features + xml + + + + org.sonatype.nexus.assemblies + nexus-startup-feature + features + xml + + + + + org.sonatype.nexus.assemblies + nexus-boot-feature + features + xml + runtime + + + + org.sonatype.nexus.assemblies + nexus-base-feature + features + xml + runtime + + + + org.sonatype.nexus + nexus-oss-edition + features + xml + runtime + + + + org.sonatype.nexus.assemblies + nexus-core-feature + features + xml + runtime + + + + + org.webjars + swagger-ui + provided + + + + + com.orientechnologies + orientdb-community + zip + runtime + + + * + * + + + + + + + org.sonatype.nexus + nexus-orient-console + provided + + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + pax-url-settings + initialize + + execute + + + + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + configure-assembly + prepare-package + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${org.ops4j.pax.logging:pax-logging-api:jar} + + + + + + + + + + + + + + + ${line.separator} + + # SONATYPE: disable .cfg store on feature install + configCfgStore=false + + # SONATYPE: disable automatic updates and retries + serviceRequirements=disable + updateSnapshots=false + scheduleMaxRun=1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + + + false + false + + false + + 1.8 + + true + + wrap + bundle + config + diagnostic + feature + jaas + log + package + shell + service + system + nexus-startup-feature + + + nexus-boot-feature + + + management + ssh + nexus-core-feature + nexus-oss-edition + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + bundle + package + + single + + + false + + ${project.basedir}/src/main/assembly/bundle.xml + + + + + + + + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/assembly/bundle.xml b/thirdparty-bundles/assemblies/nexus-base-template/src/main/assembly/bundle.xml new file mode 100644 index 0000000000000000000000000000000000000000..1e1c4b9c9dfb609e9b002650049c1f189ad04e6e --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/assembly/bundle.xml @@ -0,0 +1,73 @@ + + + + + bundle + + + zip + + + false + + + + ${project.build.directory}/assembly + ${project.build.finalName} + 0644 + 0755 + + bin/* + data/** + + + + + ${project.build.directory}/assembly + ${project.build.finalName} + 0644 + 0755 + dos + + bin/*.bat + + + + + ${project.build.directory}/assembly + ${project.build.finalName} + 0755 + 0755 + unix + + bin/* + + + bin/*.bat + + + + + ${project.build.directory}/sonatype-work + sonatype-work + 0644 + 0755 + + + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/LICENSE.txt b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..d6b3020fc7e5fcc7971e2df653af622de77ca06d --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/LICENSE.txt @@ -0,0 +1,87 @@ +Eclipse Public License -v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and + +b) in the case of each subsequent Contributor: + +i) changes to the Program, and + +ii) additions to the Program; + +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. + +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. + +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. + +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and + +b) its license agreement: + +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; + +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; + +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and + +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and + +b) a copy of this Agreement must be included with each copy of the Program. + +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/NOTICE.txt b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/NOTICE.txt new file mode 100644 index 0000000000000000000000000000000000000000..d88aaa17c4c41dd8fbe45e4a54c362eec98e2506 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/NOTICE.txt @@ -0,0 +1,10 @@ +Sonatype Nexus (TM) Open Source Version. +Copyright (c) 2008-present Sonatype, Inc. All rights reserved. +All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions + +This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, +which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + +Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks +of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the +Eclipse Foundation. All other trademarks are the property of their respective owners. diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus new file mode 100644 index 0000000000000000000000000000000000000000..c7790b460112b9890d3640018b77e8792a57662a --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus @@ -0,0 +1,436 @@ +#!/bin/sh +DIRNAME=`dirname "$0"` +PROGNAME=`basename "$0"` + +# +# Sourcing environment settings for karaf similar to tomcats setenv +# +KARAF_SCRIPT="karaf" +export KARAF_SCRIPT +if [ -f "$DIRNAME/setenv" ]; then + . "$DIRNAME/setenv" +fi + +# +# Set up some easily accessible MIN/MAX params for JVM mem usage +# +if [ "x$JAVA_MIN_MEM" = "x" ]; then + JAVA_MIN_MEM=1200M + export JAVA_MIN_MEM +fi +if [ "x$JAVA_MAX_MEM" = "x" ]; then + JAVA_MAX_MEM=1200M + export JAVA_MAX_MEM +fi +if [ "x$DIRECT_MAX_MEM" = "x" ]; then + DIRECT_MAX_MEM=2G + export DIRECT_MAX_MEM +fi + +# +# Check the mode that initiated the script +# +if [ "x$1" != "x" ]; then + MODE=$1 +fi + +warn() { + echo "${PROGNAME}: $*" +} + +die() { + warn "$*" + exit 1 +} + +detectOS() { + # OS specific support (must be 'true' or 'false'). + cygwin=false; + darwin=false; + aix=false; + os400=false; + case "`uname`" in + CYGWIN*) + cygwin=true + ;; + Darwin*) + darwin=true + ;; + AIX*) + aix=true + ;; + OS400*) + os400=true + ;; + esac + # For AIX, set an environment variable + if $aix; then + export LDR_CNTRL=MAXDATA=0xB0000000@DSA + echo $LDR_CNTRL + fi +} + +unlimitFD() { + # Use the maximum available, or set MAX_FD != -1 to use that + if [ "x$MAX_FD" = "x" ]; then + MAX_FD="maximum" + fi + + # Increase the maximum file descriptors if we can + if [ "$os400" = "false" ] && [ "$cygwin" = "false" ]; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ]; then + # use the system max + MAX_FD="$MAX_FD_LIMIT" + else + warn "Could not query system maximum file descriptor limit: $MAX_FD_LIMIT" + fi + fi + if [ "$MAX_FD" != 'unlimited' ]; then + ulimit -n $MAX_FD > /dev/null + if [ $? -ne 0 ]; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + fi + fi +} + +locateHome() { + if [ "x$KARAF_HOME" = "x" ]; then + # In POSIX shells, CDPATH may cause cd to write to stdout + (unset CDPATH) >/dev/null 2>&1 && unset CDPATH + # KARAF_HOME is not provided, fall back to default + KARAF_HOME=`cd "$DIRNAME/.."; pwd` + fi + + if [ ! -d "$KARAF_HOME" ]; then + die "KARAF_HOME is not valid: $KARAF_HOME" + fi +} + +locateBase() { + if [ "x$KARAF_BASE" != "x" ]; then + if [ ! -d "$KARAF_BASE" ]; then + die "KARAF_BASE is not valid: $KARAF_BASE" + fi + else + KARAF_BASE=$KARAF_HOME + fi +} + +locateData() { + if [ "x$KARAF_DATA" != "x" ]; then + if [ ! -d "$KARAF_DATA" ]; then + die "KARAF_DATA is not valid: $KARAF_DATA" + fi + else + KARAF_DATA=$KARAF_BASE/../sonatype-work/nexus3 + fi +} + +locateEtc() { + if [ "x$KARAF_ETC" != "x" ]; then + if [ ! -d "$KARAF_ETC" ]; then + die "KARAF_ETC is not valid: $KARAF_ETC" + fi + else + KARAF_ETC=$KARAF_BASE/etc/karaf + fi +} + +setupNativePath() { + # Support for loading native libraries + LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:$KARAF_BASE/lib:$KARAF_HOME/lib" + + # For Cygwin, set PATH from LD_LIBRARY_PATH + if $cygwin; then + LD_LIBRARY_PATH=`cygpath --path --windows "$LD_LIBRARY_PATH"` + PATH="$PATH;$LD_LIBRARY_PATH" + export PATH + fi + export LD_LIBRARY_PATH +} + +pathCanonical() { + local dst="${1}" + while [ -h "${dst}" ] ; do + ls=`ls -ld "${dst}"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + dst="$link" + else + dst="`dirname "${dst}"`/$link" + fi + done + local bas=`basename "${dst}"` + local dir=`dirname "${dst}"` + if [ "$bas" != "$dir" ]; then + dst="`pathCanonical "$dir"`/$bas" + fi + echo "${dst}" | sed -e 's#//#/#g' -e 's#/./#/#g' -e 's#/[^/]*/../#/#g' +} + +locateJava() { + # Setup the Java Virtual Machine + if $cygwin ; then + [ -n "$JAVA" ] && JAVA=`cygpath --unix "$JAVA"` + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + fi + + if [ "x$JAVA_HOME" = "x" ] && [ "$darwin" = "true" ]; then + JAVA_HOME="$(/usr/libexec/java_home -v 1.8)" + fi + if [ "x$JAVA" = "x" ] && [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi + if [ "x$JAVA" = "x" ]; then + if [ "x$JAVA_HOME" != "x" ]; then + if [ ! -d "$JAVA_HOME" ]; then + die "JAVA_HOME is not valid: $JAVA_HOME" + fi + JAVA="$JAVA_HOME/bin/java" + else + warn "JAVA_HOME not set; results may vary" + JAVA=`type java` + JAVA=`expr "$JAVA" : '.* \(/.*\)$'` + if [ "x$JAVA" = "x" ]; then + die "java command not found" + fi + fi + fi + if [ "x$JAVA_HOME" = "x" ]; then + JAVA_HOME="$(dirname $(dirname $(pathCanonical "$JAVA")))" + fi +} + +detectJVM() { + #echo "`$JAVA -version`" + # This service should call `java -version`, + # read stdout, and look for hints + if $JAVA -version 2>&1 | grep "^IBM" ; then + JVM_VENDOR="IBM" + # on OS/400, java -version does not contain IBM explicitly + elif $os400; then + JVM_VENDOR="IBM" + else + JVM_VENDOR="SUN" + fi + # echo "JVM vendor is $JVM_VENDOR" +} + +checkJvmVersion() { + # echo "`$JAVA -version`" + VERSION=`$JAVA -version 2>&1 | egrep '"([0-9].[0-9]\..*[0-9]).*"' | awk '{print substr($3,2,length($3)-2)}' | awk '{print substr($1, 3, 3)}' | sed -e 's;\.;;g'` + # echo $VERSION + if [ "$VERSION" -lt "60" ]; then + echo "JVM must be greater than 1.6" + exit 1; + fi +} + +setupDebugOptions() { + if [ "x$JAVA_OPTS" = "x" ]; then + JAVA_OPTS="$DEFAULT_JAVA_OPTS" + fi + export JAVA_OPTS + + if [ "x$EXTRA_JAVA_OPTS" != "x" ]; then + JAVA_OPTS="$JAVA_OPTS $EXTRA_JAVA_OPTS" + fi + + # Set Debug options if enabled + if [ "x$KARAF_DEBUG" != "x" ]; then + # Ignore DEBUG in case of stop, client, or status mode + if [ "x$MODE" = "xstop" ]; then + return + fi + if [ "x$MODE" = "xclient" ]; then + return + fi + if [ "x$MODE" = "xstatus" ]; then + return + fi + # Use the defaults if JAVA_DEBUG_OPTS was not set + if [ "x$JAVA_DEBUG_OPTS" = "x" ]; then + JAVA_DEBUG_OPTS="$DEFAULT_JAVA_DEBUG_OPTS" + fi + + JAVA_OPTS="$JAVA_DEBUG_OPTS $JAVA_OPTS" + warn "Enabling Java debug options: $JAVA_DEBUG_OPTS" + fi +} + +setupDefaults() { + # SONATYPE: preferred IPv4 by default + if [ "x$KARAF_OPTS" = "x" ]; then + KARAF_OPTS="-Djava.net.preferIPv4Stack=true" + fi + + DEFAULT_JAVA_OPTS="-Xms$JAVA_MIN_MEM -Xmx$JAVA_MAX_MEM -XX:MaxDirectMemorySize=$DIRECT_MAX_MEM -XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass -XX:+LogVMOutput -XX:LogFile=../sonatype-work/nexus3/log/jvm.log -XX:-OmitStackTraceInFastThrow " + + if [ "$darwin" = "true" ] && [ "$JVM_VENDOR" = "SUN" ] && [ `ulimit -n` -gt 10240 ]; then + # NEXUS-14184 - Open up OSX file descriptor limit + DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS -XX:-MaxFDLimit " + fi + + #Set the JVM_VENDOR specific JVM flags + if [ "$JVM_VENDOR" = "SUN" ]; then + # SONATYPE: removed -XX:PermSize and -XX:MaxPermSize + # SONATYPE: removed -Dcom.sun.management.jmxremote + DEFAULT_JAVA_OPTS="-server $DEFAULT_JAVA_OPTS" + elif [ "$JVM_VENDOR" = "IBM" ]; then + if $os400; then + DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS" + elif $aix; then + DEFAULT_JAVA_OPTS="-Xverify:none -Xdump:heap -Xlp $DEFAULT_JAVA_OPTS" + else + DEFAULT_JAVA_OPTS="-Xverify:none $DEFAULT_JAVA_OPTS" + fi + fi + + # Add the jars in the lib dir + for file in "$KARAF_HOME"/lib/boot/*.jar + do + if [ -z "$CLASSPATH" ]; then + CLASSPATH="$file" + else + CLASSPATH="$CLASSPATH:$file" + fi + done + + DEFAULT_JAVA_DEBUG_PORT="5005" + if [ "x$JAVA_DEBUG_PORT" = "x" ]; then + JAVA_DEBUG_PORT="$DEFAULT_JAVA_DEBUG_PORT" + fi + DEFAULT_JAVA_DEBUG_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$JAVA_DEBUG_PORT" + + ## + ## TODO: Move to conf/profiler/yourkit.{sh|cmd} + ## + # Uncomment to enable YourKit profiling + #DEFAULT_JAVA_DEBUG_OPTS="-Xrunyjpagent" +} + +init() { + # Determine if there is special OS handling we must perform + detectOS + + # Unlimit the number of file descriptors if possible + unlimitFD + + # Locate the Karaf home directory + locateHome + + # Locate the Karaf base directory + locateBase + + # Locate the Karaf data directory + locateData + + # Locate the Karaf etc directory + locateEtc + + # Setup the native library path + setupNativePath + + # Locate the Java VM to execute + locateJava + + # Determine the JVM vendor + detectJVM + + # Determine the JVM version >= 1.6 + checkJvmVersion + + # Setup default options + setupDefaults + + # Install debug options + setupDebugOptions + +} + +run() { + OPTS="-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=true" + MAIN=org.sonatype.nexus.karaf.NexusMain + while [ "$1" != "" ]; do + case $1 in + 'clean') + rm -Rf "$KARAF_DATA" + shift + ;; + 'debug') + if [ "x$JAVA_DEBUG_OPTS" = "x" ]; then + JAVA_DEBUG_OPTS="$DEFAULT_JAVA_DEBUG_OPTS" + fi + JAVA_OPTS="$JAVA_DEBUG_OPTS $JAVA_OPTS" + shift + ;; + 'status') + MAIN=org.apache.karaf.main.Status + shift + ;; + 'stop') + MAIN=org.apache.karaf.main.Stop + shift + ;; + 'run' | 'console') + shift + ;; + 'start') + NOHUP="true" + OPTS="-Dkaraf.startLocalConsole=false -Dkaraf.startRemoteShell=true" + shift + ;; + 'server') + OPTS="-Dkaraf.startLocalConsole=false -Dkaraf.startRemoteShell=true" + shift + ;; + 'client') + OPTS="-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=false" + shift + ;; + *) + break + ;; + esac + done + + # SONATYPE: commented + #JAVA_ENDORSED_DIRS="${JAVA_HOME}/jre/lib/endorsed:${JAVA_HOME}/lib/endorsed:${KARAF_HOME}/lib/endorsed" + #JAVA_EXT_DIRS="${JAVA_HOME}/jre/lib/ext:${JAVA_HOME}/lib/ext:${KARAF_HOME}/lib/ext" + if $cygwin; then + KARAF_HOME=`cygpath --path --windows "$KARAF_HOME"` + KARAF_BASE=`cygpath --path --windows "$KARAF_BASE"` + KARAF_DATA=`cygpath --path --windows "$KARAF_DATA"` + KARAF_ETC=`cygpath --path --windows "$KARAF_ETC"` + if [ ! -z "$CLASSPATH" ]; then + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + fi + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + # SONATYPE: commented + #JAVA_ENDORSED_DIRS=`cygpath --path --windows "$JAVA_ENDORSED_DIRS"` + #JAVA_EXT_DIRS=`cygpath --path --windows "$JAVA_EXT_DIRS"` + fi + cd "$KARAF_BASE" + + # SONATYPE: prepare log and tmp directories + mkdir -p "$KARAF_DATA/log" "$KARAF_DATA/tmp" + + # SONATYPE: removed -Djavax.management.builder.initial + # SONATYPE: removed -Djava.endorsed.dirs="${JAVA_ENDORSED_DIRS}" + # SONATYPE: removed -Djava.ext.dirs="${JAVA_EXT_DIRS}" + if [ "$NOHUP" = "true" ]; then + exec nohup "$JAVA" $JAVA_OPTS -Dkaraf.instances="${KARAF_DATA}/instances" -Dkaraf.home="$KARAF_HOME" -Dkaraf.base="$KARAF_BASE" -Dkaraf.data="$KARAF_DATA" -Dkaraf.etc="$KARAF_ETC" -Djava.io.tmpdir="$KARAF_DATA/tmp" -Djava.util.logging.config.file="$KARAF_ETC/java.util.logging.properties" $KARAF_OPTS $OPTS -classpath "$CLASSPATH" $MAIN "$@" > /dev/null 2>&1 & + else + exec "$JAVA" $JAVA_OPTS -Dkaraf.instances="${KARAF_DATA}/instances" -Dkaraf.home="$KARAF_HOME" -Dkaraf.base="$KARAF_BASE" -Dkaraf.data="$KARAF_DATA" -Dkaraf.etc="$KARAF_ETC" -Djava.io.tmpdir="$KARAF_DATA/tmp" -Djava.util.logging.config.file="$KARAF_ETC/java.util.logging.properties" $KARAF_OPTS $OPTS -classpath "$CLASSPATH" $MAIN "$@" + fi +} + +main() { + init + run "$@" +} + +main "$@" diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus.bat b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus.bat new file mode 100644 index 0000000000000000000000000000000000000000..877955e3a4400553d8ce580e05f4815e36c181c5 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus.bat @@ -0,0 +1,331 @@ +@echo off +if not "%ECHO%" == "" echo %ECHO% + +setlocal +set DIRNAME=%~dp0% +set PROGNAME=%~nx0% +set ARGS=%* + +rem Sourcing environment settings for karaf similar to tomcats setenv +SET KARAF_SCRIPT="karaf.bat" +if exist "%DIRNAME%setenv.bat" ( + call "%DIRNAME%setenv.bat" +) + +rem Check console window title. Set to Karaf by default +if not "%KARAF_TITLE%" == "" ( + title %KARAF_TITLE% +) else ( + title Karaf +) + +rem Check/Set up some easily accessible MIN/MAX params for JVM mem usage +if "%JAVA_MIN_MEM%" == "" ( + set JAVA_MIN_MEM=1200M +) +if "%JAVA_MAX_MEM%" == "" ( + set JAVA_MAX_MEM=1200M +) +if "%DIRECT_MAX_MEM%" == "" ( + set DIRECT_MAX_MEM=2G +) + +goto BEGIN + +:warn + echo %PROGNAME%: %* +goto :EOF + +:BEGIN + +rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +if not "%KARAF_HOME%" == "" ( + call :warn Ignoring predefined value for KARAF_HOME +) +set KARAF_HOME=%DIRNAME%.. +if not exist "%KARAF_HOME%" ( + call :warn KARAF_HOME is not valid: "%KARAF_HOME%" + goto END +) + +if not "%KARAF_BASE%" == "" ( + if not exist "%KARAF_BASE%" ( + call :warn KARAF_BASE is not valid: "%KARAF_BASE%" + goto END + ) +) +if "%KARAF_BASE%" == "" ( + set "KARAF_BASE=%KARAF_HOME%" +) + +if not "%KARAF_DATA%" == "" ( + if not exist "%KARAF_DATA%" ( + call :warn KARAF_DATA is not valid: "%KARAF_DATA%" + goto END + ) +) +if "%KARAF_DATA%" == "" ( + set "KARAF_DATA=%KARAF_BASE%\..\sonatype-work\nexus3" +) + +if not "%KARAF_ETC%" == "" ( + if not exist "%KARAF_ETC%" ( + call :warn KARAF_ETC is not valid: "%KARAF_ETC%" + goto END + ) +) +if "%KARAF_ETC%" == "" ( + set "KARAF_ETC=%KARAF_BASE%\etc\karaf" +) + +set LOCAL_CLASSPATH=%CLASSPATH% +set JAVA_MODE=-server + +set CLASSPATH=%LOCAL_CLASSPATH%;%KARAF_BASE%\conf +set DEFAULT_JAVA_DEBUG_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 + +if "%LOCAL_CLASSPATH%" == "" goto :KARAF_CLASSPATH_EMPTY + set CLASSPATH=%LOCAL_CLASSPATH%;%KARAF_BASE%\conf + goto :KARAF_CLASSPATH_END +:KARAF_CLASSPATH_EMPTY + set CLASSPATH=%KARAF_BASE%\conf +:KARAF_CLASSPATH_END + +rem Setup Karaf Home +if exist "%KARAF_HOME%\conf\karaf-rc.cmd" call %KARAF_HOME%\conf\karaf-rc.cmd +if exist "%HOME%\karaf-rc.cmd" call %HOME%\karaf-rc.cmd + +rem Support for loading native libraries +set PATH=%PATH%;%KARAF_BASE%\lib;%KARAF_HOME%\lib + +rem Setup the Java Virtual Machine +if not "%JAVA%" == "" goto :Check_JAVA_END + if not "%JAVA_HOME%" == "" goto :TryJDKEnd + call :warn JAVA_HOME not set; results may vary +:TryJRE + start /w regedit /e __reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment" + if not exist __reg1.txt goto :TryJDK + type __reg1.txt | find "CurrentVersion" > __reg2.txt + if errorlevel 1 goto :TryJDK + for /f "tokens=2 delims==" %%x in (__reg2.txt) do set JavaTemp=%%~x + if errorlevel 1 goto :TryJDK + set JavaTemp=%JavaTemp%## + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp:##=% + del __reg1.txt + del __reg2.txt + start /w regedit /e __reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\%JavaTemp%" + if not exist __reg1.txt goto :TryJDK + type __reg1.txt | find "JavaHome" > __reg2.txt + if errorlevel 1 goto :TryJDK + for /f "tokens=2 delims==" %%x in (__reg2.txt) do set JAVA_HOME=%%~x + if errorlevel 1 goto :TryJDK + del __reg1.txt + del __reg2.txt + goto TryJDKEnd +:TryJDK + start /w regedit /e __reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit" + if not exist __reg1.txt ( + goto TryRegJRE + ) + type __reg1.txt | find "CurrentVersion" > __reg2.txt + if errorlevel 1 ( + goto TryRegJRE + ) + for /f "tokens=2 delims==" %%x in (__reg2.txt) do set JavaTemp=%%~x + if errorlevel 1 ( + goto TryRegJRE + ) + set JavaTemp=%JavaTemp%## + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp:##=% + del __reg1.txt + del __reg2.txt + start /w regedit /e __reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\%JavaTemp%" + if not exist __reg1.txt ( + goto TryRegJRE + ) + type __reg1.txt | find "JavaHome" > __reg2.txt + if errorlevel 1 ( + goto TryRegJRE + ) + for /f "tokens=2 delims==" %%x in (__reg2.txt) do set JAVA_HOME=%%~x + if errorlevel 1 ( + goto TryRegJRE + ) + del __reg1.txt + del __reg2.txt +:TryRegJRE + rem try getting the JAVA_HOME from registry + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Runtime Environment" /v CurrentVersion`) DO ( + set JAVA_VERSION=%%A + ) + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Runtime Environment\%JAVA_VERSION%" /v JavaHome`) DO ( + set JAVA_HOME=%%A %%B + ) + if not exist "%JAVA_HOME%" ( + goto TryRegJDK + ) + goto TryJDKEnd +:TryRegJDK + rem try getting the JAVA_HOME from registry + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Development Kit" /v CurrentVersion`) DO ( + set JAVA_VERSION=%%A + ) + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Development Kit\%JAVA_VERSION%" /v JavaHome`) DO ( + set JAVA_HOME=%%A %%B + ) + if not exist "%JAVA_HOME%" ( + call :warn Unable to retrieve JAVA_HOME from Registry + ) + goto TryJDKEnd +:TryJDKEnd + if not exist "%JAVA_HOME%" ( + call :warn JAVA_HOME is not valid: "%JAVA_HOME%" + goto END + ) + set JAVA=%JAVA_HOME%\bin\java +:Check_JAVA_END + +if not exist "%JAVA_HOME%\bin\server\jvm.dll" ( + if not exist "%JAVA_HOME%\jre\bin\server\jvm.dll" ( + echo WARNING: Running Karaf on a Java HotSpot Client VM because server-mode is not available. + echo Install Java Developer Kit to fix this. + echo For more details see http://java.sun.com/products/hotspot/whitepaper.html#client + set JAVA_MODE=-client + ) +) + +rem SONATYPE: preferred IPv4 by default +if "%KARAF_OPTS%" == "" set KARAF_OPTS=-Djava.net.preferIPv4Stack=true + +rem SONATYPE: removed -Dcom.sun.management.jmxremote and unused derby properties +set DEFAULT_JAVA_OPTS=%JAVA_MODE% -Xms%JAVA_MIN_MEM% -Xmx%JAVA_MAX_MEM% -XX:MaxDirectMemorySize=%DIRECT_MAX_MEM% -XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass -XX:+LogVMOutput -XX:LogFile=..\sonatype-work\nexus3\log\jvm.log -XX:-OmitStackTraceInFastThrow + +rem SONATYPE: removed -XX:PermSize and -XX:MaxPermSize + +if "%JAVA_OPTS%" == "" set JAVA_OPTS=%DEFAULT_JAVA_OPTS% + +if "%EXTRA_JAVA_OPTS%" == "" goto :KARAF_EXTRA_JAVA_OPTS_END + set JAVA_OPTS=%JAVA_OPTS% %EXTRA_JAVA_OPTS% +:KARAF_EXTRA_JAVA_OPTS_END + +if "%KARAF_DEBUG%" == "" goto :KARAF_DEBUG_END + if "%1" == "stop" goto :KARAF_DEBUG_END + if "%1" == "client" goto :KARAF_DEBUG_END + if "%1" == "status" goto :KARAF_DEBUG_END + rem Use the defaults if JAVA_DEBUG_OPTS was not set + if "%JAVA_DEBUG_OPTS%" == "" set JAVA_DEBUG_OPTS=%DEFAULT_JAVA_DEBUG_OPTS% + + set JAVA_OPTS=%JAVA_DEBUG_OPTS% %JAVA_OPTS% + call :warn Enabling Java debug options: %JAVA_DEBUG_OPTS% +:KARAF_DEBUG_END + +if "%KARAF_PROFILER%" == "" goto :KARAF_PROFILER_END + set KARAF_PROFILER_SCRIPT=%KARAF_HOME%\conf\profiler\%KARAF_PROFILER%.cmd + + if exist "%KARAF_PROFILER_SCRIPT%" goto :KARAF_PROFILER_END + call :warn Missing configuration for profiler '%KARAF_PROFILER%': %KARAF_PROFILER_SCRIPT% + goto END +:KARAF_PROFILER_END + +rem Setup the classpath +pushd "%KARAF_HOME%\lib\boot" +for %%G in (*.jar) do call:APPEND_TO_CLASSPATH %%G +popd +goto CLASSPATH_END + +: APPEND_TO_CLASSPATH +set filename=%~1 +set suffix=%filename:~-4% +if %suffix% equ .jar set CLASSPATH=%CLASSPATH%;%KARAF_HOME%\lib\boot\%filename% +goto :EOF + +:CLASSPATH_END + +rem Execute the JVM or the load the profiler +if "%KARAF_PROFILER%" == "" goto :RUN + rem Execute the profiler if it has been configured + call :warn Loading profiler script: %KARAF_PROFILER_SCRIPT% + call %KARAF_PROFILER_SCRIPT% + +:RUN + SET OPTS=-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=true + SET MAIN=org.sonatype.nexus.karaf.NexusMain + SET SHIFT=false + +:RUN_LOOP + if "%1" == "stop" goto :EXECUTE_STOP + if "%1" == "status" goto :EXECUTE_STATUS + if "%1" == "run" goto :EXECUTE_CONSOLE + if "%1" == "console" goto :EXECUTE_CONSOLE + if "%1" == "start" goto :EXECUTE_SERVER + if "%1" == "server" goto :EXECUTE_SERVER + if "%1" == "client" goto :EXECUTE_CLIENT + if "%1" == "clean" goto :EXECUTE_CLEAN + if "%1" == "debug" goto :EXECUTE_DEBUG + goto :EXECUTE + +:EXECUTE_STOP + SET MAIN=org.apache.karaf.main.Stop + shift + goto :RUN_LOOP + +:EXECUTE_STATUS + SET MAIN=org.apache.karaf.main.Status + shift + goto :RUN_LOOP + +:EXECUTE_CONSOLE + shift + goto :RUN_LOOP + +:EXECUTE_SERVER + SET OPTS=-Dkaraf.startLocalConsole=false -Dkaraf.startRemoteShell=true + shift + goto :RUN_LOOP + +:EXECUTE_CLIENT + SET OPTS=-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=false + shift + goto :RUN_LOOP + +:EXECUTE_CLEAN + rmdir /S /Q "%KARAF_DATA%" + shift + goto :RUN_LOOP + +:EXECUTE_DEBUG + if "%JAVA_DEBUG_OPTS%" == "" set JAVA_DEBUG_OPTS=%DEFAULT_JAVA_DEBUG_OPTS% + set JAVA_OPTS=%JAVA_DEBUG_OPTS% %JAVA_OPTS% + shift + goto :RUN_LOOP + +:EXECUTE + SET ARGS=%1 %2 %3 %4 %5 %6 %7 %8 + rem Execute the Java Virtual Machine + cd "%KARAF_BASE%" + rem SONATYPE: removed -Djavax.management.builder.initial + rem SONATYPE: removed -Djava.endorsed.dirs="%JAVA_HOME%\jre\lib\endorsed;%JAVA_HOME%\lib\endorsed;%KARAF_HOME%\lib\endorsed" + rem SONATYPE: removed -Djava.ext.dirs="%JAVA_HOME%\jre\lib\ext;%JAVA_HOME%\lib\ext;%KARAF_HOME%\lib\ext" + "%JAVA%" %JAVA_OPTS% %OPTS% -classpath "%CLASSPATH%" -Dkaraf.instances="%KARAF_DATA%\instances" -Dkaraf.home="%KARAF_HOME%" -Dkaraf.base="%KARAF_BASE%" -Dkaraf.etc="%KARAF_ETC%" -Djava.io.tmpdir="%KARAF_DATA%\tmp" -Dkaraf.data="%KARAF_DATA%" -Djava.util.logging.config.file="%KARAF_ETC%\java.util.logging.properties" %KARAF_OPTS% %MAIN% %ARGS% + +rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +:END + +endlocal + +if not "%PAUSE%" == "" pause + +:END_NO_PAUSE + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv new file mode 100644 index 0000000000000000000000000000000000000000..912804f5fd448bf9f35d8b28765e6f0a0f9c4f5c --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv @@ -0,0 +1,31 @@ +#!/bin/sh +# handle specific scripts; the SCRIPT_NAME is exactly the name of the Karaf +# script; for example karaf, start, stop, admin, client, ... +# +# if [ "$KARAF_SCRIPT" = "SCRIPT_NAME" ]; then +# Actions go here... +# fi + +# +# general settings which should be applied for all scripts go here; please keep +# in mind that it is possible that scripts might be executed more than once, e.g. +# in example of the start script where the start script is executed first and the +# karaf script afterwards. +# + +# +# The following section shows the possible configuration options for the default +# karaf scripts +# +# JAVA_HOME # Location of Java installation +# JAVA_MIN_MEM # Minimum Java heap memory for the JVM +# JAVA_MAX_MEM # Maximum Java heap memory for the JVM +# DIRECT_MAX_MEM # Maximum direct buffer memory for the JVM +# EXTRA_JAVA_OPTS # Additional JVM options +# KARAF_HOME # Karaf home folder +# KARAF_DATA # Karaf data folder +# KARAF_BASE # Karaf base folder +# KARAF_ETC # Karaf etc folder +# KARAF_OPTS # Additional available Karaf options +# KARAF_DEBUG # Enable debug mode +# KARAF_REDIRECT # Enable/set the std/err redirection when using bin/start diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv.bat b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv.bat new file mode 100644 index 0000000000000000000000000000000000000000..8da0cab48f83f3f1675cca0202c3717e4e3daff0 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv.bat @@ -0,0 +1,44 @@ +@echo off +rem +rem handle specific scripts; the SCRIPT_NAME is exactly the name of the Karaf +rem script; for example karaf.bat, start.bat, stop.bat, admin.bat, client.bat, ... +rem +rem if "%KARAF_SCRIPT%" == "SCRIPT_NAME" ( +rem Actions go here... +rem ) + +rem +rem general settings which should be applied for all scripts go here; please keep +rem in mind that it is possible that scripts might be executed more than once, e.g. +rem in example of the start script where the start script is executed first and the +rem karaf script afterwards. +rem + +rem +rem The following section shows the possible configuration options for the default +rem karaf scripts +rem +rem Window name of the windows console +rem SET KARAF_TITLE +rem Location of Java installation +rem SET JAVA_HOME +rem Minimum Java heap memory for the JVM +rem SET JAVA_MIN_MEM +rem Maximum Java heap memory for the JVM +rem SET JAVA_MAX_MEM +rem Maximum direct buffer memory for the JVM +rem SET DIRECT_MAX_MEM +rem Additional JVM options +rem SET EXTRA_JAVA_OPTS +rem Karaf home folder +rem SET KARAF_HOME +rem Karaf data folder +rem SET KARAF_DATA +rem Karaf base folder +rem SET KARAF_BASE +rem Karaf etc folder +rem SET KARAF_ETC +rem Additional available Karaf options +rem SET KARAF_OPTS +rem Enable debug mode +rem SET KARAF_DEBUG diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/deploy/.placeholder b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/deploy/.placeholder new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/ehcache.xml b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/ehcache.xml new file mode 100644 index 0000000000000000000000000000000000000000..8e9206d4dc0d65ecf7979e17eba06cc23b7e314a --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/ehcache.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + 10000 + + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/elasticsearch.yml b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/elasticsearch.yml new file mode 100644 index 0000000000000000000000000000000000000000..de6fd25e1b3b1e69520ab6d9726e9fc228ee29aa --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/elasticsearch.yml @@ -0,0 +1,114 @@ +cluster.name: nexus + +path: + home: ${karaf.base} + conf: ${fabric.etc} + data: ${karaf.data}/elasticsearch + logs: ${karaf.data}/log + work: ${java.io.tmpdir}/elasticsearch + +node.data: true +node.master: true + +# Disable groovy script integration +script.groovy.sandbox.enabled: false + +# Disable networking +node.local: true +http.enabled: false +discovery.zen.ping.multicast.enabled: false + +# ======================== Elasticsearch Configuration ========================= +# +# NOTE: Elasticsearch comes with reasonable defaults for most settings. +# Before you set out to tweak and tune the configuration, make sure you +# understand what are you trying to accomplish and the consequences. +# +# The primary way of configuring a node is via this file. This template lists +# the most important settings you may want to configure for a production cluster. +# +# Please see the documentation for further information on configuration options: +# +# +# ---------------------------------- Cluster ----------------------------------- +# +# Use a descriptive name for your cluster: +# +# cluster.name: my-application +# +# ------------------------------------ Node ------------------------------------ +# +# Use a descriptive name for the node: +# +# node.name: node-1 +# +# Add custom attributes to the node: +# +# node.rack: r1 +# +# ----------------------------------- Paths ------------------------------------ +# +# Path to directory where to store the data (separate multiple locations by comma): +# +# path.data: /path/to/data +# +# Path to log files: +# +# path.logs: /path/to/logs +# +# ----------------------------------- Memory ----------------------------------- +# +# Lock the memory on startup: +# +# bootstrap.memory_lock: true +# +# Make sure that the `ES_HEAP_SIZE` environment variable is set to about half the memory +# available on the system and that the owner of the process is allowed to use this limit. +# +# Elasticsearch performs poorly when the system is swapping the memory. +# +# ---------------------------------- Network ----------------------------------- +# +# Set the bind address to a specific IP (IPv4 or IPv6): +# +# network.host: 192.168.0.1 +# +# Set a custom port for HTTP: +# +# http.port: 9200 +# +# For more information, see the documentation at: +# +# +# --------------------------------- Discovery ---------------------------------- +# +# Pass an initial list of hosts to perform discovery when new node is started: +# The default list of hosts is ["127.0.0.1", "[::1]"] +# +# discovery.zen.ping.unicast.hosts: ["host1", "host2"] +# +# Prevent the "split brain" by configuring the majority of nodes (total number of nodes / 2 + 1): +# +# discovery.zen.minimum_master_nodes: 3 +# +# For more information, see the documentation at: +# +# +# ---------------------------------- Gateway ----------------------------------- +# +# Block initial recovery after a full cluster restart until N nodes are started: +# +# gateway.recover_after_nodes: 3 +# +# For more information, see the documentation at: +# +# +# ---------------------------------- Various ----------------------------------- +# +# Disable starting multiple nodes on a single system: +# +# node.max_local_storage_nodes: 1 +# +# Require explicit names when deleting indices: +# +# action.destructive_requires_name: true diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/orientdb-security.json b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/orientdb-security.json new file mode 100644 index 0000000000000000000000000000000000000000..010732bf7f962445769e70da5df8413d608f3b81 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/orientdb-security.json @@ -0,0 +1,3 @@ +{ + "enabled": false +} diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-http-redirect-to-https.xml b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-http-redirect-to-https.xml new file mode 100644 index 0000000000000000000000000000000000000000..0c1ab9e3fdd0f8edee00578a282b44edc786e8e0 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-http-redirect-to-https.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + /* + + + + 2 + + + + + + + + + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-http.xml b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-http.xml new file mode 100644 index 0000000000000000000000000000000000000000..ae159057c6438223f5962fd388a1bbc08195f8e3 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-http.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-https.xml b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-https.xml new file mode 100644 index 0000000000000000000000000000000000000000..fa01fe9c26be4793f0e629269680083199d10714 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-https.xml @@ -0,0 +1,81 @@ + + + + + + + + https + + + + + + + + + + + + /keystore.jks + password + password + /keystore.jks + password + + + + + + SSL_RSA_WITH_DES_CBC_SHA + SSL_DHE_RSA_WITH_DES_CBC_SHA + SSL_DHE_DSS_WITH_DES_CBC_SHA + SSL_RSA_EXPORT_WITH_RC4_40_MD5 + SSL_RSA_EXPORT_WITH_DES40_CBC_SHA + SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA + SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA + + + + + + + + + + + + + + + + + http/1.1 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-requestlog.xml b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-requestlog.xml new file mode 100644 index 0000000000000000000000000000000000000000..46201535029cec545bea76b6fba88798a5c4be5a --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-requestlog.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + /logback-access.xml + true + + + + + + + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty.xml b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty.xml new file mode 100644 index 0000000000000000000000000000000000000000..e51b7f3aa5cf4622b70ffa420ce2b73ed20c68e8 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + /nexus-web.xml + /public + + true + + + org.eclipse.jetty.webapp.WebXmlConfiguration + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + 512 + + + + + + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/nexus-web.xml b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/nexus-web.xml new file mode 100644 index 0000000000000000000000000000000000000000..6d5a48980471d3685e9a9a999c5dc90fb7bb76e3 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/nexus-web.xml @@ -0,0 +1,32 @@ + + + + Sonatype Nexus + + + org.sonatype.nexus.bootstrap.osgi.BootstrapListener + + + + nexusFilter + org.sonatype.nexus.bootstrap.osgi.DelegatingFilter + + + + nexusFilter + /* + REQUEST + ERROR + + + + index.html + + + + /error.html + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/config.properties b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/config.properties new file mode 100644 index 0000000000000000000000000000000000000000..3a36a934d9f15936975a559da7d3ca09a7e427f5 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/config.properties @@ -0,0 +1,215 @@ +# +# This file lists Karaf default settings for this particular version of Karaf. +# For easier maintenance when upgrading Karaf and to better document which +# default values have changed, it is recommended to place any changes to +# these values in a custom.properties file in the same folder as this file. +# Each value specified in custom.properties will override the default value +# here. +# + +# +# Properties file inclusions (as a space separated list of relative paths) +# Included files will override the values specified in this file +# NB: ${includes} properties files are mandatory, it means that Karaf will not start +# if the include file is not found +# +${includes} = jre.properties custom.properties + +# +# Properties file inclusions (as a space separated list of relative paths) +# Included files will override the values specified in this file +# NB: ${optionals} properties files are optionals, it means that Karaf will just +# display a warning message but the bootstrap will be performed +# +# ${optionals} = my.properties + +# +# Framework selection properties +# +karaf.framework=felix + +# +# Location of the OSGi frameworks +# +karaf.framework.equinox=mvn\:org.eclipse.birt.runtime/org.eclipse.osgi/3.10.2.v20150203-1939 +karaf.framework.felix=mvn\:org.apache.felix/org.apache.felix.framework/5.6.2 + +# +# Framework config properties. +# +org.osgi.framework.system.packages= \ + org.osgi.dto;version="1.0",\ + org.osgi.resource;version="1.0",\ + org.osgi.resource.dto;version="1.0";uses:="org.osgi.dto",\ + org.osgi.framework;version="1.8",\ + org.osgi.framework.dto;version="1.8";uses:="org.osgi.dto",\ + org.osgi.framework.hooks.bundle;version="1.1";uses:="org.osgi.framework",\ + org.osgi.framework.hooks.resolver;version="1.0";uses:="org.osgi.framework.wiring",\ + org.osgi.framework.hooks.service;version="1.1";uses:="org.osgi.framework",\ + org.osgi.framework.hooks.weaving;version="1.1";uses:="org.osgi.framework.wiring",\ + org.osgi.framework.launch;version="1.2";uses:="org.osgi.framework",\ + org.osgi.framework.namespace;version="1.1";uses:="org.osgi.resource",\ + org.osgi.framework.startlevel;version="1.0";uses:="org.osgi.framework",\ + org.osgi.framework.startlevel.dto;version="1.0";uses:="org.osgi.dto",\ + org.osgi.framework.wiring;version="1.2";uses:="org.osgi.framework,org.osgi.resource",\ + org.osgi.framework.wiring.dto;version="1.2";uses:="org.osgi.dto,org.osgi.resource.dto",\ + org.osgi.service.condpermadmin;version="1.1.1";uses:="org.osgi.framework,org.osgi.service.permissionadmin",\ + org.osgi.service.packageadmin;version="1.2";uses:="org.osgi.framework",org.osgi.service.permissionadmin;version="1.2",\ + org.osgi.service.resolver;version="1.0";uses:="org.osgi.resource",\ + org.osgi.service.startlevel;version="1.1";uses:="org.osgi.framework",\ + org.osgi.service.url;version="1.0",\ + org.osgi.util.tracker;version="1.5.1";uses:="org.osgi.framework",\ + org.apache.karaf.version;version="4.0.9",\ + org.apache.karaf.diagnostic.core.common;uses:=org.apache.karaf.diagnostic.core;version="4.0.9",\ + org.apache.karaf.diagnostic.core;uses:=org.osgi.framework;version="4.0.9",\ + org.apache.karaf.jaas.boot.principal;uses:=javax.security.auth;version="4.0.9",\ + org.apache.karaf.jaas.boot;uses:="javax.security.auth,javax.security.auth.callback,javax.security.auth.login,javax.security.auth.spi,org.osgi.framework";version="4.0.9",\ + ${jre-${java.specification.version}} + +# +# Extra packages appended after standard packages +# +org.osgi.framework.system.packages.extra= \ + sun.security.ssl, \ + com.sun.net.httpserver, \ + javax.annotation;version=1.2, \ + javax.annotation.meta;version=1.2, \ + javax.annotation.security;version=1.2, \ + org.bouncycastle.asn1;version=1.56, \ + org.bouncycastle.asn1.x500;version=1.56, \ + org.bouncycastle.asn1.x509;version=1.56, \ + org.bouncycastle.cert;version=1.56, \ + org.bouncycastle.cert.jcajce;version=1.56, \ + org.bouncycastle.crypto.prng;version=1.56, \ + org.bouncycastle.jce.provider;version=1.56, \ + org.bouncycastle.openssl;version=1.56, \ + org.bouncycastle.openssl.jcajce;version=1.56, \ + org.bouncycastle.openpgp;version=1.56, \ + org.bouncycastle.openpgp.operator;version=1.56, \ + org.bouncycastle.operator;version=1.56, \ + org.bouncycastle.operator.jcajce;version=1.56, \ + org.bouncycastle.util.encoders;version=1.56, \ + org.bouncycastle.x509;version=1.56, \ + org.xerial.snappy, \ + org.apache.karaf.branding, \ + sun.misc, \ + sun.nio.ch + +org.osgi.framework.system.capabilities= \ + ${eecap-${java.specification.version}}, \ + osgi.service;effective:=active;objectClass=org.osgi.service.packageadmin.PackageAdmin, \ + osgi.service;effective:=active;objectClass=org.osgi.service.resolver.Resolver, \ + osgi.service;effective:=active;objectClass=org.osgi.service.startlevel.StartLevel, \ + osgi.service;effective:=active;objectClass=org.osgi.service.url.URLHandlers + +eecap-1.8= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1,1.2", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8" +eecap-1.7= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1,1.2", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7" +eecap-1.6= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1,1.2", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3,1.4,1.5,1.6" +eecap-1.5= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1,1.2", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3,1.4,1.5" +eecap-1.4= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1,1.2", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3,1.4" +eecap-1.3= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3" +eecap-1.2= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2" + +# +# javax.transaction is needed to avoid class loader constraint violation when using javax.sql +# +org.osgi.framework.bootdelegation=\ + com.sun.*, \ + javax.transaction, \ + javax.transaction.*, \ + javax.xml.crypto, \ + javax.xml.crypto.*, \ + sun.* + +# jVisualVM support +# in order to use Karaf with jvisualvm, the org.osgi.framework.bootdelegation property has to contain the org.netbeans.lib.profiler.server package +# and, so, it should look like: +# +# org.osgi.framework.bootdelegation=org.apache.karaf.jaas.boot,org.apache.karaf.jaas.boot.principal,sun.*,com.sun.*,javax.transaction,javax.transaction.*,javax.xml.crypto,javax.xml.crypto.*,org.netbeans.lib.profiler.server +# +# YourKit support +# in order to use Karaf with YourKit, the org.osgi.framework.bootdelegation property has to contain the com.yourkit.* packages +# and, so, it should look like: +# +# org.osgi.framework.bootdelegation=org.apache.karaf.jaas.boot,org.apache.karaf.jaas.boot.principal,sun.*,com.sun.*,javax.transaction,javax.transaction.*,javax.xml.crypto,javax.xml.crypto.*,com.yourkit.* +# + +# +# OSGi Execution Environment +# +org.osgi.framework.executionenvironment=J2SE-1.8,JavaSE-1.8,J2SE-1.7,JavaSE-1.7,J2SE-1.6,JavaSE-1.6,J2SE-1.5,JavaSE-1.5,J2SE-1.4,JavaSE-1.4,J2SE-1.3,JavaSE-1.3,J2SE-1.2,,JavaSE-1.2,CDC-1.1/Foundation-1.1,CDC-1.0/Foundation-1.0,J2ME,OSGi/Minimum-1.1,OSGi/Minimum-1.0 + +# +# Set the parent classloader for the bundle to the classloader that loads the Framework (i.e. everything in lib/*.jar) +# +org.osgi.framework.bundle.parent=framework + +# +# Definition of the default bundle start level +# +org.osgi.framework.startlevel.beginning=100 +karaf.startlevel.bundle=80 + +# +# The location of the Karaf shutdown port file +# +karaf.shutdown.port.file=${karaf.data}/port + +# +# Configuration FileMonitor properties +# +felix.fileinstall.enableConfigSave = true +felix.fileinstall.dir = ${karaf.etc} +felix.fileinstall.filter = .*\\.cfg +felix.fileinstall.poll = 1000 +felix.fileinstall.noInitialDelay = true +felix.fileinstall.log.level = 3 +felix.fileinstall.log.default = jul + +# Use cached urls for bundle CodeSource to avoid +# problems with JCE cached informations, see KARAF-3974 +felix.bundlecodesource.usecachedurls = true + +# +# Delay for writing the framework state to disk in equinox +# must be >= 1000 and <= 1800000 +# +eclipse.stateSaveDelayInterval = 1000 + +# +# OBR Repository list +# This property will be modified by the obr:addUrl and obr:removeUrl commands. +# +obr.repository.url = + +# +# Start blueprint bundles synchronously when possible +# +org.apache.aries.blueprint.synchronous=true + +# +# Do not weave all any classes by default +# +org.apache.aries.proxy.weaving.enabled= + +# +# mvn url handler requires config instance configuration +# +#org.ops4j.pax.url.mvn.requireConfigAdminConfig=true + +# +# Don't delay the console startup. Set to true if you want the console to start after all other bundles +# +karaf.delay.console=false + +# +# shutdown command, change if needed +# +karaf.shutdown.command=faRcsbjiOtDfNuIR49fQJVav6D191MXIUTSWeh0rOnRNlRF8R1eVDgMASDTMkJgR diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/custom.properties b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/custom.properties new file mode 100644 index 0000000000000000000000000000000000000000..c9049af2f11161dacfc1ad722d410e4427acae3f --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/custom.properties @@ -0,0 +1,22 @@ +# +# All the values specified here will override the default values given +# in config.properties. +# + +karaf.systemBundlesStartLevel=50 + +# +# You can place any customized configuration here. +# + +# A very small amount of logging before pax-logging is enabled ends up here +karaf.bootstrap.log=${karaf.data}/log/karaf.log + +# early load of mvn: protocol config +${includes}=org.ops4j.pax.url.mvn.cfg + +karaf.lock.class=org.sonatype.nexus.karaf.NexusFileLock + +# Temporary workaround for https://issues.apache.org/jira/browse/FELIX-5184 +felix.native.osname.alias.windowsserver2012=windows server 2012,win32 + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/java.util.logging.properties b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/java.util.logging.properties new file mode 100644 index 0000000000000000000000000000000000000000..c8d5d5f4b9ab0a98c9e31c853a18dcc1a9eaba62 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/java.util.logging.properties @@ -0,0 +1,4 @@ +# Empty java.util.logging.properties to prevent the log to stderr, so that +# all logs will be delegated to pax logging JUL handler only + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/jmx.acl.cfg b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/jmx.acl.cfg new file mode 100644 index 0000000000000000000000000000000000000000..f74b37a8077af4cda7d12623cea924c6abdc2205 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/jmx.acl.cfg @@ -0,0 +1,54 @@ +# +# Generic JMX ACL +# +# This file defines the roles required for MBean operations for MBeans that +# do not have this defined explicitly. +# +# The definition of ACLs for JMX operations works as follows: +# +# The required roles for JMX operations are defined in configuration files +# read via OSGi ConfigAdmin. +# +# JMX RBAC-related configuration is prefixed with jmx.acl and based on the +# JMX ObjectName that it applies to. For example specific configuration for +# an MBean with the following objectName: foo.bar:type=Test can be placed in +# a configuration file called jmx.acl.foo.bar.Test.cfg. More generic +# configuration can be placed in the domain (e.g. jmx.acl.foo.bar.cfg) or +# at the top level (jmx.acl.cfg). A simple configuration file looks like +# this: +# test : admin +# getVal : manager, viewer +# +# The system looks for required roles using the following process: +# The most specific configuration file/pid is tried first. E.g. in the +# above example the jmx.acl.foo.bar.Test.cfg is looked at first. In this +# configuration, the system looks for a: +# 1. Specific match for the current invocation, e.g. test(int)["17"] : role1 +# 2. Reg exp match for the current invocation, e.g. test(int)[/[0-9]/] : role2 +# In both cases the passed argument is converted to a String for the +# comparison. +# If any of the above match all the roles with matching definitions +# are collected and allowed. If no matches are found the following is tried: +# 3. Signature match for the invocation, e.g. test(int) : role3. If +# matched the associated roles are used. +# 4. Method name match for the invocation, e.g. test : role4. If matched +# the associated roles are used. +# 5. A method name wildcard match, e.g. te* : role5. For all the +# wildcard matches found in the current configuration file, the roles +# associated with the longest match are used. So if you have te* and * and +# the method invoked is 'test', then the roles defined with te* are used, +# not the ones defined with *. +# If no matching definition is found in the current configuration file, a +# more general configuration file is looked for. So jmx.acl.foo.bar.cfg is +# tried next, this matches the domain of the MBean. If there is no match +# found in the domain the most generic configuration file is consulted +# (jmx.acl.cfg). +# If a matching definition is found, this is used and the process will not +# look for any other matching definitions. So the most specific definition +# always takes precedence. +# +list* = viewer +get* = viewer +is* = viewer +set* = admin +* = admin \ No newline at end of file diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.felix.fileinstall-deploy.cfg b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.felix.fileinstall-deploy.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3a13eb8c8bbe772a8f9cce448b3f88b2f572b31a --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.felix.fileinstall-deploy.cfg @@ -0,0 +1,10 @@ +# +# Hot deployment of bundles +# + +felix.fileinstall.dir=${karaf.base}/deploy +felix.fileinstall.tmpdir=${karaf.data}/generated-bundles +felix.fileinstall.poll=1000 +felix.fileinstall.active.level=80 +felix.fileinstall.start.level=200 +felix.fileinstall.log.level=3 diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.jaas.cfg b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.jaas.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5551593637aed0f3b14b91cb9dc594973bc64eaf --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.jaas.cfg @@ -0,0 +1,42 @@ +# +# Boolean enabling / disabling encrypted passwords +# +encryption.enabled = false + +# +# Encryption Service name +# the default one is 'basic' +# a more powerful one named 'jasypt' is available +# when installing the encryption feature +# +encryption.name = + +# +# Encryption prefix +# +encryption.prefix = {CRYPT} + +# +# Encryption suffix +# +encryption.suffix = {CRYPT} + +# +# Set the encryption algorithm to use in Karaf JAAS login module +# Supported encryption algorithms follow: +# MD2 +# MD5 +# SHA-1 +# SHA-256 +# SHA-384 +# SHA-512 +# +encryption.algorithm = MD5 + +# +# Encoding of the encrypted password. +# Can be: +# hexadecimal +# base64 +# +encryption.encoding = hexadecimal diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.kar.cfg b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.kar.cfg new file mode 100644 index 0000000000000000000000000000000000000000..cd34c462c7fafb2e2cfe8bbd84bfd6cce6fd6a6e --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.kar.cfg @@ -0,0 +1,5 @@ +# +# Enable or disable the refresh of the bundles when installing +# the features contained in a KAR file +# +noAutoRefreshBundles=false \ No newline at end of file diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.log.cfg b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.log.cfg new file mode 100644 index 0000000000000000000000000000000000000000..75cbb8d007649282cd04b5a0cdbdbb82e2d2fe53 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.log.cfg @@ -0,0 +1,14 @@ +# +# This configuration file is used to configure the default values for the log:display +# and log:exception-display commands. +# + +# The number of log statements to be displayed using log:display +size=500 + +# this matches the pattern in logback.xml with a few caveats +# the date %d parameter is split up to avoid quoting in output +# the %n parameter is omitted to avoid duplicate new lines +# the %thread parameter becomes %t +# the %mdc parameter becomes %X and doesn't have a default value +pattern=%d{yyyy-MM-dd} %d{HH:mm:ss,SSSZ} %-5p [%t] %X{userId} %c - %m diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.management.cfg b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.management.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b17e007de97ac52c7c484a558f67763d6bc7ebb1 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.management.cfg @@ -0,0 +1,48 @@ +# +# The properties in this file define the configuration of Apache Karaf's JMX Management +# + +# +# Port number for RMI registry connection +# +rmiRegistryPort = 8099 + +# +# Host for RMI registry +# +rmiRegistryHost = 0.0.0.0 + +# +# Port number for RMI server connection +# +rmiServerPort = 8044 + +# +# Host for RMI server +# +rmiServerHost = 0.0.0.0 + +# +# Name of the JAAS realm used for authentication +# +jmxRealm = shiro + +# +# The service URL for the JMXConnectorServer +# +serviceUrl = service:jmx:rmi://${rmiServerHost}:${rmiServerPort}/jndi/rmi://${rmiRegistryHost}:${rmiRegistryPort}/karaf-${karaf.name} + +# +# Whether any threads started for the JMXConnectorServer should be started as daemon threads +# +daemon = true + +# +# Whether the JMXConnectorServer should be started in a separate thread +# +threaded = true + +# +# The ObjectName used to register the JMXConnectorServer +# +objectName = connector:name=rmi diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.service.acl.command.cfg b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.service.acl.command.cfg new file mode 100644 index 0000000000000000000000000000000000000000..766e4053da4ed76ded1113be4d3db14c259b45d4 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.service.acl.command.cfg @@ -0,0 +1,5 @@ +# +# Restrict all shell commands to admin role by default +# +service.guard = (&(osgi.command.scope=*)(osgi.command.function=*)) +execute = admin diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.shell.cfg b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.shell.cfg new file mode 100644 index 0000000000000000000000000000000000000000..0c982b7d2ac267e5df13d9db2758f3fd35533313 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.shell.cfg @@ -0,0 +1,61 @@ +# +# These properties are used to configure Karaf's ssh shell. +# + +# +# Via sshPort and sshHost you define the address you can login into Karaf. +# +sshPort = 8022 +sshHost = 0.0.0.0 + +# +# The sshIdleTimeout defines the inactivity timeout to logout the SSH session. +# The sshIdleTimeout is in milliseconds, and the default is set to 30 minutes. +# +sshIdleTimeout = 1800000 + +# +# sshRealm defines which JAAS domain to use for password authentication. +# +sshRealm = shiro + +# +# The location of the hostKey file defines where the private/public key of the server +# is located. If no file is at the defined location it will be ignored. +# +hostKey = ${karaf.data}/host.key + +# +# The format used for hostKey. +# Possible values are simple (Karaf internal), or PEM (OpenSSH format) +# +hostKeyFormat = simple + +# +# Self defined key size in 1024, 2048, 3072, or 4096 +# If not set, this defaults to 4096. +# +# keySize = 4096 + +# +# Specify host key algorithm, defaults to RSA +# +# algorithm = RSA + +# +# Specify an additional welcome banner to be displayed when a user logs into the server. +# +# welcomeBanner = + +# +# Defines the completion mode on the Karaf shell console. The possible values are: +# - GLOBAL: it's the same behavior as in previous Karaf releases. The completion displays all commands and all aliases +# ignoring if you are in a subshell or not. +# - FIRST: the completion displays all commands and all aliases only when you are not in a subshell. When you are +# in a subshell, the completion displays only the commands local to the subshell. +# - SUBSHELL: the completion displays only the subshells on the root level. When you are in a subshell, the completion +# displays only the commands local to the subshell. +# This property define the default value when you use the Karaf shell console. +# You can change the completion mode directly in the shell console, using shell:completion command. +# +completionMode = GLOBAL diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.ops4j.pax.url.mvn.cfg b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.ops4j.pax.url.mvn.cfg new file mode 100644 index 0000000000000000000000000000000000000000..13f5deecbfaa8d8c17ea8c81964d3f944122e806 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.ops4j.pax.url.mvn.cfg @@ -0,0 +1,78 @@ +########################################################################## +# customized to only lookup bundles from the assembled system repository # +########################################################################## + +# +# If set to true, the following property will not allow any certificate to be used +# when accessing Maven repositories through SSL +# +#org.ops4j.pax.url.mvn.certificateCheck= + +# +# Path to the local Maven settings file. +# The repositories defined in this file will be automatically added to the list +# of default repositories if the 'org.ops4j.pax.url.mvn.repositories' property +# below is not set. +# The following locations are checked for the existence of the settings.xml file +# * 1. looks for the specified url +# * 2. if not found looks for ${user.home}/.m2/settings.xml +# * 3. if not found looks for ${maven.home}/conf/settings.xml +# * 4. if not found looks for ${M2_HOME}/conf/settings.xml +# +# Default to empty settings file, comment this out to pick up your local Maven settings +org.ops4j.pax.url.mvn.settings=${karaf.base}/${karaf.default.repository}/settings.xml + +# +# Path to the local Maven repository which is used to avoid downloading +# artifacts when they already exist locally. +# The value of this property will be extracted from the settings.xml file +# above, or defaulted to: +# System.getProperty( "user.home" ) + "/.m2/repository" +# +# Default to Karaf's system repository, comment this out to use your local repository +org.ops4j.pax.url.mvn.localRepository=file:${karaf.base}/${karaf.default.repository} + +# +# Default this to false. It's just weird to use undocumented repos +# +org.ops4j.pax.url.mvn.useFallbackRepositories=false + +# +# Uncomment if you don't wanna use the proxy settings +# from the Maven conf/settings.xml file +# +org.ops4j.pax.url.mvn.proxySupport=false + +# +# Comma separated list of repositories scanned when resolving an artifact. +# Those repositories will be checked before iterating through the +# below list of repositories and even before the local repository +# A repository url can be appended with zero or more of the following flags: +# @snapshots : the repository contains snapshots +# @noreleases : the repository does not contain any released artifacts +# +# The following property value will add the system folder as a repo. +# +org.ops4j.pax.url.mvn.defaultRepositories=\ + file:${karaf.base}/${karaf.default.repository}@id=system.repository@snapshots + +# Use the default local repo (e.g.~/.m2/repository) as a "remote" repo +#org.ops4j.pax.url.mvn.defaultLocalRepoAsRemote= + +# +# Comma separated list of repositories scanned when resolving an artifact. +# A repository url can be appended with zero or more of the following flags: +# @snapshots : the repository contains snapshots +# @noreleases : the repository does not contain any released artifacts +# @id=repoid : the id for the repository, just like in the settings.xml this is optional but recommended +# +# The default list doesn't contain any snapshot repositories as it can impact artifact resolution. +# During development you may want to add the following repositories containing snapshots: +# https://repository.apache.org/content/groups/snapshots-group@id=apache@snapshots@noreleases +# https://oss.sonatype.org/content/repositories/snapshots@id=sonatype@snapshots@norelease +# https://oss.sonatype.org/content/repositories/ops4j-snapshots@id=ops4j@snapshots@noreleases +# +# If you want to install additional bundles from central add this entry to the list: +# https://repo1.maven.org/maven2@id=central +# +org.ops4j.pax.url.mvn.repositories= diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/shell.init.script b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/shell.init.script new file mode 100644 index 0000000000000000000000000000000000000000..ea402718c40db717cab9135793c1d0261a29e770 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/shell.init.script @@ -0,0 +1,15 @@ +// +// This script is run each time a shell is created. +// You can define here closures or variables that will be available +// in each session. +// +ld = { log:display $args } ; +lde = { log:exception-display $args } ; +la = { bundle:list -t 0 $args } ; +ls = { service:list $args } ; +cl = { config:list "(service.pid=$args)" } ; +halt = { system:shutdown -h -f $args } ; +help = { *:help $args | more } ; +man = { help $args } ; +log:list = { log:get ALL } ; +service:get = { $.context getService ($.context getServiceReference $args) }; diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/system.properties b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/system.properties new file mode 100644 index 0000000000000000000000000000000000000000..58a4a75cc478977532815456b6e2e5d21200a137 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/system.properties @@ -0,0 +1,122 @@ +# +# The properties defined in this file will be made available through system +# properties at the very beginning of the Karaf's boot process. +# + + +# Log level when the pax-logging service is not available +# This level will only be used while the pax-logging service bundle +# is not fully available. +# To change log levels, please refer to the org.ops4j.pax.logging.cfg file +# instead. +org.ops4j.pax.logging.DefaultServiceLog.level = ERROR + +# +# Name of this Karaf instance. +# +karaf.name = root + +# +# Default repository where bundles will be loaded from before using +# other Maven repositories. For the full Maven configuration, see +# the org.ops4j.pax.url.mvn.cfg file. +# +karaf.default.repository = system + +# +# Location of a shell script that will be run when starting a shell +# session. This script can be used to create aliases and define +# additional commands. +# +karaf.shell.init.script = ${karaf.etc}/shell.init.script + +# +# Sets the maximum size of the shell command history. If not set, +# defaults to 500 entries. Setting to 0 will disable history. +# +# karaf.shell.history.maxSize = 0 + +# +# Deletes the entire karaf.data directory at every start +# +karaf.clean.all = false + +# +# Deletes the karaf.data/cache directory at every start +# +karaf.clean.cache = true + +# +# User name for the Karaf local console +# +karaf.local.user = karaf + +# +# Roles to use when logging into a local Karaf console. +# +# The syntax is the following: +# [classname:]principal +# where classname is the class name of the principal object +# (defaults to org.apache.karaf.jaas.modules.RolePrincipal) +# and principal is the name of the principal of that class +# (defaults to instance). +# +karaf.local.roles = admin,manager,viewer,systembundles + +# +# Set this empty property to avoid errors when validating xml documents. +# +xml.catalog.files = + +# +# Suppress the bell in the console when hitting backspace too many times +# for example +# +jline.nobell = true + +# +# ServiceMix specs options +# +org.apache.servicemix.specs.debug = false +org.apache.servicemix.specs.timeout = 0 + +# +# Settings for the OSGi 4.3 Weaving +# By default, we will not weave any classes. Change this setting to include classes +# that you application needs to have woven. +# +org.apache.aries.proxy.weaving.enabled = none +# Classes not to weave - Aries default + Xerces which is known to have issues. +org.apache.aries.proxy.weaving.disabled = org.objectweb.asm.*,org.slf4j.*,org.apache.log4j.*,javax.*,org.apache.xerces.* + +# +# By default, only Karaf shell commands are secured, but additional services can be +# secured by expanding this filter +# +karaf.secured.services = (&(osgi.command.scope=*)(osgi.command.function=*)) + +karaf.history=${user.home}/.nexus/karaf.history + +java.awt.headless=true +networkaddress.cache.ttl=3600 + +com.sun.jndi.ldap.connect.pool.protocol=plain ssl + +org.ops4j.pax.logging.StaticLogbackContext=true +org.ops4j.pax.logging.StaticLogbackFile=${logback.etc}/logback.xml +org.jboss.logging.provider=slf4j + +# orientdb direct-memory chunk size in bytes +memory.chunk.size=268435456 + +# orientdb memory checker interval, in milliseconds (60 minutes) +profiler.memoryCheckInterval=3600000 + +# orientdb: temporary workaround for NEXUS-14409 +distributed.checkIntegrityLastTxs=0 + +# orientdb: NEXUS-14843 +index.cursor.prefetchSize=10000 + +# orientdb: NEXUS-10402 +distributed.shutdownTimeout=20000 diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback/logback-access.xml b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback/logback-access.xml new file mode 100644 index 0000000000000000000000000000000000000000..05971e96b348e614ed0e4f8426c6cfced96a70b5 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback/logback-access.xml @@ -0,0 +1,16 @@ + + + + ${karaf.data}/log/request.log + true + + %clientHost %l %user [%date] "%requestURL" %statusCode %bytesSent %elapsedTime "%header{User-Agent}" + + + ${karaf.data}/log/request-%d{yyyy-MM-dd}.log.gz + 90 + + + + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback/logback.xml b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback/logback.xml new file mode 100644 index 0000000000000000000000000000000000000000..576ddc4e3028dfea5f6a240e3b0f6d5ad979c7f5 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback/logback.xml @@ -0,0 +1,67 @@ + + + + true + + + + + + + + + + + + %d{"yyyy-MM-dd HH:mm:ss,SSSZ"} %-5p [%thread] %mdc{userId:-*SYSTEM} %c - %m%n + + + + + ${karaf.data}/log/nexus.log + true + + %d{"yyyy-MM-dd HH:mm:ss,SSSZ"} %-5p [%thread] %node %mdc{userId:-*SYSTEM} %c - %m%n + + + ${karaf.data}/log/nexus-%d{yyyy-MM-dd}.log.gz + 90 + + + + + + + + taskIdAndDate + unknown + + + + ${karaf.data}/log/tasks/${taskIdAndDate}.log + + %d{"yyyy-MM-dd HH:mm:ss,SSSZ"} %-5p [%thread] %node %mdc{userId:-*SYSTEM} %c - %m%n + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/nexus-default.properties b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/nexus-default.properties new file mode 100644 index 0000000000000000000000000000000000000000..76543378170398884e66ccb19ee61838ad973fd3 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/nexus-default.properties @@ -0,0 +1,14 @@ +## DO NOT EDIT - CUSTOMIZATIONS BELONG IN $data-dir/etc/nexus.properties +## +# Jetty section +application-port=8081 +application-host=0.0.0.0 +nexus-args=${jetty.etc}/jetty.xml,${jetty.etc}/jetty-http.xml,${jetty.etc}/jetty-requestlog.xml +nexus-context-path=/ + +# Nexus section +nexus-edition=nexus-oss-edition +nexus-features=\ + nexus-core-feature + +nexus.upgrade.warnOnMissingDependencies=true diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/ssl/.placeholder b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/etc/ssl/.placeholder new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/COPYRIGHT.html b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/COPYRIGHT.html new file mode 100644 index 0000000000000000000000000000000000000000..661315bbc3abd1d0a296731e294e28db3c0d1da8 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/COPYRIGHT.html @@ -0,0 +1,33 @@ + + + + + Copyright + + + +

+ Copyright © 2008-present Sonatype, Inc. +

+ +

+ All rights reserved. Includes the third-party code listed at + http://links.sonatype.com/products/nexus/oss/attributions +

+ +

+ Sonatype and Sonatype Nexus are trademarks of Sonatype, Inc. +
+ Apache Maven is a trademark of the Apache Foundation. +
+ M2Eclipse is a trademark of the Eclipse Foundation. +
+ All other trademarks are the property of their respective owners. +

+ + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/LICENSE.html b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/LICENSE.html new file mode 100644 index 0000000000000000000000000000000000000000..3998fcebeebe4d34f47c8777b4db160be1d1dec1 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/LICENSE.html @@ -0,0 +1,261 @@ + + + + + + +Eclipse Public License - Version 1.0 + + + + + + +

Eclipse Public License - v 1.0

+ +

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE +PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR +DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS +AGREEMENT.

+ +

1. DEFINITIONS

+ +

"Contribution" means:

+ +

a) in the case of the initial Contributor, the initial +code and documentation distributed under this Agreement, and

+

b) in the case of each subsequent Contributor:

+

i) changes to the Program, and

+

ii) additions to the Program;

+

where such changes and/or additions to the Program +originate from and are distributed by that particular Contributor. A +Contribution 'originates' from a Contributor if it was added to the +Program by such Contributor itself or anyone acting on such +Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) +are not derivative works of the Program.

+ +

"Contributor" means any person or entity that distributes +the Program.

+ +

"Licensed Patents" mean patent claims licensable by a +Contributor which are necessarily infringed by the use or sale of its +Contribution alone or when combined with the Program.

+ +

"Program" means the Contributions distributed in accordance +with this Agreement.

+ +

"Recipient" means anyone who receives the Program under +this Agreement, including all Contributors.

+ +

2. GRANT OF RIGHTS

+ +

a) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free copyright license to reproduce, prepare derivative works +of, publicly display, publicly perform, distribute and sublicense the +Contribution of such Contributor, if any, and such derivative works, in +source code and object code form.

+ +

b) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, +offer to sell, import and otherwise transfer the Contribution of such +Contributor, if any, in source code and object code form. This patent +license shall apply to the combination of the Contribution and the +Program if, at the time the Contribution is added by the Contributor, +such addition of the Contribution causes such combination to be covered +by the Licensed Patents. The patent license shall not apply to any other +combinations which include the Contribution. No hardware per se is +licensed hereunder.

+ +

c) Recipient understands that although each Contributor +grants the licenses to its Contributions set forth herein, no assurances +are provided by any Contributor that the Program does not infringe the +patent or other intellectual property rights of any other entity. Each +Contributor disclaims any liability to Recipient for claims brought by +any other entity based on infringement of intellectual property rights +or otherwise. As a condition to exercising the rights and licenses +granted hereunder, each Recipient hereby assumes sole responsibility to +secure any other intellectual property rights needed, if any. For +example, if a third party patent license is required to allow Recipient +to distribute the Program, it is Recipient's responsibility to acquire +that license before distributing the Program.

+ +

d) Each Contributor represents that to its knowledge it +has sufficient copyright rights in its Contribution, if any, to grant +the copyright license set forth in this Agreement.

+ +

3. REQUIREMENTS

+ +

A Contributor may choose to distribute the Program in object code +form under its own license agreement, provided that:

+ +

a) it complies with the terms and conditions of this +Agreement; and

+ +

b) its license agreement:

+ +

i) effectively disclaims on behalf of all Contributors +all warranties and conditions, express and implied, including warranties +or conditions of title and non-infringement, and implied warranties or +conditions of merchantability and fitness for a particular purpose;

+ +

ii) effectively excludes on behalf of all Contributors +all liability for damages, including direct, indirect, special, +incidental and consequential damages, such as lost profits;

+ +

iii) states that any provisions which differ from this +Agreement are offered by that Contributor alone and not by any other +party; and

+ +

iv) states that source code for the Program is available +from such Contributor, and informs licensees how to obtain it in a +reasonable manner on or through a medium customarily used for software +exchange.

+ +

When the Program is made available in source code form:

+ +

a) it must be made available under this Agreement; and

+ +

b) a copy of this Agreement must be included with each +copy of the Program.

+ +

Contributors may not remove or alter any copyright notices contained +within the Program.

+ +

Each Contributor must identify itself as the originator of its +Contribution, if any, in a manner that reasonably allows subsequent +Recipients to identify the originator of the Contribution.

+ +

4. COMMERCIAL DISTRIBUTION

+ +

Commercial distributors of software may accept certain +responsibilities with respect to end users, business partners and the +like. While this license is intended to facilitate the commercial use of +the Program, the Contributor who includes the Program in a commercial +product offering should do so in a manner which does not create +potential liability for other Contributors. Therefore, if a Contributor +includes the Program in a commercial product offering, such Contributor +("Commercial Contributor") hereby agrees to defend and +indemnify every other Contributor ("Indemnified Contributor") +against any losses, damages and costs (collectively "Losses") +arising from claims, lawsuits and other legal actions brought by a third +party against the Indemnified Contributor to the extent caused by the +acts or omissions of such Commercial Contributor in connection with its +distribution of the Program in a commercial product offering. The +obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In +order to qualify, an Indemnified Contributor must: a) promptly notify +the Commercial Contributor in writing of such claim, and b) allow the +Commercial Contributor to control, and cooperate with the Commercial +Contributor in, the defense and any related settlement negotiations. The +Indemnified Contributor may participate in any such claim at its own +expense.

+ +

For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those +performance claims and warranties, and if a court requires any other +Contributor to pay any damages as a result, the Commercial Contributor +must pay those damages.

+ +

5. NO WARRANTY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS +PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, +ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement , including but not limited to +the risks and costs of program errors, compliance with applicable laws, +damage to or loss of data, programs or equipment, and unavailability or +interruption of operations.

+ +

6. DISCLAIMER OF LIABILITY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT +NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR +DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

+ +

7. GENERAL

+ +

If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further action +by the parties hereto, such provision shall be reformed to the minimum +extent necessary to make such provision valid and enforceable.

+ +

If Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other +software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the +date such litigation is filed.

+ +

All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of time +after becoming aware of such noncompliance. If all Recipient's rights +under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive.

+ +

Everyone is permitted to copy and distribute copies of this +Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The +Agreement Steward reserves the right to publish new versions (including +revisions) of this Agreement from time to time. No one other than the +Agreement Steward has the right to modify this Agreement. The Eclipse +Foundation is the initial Agreement Steward. The Eclipse Foundation may +assign the responsibility to serve as the Agreement Steward to a +suitable separate entity. Each new version of the Agreement will be +given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new version +of the Agreement is published, Contributor may elect to distribute the +Program (including its Contributions) under the new version. Except as +expressly stated in Sections 2(a) and 2(b) above, Recipient receives no +rights or licenses to the intellectual property of any Contributor under +this Agreement, whether expressly, by implication, estoppel or +otherwise. All rights in the Program not expressly granted under this +Agreement are reserved.

+ +

This Agreement is governed by the laws of the State of New York and +the intellectual property laws of the United States of America. No party +to this Agreement will bring a legal action under this Agreement more +than one year after the cause of action arose. Each party waives its +rights to a jury trial in any resulting litigation.

+ + + + \ No newline at end of file diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/apple-touch-icon.png b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e3086bfff759a844746687152f904ff50357c8d5 Binary files /dev/null and b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/apple-touch-icon.png differ diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/browserconfig.xml b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/browserconfig.xml new file mode 100644 index 0000000000000000000000000000000000000000..7b1730de6ac8121dc130f5e6c40266fe4b21d8c3 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/browserconfig.xml @@ -0,0 +1,26 @@ + + + + + + + + + + #00a300 + + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon-16x16.png b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..93f5dabecbaf2fdec67598db87bfecdf49287536 Binary files /dev/null and b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon-16x16.png differ diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon-32x32.png b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..ea8d5729646361bb78ec55e606d8ab9d747427c4 Binary files /dev/null and b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon-32x32.png differ diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon.ico b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..076b8a3d1913481e4442b38c5a4a549f10fc6129 Binary files /dev/null and b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon.ico differ diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-144x144.png b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..6ca07ef3a6c61e33d4ae649e9d66277d2cb706a9 Binary files /dev/null and b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-144x144.png differ diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-150x150.png b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..bd2be50b81a8803b9f645428fa97aa2955db077e Binary files /dev/null and b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-150x150.png differ diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-310x150.png b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-310x150.png new file mode 100644 index 0000000000000000000000000000000000000000..ea359bd9c0ecafc6550e5572e52b30457bbaea36 Binary files /dev/null and b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-310x150.png differ diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-310x310.png b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-310x310.png new file mode 100644 index 0000000000000000000000000000000000000000..f114ac2e5354b73d6285da4d7caad9c7036e6099 Binary files /dev/null and b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-310x310.png differ diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-70x70.png b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-70x70.png new file mode 100644 index 0000000000000000000000000000000000000000..9d6fbbbfda308c33dc15c47d1656b0c5e026a48d Binary files /dev/null and b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-70x70.png differ diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/robots.txt b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/robots.txt new file mode 100644 index 0000000000000000000000000000000000000000..36ad3aa1347abc85187695a4728d091df319232f --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Disallow: /repository/ +Disallow: /service/ +Allow: / diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/safari-pinned-tab.svg b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/safari-pinned-tab.svg new file mode 100644 index 0000000000000000000000000000000000000000..8921cc629b227dcd0715d742d92c284edfce7209 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/public/safari-pinned-tab.svg @@ -0,0 +1,36 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + + + + + + + + + + + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/system/settings.xml b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/system/settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..f0a1b0b121a3f3e4d66de0593da6894b50b24a5f --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/overlay/system/settings.xml @@ -0,0 +1 @@ + diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/sonatype-work/nexus3/clean_cache b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/sonatype-work/nexus3/clean_cache new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/sonatype-work/nexus3/log/.placeholder b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/sonatype-work/nexus3/log/.placeholder new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/sonatype-work/nexus3/tmp/.placeholder b/thirdparty-bundles/assemblies/nexus-base-template/src/main/resources/sonatype-work/nexus3/tmp/.placeholder new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/thirdparty-bundles/assemblies/nexus-boot-feature/pom.xml b/thirdparty-bundles/assemblies/nexus-boot-feature/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..1578a10015b8912c97d2856bc2d05f55572df010 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-boot-feature/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + + org.sonatype.nexus.assemblies + nexus-assemblies + 3.8.0-02 + + + nexus-boot-feature + ${project.groupId}:${project.artifactId} + feature + + + + org.sonatype.nexus + nexus-bootstrap + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + + + + + diff --git a/thirdparty-bundles/assemblies/nexus-core-feature/pom.xml b/thirdparty-bundles/assemblies/nexus-core-feature/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..7e07b099e4b54ab04185328a6fc59ae78975f4ed --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-core-feature/pom.xml @@ -0,0 +1,153 @@ + + + 4.0.0 + + + org.sonatype.nexus.assemblies + nexus-assemblies + 3.8.0-02 + + + nexus-core-feature + ${project.groupId}:${project.artifactId} + feature + + + + + + + org.sonatype.nexus + nexus-bootstrap + provided + + + + org.sonatype.nexus + nexus-commands + provided + + + + org.sonatype.nexus + nexus-orient + provided + + + + org.sonatype.nexus + nexus-security + provided + + + + + + org.sonatype.nexus + nexus-core + + + + org.sonatype.nexus + nexus-quartz + + + + org.sonatype.nexus.plugins + nexus-audit-plugin + features + xml + + + + org.sonatype.nexus.plugins + nexus-blobstore-tasks + features + xml + + + + org.sonatype.nexus.plugins + nexus-ssl-plugin + features + xml + + + + org.sonatype.nexus.plugins + nexus-coreui-plugin + features + xml + + + + org.sonatype.nexus.plugins + nexus-repository-httpbridge + features + xml + + + + org.sonatype.nexus.plugins + nexus-repository-maven + features + xml + + + + org.sonatype.nexus.plugins + nexus-repository-raw + features + xml + + + + org.sonatype.nexus.plugins + nexus-restore-maven + features + xml + + + + org.sonatype.nexus.plugins + nexus-script-plugin + features + xml + + + + org.sonatype.nexus.plugins + nexus-task-log-cleanup + features + xml + + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + + true + + + + + + diff --git a/thirdparty-bundles/assemblies/nexus-core-feature/src/main/feature/feature.xml b/thirdparty-bundles/assemblies/nexus-core-feature/src/main/feature/feature.xml new file mode 100644 index 0000000000000000000000000000000000000000..a5c1f8c685f63f90439ec4a74f80ac9b03e5b129 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-core-feature/src/main/feature/feature.xml @@ -0,0 +1,27 @@ + + + + + nexus-audit-plugin + nexus-blobstore-tasks + nexus-ssl-plugin + nexus-coreui-plugin + nexus-repository-httpbridge + nexus-repository-maven + nexus-repository-raw + nexus-restore-maven + + diff --git a/thirdparty-bundles/assemblies/nexus-startup-feature/pom.xml b/thirdparty-bundles/assemblies/nexus-startup-feature/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..fc41711ab3ce3e3807d1aa11b727100a07a6d495 --- /dev/null +++ b/thirdparty-bundles/assemblies/nexus-startup-feature/pom.xml @@ -0,0 +1,157 @@ + + + 4.0.0 + + + org.sonatype.nexus.assemblies + nexus-assemblies + 3.8.0-02 + + + nexus-startup-feature + ${project.groupId}:${project.artifactId} + feature + + + + + org.sonatype.nexus + nexus-pax-logging + + + + com.codahale.metrics + metrics-logback + + + + javax.servlet + javax.servlet-api + + + + org.eclipse.jetty + jetty-webapp + + + + org.eclipse.jetty + jetty-continuation + + + + org.eclipse.jetty + jetty-servlet + + + + org.eclipse.jetty + jetty-security + + + + org.eclipse.jetty + jetty-server + + + + org.eclipse.jetty + jetty-deploy + + + + org.eclipse.jetty + jetty-util + + + + org.eclipse.jetty + jetty-util-ajax + + + + org.eclipse.jetty + jetty-servlets + + + + org.eclipse.jetty + jetty-proxy + + + + org.eclipse.jetty + jetty-http + + + + org.eclipse.jetty + jetty-io + + + + org.eclipse.jetty + jetty-xml + + + + org.eclipse.jetty + jetty-jmx + + + + org.eclipse.jetty + jetty-rewrite + + + + com.codahale.metrics + metrics-jetty9 + + + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.javax-inject + + + + + net.java.dev.jna + jna + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + + 8 + + + + + + diff --git a/thirdparty-bundles/assemblies/pom.xml b/thirdparty-bundles/assemblies/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..77ecee1b67714db14dda05ca8b8685bbe6c36d56 --- /dev/null +++ b/thirdparty-bundles/assemblies/pom.xml @@ -0,0 +1,63 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-parent + 3.8.0-02 + + + org.sonatype.nexus.assemblies + nexus-assemblies + ${project.groupId}:${project.artifactId} + pom + + + nexus-startup-feature + nexus-boot-feature + nexus-base-feature + nexus-core-feature + nexus-base-template + + + + + + + + + org.sonatype.nexus + nexus-components + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.plugins + nexus-plugins + pom + 3.8.0-02 + import + + + + + + diff --git a/thirdparty-bundles/buildsupport/README.md b/thirdparty-bundles/buildsupport/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7e332743eccf960f43ba2e0f7aff0e054076e909 --- /dev/null +++ b/thirdparty-bundles/buildsupport/README.md @@ -0,0 +1,19 @@ + +# Build Support + +Modules which provide `dependencyManagement` configuration in small(ish) groups. + +The `all` module aggregates to allow a single import to pick up all the configuration. diff --git a/thirdparty-bundles/buildsupport/all/pom.xml b/thirdparty-bundles/buildsupport/all/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..9624c0e79d53eddf0149959a8e6d47c3cbb24d5a --- /dev/null +++ b/thirdparty-bundles/buildsupport/all/pom.xml @@ -0,0 +1,180 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-all + ${project.groupId}:${project.artifactId} + pom + + + + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-commons + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-db + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-goodies + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-groovy + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-guice + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-httpclient + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-internal + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-jetty + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-jruby + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-logging + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-maven + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-metrics + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-osgi + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-other + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-rest + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-security + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-testing + pom + 3.8.0-02 + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-ui + pom + 3.8.0-02 + import + + + + + diff --git a/thirdparty-bundles/buildsupport/commons/pom.xml b/thirdparty-bundles/buildsupport/commons/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..9b550483c43443847e8ebf992e9d6f6f710d9463 --- /dev/null +++ b/thirdparty-bundles/buildsupport/commons/pom.xml @@ -0,0 +1,133 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-commons + ${project.groupId}:${project.artifactId} + pom + + + + + + commons-io + commons-io + 2.4 + + + + commons-codec + commons-codec + 1.10 + + + + commons-lang + commons-lang + 2.6 + + + + org.apache.commons + commons-lang3 + 3.4 + + + + commons-fileupload + commons-fileupload + 1.3.2 + + + + commons-beanutils + commons-beanutils + 1.9.2 + + + commons-logging + commons-logging + + + + + + commons-cli + commons-cli + 1.4 + + + + commons-collections + commons-collections + 3.2.2 + + + + org.apache.commons + commons-collections4 + 4.1 + + + + commons-configuration + commons-configuration + 1.10 + + + + org.apache.commons + commons-compress + 1.11 + + + + + org.tukaani + xz + 1.5 + + + + org.apache.commons + commons-email + 1.5 + + + + org.apache.commons + commons-jexl3 + 3.0 + + + commons-logging + commons-logging + + + + + + + + diff --git a/thirdparty-bundles/buildsupport/db/pom.xml b/thirdparty-bundles/buildsupport/db/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..2da5306f8a6bf2e517e9e6195dc19778c1392657 --- /dev/null +++ b/thirdparty-bundles/buildsupport/db/pom.xml @@ -0,0 +1,164 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-db + ${project.groupId}:${project.artifactId} + pom + + + 2.2.31 + 3.7.8 + 2.0.1 + + + + + + + com.orientechnologies + orientdb-core + ${orientdb.version} + + + org.xerial.snappy + snappy-java + + + + + + com.orientechnologies + orientdb-enterprise + ${orientdb.version} + + + + com.orientechnologies + orientdb-tools + ${orientdb.version} + + + com.orientechnologies + orientdb-core + + + com.orientechnologies + orientdb-client + + + com.orientechnologies + orientdb-object + + + + + + com.orientechnologies + orientdb-object + ${orientdb.version} + + + + com.orientechnologies + orientdb-server + ${orientdb.version} + + + + com.orientechnologies + orientdb-distributed + ${orientdb.version} + + + com.hazelcast + hazelcast-all + + + com.orientechnologies + orientdb-graphdb + + + + + + com.orientechnologies + orientdb-client + ${orientdb.version} + + + + com.orientechnologies + orientdb-jdbc + ${orientdb.version} + + + + com.orientechnologies + orientdb-community + ${orientdb.version} + zip + + + + mysql + mysql-connector-java + 5.1.36 + + + + com.hazelcast + hazelcast + ${hazelcast.version} + + + + com.hazelcast + hazelcast + ${hazelcast.version} + tests + + + + com.hazelcast + hazelcast-aws + ${hazelcast-aws.version} + + + + net.java.dev.jna + jna + 4.5.0 + + + + net.java.dev.jna + jna-platform + 4.5.0 + + + + + + diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/pom.xml b/thirdparty-bundles/buildsupport/extjs-maven-plugin/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..8c88df260b70845db9e2cb0fc707c84b025c9ffa --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/pom.xml @@ -0,0 +1,112 @@ + + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + extjs-maven-plugin + ${project.groupId}:${project.artifactId} + maven-plugin + + + + + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-all + pom + 3.8.0-02 + import + + + + + + + + org.apache.maven + maven-plugin-api + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + + + + com.google.guava + guava + + + + com.google.code.findbugs + jsr305 + true + + + + + org.codehaus.plexus + plexus-utils + + + + org.mozilla + rhino + + + + org.sonatype.goodies + goodies-testsupport + test + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + + + + descriptor + helpmojo + + + + true + + + + + + + + diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/AggregateJsMojo.java b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/AggregateJsMojo.java new file mode 100644 index 0000000000000000000000000000000000000000..3d805bbbae5285cdd9618800e90cf2bd81e4f0f5 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/AggregateJsMojo.java @@ -0,0 +1,129 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.Writer; +import java.util.List; +import java.util.Map; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.codehaus.plexus.util.DirectoryScanner; + +import static org.apache.maven.plugins.annotations.LifecyclePhase.PROCESS_RESOURCES; + +/** + * Aggregate javascript sources in order as required by ExtJS. + * + * @since 3.0 + */ +@Mojo(name="aggregate-js", defaultPhase = PROCESS_RESOURCES) +public class AggregateJsMojo + extends AbstractMojo +{ + private static final String[] DEFAULT_INCLUDES = { "**/*.js" }; + + @Parameter(required = true) + private File sourceDirectory; + + @Parameter + private String[] includes; + + @Parameter + private String[] excludes; + + @Parameter(required = true) + private File outputFile; + + @Parameter + private String namespace; + + /** + * Enables extra warnings which may hint at problems. + */ + @Parameter(property = "extjs.warnings", defaultValue = "false") + private boolean warnings; + + /** + * Enable omission (auto-commenting) of lines surrounded by {@code //} and {@code //}. + */ + @Parameter(property = "extjs.omit", defaultValue = "false") + private boolean omit; + + /** + * Omission flags. When named flag is enabled, sections matching those omission tokens will be commented out. + */ + @Parameter + private Map omitFlags; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + try { + doExecute(); + } + catch (Exception e) { + throw new MojoExecutionException(e.toString(), e); + } + } + + private void doExecute() throws Exception { + // build scanner to find files to aggregate + DirectoryScanner files = new DirectoryScanner(); + files.setBasedir(sourceDirectory); + if (includes == null || includes.length == 0) { + files.setIncludes(DEFAULT_INCLUDES); + } + else { + files.setIncludes(includes); + } + files.setExcludes(excludes); + files.addDefaultExcludes(); + + // build the list of ordered classes to include + ClassDefScanner scanner = new ClassDefScanner(getLog()); + scanner.setWarnings(warnings); + if (namespace == null) { + getLog().warn("Namespace is not configured; will be unable to detect and resolve MVC references"); + } + else { + scanner.setNamespace(namespace); + } + List classes = scanner.scan(files); + + // aggregate all class sources + getLog().info("Writing: " + outputFile); + Writer output = new BufferedWriter(new FileWriter(outputFile)); + try { + FileAppender appender; + if (omit) { + appender = new OmissionFileAppender(getLog(), output, omitFlags); + } + else { + appender = new FileAppender(getLog(), output); + } + + for (ClassDef def : classes) { + appender.append(def.getSource()); + } + } + finally { + output.close(); + } + } +} diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/ClassDef.java b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/ClassDef.java new file mode 100644 index 0000000000000000000000000000000000000000..b7e14228ca1bc11cffa663d72bd36c94f3c183b5 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/ClassDef.java @@ -0,0 +1,192 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * ExtJS class definition record. + * + * @since 3.0 + */ +public class ClassDef +{ + private final String name; + + private final File source; + + private final Set dependencies = new HashSet<>(); + + private final List alternateClassName = new ArrayList<>(); + + private final List alias = new ArrayList<>(); + + private double priority = 0; + + private String extend; + + private String override; + + private List requires; + + private List mixins; + + private List uses; + + private List mvcControllers; + + private List mvcModels; + + private List mvcStores; + + private List mvcViews; + + public ClassDef(final String name, final File source) { + this.name = checkNotNull(name); + this.source = checkNotNull(source); + } + + public String getName() { + return name; + } + + public File getSource() { + return source; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "name='" + name + '\'' + + ", source=" + source + + '}'; + } + + public Set getDependencies() { + return dependencies; + } + + public List getAlternateClassName() { + return alternateClassName; + } + + public List getAlias() { + return alias; + } + + public double getPriority() { + return priority; + } + + public void setPriority(final double priority) { + this.priority = priority; + } + + @Nullable + public String getExtend() { + return extend; + } + + public void setExtend(final String extend) { + this.extend = checkNotNull(extend); + dependencies.add(extend); + } + + @Nullable + public String getOverride() { + return override; + } + + public void setOverride(final String override) { + this.override = checkNotNull(override); + dependencies.add(override); + } + + @Nullable + public List getRequires() { + return requires; + } + + public void setRequires(final List requires) { + this.requires = checkNotNull(requires); + dependencies.addAll(requires); + } + + @Nullable + public List getMixins() { + return mixins; + } + + public void setMixins(final List mixins) { + this.mixins = checkNotNull(mixins); + dependencies.addAll(mixins); + } + + @Nullable + public List getUses() { + return uses; + } + + public void setUses(final List uses) { + this.uses = checkNotNull(uses); + dependencies.addAll(uses); + } + + // + // Custom handling for MVC support + // + + @Nullable + public List getMvcControllers() { + return mvcControllers; + } + + public void setMvcControllers(final List mvcControllers) { + this.mvcControllers = checkNotNull(mvcControllers); + } + + @Nullable + public List getMvcModels() { + return mvcModels; + } + + public void setMvcModels(final List mvcModels) { + this.mvcModels = checkNotNull(mvcModels); + } + + @Nullable + public List getMvcStores() { + return mvcStores; + } + + public void setMvcStores(final List mvcStores) { + this.mvcStores = checkNotNull(mvcStores); + } + + @Nullable + public List getMvcViews() { + return mvcViews; + } + + public void setMvcViews(final List mvcViews) { + this.mvcViews = checkNotNull(mvcViews); + } +} diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/ClassDefScanner.java b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/ClassDefScanner.java new file mode 100644 index 0000000000000000000000000000000000000000..a9fd7e1a4ed8862ee5bcf69f085be32c625d129e --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/ClassDefScanner.java @@ -0,0 +1,617 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import org.apache.maven.plugin.logging.Log; +import org.codehaus.plexus.util.Scanner; +import org.codehaus.plexus.util.dag.DAG; +import org.codehaus.plexus.util.dag.TopologicalSorter; +import org.codehaus.plexus.util.dag.Vertex; +import org.mozilla.javascript.CompilerEnvirons; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ErrorReporter; +import org.mozilla.javascript.Parser; +import org.mozilla.javascript.ast.ArrayLiteral; +import org.mozilla.javascript.ast.AstNode; +import org.mozilla.javascript.ast.AstRoot; +import org.mozilla.javascript.ast.FunctionCall; +import org.mozilla.javascript.ast.Name; +import org.mozilla.javascript.ast.NewExpression; +import org.mozilla.javascript.ast.NodeVisitor; +import org.mozilla.javascript.ast.NumberLiteral; +import org.mozilla.javascript.ast.ObjectLiteral; +import org.mozilla.javascript.ast.ObjectProperty; +import org.mozilla.javascript.ast.PropertyGet; +import org.mozilla.javascript.ast.StringLiteral; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.mozilla.javascript.Token.CALL; +import static org.mozilla.javascript.Token.NEW; + +/** + * ExtJS 4+ class definition scanner. + * + * @since 3.0 + */ +public class ClassDefScanner +{ + private final Map classes = new HashMap<>(); + + private final Log log; + + private boolean warnings = false; + + private String namespace; + + public ClassDefScanner(final Log log) { + this.log = checkNotNull(log); + } + + public boolean isWarnings() { + return warnings; + } + + public void setWarnings(final boolean warnings) { + this.warnings = warnings; + } + + @Nullable + public String getNamespace() { + return namespace; + } + + public void setNamespace(@Nullable final String namespace) { + this.namespace = namespace; + } + + /** + * Scan a set of files and return a list of class definitions in dependency order. + * ie. Foo extends Bar, Bar will be before Foo. + */ + public List scan(final Scanner files) throws Exception { + checkNotNull(files); + + final boolean debug = log.isDebugEnabled(); + + // reset and scan for files + classes.clear(); + + log.debug("Finding files to scan..."); + files.scan(); + + // scan each file + String[] included = files.getIncludedFiles(); + log.debug("Scanning " + included.length + " files..."); + for (String path : included) { + File file = new File(files.getBasedir(), path); + scan(file); + } + + // TODO: Sort out how/if we want to resolve aliases, ATM this data is collected by not used + + // Resolve all references and build map with all class defs (including aliases/alts) + Map classNames = new HashMap<>(); + List allClasses = new ArrayList<>(); + for (ClassDef def : classes.values()) { + log.debug("Processing: " + def); + + classNames.put(def.getName(), def); + for (String alt : def.getAlternateClassName()) { + classNames.put(alt, def); + } + + resolve(def); + + allClasses.add(def); + } + + // Sort all classes by priority, so that higher-priority value is closer to the start and processed sooner + // ie. priority=10 is closer to the start than priority=1 + Collections.sort(allClasses, new Comparator() + { + @Override + public int compare(final ClassDef a, final ClassDef b) { + return Double.compare(b.getPriority(), a.getPriority()); + } + }); + + if (debug) { + log.debug("All classes:"); + for (ClassDef def : allClasses) { + log.debug(" " + def.getName()); + } + } + + // build the graph + DAG graph = new DAG(); + for (ClassDef def : allClasses) { + graph.addVertex(def.getName()); + for (String name : def.getDependencies()) { + // resolve dependencies which have class defs to primary class name + ClassDef dep = classNames.get(name); + if (dep != null) { + name = dep.getName(); + } + graph.addEdge(def.getName(), name); + } + } + + // display some debug information about the graph + if (debug) { + log.debug("Vertices:"); + for (Vertex v : graph.getVerticies()) { + log.debug(" " + v.getLabel()); + if (!v.getParents().isEmpty()) { + log.debug(" parents:"); + for (Vertex parent : v.getParents()) { + log.debug(" " + parent.getLabel()); + } + } + if (!v.getChildren().isEmpty()) { + log.debug(" children:"); + for (Vertex child : v.getChildren()) { + log.debug(" " + child.getLabel()); + } + } + } + } + + // result sorted list of class definitions + Set results = new LinkedHashSet<>(); + log.debug("Ordered classes:"); + // the graph contains many references, only include those which are class defs + for (String className : TopologicalSorter.sort(graph)) { + ClassDef def = classNames.get(className); + // skip duplicates (due to alt/aliases) + if (def != null && !results.contains(def)) { + log.debug(" " + def.getName()); + results.add(def); + } + } + + log.debug("Found " + results.size() + " classes"); + return new ArrayList<>(results); + } + + /** + * Resolve all references. + */ + private void resolve(final ClassDef def) { + // resolve mvc references if namespace is set + if (namespace != null) { + if (def.getMvcControllers() != null) { + for (String name : def.getMvcControllers()) { + appendMvcDependency(def, "controller", name); + } + } + if (def.getMvcModels() != null) { + for (String name : def.getMvcModels()) { + appendMvcDependency(def, "model", name); + } + } + if (def.getMvcStores() != null) { + for (String name : def.getMvcStores()) { + appendMvcDependency(def, "store", name); + } + } + if (def.getMvcViews() != null) { + for (String name : def.getMvcViews()) { + appendMvcDependency(def, "view", name); + } + } + } + } + + private void appendMvcDependency(final ClassDef def, final String type, final String name) { + if (classes.containsKey(name)) { + // already qualified name + def.getDependencies().add(name); + } + else { + if (name.contains("@")) { + // namespace is embedded: @ + String[] parts = name.split("@", 2); + def.getDependencies().add(String.format("%s.%s", parts[1], parts[0])); + } + else { + def.getDependencies().add(String.format("%s.%s.%s", namespace, type, name)); + } + } + } + + /** + * Scan the given file for class definitions and accumulate dependencies. + */ + private void scan(final File source) throws IOException { + log.debug("Scanning: " + source); + + ErrorReporter errorReporter = new LogErrorReporter(log); + + CompilerEnvirons env = new CompilerEnvirons(); + env.setErrorReporter(errorReporter); + + Parser parser = new Parser(env, errorReporter); + Reader reader = new BufferedReader(new FileReader(source)); + try { + AstRoot root = parser.parse(reader, source.getAbsolutePath(), 0); + DependencyAccumulator visitor = new DependencyAccumulator(source); + root.visit(visitor); + + // complain if no def was found in this source + if (visitor.current == null) { + log.warn("No class definition was found while processing: " + source); + } + } + finally { + reader.close(); + } + } + + private class DependencyAccumulator + implements NodeVisitor + { + private final File source; + + private ClassDef current; + + private DependencyAccumulator(final File source) { + this.source = source; + } + + /** + * Helper to report error for given node position in ast tree. + */ + private RuntimeException reportError(final AstNode node, final String message, Object... params) { + throw Context.reportRuntimeError(String.format(message, params), + source.getAbsolutePath(), + node.getLineno(), // seems to always be the line before? + node.debugPrint(), + 0 // line-offset is unknown? + ); + } + + /** + * Helper to check state and report error for given node position in ast tree. + */ + private void checkState(boolean expression, final AstNode node, final String message, Object... params) { + if (!expression) { + throw reportError(node, message, params); + } + } + + @Override + public boolean visit(final AstNode node) { + int type = node.getType(); + + switch (type) { + case CALL: { + FunctionCall call = (FunctionCall) node; + String name = nameOf(call.getTarget()); + + // if we can not determine the function name, skip + if (name == null) { + break; + } + + if (name.equals("Ext.define")) { + // complain if we found more than one class + if (current != null) { + log.warn("Found duplicate class definition in source: " + source); + } + processClassDef(call); + return true; // process children + } + else if (name.equals("Ext.create")) { + // complain if we have references to classes with Ext.create() and missing requires + if (!call.getArguments().isEmpty() && call.getArguments().get(0) instanceof StringLiteral) { + String className = nameOf(call.getArguments().get(0)); + maybeWarnMissingRequires(className, "Ext.create"); + } + return false; // ignore children + } + break; + } + + case NEW: { + // complain if we have references to classes with 'new' and missing requires + NewExpression expr = (NewExpression) node; + String className = nameOf(expr.getTarget()); + maybeWarnMissingRequires(className, "new"); + break; + } + } + + return true; // process children + } + + /** + * Find the textual name of the given node. + */ + @Nullable + private String nameOf(final AstNode node) { + if (node instanceof Name) { + return ((Name) node).getIdentifier(); + } + else if (node instanceof PropertyGet) { + PropertyGet prop = (PropertyGet) node; + return String.format("%s.%s", nameOf(prop.getTarget()), nameOf(prop.getProperty())); + } + else if (node instanceof StringLiteral) { + return ((StringLiteral) node).getValue(); + } + return null; + } + + /** + * Return string literal value. + */ + private String stringLiteral(final AstNode node) { + checkState(node instanceof StringLiteral, node, "Expected string literal only"); + //noinspection ConstantConditions + StringLiteral string = (StringLiteral) node; + return string.getValue(); + } + + /** + * Return number literal value. + */ + private double numberLiteral(final AstNode node) { + checkState(node instanceof NumberLiteral, node, "Expected number literal only"); + //noinspection ConstantConditions + NumberLiteral number = (NumberLiteral) node; + return number.getNumber(); + } + + /** + * Returns string array literal values. + */ + private List arrayStringLiteral(final AstNode node) { + checkState(node instanceof ArrayLiteral, node, "Expected array literal only"); + List result = new ArrayList<>(); + //noinspection ConstantConditions + ArrayLiteral array = (ArrayLiteral) node; + for (AstNode element : array.getElements()) { + result.add(stringLiteral(element)); + } + return result; + } + + /** + * Returns string literal or array of string literals. + * + * @see #stringLiteral(AstNode) + * @see #arrayStringLiteral(AstNode) + */ + private List stringLiterals(final AstNode node) { + // string literal or array of string literals + if (node instanceof StringLiteral) { + return Collections.singletonList(stringLiteral(node)); + } + else if (node instanceof ArrayLiteral) { + return arrayStringLiteral(node); + } + else { + throw reportError(node, "Expected string literal or array of string literal only"); + } + } + + /** + * Maybe warn if a needed class has not been required. + */ + private void maybeWarnMissingRequires(final String className, final String usage) { + if (warnings) { + if (!current.getDependencies().contains(className)) { + log.warn(String.format("Class '%s' missing requires for '%s' usage of: %s", + current.getName(), usage, className)); + } + } + } + + /** + * Process an {@code Ext.define} class definition. + */ + private void processClassDef(final FunctionCall node) { + List args = node.getArguments(); + checkState(args.size() >= 2, node, + "Invalid number of arguments for Ext.define"); // simple def or def with callback + + // class-name + checkState(args.get(0) instanceof StringLiteral, node, "Ext.define arg[0] must be string"); + String className = nameOf(args.get(0)); + + // try and avoid file-name/class-name mismatches early + sanityCheckClassName(className); + + current = new ClassDef(className, source); + classes.put(className, current); + + // class def object + checkState(args.get(1) instanceof ObjectLiteral, node, "Ext.define arg[1] must be object"); + ObjectLiteral obj = (ObjectLiteral) args.get(1); + for (ObjectProperty prop : obj.getElements()) { + String name = nameOf(prop.getLeft()); + switch (name) { + + // ExtJS core class def + + case "extend": + // string literal only + current.setExtend(stringLiteral(prop.getRight())); + break; + + case "override": + // string literal only + current.setOverride(stringLiteral(prop.getRight())); + break; + + case "@aggregate_priority": + // number only + current.setPriority(numberLiteral(prop.getRight())); + break; + + case "requires": + // array of string literals only + current.setRequires(arrayStringLiteral(prop.getRight())); + break; + + case "require": + // complain if we found 'require' this almost certainly should be 'requires' + log.warn( + String.format("Found 'require' and probably should be 'requires' in: %s#%s", source, prop.getLineno())); + break; + + case "uses": + // array of string literals only + current.setUses(arrayStringLiteral(prop.getRight())); + break; + + case "alternateClassName": { + // string literal or array of string literals + current.getAlternateClassName().addAll(stringLiterals(prop.getRight())); + break; + } + + case "alias": { + // string literal or array of string literals + current.getAlias().addAll(stringLiterals(prop.getRight())); + break; + } + + case "xtype": { + // string literal only + current.getAlias().add("widget." + stringLiteral(prop.getRight())); + break; + } + + case "mixins": { + // array of strings, or object + List mixins = new ArrayList<>(); + if (prop.getRight() instanceof ArrayLiteral) { + mixins.addAll(arrayStringLiteral(prop.getRight())); + } + else if (prop.getRight() instanceof ObjectLiteral) { + ObjectLiteral child = (ObjectLiteral) prop.getRight(); + for (ObjectProperty element : child.getElements()) { + mixins.add(stringLiteral(element.getRight())); + } + } + else { + throw reportError(prop.getRight(), "Expected array or object literal only"); + } + current.setMixins(mixins); + break; + } + } + + // Additional stuff we have to detect for ExtJS MVC support + + if (isExtends("Ext.app.Application")) { + // class looks like an application, process more fields + switch (name) { + case "controllers": + // array of string literals only + current.setMvcControllers(arrayStringLiteral(prop.getRight())); + break; + + case "models": + // array of string literals only + current.setMvcModels(arrayStringLiteral(prop.getRight())); + break; + + case "stores": + // array of string literals only + current.setMvcStores(arrayStringLiteral(prop.getRight())); + break; + + case "views": + // array of string literals only + current.setMvcViews(arrayStringLiteral(prop.getRight())); + break; + } + } + else if (isMvcClass("controller")) { + // class looks like a controller, process more fields + switch (name) { + case "models": + // array of string literals only + current.setMvcModels(arrayStringLiteral(prop.getRight())); + break; + + case "stores": + // array of string literals only + current.setMvcStores(arrayStringLiteral(prop.getRight())); + break; + + case "views": + // array of string literals only + current.setMvcViews(arrayStringLiteral(prop.getRight())); + break; + } + } + else if (isMvcClass("store")) { + // class looks like a store, process more fields + switch (name) { + case "model": + // string literal only + current.getDependencies().add(stringLiteral(prop.getRight())); + break; + } + } + } + } + + /** + * Complain if classes are defined with wrong names, or in wrong files. + */ + private void sanityCheckClassName(final String className) { + String found = source.toURI().getPath(); + String expected = className.replace('.', '/') + ".js"; + if (!found.endsWith(expected)) { + log.warn(String.format("Expected class '%s' to be defined in filename '%s', but was defined in: %s", + className, expected, found)); + } + } + + /** + * Highly hackish means to determine given class-name is a MVC type. + * Requires namespace to be configured. + */ + private boolean isMvcClass(final String type) { + return namespace != null && current.getName().startsWith(String.format("%s.%s.", namespace, type)); + } + + /** + * Check if current class extends (directly) given class-name. + */ + private boolean isExtends(final String className) { + String superClass = current.getExtend(); + return superClass != null && superClass.equals(className); + } + } +} diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/FileAppender.java b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/FileAppender.java new file mode 100644 index 0000000000000000000000000000000000000000..49dc6f77acffbcce282c6fdf789609c1815fcf32 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/FileAppender.java @@ -0,0 +1,70 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Writer; + +import org.apache.maven.plugin.logging.Log; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * File appender. + * + * @since 3.0 + */ +public class FileAppender +{ + protected final Log log; + + protected final Writer output; + + public FileAppender(final Log log, final Writer output) { + this.log = checkNotNull(log); + this.output = checkNotNull(output); + } + + protected File source; + + protected long linenum; + + public void append(final File source) throws IOException { + this.source = checkNotNull(source); + this.linenum = 0; + + log.debug("Appending: " + source); + + BufferedReader input = new BufferedReader(new FileReader(source)); + try { + String line; + while ((line = input.readLine()) != null) { + linenum++; + append(line); + } + } + finally { + input.close(); + } + + output.write('\n'); + } + + protected void append(final String line) throws IOException { + output.write(line); + output.write('\n'); + } +} diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/LogErrorReporter.java b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/LogErrorReporter.java new file mode 100644 index 0000000000000000000000000000000000000000..d94fdc620166710d81c3eb43bd40a5564eb1e030 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/LogErrorReporter.java @@ -0,0 +1,53 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import org.apache.maven.plugin.logging.Log; +import org.mozilla.javascript.ErrorReporter; +import org.mozilla.javascript.EvaluatorException; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Mojo {@link Log} adapting {@link ErrorReporter}. + * + * @since 3.0 + */ +public class LogErrorReporter + implements ErrorReporter +{ + private final Log log; + + public LogErrorReporter(final Log log) { + this.log = checkNotNull(log); + } + + private String format(final String message, final String sourceName, final int line, final String lineSource, final int lineOffset) { + return String.format("%s (%s#%d:%d): %s", message, sourceName, line, lineOffset, lineSource); + } + + @Override + public void warning(final String message, final String sourceName, final int line, final String lineSource, final int lineOffset) { + log.warn(format(message, sourceName, line, lineSource, lineOffset)); + } + + @Override + public void error(final String message, final String sourceName, final int line, final String lineSource, final int lineOffset){ + log.error(format(message, sourceName, line, lineSource, lineOffset)); + } + + @Override + public EvaluatorException runtimeError(final String message, final String sourceName, final int line, final String lineSource, final int lineOffset) { + return new EvaluatorException(message, sourceName, line, lineSource, lineOffset); + } +} diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/OmissionFileAppender.java b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/OmissionFileAppender.java new file mode 100644 index 0000000000000000000000000000000000000000..60ae2eb58810f2c602f04179480ec244452f3af2 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/OmissionFileAppender.java @@ -0,0 +1,113 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +import org.apache.maven.plugin.logging.Log; + +/** + * Omission token aware file appender. + * + * @since 3.0 + */ +public class OmissionFileAppender + extends FileAppender +{ + public static final String IF_START_PREFIX = "// flags = new HashMap<>(); + + public OmissionFileAppender(final Log log, + final Writer output, + @Nullable final Map rawFlags) + { + super(log, output); + if (rawFlags != null) { + parseFlags(rawFlags); + } + } + + public Map getFlags() { + return flags; + } + + private void parseFlags(final Map rawFlags) { + for (Entry entry : rawFlags.entrySet()) { + boolean flag = Boolean.parseBoolean(entry.getValue()); + flags.put(entry.getKey(), flag); + } + log.debug("Omit flags: " + flags); + } + + private boolean omit; + + @Override + public void append(final File source) throws IOException { + this.omit = false; + + super.append(source); + + // complain if still in omit mode at the end of the file + if (omit) { + log.warn("Missing omit end token: " + source); + } + } + + @Override + protected void append(final String line) throws IOException { + // check for omission start and stop tokens + String trimmedLine = line.trim(); + if (isOmitStart(trimmedLine)) { + // complain if already in omit mode and we found a start token (nesting is not allowed) + if (omit) { + log.warn("Unexpected omit start token: " + source + "#" + linenum); + } + omit = true; + } + else if (isOmitStop(trimmedLine)) { + omit = false; + } + else if (omit) { + output.write("//"); + } + + super.append(line); + } + + private boolean isOmitStart(final String line) { + // assumes line is trimmed + if (line.startsWith(IF_START_PREFIX) && line.endsWith(IF_START_SUFFIX)) { + String flagName = line.substring(IF_START_PREFIX.length(), line.length() - IF_START_SUFFIX.length()); + Boolean flag = flags.get(flagName); + return flag != null && flag; + } + return false; + } + + private boolean isOmitStop(final String line) { + // assumes line is trimmed + return line.equals(IF_END); + } +} diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/java/org/sonatype/nexus/extjs/ClassDefScannerTest.java b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/java/org/sonatype/nexus/extjs/ClassDefScannerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..25574171e53313a2c67c23d8e9484bbd62ff4c7b --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/java/org/sonatype/nexus/extjs/ClassDefScannerTest.java @@ -0,0 +1,145 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import java.io.File; +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugin.logging.SystemStreamLog; +import org.codehaus.plexus.util.DirectoryScanner; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link ClassDefScanner}. + */ +public class ClassDefScannerTest + extends TestSupport +{ + private ClassDefScanner scanner; + + @Before + public void setUp() throws Exception { + Log log = new SystemStreamLog() + { + @Override + public boolean isDebugEnabled() { + return true; + } + }; + this.scanner = new ClassDefScanner(log); + } + + private List scan(final File basedir) throws Exception { + DirectoryScanner files = new DirectoryScanner(); + files.setBasedir(basedir); + files.setIncludes(new String[]{"**/*.js"}); + + List classes = scanner.scan(files); + log("Classes:"); + for (ClassDef def : classes) { + log(" {} [priority: {}]", def.getName(), def.getPriority()); + for (String dep : def.getDependencies()) { + log(" + {}", dep); + } + } + return classes; + } + + @Test + public void basicOrder() throws Exception { + File basedir = util.resolveFile("src/test/resources/basic"); + List classes = scan(basedir); + + assertThat(classes, hasSize(3)); + + ClassDef baz = classes.get(0); + assertThat(baz.getName(), is("Baz")); + + ClassDef bar = classes.get(1); + assertThat(bar.getName(), is("Bar")); + assertThat(bar.getDependencies(), contains("Baz")); + + ClassDef foo = classes.get(2); + assertThat(foo.getName(), is("Foo")); + assertThat(foo.getDependencies(), contains("Bar")); + } + + @Test + public void priorityOrder() throws Exception { + File basedir = util.resolveFile("src/test/resources/priority"); + List classes = scan(basedir); + + assertThat(classes, hasSize(5)); + + ClassDef poo = classes.get(0); + assertThat(poo.getName(), is("Poo")); + + ClassDef ick = classes.get(1); + assertThat(ick.getName(), is("Ick")); + assertThat(ick.getDependencies(), contains("Poo")); + + ClassDef baz = classes.get(2); + assertThat(baz.getName(), is("Baz")); + + ClassDef bar = classes.get(3); + assertThat(bar.getName(), is("Bar")); + assertThat(bar.getDependencies(), contains("Baz")); + + ClassDef foo = classes.get(4); + assertThat(foo.getName(), is("Foo")); + assertThat(foo.getDependencies(), contains("Bar")); + } + + @Test + public void altClassNameOrder() throws Exception { + File basedir = util.resolveFile("src/test/resources/altclassname"); + List classes = scan(basedir); + + assertThat(classes, hasSize(3)); + + ClassDef baz = classes.get(0); + assertThat(baz.getName(), is("Baz")); + + ClassDef bar = classes.get(1); + assertThat(bar.getName(), is("Bar")); + assertThat(bar.getAlternateClassName(), containsInAnyOrder("Test.Bar", "Other.Bar")); + assertThat(bar.getDependencies(), contains("Baz")); + + ClassDef foo = classes.get(2); + assertThat(foo.getName(), is("Foo")); + assertThat(foo.getAlternateClassName(), containsInAnyOrder("Test.Foo")); + assertThat(foo.getDependencies(), contains("Test.Bar")); + } + + @Test + public void mvcOrder() throws Exception { + File basedir = util.resolveFile("src/test/resources/mvc"); + scanner.setNamespace("Test"); + List classes = scan(basedir); + + assertThat(classes, hasSize(5)); + + assertThat(classes.get(4).getName(), is("Test.Application")); + assertThat(classes.get(4).getDependencies(), containsInAnyOrder("Ext.app.Application", "Test.controller.Fun")); + } +} diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Bar.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Bar.js new file mode 100644 index 0000000000000000000000000000000000000000..c8ce309d5fd7533be011e99c9848251ee6d2f151 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Bar.js @@ -0,0 +1,9 @@ +Ext.define('Bar', { + alternateClassName: [ + 'Test.Bar', + 'Other.Bar' + ], + requires: [ + 'Baz' + ] +}); diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Baz.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Baz.js new file mode 100644 index 0000000000000000000000000000000000000000..b1f7b6ae1e222646991132f9cf6a8ec63c9962f7 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Baz.js @@ -0,0 +1 @@ +Ext.define('Baz', {}); \ No newline at end of file diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Foo.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Foo.js new file mode 100644 index 0000000000000000000000000000000000000000..efaa030112cc017bc47076754562a17471d7af0e --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Foo.js @@ -0,0 +1,6 @@ +Ext.define('Foo', { + alternateClassName: 'Test.Foo', + requires: [ + 'Test.Bar' + ] +}); diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/basic/Bar.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/basic/Bar.js new file mode 100644 index 0000000000000000000000000000000000000000..985ef1fee81b116b59e25acf78c3702d863bf011 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/basic/Bar.js @@ -0,0 +1,5 @@ +Ext.define('Bar', { + requires: [ + 'Baz' + ] +}); diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/basic/Baz.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/basic/Baz.js new file mode 100644 index 0000000000000000000000000000000000000000..b1f7b6ae1e222646991132f9cf6a8ec63c9962f7 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/basic/Baz.js @@ -0,0 +1 @@ +Ext.define('Baz', {}); \ No newline at end of file diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/basic/Foo.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/basic/Foo.js new file mode 100644 index 0000000000000000000000000000000000000000..562d52fcf420709bd48583ee5d5eced143ffd10a --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/basic/Foo.js @@ -0,0 +1,5 @@ +Ext.define('Foo', { + requires: [ + 'Bar' + ] +}); diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/Application.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/Application.js new file mode 100644 index 0000000000000000000000000000000000000000..1e3aaf41d07a06425c653acb92706382ede9518d --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/Application.js @@ -0,0 +1,7 @@ +Ext.define('Test.Application', { + extend: 'Ext.app.Application', + + controllers: [ + 'Fun' + ] +}); diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/controller/Fun.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/controller/Fun.js new file mode 100644 index 0000000000000000000000000000000000000000..715fe24ffadfa95967a54293d7f853dc5d447dad --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/controller/Fun.js @@ -0,0 +1,10 @@ +Ext.define('Test.controller.Fun', { + extend: 'Ext.app.Controller', + + stores: [ + 'Fun' + ], + views: [ + 'Fun' + ] +}); diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/model/Fun.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/model/Fun.js new file mode 100644 index 0000000000000000000000000000000000000000..27286dae349a4f266608619ed2e16abd6d3b60ab --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/model/Fun.js @@ -0,0 +1,3 @@ +Ext.define('Test.model.Fun', { + extend: 'Ext.data.Model' +}); diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/store/Fun.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/store/Fun.js new file mode 100644 index 0000000000000000000000000000000000000000..ce394d17b905ba24194a7eff8530fa7af9a91948 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/store/Fun.js @@ -0,0 +1,4 @@ +Ext.define('Test.store.Fun', { + extend: 'Ext.data.Store', + model: 'Test.model.Fun' +}); diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/view/Fun.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/view/Fun.js new file mode 100644 index 0000000000000000000000000000000000000000..be94030587a25472b1483d8f503e4d8bda9dfbce --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/mvc/view/Fun.js @@ -0,0 +1,3 @@ +Ext.define('Test.view.Fun', { + extend: 'Ext.panel.Panel' +}); diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Bar.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Bar.js new file mode 100644 index 0000000000000000000000000000000000000000..985ef1fee81b116b59e25acf78c3702d863bf011 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Bar.js @@ -0,0 +1,5 @@ +Ext.define('Bar', { + requires: [ + 'Baz' + ] +}); diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Baz.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Baz.js new file mode 100644 index 0000000000000000000000000000000000000000..b1f7b6ae1e222646991132f9cf6a8ec63c9962f7 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Baz.js @@ -0,0 +1 @@ +Ext.define('Baz', {}); \ No newline at end of file diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Foo.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Foo.js new file mode 100644 index 0000000000000000000000000000000000000000..562d52fcf420709bd48583ee5d5eced143ffd10a --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Foo.js @@ -0,0 +1,5 @@ +Ext.define('Foo', { + requires: [ + 'Bar' + ] +}); diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Ick.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Ick.js new file mode 100644 index 0000000000000000000000000000000000000000..6d27a8c6e49f8520b21e87a0a57c8bbb9c8e3567 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Ick.js @@ -0,0 +1,6 @@ +Ext.define('Ick', { + '@aggregate_priority': 10, + requires: [ + 'Poo' + ] +}); diff --git a/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Poo.js b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Poo.js new file mode 100644 index 0000000000000000000000000000000000000000..b8b027779f0d46e1833a68f99d19336e7b562d76 --- /dev/null +++ b/thirdparty-bundles/buildsupport/extjs-maven-plugin/src/test/resources/priority/Poo.js @@ -0,0 +1,3 @@ +Ext.define('Poo', { + '@aggregate_priority': 9 +}); diff --git a/thirdparty-bundles/buildsupport/goodies/pom.xml b/thirdparty-bundles/buildsupport/goodies/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..a88985ab316c2b3e96613c51a6265d7dc4e9187f --- /dev/null +++ b/thirdparty-bundles/buildsupport/goodies/pom.xml @@ -0,0 +1,76 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-goodies + ${project.groupId}:${project.artifactId} + pom + + + 2.2.4 + + + + + + + org.sonatype.goodies + goodies-common + ${goodies.version} + + + + org.sonatype.goodies + goodies-httpfixture + ${goodies.version} + + + + org.sonatype.goodies + goodies-i18n + ${goodies.version} + + + + org.sonatype.goodies + goodies-lifecycle + ${goodies.version} + + + + org.sonatype.goodies + goodies-prefs + ${goodies.version} + + + + org.sonatype.goodies + goodies-testsupport + ${goodies.version} + + + + + + diff --git a/thirdparty-bundles/buildsupport/groovy/pom.xml b/thirdparty-bundles/buildsupport/groovy/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..de5e555b30a9a03d490392774aba5eaa58a62c28 --- /dev/null +++ b/thirdparty-bundles/buildsupport/groovy/pom.xml @@ -0,0 +1,96 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-groovy + ${project.groupId}:${project.artifactId} + pom + + + 2.4.11 + 1.9.7 + 1.0-groovy-2.4 + + + + + + + + + org.codehaus.groovy + groovy-all + ${groovy.version} + + + + org.codehaus.groovy + groovy + ${groovy.version} + + + + org.apache.ant + ant + ${ant.version} + + + + org.apache.ant + ant-launcher + ${ant.version} + + + + org.spockframework + spock-core + ${spock.version} + + + + org.spockframework + spock-guice + ${spock.version} + + + + cglib + cglib-nodep + 3.2.0 + + + + org.objenesis + objenesis + 2.2 + + + + + + diff --git a/thirdparty-bundles/buildsupport/guice/pom.xml b/thirdparty-bundles/buildsupport/guice/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..0f2cc3802dd3e2095c83f653330ba27219cc017e --- /dev/null +++ b/thirdparty-bundles/buildsupport/guice/pom.xml @@ -0,0 +1,109 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-guice + ${project.groupId}:${project.artifactId} + pom + + + 0.3.2 + 4.1.0 + + + + + + javax.annotation + javax.annotation-api + 1.2 + + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.javax-inject + 1_2 + + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.aopalliance + 1.0_6 + + + + javax.enterprise + cdi-api + 1.2 + + + javax.inject + javax.inject + + + + + + com.google.inject + guice + ${guice.version} + + + javax.inject + javax.inject + + + aopalliance + aopalliance + + + + + + com.google.inject.extensions + guice-servlet + ${guice.version} + + + + com.google.inject.extensions + guice-multibindings + ${guice.version} + + + + com.google.inject.extensions + guice-assistedinject + ${guice.version} + + + + org.eclipse.sisu + org.eclipse.sisu.inject + ${eclipse-sisu.version} + + + + + diff --git a/thirdparty-bundles/buildsupport/httpclient/pom.xml b/thirdparty-bundles/buildsupport/httpclient/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..f70f051bf5a00939e62b6d100bf2ed4503f0f8fb --- /dev/null +++ b/thirdparty-bundles/buildsupport/httpclient/pom.xml @@ -0,0 +1,60 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-httpclient + ${project.groupId}:${project.artifactId} + pom + + + + + + org.apache.httpcomponents + httpclient + 4.5.2 + + + commons-logging + commons-logging + + + + + + org.apache.httpcomponents + httpmime + 4.5.2 + + + + org.apache.httpcomponents + httpcore + 4.4.6 + + + + + + diff --git a/thirdparty-bundles/buildsupport/internal/pom.xml b/thirdparty-bundles/buildsupport/internal/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..3a7be49bd90435757c0cf483df23fcb5fbd4d7bf --- /dev/null +++ b/thirdparty-bundles/buildsupport/internal/pom.xml @@ -0,0 +1,192 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-internal + ${project.groupId}:${project.artifactId} + pom + + + 1.4.3 + 1.7.1-01 + 1.35.0-02 + 2.8.0-01 + + + + + + + + + com.sonatype.licensing + license-bundle + ${sonatype-licensing.version} + + + aopalliance + aopalliance + + + org.sonatype.sisu + sisu-guice + + + xmlpull + xmlpull + + + xpp3 + xpp3_min + + + + + + com.sonatype.licensing + license-creator + ${sonatype-licensing.version} + + + + com.sonatype.licensing + license-extension + ${sonatype-licensing.version} + + + + + + com.sonatype.clm + com.sonatype.clm.dto.model + ${clm.dto.model.version} + + + + com.sonatype.insight.scan + insight-scanner-archive + ${insight-scanner.version} + + + + com.sonatype.insight.scan + insight-scanner-core + ${insight-scanner.version} + + + + com.sonatype.insight.scan + insight-scanner-model-io + ${insight-scanner.version} + + + + com.sonatype.insight.scan + insight-scanner-model + ${insight-scanner.version} + + + + com.sonatype.insight.scan + insight-test-reverse-proxy + ${insight-scanner.version} + + + + com.sonatype.insight.brain + hds-mock-server + ${insight-brain.version} + + + + com.sonatype.insight.brain + insight-rm-common + ${insight-brain.version} + + + + com.sonatype.insight.brain + insight-brain-data + ${insight-brain.version} + tests + + + * + * + + + + + + com.sonatype.insight.brain + insight-brain-service + ${insight-brain.version} + + + + junit + junit + + + com.sonatype.licensing + license-bundle + + + + + + com.sonatype.insight.brain + insight-brain-service + ${insight-brain.version} + tests + + + * + * + + + + + + + + com.sonatype.analytics + analytics-client + 1.1.0 + + + + com.sonatype.analytics + analytics-assembly + bundle + zip + 1.1.0 + + + + + + diff --git a/thirdparty-bundles/buildsupport/jetty/pom.xml b/thirdparty-bundles/buildsupport/jetty/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..053cc0a170788446debf0577dd1172a63ef0f4ae --- /dev/null +++ b/thirdparty-bundles/buildsupport/jetty/pom.xml @@ -0,0 +1,147 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-jetty + ${project.groupId}:${project.artifactId} + pom + + + 9.4.8.v20171121 + + + + + + javax.servlet + javax.servlet-api + 3.1.0 + + + + org.eclipse.jetty + jetty-webapp + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-continuation + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-servlet + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-servlet + tests + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-security + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-server + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-deploy + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-client + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-util + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-util-ajax + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-servlets + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-proxy + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-http + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-io + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-xml + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-jmx + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-rewrite + ${eclipse-jetty.version} + + + + + diff --git a/thirdparty-bundles/buildsupport/jruby/pom.xml b/thirdparty-bundles/buildsupport/jruby/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..68ed0995225e125c835fa8b9d27d810247b3ac4f --- /dev/null +++ b/thirdparty-bundles/buildsupport/jruby/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-jruby + ${project.groupId}:${project.artifactId} + pom + + + + + + org.jruby + jruby-complete + 9.0.5.0 + + + + org.yaml + snakeyaml + 1.18 + + + + + + diff --git a/thirdparty-bundles/buildsupport/logging/pom.xml b/thirdparty-bundles/buildsupport/logging/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..2cbd8d15cdd9cd0541885520aac7259ed0005785 --- /dev/null +++ b/thirdparty-bundles/buildsupport/logging/pom.xml @@ -0,0 +1,128 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-logging + ${project.groupId}:${project.artifactId} + pom + + + 1.7.25 + 1.2.2 + 1.10.0 + + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + org.slf4j + slf4j-simple + ${slf4j.version} + + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + + + + org.slf4j + log4j-over-slf4j + ${slf4j.version} + + + + org.slf4j + jul-to-slf4j + ${slf4j.version} + + + + ch.qos.logback + logback-core + ${logback.version} + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + ch.qos.logback + logback-access + ${logback.version} + + + + org.ops4j.pax.logging + pax-logging-api + ${pax-logging.version} + + + + org.ops4j.pax.logging + pax-logging-logback + ${pax-logging.version} + + + ch.qos.logback + logback-core + + + ch.qos.logback + logback-classic + + + ch.qos.logback.contrib + logback-jackson + + + ch.qos.logback.contrib + logback-json-core + + + ch.qos.logback.contrib + logback-json-classic + + + + + + uk.org.lidalia + sysout-over-slf4j + 1.0.2 + + + + + diff --git a/thirdparty-bundles/buildsupport/maven/pom.xml b/thirdparty-bundles/buildsupport/maven/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..87e651d475518dc7c072393ecb6ec0369c675216 --- /dev/null +++ b/thirdparty-bundles/buildsupport/maven/pom.xml @@ -0,0 +1,157 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-maven + ${project.groupId}:${project.artifactId} + pom + + + 3.3.3 + 1.0.2.v20150114 + + + + + + org.apache.maven + maven-aether-provider + ${apache-maven.version} + + + + org.apache.maven + maven-model + ${apache-maven.version} + + + + org.apache.maven + maven-repository-metadata + ${apache-maven.version} + + + + org.apache.maven + maven-artifact + ${apache-maven.version} + + + + org.apache.maven + maven-settings + ${apache-maven.version} + + + + org.apache.maven + maven-settings-builder + ${apache-maven.version} + + + + org.apache.maven + maven-core + ${apache-maven.version} + + + + org.apache.maven + maven-plugin-api + ${apache-maven.version} + + + + org.apache.maven + maven-compat + ${apache-maven.version} + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.4 + + + + org.apache.maven.shared + maven-shared-utils + 0.8 + + + + org.apache.maven.shared + maven-verifier + 1.6 + + + junit + junit-dep + + + junit + junit + + + + + + org.eclipse.aether + aether-api + ${aether.version} + + + + org.eclipse.aether + aether-util + ${aether.version} + + + + org.eclipse.aether + aether-spi + ${aether.version} + + + + org.eclipse.aether + aether-impl + ${aether.version} + + + + org.apache.maven.indexer + indexer-reader + 5.1.2-816025a + + + + org.apache.maven.archetype + archetype-catalog + 2.4 + + + + + diff --git a/thirdparty-bundles/buildsupport/metrics/pom.xml b/thirdparty-bundles/buildsupport/metrics/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..3c4c193350b6f4278460a7d0ad416d0a0b226c0f --- /dev/null +++ b/thirdparty-bundles/buildsupport/metrics/pom.xml @@ -0,0 +1,92 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-metrics + ${project.groupId}:${project.artifactId} + pom + + + 3.0.2 + + + + + + com.codahale.metrics + metrics-core + ${metrics.version} + + + + com.codahale.metrics + metrics-annotation + ${metrics.version} + + + + com.palominolabs.metrics + metrics-guice + ${metrics.version} + + + + com.codahale.metrics + metrics-servlets + ${metrics.version} + + + + com.codahale.metrics + metrics-jetty9 + ${metrics.version} + + + org.eclipse.jetty + jetty-server + + + + + + com.codahale.metrics + metrics-httpclient + ${metrics.version} + + + + com.codahale.metrics + metrics-logback + ${metrics.version} + + + + com.codahale.metrics + metrics-servlet + ${metrics.version} + + + + + diff --git a/thirdparty-bundles/buildsupport/osgi/pom.xml b/thirdparty-bundles/buildsupport/osgi/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..6d1de71c0734f19f6927df134ca1bb874241fda6 --- /dev/null +++ b/thirdparty-bundles/buildsupport/osgi/pom.xml @@ -0,0 +1,119 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-osgi + ${project.groupId}:${project.artifactId} + pom + + + 4.0.9 + + + + + + org.osgi + org.osgi.core + 6.0.0 + + + + org.osgi + org.osgi.compendium + 5.0.0 + + + + org.apache.felix + org.apache.felix.framework + 5.6.2 + + + + org.apache.karaf + org.apache.karaf.main + ${karaf.version} + + + + org.apache.karaf.bundle + org.apache.karaf.bundle.core + ${karaf.version} + + + + org.apache.karaf.features + framework + ${karaf.version} + kar + + + + org.apache.karaf.features + standard + ${karaf.version} + features + xml + + + + org.eclipse.equinox + org.eclipse.equinox.region + 1.1.0.v20120522-1841 + + + + org.apache.karaf.features + org.apache.karaf.features.core + ${karaf.version} + + + + org.apache.karaf.jaas + org.apache.karaf.jaas.boot + ${karaf.version} + + + + org.apache.karaf.jaas + org.apache.karaf.jaas.config + ${karaf.version} + + + + org.apache.karaf.shell + org.apache.karaf.shell.core + ${karaf.version} + + + + org.apache.karaf.shell + org.apache.karaf.shell.commands + ${karaf.version} + + + + + diff --git a/thirdparty-bundles/buildsupport/other/pom.xml b/thirdparty-bundles/buildsupport/other/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..ecd6ae71d8873ec2ed7d4bd416d80c5cc0de81ed --- /dev/null +++ b/thirdparty-bundles/buildsupport/other/pom.xml @@ -0,0 +1,316 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-other + ${project.groupId}:${project.artifactId} + pom + + + 1.11.201 + + + + + + com.google.guava + guava + 21.0 + + + + com.google.code.findbugs + jsr305 + 3.0.0 + + + + javax.mail + mail + 1.4.7 + + + + org.apache.velocity + velocity + 1.7 + + + + joda-time + joda-time + 2.9.5 + + + + org.hdrhistogram + HdrHistogram + 2.1.6 + + + + org.apache.tika + tika-core + 1.14 + + + + org.codehaus.mojo + animal-sniffer-annotations + 1.14 + + + + eu.bitwalker + UserAgentUtils + 1.19 + + + + javax.validation + validation-api + 1.1.0.Final + + + + javax.el + javax.el-api + 2.2.5 + + + + org.glassfish.web + javax.el + 2.2.6 + + + + org.hibernate + hibernate-validator + 5.1.2.Final + + + + org.quartz-scheduler + quartz + 2.2.2 + + + + org.sonatype.sisu + sisu-odata4j + 0.0.7 + + + + org.elasticsearch + elasticsearch + 2.4.3 + + + + com.vividsolutions + jts + 1.13 + + + + javax.interceptor + javax.interceptor-api + 1.2 + + + + javax.transaction + javax.transaction-api + 1.2 + + + + com.thoughtworks.paranamer + paranamer + 2.8 + + + + org.codehaus.plexus + plexus-interpolation + 1.24 + + + + org.codehaus.plexus + plexus-utils + 3.0.24 + + + + + + + + + + + org.apache.geronimo.specs + geronimo-jcache_1.0_spec + 1.0-alpha-1 + + + + org.ehcache + ehcache + 3.2.1 + + + + com.squareup + tape + 1.2.3 + + + + org.freemarker + freemarker + 2.3.23 + + + + org.mozilla + rhino + 1.7.7 + + + + com.atlassian.crowd.client + atlassian-crowd-rest-client + 1.1 + + + + commons-httpclient + commons-httpclient + + + + commons-lang + commons-lang + + + commons-codec + commons-codec + + + + + + se.sawano.java + alphanumeric-comparator + 1.4.1 + + + + org.redline-rpm + redline + 1.2.5 + + + + com.google.jimfs + jimfs + 1.1 + + + + com.amazonaws + aws-java-sdk-core + ${aws-java-sdk.version} + + + commons-logging + commons-logging + + + + + + com.amazonaws + aws-java-sdk-s3 + ${aws-java-sdk.version} + + + commons-logging + commons-logging + + + + + + com.amazonaws + aws-java-sdk-sts + ${aws-java-sdk.version} + + + commons-logging + commons-logging + + + + + + com.amazonaws + aws-java-sdk-kms + ${aws-java-sdk.version} + + + commons-logging + commons-logging + + + + + + software.amazon.ion + ion-java + 1.0.2 + + + + com.amazonaws + jmespath-java + ${aws-java-sdk.version} + + + commons-logging + commons-logging + + + + + + + + diff --git a/thirdparty-bundles/buildsupport/pom.xml b/thirdparty-bundles/buildsupport/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..51727ffdb698ef6fe7484f7b3e1ef48d1765ceb2 --- /dev/null +++ b/thirdparty-bundles/buildsupport/pom.xml @@ -0,0 +1,66 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-parent + 3.8.0-02 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + ${project.groupId}:${project.artifactId} + pom + + + commons + db + goodies + groovy + guice + httpclient + internal + jetty + jruby + logging + maven + metrics + osgi + other + rest + security + testing + ui + + + all + + extjs-maven-plugin + + + + + include-scripts + + scripts + + + + + diff --git a/thirdparty-bundles/buildsupport/rest/pom.xml b/thirdparty-bundles/buildsupport/rest/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..320f2d00af966c62520d2c71276d643cfa5ba5f3 --- /dev/null +++ b/thirdparty-bundles/buildsupport/rest/pom.xml @@ -0,0 +1,248 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-rest + ${project.groupId}:${project.artifactId} + pom + + + 2.9.2 + 3.1.3.Final + 1.5.12 + + + + + + + javax.ws.rs + javax.ws.rs-api + 2.0 + + + + org.jsoup + jsoup + 1.9.1 + + + + + com.thoughtworks.xstream + xstream + 1.4.10 + + + xmlpull + xmlpull + + + xpp3 + xpp3_min + + + + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.xpp3 + 1.1.4c_7 + + + + com.sun.xml.fastinfoset + FastInfoset + 1.2.13 + + + + com.ning + compress-lzf + 1.0.2 + + + + io.netty + netty + 3.10.6.Final + + + + io.swagger + swagger-annotations + ${swagger.version} + + + + io.swagger + swagger-jaxrs + ${swagger.version} + + + + org.webjars + swagger-ui + 2.2.10 + + + + + + org.jboss.resteasy + resteasy-jaxrs + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-jaxb-provider + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-multipart-provider + ${resteasy.version} + + + commons-logging + commons-logging + + + + + + org.jboss.resteasy + resteasy-atom-provider + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-client + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-jackson2-provider + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-validator-provider-11 + ${resteasy.version} + + + + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson2.version} + + + + com.fasterxml.jackson.core + jackson-core + ${jackson2.version} + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson2.version} + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + ${jackson2.version} + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-base + ${jackson2.version} + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-smile-provider + ${jackson2.version} + + + + com.fasterxml.jackson.module + jackson-module-jaxb-annotations + ${jackson2.version} + + + + com.fasterxml.jackson.module + jackson-module-parameter-names + ${jackson2.version} + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-cbor + ${jackson2.version} + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-smile + ${jackson2.version} + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + ${jackson2.version} + + + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson2.version} + + + + com.fasterxml.jackson.datatype + jackson-datatype-guava + ${jackson2.version} + + + + + diff --git a/thirdparty-bundles/buildsupport/scripts/fixcrlf.groovy b/thirdparty-bundles/buildsupport/scripts/fixcrlf.groovy new file mode 100644 index 0000000000000000000000000000000000000000..0716253ee3fd1b383bd1486d7d0adbe8ed52d7bf --- /dev/null +++ b/thirdparty-bundles/buildsupport/scripts/fixcrlf.groovy @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +def ant = new AntBuilder() + +def basedir = System.getProperty('basedir', '.') as File +def fixlast = System.getProperty('fixlast', 'false') + +println "Normalizing line-endings to LF in: ${basedir.canonicalFile}" +println " fixlast: $fixlast" + +ant.fixcrlf( + srcDir: basedir, + eol: 'lf', + eof: 'remove', + fixlast: fixlast +) { + include(name: '**/*.java') + include(name: '**/*.groovy') + include(name: '**/*.js') + include(name: '**/*.css') + include(name: '**/*.vm') + include(name: '**/*.tpl') + include(name: '**/*.properties') + include(name: '**/*.xml') + include(name: '**/*.yml') + include(name: '**/*.txt') +} diff --git a/thirdparty-bundles/buildsupport/scripts/gobble.groovy b/thirdparty-bundles/buildsupport/scripts/gobble.groovy new file mode 100644 index 0000000000000000000000000000000000000000..fc4c3ef3a3ef2c5f1b3a654934716d2102f99d70 --- /dev/null +++ b/thirdparty-bundles/buildsupport/scripts/gobble.groovy @@ -0,0 +1,56 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +// +// Helper to consume the output of `mvn versions:display-dependency-updates versions:display-plugin-updates` +// and spit out unique version upgrades available. +// +// Usage: +// +// mvn versions:display-dependency-updates versions:display-plugin-updates | groovy buildsupport/scripts/gobble.groovy +// + +def lines = System.in.readLines() + +def iter = lines.iterator() +while (iter.hasNext()) { + def line = iter.next() + if (line.startsWith('[INFO] --- versions-maven-plugin')) { + break + } +} + +def chomp(line) { + return line[6..-1].trim() +} + +def updates = new HashSet() +while (iter.hasNext()) { + def line = chomp(iter.next()) + + if (line.endsWith(' ...')) { + line = line + ' ' + chomp(iter.next()) + } + + if (line.contains('->')) { + def parts = line.split('\\s') + def update="${parts[0]}: ${parts[2]} -> ${parts[4]}" + updates << update + } +} + +updates = new ArrayList(updates) +updates.sort() + +for (update in updates) { + println update +} \ No newline at end of file diff --git a/thirdparty-bundles/buildsupport/scripts/i18nVerifier.groovy b/thirdparty-bundles/buildsupport/scripts/i18nVerifier.groovy new file mode 100644 index 0000000000000000000000000000000000000000..69e9d718ee74a694e374790605b8673618ff9323 --- /dev/null +++ b/thirdparty-bundles/buildsupport/scripts/i18nVerifier.groovy @@ -0,0 +1,193 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +import groovy.io.FileType +import groovy.json.JsonOutput +import groovy.util.logging.Slf4j +import jdk.nashorn.internal.ir.CallNode +import jdk.nashorn.internal.ir.FunctionNode +import jdk.nashorn.internal.ir.LexicalContext +import jdk.nashorn.internal.ir.Node +import jdk.nashorn.internal.ir.ObjectNode +import jdk.nashorn.internal.ir.PropertyNode +import jdk.nashorn.internal.ir.visitor.NodeVisitor +import jdk.nashorn.internal.parser.Parser +import jdk.nashorn.internal.runtime.Context +import jdk.nashorn.internal.runtime.ErrorManager +import jdk.nashorn.internal.runtime.Source +import jdk.nashorn.internal.runtime.options.Options + +@Slf4j +@Grab('ch.qos.logback:logback-classic:1.1.2') +/** + * Parse Javascript files named 'PluginStrings.js' for i18n definitions and compare those to all calls against the + * NX.I18n service to find calls which would result in error. + */ +class NashornExtI18nParser +{ + private final List folders + + private final Options options + + NashornExtI18nParser(final List folders) { + this.folders = folders + folders.each { assert it.exists() && it.isDirectory(), "File doesn't exist or isn't a folder: $it" } + this.options = new Options('nashorn') + options.set('anon.functions', true) + options.set('parse.only', true) + options.set('scripting', true) + } + + Map parse() { + def candidates = [] + def pluginStringsFiles = [] + + folders.each { target -> + target.eachFileRecurse(FileType.FILES) { File file -> + if (file.name.endsWith('.js')) { + if (file.name == 'PluginStrings.js') { + pluginStringsFiles << file + } + else { + if(!shouldIgnore(file)) { + candidates << file + } + } + } + } + } + + def keys = [] as TreeSet + pluginStringsFiles.each { + log.debug "Processing PluginStrings.js: $it" + def pluginStrings = parsePluginStrings(it) + keys.addAll(pluginStrings) + log.debug "Added ${pluginStrings.size()} keys" + } + + LexicalContext lexicalContext = new LexicalContext() + NodeVisitor i18nVisitor = new I18NVisitor(lexicalContext) + NodeVisitor labelHelpVisitor = new LabelHelpVisitor(lexicalContext) + + candidates.each { File file -> + log.debug "Processing candidate: $file" + parseJS(file).accept(lexicalContext, i18nVisitor) + parseJS(file).accept(lexicalContext, labelHelpVisitor) + } + + Set usedKeys = i18nVisitor.keys + def unusedKeys = keys - usedKeys + def undefinedKeys = usedKeys - keys + return [ + 'Available key size': keys.size(), + 'Used key size' : usedKeys.size(), + 'Unused keys' : unusedKeys, + 'Undefined keys' : undefinedKeys, + 'Number of fields with help text' : labelHelpVisitor.fieldsWithHelp.size(), + 'Number of fields without help text' : labelHelpVisitor.fieldsWithoutHelp.size(), + 'Fields without help' : labelHelpVisitor.fieldsWithoutHelp + ] + } + + private List parsePluginStrings(File pluginStrings) { + FunctionNode functionNode = parseJS(pluginStrings) + + def extDefine = functionNode.body.statements[0] + def defineArgs = extDefine.expression.args[1] + def keysField = defineArgs.elements.find { it.keyName == 'keys' } + return keysField.value.elements.collect { it.keyName } + } + + private FunctionNode parseJS(File jsFile) { + ErrorManager errors = new ErrorManager() + Context context = new Context(options, errors, Thread.currentThread().getContextClassLoader()) + return new Parser(context.getEnv(), Source.sourceFor('temp', jsFile.text), errors).parse() + } + + private boolean shouldIgnore(File file) { + def isDev = file.absolutePath.split('/').contains('dev') + if(isDev) { + log.trace("Ignoring $file") + } + return isDev + } +} + +@Slf4j +class I18NVisitor + extends NodeVisitor +{ + + final Set keys = [] as TreeSet + + I18NVisitor(final LexicalContext lc) { + super(lc) + } + + @Override + protected boolean enterDefault(final Node node) { + if (node.getClass() == CallNode && node.toString().contains('NX.I18n') && + (node.function.property == 'get' || node.function.property == 'format')) { + log.trace('Found key: {}',node.args[0].value) + keys << node.args[0].value + } + return true + } +} + +@Slf4j +class LabelHelpVisitor + extends NodeVisitor +{ + static final Closure KEY_FINDER = { String keyName, Node n -> + n.getClass() == PropertyNode && n.keyName == keyName + } + static final Closure FIELD_LABEL = KEY_FINDER.curry('fieldLabel') + static final Closure HELP_TEXT = KEY_FINDER.curry('helpText') + + final Set fieldsWithHelp = [] as TreeSet + final Set fieldsWithoutHelp = [] as TreeSet + + LabelHelpVisitor(final LexicalContext lc) { + super(lc) + } + + @Override + protected boolean enterDefault(final Node node) { + if (node.getClass() == ObjectNode && node.elements.find(FIELD_LABEL)) { + PropertyNode fieldLabel = node.elements.find(FIELD_LABEL) + PropertyNode helpText = node.elements.find(HELP_TEXT) + log.trace(" Found fieldLabel: ${fieldLabel.keyName} with value: ${fieldLabel.value} and helpText: ${helpText?.value}") + if(helpText) { + fieldsWithHelp << fieldLabel.value.toString() + } + else { + fieldsWithoutHelp << fieldLabel.value.toString() + } + } + return true + } +} + +/** + * Default if run from parent of nexus-oss/nexus-pro folders, can be overridden by space separated command line + * parameters + */ +def targetFolders = args ?: [ + 'plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui', + 'private/plugins/nexus-proui-plugin/src/main/resources/static/rapture/NX/proui', + 'components/nexus-rapture/src/main/resources/static/rapture/NX' +] +println JsonOutput. + prettyPrint(JsonOutput. + toJson(new NashornExtI18nParser(targetFolders.collect { String folder -> new File(folder) }).parse())) diff --git a/thirdparty-bundles/buildsupport/scripts/nexusresourcedirs.groovy b/thirdparty-bundles/buildsupport/scripts/nexusresourcedirs.groovy new file mode 100644 index 0000000000000000000000000000000000000000..d55da06fe0c983781b61771b24760772eb20a298 --- /dev/null +++ b/thirdparty-bundles/buildsupport/scripts/nexusresourcedirs.groovy @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +// +// Helper to generate NEXUS_RESOURCE_DIRS environment variable. +// +// Usage from project directory: +// +// export NEXUS_RESOURCE_DIRS=`groovy ./buildsupport/scripts/nexusresourcedirs.groovy` +// +// Aggregate projects using 'roots' property, from parent of project directories: +// +// export NEXUS_RESOURCE_DIRS=`groovy -Droots=nexus-oss,nexus-pro,nexus-bundles nexus-oss/buildsupport/scripts/nexusresourcedirs.groovy` +// + +def roots = System.getProperty('roots', '.') +def dirs = [] + +roots.split(',').each { root -> + def dir = new File(root) + if (dir.exists()) { + dir.eachDirRecurse { + if (it.path.endsWith('src/main/resources/static')) { + dirs << it.parentFile.canonicalPath + } + else if (it.path.endsWith('src/test/ft-resources')) { + dirs << it.canonicalPath + } + } + } +} + +println dirs.join(',') diff --git a/thirdparty-bundles/buildsupport/scripts/pom.xml b/thirdparty-bundles/buildsupport/scripts/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..9de48bb83590bdb47aff0b4e5b141b152d83caf9 --- /dev/null +++ b/thirdparty-bundles/buildsupport/scripts/pom.xml @@ -0,0 +1,62 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-scripts + ${project.groupId}:${project.artifactId} + pom + + + + + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-all + pom + 3.8.0-02 + import + + + + + + + + + idea + + + org.codehaus.groovy + groovy-all + provided + + + + + + diff --git a/thirdparty-bundles/buildsupport/security/pom.xml b/thirdparty-bundles/buildsupport/security/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..288e9370665a4491ed99da360dfc6fbc7eaddbd5 --- /dev/null +++ b/thirdparty-bundles/buildsupport/security/pom.xml @@ -0,0 +1,104 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-security + ${project.groupId}:${project.artifactId} + pom + + + 1.56 + 1.3.2 + + + + + + + + + org.bouncycastle + bcprov-jdk15on + ${bouncycastle.version} + + + + org.bouncycastle + bcpg-jdk15on + ${bouncycastle.version} + + + + org.bouncycastle + bcpkix-jdk15on + ${bouncycastle.version} + + + + + + org.apache.geronimo.framework + geronimo-crypto + 2.2.1 + + + + + + org.apache.shiro + shiro-core + ${apache-shiro.version} + + + commons-logging + commons-logging + + + commons-beanutils + commons-beanutils + + + + + + org.apache.shiro + shiro-guice + ${apache-shiro.version} + + + + org.apache.shiro + shiro-web + ${apache-shiro.version} + + + commons-logging + commons-logging + + + + + + + diff --git a/thirdparty-bundles/buildsupport/testing/pom.xml b/thirdparty-bundles/buildsupport/testing/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..995a4991487050357db9e3fc74917fd51b9c54ee --- /dev/null +++ b/thirdparty-bundles/buildsupport/testing/pom.xml @@ -0,0 +1,196 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-testing + ${project.groupId}:${project.artifactId} + pom + + + 4.9.2 + 2.38.0 + + + + + + + org.databene + contiperf + 2.3.4 + + + + com.github.dblock + oshi-core + 1.4 + + + + com.icegreen + greenmail + 1.4.1 + + + javax.activation + activation + + + + + + junit + junit + 4.12 + + + + com.jayway.awaitility + awaitility + 1.7.0 + + + + net.javacrumbs.json-unit + json-unit + 1.15.0 + + + + org.ops4j.pax.exam + pax-exam + ${pax-exam.version} + + + + org.ops4j.pax.exam + pax-exam-junit4 + ${pax-exam.version} + + + + org.ops4j.pax.exam + pax-exam-spi + ${pax-exam.version} + + + + org.ops4j.pax.exam + pax-exam-extender-service + ${pax-exam.version} + + + + org.ops4j.pax.exam + pax-exam-container-karaf + ${pax-exam.version} + + + + org.ops4j.pax.exam + pax-exam-features + ${pax-exam.version} + xml + + + + org.ops4j.pax.url + pax-url-aether + 2.4.7 + + + + org.littleshoot + littleproxy + 1.1.0 + + + + + org.apache.directory.server + apacheds-test-framework + 2.0.0-M19 + + + org.slf4j + slf4j-log4j12 + + + bouncycastle + bcprov-jdk15 + + + org.apache.directory.server + apacheds-jdbm-partition + + + org.apache.directory.jdbm + apacheds-jdbm1 + + + + + + com.bryntum.siesta + siesta-standard + 3.1.0 + zip + + + + com.spotify + docker-client + 6.1.0 + shaded + + + + com.github.stefanbirkner + system-rules + 1.16.1 + + + + com.unboundid + unboundid-ldapsdk + 3.2.1 + + + + org.xmlunit + xmlunit-core + 2.3.0 + + + + io.gatling.highcharts + gatling-charts-highcharts + 2.2.5 + + + + + diff --git a/thirdparty-bundles/buildsupport/ui/pom.xml b/thirdparty-bundles/buildsupport/ui/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..e1e768d2b3f8149f2bf18560c110888940865cfb --- /dev/null +++ b/thirdparty-bundles/buildsupport/ui/pom.xml @@ -0,0 +1,54 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.8.0-02 + + + nexus-buildsupport-ui + ${project.groupId}:${project.artifactId} + pom + + + + + com.sencha + ext + commercial + zip + 4.2.3 + + + + org.sonatype.directjngine + directjngine + 2.2.1 + + + + com.google.code.gson + gson + 2.3.1 + + + + + diff --git a/thirdparty-bundles/components/nexus-analytics-api/pom.xml b/thirdparty-bundles/components/nexus-analytics-api/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..1b2b96a9ffc4a3ecee8ac0230f07df63d0c86bc0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-analytics-api/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.8.0-02 + + + nexus-analytics-api + ${project.groupId}:${project.artifactId} + bundle + + + + org.sonatype.nexus + nexus-common + + + + org.apache.shiro + shiro-core + + + + org.sonatype.goodies + goodies-testsupport + test + + + + diff --git a/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventData.java b/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventData.java new file mode 100644 index 0000000000000000000000000000000000000000..070e8071a89ac6bd057509329d7b89d9f95a99b3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventData.java @@ -0,0 +1,152 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.sonatype.nexus.common.entity.AbstractEntity; + +/** + * Analytics event data. + * + * @since 3.0 + */ +public class EventData + extends AbstractEntity + implements Cloneable, Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * Event type. + */ + private String type; + + /** + * Event timestamp in milliseconds. + */ + private Long timestamp; + + /** + * Event sequence (rolling) to distinguish events which have the same timestamp. + */ + private Long sequence; + + /** + * Event duration in nanoseconds. + */ + private Long duration; + + /** + * Event user-id. + */ + private String userId; + + /** + * Event session-id. + */ + private String sessionId; + + /** + * Event attributes. + */ + private Map attributes = new HashMap<>(); + + public String getType() { + return type; + } + + public void setType(final String type) { + this.type = type; + } + + public Long getTimestamp() { + return timestamp; + } + + public void setTimestamp(final Long timestamp) { + this.timestamp = timestamp; + } + + public Long getSequence() { + return sequence; + } + + public void setSequence(final Long sequence) { + this.sequence = sequence; + } + + public Long getDuration() { + return duration; + } + + public void setDuration(final Long duration) { + this.duration = duration; + } + + public String getUserId() { + return userId; + } + + public void setUserId(final String userId) { + this.userId = userId; + } + + public String getSessionId() { + return sessionId; + } + + public void setSessionId(final String sessionId) { + this.sessionId = sessionId; + } + + public Map getAttributes() { + return attributes; + } + + /** + * @since 3.5 + */ + public void setAttributes(final Map attributes) { + this.attributes = attributes; + } + + /** + * Returns a deeply cloned copy. + */ + public EventData copy() { + try { + EventData copy = (EventData) clone(); + copy.attributes = new HashMap<>(this.attributes); + return copy; + } + catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "type='" + type + '\'' + + ", timestamp=" + timestamp + + ", sequence=" + sequence + + ", duration=" + duration + + ", userId='" + userId + '\'' + + ", sessionId='" + sessionId + '\'' + + ", attributes=" + attributes + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventDataFactory.java b/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventDataFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..43d5ba7349fc97348cbf238bedf3443a2176cdac --- /dev/null +++ b/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventDataFactory.java @@ -0,0 +1,72 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.node.NodeAccess; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.session.Session; +import org.apache.shiro.subject.Subject; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Helper to build {@link EventData} instances. + * + * @since 3.6.0 + */ +@Named +@Singleton +public class EventDataFactory +{ + private final RollingCounter counter = new RollingCounter(999_999_999_999_999L); + + private final NodeAccess nodeAccess; + + @Inject + public EventDataFactory(final NodeAccess nodeAccess) { + this.nodeAccess = checkNotNull(nodeAccess); + } + + public EventData create(final String type) { + EventData data = new EventData(); + data.setType(type); + data.setTimestamp(System.currentTimeMillis()); + data.setSequence(counter.next()); + + // capture the user and session ids if we can + Subject subject = SecurityUtils.getSubject(); + if (subject != null) { + Object principal = subject.getPrincipal(); + if (principal != null) { + data.setUserId(principal.toString()); + } + + Session session = subject.getSession(false); + if (session != null) { + data.setSessionId(session.getId().toString()); + } + } + + // capture node id + if (nodeAccess.getId() != null) { + data.getAttributes().put("nodeId", nodeAccess.getId()); + } + + return data; + } +} diff --git a/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventRecorder.java b/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventRecorder.java new file mode 100644 index 0000000000000000000000000000000000000000..10362c9cb642f8fad5aa29e56d24287c3e65c013 --- /dev/null +++ b/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventRecorder.java @@ -0,0 +1,25 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +/** + * Analytics event data recorder. + * + * @since 3.0 + */ +public interface EventRecorder +{ + boolean isEnabled(); + + void record(EventData data); +} diff --git a/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/RollingCounter.java b/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/RollingCounter.java new file mode 100644 index 0000000000000000000000000000000000000000..754407c33ce205020d94affe5a34d695bd93bf96 --- /dev/null +++ b/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/RollingCounter.java @@ -0,0 +1,44 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import java.util.concurrent.atomic.AtomicLong; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * Rolling (zero-based) long counter. + * + * @since 3.0 + */ +class RollingCounter +{ + private final AtomicLong value = new AtomicLong(-1); + + private final long max; + + RollingCounter(final long max) { + checkArgument(max > 0); + this.max = max + 1; // inclusive + } + + public long next() { + long current, next; + do { + current = value.get(); + next = (current + 1) % max; + } + while (!value.compareAndSet(current, next)); + return next; + } +} diff --git a/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/package-info.java b/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..5cad4397d5075923e9f19880f220ec414b2b09db --- /dev/null +++ b/thirdparty-bundles/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Analytics event collection. + * + * @since 3.0 + */ +package org.sonatype.nexus.analytics; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-analytics-api/src/test/java/org/sonatype/nexus/analytics/EventDataFactoryTest.java b/thirdparty-bundles/components/nexus-analytics-api/src/test/java/org/sonatype/nexus/analytics/EventDataFactoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7c77b7d85689bd25c3239301b97868fdaccfdd8b --- /dev/null +++ b/thirdparty-bundles/components/nexus-analytics-api/src/test/java/org/sonatype/nexus/analytics/EventDataFactoryTest.java @@ -0,0 +1,109 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.node.NodeAccess; + +import org.apache.shiro.session.Session; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.subject.support.SubjectThreadState; +import org.apache.shiro.util.ThreadState; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link EventDataFactory}. + */ +public class EventDataFactoryTest + extends TestSupport +{ + private ThreadState threadState; + + @Mock + private Subject subject; + + @Mock + private NodeAccess nodeAccess; + + @Before + public void setUp() throws Exception { + threadState = new SubjectThreadState(subject); + threadState.bind(); + } + + @After + public void tearDown() throws Exception { + if (threadState != null) { + threadState.clear(); + threadState = null; + } + } + + @Test + public void basics() throws Exception { + when(subject.getPrincipal()).thenReturn("foo"); + Session session = mock(Session.class); + when(subject.getSession(false)).thenReturn(session); + when(session.getId()).thenReturn("1234"); + + EventData event = new EventDataFactory(nodeAccess).create("TEST"); + assertThat(event, notNullValue()); + assertThat(event.getType(), is("TEST")); + assertThat(event.getTimestamp(), notNullValue()); + assertThat(event.getSequence(), notNullValue()); + assertThat(event.getUserId(), is("foo")); + assertThat(event.getSessionId(), is("1234")); + } + + @Test + public void subjectWithNullPrincipal() throws Exception { + when(subject.getPrincipal()).thenReturn(null); + + EventData event = new EventDataFactory(nodeAccess).create("TEST"); + assertThat(event, notNullValue()); + assertThat(event.getUserId(), nullValue()); + } + + @Test + public void subjectWithNullSession() throws Exception { + when(subject.getSession(false)).thenReturn(null); + + EventData event = new EventDataFactory(nodeAccess).create("TEST"); + assertThat(event, notNullValue()); + assertThat(event.getSessionId(), nullValue()); + } + + @Test + public void nodeIdWhenClustered() throws Exception { + when(nodeAccess.getId()).thenReturn("e91d48ec-8460-11e7-bb31-be2e44b06b34"); + + EventData event = new EventDataFactory(nodeAccess).create("TEST"); + + assertThat(event, notNullValue()); + assertThat(event.getAttributes().entrySet(), hasSize(1)); + assertThat(event.getAttributes(), hasEntry("nodeId", "e91d48ec-8460-11e7-bb31-be2e44b06b34")); + } +} diff --git a/thirdparty-bundles/components/nexus-analytics-api/src/test/java/org/sonatype/nexus/analytics/RollingCounterTest.java b/thirdparty-bundles/components/nexus-analytics-api/src/test/java/org/sonatype/nexus/analytics/RollingCounterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..147aa92c9e5e5befaea5de8767c31811ed176387 --- /dev/null +++ b/thirdparty-bundles/components/nexus-analytics-api/src/test/java/org/sonatype/nexus/analytics/RollingCounterTest.java @@ -0,0 +1,59 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +/** + * Tests for {@link RollingCounter}. + */ +public class RollingCounterTest + extends TestSupport +{ + @Test + public void basics() { + RollingCounter underTest = new RollingCounter(5); + assertThat(underTest.next(), is(0L)); + assertThat(underTest.next(), is(1L)); + assertThat(underTest.next(), is(2L)); + assertThat(underTest.next(), is(3L)); + assertThat(underTest.next(), is(4L)); + assertThat(underTest.next(), is(5L)); + + // rolls + assertThat(underTest.next(), is(0L)); + assertThat(underTest.next(), is(1L)); + assertThat(underTest.next(), is(2L)); + assertThat(underTest.next(), is(3L)); + assertThat(underTest.next(), is(4L)); + assertThat(underTest.next(), is(5L)); + + // rolls + assertThat(underTest.next(), is(0L)); + assertThat(underTest.next(), is(1L)); + assertThat(underTest.next(), is(2L)); + assertThat(underTest.next(), is(3L)); + assertThat(underTest.next(), is(4L)); + assertThat(underTest.next(), is(5L)); + } + + @Test(expected = IllegalArgumentException.class) + public void maxTooSmall() { + new RollingCounter(0); + } +} diff --git a/thirdparty-bundles/components/nexus-audit/pom.xml b/thirdparty-bundles/components/nexus-audit/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..e10530e6b13b698a779d2ce7ab6e501bf69992fe --- /dev/null +++ b/thirdparty-bundles/components/nexus-audit/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.8.0-02 + + + nexus-audit + ${project.groupId}:${project.artifactId} + bundle + + + + org.sonatype.nexus + nexus-common + + + + org.sonatype.goodies + goodies-testsupport + test + + + + diff --git a/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/AuditData.java b/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/AuditData.java new file mode 100644 index 0000000000000000000000000000000000000000..eb78a06f459ee304ea58f061e9502b2c0185221a --- /dev/null +++ b/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/AuditData.java @@ -0,0 +1,177 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +package org.sonatype.nexus.audit; + +import java.io.Serializable; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.sonatype.nexus.common.entity.AbstractEntity; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.common.node.NodeAccess; + +/** + * Audit data. + * + * @since 3.1 + */ +public class AuditData + extends AbstractEntity + implements Cloneable, Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * The domain of the audit data. + * + * ex: 'security.user' + * + * Domain should use dot-notation to encode hierarchy structure. + */ + private String domain; + + /** + * The type of audit data relevant for domain. + * + * ex: 'create' + */ + private String type; + + /** + * Context of the audit data for domain and type. + * + * ex: 'jdillon' + */ + private String context; + + /** + * When the change occurred. + */ + private Date timestamp; + + /** + * The node-id where the change occurred. + * + * @see NodeAccess#getId() + */ + private String nodeId; + + /** + * Who initiated the change. + * + * ex: 'admin/192.168.0.57' + */ + private String initiator; + + /** + * Extensible attributes for the change. + */ + private Map attributes = new LinkedHashMap<>(); + + public String getDomain() { + return domain; + } + + public void setDomain(final String domain) { + this.domain = domain; + } + + public String getType() { + return type; + } + + public void setType(final String type) { + this.type = type; + } + + public String getContext() { + return context; + } + + public void setContext(final String context) { + this.context = context; + } + + public Date getTimestamp() { + return timestamp; + } + + public void setTimestamp(final Date timestamp) { + this.timestamp = timestamp; + } + + public String getNodeId() { + return nodeId; + } + + public void setNodeId(final String nodeId) { + this.nodeId = nodeId; + } + + public String getInitiator() { + return initiator; + } + + public void setInitiator(final String initiator) { + this.initiator = initiator; + } + + public Map getAttributes() { + return attributes; + } + + /** + * @since 3.5 + */ + public void setAttributes(final Map attributes) { + this.attributes = attributes; + } + + /** + * Returns a deeply cloned copy. + */ + public AuditData copy() { + try { + AuditData copy = (AuditData) clone(); + copy.attributes = new LinkedHashMap<>(this.attributes); + return copy; + } + catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + /** + * Returns deeply cloned copy detached from {@link EntityMetadata}. + */ + public AuditData detach() { + AuditData copy = copy(); + copy.setEntityMetadata(null); + return copy; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "domain='" + domain + '\'' + + ", type='" + type + '\'' + + ", context='" + context + '\'' + + ", timestamp=" + timestamp + + ", nodeId='" + nodeId + '\'' + + ", initiator='" + initiator + '\'' + + ", attributes=" + attributes + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/AuditDataRecordedEvent.java b/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/AuditDataRecordedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..2cf73d9b105f6dedd1c409465d694cefff038dda --- /dev/null +++ b/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/AuditDataRecordedEvent.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +package org.sonatype.nexus.audit; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Event fired after {@link AuditData} had been recorded. + * + * @since 3.1 + */ +public class AuditDataRecordedEvent +{ + private final AuditData data; + + public AuditDataRecordedEvent(final AuditData data) { + this.data = checkNotNull(data); + } + + public AuditData getData() { + return data; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "data=" + data + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/AuditRecorder.java b/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/AuditRecorder.java new file mode 100644 index 0000000000000000000000000000000000000000..45a9d5b6e8274d266e616e4052c6877922564524 --- /dev/null +++ b/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/AuditRecorder.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +package org.sonatype.nexus.audit; + +/** + * Audit data recorder. + * + * @since 3.1 + */ +public interface AuditRecorder +{ + boolean isEnabled(); + + /** + * Record audit data and fires {@link AuditDataRecordedEvent}. + */ + void record(AuditData data); +} diff --git a/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/AuditorSupport.java b/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/AuditorSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..18595777db6d1c4dbdb07a0785adec0c4e23010b --- /dev/null +++ b/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/AuditorSupport.java @@ -0,0 +1,142 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +package org.sonatype.nexus.audit; + +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Provider; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.event.EventHelper; +import org.sonatype.nexus.common.text.Strings2; + +import com.google.common.base.Joiner; + +import static com.google.common.base.Preconditions.checkState; + +/** + * Support for auditor implementations. + * + * @since 3.1 + */ +public abstract class AuditorSupport + extends ComponentSupport +{ + /** + * Context value to use for global/system audit data when there is nothing more specific to use. + */ + protected static final String SYSTEM_CONTEXT = "system"; + + /** + * Common type for created events. + */ + protected static final String CREATED_TYPE = "created"; + + /** + * Common type for updated events. + */ + protected static final String UPDATED_TYPE = "updated"; + + /** + * Common type for deleted events. + */ + protected static final String DELETED_TYPE = "deleted"; + + /** + * Common type for changed events. + */ + protected static final String CHANGED_TYPE = "changed"; + + /** + * Helper to join a list into a string for attributes. + */ + private static final Joiner LIST_JOINER = Joiner.on(", ").skipNulls(); + + private Provider auditRecorder; + + /** + * Mapping of class to simple type names for auditing. + */ + private Map typeLookup = new HashMap<>(); + + @Inject + public void setAuditRecorder(final Provider auditRecorder) { + this.auditRecorder = auditRecorder; + } + + /** + * Register a simple type name for given class. + */ + protected void registerType(final Class type, final String name) { + typeLookup.put(type, name); + } + + /** + * Lookup simple type name for given class. + * + * If there is no mapping then the class.simpleName will be used. + */ + protected String type(final Class type) { + String name = typeLookup.get(type); + if (name == null) { + return Strings2.lower(type.getSimpleName()); + } + return name; + } + + private AuditRecorder recorder() { + checkState(auditRecorder != null, "Missing audit-recorder"); + return auditRecorder.get(); + } + + /** + * @deprecated use {@link #isRecording()} instead + */ + @Deprecated + protected boolean isEnabled() { + return isRecording(); + } + + /** + * @since 3.2 + */ + protected boolean isRecording() { + return recorder().isEnabled() && !EventHelper.isReplicating(); + } + + protected void record(final AuditData data) { + recorder().record(data); + } + + /** + * Helper to convert an object into a string. + */ + @Nullable + protected static String string(final Object value) { + if (value == null) { + return null; + } + return String.valueOf(value); + } + + /** + * Helper to conviert an iterable into a string. + */ + protected static String string(final Iterable value) { + return LIST_JOINER.join(value); + } +} diff --git a/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/InitiatorProvider.java b/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/InitiatorProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..8ce5c459a52e86c2e940d4b98bcbf170c9a65a32 --- /dev/null +++ b/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/InitiatorProvider.java @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.audit; + +import javax.annotation.Nonnull; + +/** + * Provides the current context {@link AuditData#initiator} value. + * + * @since 3.1 + */ +public interface InitiatorProvider +{ + @Nonnull + String get(); +} diff --git a/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/package-info.java b/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..6052a8609a8a29521cb6ab7ff70e253d8538dd65 --- /dev/null +++ b/thirdparty-bundles/components/nexus-audit/src/main/java/org/sonatype/nexus/audit/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Audit framework. + * + * @since 3.1 + */ +package org.sonatype.nexus.audit; diff --git a/thirdparty-bundles/components/nexus-base/pom.xml b/thirdparty-bundles/components/nexus-base/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..79b96670fdafcf15d3d51c3aa0f86d63fbc032e3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/pom.xml @@ -0,0 +1,190 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.8.0-02 + + + nexus-base + ${project.groupId}:${project.artifactId} + bundle + + + + org.apache.karaf.jaas + org.apache.karaf.jaas.boot + provided + + + + org.apache.karaf.jaas + org.apache.karaf.jaas.config + provided + + + + org.slf4j + jcl-over-slf4j + + + + javax.ws.rs + javax.ws.rs-api + + + + ch.qos.logback + logback-core + + + + ch.qos.logback + logback-classic + + + + com.codahale.metrics + metrics-core + + + + com.palominolabs.metrics + metrics-guice + + + + com.codahale.metrics + metrics-httpclient + + + + com.codahale.metrics + metrics-servlets + + + + com.codahale.metrics + metrics-servlet + + + + com.orientechnologies + orientdb-tools + + + + org.apache.velocity + velocity + + + + org.sonatype.nexus + nexus-commands + + + + org.sonatype.nexus + nexus-mime + + + + org.sonatype.nexus + nexus-orient + + + + org.sonatype.nexus + nexus-scheduling + + + + org.sonatype.nexus + nexus-security + + + + org.sonatype.nexus + nexus-servlet + + + + org.sonatype.nexus + nexus-ssl + + + + org.sonatype.nexus + nexus-thread + + + + org.sonatype.nexus + nexus-upgrade + + + + org.sonatype.nexus + nexus-supportzip-api + + + + org.sonatype.nexus + nexus-webresources-api + + + + org.sonatype.goodies + goodies-testsupport + test + + + + org.spockframework + spock-core + test + + + + cglib + cglib-nodep + test + + + + org.objenesis + objenesis + test + + + + com.jayway.awaitility + awaitility + test + + + + org.eclipse.jetty + jetty-servlet + test + + + + diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/app/ApplicationDirectoriesImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/app/ApplicationDirectoriesImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..797bc06dcd2125c7876e71d5c8f73bb9825cea97 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/app/ApplicationDirectoriesImpl.java @@ -0,0 +1,139 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.app; + +import java.io.File; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.common.io.DirectoryHelper; + +import com.google.common.base.Throwables; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default {@link ApplicationDirectories} implementation. + * + * @since 2.8 + */ +@Named +@Singleton +public class ApplicationDirectoriesImpl + extends ComponentSupport + implements ApplicationDirectories +{ + private final File installDir; + + private final File configDir; + + private final File workDir; + + private final File tempDir; + + @Inject + public ApplicationDirectoriesImpl(@Named("${karaf.base}") final File installDir, + @Named("${karaf.data}") final File workDir) + { + this.installDir = resolve(installDir, false); + log.debug("Install dir: {}", this.installDir); + + this.configDir = resolve(new File(installDir, "etc"), false); + log.debug("Config dir: {}", this.configDir); + + this.workDir = resolve(workDir, true); + log.debug("Work dir: {}", this.workDir); + + // Resolve the tmp dir from system properties. + String tmplocation = System.getProperty("java.io.tmpdir", "tmp"); + this.tempDir = resolve(new File(tmplocation), true); + log.debug("Temp dir: {}", this.tempDir); + } + + @Override + public File getInstallDirectory() { + return installDir; + } + + @Override + public File getConfigDirectory(final String subsystem) { + checkNotNull(subsystem); + return new File(configDir, subsystem); + } + + @Override + public File getTemporaryDirectory() { + return tempDir; + } + + @Override + public File getWorkDirectory() { + return workDir; + } + + @Override + public File getWorkDirectory(final String path, final boolean create) { + checkNotNull(path); + File dir = new File(path); + if (!dir.isAbsolute()) { + dir = new File(getWorkDirectory(), path); + } + return resolve(dir, create); + } + + @Override + public File getWorkDirectory(final String path) { + return getWorkDirectory(path, true); + } + + private void mkdir(final File dir) { + if (dir.isDirectory()) { + // skip already exists + return; + } + + try { + DirectoryHelper.mkdir(dir.toPath()); + log.debug("Created directory: {}", dir); + } + catch (Exception e) { + log.error("Failed to create directory: {}", dir); + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + } + + private File resolve(File dir, final boolean create) { + checkNotNull(dir); + + log.trace("Resolving directory: {}; create: {}", dir, create); + try { + dir = dir.getCanonicalFile(); + } + catch (Exception e) { + log.error("Failed to canonicalize directory: {}", dir); + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + + if (create) { + mkdir(dir); + } + + return dir; + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/app/BaseUrlManagerImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/app/BaseUrlManagerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..80429d633ea11a2010b1db664e4b1b85a931dcec --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/app/BaseUrlManagerImpl.java @@ -0,0 +1,128 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.app; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; +import javax.servlet.http.HttpServletRequest; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.app.BaseUrlHolder; +import org.sonatype.nexus.common.app.BaseUrlManager; + +import com.google.common.base.Strings; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default {@link BaseUrlManager}. + * + * @since 3.0 + */ +@Named +@Singleton +public class BaseUrlManagerImpl + extends ComponentSupport + implements BaseUrlManager +{ + private final Provider requestProvider; + + private volatile String url; + + private volatile boolean force; + + @Inject + public BaseUrlManagerImpl(final Provider requestProvider, + @Named("${org.sonatype.nexus.internal.app.BaseUrlManagerImpl.force:-false}") final boolean force) { + this.requestProvider = checkNotNull(requestProvider); + this.force = force; + log.debug("Force: {}", force); + } + + @Override + public void setUrl(final String url) { + this.url = url; + } + + @Override + public String getUrl() { + return url; + } + + @Override + public boolean isForce() { + return force; + } + + @Override + public void setForce(final boolean force) { + this.force = force; + } + + /** + * Return the current HTTP servlet-request if there is one in the current scope. + */ + @Nullable + private HttpServletRequest httpRequest() { + try { + return requestProvider.get(); + } + catch (Exception e) { + log.trace("Unable to resolve HTTP servlet-request", e); + return null; + } + } + + /** + * Detect base-url from forced settings, request or non-forced settings. + */ + @Nullable + @Override + public String detectUrl() { + // force base-url always wins if set + if (force && !Strings.isNullOrEmpty(url)) { + return url; + } + + // attempt to detect from HTTP request + HttpServletRequest request = httpRequest(); + if (request != null) { + StringBuffer url = request.getRequestURL(); + String uri = request.getRequestURI(); + String ctx = request.getContextPath(); + return url.substring(0, url.length() - uri.length() + ctx.length()); + } + + // no request in context, non-forced base-url + if (!Strings.isNullOrEmpty(url)) { + return url; + } + + // unable to determine base-url + return null; + } + + /** + * Detect and set (if non-null) the base-url. + */ + @Override + public void detectAndHoldUrl() { + String url = detectUrl(); + if (url != null) { + BaseUrlHolder.set(url); + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/app/GlobalComponentLookupHelperImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/app/GlobalComponentLookupHelperImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..c441460190a3ab18a436bf37af9f57045556db83 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/app/GlobalComponentLookupHelperImpl.java @@ -0,0 +1,121 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.app; + +import java.util.Iterator; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.app.GlobalComponentLookupHelper; + +import com.google.inject.Key; +import org.eclipse.sisu.BeanEntry; +import org.eclipse.sisu.inject.BeanLocator; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.inject.name.Names.named; + +/** + * Default {@link GlobalComponentLookupHelper}. + * + * @since 3.0 + */ +@Named +@Singleton +public class GlobalComponentLookupHelperImpl + extends ComponentSupport + implements GlobalComponentLookupHelper +{ + private final ClassLoader classLoader; + + private final BeanLocator beanLocator; + + @Inject + public GlobalComponentLookupHelperImpl(@Named("nexus-uber") final ClassLoader classLoader, + final BeanLocator beanLocator) + { + this.classLoader = checkNotNull(classLoader); + this.beanLocator = checkNotNull(beanLocator); + } + + @Override + @Nullable + public Object lookup(final String className) { + checkNotNull(className); + try { + log.trace("Looking up component by class-name: {}", className); + Class type = classLoader.loadClass(className); + return lookup(type); + } + catch (Exception e) { + log.trace("Unable to lookup component by class-name: {}; ignoring", className, e); + } + return null; + } + + @Override + @Nullable + @SuppressWarnings("unchecked") + public T lookup(final Class clazz) { + checkNotNull(clazz); + return (T) lookup(Key.get(clazz)); + } + + @Override + @Nullable + @SuppressWarnings("unchecked") + public T lookup(final Class clazz, final String name) { + checkNotNull(clazz); + checkNotNull(name); + return (T) lookup(Key.get(clazz, named(name))); + } + + @Override + @Nullable + public Object lookup(final Key key) { + checkNotNull(key); + try { + log.trace("Looking up component by key: {}", key); + @SuppressWarnings("unchecked") + Iterator iter = beanLocator.locate(key).iterator(); + if (iter.hasNext()) { + return iter.next().getValue(); + } + else { + log.trace("Component not found for key: {}", key); + } + } + catch (Exception e) { + log.trace("Unable to lookup component by key: {}; ignoring", key, e); + } + return null; + } + + @Override + @Nullable + public Class type(final String className) { + checkNotNull(className); + try { + log.trace("Looking up type: {}", className); + return classLoader.loadClass(className); + } + catch (Exception e) { + log.trace("Unable to lookup type: {}; ignoring", className, e); + } + return null; + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/app/NexusUberClassloader.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/app/NexusUberClassloader.java new file mode 100644 index 0000000000000000000000000000000000000000..a4d1d88c64ae25339767a8829e7d1359ccf9cbc6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/app/NexusUberClassloader.java @@ -0,0 +1,104 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.app; + +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import com.google.common.base.Throwables; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.Lists; +import org.eclipse.sisu.space.ClassSpace; + +import static com.google.common.base.Preconditions.checkNotNull; + +// FIXME: Rename to GlobalClassLoader, and "global" for named key + +/** + * ClassLoader which exposes all {@link ClassSpace}s in the application. + * + * @since 2.6 + */ +@Named("nexus-uber") +@Singleton +public class NexusUberClassloader + extends ClassLoader +{ + private final List spaces; + + private final Cache> classLookups = CacheBuilder.newBuilder().weakValues().build(); + + @Inject + public NexusUberClassloader(final List spaces) { + this.spaces = checkNotNull(spaces); + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return loadClass(name, false); + } + + @Override + protected Class loadClass(final String name, final boolean resolve) throws ClassNotFoundException { + try { + // cache successful results to save having to find them again + return classLookups.get(name, () -> searchSpacesForClass(name)); + } + catch (ExecutionException e) { // NOSONAR: only interested in the cause + Throwables.propagateIfPossible(e.getCause(), ClassNotFoundException.class); + throw new ClassNotFoundException(name, e.getCause()); + } + } + + @Override + public URL getResource(final String name) { + for (ClassSpace space : spaces) { + URL result = space.getResource(name); + if (result != null) { + return result; + } + } + return null; + } + + @Override + public Enumeration getResources(final String name) { + List result = Lists.newArrayList(); + for (ClassSpace space : spaces) { + for (Enumeration resources = space.getResources(name); resources.hasMoreElements(); ) { + result.add(resources.nextElement()); + } + } + return Collections.enumeration(result); + } + + private Class searchSpacesForClass(final String name) throws ClassNotFoundException { + for (ClassSpace space : spaces) { + try { + return space.loadClass(name); + } + catch (TypeNotPresentException e) { + // ignore it + } + } + throw new ClassNotFoundException(name); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/event/DebugEventInspector.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/event/DebugEventInspector.java new file mode 100644 index 0000000000000000000000000000000000000000..738541012df3512286f67d95f0e5f6529908c7f3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/event/DebugEventInspector.java @@ -0,0 +1,87 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.event; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.common.property.SystemPropertiesHelper; +import org.sonatype.nexus.jmx.reflect.ManagedAttribute; +import org.sonatype.nexus.jmx.reflect.ManagedObject; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.Subscribe; +import org.eclipse.sisu.EagerSingleton; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A simple "debug" helper component, that dumps out events to log. + * + * Usable for debugging or problem solving, not for production use! + * + * It will register itself to listen for events only when enabled, + * otherwise it will not spend any CPU cycles being dormant. + * + * It can be enabled via System property or JMX. + * + * @author cstamas + * @since 2.1 + */ +@Named +@EagerSingleton +@ManagedObject +public class DebugEventInspector + extends ComponentSupport +{ + private static final boolean ENABLED_DEFAULT = SystemPropertiesHelper.getBoolean( + DebugEventInspector.class.getName() + ".enabled", false); + + private volatile boolean enabled; + + private final EventManager eventManager; + + @Inject + public DebugEventInspector(final EventManager eventManager) { + this.eventManager = checkNotNull(eventManager); + setEnabled(ENABLED_DEFAULT); + } + + @ManagedAttribute + public boolean isEnabled() { + return enabled; + } + + @ManagedAttribute + public void setEnabled(boolean enabled) { + try { + if (enabled && !this.enabled) { + eventManager.register(this); + } + else if (!enabled && this.enabled) { + eventManager.unregister(this); + } + } + finally { + this.enabled = enabled; + } + } + + @Subscribe + @AllowConcurrentEvents + public void accept(final Object event) { + log.info("{}", event); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/event/EventExecutor.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/event/EventExecutor.java new file mode 100644 index 0000000000000000000000000000000000000000..41211570e4d7beb5b94685d97231db6c67cc516e --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/event/EventExecutor.java @@ -0,0 +1,146 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.event; + +import java.util.concurrent.Executor; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; +import java.util.concurrent.TimeUnit; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.lifecycle.LifecycleSupport; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.event.EventAware.Asynchronous; +import org.sonatype.nexus.thread.NexusExecutorService; +import org.sonatype.nexus.thread.NexusThreadFactory; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.util.concurrent.MoreExecutors; + +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.TASKS; +import static org.sonatype.nexus.common.event.EventHelper.asReplicating; +import static org.sonatype.nexus.common.event.EventHelper.isReplicating; +import static org.sonatype.nexus.internal.event.EventManagerImpl.HOST_THREAD_POOL_SIZE; + +/** + * Custom {@link Executor} used to dispatch events to {@link Asynchronous} subscribers. + * + * As Nexus starts, subscribers are called directly by the originating thread. Once the + * TASKS phase is reached subscribers will be called asynchronously using a thread pool. + * + * Conversely as Nexus stops, the thread pool is shutdown after leaving the TASKS phase + * and subscribers will again be called directly by the originating thread. This avoids + * asynchronous subscribers from having services disappear beneath them. + * + * @since 3.2 + */ +@Named +@ManagedLifecycle(phase = TASKS) +@Singleton +class EventExecutor + extends LifecycleSupport + implements Executor +{ + private volatile Executor delegate; + + public EventExecutor() { + // single-threaded until we reach TASKS phase + this.delegate = MoreExecutors.directExecutor(); + } + + /** + * Move from direct to asynchronous subscriber processing. + */ + @Override + protected void doStart() throws Exception { + // direct hand-off used! Host pool will use caller thread to execute async subscribers when pool full! + ThreadPoolExecutor threadPool = new ThreadPoolExecutor( + 0, + HOST_THREAD_POOL_SIZE, + 60L, + TimeUnit.SECONDS, + new SynchronousQueue<>(), + new NexusThreadFactory("event", "event-manager"), + new CallerRunsPolicy() + ); + + // begin distributing events in truly asynchronous fashion + delegate = NexusExecutorService.forCurrentSubject(threadPool); + } + + /** + * Move from asynchronous to direct subscriber processing. + */ + @Override + protected void doStop() throws Exception { + Executor currentExecutor = delegate; + + if (currentExecutor instanceof NexusExecutorService) { + // go back to single-threaded for rest of shutdown + delegate = MoreExecutors.directExecutor(); + + // wait for all background event subscribers to finish to have consistent state + ((NexusExecutorService) currentExecutor).shutdown(); + try { + ((NexusExecutorService) currentExecutor).awaitTermination(5L, TimeUnit.SECONDS); + } + catch (InterruptedException e) { + log.debug("Interrupted while waiting for termination", e); + } + } + } + + /** + * Used by UTs and ITs only, to "wait for calm period", when all the async event subscribers finished. + */ + @VisibleForTesting + boolean isCalmPeriod() { + Executor currentExecutor = delegate; + + if (currentExecutor instanceof NexusExecutorService) { + ThreadPoolExecutor threadPool = (ThreadPoolExecutor) + ((NexusExecutorService) currentExecutor).getTargetExecutorService(); + + // "calm period" is when we have no queued nor active threads + return threadPool.getQueue().isEmpty() && threadPool.getActiveCount() == 0; + } + + return true; // single-threaded mode + } + + @Override + public void execute(final Runnable command) { + delegate.execute(inheritIsReplicating(command)); + } + + /** + * Binds current "isReplicating" context to the {@link Runnable} regardless which thread executes it. + */ + private static Runnable inheritIsReplicating(final Runnable command) { + if (!isReplicating()) { + return command; // no need to inherit flag + } + return () -> { + // check state from context of thread doing the running + if (!isReplicating()) { + asReplicating(command); // set flag for duration of command + } + else { + command.run(); // flag already set, maintain it + } + }; + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/event/EventManagerImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/event/EventManagerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..4fdf94c86e675b802dfc417aebf66a2f86922ed4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/event/EventManagerImpl.java @@ -0,0 +1,136 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.event; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.lifecycle.LifecycleSupport; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.event.EventAware; +import org.sonatype.nexus.common.event.EventAware.Asynchronous; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.common.property.SystemPropertiesHelper; +import org.sonatype.nexus.jmx.reflect.ManagedAttribute; +import org.sonatype.nexus.jmx.reflect.ManagedObject; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.eventbus.EventBus; +import com.google.inject.Key; +import org.eclipse.sisu.BeanEntry; +import org.eclipse.sisu.Mediator; +import org.eclipse.sisu.inject.BeanLocator; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.EVENTS; +import static org.sonatype.nexus.common.event.EventBusFactory.reentrantAsyncEventBus; +import static org.sonatype.nexus.common.event.EventBusFactory.reentrantEventBus; + +/** + * Default {@link EventManager}. + */ +@Named +@ManagedLifecycle(phase = EVENTS) +@ManagedObject(typeClass=EventManager.class) +@Singleton +public class EventManagerImpl + extends LifecycleSupport + implements EventManager +{ + static final int HOST_THREAD_POOL_SIZE = SystemPropertiesHelper.getInteger( + EventManagerImpl.class.getName() + ".poolSize", 500); + + private final BeanLocator beanLocator; + + private final EventExecutor eventExecutor; + + private final EventBus eventBus; + + private final EventBus asyncBus; + + @Inject + public EventManagerImpl(final BeanLocator beanLocator, final EventExecutor eventExecutor) + { + this.beanLocator = checkNotNull(beanLocator); + this.eventExecutor = checkNotNull(eventExecutor); + + this.eventBus = reentrantEventBus("nexus"); + this.asyncBus = reentrantAsyncEventBus("nexus.async", eventExecutor); + } + + /** + * Mediator to register and unregister {@link EventAware} components. + */ + private static class EventAwareMediator + implements Mediator + { + @Override + public void add(final BeanEntry entry, final EventManagerImpl watcher) { + watcher.register(entry.getValue()); + } + + @Override + public void remove(final BeanEntry entry, final EventManagerImpl watcher) { + watcher.unregister(entry.getValue()); + } + } + + @Override + protected void doStart() throws Exception { + // watch for EventSubscriber components and register/unregister them + beanLocator.watch(Key.get(EventAware.class, Named.class), new EventAwareMediator(), this); + } + + @Override + public void register(final Object object) { + boolean async = object instanceof Asynchronous; + + if (async) { + asyncBus.register(object); + } + else { + eventBus.register(object); + } + + log.trace("Registered {}{}", async ? "ASYNC " : "", object); + } + + @Override + public void unregister(final Object object) { + boolean async = object instanceof Asynchronous; + + if (async) { + asyncBus.unregister(object); + } + else { + eventBus.unregister(object); + } + + log.trace("Unregistered {}{}", async ? "ASYNC " : "", object); + } + + @Override + public void post(final Object event) { + // notify synchronous subscribers before going asynchronous + eventBus.post(event); + asyncBus.post(event); + } + + @Override + @VisibleForTesting + @ManagedAttribute + public boolean isCalmPeriod() { + return eventExecutor.isCalmPeriod(); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogConfigurationCustomizerImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogConfigurationCustomizerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..cbd529cd651559b3cd0490deff7f83fdb7b4f30c --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogConfigurationCustomizerImpl.java @@ -0,0 +1,53 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.log.LogConfigurationCustomizer; +import org.sonatype.nexus.common.log.LoggerLevel; + +/** + * Configures core Nexus loggers. + * + * @since 2.7 + */ +@Named +@Singleton +public class LogConfigurationCustomizerImpl + implements LogConfigurationCustomizer +{ + @Override + public void customize(final Configuration configuration) { + // non Nexus loggers + configuration.setLoggerLevel("org.apache.commons", LoggerLevel.WARN); + + configuration.setLoggerLevel("org.eclipse.jetty", LoggerLevel.INFO); + configuration.setLoggerLevel("eu.medsea.mimeutil.MimeUtil2", LoggerLevel.INFO); + + // NEXUS-5456: limit noisy guice timing logger + configuration.setLoggerLevel("com.google.inject.internal.util.Stopwatch", LoggerLevel.INFO); + + // NEXUS-5835: limit noisy jmx connections to Nexus when root.level is DEBUG + configuration.setLoggerLevel("javax.management", LoggerLevel.INFO); + configuration.setLoggerLevel("sun.rmi", LoggerLevel.INFO); + + // Useful loggers (level will be calculated as effective level) + configuration.setLoggerLevel("org.sonatype.nexus", LoggerLevel.DEFAULT); + + configuration.setLoggerLevel("org.sonatype.nexus.jmx", LoggerLevel.DEFAULT); + configuration.setLoggerLevel("org.sonatype.nexus.internal.log", LoggerLevel.DEFAULT); + configuration.setLoggerLevel("org.sonatype.nexus.plugins", LoggerLevel.DEFAULT); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogMarkerImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogMarkerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..87ebfafee149e99dae9ed5ce7506f611660c9345 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogMarkerImpl.java @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.common.log.LogManager; +import org.sonatype.nexus.common.log.LogMarkInsertedEvent; +import org.sonatype.nexus.common.log.LogMarker; +import org.sonatype.nexus.common.log.LoggerLevel; + +import com.google.common.base.Strings; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default implementation of {@link LogMarker}. + * + * @since 3.1 + */ +@Named +@Singleton +public class LogMarkerImpl + extends ComponentSupport + implements LogMarker +{ + private final LogManager logManager; + + private final EventManager eventManager; + + @Inject + public LogMarkerImpl(final LogManager logManager, final EventManager eventManager) { + this.logManager = checkNotNull(logManager); + this.eventManager = checkNotNull(eventManager); + } + + @Override + public void markLog(final String message) { + // ensure that level for marking logger is enabled + LoggerLevel loggerLevel = logManager.getLoggerEffectiveLevel(log.getName()); + if (LoggerLevel.INFO.compareTo(loggerLevel) < 0) { + logManager.setLoggerLevel(log.getName(), LoggerLevel.INFO); + } + + String asterixes = Strings.repeat("*", message.length() + 4); + log.info("\n{}\n* {} *\n{}", asterixes, message, asterixes); + + eventManager.post(new LogMarkInsertedEvent(message)); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogbackLevels.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogbackLevels.java new file mode 100644 index 0000000000000000000000000000000000000000..7277410d30b157d6e0deadbdd3ebdff2f2952ff3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogbackLevels.java @@ -0,0 +1,62 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log; + +import org.sonatype.nexus.common.log.LoggerLevel; + +import ch.qos.logback.classic.Level; + +/** + * Logback levels helper. + * + * @since 3.0 + */ +class LogbackLevels +{ + private LogbackLevels() { + // empty + } + + /** + * Convert a Logback {@link Level} into a {@link LoggerLevel}. + */ + public static LoggerLevel convert(final Level level) { + switch (level.toInt()) { + case Level.ERROR_INT: + return LoggerLevel.ERROR; + + case Level.WARN_INT: + return LoggerLevel.WARN; + + case Level.INFO_INT: + return LoggerLevel.INFO; + + case Level.DEBUG_INT: + return LoggerLevel.DEBUG; + + case Level.OFF_INT: + return LoggerLevel.OFF; + + case Level.TRACE_INT: + default: + return LoggerLevel.TRACE; + } + } + + /** + * Convert a {@link LoggerLevel} into a Logback {@link Level}. + */ + public static Level convert(final LoggerLevel level) { + return Level.valueOf(level.name()); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogbackLogManager.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogbackLogManager.java new file mode 100644 index 0000000000000000000000000000000000000000..abcef42ab7ba44bd602340f6f2893501266fd046 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogbackLogManager.java @@ -0,0 +1,434 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.common.log.LogConfigurationCustomizer; +import org.sonatype.nexus.common.log.LogManager; +import org.sonatype.nexus.common.log.LoggerLevel; +import org.sonatype.nexus.common.log.LoggerLevelChangedEvent; +import org.sonatype.nexus.common.log.LoggersResetEvent; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.FileAppender; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.io.ByteStreams; +import com.google.inject.Key; +import org.eclipse.sisu.BeanEntry; +import org.eclipse.sisu.Mediator; +import org.eclipse.sisu.inject.BeanLocator; +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.impl.StaticLoggerBinder; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.KERNEL; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; + +/** + * Logback {@link LogManager}. + */ +@Named +@ManagedLifecycle(phase = KERNEL) +@Singleton +public class LogbackLogManager + extends StateGuardLifecycleSupport + implements LogManager +{ + private final EventManager eventManager; + + private final BeanLocator beanLocator; + + private final Map customizations; + + private final LoggerOverrides overrides; + + @Inject + public LogbackLogManager(final EventManager eventManager, + final BeanLocator beanLocator, + final LoggerOverrides overrides) + { + this.eventManager = checkNotNull(eventManager); + this.beanLocator = checkNotNull(beanLocator); + this.overrides = checkNotNull(overrides); + this.customizations = new HashMap<>(); + } + + /** + * Mediator to register customizers. + */ + private static class CustomizerMediator + implements Mediator + { + @Override + public void add(final BeanEntry entry, final LogbackLogManager watcher) { + watcher.registerCustomization(entry.getValue()); + } + + @Override + public void remove(final BeanEntry entry, final LogbackLogManager watcher) { + // ignore + } + } + + @Override + protected void doStart() throws Exception { + configure(); + + // watch for LogConfigurationCustomizer components + beanLocator.watch(Key.get(LogConfigurationCustomizer.class, Named.class), new CustomizerMediator(), this); + } + + private void configure() { + log.info("Configuring"); + + // sanity clear customizations + customizations.clear(); + + // load and apply overrides + overrides.load(); + for (Entry entry : overrides) { + setLogbackLoggerLevel(entry.getKey(), LogbackLevels.convert(entry.getValue())); + } + } + + @Override + protected void doStop() throws Exception { + // inform logback to shutdown + loggerContext().stop(); + } + + @Override + @Guarded(by = STARTED) + public Set getLogFiles() { + HashSet files = new HashSet<>(); + + for (Appender appender : appenders()) { + if (appender instanceof FileAppender) { + String path = ((FileAppender) appender).getFile(); + files.add(new File(path)); + } + } + + return files; + } + + @Override + @Nullable + @Guarded(by = STARTED) + public File getLogFile(final String fileName) { + Set files = getLogFiles(); + for (File file : files) { + if (file.getName().equals(fileName)) { + return file; + } + } + return null; + } + + @Override + @Nullable + @Guarded(by = STARTED) + public InputStream getLogFileStream(final String fileName, final long from, final long count) throws IOException { + log.debug("Retrieving log file: {}", fileName); + + // checking for platform or normalized path-separator (on unix these are the same) + if (fileName.contains(File.pathSeparator) || fileName.contains("/")) { + log.warn("Cannot retrieve log files with path separators in their name"); + return null; + } + + File file = getLogFile(fileName); + if (file == null || !file.exists()) { + log.warn("Log file does not exist: {}", fileName); + return null; + } + + long fromByte = from; + long bytesCount = count; + if (count < 0) { + bytesCount = Math.abs(count); + fromByte = Math.max(0, file.length() - bytesCount); + } + + InputStream input = new BufferedInputStream(new FileInputStream(file)); + if (fromByte == 0 && bytesCount >= file.length()) { + return input; + } + else { + input.skip(fromByte); + return ByteStreams.limit(input, bytesCount); + } + } + + @Override + @Guarded(by = STARTED) + public Map getLoggers() { + Map loggers = new HashMap<>(); + + // add all loggers which are defined in context which have a level (ie. not inheriting from parent) + LoggerContext ctx = loggerContext(); + for (ch.qos.logback.classic.Logger logger : ctx.getLoggerList()) { + String name = logger.getName(); + Level level = logger.getLevel(); + // only include loggers which explicit levels configured + if (level != null) { + loggers.put(name, LogbackLevels.convert(level)); + } + } + + // add all customized loggers + for (Entry entry : customizations.entrySet()) { + String name = entry.getKey(); + LoggerLevel level = entry.getValue(); + + // skip if there is already a logger with a set level in context + if (!loggers.containsKey(name)) { + // resolve effective level of logger + if (LoggerLevel.DEFAULT == level) { + level = getLoggerEffectiveLevel(entry.getKey()); + } + loggers.put(name, level); + } + } + + return loggers; + } + + /** + * @since 3.2 + */ + @Override + @Guarded(by = STARTED) + public Map getOverriddenLoggers() { + Map loggers = new HashMap<>(); + overrides.forEach(override -> loggers.put(override.getKey(), override.getValue())); + return loggers; + } + + @Override + @Guarded(by = STARTED) + public void resetLoggers() { + log.debug("Resetting loggers"); + + // reset all overridden logger levels to null (inherit from parent) + for (Map.Entry entry : overrides) { + if (!Logger.ROOT_LOGGER_NAME.equals(entry.getKey())) { + setLogbackLoggerLevel(entry.getKey(), null); + } + } + + // clear overrides cache and update persistence + overrides.reset(); + + // reset root level to default + setLoggerLevel(Logger.ROOT_LOGGER_NAME, LoggerLevel.DEFAULT); + + // re-apply customizations + applyCustomizations(); + + eventManager.post(new LoggersResetEvent()); + + log.debug("Loggers reset to default levels"); + } + + // + // Logger levels + // + + @Override + @Guarded(by = STARTED) + public void setLoggerLevel(final String name, @Nullable final LoggerLevel level) { + if (level == null) { + unsetLoggerLevel(name); + return; + } + + log.debug("Set logger level: {}={}", name, level); + LoggerLevel calculated = null; + + if (Logger.ROOT_LOGGER_NAME.equals(name)) { + calculated = (level == LoggerLevel.DEFAULT ? LoggerLevel.INFO : level); + overrides.set(name, calculated); + } + else { + // else we customize the logger overrides configuration + if (level == LoggerLevel.DEFAULT) { + boolean customizedByUser = overrides.contains(name) && !customizations.containsKey(name); + unsetLoggerLevel(name); + if (customizedByUser) { + overrides.set(name, calculated = getLoggerEffectiveLevel(name)); + } + else { + LoggerLevel customizedLevel = customizations.get(name); + if (customizedLevel != null && customizedLevel != LoggerLevel.DEFAULT) { + calculated = customizedLevel; + } + } + } + else { + overrides.set(name, calculated = level); + } + } + + // update override persistence + overrides.save(); + + if (calculated != null) { + setLogbackLoggerLevel(name, LogbackLevels.convert(calculated)); + } + + eventManager.post(new LoggerLevelChangedEvent(name, level)); + } + + @Override + @Guarded(by = STARTED) + public void unsetLoggerLevel(final String name) { + log.debug("Unset logger level: {}", name); + + if (overrides.remove(name) != null) { + overrides.save(); + } + + if (Logger.ROOT_LOGGER_NAME.equals(name)) { + setLogbackLoggerLevel(name, Level.INFO); + } + else { + setLogbackLoggerLevel(name, null); + } + + eventManager.post(new LoggerLevelChangedEvent(name, null)); + } + + @Override + @Nullable + @Guarded(by = STARTED) + public LoggerLevel getLoggerLevel(final String name) { + Level level = loggerContext().getLogger(name).getLevel(); + if (level != null) { + return LogbackLevels.convert(level); + } + return null; + } + + @Override + @Guarded(by = STARTED) + public LoggerLevel getLoggerEffectiveLevel(final String name) { + Level level = loggerContext().getLogger(name).getEffectiveLevel(); + return LogbackLevels.convert(level); + } + + /** + * Helper to set a named logback logger level. + */ + private void setLogbackLoggerLevel(final String name, @Nullable final Level level) { + log.trace("Set logback logger level: {}={}", name, level); + loggerContext().getLogger(name).setLevel(level); + } + + // + // Customizations + // + + /** + * Register and apply customizations. + */ + @VisibleForTesting + void registerCustomization(final LogConfigurationCustomizer customizer) { + log.debug("Registering customizations: {}", customizer); + + customizer.customize((name, level) -> { + checkNotNull(name); + checkNotNull(level); + customizations.put(name, level); + + // only apply customization if there is not an override, and the level is not DEFAULT + if (!overrides.contains(name) && level != LoggerLevel.DEFAULT) { + setLogbackLoggerLevel(name, LogbackLevels.convert(level)); + } + }); + } + + /** + * Apply all registered customizations. + */ + private void applyCustomizations() { + log.debug("Applying customizations"); + + for (Entry entry : customizations.entrySet()) { + if (entry.getValue() != LoggerLevel.DEFAULT) { + setLogbackLoggerLevel(entry.getKey(), LogbackLevels.convert(entry.getValue())); + } + } + } + + // + // Helpers + // + + /** + * Returns the current logger-context. + */ + @VisibleForTesting + static LoggerContext loggerContext() { + ILoggerFactory factory = LoggerFactory.getILoggerFactory(); + if (factory instanceof LoggerContext) { + return (LoggerContext) factory; + } + // Pax-Logging registers a custom implementation of ILoggerFactory which hides logback; as a workaround + // we set org.ops4j.pax.logging.StaticLogbackContext=true in system.properties and access it statically + return (LoggerContext) StaticLoggerBinder.getSingleton().getLoggerFactory(); + } + + /** + * Returns all configured appenders. + */ + private static Collection> appenders() { + List> result = new ArrayList<>(); + for (Logger l : loggerContext().getLoggerList()) { + ch.qos.logback.classic.Logger log = (ch.qos.logback.classic.Logger) l; + Iterator> iter = log.iteratorForAppenders(); + while (iter.hasNext()) { + result.add(iter.next()); + } + } + return result; + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogbackLoggerOverrides.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogbackLoggerOverrides.java new file mode 100644 index 0000000000000000000000000000000000000000..692b75693a6a54287da8ec627e0a7ce4a525d2a1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LogbackLoggerOverrides.java @@ -0,0 +1,216 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.xml.parsers.SAXParserFactory; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.goodies.common.FileReplacer; +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.common.log.LoggerLevel; + +import ch.qos.logback.classic.Logger; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Logback {@link LoggerOverrides} implementation. + * + * Special handling for {@code ROOT} logger, which is persisted as {@code root.level} property. + * + * @since 2.7 + */ +@Named +@Singleton +public class LogbackLoggerOverrides + extends ComponentSupport + implements LoggerOverrides +{ + private final File file; + + private final Map loggerLevels = new HashMap<>(); + + @Inject + public LogbackLoggerOverrides(final ApplicationDirectories applicationDirectories) { + checkNotNull(applicationDirectories); + this.file = new File(applicationDirectories.getWorkDirectory("etc/logback"), "logback-overrides.xml"); + log.info("File: {}", file); + } + + @VisibleForTesting + LogbackLoggerOverrides(final File file) { + this.file = checkNotNull(file); + } + + @Override + public synchronized void load() { + log.debug("Load"); + + loggerLevels.clear(); + if (file.exists()) { + try { + loggerLevels.putAll(read(file)); + } + catch (Exception e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + } + } + + @Override + public synchronized void save() { + log.debug("Save"); + + try { + write(file, loggerLevels); + } + catch (Exception e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + } + + @Override + public synchronized void reset() { + log.debug("Reset"); + + loggerLevels.clear(); + try { + write(file, loggerLevels); + } + catch (Exception e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + } + + @Override + public synchronized void set(final String name, final LoggerLevel level) { + log.debug("Set: {}={}", name, level); + + loggerLevels.put(name, level); + } + + @Override + @Nullable + public synchronized LoggerLevel get(final String name) { + return loggerLevels.get(name); + } + + @Override + @Nullable + public synchronized LoggerLevel remove(final String name) { + log.debug("Remove: {}", name); + + return loggerLevels.remove(name); + } + + @Override + public synchronized boolean contains(final String name) { + return loggerLevels.containsKey(name); + } + + @Override + public synchronized Iterator> iterator() { + return ImmutableMap.copyOf(loggerLevels).entrySet().iterator(); + } + + // + // Logback xml-format I/O + // + + /** + * Read logger levels from logback.xml formatted include file. + */ + private Map read(final File inputFile) throws Exception { + final Map result = Maps.newHashMap(); + + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + parserFactory.setValidating(false); + parserFactory.setNamespaceAware(true); + parserFactory.newSAXParser().parse(inputFile, new DefaultHandler() + { + @Override + public void startElement(final String uri, + final String localName, + final String qName, + final Attributes attributes) throws SAXException + { + // NOTE: ATM we are ignoring 'property' elements, this is needed for root, but is only needed + // NOTE: to persist as a property for use in top-level logback.xml file + + if ("logger".equals(localName)) { + String name = attributes.getValue("name"); + String level = attributes.getValue("level"); + result.put(name, LoggerLevel.valueOf(level)); + } + } + }); + return result; + } + + /** + * Write logger levels and root property to logback.xml formatted include file. + */ + private void write(final File outputFile, final Map overrides) throws Exception { + final FileReplacer fileReplacer = new FileReplacer(outputFile); + fileReplacer.setDeleteBackupFile(true); + fileReplacer.replace(output -> { + try (final BufferedWriter out = new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8))) { + out.write(""); + out.newLine(); + out.newLine(); + out.write(""); + out.newLine(); + out.newLine(); + out.write(""); + out.newLine(); + for (Entry entry : overrides.entrySet()) { + if (Logger.ROOT_LOGGER_NAME.equals(entry.getKey())) { + out.write(String.format(" %n", entry.getValue())); + } + else { + out.write(String.format(" %n", entry.getKey(), entry.getValue())); + } + } + out.write(""); + out.newLine(); + } + }); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggerAction.groovy b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggerAction.groovy new file mode 100644 index 0000000000000000000000000000000000000000..4c6b04afe9afddecbc86fc02f22aaae43aeac10e --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggerAction.groovy @@ -0,0 +1,82 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log + +import javax.inject.Inject +import javax.inject.Named + +import org.sonatype.nexus.common.log.LogManager +import org.sonatype.nexus.common.log.LoggerLevel + +import org.apache.karaf.shell.api.action.Action +import org.apache.karaf.shell.api.action.Argument +import org.apache.karaf.shell.api.action.Command +import org.apache.karaf.shell.api.action.Completion +import org.apache.karaf.shell.api.action.Option + +/** + * Action to set or display logger level. + * + * @since 3.0 + */ +@Named +@Command(name='logger', scope = 'nexus', description = 'Set or display logger level') +class LoggerAction + implements Action +{ + @Inject + LogManager logManager + + @Option(name='-d', aliases = ['--delete'], description = 'Delete logger') + Boolean delete + + @Option(name='-e', aliases = ['--effective'], description = 'Return effective logger level') + Boolean effective + + // FIXME: Presently a strict flag is set on some Karaf stuff related to completion, and + // FIXME: ... unless you use the logger-name completer, then the level completion will not get picked up + // FIXME: ... unsure where, but looks like if a completer fails, all completers after it are ignored + + @Argument(name="name", index = 0, required = true, description = 'Logger name') + @Completion(LoggerNameCompleter) + String name + + @Argument(name="level", index = 1, description = 'Logger level') + LoggerLevel level + + @Override + public Object execute() throws Exception { + if (delete) { + logManager.unsetLoggerLevel(name) + } + else if (level) { + logManager.setLoggerLevel(name, level) + } + else { + if (effective) { + level = logManager.getLoggerEffectiveLevel(name) + } + else { + level = logManager.getLoggerLevel(name) + } + + if (level) { + println "$name = $level" + } + else { + println "$name is not set" + } + } + return null + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggerNameCompleter.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggerNameCompleter.java new file mode 100644 index 0000000000000000000000000000000000000000..831de81af3c97f9464393029774b713431c0b529 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggerNameCompleter.java @@ -0,0 +1,49 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log; + +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.nexus.common.log.LogManager; + +import org.apache.karaf.shell.api.console.CommandLine; +import org.apache.karaf.shell.api.console.Completer; +import org.apache.karaf.shell.api.console.Session; +import org.apache.karaf.shell.support.completers.StringsCompleter; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Logger name completer. + * + * @since 3.0 + */ +@Named +public class LoggerNameCompleter + implements Completer +{ + private final LogManager logManager; + + @Inject + public LoggerNameCompleter(final LogManager logManager) { + this.logManager = checkNotNull(logManager); + } + + @Override + public int complete(final Session session, final CommandLine commandLine, final List candidates) { + return new StringsCompleter(logManager.getLoggers().keySet()).complete(session, commandLine, candidates); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggerOverrides.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggerOverrides.java new file mode 100644 index 0000000000000000000000000000000000000000..152350d3f0fc2e3ed4707177e028fbbeacdbec53 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggerOverrides.java @@ -0,0 +1,65 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log; + +import java.util.Map; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.log.LoggerLevel; + +/** + * Manages user-configured logger overrides. + * + * @since 3.0 + */ +public interface LoggerOverrides + extends Iterable> +{ + /** + * Load overrides configuration from storage. + */ + void load(); + + /** + * Save current overrides configuration to storage. + */ + void save(); + + /** + * Reset overrides configuration to default state. + */ + void reset(); + + /** + * Set a logger level override. + */ + void set(String name, LoggerLevel level); + + /** + * Get the level of an overridden logger. + */ + @Nullable + LoggerLevel get(final String name); + + /** + * Remove override configuration for a logger. + */ + @Nullable + LoggerLevel remove(final String name); + + /** + * Check if a logger has been overridden. + */ + boolean contains(final String name); +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggersAction.groovy b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggersAction.groovy new file mode 100644 index 0000000000000000000000000000000000000000..e0b4dce64068ad2aac297559fb18d42d8d47f5e3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggersAction.groovy @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log + +import javax.inject.Inject +import javax.inject.Named + +import org.sonatype.nexus.common.log.LogManager + +import org.apache.karaf.shell.api.action.Action +import org.apache.karaf.shell.api.action.Command +import org.apache.karaf.shell.api.action.Option +import org.apache.karaf.shell.support.table.ShellTable + +/** + * Action to display configured loggers. + * + * @since 3.0 + */ +@Named +@Command(name='loggers', scope = 'nexus', description = 'Display loggers') +class LoggersAction + implements Action +{ + @Inject + LogManager logManager + + @Option(name='-r', aliases = ['--reset'], description = 'Reset loggers') + Boolean reset + + @Override + public Object execute() throws Exception { + if (reset) { + logManager.resetLoggers() + return null + } + + def table = new ShellTable() + table.column('Name') + table.column('Level').alignRight() + + def loggers = logManager.loggers + loggers.keySet().sort().each { name -> + table.addRow().addContent(name, loggers[name]) + } + + table.print System.out + return null + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggingSecurityContributor.groovy b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggingSecurityContributor.groovy new file mode 100644 index 0000000000000000000000000000000000000000..c53d032204b22ea0ba2446757c67380ec0966f49 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/log/LoggingSecurityContributor.groovy @@ -0,0 +1,76 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log + +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.nexus.security.config.CPrivilege +import org.sonatype.nexus.security.config.MemorySecurityConfiguration +import org.sonatype.nexus.security.config.SecurityContributor + +/** + * Logging security configuration. + * + * @since 3.0 + */ +@Named +@Singleton +class LoggingSecurityContributor + implements SecurityContributor +{ + @Override + MemorySecurityConfiguration getContribution() { + return new MemorySecurityConfiguration( + privileges: [ + new CPrivilege( + id: 'nx-logging-all', + description: 'All permissions for Logging', + type: 'application', + properties: [ + domain: 'logging', + actions: '*' + ] + ), + new CPrivilege( + id: 'nx-logging-read', + description: 'Read permission for Logging', + type: 'application', + properties: [ + domain: 'logging', + actions: 'read' + ] + ), + new CPrivilege( + id: 'nx-logging-update', + description: 'Update permission for Logging', + type: 'application', + properties: [ + domain: 'logging', + actions: 'update' + ] + ), + new CPrivilege( + id: 'nx-logging-mark', + description: 'Mark permission for Logging', + type: 'application', + properties: [ + domain: 'logging', + actions: 'mark' + ] + ), + ] + ) + } +} + diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/DeadlockHealthCheckProvider.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/DeadlockHealthCheckProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..8e0a74806854724eaf48f44970e2b3cad32cc23c --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/DeadlockHealthCheckProvider.java @@ -0,0 +1,35 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.metrics; + +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import com.codahale.metrics.health.HealthCheck; +import com.codahale.metrics.health.jvm.ThreadDeadlockHealthCheck; + +/** + * {@link ThreadDeadlockHealthCheck} provider. + * + * @since 2.8 + */ +@Named("deadlocks") +@Singleton +public class DeadlockHealthCheckProvider + implements Provider +{ + public HealthCheck get() { + return new ThreadDeadlockHealthCheck(); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/HealthCheckMediator.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/HealthCheckMediator.java new file mode 100644 index 0000000000000000000000000000000000000000..75d782b821a8d144a27f915b3aceb4b166457f11 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/HealthCheckMediator.java @@ -0,0 +1,44 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.metrics; + +import javax.inject.Named; + +import org.sonatype.goodies.common.ComponentSupport; + +import com.codahale.metrics.health.HealthCheck; +import com.codahale.metrics.health.HealthCheckRegistry; +import org.eclipse.sisu.BeanEntry; +import org.eclipse.sisu.Mediator; + +/** + * Manages {@link HealthCheck} registrations via Sisu component mediation. + * + * @since 2.8 + */ +@Named +public class HealthCheckMediator + extends ComponentSupport + implements Mediator +{ + public void add(final BeanEntry entry, final HealthCheckRegistry registry) throws Exception { + log.debug("Registering: {}", entry); + registry.register(entry.getKey().value(), entry.getValue()); + } + + public void remove(final BeanEntry entry, final HealthCheckRegistry registry) throws Exception { + log.debug("Un-registering: {}", entry); + registry.unregister(entry.getKey().value()); + } +} + diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/HealthCheckServlet.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/HealthCheckServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..89fc2cafb7e782cc48855f3555faeb635c65fecf --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/HealthCheckServlet.java @@ -0,0 +1,34 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.metrics; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import com.codahale.metrics.health.HealthCheckRegistry; + +/** + * Customized {@link com.codahale.metrics.servlets.HealthCheckServlet} to support injection. + * + * @see HealthCheckMediator + * @since 3.0 + */ +@Singleton +public class HealthCheckServlet + extends com.codahale.metrics.servlets.HealthCheckServlet +{ + @Inject + public HealthCheckServlet(final HealthCheckRegistry registry) { + super(registry); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/MetricMediator.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/MetricMediator.java new file mode 100644 index 0000000000000000000000000000000000000000..2f08af4dd98f938b9290dde9d3122d0ee3a3d710 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/MetricMediator.java @@ -0,0 +1,43 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.metrics; + +import javax.inject.Named; + +import org.sonatype.goodies.common.ComponentSupport; + +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricRegistry; +import org.eclipse.sisu.BeanEntry; +import org.eclipse.sisu.Mediator; + +/** + * Manages {@link Metric} registrations via Sisu component mediation. + * + * @since 3.6 + */ +@Named +public class MetricMediator + extends ComponentSupport + implements Mediator +{ + public void add(final BeanEntry entry, final MetricRegistry registry) throws Exception { + log.debug("Registering: {}", entry); + registry.register(entry.getKey().value(), entry.getValue()); + } + + public void remove(final BeanEntry entry, final MetricRegistry registry) throws Exception { + log.debug("Un-registering: {}", entry); + registry.remove(entry.getKey().value()); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/MetricsModule.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/MetricsModule.java new file mode 100644 index 0000000000000000000000000000000000000000..6329e2cb0ab2e8b57ba9d2a1e07116ba22196ee9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/MetricsModule.java @@ -0,0 +1,100 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.metrics; + +import org.sonatype.nexus.security.FilterChainModule; +import org.sonatype.nexus.security.SecurityFilter; +import org.sonatype.nexus.security.anonymous.AnonymousFilter; +import org.sonatype.nexus.security.authc.NexusAuthenticationFilter; +import org.sonatype.nexus.security.authz.PermissionsFilter; + +import com.codahale.metrics.Clock; +import com.codahale.metrics.servlet.InstrumentedFilter; +import com.codahale.metrics.servlets.PingServlet; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.inject.AbstractModule; +import com.google.inject.servlet.ServletModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Dropwizard Metrics guice configuration. + * + * Installs servlet endpoints: + * + *
    + *
  • /service/metrics/ping
  • + *
  • /service/metrics/threads
  • + *
  • /service/metrics/data
  • + *
  • /service/metrics/healthcheck
  • + *
+ * + * Protected by {@code nexus:metrics:read} permission. + * + * @since 2.5 + */ +public class MetricsModule + extends AbstractModule +{ + private static final Logger log = LoggerFactory.getLogger(MetricsModule.class); + + private static final String MOUNT_POINT = "/service/metrics"; + + @Override + protected void configure() { + // NOTE: AdminServletModule (metrics-guice integration) generates invalid links, so wire up servlets ourselves + + final Clock clock = Clock.defaultClock(); + bind(Clock.class).toInstance(clock); + + final JsonFactory jsonFactory = new JsonFactory(new ObjectMapper()); + bind(JsonFactory.class).toInstance(jsonFactory); + + install(new ServletModule() + { + @Override + protected void configureServlets() { + bind(MetricsServlet.class); + bind(HealthCheckServlet.class); + + serve(MOUNT_POINT + "/ping").with(new PingServlet()); + serve(MOUNT_POINT + "/threads").with(new ThreadDumpServlet()); + serve(MOUNT_POINT + "/data").with(MetricsServlet.class); + serve(MOUNT_POINT + "/healthcheck").with(HealthCheckServlet.class); + + // record metrics for all webapp access + filter("/*").through(new InstrumentedFilter()); + + bind(SecurityFilter.class); + + // configure security + filter(MOUNT_POINT + "/*").through(SecurityFilter.class); + } + }); + + // require permission to use endpoints + install(new FilterChainModule() + { + @Override + protected void configure() { + addFilterChain(MOUNT_POINT + "/**", + NexusAuthenticationFilter.NAME, + AnonymousFilter.NAME, + PermissionsFilter.config("nexus:metrics:read")); + } + }); + + log.info("Metrics support configured"); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/MetricsSecurityContributor.groovy b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/MetricsSecurityContributor.groovy new file mode 100644 index 0000000000000000000000000000000000000000..30eb16b062a3a32cffd2d5ff9704beb4d462a847 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/MetricsSecurityContributor.groovy @@ -0,0 +1,49 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.metrics + +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.nexus.security.config.CPrivilege +import org.sonatype.nexus.security.config.MemorySecurityConfiguration +import org.sonatype.nexus.security.config.SecurityContributor + +/** + * Metrics security configuration. + * + * @since 3.0 + */ +@Named +@Singleton +class MetricsSecurityContributor + implements SecurityContributor +{ + @Override + MemorySecurityConfiguration getContribution() { + return new MemorySecurityConfiguration( + privileges: [ + new CPrivilege( + id: 'nx-metrics-all', + description: 'All permissions for Metrics', + type: 'application', + properties: [ + domain: 'metrics', + actions: '*' + ] + ) + ] + ) + } +} + diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/MetricsServlet.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/MetricsServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..80008590e02e8c8255b46ee90802213a75e2abf2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/MetricsServlet.java @@ -0,0 +1,68 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.metrics; + +import java.io.IOException; +import java.lang.management.ManagementFactory; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.codahale.metrics.JvmAttributeGaugeSet; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.jvm.BufferPoolMetricSet; +import com.codahale.metrics.jvm.FileDescriptorRatioGauge; +import com.codahale.metrics.jvm.GarbageCollectorMetricSet; +import com.codahale.metrics.jvm.MemoryUsageGaugeSet; +import com.codahale.metrics.jvm.ThreadStatesGaugeSet; + +import static com.codahale.metrics.MetricRegistry.name; +import static com.google.common.net.HttpHeaders.CONTENT_DISPOSITION; + +/** + * Customized {@link com.codahale.metrics.servlets.MetricsServlet} to support injection and download. + * + * @since 3.0 + */ +@Singleton +public class MetricsServlet + extends com.codahale.metrics.servlets.MetricsServlet +{ + @Inject + public MetricsServlet(final MetricRegistry registry) { + super(registry); + + // JVM metrics are no longer automatically added in codahale-metrics + registry.register(name("jvm", "vm"), new JvmAttributeGaugeSet()); + registry.register(name("jvm", "memory"), new MemoryUsageGaugeSet()); + registry.register(name("jvm", "buffers"), new BufferPoolMetricSet(ManagementFactory.getPlatformMBeanServer())); + registry.register(name("jvm", "fd_usage"), new FileDescriptorRatioGauge()); + registry.register(name("jvm", "thread-states"), new ThreadStatesGaugeSet()); + registry.register(name("jvm", "garbage-collectors"), new GarbageCollectorMetricSet()); + } + + @Override + protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) + throws ServletException, IOException + { + boolean download = Boolean.parseBoolean(req.getParameter("download")); + if (download) { + resp.addHeader(CONTENT_DISPOSITION, "attachment; filename='metrics.json'"); + } + + super.doGet(req, resp); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/ThreadDumpServlet.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/ThreadDumpServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..7dedfb0cc01e8a47168d07ca896e598840d55191 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/metrics/ThreadDumpServlet.java @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.metrics; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import static com.google.common.net.HttpHeaders.CONTENT_DISPOSITION; + +/** + * Customized {@link com.codahale.metrics.servlets.ThreadDumpServlet} to support download. + * + * @since 3.0 + */ +public class ThreadDumpServlet + extends com.codahale.metrics.servlets.ThreadDumpServlet +{ + @Override + protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) + throws ServletException, IOException + { + boolean download = Boolean.parseBoolean(req.getParameter("download")); + if (download) { + resp.addHeader(CONTENT_DISPOSITION, "attachment; filename='threads.txt'"); + } + + super.doGet(req, resp); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/FileKeyStoreStorage.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/FileKeyStoreStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..256229d18e2a79d6ac42735c477064491fe45276 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/FileKeyStoreStorage.java @@ -0,0 +1,89 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.node; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; + +import org.sonatype.nexus.ssl.spi.KeyStoreStorage; + +import com.google.common.annotations.VisibleForTesting; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Implementation of {@link KeyStoreStorage} backed by local filesystem. + * + * @since 3.1 + */ +public class FileKeyStoreStorage + implements KeyStoreStorage +{ + private final File keyStoreFile; + + private long lastRead; + + public FileKeyStoreStorage(final File keyStoreFile) { + this.keyStoreFile = checkNotNull(keyStoreFile); + } + + @VisibleForTesting + public File getKeyStoreFile() { + return keyStoreFile; + } + + @Override + public boolean exists() { + return keyStoreFile.exists(); + } + + @Override + public boolean modified() { + return lastRead < keyStoreFile.lastModified(); + } + + @Override + public void load(final KeyStore keyStore, final char[] password) + throws NoSuchAlgorithmException, CertificateException, IOException + { + long readStart = System.currentTimeMillis(); + try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(keyStoreFile))) { + keyStore.load(bis, password); + } + lastRead = readStart; + } + + @Override + public void save(final KeyStore keyStore, final char[] password) + throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException + { + keyStoreFile.getParentFile().mkdirs(); // NOSONAR + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(keyStoreFile))) { + keyStore.store(bos, password); + } + lastRead = System.currentTimeMillis(); + } + + @Override + public String toString() { + return keyStoreFile.toURI().toString(); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/KeyStoreManagerConfigurationImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/KeyStoreManagerConfigurationImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..0d9d3f81ea7169a7ff2c32e250194846977ca281 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/KeyStoreManagerConfigurationImpl.java @@ -0,0 +1,80 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.node; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.Time; +import org.sonatype.nexus.ssl.KeyStoreManagerConfiguration; +import org.sonatype.nexus.ssl.KeyStoreManagerConfigurationSupport; + +import com.google.common.annotations.VisibleForTesting; + +/** + * Node {@link KeyStoreManagerConfiguration}. + * + * @since 3.0 + */ +@Named(KeyStoreManagerImpl.NAME) +@Singleton +public class KeyStoreManagerConfigurationImpl + extends KeyStoreManagerConfigurationSupport +{ + private static final String CPREFIX = "${node.keyStoreManager"; + + /** + * Private key-store password. + */ + private static final char[] PKSP = "uuPWrk3UEQRaolpd".toCharArray(); + + /** + * Trusted key-store password. + */ + private static final char[] TKSP = "1bmcqcHV3sp6fVKD".toCharArray(); + + /** + * Private-key password. + */ + private static final char[] PKP = "CyQM8zCFeorarTA8".toCharArray(); + + @Inject + public KeyStoreManagerConfigurationImpl( + @Named(CPREFIX + ".keyStoreType:-JKS}") final String keyStoreType, + @Named(CPREFIX + ".keyAlgorithm:-RSA}") final String keyAlgorithm, + @Named(CPREFIX + ".keyAlgorithmSize:-2048}") final int keyAlgorithmSize, + @Named(CPREFIX + ".certificateValidity:-36500d}") final Time certificateValidity, + @Named(CPREFIX + ".signatureAlgorithm:-SHA1WITHRSA}") final String signatureAlgorithm, + @Named(CPREFIX + ".keyManagerAlgorithm:-DEFAULT}") final String keyManagerAlgorithm, + @Named(CPREFIX + ".trustManagerAlgorithm:-DEFAULT}") final String trustManagerAlgorithm) + { + setPrivateKeyStorePassword(PKSP); + setTrustedKeyStorePassword(TKSP); + setPrivateKeyPassword(PKP); + setKeyStoreType(keyStoreType); + setKeyAlgorithm(keyAlgorithm); + setKeyAlgorithmSize(keyAlgorithmSize); + setCertificateValidity(certificateValidity); + setSignatureAlgorithm(signatureAlgorithm); + setKeyManagerAlgorithm(keyManagerAlgorithm); + setTrustManagerAlgorithm(trustManagerAlgorithm); + } + + @VisibleForTesting + public KeyStoreManagerConfigurationImpl() { + setPrivateKeyStorePassword(PKSP); + setTrustedKeyStorePassword(TKSP); + setPrivateKeyPassword(PKP); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/KeyStoreManagerImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/KeyStoreManagerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..5bef1641d330d232818fe9f91a9909b78fe0e2fd --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/KeyStoreManagerImpl.java @@ -0,0 +1,43 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.node; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.crypto.CryptoHelper; +import org.sonatype.nexus.ssl.KeyStoreManager; +import org.sonatype.nexus.ssl.KeyStoreManagerConfiguration; +import org.sonatype.nexus.ssl.spi.KeyStoreStorageManager; + +/** + * Node {@link KeyStoreManager}. + * + * @since 3.0 + */ +@Named(KeyStoreManagerImpl.NAME) +@Singleton +public class KeyStoreManagerImpl + extends org.sonatype.nexus.ssl.KeyStoreManagerImpl +{ + public static final String NAME = "node"; + + @Inject + public KeyStoreManagerImpl(final CryptoHelper crypto, + @Named(NAME) final KeyStoreStorageManager storageManager, + @Named(NAME) final KeyStoreManagerConfiguration config) + { + super(crypto, storageManager, config); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/KeyStoreStorageManagerImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/KeyStoreStorageManagerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..723a880a011326db74d46aa8b4a154f93ba93e5c --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/KeyStoreStorageManagerImpl.java @@ -0,0 +1,57 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.node; + +import java.io.File; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.ssl.spi.KeyStoreStorage; +import org.sonatype.nexus.ssl.spi.KeyStoreStorageManager; + +import com.google.common.annotations.VisibleForTesting; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Implementation of {@link KeyStoreStorageManager} for the node identity. Uses local filesystem as backing storage so + * that node identity is specific to each node. + * + * @since 3.1 + */ +@Named(KeyStoreManagerImpl.NAME) +@Singleton +public class KeyStoreStorageManagerImpl + implements KeyStoreStorageManager +{ + private final File basedir; + + @Inject + public KeyStoreStorageManagerImpl(final ApplicationDirectories directories) { + this.basedir = new File(directories.getWorkDirectory("keystores"), KeyStoreManagerImpl.NAME); + } + + @VisibleForTesting + public KeyStoreStorageManagerImpl(final File basedir) { + this.basedir = checkNotNull(basedir); + } + + @Override + public KeyStoreStorage createStorage(final String keyStoreName) { + checkNotNull(keyStoreName); + return new FileKeyStoreStorage(new File(basedir, keyStoreName)); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/LocalNodeAccess.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/LocalNodeAccess.java new file mode 100644 index 0000000000000000000000000000000000000000..e451a8421a8041ed7780639c31321b4d8c52201e --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/LocalNodeAccess.java @@ -0,0 +1,155 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.node; + +import java.security.cert.Certificate; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.ssl.CertificateUtil; +import org.sonatype.nexus.ssl.KeyStoreManager; + +import com.google.common.collect.ImmutableMap; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; + +/** + * Local {@link NodeAccess}. + * + * @since 3.0 + */ +@Named("local") +@Singleton +public class LocalNodeAccess + extends StateGuardLifecycleSupport + implements NodeAccess +{ + private final Provider keyStoreProvider; + + private Certificate certificate; + + private String fingerprint; + + private String id; + + private boolean freshNode; + + private Map memberAliases = Collections.emptyMap(); + + @Inject + public LocalNodeAccess(@Named(KeyStoreManagerImpl.NAME) final Provider keyStoreProvider) { + this.keyStoreProvider = checkNotNull(keyStoreProvider); + } + + @Override + protected void doStart() throws Exception { + KeyStoreManager keyStoreManager = keyStoreProvider.get(); + + // Generate identity key-pair if not already created + if (!keyStoreManager.isKeyPairInitialized()) { + log.info("Generating certificate"); + + // For now give something unique to the cert for additional identification purposes + UUID cn = UUID.randomUUID(); + keyStoreManager.generateAndStoreKeyPair( + cn.toString(), + "Nexus", + "Sonatype", + "Silver Spring", + "MD", + "US"); + + freshNode = true; // nodes with newly created identities are considered 'fresh' + } + + certificate = keyStoreManager.getCertificate(); + log.trace("Certificate:\n{}", certificate); + + fingerprint = CertificateUtil.calculateFingerprint(certificate); + log.debug("Fingerprint: {}", fingerprint); + + id = NodeIdEncoding.nodeIdForCertificate(certificate); + log.info("ID: {}", id); + + memberAliases = ImmutableMap.of(id, id); + } + + @Override + protected void doStop() throws Exception { + certificate = null; + fingerprint = null; + id = null; + } + + @Override + @Guarded(by = STARTED) + public Certificate getCertificate() { + return certificate; + } + + @Override + @Guarded(by = STARTED) + public String getFingerprint() { + return fingerprint; + } + + @Override + @Guarded(by = STARTED) + public String getId() { + return id; + } + + @Override + @Guarded(by = STARTED) + public boolean isClustered() { + return false; + } + + @Override + public Set getMemberIds() { + return memberAliases.keySet(); + } + + @Override + public boolean isFreshNode() { + return freshNode; + } + + @Override + public boolean isOldestNode() { + return true; + } + + @Override + public Map getMemberAliases() { + return memberAliases; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "id='" + id + '\'' + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/NodeIdEncoding.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/NodeIdEncoding.java new file mode 100644 index 0000000000000000000000000000000000000000..56840558eb7d8683297ac224e1925f56f6106d3c --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/node/NodeIdEncoding.java @@ -0,0 +1,69 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.node; + +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.util.Locale; + +import org.sonatype.nexus.common.text.Strings2; + +import com.google.common.hash.Hashing; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Node ID encoding helpers. + * + * @since 3.0 + */ +public class NodeIdEncoding +{ + private NodeIdEncoding() { + // empty + } + + /** + * Encode plain Certificate SHA1 into node-id string. + */ + public static String nodeIdForSha1(final String input) { + checkNotNull(input); + return Strings2.encodeSeparator(input, '-', 8); + } + + /** + * Decode node-id into plain SHA1 string. + */ + public static String sha1ForNodeId(final String input) { + checkNotNull(input); + return input.replaceAll("-", ""); + } + + /** + * Return node-id for certificate. + */ + public static String nodeIdForCertificate(final Certificate cert) throws CertificateEncodingException { + checkNotNull(cert); + String sha1 = Hashing.sha1().hashBytes(cert.getEncoded()).toString().toUpperCase(Locale.US); + return nodeIdForSha1(sha1); + } + + /** + * Return node-id for certificate fingerprint. + */ + public static String nodeIdForFingerprint(final String fingerprint) { + checkNotNull(fingerprint); + String sha1 = fingerprint.replace(":", ""); + return nodeIdForSha1(sha1); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/ConfigDatabase.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/ConfigDatabase.java new file mode 100644 index 0000000000000000000000000000000000000000..eec4a281c8dbc9224cc3141e1d7d041304774837 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/ConfigDatabase.java @@ -0,0 +1,92 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.orient.DatabaseExternalizer; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseManager; +import org.sonatype.nexus.supportzip.SupportBundle; +import org.sonatype.nexus.supportzip.SupportBundle.ContentSource.Type; +import org.sonatype.nexus.supportzip.SupportBundleCustomizer; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Shared {@code config} database components. + * + * @since 3.0 + */ +@SuppressWarnings("UnusedDeclaration") +public class ConfigDatabase +{ + private ConfigDatabase() { + // empty + } + + public static final String NAME = "config"; + + /** + * Shared {@code config} database instance provider. + */ + @Named(NAME) + @Singleton + public static class ProviderImpl + implements Provider + { + private final DatabaseManager databaseManager; + + @Inject + public ProviderImpl(final DatabaseManager databaseManager) { + this.databaseManager = checkNotNull(databaseManager); + } + + @Override + public DatabaseInstance get() { + return databaseManager.instance(NAME); + } + } + + /** + * Includes export of the {@code config} database in support-zip. + */ + @Named + @Singleton + public static class SupportBundleCustomizerImpl + extends ComponentSupport + implements SupportBundleCustomizer + { + private final Provider databaseInstance; + + @Inject + public SupportBundleCustomizerImpl(@Named(NAME) final Provider databaseInstance) { + this.databaseInstance = checkNotNull(databaseInstance); + } + + @Override + public void customize(final SupportBundle supportBundle) { + String path = String.format("work/%s/%s/%s", + DatabaseManagerImpl.WORK_PATH, + databaseInstance.get().getName(), + DatabaseExternalizer.EXPORT_FILENAME + ); + + supportBundle.add(new PasswordSanitizedJsonSource(Type.CONFIG, path, databaseInstance)); + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/ConfigDatabaseCheckpoint.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/ConfigDatabaseCheckpoint.java new file mode 100644 index 0000000000000000000000000000000000000000..3e93ac7b378213f128b7181180e0206444e6ddfd --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/ConfigDatabaseCheckpoint.java @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.common.upgrade.Checkpoints; +import org.sonatype.nexus.orient.DatabaseCheckpointSupport; +import org.sonatype.nexus.orient.DatabaseInstance; + +/** + * Upgrade checkpoint for the "config" database. + * + * @since 3.1 + */ +@Named +@Singleton +@Checkpoints(model = ConfigDatabase.NAME) +public class ConfigDatabaseCheckpoint + extends DatabaseCheckpointSupport +{ + @Inject + public ConfigDatabaseCheckpoint(@Named(ConfigDatabase.NAME) final Provider databaseInstance, + final ApplicationDirectories appDirectories) + { + super(ConfigDatabase.NAME, databaseInstance, appDirectories); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DatabaseManagerImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DatabaseManagerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..59f0abbd69f69ceaca6b8b3ff762f28eeab02bff --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DatabaseManagerImpl.java @@ -0,0 +1,115 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import java.io.File; +import java.io.IOException; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.common.io.DirectoryHelper; +import org.sonatype.nexus.jmx.reflect.ManagedAttribute; +import org.sonatype.nexus.jmx.reflect.ManagedObject; +import org.sonatype.nexus.orient.DatabaseExternalizer; +import org.sonatype.nexus.orient.DatabaseExternalizerImpl; +import org.sonatype.nexus.orient.DatabaseRestorer; +import org.sonatype.nexus.orient.DatabaseManager; +import org.sonatype.nexus.orient.DatabaseManagerSupport; + +import com.google.common.annotations.VisibleForTesting; +import com.orientechnologies.common.io.OFileUtils; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default {@code plocal} {@link DatabaseManager} implementation. + * + * @since 3.0 + */ +@Named +@Singleton +@ManagedObject +public class DatabaseManagerImpl + extends DatabaseManagerSupport +{ + public static final String WORK_PATH = "db"; + + private final File databasesDirectory; + + private final DatabaseRestorer databaseRestorer; + + @Inject + public DatabaseManagerImpl(final ApplicationDirectories applicationDirectories, + final DatabaseRestorer databaseRestorer) { + checkNotNull(applicationDirectories); + this.databasesDirectory = applicationDirectories.getWorkDirectory(WORK_PATH); + log.debug("Databases directory: {}", databasesDirectory); + this.databaseRestorer = checkNotNull(databaseRestorer); + } + + @VisibleForTesting + public DatabaseManagerImpl(final File databasesDirectory, + final DatabaseRestorer databaseRestorer) { + this.databasesDirectory = checkNotNull(databasesDirectory); + log.debug("Databases directory: {}", databasesDirectory); + this.databaseRestorer = checkNotNull(databaseRestorer); + } + + @ManagedAttribute + public File getDatabasesDirectory() { + return databasesDirectory; + } + + /** + * Returns the directory for the given named database. Directory may or may not exist. + */ + private File directory(final String name) throws IOException { + return new File(databasesDirectory, name).getCanonicalFile(); + } + + @Override + protected String connectionUri(final String name) { + try { + File dir = directory(name); + DirectoryHelper.mkdir(dir); + + // OHazelcastPlugin.onOpen() assumes that dbUri.startsWith("plocal:" + dbDirectory) + // We're well advised to meet that assumption or clustering won't work, more specifically we need to form + // plocal:/data/dbName for Unix/OSX and plocal:D:/data/dbName for Win (no slash before drive letter) + return "plocal:" + OFileUtils.getPath(dir.getAbsolutePath()).replace("//", "/"); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * When the database is being created, maybe import from the standard export location or restore backups. + * + * @see DatabaseExternalizer#EXPORT_FILENAME + * @see DatabaseExternalizer#EXPORT_GZ_FILENAME + * @see DatabaseExternalizerImpl#maybeImportFromStandardLocation(ODatabaseDocumentTx, File) + */ + @Override + protected void created(final ODatabaseDocumentTx db, final String name) throws Exception { + File dir = directory(name); + if (!databaseRestorer.maybeRestoreDatabase(db, name)) { + DatabaseExternalizerImpl externalizer = externalizer(name); + externalizer.maybeImportFromStandardLocation(db, dir); + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DatabaseServerImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DatabaseServerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..6b766e51101b6950d04b2c3976529be6a05eb959 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DatabaseServerImpl.java @@ -0,0 +1,395 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.common.event.EventAware; +import org.sonatype.nexus.common.log.LoggerLevelChangedEvent; +import org.sonatype.nexus.common.log.LoggersResetEvent; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.jmx.reflect.ManagedAttribute; +import org.sonatype.nexus.jmx.reflect.ManagedObject; +import org.sonatype.nexus.orient.DatabaseServer; +import org.sonatype.nexus.orient.OrientConfigCustomizer; +import org.sonatype.nexus.orient.entity.EntityHook; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.eventbus.Subscribe; +import com.orientechnologies.common.log.OLogManager; +import com.orientechnologies.orient.core.OConstants; +import com.orientechnologies.orient.core.Orient; +import com.orientechnologies.orient.core.config.OGlobalConfiguration; +import com.orientechnologies.orient.server.OServer; +import com.orientechnologies.orient.server.config.OServerCommandConfiguration; +import com.orientechnologies.orient.server.config.OServerConfiguration; +import com.orientechnologies.orient.server.config.OServerEntryConfiguration; +import com.orientechnologies.orient.server.config.OServerHandlerConfiguration; +import com.orientechnologies.orient.server.config.OServerNetworkConfiguration; +import com.orientechnologies.orient.server.config.OServerNetworkListenerConfiguration; +import com.orientechnologies.orient.server.config.OServerNetworkProtocolConfiguration; +import com.orientechnologies.orient.server.config.OServerParameterConfiguration; +import com.orientechnologies.orient.server.config.OServerSecurityConfiguration; +import com.orientechnologies.orient.server.config.OServerStorageConfiguration; +import com.orientechnologies.orient.server.config.OServerUserConfiguration; +import com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary; +import com.orientechnologies.orient.server.network.protocol.http.ONetworkProtocolHttpDb; +import com.orientechnologies.orient.server.network.protocol.http.command.get.OServerCommandGetStaticContent; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; + +/** + * Default {@link DatabaseServer} implementation. + * + * @since 3.0 + */ +@Named +@Singleton +@ManagedObject +public class DatabaseServerImpl + extends StateGuardLifecycleSupport + implements DatabaseServer, EventAware, EventAware.Asynchronous, Provider +{ + private static final String JUL_ROOT_LOGGER = ""; + + private static final String ORIENTDB_PARENT_LOGGER = "com"; + + private static final String ORIENTDB_LOGGER = ORIENTDB_PARENT_LOGGER + ".orientechnologies"; + + private final ApplicationDirectories applicationDirectories; + + private final List injectedHandlers; + + private final List configCustomizers; + + private final EntityHook entityHook; + + private final ClassLoader uberClassLoader; + + private final boolean binaryListenerEnabled; + + private final boolean httpListenerEnabled; + + private boolean dynamicPlugins; + + private final String binaryPortRange; + + private final String httpPortRange; + + private OServer orientServer; + + @Inject + public DatabaseServerImpl(final ApplicationDirectories applicationDirectories, + final List injectedHandlers, + final List configCustomizers, + @Named("nexus-uber") final ClassLoader uberClassLoader, + @Named("${nexus.orient.binaryListenerEnabled:-false}") final boolean binaryListenerEnabled, + @Named("${nexus.orient.httpListenerEnabled:-false}") final boolean httpListenerEnabled, + @Named("${nexus.orient.dynamicPlugins:-false}") final boolean dynamicPlugins, + @Named("${nexus.orient.binaryListener.portRange:-2424-2430}") final String binaryPortRange, + @Named("${nexus.orient.httpListener.portRange:-2480-2490}") final String httpPortRange, + final NodeAccess nodeAccess, + final EntityHook entityHook) + { + this.applicationDirectories = checkNotNull(applicationDirectories); + this.injectedHandlers = checkNotNull(injectedHandlers); + this.configCustomizers = checkNotNull(configCustomizers); + this.uberClassLoader = checkNotNull(uberClassLoader); + this.httpListenerEnabled = httpListenerEnabled; + this.dynamicPlugins = dynamicPlugins; + this.binaryPortRange = binaryPortRange; + this.httpPortRange = httpPortRange; + this.entityHook = checkNotNull(entityHook); + + if (nodeAccess.isClustered()) { + this.binaryListenerEnabled = true; // clustered mode requires binary listener + } + else { + this.binaryListenerEnabled = binaryListenerEnabled; + } + + log.info("OrientDB version: {}", OConstants.getVersion()); + } + + @ManagedAttribute + public boolean isBinaryListenerEnabled() { + return binaryListenerEnabled; + } + + @ManagedAttribute + public boolean isHttpListenerEnabled() { + return httpListenerEnabled; + } + + // FIXME: May need to revisit embedded configuration strategy, this is quite nasty + + @Override + protected void doStart() throws Exception { + // global startup + Orient.instance().startup(); + + // instance startup + OServer server = new OServer(); + configureOrientMinimumLogLevel(); + server.setExtensionClassLoader(uberClassLoader); + OServerConfiguration config = createConfiguration(); + server.startup(config); + + // remove Orient shutdown-hooks added during startup, we'll manage shutdown ourselves + Orient.instance().removeShutdownHook(); + server.removeShutdownHook(); + + // create default root user to avoid orientdb prompt on console + server.addUser(OServerConfiguration.DEFAULT_ROOT_USER, null, "*"); + + // Log global configuration + if (log.isDebugEnabled()) { + // dumpConfiguration() only accepts ancient stream api + String encoding = StandardCharsets.UTF_8.name(); + ByteArrayOutputStream buff = new ByteArrayOutputStream(); + OGlobalConfiguration.dumpConfiguration(new PrintStream(buff, true, encoding)); + log.debug("Global configuration:\n{}", new String(buff.toByteArray(), encoding)); + } + + Orient.instance().addDbLifecycleListener(entityHook); + + server.activate(); + log.info("Activated"); + + this.orientServer = server; + } + + private OServerConfiguration createConfiguration() { + File configDir = applicationDirectories.getConfigDirectory("fabric"); + + // FIXME: Unsure what this directory is used for + File orientDir = applicationDirectories.getWorkDirectory("orient"); + System.setProperty("orient.home", orientDir.getPath()); + System.setProperty(Orient.ORIENTDB_HOME, orientDir.getPath()); + + OServerConfiguration config = new OServerConfiguration(); + + // FIXME: Unsure what this is used for, its apparently assigned to xml location, but forcing it here + config.location = "DYNAMIC-CONFIGURATION"; + + File databaseDir = applicationDirectories.getWorkDirectory("db"); + File securityFile = new File(configDir, "orientdb-security.json"); + + config.properties = new OServerEntryConfiguration[]{ + new OServerEntryConfiguration("server.database.path", databaseDir.getPath()), + new OServerEntryConfiguration("server.security.file", securityFile.getPath()), + new OServerEntryConfiguration("plugin.dynamic", String.valueOf(dynamicPlugins)) + }; + + config.handlers = new ArrayList<>(injectedHandlers); + config.hooks = new ArrayList<>(); + + config.network = new OServerNetworkConfiguration(); + config.network.protocols = Lists.newArrayList( + new OServerNetworkProtocolConfiguration("binary", ONetworkProtocolBinary.class.getName()), + new OServerNetworkProtocolConfiguration("http", ONetworkProtocolHttpDb.class.getName()) + ); + + config.network.listeners = new ArrayList<>(); + + OServerNetworkListenerConfiguration binaryListener = null, httpListener = null; + + if (binaryListenerEnabled) { + binaryListener = createBinaryListener(binaryPortRange); + config.network.listeners.add(binaryListener); + } + + if (httpListenerEnabled) { + httpListener = createHttpListener(httpPortRange); + config.network.listeners.add(httpListener); + } + + config.storages = new OServerStorageConfiguration[]{}; + + config.users = new OServerUserConfiguration[]{ + new OServerUserConfiguration("admin", "admin", "*") + }; + + config.security = new OServerSecurityConfiguration(); + config.security.users = new ArrayList<>(); + config.security.resources = new ArrayList<>(); + + // latest advice is to disable DB compression as it doesn't buy much, + // also snappy has issues with use of native lib (unpacked under tmp) + OGlobalConfiguration.STORAGE_COMPRESSION_METHOD.setValue("nothing"); + + // ensure we don't set a file lock, which can behave badly on NFS https://issues.sonatype.org/browse/NEXUS-11289 + OGlobalConfiguration.FILE_LOCK.setValue(false); + + // disable auto removal of servers, SharedHazelcastPlugin removes gracefully shutdown nodes but for crashes and + // especially network partitions we don't want the write quorum getting lowered and endanger consistency + OGlobalConfiguration.DISTRIBUTED_AUTO_REMOVE_OFFLINE_SERVERS.setValue(-1); + + // Apply customizations to server configuration + configCustomizers.forEach((it) -> it.apply(config)); + + if (binaryListener != null) { + log.info("Binary listener enabled: {}:[{}]", binaryListener.ipAddress, binaryListener.portRange); + } + + if (httpListener != null) { + log.info("HTTP listener enabled: {}:[{}]", httpListener.ipAddress, httpListener.portRange); + } + + return config; + } + + private OServerNetworkListenerConfiguration createBinaryListener(final String binaryPortRange) { + OServerNetworkListenerConfiguration listener; + listener = new OServerNetworkListenerConfiguration(); + listener.ipAddress = "0.0.0.0"; + listener.portRange = binaryPortRange; + listener.protocol = "binary"; + listener.socket = "default"; + return listener; + } + + private OServerNetworkListenerConfiguration createHttpListener(final String httpPortRange) { + OServerNetworkListenerConfiguration listener; + listener = new OServerNetworkListenerConfiguration(); + listener.ipAddress = "0.0.0.0"; + listener.portRange = httpPortRange; + listener.protocol = "http"; + listener.socket = "default"; + listener.parameters = new OServerParameterConfiguration[] { + new OServerParameterConfiguration("network.http.charset", "UTF-8"), + new OServerParameterConfiguration("network.http.jsonResponseError", "true") + }; + + OServerCommandConfiguration getCommand = new OServerCommandConfiguration(); + getCommand.implementation = OServerCommandGetStaticContent.class.getName(); + getCommand.pattern = "GET|www GET|studio/ GET| GET|*.htm GET|*.html GET|*.xml GET|*.jpeg GET|*.jpg GET|*.png GET|*.gif GET|*.js GET|*.css GET|*.swf GET|*.ico GET|*.txt GET|*.otf GET|*.pjs GET|*.svg GET|*.json GET|*.woff GET|*.ttf GET|*.svgz"; + getCommand.parameters = new OServerEntryConfiguration[] { + new OServerEntryConfiguration("http.cache:*.htm *.html", "Cache-Control: no-cache, no-store, max-age=0, must-revalidate\\r\\nPragma: no-cache"), + new OServerEntryConfiguration("http.cache:default", "Cache-Control: max-age=120") + }; + listener.commands = new OServerCommandConfiguration[] { + getCommand + }; + return listener; + } + + @Override + protected void doStop() throws Exception { + // instance shutdown + orientServer.shutdown(); + orientServer = null; + + // global shutdown + Orient.instance().shutdown(); + + log.info("Shutdown"); + } + + @Override + @Guarded(by = STARTED) + public List databases() { + return ImmutableList.copyOf(orientServer.getAvailableStorageNames().keySet()); + } + + @Override + @Guarded(by = STARTED) + public OServer get() { + return orientServer; + } + + @Subscribe + public void onLoggerLevelChanged(final LoggerLevelChangedEvent event) { + String logger = event.getLogger(); + if (ORIENTDB_LOGGER.startsWith(logger) || logger.startsWith(ORIENTDB_LOGGER) + || org.slf4j.Logger.ROOT_LOGGER_NAME.equals(logger)) { + configureOrientMinimumLogLevel(); + } + } + + @Subscribe + public void onLoggersReset(final LoggersResetEvent event) { + configureOrientMinimumLogLevel(); + } + + /** + * Until OrientDB cleans up its logging infrastructure, this synchronizes its global minimum log level to the minimum + * log level configured for any of its loggers. + * + * @see http://www.prjhub.com/#/issues/3744 + * @see http://www.prjhub.com/#/issues/5327 + */ + private void configureOrientMinimumLogLevel() { + int minimumLevel = getOrientMininumLogLevel(); + log.debug("Configuring OrientDB global minimum log level to {}", minimumLevel); + OLogManager logManager = OLogManager.instance(); + logManager.setDebugEnabled(minimumLevel <= Level.FINE.intValue()); + logManager.setInfoEnabled(minimumLevel <= Level.INFO.intValue()); + logManager.setWarnEnabled(minimumLevel <= Level.WARNING.intValue()); + logManager.setErrorEnabled(minimumLevel <= Level.SEVERE.intValue()); + } + + private int getOrientMininumLogLevel() { + int minimumLogLevel = 0; + for (String loggerName : new String[] { ORIENTDB_LOGGER, ORIENTDB_PARENT_LOGGER, JUL_ROOT_LOGGER }) { + Integer logLevel = getSafeLogLevel(loggerName); + if (logLevel != null) { + minimumLogLevel = logLevel; + break; + } + } + String orientLoggerPrefix = ORIENTDB_LOGGER + '.'; + for (Enumeration en = LogManager.getLogManager().getLoggerNames(); en.hasMoreElements() + && minimumLogLevel > Level.FINE.intValue();) { + String loggerName = en.nextElement(); + if (!loggerName.startsWith(orientLoggerPrefix)) { + continue; + } + Integer logLevel = getSafeLogLevel(loggerName); + if (logLevel != null && logLevel < minimumLogLevel) { + minimumLogLevel = logLevel; + } + } + return minimumLogLevel; + } + + @Nullable + private Integer getSafeLogLevel(final String loggerName) { + Logger logger = LogManager.getLogManager().getLogger(loggerName); + if (logger == null) { + return null; + } + Level level = logger.getLevel(); + return (level != null) ? level.intValue() : null; + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DeploymentAccessImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DeploymentAccessImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..5939f5cd5837f459dab60e7ca652c1255d531a3c --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DeploymentAccessImpl.java @@ -0,0 +1,111 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +package org.sonatype.nexus.internal.orient; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.node.DeploymentAccess; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.orient.DatabaseInstance; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SCHEMAS; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; +import static org.sonatype.nexus.orient.DatabaseInstanceNames.CONFIG; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTx; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; + +/** + * Class used to identify a deployment uniquely and permanently. + * + * {@link DeploymentIdentifier#getId()} is generated at first run and not modifiable. + * + * @since 3.6.1 + */ +@Named +@ManagedLifecycle(phase = SCHEMAS) +@Singleton +public class DeploymentAccessImpl + extends StateGuardLifecycleSupport + implements DeploymentAccess +{ + private final Provider databaseInstance; + + private final DeploymentIdentifierEntityAdapter entityAdapter; + + private final NodeAccess nodeAccess; + + // cached local copy; initialized by #doStart + private String id; + + @Inject + public DeploymentAccessImpl(@Named(CONFIG) final Provider databaseInstance, + final NodeAccess nodeAccess) + { + this.databaseInstance = checkNotNull(databaseInstance); + this.entityAdapter = new DeploymentIdentifierEntityAdapter(); + this.nodeAccess = nodeAccess; + } + + @Override + protected void doStart() throws Exception { + try (ODatabaseDocumentTx tx = databaseInstance.get().connect()) { + entityAdapter.register(tx); + } + + this.id = inTxRetry(databaseInstance).call(db -> { + DeploymentIdentifier identifier = entityAdapter.get(db); + if (identifier == null) { + identifier = new DeploymentIdentifier(); + identifier.setId(nodeAccess.getId()); + entityAdapter.set(db, identifier); + log.info("Created new deployment identifier: {}", identifier); + } + return identifier.getId(); + }); + } + + @Override + @Guarded(by = STARTED) + public String getId() { + return id; + } + + @Override + @Guarded(by = STARTED) + public String getAlias() { + return inTx(databaseInstance).call(db -> { + DeploymentIdentifier identifier = entityAdapter.get(db); + return identifier.getAlias(); + }); + } + + @Override + @Guarded(by = STARTED) + public void setAlias(final String newAlias) { + inTxRetry(databaseInstance).run(db -> { + DeploymentIdentifier identifier = entityAdapter.get(db); + identifier.setAlias(newAlias); + entityAdapter.set(db, identifier); + }); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DeploymentIdentifier.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DeploymentIdentifier.java new file mode 100644 index 0000000000000000000000000000000000000000..cba8eec8a380ed57c34cde9f8eb19a52fcdd0644 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DeploymentIdentifier.java @@ -0,0 +1,55 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import org.sonatype.nexus.common.entity.AbstractEntity; + +/** + * Representation of a unique identifier for this NXRM deployment. + * + * {@link #getId()} is randomly generated at first launch for NXRM and is not modifiable. + * + * @since 3.6.1 + */ +class DeploymentIdentifier + extends AbstractEntity +{ + private String id; + private String alias; + + public String getId() { + return id; + } + + DeploymentIdentifier setId(final String id) { + this.id = id; + return this; + } + + public String getAlias() { + return alias; + } + + public DeploymentIdentifier setAlias(final String alias) { + this.alias = alias; + return this; + } + + @Override + public String toString() { + return "DeploymentIdentifier{" + + "id='" + id + '\'' + + ", alias='" + alias + '\'' + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DeploymentIdentifierEntityAdapter.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DeploymentIdentifierEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..b1693c434133f310f8023ce293ab26e050956535 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/DeploymentIdentifierEntityAdapter.java @@ -0,0 +1,68 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.entity.SingletonEntityAdapter; + +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; + +/** + * {@link SingletonEntityAdapter} to store our single {@link DeploymentIdentifier} record. + * + * @since 3.6.1 + */ +class DeploymentIdentifierEntityAdapter + extends SingletonEntityAdapter +{ + private static final String DB_CLASS = new OClassNameBuilder() + .type("deploymentidentifier") + .build(); + + static final String P_ID = "id"; + + static final String P_ALIAS = "alias"; + + public DeploymentIdentifierEntityAdapter() { + super(DB_CLASS); + } + + @Override + protected void defineType(final OClass type) { + type.createProperty(P_ID, OType.STRING) + .setMandatory(true) + .setNotNull(true); + type.createProperty(P_ALIAS, OType.STRING) + .setMandatory(false) + .setNotNull(false); + } + + @Override + protected DeploymentIdentifier newEntity() { + return new DeploymentIdentifier(); + } + + @Override + protected void readFields(final ODocument document, final DeploymentIdentifier entity) { + entity.setId(document.field(P_ID, OType.STRING)) + .setAlias(document.field(P_ALIAS, OType.STRING)); + } + + @Override + protected void writeFields(final ODocument document, final DeploymentIdentifier entity) { + document.field(P_ID, entity.getId()); + document.field(P_ALIAS, entity.getAlias()); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/JmxHandlerConfiguration.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/JmxHandlerConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..984f097ce74a1cb93a2193489fcbc28feefdfe00 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/JmxHandlerConfiguration.java @@ -0,0 +1,39 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import javax.inject.Named; +import javax.inject.Singleton; + +import com.orientechnologies.orient.server.config.OServerHandlerConfiguration; +import com.orientechnologies.orient.server.config.OServerParameterConfiguration; +import com.orientechnologies.orient.server.handler.OJMXPlugin; + +/** + * Enables the JMX plugin on the OrientDB server. + * + * @since 3.0 + */ +@Named +@Singleton +public class JmxHandlerConfiguration + extends OServerHandlerConfiguration +{ + public JmxHandlerConfiguration() { + clazz = OJMXPlugin.class.getName(); + parameters = new OServerParameterConfiguration[] { + new OServerParameterConfiguration("enabled", "true"), + new OServerParameterConfiguration("profilerManaged", "true") + }; + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/LogConfigurationCustomizerImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/LogConfigurationCustomizerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..bf7fcb767c47ca3330343aa5d677acec3da1e648 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/LogConfigurationCustomizerImpl.java @@ -0,0 +1,49 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import javax.inject.Named; +import javax.inject.Singleton; + +import com.orientechnologies.orient.core.storage.impl.local.paginated.OLocalPaginatedStorage; + +import org.sonatype.nexus.common.log.LogConfigurationCustomizer; + +import static org.sonatype.nexus.common.log.LoggerLevel.DEFAULT; +import static org.sonatype.nexus.common.log.LoggerLevel.INFO; +import static org.sonatype.nexus.common.log.LoggerLevel.OFF; + +/** + * OrientDB {@link LogConfigurationCustomizer}. + * + * @since 3.0 + */ +@Named +@Singleton +public class LogConfigurationCustomizerImpl + implements LogConfigurationCustomizer +{ + @Override + public void customize(final Configuration config) { + config.setLoggerLevel("org.sonatype.nexus.orient", DEFAULT); + config.setLoggerLevel("org.sonatype.nexus.internal.orient", DEFAULT); + config.setLoggerLevel("com.orientechnologies", DEFAULT); + + // leave explain logging off by default regardless of root level because it incurs a penalty + // and disables the connection pool; so it's better to explicitly enable it when you need it + config.setLoggerLevel("org.sonatype.nexus.orient.explain", OFF); + + // OLocalPaginatedStorage produces too much output if the ROOT logger is switched to DEBUG + config.setLoggerLevel(OLocalPaginatedStorage.class.getName(), INFO); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/OrientBootstrap.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/OrientBootstrap.java new file mode 100644 index 0000000000000000000000000000000000000000..08cbb8f963f9428bf1b1d437a0319474d242e030 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/OrientBootstrap.java @@ -0,0 +1,106 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import javax.annotation.Priority; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.lifecycle.Lifecycles; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.orient.DatabaseManager; +import org.sonatype.nexus.orient.DatabaseServer; + +import com.orientechnologies.orient.core.compression.OCompression; +import com.orientechnologies.orient.core.compression.OCompressionFactory; +import com.orientechnologies.orient.core.sql.OSQLEngine; +import com.orientechnologies.orient.core.sql.functions.OSQLFunctionAbstract; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.STORAGE; + +/** + * Orient bootstrap. + * + * @since 3.0 + */ +@Named +@ManagedLifecycle(phase = STORAGE) +@Priority(Integer.MAX_VALUE) // make sure this starts first +@Singleton +public class OrientBootstrap + extends StateGuardLifecycleSupport +{ + private final NodeAccess nodeAccess; + + private final Provider databaseServer; + + private final Provider databaseManager; + + @Inject + public OrientBootstrap(final NodeAccess nodeAccess, + final Provider databaseServer, + final Provider databaseManager, + final Iterable managedCompressions, + final Iterable functions) + { + this.nodeAccess = checkNotNull(nodeAccess); + this.databaseServer = checkNotNull(databaseServer); + this.databaseManager = checkNotNull(databaseManager); + registerCompressions(checkNotNull(managedCompressions)); + registerCustomFunctions(checkNotNull(functions)); + } + + + @Override + protected void doStart() throws Exception { + nodeAccess.start(); + + databaseServer.get().start(); + + Lifecycles.start(databaseManager.get()); + } + + @Override + protected void doStop() throws Exception { + Lifecycles.stop(databaseManager.get()); + + databaseServer.get().stop(); + + nodeAccess.stop(); + } + + private void registerCompressions(final Iterable compressions) { + for (final OCompression compression : compressions) { + try { + log.debug("Registering OrientDB compression {} as '{}'", compression, compression.name()); + OCompressionFactory.INSTANCE.register(compression); + } + catch (final IllegalArgumentException e) { + log.debug("An OrientDB compression named '{}' was already registered", compression.name(), e); + } + } + } + + private void registerCustomFunctions(final Iterable functions) { + log.debug("Registering custom OrientDB functions"); + for (OSQLFunctionAbstract function : functions) { + log.debug("Registering OrientDB function " + function.getName()); + OSQLEngine.getInstance().registerFunction(function.getName(), function); + } + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/OrientCommands.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/OrientCommands.java new file mode 100644 index 0000000000000000000000000000000000000000..2333c1f623028edb9d2bdba2f94138e79d0d0ab1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/OrientCommands.java @@ -0,0 +1,156 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import java.lang.reflect.Method; +import java.util.List; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.nexus.commands.CommandSupport; +import org.sonatype.nexus.common.text.Strings2; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.orientechnologies.common.console.annotation.ConsoleCommand; +import com.orientechnologies.orient.console.OConsoleDatabaseApp; +import org.apache.karaf.shell.api.action.Action; +import org.apache.karaf.shell.api.console.Completer; +import org.apache.karaf.shell.api.console.Function; +import org.apache.karaf.shell.api.console.Session; +import org.apache.karaf.shell.api.console.SessionFactory; +import org.eclipse.sisu.EagerSingleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.CaseFormat.LOWER_CAMEL; +import static com.google.common.base.CaseFormat.LOWER_UNDERSCORE; + +/** + * Registers OrientDB commands with Karaf's shell; handles execution requests and displays results. + * + * @since 3.0 + */ +@Named +@EagerSingleton +public class OrientCommands + extends OConsoleDatabaseApp +{ + private static final Logger log = LoggerFactory.getLogger(OrientCommands.class); + + private static final String SCOPE = "orient"; + + private final SessionFactory sessionFactory; + + @Inject + public OrientCommands(@Nullable final SessionFactory sessionFactory) { + super(new String[0]); + + this.sessionFactory = sessionFactory; // might be null during tests + + for (Method method : getConsoleMethods().keySet()) { + if (method.isAnnotationPresent(ConsoleCommand.class)) { + register(createOrientCommand(method)); + } + } + + onBefore(); // set OrientDB command defaults + } + + private void register(final Function command) { + log.debug("Registering command: {}", command); + if (sessionFactory != null) { + sessionFactory.getRegistry().register(command); + } + else { + log.warn("Unable to register command, sessionFactory is null: {}", command); + } + } + + @Override + protected void printApplicationInfo() { + // hide banner as it doesn't apply here + } + + private Function createOrientCommand(final Method method) { + return new CommandSupport(Action.class) + { + // OrientDB expects the method name to be transformed from camel-case into lower-case with spaces + private final String command = LOWER_CAMEL.to(LOWER_UNDERSCORE, method.getName()).replace('_', ' '); + + @Override + public Object execute(final Session session, final List arguments) throws Exception { + return OrientCommands.this.execute(method, command, arguments); + } + + @Override + public String getScope() { + return SCOPE; + } + + @Override + public String getName() { + return method.getName(); + } + + @Override + public String getDescription() { + return method.getAnnotation(ConsoleCommand.class).description(); + } + + @Override + public Completer getCompleter(final boolean scoped) { + return null; // no special completion + } + + @Override + public Action createNewAction(final Session session) { + return null; // not used + } + + @Override + protected void releaseAction(final Action action) { + // no-op + } + + @Override + public String toString() { + return method.toString(); + } + }; + } + + public Object execute(final Method method, final String command, final List params) { + if (params.isEmpty() || !"--help".equals(params.get(0))) { + + // rebuild expression so OrientDB can re-parse it to handle optional params, etc. + List expression = ImmutableList.builder().add(command).addAll(params).build(); + execute(Joiner.on(' ').useForNull("null").join(expression)); + + return Strings2.NL; + } + + syntaxError(command, method); + return ""; // avoid spurious newline + } + + @Override + public void error(final String message, final Object... args) { + // clean up error messages to remove redundant/irrelevant content + if (!message.contains("Unrecognized command")) { + super.error(message.replaceFirst("(?s)!Wrong syntax.*Expected", "Syntax"), args); + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/OrientModule.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/OrientModule.java new file mode 100644 index 0000000000000000000000000000000000000000..0bdcf638acdfe4a37e9481d039f35cc99c84bb6d --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/OrientModule.java @@ -0,0 +1,53 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import org.sonatype.nexus.orient.DatabaseManager; +import org.sonatype.nexus.orient.DatabaseServer; +import org.sonatype.nexus.orient.EncryptedRecordIdObfuscator; +import org.sonatype.nexus.orient.RecordIdObfuscator; + +import com.google.inject.AbstractModule; + +/** + * Orient module. + * + * @since 3.0 + */ +public class OrientModule + extends AbstractModule +{ + @Override + protected void configure() { + preloadOrientEngine(); + + // configure default implementations + bind(DatabaseServer.class).to(DatabaseServerImpl.class); + bind(DatabaseManager.class).to(DatabaseManagerImpl.class); + bind(RecordIdObfuscator.class).to(EncryptedRecordIdObfuscator.class); + } + + /** + * Pre-loads OrientDB using empty (system) TCCL so javax.script engines can be found. + */ + private static void preloadOrientEngine() { + ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(null); + com.orientechnologies.orient.core.Orient.instance(); + } + finally { + Thread.currentThread().setContextClassLoader(tccl); + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/PasswordSanitizedJsonSource.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/PasswordSanitizedJsonSource.java new file mode 100644 index 0000000000000000000000000000000000000000..ec2aa38887d02538c0df3a9e9e8b924e2fd50ae4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/PasswordSanitizedJsonSource.java @@ -0,0 +1,71 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Provider; + +import org.sonatype.nexus.orient.DatabaseExternalizer; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.supportzip.GeneratedContentSourceSupport; +import org.sonatype.nexus.common.io.SanitizingJsonOutputStream; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Specialized {@link GeneratedContentSourceSupport} that blanks out known password fields in JSON. + * + * @since 3.0 + */ +class PasswordSanitizedJsonSource + extends GeneratedContentSourceSupport +{ + private static final List FIELDS = Arrays.asList( + "applicationPassword", "password", "systemPassword", "secret"); + + private static final Set EXCLUDED_CLASSES = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList("api_key", "usertoken_record"))); + + private static final String REPLACEMENT = "**REDACTED**"; + + private final Provider databaseInstance; + + /** + * Constructor. + */ + public PasswordSanitizedJsonSource(final Type type, + final String path, + final Provider databaseInstance) + { + super(type, path, Priority.REQUIRED); + this.databaseInstance = checkNotNull(databaseInstance); + } + + @Override + protected void generate(final File file) throws Exception { + try (OutputStream output = new SanitizingJsonOutputStream( + new BufferedOutputStream(new FileOutputStream(file)), FIELDS, REPLACEMENT)) { + DatabaseExternalizer externalizer = databaseInstance.get().externalizer(); + externalizer.export(output, EXCLUDED_CLASSES); + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/SecurityDatabase.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/SecurityDatabase.java new file mode 100644 index 0000000000000000000000000000000000000000..b6e255a927b8a9fa27899d0aa9c7b7d666267c8f --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/SecurityDatabase.java @@ -0,0 +1,92 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.orient.DatabaseExternalizer; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseManager; +import org.sonatype.nexus.supportzip.SupportBundle; +import org.sonatype.nexus.supportzip.SupportBundle.ContentSource.Type; +import org.sonatype.nexus.supportzip.SupportBundleCustomizer; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Shared {@code security} database components. + * + * @since 3.0 + */ +@SuppressWarnings("UnusedDeclaration") +public class SecurityDatabase +{ + private SecurityDatabase() { + // empty + } + + public static final String NAME = "security"; + + /** + * Shared {@code security} database instance provider. + */ + @Named(NAME) + @Singleton + public static class ProviderImpl + implements Provider + { + private final DatabaseManager databaseManager; + + @Inject + public ProviderImpl(final DatabaseManager databaseManager) { + this.databaseManager = checkNotNull(databaseManager); + } + + @Override + public DatabaseInstance get() { + return databaseManager.instance(NAME); + } + } + + /** + * Includes export of the {@code security} database in support-zip. + */ + @Named + @Singleton + public static class SupportBundleCustomizerImpl + extends ComponentSupport + implements SupportBundleCustomizer + { + private final Provider databaseInstance; + + @Inject + public SupportBundleCustomizerImpl(@Named(NAME) final Provider databaseInstance) { + this.databaseInstance = checkNotNull(databaseInstance); + } + + @Override + public void customize(final SupportBundle supportBundle) { + String path = String.format("work/%s/%s/%s", + DatabaseManagerImpl.WORK_PATH, + databaseInstance.get().getName(), + DatabaseExternalizer.EXPORT_FILENAME + ); + + supportBundle.add(new PasswordSanitizedJsonSource(Type.SECURITY, path, databaseInstance)); + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/SecurityDatabaseCheckpoint.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/SecurityDatabaseCheckpoint.java new file mode 100644 index 0000000000000000000000000000000000000000..1e058509a9d4a31f9a2310177777536535c6a0f6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/orient/SecurityDatabaseCheckpoint.java @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.common.upgrade.Checkpoints; +import org.sonatype.nexus.orient.DatabaseCheckpointSupport; +import org.sonatype.nexus.orient.DatabaseInstance; + +/** + * Upgrade checkpoint for the "security" database. + * + * @since 3.1 + */ +@Named +@Singleton +@Checkpoints(model = SecurityDatabase.NAME) +public class SecurityDatabaseCheckpoint + extends DatabaseCheckpointSupport +{ + @Inject + public SecurityDatabaseCheckpoint(@Named(SecurityDatabase.NAME) final Provider databaseInstance, + final ApplicationDirectories appDirectories) + { + super(SecurityDatabase.NAME, databaseInstance, appDirectories); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/AuthenticationEventSubscriber.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/AuthenticationEventSubscriber.java new file mode 100644 index 0000000000000000000000000000000000000000..6bc57ba0ed9a2fe0d02768457945532ed3e49163 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/AuthenticationEventSubscriber.java @@ -0,0 +1,63 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.event.EventAware; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.security.ClientInfo; +import org.sonatype.nexus.security.ClientInfoProvider; +import org.sonatype.nexus.security.authc.AuthenticationEvent; +import org.sonatype.nexus.security.authc.NexusAuthenticationEvent; + +import com.google.common.eventbus.Subscribe; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Forwards {@link AuthenticationEvent} as {@link NexusAuthenticationEvent}. + * + * @since 3.0 + */ +@Named +@Singleton +public class AuthenticationEventSubscriber + implements EventAware +{ + private final Provider eventManager; + + private final Provider clientInfoProvider; + + @Inject + public AuthenticationEventSubscriber(final Provider eventManager, + final Provider clientInfoProvider) + { + this.eventManager = checkNotNull(eventManager); + this.clientInfoProvider = checkNotNull(clientInfoProvider); + } + + @Subscribe + public void on(final AuthenticationEvent event) { + ClientInfo clientInfo = clientInfoProvider.get().getCurrentThreadClientInfo(); + eventManager.get().post(new NexusAuthenticationEvent( + clientInfo == null + ? new ClientInfo(event.getUserId(), null, null) + : new ClientInfo(event.getUserId(), clientInfo.getRemoteIP(), clientInfo.getUserAgent()), + event.isSuccessful() + )); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/LogConfigurationCustomizerImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/LogConfigurationCustomizerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..b758030c21dc7d407fba716ea186c24b5181dfd8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/LogConfigurationCustomizerImpl.java @@ -0,0 +1,38 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.log.LogConfigurationCustomizer; +import org.sonatype.nexus.common.log.LoggerLevel; + +/** + * Security {@link LogConfigurationCustomizer}. + * + * @since 3.0 + */ +@Named +@Singleton +public class LogConfigurationCustomizerImpl + implements LogConfigurationCustomizer +{ + @Override + public void customize(final Configuration configuration) { + configuration.setLoggerLevel("org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter", LoggerLevel.INFO); + configuration.setLoggerLevel("org.apache.shiro.web.filter.mgt.DefaultFilterChainManager", LoggerLevel.INFO); + configuration.setLoggerLevel("org.sonatype.nexus.security", LoggerLevel.DEFAULT); + configuration.setLoggerLevel("org.sonatype.nexus.internal.security", LoggerLevel.DEFAULT); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/NexusSecurityContributor.groovy b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/NexusSecurityContributor.groovy new file mode 100644 index 0000000000000000000000000000000000000000..c2664048227d00ab0180ae611e8421503768f86b --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/NexusSecurityContributor.groovy @@ -0,0 +1,336 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security + +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.nexus.security.Roles +import org.sonatype.nexus.security.config.CPrivilege +import org.sonatype.nexus.security.config.CRole +import org.sonatype.nexus.security.config.MemorySecurityConfiguration +import org.sonatype.nexus.security.config.SecurityContributor + +/** + * Default Nexus security configuration. + * + * @since 3.0 + */ +@Named +@Singleton +class NexusSecurityContributor + implements SecurityContributor +{ + @Override + MemorySecurityConfiguration getContribution() { + return new MemorySecurityConfiguration( + privileges: [ + /** + * Grants permission for anything in the 'nexus:' namespace. + */ + new CPrivilege( + id: 'nx-all', + type: 'wildcard', + description: 'All permissions', + properties: [ + pattern: 'nexus:*' + ] + ), + + // + // nexus:settings + // + + new CPrivilege( + id: 'nx-settings-all', + description: 'All permissions for Settings', + type: 'application', + properties: [ + domain : 'settings', + actions: '*' + ] + ), + new CPrivilege( + id: 'nx-settings-read', + description: 'Read permission for Settings', + type: 'application', + properties: [ + domain : 'settings', + actions: 'read' + ] + ), + new CPrivilege( + id: 'nx-settings-update', + description: 'Update permission for Settings', + type: 'application', + properties: [ + domain : 'settings', + actions: 'update,read' + ] + ), + + // + // nexus:bundles + // + + new CPrivilege( + id: 'nx-bundles-all', + description: 'All permissions for Bundles', + type: 'application', + properties: [ + domain : 'bundles', + actions: '*' + ] + ), + new CPrivilege( + id: 'nx-bundles-read', + description: 'Read permission for Bundles', + type: 'application', + properties: [ + domain : 'bundles', + actions: 'read' + ] + ), + + // + // nexus:search + // + + new CPrivilege( + id: 'nx-search-read', + description: 'Read permission for Search', + type: 'application', + properties: [ + domain : 'search', + actions: 'read' + ] + ), + + // + // nexus:apikey + // + + new CPrivilege( + id: 'nx-apikey-all', + description: 'All permissions for APIKey', + type: 'application', + properties: [ + domain : 'apikey', + actions: '*' + ] + ), + + // + // nexus:privileges + // + + new CPrivilege( + id: 'nx-privileges-all', + description: 'All permissions for Privileges', + type: 'application', + properties: [ + domain : 'privileges', + actions: '*' + ] + ), + new CPrivilege( + id: 'nx-privileges-create', + description: 'Create permission for Privileges', + type: 'application', + properties: [ + domain : 'privileges', + actions: 'create,read' + ] + ), + new CPrivilege( + id: 'nx-privileges-read', + description: 'Read permission for Privileges', + type: 'application', + properties: [ + domain : 'privileges', + actions: 'read' + ] + ), + new CPrivilege( + id: 'nx-privileges-update', + description: 'Update permission for Privileges', + type: 'application', + properties: [ + domain : 'privileges', + actions: 'update,read' + ] + ), + new CPrivilege( + id: 'nx-privileges-delete', + description: 'Delete permission for Privileges', + type: 'application', + properties: [ + domain : 'privileges', + actions: 'delete,read' + ] + ), + + // + // nexus:roles + // + + new CPrivilege( + id: 'nx-roles-all', + description: 'All permissions for Roles', + type: 'application', + properties: [ + domain : 'roles', + actions: '*' + ] + ), + new CPrivilege( + id: 'nx-roles-create', + description: 'Create permission for Roles', + type: 'application', + properties: [ + domain : 'roles', + actions: 'create,read' + ] + ), + new CPrivilege( + id: 'nx-roles-read', + description: 'Read permission for Roles', + type: 'application', + properties: [ + domain : 'roles', + actions: 'read' + ] + ), + new CPrivilege( + id: 'nx-roles-update', + description: 'Update permission for Roles', + type: 'application', + properties: [ + domain : 'roles', + actions: 'update,read' + ] + ), + new CPrivilege( + id: 'nx-roles-delete', + description: 'Delete permission for Roles', + type: 'application', + properties: [ + domain : 'roles', + actions: 'delete,read' + ] + ), + + // + // nexus:users + // + + new CPrivilege( + id: 'nx-users-all', + description: 'All permissions for Users', + type: 'application', + properties: [ + domain : 'users', + actions: '*' + ] + ), + new CPrivilege( + id: 'nx-users-create', + description: 'Create permission for Users', + type: 'application', + properties: [ + domain : 'users', + actions: 'create,read' + ] + ), + new CPrivilege( + id: 'nx-users-read', + description: 'Read permission for Users', + type: 'application', + properties: [ + domain : 'users', + actions: 'read' + ] + ), + new CPrivilege( + id: 'nx-users-update', + description: 'Update permission for Users', + type: 'application', + properties: [ + domain : 'users', + actions: 'update,read' + ] + ), + new CPrivilege( + id: 'nx-users-delete', + description: 'Delete permission for Users', + type: 'application', + properties: [ + domain : 'users', + actions: 'delete,read' + ] + ), + + // FIXME: Sort out what the use-case is for this distinct permission, consider nexus:users:change-password? + new CPrivilege( + id: 'nx-userschangepw', + description: 'Change password permission', + type: 'application', + properties: [ + domain : 'userschangepw', + actions: 'create,read' + ] + ), + + // nexus:component + + new CPrivilege( + id: 'nx-component-upload', + description: 'Upload component permission', + type: 'application', + properties: [ + domain : 'component', + actions: 'add' + ] + ), + ], + + roles: [ + /** + * Admin role grants all permissions (ie. super-user) + */ + new CRole( + id: Roles.ADMIN_ROLE_ID, + description: 'Administrator Role', + privileges: [ + 'nx-all' + ] + ), + + /** + * Anonymous role grants permissions to non-authenticated users. + */ + new CRole( + id: Roles.ANONYMOUS_ROLE_ID, + description: 'Anonymous Role', + privileges: [ + 'nx-search-read', + 'nx-healthcheck-read', + 'nx-repository-view-*-*-browse', + 'nx-repository-view-*-*-read' + ] + ) + ] + ) + } +} + diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/SecurityModule.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/SecurityModule.java new file mode 100644 index 0000000000000000000000000000000000000000..b243efefbdd8f1c3921dd7bd4ea345499cc3fbcc --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/SecurityModule.java @@ -0,0 +1,78 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.internal.security.anonymous.AnonymousConfigurationStore; +import org.sonatype.nexus.internal.security.anonymous.OrientAnonymousConfigurationStore; +import org.sonatype.nexus.internal.security.realm.OrientRealmConfigurationStore; +import org.sonatype.nexus.security.FilterProviderSupport; +import org.sonatype.nexus.security.anonymous.AnonymousFilter; +import org.sonatype.nexus.security.authc.NexusAuthenticationFilter; +import org.sonatype.nexus.security.authc.apikey.ApiKeyAuthenticationFilter; +import org.sonatype.nexus.security.authz.PermissionsFilter; +import org.sonatype.nexus.security.realm.RealmConfigurationStore; + +import com.google.inject.AbstractModule; + +import static org.sonatype.nexus.security.FilterProviderSupport.filterKey; + +/** + * Security module. + */ +@Named +public class SecurityModule + extends AbstractModule +{ + @Override + protected void configure() { + bind(filterKey(AnonymousFilter.NAME)).to(AnonymousFilter.class); + bind(filterKey(NexusAuthenticationFilter.NAME)).to(NexusAuthenticationFilter.class); + bind(filterKey(ApiKeyAuthenticationFilter.NAME)).to(ApiKeyAuthenticationFilter.class); + bind(filterKey(PermissionsFilter.NAME)).to(PermissionsFilter.class); + + // FIXME: Sort out, and deal with naming the "authcBasic" are presently auth-token bits + bind(filterKey("authcBasic")).toProvider(AuthcBasicFilterProvider.class); + + // FIXME: This likely should be normalized with the auth-token bits + bind(filterKey("authcApiKey")).toProvider(AuthcApiKeyFilterProvider.class); + + bind(AnonymousConfigurationStore.class).to(OrientAnonymousConfigurationStore.class); + bind(RealmConfigurationStore.class).to(OrientRealmConfigurationStore.class); + } + + // FIXME: Probably do not need provider here at all anymore + + @Singleton + static class AuthcBasicFilterProvider + extends FilterProviderSupport + { + @Inject + AuthcBasicFilterProvider(final NexusAuthenticationFilter filter) { + super(filter); + } + } + + @Singleton + static class AuthcApiKeyFilterProvider + extends FilterProviderSupport + { + @Inject + AuthcApiKeyFilterProvider(final ApiKeyAuthenticationFilter filter) { + super(filter); + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationCreatedEvent.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationCreatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..c2d8619fec73e97c1d54251c8aaf9d85f5d03d71 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationCreatedEvent.java @@ -0,0 +1,31 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.anonymous; + +import org.sonatype.nexus.common.entity.EntityCreatedEvent; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.security.anonymous.AnonymousConfiguration; + +/** + * {@link AnonymousConfiguration} created event. + * + * @since 3.2 + */ +public class AnonymousConfigurationCreatedEvent + extends EntityCreatedEvent + implements AnonymousConfigurationEvent +{ + public AnonymousConfigurationCreatedEvent(final EntityMetadata metadata) { + super(metadata); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationDeletedEvent.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationDeletedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..2699f02a1f87b745c79d24c348b15ab831e50be1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationDeletedEvent.java @@ -0,0 +1,31 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.anonymous; + +import org.sonatype.nexus.common.entity.EntityDeletedEvent; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.security.anonymous.AnonymousConfiguration; + +/** + * {@link AnonymousConfiguration} deleted event. + * + * @since 3.2 + */ +public class AnonymousConfigurationDeletedEvent + extends EntityDeletedEvent + implements AnonymousConfigurationEvent +{ + public AnonymousConfigurationDeletedEvent(final EntityMetadata metadata) { + super(metadata); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationEntityAdapter.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..f0fb2e646d733f3028ae49004335da00a199ff72 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationEntityAdapter.java @@ -0,0 +1,104 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.anonymous; + +import javax.annotation.Nullable; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.entity.EntityEvent; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.entity.AttachedEntityMetadata; +import org.sonatype.nexus.orient.entity.SingletonEntityAdapter; +import org.sonatype.nexus.security.anonymous.AnonymousConfiguration; + +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; + +/** + * {@link AnonymousConfiguration} entity-adapter. + * + * @since 3.0 + */ +@Named +@Singleton +public class AnonymousConfigurationEntityAdapter + extends SingletonEntityAdapter +{ + private static final String DB_CLASS = new OClassNameBuilder() + .type("anonymous") + .build(); + + private static final String P_ENABLED = "enabled"; + + private static final String P_USER_ID = "user_id"; + + private static final String P_REALM_NAME = "realm_name"; + + public AnonymousConfigurationEntityAdapter() { + super(DB_CLASS); + } + + @Override + protected void defineType(final OClass type) { + type.createProperty(P_ENABLED, OType.BOOLEAN) + .setMandatory(true) + .setNotNull(true); + type.createProperty(P_USER_ID, OType.STRING) + .setNotNull(true); + type.createProperty(P_REALM_NAME, OType.STRING) + .setNotNull(true); + } + + @Override + protected AnonymousConfiguration newEntity() { + return new AnonymousConfiguration(); + } + + @Override + protected void readFields(final ODocument document, final AnonymousConfiguration entity) { + boolean enabled = document.field(P_ENABLED, OType.BOOLEAN); + String userId = document.field(P_USER_ID, OType.STRING); + String realmName = document.field(P_REALM_NAME, OType.STRING); + + entity.setEnabled(enabled); + entity.setUserId(userId); + entity.setRealmName(realmName); + } + + @Override + protected void writeFields(final ODocument document, final AnonymousConfiguration entity) { + document.field(P_ENABLED, entity.isEnabled()); + document.field(P_USER_ID, entity.getUserId()); + document.field(P_REALM_NAME, entity.getRealmName()); + } + + @Override + @Nullable + public EntityEvent newEvent(final ODocument document, final EventKind eventKind) { + EntityMetadata metadata = new AttachedEntityMetadata(this, document); + log.debug("Emitted {} event with metadata {}", eventKind, metadata); + switch (eventKind) { + case CREATE: + return new AnonymousConfigurationCreatedEvent(metadata); + case UPDATE: + return new AnonymousConfigurationUpdatedEvent(metadata); + case DELETE: + return new AnonymousConfigurationDeletedEvent(metadata); + default: + return null; + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationEvent.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..91f2e25807e0bc57e4e6565a6eed1dafba44dee9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationEvent.java @@ -0,0 +1,27 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.anonymous; + +import org.sonatype.nexus.security.anonymous.AnonymousConfiguration; + +/** + * {@link AnonymousConfiguration} event. + * + * @since 3.2 + */ +public interface AnonymousConfigurationEvent +{ + boolean isLocal(); + + String getRemoteNodeId(); +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationStore.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationStore.java new file mode 100644 index 0000000000000000000000000000000000000000..3b1c5aa2c5cba02bebd70995dabe74263af7708e --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationStore.java @@ -0,0 +1,32 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.anonymous; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.security.anonymous.AnonymousConfiguration; + +/** + * {@link AnonymousConfiguration} store. + * + * @since 3.0 + */ +public interface AnonymousConfigurationStore +{ + // TODO: Sort out exceptions, both of these should have some expected exceptions + + @Nullable + AnonymousConfiguration load(); + + void save(AnonymousConfiguration configuration); +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationUpdatedEvent.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationUpdatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..e098596935282e9595f634f11cc4cb80aeac32c9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousConfigurationUpdatedEvent.java @@ -0,0 +1,31 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.anonymous; + +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.common.entity.EntityUpdatedEvent; +import org.sonatype.nexus.security.anonymous.AnonymousConfiguration; + +/** + * {@link AnonymousConfiguration} updated event. + * + * @since 3.2 + */ +public class AnonymousConfigurationUpdatedEvent + extends EntityUpdatedEvent + implements AnonymousConfigurationEvent +{ + public AnonymousConfigurationUpdatedEvent(final EntityMetadata metadata) { + super(metadata); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousManagerImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousManagerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..5c543ebcea9173312e78b575a3e93973be7eb8f4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/AnonymousManagerImpl.java @@ -0,0 +1,180 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.anonymous; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.goodies.common.Mutex; +import org.sonatype.nexus.common.event.EventAware; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.jmx.reflect.ManagedAttribute; +import org.sonatype.nexus.jmx.reflect.ManagedObject; +import org.sonatype.nexus.security.anonymous.AnonymousConfiguration; +import org.sonatype.nexus.security.anonymous.AnonymousConfigurationChangedEvent; +import org.sonatype.nexus.security.anonymous.AnonymousManager; +import org.sonatype.nexus.security.anonymous.AnonymousPrincipalCollection; + +import com.google.common.eventbus.Subscribe; +import org.apache.shiro.subject.PrincipalCollection; +import org.apache.shiro.subject.Subject; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default {@link AnonymousManagerImpl}. + * + * @since 3.0 + */ +@Named +@Singleton +@ManagedObject +public class AnonymousManagerImpl + extends ComponentSupport + implements AnonymousManager, EventAware +{ + private final EventManager eventManager; + + private final AnonymousConfigurationStore store; + + private final Provider defaults; + + private final Mutex lock = new Mutex(); + + private AnonymousConfiguration configuration; + + @Inject + public AnonymousManagerImpl(final EventManager eventManager, + final AnonymousConfigurationStore store, + @Named("initial") final Provider defaults) + { + this.eventManager = checkNotNull(eventManager); + this.store = checkNotNull(store); + log.debug("Store: {}", store); + this.defaults = checkNotNull(defaults); + log.debug("Defaults: {}", defaults); + } + + // + // Configuration + // + + /** + * Load configuration from store, or use defaults. + */ + private AnonymousConfiguration loadConfiguration() { + AnonymousConfiguration model = store.load(); + + // use defaults if no configuration was loaded from the store + if (model == null) { + model = defaults.get(); + + // default config must not be null + checkNotNull(model); + + log.info("Using default configuration: {}", model); + } + else { + log.info("Loaded configuration: {}", model); + } + + return model; + } + + /** + * Return configuration, loading if needed. + * + * The result model should be considered _immutable_ unless copied. + */ + private AnonymousConfiguration getConfigurationInternal() { + synchronized (lock) { + if (configuration == null) { + configuration = loadConfiguration(); + } + return configuration; + } + } + + /** + * Return _copy_ of configuration. + */ + @Override + public AnonymousConfiguration getConfiguration() { + return getConfigurationInternal().copy(); + } + + @Override + public void setConfiguration(final AnonymousConfiguration configuration) { + checkNotNull(configuration); + + AnonymousConfiguration model = configuration.copy(); + // TODO: Validate configuration before saving? Or leave to ext.direct? Should we try and verify the user exists? + + log.info("Saving configuration: {}", model); + synchronized (lock) { + store.save(model); + this.configuration = model; + } + + eventManager.post(new AnonymousConfigurationChangedEvent(model)); + } + + // + // Helpers + // + + @Override + @ManagedAttribute + public boolean isEnabled() { + return getConfigurationInternal().isEnabled(); + } + + @Override + public Subject buildSubject() { + AnonymousConfiguration model = getConfigurationInternal(); + + log.trace("Building anonymous subject with user-id: {}, realm-name: {}", model.getUserId(), model.getRealmName()); + + // custom principals to aid with anonymous subject detection + PrincipalCollection principals = new AnonymousPrincipalCollection( + model.getUserId(), + model.getRealmName() + ); + + // FIXME: buildSubject() calls deeply into various shiro dao/save bits which are probably overhead we don't need here at all + + return new Subject.Builder() + .principals(principals) + .authenticated(false) + .sessionCreationEnabled(false) + .buildSubject(); + } + + /** + * @since 3.2 + */ + @Subscribe + public void onStoreChanged(AnonymousConfigurationEvent event) { + if (!event.isLocal()) { + log.debug("Reloading configuration after change by node {}", event.getRemoteNodeId()); + AnonymousConfiguration model; + synchronized (lock) { + configuration = model = loadConfiguration(); + } + eventManager.post(new AnonymousConfigurationChangedEvent(model)); + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/MemoryAnonymousConfigurationStore.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/MemoryAnonymousConfigurationStore.java new file mode 100644 index 0000000000000000000000000000000000000000..acc54f913f4eff1239c7b203dad070797a6d49ea --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/MemoryAnonymousConfigurationStore.java @@ -0,0 +1,48 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.anonymous; + +import javax.annotation.Nullable; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.security.anonymous.AnonymousConfiguration; + +import com.google.common.annotations.VisibleForTesting; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * In-memory {@link AnonymousConfigurationStore}. + */ +@Named("memory") +@Singleton +@VisibleForTesting +public class MemoryAnonymousConfigurationStore + extends ComponentSupport + implements AnonymousConfigurationStore +{ + private AnonymousConfiguration model; + + @Override + @Nullable + public synchronized AnonymousConfiguration load() { + return model; + } + + @Override + public synchronized void save(final AnonymousConfiguration configuration) { + this.model = checkNotNull(configuration); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/OrientAnonymousConfigurationStore.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/OrientAnonymousConfigurationStore.java new file mode 100644 index 0000000000000000000000000000000000000000..a93d264169f57c73fdc6a8cb5998a51565ad4d57 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/anonymous/OrientAnonymousConfigurationStore.java @@ -0,0 +1,79 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.anonymous; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseInstanceNames; +import org.sonatype.nexus.security.anonymous.AnonymousConfiguration; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SCHEMAS; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTx; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; + +/** + * Orient {@link AnonymousConfigurationStore}. + * + * @since 3.0 + */ +@Named("orient") +@ManagedLifecycle(phase = SCHEMAS) +@Singleton +public class OrientAnonymousConfigurationStore + extends StateGuardLifecycleSupport + implements AnonymousConfigurationStore +{ + private final Provider databaseInstance; + + private final AnonymousConfigurationEntityAdapter entityAdapter; + + @Inject + public OrientAnonymousConfigurationStore(@Named(DatabaseInstanceNames.SECURITY) final Provider databaseInstance, + final AnonymousConfigurationEntityAdapter entityAdapter) + { + this.databaseInstance = checkNotNull(databaseInstance); + this.entityAdapter = checkNotNull(entityAdapter); + } + + @Override + protected void doStart() { + try (ODatabaseDocumentTx db = databaseInstance.get().connect()) { + entityAdapter.register(db); + } + } + + @Override + @Nullable + @Guarded(by = STARTED) + public AnonymousConfiguration load() { + return inTx(databaseInstance).call(entityAdapter::get); + } + + @Override + @Guarded(by = STARTED) + public void save(final AnonymousConfiguration configuration) { + inTxRetry(databaseInstance).run(db -> entityAdapter.set(db, configuration)); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/jaas/ShiroJaasRealm.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/jaas/ShiroJaasRealm.java new file mode 100644 index 0000000000000000000000000000000000000000..f0ac11237a48bef5f4a4d2d088d49f0d5e49c959 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/jaas/ShiroJaasRealm.java @@ -0,0 +1,83 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.jaas; + +import java.util.HashMap; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.security.auth.login.AppConfigurationEntry; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.security.SecurityHelper; +import org.sonatype.nexus.security.SecuritySystem; + +import org.apache.karaf.jaas.boot.ProxyLoginModule; +import org.apache.karaf.jaas.config.JaasRealm; +import org.eclipse.sisu.EagerSingleton; +import org.osgi.framework.BundleContext; + +import static javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT; +import static org.apache.karaf.jaas.boot.ProxyLoginModule.PROPERTY_BUNDLE; +import static org.apache.karaf.jaas.boot.ProxyLoginModule.PROPERTY_MODULE; + +/** + * {@link JaasRealm} that configures {@link ShiroLoginModule} for use with Karaf. + * + * @since 3.0 + */ +@Named +@EagerSingleton +public class ShiroJaasRealm + extends ComponentSupport + implements JaasRealm +{ + private final AppConfigurationEntry[] entries; + + @Inject + public ShiroJaasRealm(final BundleContext bundleContext, + final SecurityHelper securityHelper, + final SecuritySystem securitySystem) + { + Map options = new HashMap<>(); + + options.put(BundleContext.class.getName(), bundleContext); + options.put(SecurityHelper.class.getName(), securityHelper); + options.put(SecuritySystem.class.getName(), securitySystem); + + options.put(PROPERTY_MODULE, ShiroLoginModule.class.getName()); + options.put(PROPERTY_BUNDLE, Long.toString(bundleContext.getBundle().getBundleId())); + + entries = new AppConfigurationEntry[] { + new AppConfigurationEntry(ProxyLoginModule.class.getName(), SUFFICIENT, options) + }; + + bundleContext.registerService(JaasRealm.class, this, null); + } + + @Override + public String getName() { + return "shiro"; + } + + @Override + public int getRank() { + return 0; + } + + @Override + public AppConfigurationEntry[] getEntries() { + return entries.clone(); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/jaas/ShiroLoginModule.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/jaas/ShiroLoginModule.java new file mode 100644 index 0000000000000000000000000000000000000000..2023056bd66df9a1fa18d12bec67d1bcd3d4c5e4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/jaas/ShiroLoginModule.java @@ -0,0 +1,178 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.jaas; + +import java.io.IOException; +import java.security.Principal; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.security.Roles; +import org.sonatype.nexus.security.SecurityHelper; +import org.sonatype.nexus.security.SecuritySystem; +import org.sonatype.nexus.security.role.RoleIdentifier; +import org.sonatype.nexus.security.user.User; +import org.sonatype.nexus.security.user.UserNotFoundException; + +import org.apache.karaf.jaas.boot.principal.RolePrincipal; +import org.apache.karaf.jaas.boot.principal.UserPrincipal; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.UsernamePasswordToken; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * JAAS {@link LoginModule} that delegates to Shiro for authentication. + * + * @since 3.0 + */ +public class ShiroLoginModule + extends ComponentSupport + implements LoginModule +{ + private final Set principals = new HashSet(); + + private Subject jaasSubject; + + private CallbackHandler callbackHandler; + + private SecurityHelper securityHelper; + + private SecuritySystem securitySystem; + + private User user; + + @Override + public void initialize(final Subject jaasSubject, + final CallbackHandler callbackHandler, + final Map sharedState, + final Map options) + { + this.jaasSubject = checkNotNull(jaasSubject); + this.callbackHandler = checkNotNull(callbackHandler); + + securityHelper = (SecurityHelper) checkNotNull(options.get(SecurityHelper.class.getName())); + securitySystem = (SecuritySystem) checkNotNull(options.get(SecuritySystem.class.getName())); + } + + @Override + public boolean login() throws LoginException { + Callback[] callbacks = new Callback[2]; + + callbacks[0] = new NameCallback("Username: "); + callbacks[1] = new PasswordCallback("Password: ", false); + + try { + callbackHandler.handle(callbacks); + } + catch (IOException | UnsupportedCallbackException e) { + log.debug("Missing credentials", e); + throw new LoginException(e.getMessage()); + } + + org.apache.shiro.subject.Subject shiroSubject = securityHelper.subject(); + checkState(shiroSubject != null); + + try { + shiroSubject.login( + new UsernamePasswordToken( + ((NameCallback) callbacks[0]).getName(), + ((PasswordCallback) callbacks[1]).getPassword() + ) + ); + + if (!shiroSubject.hasRole(Roles.ANONYMOUS_ROLE_ID)) { + user = securitySystem.getUser(shiroSubject.getPrincipal().toString()); + } + else { + throw new LoginException("Invalid username or password"); + } + } + catch (AuthenticationException | UserNotFoundException e) { + log.debug("Authentication failed", e); + throw new LoginException("Invalid username or password"); + } + finally { + if (user == null) { + shiroSubject.logout(); + } + } + + return true; + } + + @Override + public boolean commit() throws LoginException { + if (user != null) { + principals.add(new UserPrincipal(user.getUserId())); + for (RoleIdentifier role : user.getRoles()) { + String roleId = role.getRoleId(); + if (Roles.ADMIN_ROLE_ID.equals(roleId)) { + // Karaf default roles implied by nx-admin + principals.add(new RolePrincipal("admin")); + principals.add(new RolePrincipal("manager")); + principals.add(new RolePrincipal("viewer")); + } + else if (roleId.startsWith("karaf-")) { + // flatten Karaf name-spaced roles by removing prefix + principals.add(new RolePrincipal(roleId.substring(6))); + } + // ignore non-admin/non-karaf roles... + } + jaasSubject.getPrincipals().addAll(principals); + return true; + } + else { + return clearState(); + } + } + + @Override + public boolean abort() throws LoginException { + return clearState(); + } + + @Override + public boolean logout() throws LoginException { + return clearState(); + } + + /** + * Clears cached user state; returns {@code true} if user was authenticated, otherwise {@code false}. + */ + private boolean clearState() { + if (user != null) { + jaasSubject.getPrincipals().removeAll(principals); + principals.clear(); + user = null; + + securityHelper.subject().logout(); + return true; + } + else { + return false; + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/CPrivilegeEntityAdapter.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/CPrivilegeEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..d215b728bee33d5f75e908dd8221fc63207b6313 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/CPrivilegeEntityAdapter.java @@ -0,0 +1,141 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.model; + +import java.util.Map; + +import javax.annotation.Nullable; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.OIndexNameBuilder; +import org.sonatype.nexus.orient.entity.IterableEntityAdapter; +import org.sonatype.nexus.orient.entity.action.DeleteEntityByPropertyAction; +import org.sonatype.nexus.orient.entity.action.ReadEntityByPropertyAction; +import org.sonatype.nexus.orient.entity.action.UpdateEntityByPropertyAction; +import org.sonatype.nexus.security.config.CPrivilege; + +import com.google.common.collect.Maps; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; + +/** + * {@link CPrivilege} entity adapter. + * + * @since 3.0 + */ +@Named +@Singleton +public class CPrivilegeEntityAdapter + extends IterableEntityAdapter +{ + private static final String DB_CLASS = new OClassNameBuilder() + .type("privilege") + .build(); + + private static final String P_ID = "id"; + + private static final String P_NAME = "name"; + + private static final String P_DESCRIPTION = "description"; + + private static final String P_TYPE = "type"; + + private static final String P_PROPERTIES = "properties"; + + private static final String I_ID = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_ID) + .build(); + + private final ReadEntityByPropertyAction read = new ReadEntityByPropertyAction<>(this, P_ID); + + private final DeleteEntityByPropertyAction delete = new DeleteEntityByPropertyAction(this, P_ID); + + private final UpdateEntityByPropertyAction update = new UpdateEntityByPropertyAction<>(this, P_ID); + + public CPrivilegeEntityAdapter() { + super(DB_CLASS); + } + + @Override + protected void defineType(final OClass type) { + type.createProperty(P_ID, OType.STRING) + .setNotNull(true); + type.createProperty(P_NAME, OType.STRING) + .setNotNull(true); + type.createProperty(P_DESCRIPTION, OType.STRING); + type.createProperty(P_TYPE, OType.STRING) + .setNotNull(true); + type.createProperty(P_PROPERTIES, OType.EMBEDDEDMAP) + .setNotNull(true); + + type.createIndex(I_ID, INDEX_TYPE.UNIQUE, P_ID); + } + + @Override + protected CPrivilege newEntity() { + return new CPrivilege(); + } + + @Override + protected void readFields(final ODocument document, final CPrivilege entity) throws Exception { + entity.setId(document.field(P_ID, OType.STRING)); + entity.setName(document.field(P_NAME, OType.STRING)); + entity.setDescription(document.field(P_DESCRIPTION, OType.STRING)); + entity.setType(document.field(P_TYPE, OType.STRING)); + entity.setReadOnly(false); + entity.setProperties(Maps.newHashMap(document.>field(P_PROPERTIES, OType.EMBEDDEDMAP))); + + entity.setVersion(String.valueOf(document.getVersion())); + } + + @Override + protected void writeFields(final ODocument document, final CPrivilege entity) throws Exception { + document.field(P_ID, entity.getId()); + document.field(P_NAME, entity.getName()); + document.field(P_DESCRIPTION, entity.getDescription()); + document.field(P_TYPE, entity.getType()); + document.field(P_PROPERTIES, entity.getProperties()); + } + + // + // Actions + // + + /** + * @since 3.1 + */ + @Nullable + public CPrivilege read(final ODatabaseDocumentTx db, final String id) { + return read.execute(db, id); + } + + /** + * @since 3.1 + */ + public boolean delete(final ODatabaseDocumentTx db, final String id) { + return delete.execute(db, id); + } + + /** + * @since 3.6.1 + */ + public boolean update(final ODatabaseDocumentTx db, final CPrivilege entity) { + return update.execute(db, entity, entity.getId()); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/CRoleEntityAdapter.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/CRoleEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..c92010956fe51bc23012d0b2cfcfb93477e21164 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/CRoleEntityAdapter.java @@ -0,0 +1,139 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.model; + +import java.util.Set; + +import javax.annotation.Nullable; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.OIndexNameBuilder; +import org.sonatype.nexus.orient.entity.IterableEntityAdapter; +import org.sonatype.nexus.orient.entity.action.DeleteEntityByPropertyAction; +import org.sonatype.nexus.orient.entity.action.ReadEntityByPropertyAction; +import org.sonatype.nexus.orient.entity.action.UpdateEntityByPropertyAction; +import org.sonatype.nexus.security.config.CRole; + +import com.google.common.collect.Sets; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; + +/** + * {@link CRole} entity adapter. + * + * @since 3.0 + */ +@Named +@Singleton +public class CRoleEntityAdapter + extends IterableEntityAdapter +{ + private static final String DB_CLASS = new OClassNameBuilder() + .type("role") + .build(); + + private static final String P_ID = "id"; + + private static final String P_NAME = "name"; + + private static final String P_DESCRIPTION = "description"; + + private static final String P_PRIVILEGES = "privileges"; + + private static final String P_ROLES = "roles"; + + private static final String I_ID = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_ID) + .build(); + + private final ReadEntityByPropertyAction read = new ReadEntityByPropertyAction<>(this, P_ID); + + private final DeleteEntityByPropertyAction delete = new DeleteEntityByPropertyAction(this, P_ID); + + private final UpdateEntityByPropertyAction update = new UpdateEntityByPropertyAction<>(this, P_ID); + + public CRoleEntityAdapter() { + super(DB_CLASS); + } + + @Override + protected void defineType(final OClass type) { + type.createProperty(P_ID, OType.STRING) + .setNotNull(true); + type.createProperty(P_NAME, OType.STRING) + .setNotNull(true); + type.createProperty(P_DESCRIPTION, OType.STRING); + type.createProperty(P_PRIVILEGES, OType.EMBEDDEDSET); + type.createProperty(P_ROLES, OType.EMBEDDEDSET); + + type.createIndex(I_ID, INDEX_TYPE.UNIQUE, P_ID); + } + + @Override + protected CRole newEntity() { + return new CRole(); + } + + @Override + protected void readFields(final ODocument document, final CRole entity) throws Exception { + entity.setId(document.field(P_ID, OType.STRING)); + entity.setName(document.field(P_NAME, OType.STRING)); + entity.setDescription(document.field(P_DESCRIPTION, OType.STRING)); + entity.setPrivileges(Sets.newHashSet(document.>field(P_PRIVILEGES, OType.EMBEDDEDSET))); + entity.setRoles(Sets.newHashSet(document.>field(P_ROLES, OType.EMBEDDEDSET))); + entity.setReadOnly(false); + + entity.setVersion(String.valueOf(document.getVersion())); + } + + @Override + protected void writeFields(final ODocument document, final CRole entity) throws Exception { + document.field(P_ID, entity.getId()); + document.field(P_NAME, entity.getName()); + document.field(P_DESCRIPTION, entity.getDescription()); + document.field(P_PRIVILEGES, entity.getPrivileges()); + document.field(P_ROLES, entity.getRoles()); + } + + // + // Actions + // + + /** + * @since 3.1 + */ + @Nullable + public CRole read(final ODatabaseDocumentTx db, final String id) { + return read.execute(db, id); + } + + /** + * @since 3.1 + */ + public boolean delete(final ODatabaseDocumentTx db, final String id) { + return delete.execute(db, id); + } + + /** + * @since 3.6.1 + */ + public boolean update(final ODatabaseDocumentTx db, final CRole entity) { + return update.execute(db, entity, entity.getId()); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/CUserEntityAdapter.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/CUserEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..f6516dd29b25f7aa250c3ac6b7d224005cdd2443 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/CUserEntityAdapter.java @@ -0,0 +1,142 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.model; + +import javax.annotation.Nullable; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.OIndexNameBuilder; +import org.sonatype.nexus.orient.entity.IterableEntityAdapter; +import org.sonatype.nexus.orient.entity.action.DeleteEntityByPropertyAction; +import org.sonatype.nexus.orient.entity.action.ReadEntityByPropertyAction; +import org.sonatype.nexus.orient.entity.action.UpdateEntityByPropertyAction; +import org.sonatype.nexus.security.config.CUser; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; + +/** + * {@link CUser} entity adapter. + * + * @since 3.0 + */ +@Named +@Singleton +public class CUserEntityAdapter + extends IterableEntityAdapter +{ + private static final String DB_CLASS = new OClassNameBuilder() + .type("user") + .build(); + + private static final String P_ID = "id"; + + private static final String P_FIRST_NAME = "firstName"; + + private static final String P_LAST_NAME = "lastName"; + + private static final String P_PASSWORD = "password"; + + private static final String P_STATUS = "status"; + + private static final String P_EMAIL = "email"; + + private static final String I_ID = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_ID) + .build(); + + private final ReadEntityByPropertyAction read = new ReadEntityByPropertyAction<>(this, P_ID); + + private final DeleteEntityByPropertyAction delete = new DeleteEntityByPropertyAction(this, P_ID); + + private final UpdateEntityByPropertyAction update = new UpdateEntityByPropertyAction<>(this, P_ID); + + public CUserEntityAdapter() { + super(DB_CLASS); + } + + @Override + protected void defineType(final OClass type) { + type.createProperty(P_ID, OType.STRING) + .setNotNull(true); + type.createProperty(P_FIRST_NAME, OType.STRING); + type.createProperty(P_LAST_NAME, OType.STRING); + type.createProperty(P_PASSWORD, OType.STRING) + .setNotNull(true); + type.createProperty(P_STATUS, OType.STRING) + .setNotNull(true); + type.createProperty(P_EMAIL, OType.STRING) + .setNotNull(true); + + type.createIndex(I_ID, INDEX_TYPE.UNIQUE, P_ID); + } + + @Override + protected CUser newEntity() { + return new CUser(); + } + + @Override + protected void readFields(final ODocument document, final CUser entity) throws Exception { + entity.setId(document.field(P_ID, OType.STRING)); + entity.setFirstName(document.field(P_FIRST_NAME, OType.STRING)); + entity.setLastName(document.field(P_LAST_NAME, OType.STRING)); + entity.setPassword(document.field(P_PASSWORD, OType.STRING)); + entity.setStatus(document.field(P_STATUS, OType.STRING)); + entity.setEmail(document.field(P_EMAIL, OType.STRING)); + + entity.setVersion(String.valueOf(document.getVersion())); + } + + @Override + protected void writeFields(final ODocument document, final CUser entity) throws Exception { + document.field(P_ID, entity.getId()); + document.field(P_FIRST_NAME, entity.getFirstName()); + document.field(P_LAST_NAME, entity.getLastName()); + document.field(P_STATUS, entity.getStatus()); + document.field(P_EMAIL, entity.getEmail()); + document.field(P_PASSWORD, entity.getPassword()); + } + + // + // Actions + // + + /** + * @since 3.1 + */ + @Nullable + public CUser read(final ODatabaseDocumentTx db, final String id) { + return read.execute(db, id); + } + + /** + * @since 3.1 + */ + public boolean delete(final ODatabaseDocumentTx db, final String id) { + return delete.execute(db, id); + } + + /** + * @since 3.6.1 + */ + public boolean update(final ODatabaseDocumentTx db, final CUser entity) { + return update.execute(db, entity, entity.getId()); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/CUserRoleMappingEntityAdapter.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/CUserRoleMappingEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..9a43cd2571424c63e49f2b0db9a78c4b53ddf175 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/CUserRoleMappingEntityAdapter.java @@ -0,0 +1,120 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.model; + +import java.util.Set; + +import javax.annotation.Nullable; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.OIndexNameBuilder; +import org.sonatype.nexus.orient.entity.IterableEntityAdapter; +import org.sonatype.nexus.orient.entity.action.DeleteEntityByPropertyAction; +import org.sonatype.nexus.orient.entity.action.ReadEntityByPropertyAction; +import org.sonatype.nexus.orient.entity.action.UpdateEntityByPropertyAction; +import org.sonatype.nexus.security.config.CUserRoleMapping; + +import com.google.common.collect.Sets; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; + +/** + * {@link CUserRoleMapping} entity adapter. + * + * @since 3.0 + */ +@Named +@Singleton +public class CUserRoleMappingEntityAdapter + extends IterableEntityAdapter +{ + private static final String DB_CLASS = new OClassNameBuilder() + .type("user_role_mapping") + .build(); + + private static final String P_USER_ID = "userId"; + + private static final String P_SOURCE = "source"; + + private static final String P_ROLES = "roles"; + + private static final String I_USER_ID_SOURCE = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_USER_ID) + .property(P_SOURCE) + .build(); + + private final ReadEntityByPropertyAction read = new ReadEntityByPropertyAction<>(this, P_USER_ID, + P_SOURCE); + + private final DeleteEntityByPropertyAction delete = new DeleteEntityByPropertyAction(this, P_USER_ID, P_SOURCE); + + private final UpdateEntityByPropertyAction update = new UpdateEntityByPropertyAction<>(this, P_USER_ID, P_SOURCE); + + public CUserRoleMappingEntityAdapter() { + super(DB_CLASS); + } + + @Override + protected void defineType(final OClass type) { + type.createProperty(P_USER_ID, OType.STRING) + .setNotNull(true); + type.createProperty(P_SOURCE, OType.STRING) + .setNotNull(true); + type.createProperty(P_ROLES, OType.EMBEDDEDSET); + + type.createIndex(I_USER_ID_SOURCE, INDEX_TYPE.UNIQUE, P_USER_ID, P_SOURCE); + } + + @Override + protected CUserRoleMapping newEntity() { + return new CUserRoleMapping(); + } + + @Override + protected void readFields(final ODocument document, final CUserRoleMapping entity) throws Exception { + entity.setUserId(document.field(P_USER_ID, OType.STRING)); + entity.setSource(document.field(P_SOURCE, OType.STRING)); + entity.setRoles(Sets.newHashSet(document.>field(P_ROLES, OType.EMBEDDEDSET))); + + entity.setVersion(String.valueOf(document.getVersion())); + } + + @Override + protected void writeFields(final ODocument document, final CUserRoleMapping entity) throws Exception { + document.field(P_USER_ID, entity.getUserId()); + document.field(P_SOURCE, entity.getSource()); + document.field(P_ROLES, entity.getRoles()); + } + + @Nullable + public CUserRoleMapping read(final ODatabaseDocumentTx db, final String userId, final String source) { + return read.execute(db, userId, source); + } + + public boolean delete(final ODatabaseDocumentTx db, final String userId, final String source) { + return delete.execute(db, userId, source); + } + + /** + * @since 3.6.1 + */ + public boolean update(final ODatabaseDocumentTx db, final CUserRoleMapping entity) { + return update.execute(db, entity, entity.getUserId(), entity.getSource()); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/OrientSecurityConfigurationSource.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/OrientSecurityConfigurationSource.java new file mode 100644 index 0000000000000000000000000000000000000000..ad68f6be4bced262c49f852492060c59ceb6d40a --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/model/OrientSecurityConfigurationSource.java @@ -0,0 +1,472 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.model; + +import java.util.ConcurrentModificationException; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseInstanceNames; +import org.sonatype.nexus.security.config.CPrivilege; +import org.sonatype.nexus.security.config.CRole; +import org.sonatype.nexus.security.config.CUser; +import org.sonatype.nexus.security.config.CUserRoleMapping; +import org.sonatype.nexus.security.config.SecurityConfiguration; +import org.sonatype.nexus.security.config.SecurityConfigurationSource; +import org.sonatype.nexus.security.privilege.NoSuchPrivilegeException; +import org.sonatype.nexus.security.role.NoSuchRoleException; +import org.sonatype.nexus.security.user.NoSuchRoleMappingException; +import org.sonatype.nexus.security.user.UserManager; +import org.sonatype.nexus.security.user.UserNotFoundException; + +import com.google.common.collect.ImmutableList; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.exception.OConcurrentModificationException; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.String.valueOf; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SCHEMAS; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTx; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; + +/** + * Default {@link SecurityConfigurationSource} implementation using Orient db as store. + * + * @since 3.0 + */ +@Named +@ManagedLifecycle(phase = SCHEMAS) +@Singleton +public class OrientSecurityConfigurationSource + extends StateGuardLifecycleSupport + implements SecurityConfigurationSource +{ + /** + * Security database. + */ + private final Provider databaseInstance; + + /** + * The defaults configuration source. + */ + private final SecurityConfigurationSource securityDefaults; + + private final CUserEntityAdapter userEntityAdapter; + + private final CRoleEntityAdapter roleEntityAdapter; + + private final CPrivilegeEntityAdapter privilegeEntityAdapter; + + private final CUserRoleMappingEntityAdapter userRoleMappingEntityAdapter; + + /** + * The configuration. + */ + private SecurityConfiguration configuration; + + @Inject + public OrientSecurityConfigurationSource(@Named(DatabaseInstanceNames.SECURITY) final Provider databaseInstance, + @Named("static") final SecurityConfigurationSource defaults, + final CUserEntityAdapter userEntityAdapter, + final CRoleEntityAdapter roleEntityAdapter, + final CPrivilegeEntityAdapter privilegeEntityAdapter, + final CUserRoleMappingEntityAdapter userRoleMappingEntityAdapter) + { + this.databaseInstance = checkNotNull(databaseInstance); + this.securityDefaults = checkNotNull(defaults); + this.userEntityAdapter = checkNotNull(userEntityAdapter); + this.roleEntityAdapter = checkNotNull(roleEntityAdapter); + this.privilegeEntityAdapter = checkNotNull(privilegeEntityAdapter); + this.userRoleMappingEntityAdapter = checkNotNull(userRoleMappingEntityAdapter); + } + + @Override + protected void doStart() { + try (ODatabaseDocumentTx db = databaseInstance.get().connect()) { + userEntityAdapter.register(db, new Runnable() + { + @Override + public void run() { + List users = securityDefaults.getConfiguration().getUsers(); + if (users != null && !users.isEmpty()) { + log.info("Initializing default users"); + for (CUser user : users) { + userEntityAdapter.addEntity(db, user); + } + } + } + }); + + roleEntityAdapter.register(db, new Runnable() + { + @Override + public void run() { + List roles = securityDefaults.getConfiguration().getRoles(); + if (roles != null && !roles.isEmpty()) { + log.info("Initializing default roles"); + for (CRole role : roles) { + roleEntityAdapter.addEntity(db, role); + } + } + } + }); + + privilegeEntityAdapter.register(db, new Runnable() + { + @Override + public void run() { + List privileges = securityDefaults.getConfiguration().getPrivileges(); + if (privileges != null && !privileges.isEmpty()) { + log.info("Initializing default privileges"); + for (CPrivilege privilege : privileges) { + privilegeEntityAdapter.addEntity(db, privilege); + } + } + } + }); + + userRoleMappingEntityAdapter.register(db, new Runnable() + { + @Override + public void run() { + List mappings = securityDefaults.getConfiguration().getUserRoleMappings(); + if (mappings != null && !mappings.isEmpty()) { + log.info("Initializing default user/role mappings"); + for (CUserRoleMapping mapping : mappings) { + userRoleMappingEntityAdapter.addEntity(db, mapping); + } + } + } + }); + } + } + + @Override + public SecurityConfiguration getConfiguration() { + return configuration; + } + + @Override + public SecurityConfiguration loadConfiguration() { + configuration = new OrientSecurityConfiguration(); + return getConfiguration(); + } + + private class OrientSecurityConfiguration + implements SecurityConfiguration + { + private ConcurrentModificationException concurrentlyModified(final String type, final String value) { + throw new ConcurrentModificationException(type + " '" + value + "' updated in the meantime"); + } + + // + // Users + // + + @Override + public List getUsers() { + log.trace("Retrieving all users"); + + return inTx(databaseInstance).call(db -> ImmutableList.copyOf(userEntityAdapter.browse(db))); + } + + @Override + public CUser getUser(final String id) { + checkNotNull(id); + log.trace("Retrieving user: {}", id); + + return inTx(databaseInstance).call(db -> userEntityAdapter.read(db, id)); + } + + @Override + public void addUser(final CUser user, final Set roles) { + checkNotNull(user); + checkNotNull(user.getId()); + log.trace("Adding user: {}", user.getId()); + + inTxRetry(databaseInstance).run(db -> { + userEntityAdapter.addEntity(db, user); + addUserRoleMapping(mapping(user.getId(), roles)); + }); + } + + @Override + public void updateUser(final CUser user, final Set roles) throws UserNotFoundException { + checkNotNull(user); + checkNotNull(user.getId()); + log.trace("Updating user: {}", user.getId()); + + try { + inTxRetry(databaseInstance).throwing(UserNotFoundException.class).run(db -> { + CUser existing = userEntityAdapter.read(db, user.getId()); + if (existing == null) { + throw new UserNotFoundException(user.getId()); + } + if (!Objects.equals(user.getVersion(), existing.getVersion())) { + throw concurrentlyModified("User", user.getId()); + } + userEntityAdapter.update(db, user); + + CUserRoleMapping mapping = userRoleMappingEntityAdapter.read(db, user.getId(), UserManager.DEFAULT_SOURCE); + if (mapping == null) { + addUserRoleMapping(mapping(user.getId(), roles)); + } + else { + try { + mapping.setRoles(roles); + updateUserRoleMapping(mapping); + } + catch (NoSuchRoleMappingException e) { + throw new RuntimeException(e); + } + } + }); + } + catch (OConcurrentModificationException e) { + throw concurrentlyModified("User", user.getId()); + } + } + + @Override + public boolean removeUser(final String id) { + checkNotNull(id); + log.trace("Removing user: {}", id); + + try { + return inTxRetry(databaseInstance).call(db -> { + if (userEntityAdapter.delete(db, id)) { + removeUserRoleMapping(id, UserManager.DEFAULT_SOURCE); + return true; + } + return false; + }); + } + catch (OConcurrentModificationException e) { + throw concurrentlyModified("User", id); + } + } + + // + // Privileges + // + + @Override + public List getPrivileges() { + log.trace("Retrieving all privileges"); + + return inTx(databaseInstance).call(db -> ImmutableList.copyOf(privilegeEntityAdapter.browse(db))); + } + + @Override + public CPrivilege getPrivilege(final String id) { + checkNotNull(id); + log.trace("Retrieving privilege {}", id); + + return inTx(databaseInstance).call(db -> privilegeEntityAdapter.read(db, id)); + } + + @Override + public void addPrivilege(final CPrivilege privilege) { + checkNotNull(privilege); + checkNotNull(privilege.getId()); + log.trace("Adding privilege: {}", privilege.getId()); + + inTxRetry(databaseInstance).run(db -> privilegeEntityAdapter.addEntity(db, privilege)); + } + + @Override + public void updatePrivilege(final CPrivilege privilege) throws NoSuchPrivilegeException { + checkNotNull(privilege); + checkNotNull(privilege.getId()); + log.trace("Updating privilege: {}", privilege.getId()); + + try { + inTxRetry(databaseInstance).throwing(NoSuchPrivilegeException.class).run(db -> { + CPrivilege existing = privilegeEntityAdapter.read(db, privilege.getId()); + if (existing == null) { + throw new NoSuchPrivilegeException(privilege.getId()); + } + if (!Objects.equals(privilege.getVersion(), existing.getVersion())) { + throw concurrentlyModified("Privilege", privilege.getId()); + } + privilegeEntityAdapter.update(db, privilege); + }); + } + catch (OConcurrentModificationException e) { + throw concurrentlyModified("Privilege", privilege.getId()); + } + } + + @Override + public boolean removePrivilege(final String id) { + checkNotNull(id); + log.trace("Removing privilege: {}", id); + + try { + return inTxRetry(databaseInstance).call(db -> privilegeEntityAdapter.delete(db, id)); + } + catch (OConcurrentModificationException e) { + throw concurrentlyModified("Privilege", id); + } + } + + // + // Roles + // + + @Override + public List getRoles() { + log.trace("Retrieving all roles"); + + return inTx(databaseInstance).call(db -> ImmutableList.copyOf(roleEntityAdapter.browse(db))); + } + + @Override + public CRole getRole(final String id) { + checkNotNull(id); + log.trace("Retrieving role: {}", id); + + return inTx(databaseInstance).call(db -> roleEntityAdapter.read(db, id)); + } + + @Override + public void addRole(final CRole role) { + checkNotNull(role); + checkNotNull(role.getId()); + log.trace("Adding role: {}", role.getId()); + + inTxRetry(databaseInstance).run(db -> roleEntityAdapter.addEntity(db, role)); + } + + @Override + public void updateRole(final CRole role) throws NoSuchRoleException { + checkNotNull(role); + checkNotNull(role.getId()); + log.trace("Updating role: {}", role.getId()); + + try { + inTxRetry(databaseInstance).throwing(NoSuchRoleException.class).run(db -> { + CRole existing = roleEntityAdapter.read(db, role.getId()); + if (existing == null) { + throw new NoSuchRoleException(role.getId()); + } + if (!Objects.equals(role.getVersion(), existing.getVersion())) { + throw concurrentlyModified("Role", role.getId()); + } + roleEntityAdapter.update(db, role); + }); + } + catch (OConcurrentModificationException e) { + throw concurrentlyModified("Role", role.getId()); + } + } + + @Override + public boolean removeRole(final String id) { + checkNotNull(id); + log.trace("Removing role: {}", id); + + try { + return inTxRetry(databaseInstance).call(db -> roleEntityAdapter.delete(db, id)); + } + catch (OConcurrentModificationException e) { + throw concurrentlyModified("Role", id); + } + } + + // + // User-Role Mappings + // + + private CUserRoleMapping mapping(final String userId, final Set roles) { + CUserRoleMapping mapping = new CUserRoleMapping(); + mapping.setUserId(userId); + mapping.setSource(UserManager.DEFAULT_SOURCE); + mapping.setRoles(roles); + return mapping; + } + + @Override + public List getUserRoleMappings() { + log.trace("Retrieving all user/role mappings"); + + return inTx(databaseInstance).call(db -> ImmutableList.copyOf(userRoleMappingEntityAdapter.browse(db))); + } + + @Override + public CUserRoleMapping getUserRoleMapping(final String userId, final String source) { + checkNotNull(userId); + checkNotNull(source); + log.trace("Retrieving user/role mappings of: {}/{}", userId, source); + + return inTx(databaseInstance).call(db -> userRoleMappingEntityAdapter.read(db, userId, source)); + } + + @Override + public void addUserRoleMapping(final CUserRoleMapping mapping) { + checkNotNull(mapping); + checkNotNull(mapping.getUserId()); + checkNotNull(mapping.getSource()); + log.trace("Adding user/role mappings for: {}/{}", mapping.getUserId(), mapping.getSource()); + + inTxRetry(databaseInstance).run(db -> userRoleMappingEntityAdapter.addEntity(db, mapping)); + } + + @Override + public void updateUserRoleMapping(final CUserRoleMapping mapping) throws NoSuchRoleMappingException { + checkNotNull(mapping); + checkNotNull(mapping.getUserId()); + checkNotNull(mapping.getSource()); + log.trace("Updating user/role mappings for: {}/{}", mapping.getUserId(), mapping.getSource()); + + try { + inTxRetry(databaseInstance).throwing(NoSuchRoleMappingException.class).run(db -> { + CUserRoleMapping existing = userRoleMappingEntityAdapter.read(db, mapping.getUserId(), mapping.getSource()); + if (existing == null) { + throw new NoSuchRoleMappingException(mapping.getUserId()); + } + if (!Objects.equals(mapping.getVersion(), existing.getVersion())) { + throw concurrentlyModified("User-role mapping", mapping.getUserId()); + } + userRoleMappingEntityAdapter.update(db, mapping); + }); + } + catch (OConcurrentModificationException e) { + throw concurrentlyModified("User-role mapping", mapping.getUserId()); + } + } + + @Override + public boolean removeUserRoleMapping(final String userId, final String source) { + checkNotNull(userId); + checkNotNull(source); + log.trace("Removing user/role mappings for: {}/{}", userId, source); + + try { + return inTxRetry(databaseInstance).call(db -> userRoleMappingEntityAdapter.delete(db, userId, source)); + } + catch (OConcurrentModificationException e) { + throw concurrentlyModified("User-role mapping", userId); + } + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/realm/OrientRealmConfigurationStore.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/realm/OrientRealmConfigurationStore.java new file mode 100644 index 0000000000000000000000000000000000000000..f1d87e47b28ed97f41f2b8f9ae22237b9f24fdad --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/realm/OrientRealmConfigurationStore.java @@ -0,0 +1,80 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.realm; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseInstanceNames; +import org.sonatype.nexus.security.realm.RealmConfiguration; +import org.sonatype.nexus.security.realm.RealmConfigurationStore; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SCHEMAS; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTx; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; + +/** + * Orient {@link RealmConfigurationStore}. + * + * @since 3.0 + */ +@Named("orient") +@ManagedLifecycle(phase = SCHEMAS) +@Singleton +public class OrientRealmConfigurationStore + extends StateGuardLifecycleSupport + implements RealmConfigurationStore +{ + private final Provider databaseInstance; + + private final RealmConfigurationEntityAdapter entityAdapter; + + @Inject + public OrientRealmConfigurationStore(@Named(DatabaseInstanceNames.SECURITY) final Provider databaseInstance, + final RealmConfigurationEntityAdapter entityAdapter) + { + this.databaseInstance = checkNotNull(databaseInstance); + this.entityAdapter = checkNotNull(entityAdapter); + } + + @Override + protected void doStart() { + try (ODatabaseDocumentTx db = databaseInstance.get().connect()) { + entityAdapter.register(db); + } + } + + @Override + @Nullable + @Guarded(by = STARTED) + public RealmConfiguration load() { + return inTx(databaseInstance).call(entityAdapter::get); + } + + @Override + @Guarded(by = STARTED) + public void save(final RealmConfiguration configuration) { + inTxRetry(databaseInstance).run(db -> entityAdapter.set(db, configuration)); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/realm/RealmConfigurationEntityAdapter.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/realm/RealmConfigurationEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..c06139ed3f462cfe6b6089509f9d392ce8915f02 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/security/realm/RealmConfigurationEntityAdapter.java @@ -0,0 +1,90 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.realm; + +import java.util.List; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.entity.EntityEvent; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.entity.AttachedEntityMetadata; +import org.sonatype.nexus.orient.entity.SingletonEntityAdapter; +import org.sonatype.nexus.security.realm.RealmConfiguration; +import org.sonatype.nexus.security.realm.RealmConfigurationCreatedEvent; +import org.sonatype.nexus.security.realm.RealmConfigurationDeletedEvent; +import org.sonatype.nexus.security.realm.RealmConfigurationUpdatedEvent; + +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; + +/** + * {@link RealmConfiguration} entity-adapter. + * + * @since 3.0 + */ +@Named +@Singleton +public class RealmConfigurationEntityAdapter + extends SingletonEntityAdapter +{ + private static final String DB_CLASS = new OClassNameBuilder() + .type("realm") + .build(); + + private static final String P_REALM_NAMES = "realm_names"; + + public RealmConfigurationEntityAdapter() { + super(DB_CLASS); + } + + @Override + protected void defineType(final OClass type) { + type.createProperty(P_REALM_NAMES, OType.EMBEDDEDLIST); + } + + @Override + protected RealmConfiguration newEntity() { + return new RealmConfiguration(); + } + + @Override + protected void readFields(final ODocument document, final RealmConfiguration entity) { + List realms = document.field(P_REALM_NAMES, OType.EMBEDDEDLIST); + + entity.setRealmNames(realms); + } + + @Override + protected void writeFields(final ODocument document, final RealmConfiguration entity) { + document.field(P_REALM_NAMES, entity.getRealmNames()); + } + + @Override + public EntityEvent newEvent(final ODocument document, final EventKind eventKind) { + AttachedEntityMetadata metadata = new AttachedEntityMetadata(this, document); + log.debug("Emitted {} event with metadata {}", eventKind, metadata); + switch (eventKind) { + case CREATE: + return new RealmConfigurationCreatedEvent(metadata); + case UPDATE: + return new RealmConfigurationUpdatedEvent(metadata); + case DELETE: + return new RealmConfigurationDeletedEvent(metadata); + default: + return null; + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/template/Slf4jLogChute.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/template/Slf4jLogChute.java new file mode 100644 index 0000000000000000000000000000000000000000..b4a1843b3e68c35550a98470fe5122e6972467bf --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/template/Slf4jLogChute.java @@ -0,0 +1,119 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.template; + +import org.sonatype.goodies.common.Loggers; + +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.runtime.RuntimeServices; +import org.apache.velocity.runtime.log.LogChute; +import org.slf4j.Logger; + +/** + * Slf4j LogChute. + */ +class Slf4jLogChute + implements LogChute +{ + /** + * The SLF4J Logger instance to use for logging. + */ + private final Logger logger; + + /** + * A flag to redirect Velocity INFO level to DEBUG level + * + * Velocity is kinda chatty in INFO level, that is not always what we need. + */ + private final boolean redirectVelocityInfoToDebug; + + public Slf4jLogChute() { + this.logger = Loggers.getLogger(VelocityEngine.class); + this.redirectVelocityInfoToDebug = true; + } + + public void init(final RuntimeServices srv) throws Exception { + // nothing + } + + public boolean isLevelEnabled(final int level) { + switch (level) { + case TRACE_ID: + return logger.isTraceEnabled(); + case DEBUG_ID: + return logger.isDebugEnabled(); + case INFO_ID: + return redirectVelocityInfoToDebug ? logger.isDebugEnabled() : logger.isInfoEnabled(); + case WARN_ID: + return logger.isWarnEnabled(); + case ERROR_ID: + return logger.isErrorEnabled(); + default: + return level > INFO_ID; + } + } + + public void log(final int level, final String msg) { + switch (level) { + case TRACE_ID: + logger.trace(msg); + break; + case DEBUG_ID: + logger.debug(msg); + break; + case INFO_ID: + if (redirectVelocityInfoToDebug) { + logger.debug(msg); + } + else { + logger.info(msg); + } + break; + case WARN_ID: + logger.warn(msg); + break; + case ERROR_ID: + logger.error(msg); + break; + default: + logger.info(msg); + } + } + + public void log(final int level, final String msg, final Throwable t) { + switch (level) { + case TRACE_ID: + logger.trace(msg, t); + break; + case DEBUG_ID: + logger.debug(msg, t); + break; + case INFO_ID: + if (redirectVelocityInfoToDebug) { + logger.debug(msg, t); + } + else { + logger.info(msg, t); + } + break; + case WARN_ID: + logger.warn(msg, t); + break; + case ERROR_ID: + logger.error(msg, t); + break; + default: + logger.info(msg, t); + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/template/TemplateHelperImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/template/TemplateHelperImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..d62ac752225e934e3579700ee12992d1e4b0f813 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/template/TemplateHelperImpl.java @@ -0,0 +1,94 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.template; + +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringWriter; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.app.ApplicationVersion; +import org.sonatype.nexus.common.app.BaseUrlHolder; +import org.sonatype.nexus.common.template.EscapeHelper; +import org.sonatype.nexus.common.template.TemplateHelper; +import org.sonatype.nexus.common.template.TemplateParameters; + +import com.google.common.base.Throwables; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default {@link TemplateHelper}. + * + * @since 3.0 + */ +@Named +@Singleton +public class TemplateHelperImpl + extends ComponentSupport + implements TemplateHelper +{ + private final ApplicationVersion applicationVersion; + + private final VelocityEngine velocityEngine; + + @Inject + public TemplateHelperImpl(final ApplicationVersion applicationVersion, + final VelocityEngine velocityEngine) + { + this.applicationVersion = checkNotNull(applicationVersion); + this.velocityEngine = checkNotNull(velocityEngine); + } + + @Override + public TemplateParameters parameters() { + TemplateParameters params = new TemplateParameters(); + params.set("nexusVersion", applicationVersion.getVersion()); + params.set("nexusEdition", applicationVersion.getEdition()); + params.set("nexusBrandedEditionAndVersion", applicationVersion.getBrandedEditionAndVersion()); + params.set("nexusUrl", BaseUrlHolder.get()); + params.set("urlSuffix", applicationVersion.getVersion()); // for cache busting + params.set("esc", new EscapeHelper()); + return params; + } + + @Override + public String render(final URL template, final TemplateParameters parameters) { + checkNotNull(template); + checkNotNull(parameters); + + log.trace("Rendering template: {} w/params: {}", template, parameters); + + try (Reader input = new InputStreamReader(template.openStream(), StandardCharsets.UTF_8)) { + StringWriter buff = new StringWriter(); + velocityEngine.evaluate(new VelocityContext(parameters.get()), buff, template.getFile(), input); + + String result = buff.toString(); + log.trace("Result: {}", result); + + return result; + } + catch (Exception e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/template/VelocityEngineProvider.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/template/VelocityEngineProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..277ecc30167f76801e38b629cc030277a37fd142 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/template/VelocityEngineProvider.java @@ -0,0 +1,92 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.template; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; + +import com.google.common.base.Throwables; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.runtime.RuntimeConstants; + +/** + * Nexus preconfigured ans shared Velocity provider. + * + * @since 2.8 + */ +@Named +@Singleton +public class VelocityEngineProvider + extends ComponentSupport + implements Provider +{ + private final VelocityEngine engine; + + @Inject + public VelocityEngineProvider() { + this.engine = create(); + } + + @Override + public VelocityEngine get() { + return engine; + } + + private VelocityEngine create() { + VelocityEngine engine = new VelocityEngine(); + + // log using our chute (slf4j with level fix) + engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new Slf4jLogChute()); + + // to avoid "unable to find resource 'VM_global_library.vm' in any resource loader." + engine.setProperty("velocimacro.library", ""); + + // to use classpath loader + engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "class"); + engine.setProperty("class.resource.loader.class", + "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + + // to make us strict with template references (early problem detection) + engine.setProperty("runtime.references.strict", "true"); + engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "class"); + engine.setProperty("class.resource.loader.class", + "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + + // to set caching ON + engine.setProperty("class.resource.loader.cache", "true"); + + // to never check for template modification (they are JARred) + engine.setProperty("class.resource.loader.modificationCheckInterval", "0"); + + // to set strict mode OFF + engine.setProperty("runtime.references.strict", "false"); + + // to force templates having inline local scope for VM definitions + engine.setProperty("velocimacro.permissions.allow.inline.local.scope", "true"); + + log.debug("Initializing: {}", engine); + try { + engine.init(); + } + catch (Exception e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + + return engine; + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/ClientInfoProviderImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/ClientInfoProviderImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..ab4bacdceed9ac517fae295572417fe6919a62b3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/ClientInfoProviderImpl.java @@ -0,0 +1,65 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.web; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; +import javax.servlet.http.HttpServletRequest; + +import org.sonatype.nexus.security.ClientInfo; +import org.sonatype.nexus.security.ClientInfoProvider; +import org.sonatype.nexus.security.UserIdHelper; + +import com.google.common.net.HttpHeaders; +import com.google.inject.OutOfScopeException; +import com.google.inject.ProvisionException; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default {@link ClientInfoProvider} + * + * @since 3.0 + */ +@Named +@Singleton +public class ClientInfoProviderImpl + implements ClientInfoProvider +{ + private final Provider httpRequestProvider; + + @Inject + public ClientInfoProviderImpl(final Provider httpRequestProvider) { + this.httpRequestProvider = checkNotNull(httpRequestProvider); + } + + @Override + @Nullable + public ClientInfo getCurrentThreadClientInfo() { + try { + HttpServletRequest request = httpRequestProvider.get(); + return new ClientInfo( + UserIdHelper.get(), + request.getRemoteAddr(), + request.getHeader(HttpHeaders.USER_AGENT) + ); + } + catch (ProvisionException | OutOfScopeException e) { + // ignore; this happens when called out of scope of http request + return null; + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/EnvironmentFilter.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/EnvironmentFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..cf9a4c26127a55c7b3e1a0334660b46b9446d00e --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/EnvironmentFilter.java @@ -0,0 +1,122 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.web; + +import java.io.IOException; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.app.ApplicationVersion; +import org.sonatype.nexus.common.app.BaseUrlManager; +import org.sonatype.nexus.security.UserIdMdcHelper; + +import org.eclipse.sisu.Hidden; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.net.HttpHeaders.SERVER; +import static com.google.common.net.HttpHeaders.X_CONTENT_TYPE_OPTIONS; +import static com.google.common.net.HttpHeaders.X_FRAME_OPTIONS; + +/** + * Sets up the basic environment for web-requests. + * + * @since 3.0 + */ +@Named +@Hidden // hide from DynamicFilterChainManager because we statically install it in WebModule +@Singleton +public class EnvironmentFilter + extends ComponentSupport + implements Filter +{ + private final String serverBanner; + + private final String serverHeader; + + private final BaseUrlManager baseUrlManager; + + @Inject + public EnvironmentFilter(final ApplicationVersion applicationVersion, + final BaseUrlManager baseUrlManager) + { + // cache "Server" header value + checkNotNull(applicationVersion); + + this.serverBanner = String.format("Sonatype Nexus %s %s", + applicationVersion.getEdition(), + applicationVersion.getVersion() + ); + + this.serverHeader = String.format("Nexus/%s (%s)", + applicationVersion.getVersion(), + applicationVersion.getEdition() + ); + + this.baseUrlManager = checkNotNull(baseUrlManager); + } + + @Override + public void init(final FilterConfig filterConfig) throws ServletException { + filterConfig.getServletContext().setAttribute("nexus-banner", serverBanner); + } + + @Override + public void destroy() { + // ignore + } + + @Override + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) + throws IOException, ServletException + { + // start with default unknown user-id in MDC + UserIdMdcHelper.unknown(); + + // detect base-url + baseUrlManager.detectAndHoldUrl(); + + // fill in default response headers + defaultHeaders((HttpServletResponse) response); + + try { + chain.doFilter(request, response); + } + finally { + // unset user-id MDC + UserIdMdcHelper.unset(); + } + } + + /** + * Add default headers to servlet response. + */ + private void defaultHeaders(final HttpServletResponse response) { + response.setHeader(SERVER, serverHeader); + + // NEXUS-6569 Add X-Frame-Options header + response.setHeader(X_FRAME_OPTIONS, "SAMEORIGIN"); + + // NEXUS-5023 disable IE for sniffing into response content + response.setHeader(X_CONTENT_TYPE_OPTIONS, "nosniff"); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/ErrorPageFilter.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/ErrorPageFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..da95e5a6e57045d5199ef4859870268e6c9ec663 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/ErrorPageFilter.java @@ -0,0 +1,75 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.web; + +import java.io.IOException; + +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.sonatype.goodies.common.ComponentSupport; + +import org.eclipse.sisu.Hidden; + +import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; + +/** + * Servlet filter to add error page rendering. + * + * @since 2.8 + * + * @see ErrorPageServlet + */ +@Named +@Hidden // hide from DynamicFilterChainManager because we statically install it in WebModule +@Singleton +public class ErrorPageFilter + extends ComponentSupport + implements Filter +{ + @Override + public void init(final FilterConfig config) throws ServletException { + // ignore + } + + @Override + public void destroy() { + // ignore + } + + @Override + public void doFilter(final ServletRequest req, final ServletResponse resp, final FilterChain chain) + throws IOException, ServletException + { + final HttpServletRequest request = (HttpServletRequest) req; + final HttpServletResponse response = (HttpServletResponse) resp; + + // Delegate any exceptions to the ErrorPageServlet via standard sendError servlet api + // Custom handling here to avoid logging from Jetty implementation + try { + chain.doFilter(request, response); + } + catch (Exception e) { + ErrorPageServlet.attachCause(request, e); + response.sendError(SC_INTERNAL_SERVER_ERROR); + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/ErrorPageServlet.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/ErrorPageServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..af982aedbf9273fc54f2546d4bbedcab35752193 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/ErrorPageServlet.java @@ -0,0 +1,171 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.web; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.URL; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Response.Status; + +import org.sonatype.nexus.common.template.TemplateHelper; +import org.sonatype.nexus.common.template.TemplateParameters; +import org.sonatype.nexus.common.template.TemplateThrowableAdapter; +import org.sonatype.nexus.servlet.ServletHelper; + +import org.apache.commons.lang.StringEscapeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Throwables.getRootCause; +import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; + +/** + * An {@code error.html} servlet to handle generic servlet error-page dispatched requests. + * + * @since 2.8 + * + * @see ErrorPageFilter + */ +@Named +@Singleton +public class ErrorPageServlet + extends HttpServlet +{ + private static final Logger log = LoggerFactory.getLogger(ErrorPageServlet.class); + + private static final String TEMPLATE_RESOURCE = "errorPageHtml.vm"; + + /** + * @since 3.0 + */ + private static final String ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name"; + + /** + * @since 3.0 + */ + private static final String ERROR_REQUEST_URI = "javax.servlet.error.request_uri"; + + /** + * @since 3.0 + */ + private static final String ERROR_STATUS_CODE = "javax.servlet.error.status_code"; + + /** + * @since 3.0 + */ + private static final String ERROR_MESSAGE = "javax.servlet.error.message"; + + /** + * @since 3.0 + */ + private static final String ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type"; + + /** + * @since 3.0 + */ + private static final String ERROR_EXCEPTION = "javax.servlet.error.exception"; + + private final TemplateHelper templateHelper; + + private final URL template; + + @Inject + public ErrorPageServlet(final TemplateHelper templateHelper) + { + this.templateHelper = checkNotNull(templateHelper); + template = getClass().getResource(TEMPLATE_RESOURCE); + checkNotNull(template); + } + + @SuppressWarnings("unused") + @Override + protected void service(final HttpServletRequest request, final HttpServletResponse response) + throws ServletException, IOException + { + ServletHelper.addNoCacheResponseHeaders(response); + + String servletName = (String) request.getAttribute(ERROR_SERVLET_NAME); + String requestUri = (String) request.getAttribute(ERROR_REQUEST_URI); + Integer errorCode = (Integer) request.getAttribute(ERROR_STATUS_CODE); + String errorMessage = (String) request.getAttribute(ERROR_MESSAGE); + Class causeType = (Class) request.getAttribute(ERROR_EXCEPTION_TYPE); + Throwable cause = (Throwable) request.getAttribute(ERROR_EXCEPTION); + + // this happens if someone browses directly to the error page + if (errorCode == null) { + errorCode = SC_NOT_FOUND; + errorMessage = "Not found"; + } + + // maintain custom status message when (re)setting the status code, + // we can't use sendError because it doesn't allow custom html body + if (errorMessage == null) { + response.setStatus(errorCode); + } + else { + response.setStatus(errorCode, errorMessage); + } + + response.setContentType("text/html"); + + // ensure sanity of passed in strings which are used to render html content + String errorDescription = errorMessage != null ? StringEscapeUtils.escapeHtml(errorMessage) : "Unknown error"; + + TemplateParameters params = templateHelper.parameters(); + params.set("errorCode", errorCode); + params.set("errorName", Status.fromStatusCode(errorCode).getReasonPhrase()); + params.set("errorDescription", errorDescription); + + // add cause if ?debug enabled and there is an exception + if (cause != null && ServletHelper.isDebug(request)) { + params.set("errorCause", new TemplateThrowableAdapter(cause)); + } + + String html = templateHelper.render(template, params); + try (PrintWriter out = new PrintWriter(new OutputStreamWriter(response.getOutputStream()))) { + out.println(html); + } + } + + /** + * Attach exception details to request. + * + * @since 3.0 + */ + static void attachCause(final HttpServletRequest request, final Throwable cause) { + if (isJavaLangError(cause)) { + // Log java.lang.Error exceptions at error level + log.error("Unexpected exception", getRootCause(cause)); + } + else { + log.debug("Attaching cause", cause); + } + request.setAttribute(ERROR_EXCEPTION_TYPE, cause.getClass()); + request.setAttribute(ERROR_EXCEPTION, cause); + } + + private static boolean isJavaLangError(final Throwable e) { + return getRootCause(e) instanceof Error; + } + +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/HeaderPatternFilter.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/HeaderPatternFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..08a1de54c78940fb87df7c22a408e112e6cbcbf6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/HeaderPatternFilter.java @@ -0,0 +1,116 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.web; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.sonatype.goodies.common.ComponentSupport; + +import com.google.common.base.Joiner; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; +import org.eclipse.sisu.Hidden; + +/** + * Filter for sanitizing http header values + * + * @since 3.2 + */ +@Named +@Hidden // hide from DynamicFilterChainManager because we statically install it in WebModule +@Singleton +public class HeaderPatternFilter + extends ComponentSupport + implements Filter +{ + + private static final String PATTERNS_PROPERTIES_FILE = "http-headers-patterns.properties"; + + private ImmutableMap validHeaderPatterns; + + @Override + public void init(final FilterConfig filterConfig) throws ServletException { + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); + Properties properties = new Properties(); + try (InputStream stream = getClass().getResourceAsStream(PATTERNS_PROPERTIES_FILE)) { + properties.load(stream); + } + catch (IOException ioe) { + log.error("IOException loading {} as a resource stream", PATTERNS_PROPERTIES_FILE, ioe); + } + for (String key : properties.stringPropertyNames()) { + String val = properties.getProperty(key); + try { + builder.put(key, Pattern.compile(val)); + } + catch (PatternSyntaxException pse) { + log.error("unable to compile the pattern for the header '{}', failed pattern is '{}', skipping", key, val, pse); + } + } + + validHeaderPatterns = builder.build(); + } + + @Override + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) + throws IOException, ServletException + { + if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) { + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + for (Entry entry : validHeaderPatterns.entrySet()) { + if (checkForBadHeader(httpRequest.getHeaders(entry.getKey()), entry.getValue())) { + log.warn("rejecting request from {} due to invalid header '{}: {}'", request.getRemoteHost(), entry.getKey(), + Joiner.on(",").join(Collections.list(httpRequest.getHeaders(entry.getKey())))); + httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + } + } + chain.doFilter(request, response); + } + + @Override + public void destroy() { + // ignore + } + + private static boolean checkForBadHeader(final Enumeration headers, final Pattern expression) { + while (headers != null && headers.hasMoreElements()) { + String header = headers.nextElement(); + if (!Strings.isNullOrEmpty(header) && !expression.matcher(header).matches()) { + return true; + } + } + return false; + } + +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/ThrowServlet.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/ThrowServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..bfc9aee3aaf7269d7888baf64fb0a5d0b3c96840 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/ThrowServlet.java @@ -0,0 +1,79 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.web; + +import java.io.IOException; + +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.sonatype.nexus.common.text.Strings2; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Helper servlet to throw an exception. + * + * @since 3.0 + */ +@Named +@Singleton +public class ThrowServlet + extends HttpServlet +{ + private static final Logger log = LoggerFactory.getLogger(ThrowServlet.class); + + private enum Type + { + RUNTIME, + ERROR, + IO, + SERVLET; + + static Type parse(final String value) { + if (value == null) { + return RUNTIME; + } + return valueOf(Strings2.upper(value)); + } + } + + @Override + protected void service(final HttpServletRequest request, final HttpServletResponse response) + throws ServletException, IOException + { + Type type = Type.parse(request.getParameter("type")); + String message = request.getParameter("message"); + log.info("Throwing {} w/message: {}", type, message); + + switch (type) { + case RUNTIME: + throw new RuntimeException(message); + + case IO: + throw new IOException(message); + + case SERVLET: + throw new ServletException(message); + + case ERROR: + default: + throw new Error(message); + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/WebModule.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/WebModule.java new file mode 100644 index 0000000000000000000000000000000000000000..c20cc50e480c8182381d7f67c2c41ce8a72e856c --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/web/WebModule.java @@ -0,0 +1,66 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.web; + +import javax.inject.Named; + +import org.sonatype.nexus.internal.metrics.MetricsModule; +import org.sonatype.nexus.internal.orient.OrientModule; + +import com.google.inject.AbstractModule; +import com.google.inject.Binder; +import com.google.inject.servlet.DynamicGuiceFilter; +import com.google.inject.servlet.GuiceFilter; +import com.google.inject.servlet.ServletModule; +import org.eclipse.sisu.inject.Sources; + +/** + * Web module. + * + * @since 3.0 + */ +@Named +public class WebModule + extends AbstractModule +{ + @Override + protected void configure() { + bind(GuiceFilter.class).to(DynamicGuiceFilter.class); + + // our configuration needs to be first-most when calculating order (some fudge room for edge-cases) + final Binder highPriorityBinder = binder().withSource(Sources.prioritize(0x70000000)); + + highPriorityBinder.install(new ServletModule() + { + @Override + protected void configureServlets() { + bind(HeaderPatternFilter.class); + bind(EnvironmentFilter.class); + bind(ErrorPageFilter.class); + + filter("/*").through(HeaderPatternFilter.class); + filter("/*").through(EnvironmentFilter.class); + filter("/*").through(ErrorPageFilter.class); + + bind(ErrorPageServlet.class); + + serve("/error.html").with(ErrorPageServlet.class); + serve("/throw.html").with(ThrowServlet.class); + } + }); + + highPriorityBinder.install(new MetricsModule()); + + install(new OrientModule()); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/webresources/DevModeResources.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/webresources/DevModeResources.java new file mode 100644 index 0000000000000000000000000000000000000000..7cb22c7a79097ebf67984a804d1b023a41036812 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/webresources/DevModeResources.java @@ -0,0 +1,132 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.webresources; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nullable; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; + +import com.google.common.base.Throwables; + +/** + * Utility related to finding resources when {@code NEXUS_RESOURCE_DIRS} environment-variable + * or {@code nexus.resource.dirs} system-property is set. + * + * @since 2.7 + */ +@Named +@Singleton +public class DevModeResources + extends ComponentSupport +{ + private static final String ENV_VAR = "NEXUS_RESOURCE_DIRS"; + + private static final String SYS_PROP = "nexus.resource.dirs"; + + /** + * List of directories configured as dev-mode resource locations, or null if not configured. + */ + @Nullable + private final List resourceLocations; + + public DevModeResources() { + this.resourceLocations = initializeResourceLocations(); + } + + /** + * Detect the dev-mode resources search path from environment-variable or system-property, or null if not configured. + */ + @Nullable + private String detectSearchPath() { + String envVar = System.getenv(ENV_VAR); + String sysProp = System.getProperty(SYS_PROP); + + // complain if both mechanisms to configure are present, this can/will cause confusion + if (envVar != null && sysProp != null) { + log.warn("Both environment-variable: {} and system-property: {} are set; environment-variable takes precedence", + ENV_VAR, SYS_PROP); + } + + if (envVar != null) { + log.info("Search-path configured from environment-variable: {}", ENV_VAR); + return envVar; + } + if (sysProp != null) { + log.info("Search-path configured from system-property: {}", SYS_PROP); + return sysProp; + } + + // not configured + return null; + } + + @Nullable + private List initializeResourceLocations() { + String searchPath = detectSearchPath(); + + if (searchPath != null) { + List locations = new ArrayList<>(); + for (String segment : searchPath.split(",")) { + try { + File dir = new File(segment).getCanonicalFile(); + if (dir.exists() && dir.isDirectory()) { + locations.add(dir); + } + else { + log.warn("Invalid search-path segment: {}; ignoring", segment); + } + } + catch (Exception e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + } + if (!locations.isEmpty()) { + return locations; + } + } + return null; + } + + /** + * Returns list of detected dev-mode resource locations or null if not configured or not valid locations detected. + * + * @since 3.0 + */ + @Nullable + public List getResourceLocations() { + return resourceLocations; + } + + /** + * Returns a file reference for given path if dev-mode is configured and a matching file exists. + */ + @Nullable + public File getFileIfOnFileSystem(final String path) { + if (resourceLocations != null) { + for (File dir : resourceLocations) { + File file = new File(dir, path); + if (file.exists()) { + return file; + } + } + } + return null; + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/webresources/WebResourceServiceImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/webresources/WebResourceServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..e615509f1a50162da0f17d18284fe5491a70e4b8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/webresources/WebResourceServiceImpl.java @@ -0,0 +1,175 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.webresources; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.ServletContext; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.mime.MimeSupport; +import org.sonatype.nexus.webresources.FileWebResource; +import org.sonatype.nexus.webresources.UrlWebResource; +import org.sonatype.nexus.webresources.WebResource; +import org.sonatype.nexus.webresources.WebResourceBundle; +import org.sonatype.nexus.webresources.WebResourceService; + +import com.google.common.collect.Maps; +import org.eclipse.sisu.BeanEntry; +import org.eclipse.sisu.Mediator; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default {@link WebResourceService} implementation. + * + * @since 2.8 + */ +@Singleton +@Named +public class WebResourceServiceImpl + extends ComponentSupport + implements WebResourceService +{ + private final DevModeResources devModeResources; + + private final ServletContext servletContext; + + private final MimeSupport mimeSupport; + + private final Map resourcePaths; + + @Inject + public WebResourceServiceImpl(final DevModeResources devModeResources, + final ServletContext servletContext, + final MimeSupport mimeSupport) + { + this.devModeResources = checkNotNull(devModeResources); + this.servletContext = checkNotNull(servletContext); + this.mimeSupport = checkNotNull(mimeSupport); + this.resourcePaths = Maps.newHashMap(); + + // make it clear we have DEV mode enabled + List locations = devModeResources.getResourceLocations(); + if (locations != null) { + log.warn("DEV mode resources is ENABLED"); + // spit out the locations where we will look for resources + for (File file : locations) { + log.info(" {}", file); + } + } + } + + private void addResource(final WebResource resource) { + String path = resource.getPath(); + log.trace("Adding resource: {} -> {}", path, resource); + final WebResource old = resourcePaths.put(path, resource); + if (old != null) { + // complain if any resources overlap + log.warn("Overlapping resources on path {}: old={}, new={}", path, old, resource); + } + } + + @Override + public Collection getPaths() { + return Collections.unmodifiableCollection(resourcePaths.keySet()); + } + + @Override + public Collection getResources() { + return Collections.unmodifiableCollection(resourcePaths.values()); + } + + @Override + public WebResource getResource(final String path) { + log.trace("Looking up resource: {}", path); + + WebResource resource = null; + + // 1) first "dev" resources if enabled (to override everything else) + File file = devModeResources.getFileIfOnFileSystem(path); + if (file != null) { + resource = new FileWebResource(file, path, mimeSupport.guessMimeTypeFromPath(file.getName()), false); + log.trace("Found dev-mode resource: {}", resource); + } + + // 2) second, look at "ordinary" resources, but only if devResource did not hit anything + if (resource == null) { + resource = resourcePaths.get(path); + if (resource != null) { + log.trace("Found bound resource: {}", resource); + } + } + + // 3) third, look into WAR embedded resources + if (resource == null) { + URL url; + try { + url = servletContext.getResource(path); + if (url != null) { + resource = new UrlWebResource(url, path, mimeSupport.guessMimeTypeFromPath(path)); + log.trace("Found servlet-context resource: {}", resource); + } + } + catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + return resource; + } + + @Named + static class ResourceBundleMediator + implements Mediator + { + @Override + public void add(BeanEntry entry, WebResourceServiceImpl watcher) throws Exception { + List resources = entry.getValue().getResources(); + if (resources != null) { + for (WebResource resource : resources) { + watcher.addResource(resource); + } + } + } + + @Override + public void remove(BeanEntry entry, WebResourceServiceImpl watcher) throws Exception { + // no-op + } + } + + @Named + static class ResourceMediator + implements Mediator + { + @Override + public void add(BeanEntry entry, WebResourceServiceImpl watcher) throws Exception { + watcher.addResource(entry.getValue()); + } + + @Override + public void remove(BeanEntry entry, WebResourceServiceImpl watcher) throws Exception { + // no-op + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/webresources/WebResourceServlet.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/webresources/WebResourceServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..fe3649cc4c4ceb09de392251adfe9beac5fcb4c7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/webresources/WebResourceServlet.java @@ -0,0 +1,154 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.webresources; + +import java.io.IOException; +import java.io.InputStream; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.sonatype.goodies.common.Time; +import org.sonatype.nexus.common.app.BaseUrlHolder; +import org.sonatype.nexus.servlet.ServletHelper; +import org.sonatype.nexus.webresources.WebResource; +import org.sonatype.nexus.webresources.WebResource.Prepareable; +import org.sonatype.nexus.webresources.WebResourceService; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.net.HttpHeaders.CACHE_CONTROL; +import static com.google.common.net.HttpHeaders.CONTENT_LENGTH; +import static com.google.common.net.HttpHeaders.CONTENT_TYPE; +import static com.google.common.net.HttpHeaders.IF_MODIFIED_SINCE; +import static com.google.common.net.HttpHeaders.LAST_MODIFIED; +import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; +import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED; + +/** + * Provides access to resources via configured {@link WebResourceService}. + * + * @since 2.8 + */ +@Singleton +@Named +public class WebResourceServlet + extends HttpServlet +{ + private static final Logger log = LoggerFactory.getLogger(WebResourceServlet.class); + + private final WebResourceService webResources; + + private final long maxAgeSeconds; + + @Inject + public WebResourceServlet(final WebResourceService webResources, + @Named("${nexus.webresources.maxAge:-30days}") final Time maxAge) + { + this.webResources = checkNotNull(webResources); + this.maxAgeSeconds = checkNotNull(maxAge.toSeconds()); + log.info("Max-age: {} ({} seconds)", maxAge, maxAgeSeconds); + } + + @Override + protected void doGet(final HttpServletRequest request, final HttpServletResponse response) + throws ServletException, IOException + { + String path = request.getPathInfo(); + + // default-page handling + if ("".equals(path) || "/".equals(path)) { + path = "/index.html"; + } + else if (path.endsWith("/")) { + path += "index.html"; + } + + WebResource resource = webResources.getResource(path); + if (resource == null) { + // if there is an index.html for the requested path, redirect to it + if (webResources.getResource(path + "/index.html") != null) { + String location = String.format("%s%s/", BaseUrlHolder.get(), path); + log.debug("Redirecting: {} -> {}", path, location); + response.sendRedirect(location); + } + else { + response.sendError(SC_NOT_FOUND); + } + return; + } + + serveResource(resource, request, response); + } + + private void serveResource(WebResource resource, + final HttpServletRequest request, + final HttpServletResponse response) + throws IOException + { + log.trace("Serving resource: {}", resource); + + // support resources which need to be prepared before serving + if (resource instanceof Prepareable) { + resource = ((Prepareable) resource).prepare(); + checkState(resource != null, "Prepared resource is null"); + } + assert resource != null; + + String contentType = resource.getContentType(); + if (contentType == null) { + contentType = WebResource.UNKNOWN_CONTENT_TYPE; + } + response.setHeader(CONTENT_TYPE, contentType); + response.setDateHeader(LAST_MODIFIED, resource.getLastModified()); + + // set content-length, complain if invalid + long size = resource.getSize(); + if (size < 0) { + log.warn("Resource {} has invalid size: {}", resource.getPath(), size); + } + response.setHeader(CONTENT_LENGTH, String.valueOf(size)); + + // set max-age if cacheable + if (resource.isCacheable()) { + response.setHeader(CACHE_CONTROL, "max-age=" + maxAgeSeconds); + } + else { + ServletHelper.addNoCacheResponseHeaders(response); + } + + // honor if-modified-since GETs + long ifModifiedSince = request.getDateHeader(IF_MODIFIED_SINCE); + // handle conditional GETs + if (ifModifiedSince > -1 && resource.getLastModified() <= ifModifiedSince) { + // this is a conditional GET using time-stamp, and resource is not modified + response.setStatus(SC_NOT_MODIFIED); + } + else { + // send the content only if needed (this method will be called for HEAD requests too) + if ("GET".equalsIgnoreCase(request.getMethod())) { + try (InputStream in = resource.getInputStream()) { + ServletHelper.sendContent(in, response); + } + } + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/webresources/WebResourcesModule.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/webresources/WebResourcesModule.java new file mode 100644 index 0000000000000000000000000000000000000000..4fad4b1c6d72094985f828f399e57d1f979bf161 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/webresources/WebResourcesModule.java @@ -0,0 +1,56 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.webresources; + +import javax.inject.Named; + +import org.sonatype.nexus.security.FilterChainModule; +import org.sonatype.nexus.security.SecurityFilter; +import org.sonatype.nexus.security.anonymous.AnonymousFilter; + +import com.google.inject.AbstractModule; +import com.google.inject.Binder; +import com.google.inject.servlet.ServletModule; +import org.eclipse.sisu.inject.Sources; + +/** + * Web resources module. Both servlet and filter-chain are installed with the lowest priority. + * + * @since 2.8 + */ +@Named +public class WebResourcesModule + extends AbstractModule +{ + @Override + protected void configure() { + final Binder lowPriorityBinder = binder().withSource(Sources.prioritize(Integer.MIN_VALUE)); + + lowPriorityBinder.install(new ServletModule() + { + @Override + protected void configureServlets() { + serve("/*").with(WebResourceServlet.class); + filter("/*").through(SecurityFilter.class); + } + }); + + lowPriorityBinder.install(new FilterChainModule() + { + @Override + protected void configure() { + addFilterChain("/**", AnonymousFilter.NAME); + } + }); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/AuthTicketCache.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/AuthTicketCache.java new file mode 100644 index 0000000000000000000000000000000000000000..7e34249685e3d9bea422285e3e325ba4bd1a341e --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/AuthTicketCache.java @@ -0,0 +1,119 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.wonderland; + +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.goodies.common.Mutex; +import org.sonatype.goodies.common.Time; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Maps; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * Manages cache (and expiration) of authentication tickets. + * + * @since 2.7 + */ +@Named +public class AuthTicketCache + extends ComponentSupport +{ + private static final String CPREFIX = "${wonderland.authTicketCache"; + + private final Mutex lock = new Mutex(); + + private final Map tokens = Maps.newHashMap(); + + private final Time expireAfter; + + @Inject + public AuthTicketCache(@Named(CPREFIX + ".expireAfter:-20s}") final Time expireAfter) { + this.expireAfter = checkNotNull(expireAfter); + log.debug("Expire after: {}", expireAfter); + } + + @VisibleForTesting + public AuthTicketCache() { + this(Time.seconds(2)); + } + + private long now() { + return System.currentTimeMillis(); + } + + /** + * Expires any tokens older than {@link #expireAfter}. + */ + private void expireTokens() { + boolean trace = log.isTraceEnabled(); + long now = now(); + Iterator> iter = tokens.entrySet().iterator(); + while (iter.hasNext()) { + Entry entry = iter.next(); + if (isTokenExpired(now, entry)) { + iter.remove(); + if (trace) { + log.trace("Expired token: {}", entry.getKey()); + } + } + } + + if (trace && !tokens.isEmpty()) { + log.trace("Valid tokens:"); + for (Entry entry : tokens.entrySet()) { + log.trace(" {}", entry.getKey()); + } + } + } + + @VisibleForTesting + protected boolean isTokenExpired(final long now, final Entry entry) { + long diff = now - entry.getValue(); + return diff > expireAfter.toMillis(); + } + + /** + * Add token to the cache. + */ + public void add(final String token) { + synchronized (lock) { + expireTokens(); + // Sanity check we don't clobber tokens + checkState(!tokens.containsKey(token), "Duplicate token"); //NON-NLS + tokens.put(token, now()); + } + } + + /** + * Remove token from cache. + * + * @return True if the token existed (was added and not yet expired) + */ + public boolean remove(final String token) { + synchronized (lock) { + expireTokens(); + Long tmp = tokens.remove(token); + return tmp != null; + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/AuthTicketGenerator.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/AuthTicketGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..cb86666c6ce599807c4f0e33a90b5fa3c90192d0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/AuthTicketGenerator.java @@ -0,0 +1,63 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.wonderland; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.crypto.RandomBytesGenerator; + +import com.google.common.io.BaseEncoding; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Generates random authentication tickets. + * + * @since 2.7 + */ +@Named +public class AuthTicketGenerator + extends ComponentSupport +{ + private static final String CPREFIX = "${wonderland.authTicketGenerator"; + + private final RandomBytesGenerator randomBytes; + + private final int defaultSize; + + // NOTE: Default size is 66 to make full use of base64 encoding w/o padding + + @Inject + public AuthTicketGenerator(final RandomBytesGenerator randomBytes, + @Named(CPREFIX + ".defaultSize:-66}") final int defaultSize) + { + this.randomBytes = checkNotNull(randomBytes); + this.defaultSize = defaultSize; + log.debug("Default size: {}", defaultSize); + } + + protected String encode(final byte[] bytes) { + return BaseEncoding.base64().encode(bytes); + } + + public String generate(final int size) { + byte[] bytes = randomBytes.generate(size); + return encode(bytes); + } + + public String generate() { + return generate(defaultSize); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/AuthTicketServiceImpl.java b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/AuthTicketServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..9f54fe4d05161963017d25588c2f393b2d10eaf3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/AuthTicketServiceImpl.java @@ -0,0 +1,59 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.wonderland; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.wonderland.AuthTicketService; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default {@link AuthTicketService} implementation. + * + * @since 2.7 + */ +@Named +@Singleton +public class AuthTicketServiceImpl + extends ComponentSupport + implements AuthTicketService +{ + private final AuthTicketGenerator authTicketGenerator; + + private final AuthTicketCache authTicketCache; + + @Inject + public AuthTicketServiceImpl(final AuthTicketGenerator authTicketGenerator, + final AuthTicketCache authTicketCache) + { + this.authTicketGenerator = checkNotNull(authTicketGenerator); + this.authTicketCache = checkNotNull(authTicketCache); + } + + @Override + public String createTicket() { + String ticket = authTicketGenerator.generate(); + authTicketCache.add(ticket); + return ticket; + } + + @Override + public boolean redeemTicket(final String ticket) { + checkNotNull(ticket); + return authTicketCache.remove(ticket); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/DownloadServiceImpl.groovy b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/DownloadServiceImpl.groovy new file mode 100644 index 0000000000000000000000000000000000000000..5d58ffd2873756d9468a8f0c4e34f9a30cc062c7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/DownloadServiceImpl.groovy @@ -0,0 +1,111 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.wonderland + +import java.nio.file.Files +import java.text.SimpleDateFormat +import java.util.concurrent.atomic.AtomicLong + +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.goodies.common.ComponentSupport +import org.sonatype.nexus.common.app.ApplicationDirectories +import org.sonatype.nexus.common.wonderland.AuthTicketService +import org.sonatype.nexus.common.wonderland.DownloadService + +import static com.google.common.base.Preconditions.checkArgument +import static com.google.common.base.Preconditions.checkNotNull + +/** + * Default {@link DownloadService}. + * + * @since 2.8 + */ +@Named +@Singleton +class DownloadServiceImpl +extends ComponentSupport +implements DownloadService +{ + + /** + * Counter used to generate unique names. + */ + private static final AtomicLong counter = new AtomicLong() + + /** + * Directory where files to be downloaded are stored. + */ + private final File downloadDir + + private final AuthTicketService authTickets + + @Inject + DownloadServiceImpl(final ApplicationDirectories applicationDirectories, + final AuthTicketService authTicketService) + { + checkNotNull(applicationDirectories) + this.authTickets = checkNotNull(authTicketService) + + // resolve where files to be downloaded will be stored + downloadDir = applicationDirectories.getWorkDirectory('downloads') + log.info 'Downloads directory: {}', downloadDir + } + + @Override + File getDirectory() { + return downloadDir + } + + @Override + File get(String fileName, String authTicket) { + log.info 'Download: {}', fileName + + if (!authTickets.redeemTicket(authTicket)) { + throw new IllegalArgumentException('Invalid authentication ticket') + } + + def file = new File(downloadDir, fileName) + + ensureWithinDownloads(file) + + if (!file.exists() && file.isFile()) { + log.warn 'File {} not found in download directory (or is not a file)', file + return null + } + return file + } + + @Override + File move(File source, String name) { + def target = new File(downloadDir, name) + ensureWithinDownloads(target) + Files.move(source.toPath(), target.toPath()) + log.debug 'Moved {} to {}', source, target + return target + } + + @Override + String uniqueName(String prefix) { + return prefix + new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()) + "-" + counter.incrementAndGet() + } + + /** + * Ensure we do not leak references outside of the downloads directory, only direct children can be served + */ + private void ensureWithinDownloads(File file) { + checkArgument(file.parentFile == downloadDir, "Reference outside of downloads dir: $file") + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/WonderlandSecurityContributor.groovy b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/WonderlandSecurityContributor.groovy new file mode 100644 index 0000000000000000000000000000000000000000..aff7f75729468c74e1cd6f13282376b44e63247c --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/java/org/sonatype/nexus/internal/wonderland/WonderlandSecurityContributor.groovy @@ -0,0 +1,49 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.wonderland + +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.nexus.security.config.CPrivilege +import org.sonatype.nexus.security.config.MemorySecurityConfiguration +import org.sonatype.nexus.security.config.SecurityContributor + +/** + * Wonderland security configuration. + * + * @since 3.0 + */ +@Named +@Singleton +class WonderlandSecurityContributor + implements SecurityContributor +{ + @Override + MemorySecurityConfiguration getContribution() { + return new MemorySecurityConfiguration( + privileges: [ + new CPrivilege( + id: 'nx-wonderland-all', + description: 'All permissions for Wonderland', + type: 'application', + properties: [ + domain: 'wonderland', + actions: '*' + ] + ) + ] + ) + } +} + diff --git a/thirdparty-bundles/components/nexus-base/src/main/resources/org/sonatype/nexus/internal/web/errorPageHtml.vm b/thirdparty-bundles/components/nexus-base/src/main/resources/org/sonatype/nexus/internal/web/errorPageHtml.vm new file mode 100644 index 0000000000000000000000000000000000000000..db4d2444c04ccc16a69e7d43430ab0904a537bda --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/resources/org/sonatype/nexus/internal/web/errorPageHtml.vm @@ -0,0 +1,82 @@ +#* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + *### + + + + + $errorCode - Nexus Repository Manager + + +#* START favicon links and meta + * favicon set for multiple browsers, OS shortcuts, etc. + * Generated at Real Favicon Generator, https://realfavicongenerator.net/ + * + * Associated files not referenced explicitly in link or meta tags: + * mstile-*.png - Win8+ desktop support + * browserconfig.xml - Win8+ desktop support + * apple-touch-icon.png - OSX Safari desktop support +*# +## IE, early versions (comment required) + +## Safari on MacOS and iOS + + +## classic favicon, shown in browser tabs + +## IE, later versions + +## Win8+ + + +## END favicon links + + + + + + +
+
+ + Error $errorCode + $errorName +
+
+
+ $errorDescription +
+ #if($errorCause) +
+
${errorCause.trace}
+
+ #end +
+
+ + diff --git a/thirdparty-bundles/components/nexus-base/src/main/resources/org/sonatype/nexus/internal/web/http-headers-patterns.properties b/thirdparty-bundles/components/nexus-base/src/main/resources/org/sonatype/nexus/internal/web/http-headers-patterns.properties new file mode 100644 index 0000000000000000000000000000000000000000..c7e5d76d30eec40274f70464bf1179c13c9a8d8b --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/resources/org/sonatype/nexus/internal/web/http-headers-patterns.properties @@ -0,0 +1,32 @@ +# +# Sonatype Nexus (TM) Open Source Version +# Copyright (c) 2008-present Sonatype, Inc. +# All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. +# +# This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, +# which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. +# +# Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks +# of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the +# Eclipse Foundation. All other trademarks are the property of their respective owners. +# + +# +# Joined Regex for +# - Valid IPv4 Address +# - Valid IPv6 Address +# - Valid IPv6 Hex Compressed Address +# - Valid Hostname according to RFC-1123 +# - Port suffix +# See also HostnameValidator.java and Validator.js for other uses of this regex. +Host=^(((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|\ + (\\[(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\])|\ + (\\[((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\])|\ + (([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9]))\ + (:([0-9]+))?$ +X-Forwarded-Proto=^[a-zA-Z]+$ + +#Kinda ugly, but here's an explanation +#Left hand side matches the header when proto is present and followed by one or more alphabetic characters +#Right hand side matches when proto is not present, as it's optional in the Forwarded header +Forwarded=(^|.*;)\\s*proto=["a-zA-Z]+.*|^((?!proto).)*$ diff --git a/thirdparty-bundles/components/nexus-base/src/main/resources/static/css/nexus-content.css b/thirdparty-bundles/components/nexus-base/src/main/resources/static/css/nexus-content.css new file mode 100644 index 0000000000000000000000000000000000000000..25442479f5b86884c7d6626351bf1047e24d26d1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/main/resources/static/css/nexus-content.css @@ -0,0 +1,189 @@ +/** + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +* { + box-sizing: border-box; +} + +body { + font-family: 'Proxima Nova', 'Helvetica Neue', Helvetica, Arial, sans-serif; + background-color: #dddddd; + margin: 0; + padding: 0; +} + +body.htmlIndex { + padding: 0.5em; +} + +.nexus-header { + position: absolute; + left: 0; + top: 0; + right: 0; + height: 40px; + padding: 2px 4px 2px 16px; + + font-size: 0; + + background-color: #000000; + color: #ffffff; +} + +.nexus-header a { + color: #FFFFFF; + text-decoration: none; +} +.nexus-header a:hover, +.nexus-header a:focus { + color: #96CAEE; +} +.nexus-header a:active { + color: #0F4976; +} + + +.nexus-header .product-logo { + display: inline-block; + vertical-align: middle; + margin-right: 8px; +} +.nexus-header .product-id { + display: inline-block; + vertical-align: middle; +} + +.nexus-header .product-id__line-1 { + font-size: 20px; + line-height: 1.2em; +} +.nexus-header .product-id__line-2 { + font-size: 11px; + line-height: 1.1em; +} + +.nexus-header .product-logo img { + vertical-align: middle; + border: 0; +} + +.nexus-header .product-name { + display: inline-block; + letter-spacing: 0.05em; + font-weight: 100; +} +.nexus-header .product-spec { + display: inline-block; + letter-spacing: 0.05em; + font-weight: 700; +} + + + +.nexus-body { + padding: 0 20px 0 20px; + position: absolute; + left: 0; + top: 40px; + right: 0; + bottom: 0; + border-left: #cbcbcb 1px solid; + border-right: #cbcbcb 1px solid; + background-color: #f4f4f4; +} + +.nexus-body img { + float: left; + margin-right: 10px; +} + +.nexus-body .content-header { + font-weight: bold; + position: absolute; + left: 0; + right: 0; + padding: 10px; + background-color: #ffffff; + border-bottom: 1px solid #dddddd; + z-index: 1; +} + +.nexus-body .content-header .title { + font-size: 26px; +} + +.nexus-body .content-header .description { + font-size: 13px; + padding-left: 6px; + font-weight: normal; +} + +.nexus-body .content-body { + position: absolute; + top: 53px; + left: 0; + right: 0; + bottom: 0; + padding: 10px 0 10px 0; + margin: 0 10px 0 10px; + overflow: auto; +} + +.nexus-body .content-section { + font-size: 13px; + line-height: 20px; + background-color: #ffffff; + border: #dddddd solid 1px; + word-wrap: break-word; + padding: 10px; + margin-bottom: 10px; +} + +/* + * Tabular data + */ + +.content-section table { + font-size: 13px; + margin-top: 13px; + border-spacing: 0; + width: 100%; +} + +.content-section tbody tr:nth-child(odd) { + background-color: #FAFAFA; +} + +.content-section th, +.content-section td { + text-align: left; + vertical-align: top; + padding: 5px 24px 5px 5px; + border-bottom: solid 1px #EDEDED; +} + +/* Force the first column to have a fixed width */ +.content-section td:first-child { + width: 1%; + white-space: nowrap; +} + +.content-section th:last-child, +.content-section td:last-child { + padding-right: 5px; +} + +.content-section th { + font-weight: normal; + font-style: italic; +} diff --git a/thirdparty-bundles/components/nexus-base/src/main/resources/static/images/nexus.png b/thirdparty-bundles/components/nexus-base/src/main/resources/static/images/nexus.png new file mode 100644 index 0000000000000000000000000000000000000000..e9ebf2e735d416c14a808b18454f41d97f51fde8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-base/src/main/resources/static/images/nexus.png differ diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/app/ApplicationDirectoriesImplTest.java b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/app/ApplicationDirectoriesImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..be67ff9dfad86eea61c0ce3746177add1d10416c --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/app/ApplicationDirectoriesImplTest.java @@ -0,0 +1,92 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.app; + +import java.io.File; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.goodies.testsupport.hamcrest.FileMatchers; + +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; + +/** + * Tests for {@link ApplicationDirectoriesImpl} + */ +public class ApplicationDirectoriesImplTest + extends TestSupport +{ + private File installDir; + + private File workDir; + + private ApplicationDirectoriesImpl underTest; + + @Before + public void setUp() throws Exception { + installDir = util.createTempDir("install"); + workDir = util.createTempDir("work"); + underTest = new ApplicationDirectoriesImpl(installDir, workDir); + } + + @Test + public void ensureTempDir_exists() throws Exception { + File dir = underTest.getTemporaryDirectory(); + assertThat(dir, notNullValue()); + } + + @Test + public void ensureWorkDir_exists() throws Exception { + File dir = underTest.getWorkDirectory(); + assertThat(dir, notNullValue()); + assertThat(dir, is(workDir)); + assertThat(dir, FileMatchers.exists()); + } + + @Test + public void ensureWorkDir_childExists() throws Exception { + File dir = underTest.getWorkDirectory("child"); + assertThat(dir, notNullValue()); + assertThat(dir, FileMatchers.exists()); + } + + @Test + public void ensureWorkDir_childWithCreateExists() throws Exception { + File dir = underTest.getWorkDirectory("child", true); + assertThat(dir, notNullValue()); + assertThat(dir, FileMatchers.exists()); + } + + @Test + public void ensureWorkDir_childNoCreateNotExists() throws Exception { + File dir = underTest.getWorkDirectory("child", false); + assertThat(dir, notNullValue()); + assertThat(dir, not(FileMatchers.exists())); + } + + @Test + public void ensureWorkDir_referencesSonatypeWorkFolderUnlessAbsolute() throws Exception { + File tempDir = util.createTempDir("temp"); + + File relative = underTest.getWorkDirectory("."); + File absolute = underTest.getWorkDirectory(tempDir.getAbsolutePath()); + assertThat(relative.getCanonicalFile(), equalTo(workDir.getCanonicalFile())); + assertThat(absolute.getCanonicalFile(), equalTo(tempDir.getCanonicalFile())); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/app/BaseUrlManagerImplTest.java b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/app/BaseUrlManagerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3babb51fce4e33891dfd5bc83075e291f99f6b6a --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/app/BaseUrlManagerImplTest.java @@ -0,0 +1,101 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.app; + +import javax.servlet.http.HttpServletRequest; + +import org.sonatype.goodies.testsupport.TestSupport; + +import com.google.inject.Provider; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link BaseUrlManagerImpl} + */ +public class BaseUrlManagerImplTest + extends TestSupport +{ + static final String NUGET_QUERY = "/repository/nuget.org-proxy/Packages(Id='jQuery',Version='2.1.4')"; + static final String ENCODED_NUGET_QUERY = "/repository/nuget.org-proxy/Packages(Id=%27jQuery%27,Version=%272.1.4%27)"; + static final String FAKE_URL = "http://example.com:1234/foo/bar"; + + @Mock + Provider requestProvider; + + @Mock + HttpServletRequest request; + + BaseUrlManagerImpl underTest; + + @Before + public void setUp() { + underTest = new BaseUrlManagerImpl(requestProvider, false); + } + + @Test + public void forceUrl() { + underTest.setForce(true); + underTest.setUrl(FAKE_URL); + assertThat(underTest.detectUrl(), equalTo(FAKE_URL)); + } + + @Test + public void noRequest() { + underTest.setUrl(FAKE_URL); + + when(requestProvider.get()).thenReturn(null); + + assertThat(underTest.detectUrl(), equalTo(FAKE_URL)); + } + + @Test + public void noBaseUrl() { + when(requestProvider.get()).thenReturn(null); + + assertThat(underTest.detectUrl(), nullValue()); + } + + @Test + public void defaultContext() { + testNuGetQuery("http://localhost:8081", ""); + } + + @Test + public void nexusContext() { + testNuGetQuery("http://localhost:8081", "/nexus"); + } + + @Test + public void nexusFooBarBazContext() { + testNuGetQuery("http://localhost:8081", "/nexus/foo/bar/baz"); + } + + void testNuGetQuery(String protocolHostPort, String context) { + String expectedBaseUrl = protocolHostPort + context; + + when(requestProvider.get()).thenReturn(request); + when(request.getRequestURL()).thenReturn(new StringBuffer(expectedBaseUrl + ENCODED_NUGET_QUERY)); + when(request.getContextPath()).thenReturn(context); + when(request.getRequestURI()).thenReturn(context + ENCODED_NUGET_QUERY); + when(request.getServletPath()).thenReturn(NUGET_QUERY); + + assertThat(underTest.detectUrl(), equalTo(expectedBaseUrl)); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/event/EventManagerImplTest.java b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/event/EventManagerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..555076881ff46da39f7cc97ddebde907dffaadde --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/event/EventManagerImplTest.java @@ -0,0 +1,203 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.event; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.event.EventAware.Asynchronous; +import org.sonatype.nexus.common.event.EventHelper; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.security.subject.FakeAlmightySubject; + +import com.google.common.base.Throwables; +import com.google.common.eventbus.Subscribe; +import org.eclipse.sisu.inject.DefaultBeanLocator; +import org.junit.Test; + +import static com.jayway.awaitility.Awaitility.await; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +/** + * Tests for {@link EventManagerImpl}. + */ +public class EventManagerImplTest + extends TestSupport +{ + @Test + public void dispatchOrder() { + EventManager underTest = new EventManagerImpl(new DefaultBeanLocator(), new EventExecutor()); + ReentrantHandler handler = new ReentrantHandler(underTest); + + underTest.register(handler); + underTest.post("a string"); + + assertThat(handler.firstCalled, is("handle2")); + } + + private class ReentrantHandler + { + private final EventManager eventManager; + + private String firstCalled = null; + + ReentrantHandler(final EventManager eventManager) { + this.eventManager = eventManager; + } + + @Subscribe + public void handle1(final String event) { + eventManager.post(1); + if (firstCalled == null) { + firstCalled = "handle1"; + } + } + + @Subscribe + public void handle2(final Integer event) { + if (firstCalled == null) { + firstCalled = "handle2"; + } + } + } + + @Test + public void asyncInheritsIsReplicating() throws Exception { + EventExecutor executor = new EventExecutor(); + EventManager underTest = new EventManagerImpl(new DefaultBeanLocator(), executor); + AsyncReentrantHandler handler = new AsyncReentrantHandler(underTest); + underTest.register(handler); + + executor.start(); // enable multi-threaded mode + + // non-replicating case + FakeAlmightySubject.forUserId("testUser").execute( + () -> underTest.post("a string")); + + await().atMost(5, TimeUnit.SECONDS).until(underTest::isCalmPeriod); + + // handled two events, neither were replicating + assertThat(handler.handledCount.get(), is(2)); + assertThat(handler.replicatingCount.get(), is(0)); + + // replicating case + FakeAlmightySubject.forUserId("testUser").execute( + () -> EventHelper.asReplicating( + () -> underTest.post("a string"))); + + await().atMost(5, TimeUnit.SECONDS).until(underTest::isCalmPeriod); + + // handled two more events, both were replicating + assertThat(handler.handledCount.get(), is(4)); + assertThat(handler.replicatingCount.get(), is(2)); + + executor.stop(); // go back to single-threaded mode + } + + private class AsyncReentrantHandler + implements Asynchronous + { + private final EventManager eventManager; + + private AtomicInteger handledCount = new AtomicInteger(); + + private AtomicInteger replicatingCount = new AtomicInteger(); + + AsyncReentrantHandler(final EventManager eventManager) { + this.eventManager = eventManager; + } + + @Subscribe + public void handle1(final String event) { + eventManager.post(2.0f); + + handledCount.incrementAndGet(); + if (EventHelper.isReplicating()) { + replicatingCount.incrementAndGet(); + } + } + + @Subscribe + public void handle2(final Float event) { + handledCount.incrementAndGet(); + if (EventHelper.isReplicating()) { + replicatingCount.incrementAndGet(); + } + } + } + + @Test + public void singleThreadedOnShutdown() throws Exception { + EventExecutor executor = new EventExecutor(); + EventManager underTest = new EventManagerImpl(new DefaultBeanLocator(), executor); + AsyncHandler handler = new AsyncHandler(); + underTest.register(handler); + + FakeAlmightySubject.forUserId("testUser").execute(() -> underTest.post("first")); + + // executor is initially in single-threaded mode + + assertThat(handler.handledByThread, hasSize(1)); + assertThat(handler.handledByThread.get(0), is(Thread.currentThread())); + + executor.start(); // enable multi-threaded mode + + FakeAlmightySubject.forUserId("testUser").execute(() -> underTest.post("foo")); + FakeAlmightySubject.forUserId("testUser").execute(() -> underTest.post("bar")); + + executor.stop(); // waits for threads to finish + + assertThat(handler.handledByThread, hasSize(3)); + assertThat(handler.handledByThread.get(1), is(not(Thread.currentThread()))); + assertThat(handler.handledByThread.get(2), is(not(Thread.currentThread()))); + assertThat(handler.handledByThread.get(1), is(not(handler.handledByThread.get(2)))); + + // executor is now back in single-threaded mode + + FakeAlmightySubject.forUserId("testUser").execute(() -> underTest.post("last")); + + assertThat(handler.handledByThread, hasSize(4)); + assertThat(handler.handledByThread.get(3), is(Thread.currentThread())); + } + + @Test + public void singleThreadedOnShutdownWhenReplicating() throws Exception { + EventHelper.asReplicating(() -> { + try { + singleThreadedOnShutdown(); + } + catch (Exception e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + }); + } + + private class AsyncHandler + implements Asynchronous + { + private List handledByThread = new CopyOnWriteArrayList<>(); + + @Subscribe + public void handle(final String event) throws Exception { + handledByThread.add(Thread.currentThread()); + Thread.sleep(100); // make sure events are handled by different threads from the pool + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/log/LogMarkerImplTest.java b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/log/LogMarkerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3291622a9d88582848a6edd42d7458a5111ee153 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/log/LogMarkerImplTest.java @@ -0,0 +1,85 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log; + +import java.util.EnumSet; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.common.log.LogManager; +import org.sonatype.nexus.common.log.LogMarkInsertedEvent; +import org.sonatype.nexus.common.log.LoggerLevel; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Tests {@link LogMarkerImpl}. + */ +public class LogMarkerImplTest + extends TestSupport +{ + private static final String LOG_NAME = LogMarkerImpl.class.getName(); + + @Mock + private LogManager logManager; + + @Mock + private EventManager eventManager; + + private LogMarkerImpl logMarker; + + @Before + public void setUp() { + logMarker = new LogMarkerImpl(logManager, eventManager); + when(logManager.getLoggerEffectiveLevel(LOG_NAME)).thenReturn(LoggerLevel.INFO); + } + + @Test + public void testMarkLog_EmitEvent() { + logMarker.markLog("test"); + ArgumentCaptor argCaptor = ArgumentCaptor.forClass(LogMarkInsertedEvent.class); + verify(eventManager).post(argCaptor.capture()); + assertThat(argCaptor.getValue().getMessage(), is("test")); + } + + @Test + public void testMarkLog_EnsureLogLevel() { + for (LoggerLevel level : EnumSet.complementOf(EnumSet.of(LoggerLevel.DEFAULT))) { + reset(logManager); + when(logManager.getLoggerEffectiveLevel(LOG_NAME)).thenReturn(level); + logMarker.markLog("test"); + try { + if (EnumSet.of(LoggerLevel.OFF, LoggerLevel.ERROR, LoggerLevel.WARN).contains(level)) { + verify(logManager).setLoggerLevel(LOG_NAME, LoggerLevel.INFO); + } + else { + verify(logManager, never()).setLoggerLevel(anyObject(), anyObject()); + } + } + catch (AssertionError e) { + throw new AssertionError("Mishandled log level " + level, e); + } + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/log/LogbackLogManagerTest.groovy b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/log/LogbackLogManagerTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..d429491047fcde3de485100870f24fb1a3e99072 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/log/LogbackLogManagerTest.groovy @@ -0,0 +1,110 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.common.event.EventManager +import org.sonatype.nexus.common.log.LogConfigurationCustomizer +import org.sonatype.nexus.common.log.LoggerLevel + +import ch.qos.logback.classic.Level +import org.eclipse.sisu.inject.BeanLocator +import org.junit.Before +import org.junit.Test + +import static org.mockito.Mockito.mock + +/** + * Tests for {@link LogbackLogManager}. + */ +public class LogbackLogManagerTest + extends TestSupport +{ + String fooLoggerName + + String barLoggerName + + @Before + void setUp() { + // generate some unique logger names to avoid having to mock up logback which is used by tests + fooLoggerName = 'foo' + System.currentTimeMillis() + barLoggerName = 'bar' + System.currentTimeMillis() + } + + /** + * Ensure if a customizer configures a level a logger is created with that level, + * and if a customizer configures a default that it does not create a logger. + */ + @Test + void 'customizer sets logger level'() { + def customizer = new LogConfigurationCustomizer() { + @Override + void customize(final LogConfigurationCustomizer.Configuration configuration) { + configuration.setLoggerLevel(fooLoggerName, LoggerLevel.DEBUG) + configuration.setLoggerLevel(barLoggerName, LoggerLevel.DEFAULT) + } + } + + def underTest = new LogbackLogManager(mock(EventManager.class), mock(BeanLocator.class), new MemoryLoggerOverrides()) + + def context = LogbackLogManager.loggerContext() + + // verify default state + assert !context.exists(fooLoggerName) + assert !context.exists(barLoggerName) + + // start the manager + underTest.start() + + // manually apply customizer (normally handled by mediator) + underTest.registerCustomization(customizer) + + // verify customization was applied + assert context.getLogger(fooLoggerName).level == Level.DEBUG + + // DEFAULT logger should not have been created + assert !context.exists(barLoggerName) + } + + /** + * Ensure that if a customizer defines a non-default level, and overrides set a different level, + * that the end result is the logger has the level from overrides. + */ + @Test + void 'overrides sets logger level when customizer is present'() { + def customizer = new LogConfigurationCustomizer() { + @Override + void customize(final LogConfigurationCustomizer.Configuration configuration) { + configuration.setLoggerLevel(fooLoggerName, LoggerLevel.DEBUG) + } + } + + MemoryLoggerOverrides overrides = new MemoryLoggerOverrides() + overrides.set(fooLoggerName, LoggerLevel.ERROR) + def underTest = new LogbackLogManager(mock(EventManager.class), mock(BeanLocator.class), overrides) + + def context = LogbackLogManager.loggerContext() + + // verify default state + assert !context.exists(fooLoggerName) + + // start the manager + underTest.start() + + // manually apply customizer (normally handled by mediator) + underTest.registerCustomization(customizer) + + // verify customization was applied + assert context.getLogger(fooLoggerName).level == Level.ERROR + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/log/LogbackLoggerOverridesTest.groovy b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/log/LogbackLoggerOverridesTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..fa97ff6be0a71d9bd059577d23753679003c3b7f --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/log/LogbackLoggerOverridesTest.groovy @@ -0,0 +1,92 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.common.log.LoggerLevel + +import ch.qos.logback.classic.Logger +import groovy.xml.MarkupBuilder +import org.junit.Before +import org.junit.Test + +/** + * Tests for {@link LogbackLoggerOverrides}. + */ +public class LogbackLoggerOverridesTest + extends TestSupport +{ + private File file + + private LogbackLoggerOverrides underTest + + @Before + void setUp() { + file = util.createTempFile('logback-overrides') + underTest = new LogbackLoggerOverrides(file) + } + + @Test + void 'loggers are written in an expected logback xml-format'() { + underTest.set(Logger.ROOT_LOGGER_NAME, LoggerLevel.WARN) + underTest.set('foo', LoggerLevel.ERROR) + underTest.set('bar', LoggerLevel.INFO) + underTest.save() + + assert file.exists() + + log('XML:\n{}', file.text) + + def doc = new XmlSlurper().parse(file) + assert doc.property.size() == 1 + assert doc.logger.size() == 2 + + // root is stored as property name=root.level + assert doc.property.breadthFirst().find {it.@name == 'root.level'}.@value == 'WARN' + + assert doc.logger.breadthFirst().find {it.@name == 'foo'}.@level == 'ERROR' + assert doc.logger.breadthFirst().find {it.@name == 'bar'}.@level == 'INFO' + } + + @Test + void 'loggers are read from logback xml-format'() { + def xml = new MarkupBuilder(file.newWriter()) + xml.included { + logger(name: 'foo', level: 'ERROR') + logger(name: 'bar', level: 'INFO') + } + + log('XML:\n{}', file.text) + + underTest.load() + + assert underTest.get('foo') == LoggerLevel.ERROR + assert underTest.get('bar') == LoggerLevel.INFO + } + + @Test + void 'logger elements are removed when reset'() { + underTest.set('foo', LoggerLevel.ERROR) + underTest.set('bar', LoggerLevel.INFO) + underTest.save() + assert file.exists() + + underTest.reset() + assert file.exists() + + log('XML:\n{}', file.text) + + def doc = new XmlSlurper().parse(file) + assert doc.logger.size() == 0 + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/log/MemoryLoggerOverrides.java b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/log/MemoryLoggerOverrides.java new file mode 100644 index 0000000000000000000000000000000000000000..6d6514ebce66a230b062e6953466df1dd0e90182 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/log/MemoryLoggerOverrides.java @@ -0,0 +1,79 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.log; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.log.LoggerLevel; + +/** + * In-memory {@link LoggerOverrides}. + */ +public class MemoryLoggerOverrides + extends ComponentSupport + implements LoggerOverrides +{ + private final Map backing = new HashMap<>(); + + public Map getBacking() { + return backing; + } + + @Override + public void load() { + // empty + } + + @Override + public void save() { + // empty + } + + @Override + public void reset() { + backing.clear(); + } + + @Override + public void set(final String name, final LoggerLevel level) { + backing.put(name, level); + } + + @Override + @Nullable + public LoggerLevel get(final String name) { + return backing.get(name); + } + + @Override + @Nullable + public LoggerLevel remove(final String name) { + return backing.remove(name); + } + + @Override + public boolean contains(final String name) { + return backing.containsKey(name); + } + + @Override + public Iterator> iterator() { + return backing.entrySet().iterator(); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/node/FileKeyStoreStorageTest.java b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/node/FileKeyStoreStorageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..298fc2218641d6325796208785e5f6719d6f2b1f --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/node/FileKeyStoreStorageTest.java @@ -0,0 +1,115 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.node; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.cert.Certificate; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.ssl.CertificateUtil; + +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class FileKeyStoreStorageTest + extends TestSupport +{ + private static final char[] STORE_PASSWORD = "very-secret".toCharArray(); + + private static final String CERT_ALIAS = "test-cert"; + + private File basedir = util.createTempDir(); + + private File keyStoreFile = new File(basedir, "test.ks"); + + private FileKeyStoreStorage storage; + + private KeyStore newKeyStore() throws Exception { + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null, STORE_PASSWORD); + return keyStore; + } + + private KeyStore newKeyStoreWithData() throws Exception { + KeyStore keyStore = newKeyStore(); + KeyPairGenerator kpgen = KeyPairGenerator.getInstance("RSA"); + kpgen.initialize(512); + KeyPair keyPair = kpgen.generateKeyPair(); + Certificate cert = CertificateUtil.generateCertificate(keyPair.getPublic(), keyPair.getPrivate(), "SHA1WITHRSA", 7, + "testing", "Nexus", "Sonatype", "Fulton", "MD", "USA"); + keyStore.setCertificateEntry(CERT_ALIAS, cert); + return keyStore; + } + + @Before + public void setUp() { + storage = new FileKeyStoreStorage(keyStoreFile); + } + + @Test + public void testExists() throws Exception { + assertThat(storage.exists(), is(false)); + keyStoreFile.createNewFile(); + assertThat(storage.exists(), is(true)); + } + + @Test + public void testModified() throws Exception { + assertThat(storage.modified(), is(false)); + keyStoreFile.createNewFile(); + assertThat(storage.modified(), is(true)); + } + + @Test + public void testLoad() throws Exception { + KeyStore keyStore = newKeyStoreWithData(); + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(keyStoreFile))) { + keyStore.store(bos, STORE_PASSWORD); + } + assertThat(storage.modified(), is(true)); + keyStore = newKeyStore(); + storage.load(keyStore, STORE_PASSWORD); + assertThat(keyStore.containsAlias(CERT_ALIAS), is(true)); + assertThat(storage.modified(), is(false)); + } + + @Test + public void testSave() throws Exception { + KeyStore keyStore = newKeyStoreWithData(); + storage.save(keyStore, STORE_PASSWORD); + assertThat(storage.modified(), is(false)); + keyStore = newKeyStore(); + try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(keyStoreFile))) { + keyStore.load(bis, STORE_PASSWORD); + } + assertThat(keyStore.containsAlias(CERT_ALIAS), is(true)); + } + + @Test + public void testSave_CreateParentDirectories() throws Exception { + keyStoreFile = new File(basedir, "sub/dir/test.ks"); + storage = new FileKeyStoreStorage(keyStoreFile); + storage.save(newKeyStore(), STORE_PASSWORD); + assertThat(keyStoreFile.isFile(), is(true)); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/node/KeyStoreStorageManagerImplTest.java b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/node/KeyStoreStorageManagerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..85740fb306736989b6c0d3931839b8d9f578b1fc --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/node/KeyStoreStorageManagerImplTest.java @@ -0,0 +1,52 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.node; + +import java.io.File; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.ssl.spi.KeyStoreStorage; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.when; + +public class KeyStoreStorageManagerImplTest + extends TestSupport +{ + private File keyStoreDir = util.createTempDir("keystores"); + + @Mock + private ApplicationDirectories appDirs; + + private KeyStoreStorageManagerImpl storageManager; + + @Before + public void setUp() { + when(appDirs.getWorkDirectory("keystores")).thenReturn(keyStoreDir); + storageManager = new KeyStoreStorageManagerImpl(appDirs); + } + + @Test + public void testCreateStorage() { + KeyStoreStorage storage = storageManager.createStorage("test.ks"); + assertThat(storage, is(instanceOf(FileKeyStoreStorage.class))); + assertThat(((FileKeyStoreStorage) storage).getKeyStoreFile(), is(new File(keyStoreDir, "node/test.ks"))); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/node/LocalNodeAccessTest.java b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/node/LocalNodeAccessTest.java new file mode 100644 index 0000000000000000000000000000000000000000..810a905cacac99659562fb79aa2ac3b6bc1d51a4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/node/LocalNodeAccessTest.java @@ -0,0 +1,79 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.node; + +import java.io.File; +import java.security.cert.Certificate; +import java.util.Collections; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.crypto.internal.CryptoHelperImpl; +import org.sonatype.nexus.ssl.KeyStoreManager; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +/** + * Tests for local {@link NodeAccess}. + */ +@SuppressWarnings("HardCodedStringLiteral") +public class LocalNodeAccessTest + extends TestSupport +{ + private KeyStoreManager keyStoreManager; + + private NodeAccess nodeAccess; + + @Before + public void setUp() throws Exception { + File dir = util.createTempDir("keystores"); + KeyStoreManagerConfigurationImpl config = new KeyStoreManagerConfigurationImpl(); + // use lower strength for faster test execution + config.setKeyAlgorithmSize(512); + keyStoreManager = new KeyStoreManagerImpl(new CryptoHelperImpl(), new KeyStoreStorageManagerImpl(dir), config); + keyStoreManager.generateAndStoreKeyPair("a", "b", "c", "d", "e", "f"); + + nodeAccess = new LocalNodeAccess(() -> keyStoreManager); + nodeAccess.start(); + } + + @After + public void tearDown() throws Exception { + if (nodeAccess != null) { + nodeAccess.stop(); + } + } + + @Test + public void idEqualToIdentityCertificate() throws Exception { + Certificate cert = keyStoreManager.getCertificate(); + assertThat(nodeAccess.getId(), equalTo(NodeIdEncoding.nodeIdForCertificate(cert))); + } + + @Test + public void localIsOldestNode() { + assertThat(nodeAccess.isOldestNode(), is(true)); + } + + @Test + public void getMemberAliasesKeyValueEqualToIdentity() { + assertThat(nodeAccess.getMemberAliases(), + equalTo(Collections.singletonMap(nodeAccess.getId(), nodeAccess.getId()))); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/node/NodeIdEncodingTest.java b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/node/NodeIdEncodingTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e629d78a09aea0e6dbf6f0e0e9ec02b5f67cf659 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/node/NodeIdEncodingTest.java @@ -0,0 +1,51 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.node; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +/** + * Tests for {@link NodeIdEncoding}. + */ +public class NodeIdEncodingTest + extends TestSupport +{ + private static final String SHA1 = "05F4743FA756584643FDF9D0577BE4FB079289C6"; + + private static final String NODE_ID = "05F4743F-A7565846-43FDF9D0-577BE4FB-079289C6"; + + private static final String FINGERPRINT = "05:F4:74:3F:A7:56:58:46:43:FD:F9:D0:57:7B:E4:FB:07:92:89:C6"; + + @Test + public void nodeIdForSha1() { + String output = NodeIdEncoding.nodeIdForSha1(SHA1); + assertThat(output, equalTo(NODE_ID)); + } + + @Test + public void sha1ForNodeId() { + String output = NodeIdEncoding.sha1ForNodeId(NODE_ID); + assertThat(output, equalTo(SHA1)); + } + + @Test + public void nodeIdForFingerprint() { + String output = NodeIdEncoding.nodeIdForFingerprint(FINGERPRINT); + assertThat(output, equalTo(NODE_ID)); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/orient/DatabaseManagerImplTest.java b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/orient/DatabaseManagerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8b46127aca4eca3f1f1e6c916e17a592dda1d365 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/orient/DatabaseManagerImplTest.java @@ -0,0 +1,50 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient; + +import java.io.File; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.orient.DatabaseRestorer; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; + +/** + * Tests {@link DatabaseManagerImpl}. + */ +public class DatabaseManagerImplTest + extends TestSupport +{ + @Rule + public TemporaryFolder tmpDir = new TemporaryFolder(); + + private DatabaseManagerImpl databaseManager; + + @Before + public void setUp() { + databaseManager = new DatabaseManagerImpl(tmpDir.getRoot(), mock(DatabaseRestorer.class)); + } + + @Test + public void testConnectionUri() throws Exception { + String dbPath = new File(tmpDir.getRoot(), "dbName").getCanonicalPath().replace('\\', '/'); + assertThat(databaseManager.connectionUri("dbName"), is("plocal:" + dbPath)); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/orient/DeploymentAccessImplTest.groovy b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/orient/DeploymentAccessImplTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..5b3ebe1f141685bb4812731f15d1e28ac8f8c425 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/orient/DeploymentAccessImplTest.groovy @@ -0,0 +1,79 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +package org.sonatype.nexus.internal.orient + +import org.sonatype.nexus.common.node.NodeAccess +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule + +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.runners.MockitoJUnitRunner + +import static org.mockito.Mockito.when + +/** + * Unit tests for {@link DeploymentAccessImpl}. + */ +@RunWith(MockitoJUnitRunner.class) +class DeploymentAccessImplTest +{ + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory('test') + + @Mock + NodeAccess nodeAccess + + private DeploymentAccessImpl service + + @Before + void setup() { + when(nodeAccess.getId()).thenReturn('test-id') + service = new DeploymentAccessImpl(database.instanceProvider, nodeAccess) + service.start() + } + + @Test + void 'confirm id is present and alias is null in initial state'() { + assert service.getId() == 'test-id' + assert service.getAlias() == null + } + + @Test + void 'start is idempotent'() { + def before = service.getId() + + service.start() + + assert before == service.getId() + } + + @Test + void 'setAlias is successful'() { + assert service.getAlias() == null + + service.setAlias('some-new-alias') + + assert service.getId() != null + assert service.getAlias() == 'some-new-alias' + + // accept null values + service.setAlias(null) + + assert service.getAlias() == null + + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/orient/PasswordSanitizedJsonSourceTest.groovy b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/orient/PasswordSanitizedJsonSourceTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..c85dc977682ea52784239d985e541e123a7cf46e --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/orient/PasswordSanitizedJsonSourceTest.groovy @@ -0,0 +1,82 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.orient + +import javax.inject.Provider + +import org.sonatype.nexus.supportzip.SupportBundle.ContentSource.Type +import org.sonatype.nexus.orient.DatabaseExternalizer +import org.sonatype.nexus.orient.DatabaseInstance + +import groovy.json.JsonSlurper +import org.junit.rules.TemporaryFolder +import org.junit.Rule +import spock.lang.Specification + +/** + * Tests for {@link PasswordSanitizedJsonSource}. + */ +class PasswordSanitizedJsonSourceTest + extends Specification +{ + @Rule + TemporaryFolder temporaryFolder = new TemporaryFolder() + + def 'Excludes sensitive classes from the export.'() { + given: 'a PasswordSanitizedJsonSource' + DatabaseInstance instance = Mock(DatabaseInstance) + DatabaseExternalizer externalizer = Mock(DatabaseExternalizer) + Provider instanceProvider = Mock(Provider) + instanceProvider.get() >> instance + instance.externalizer() >> externalizer + PasswordSanitizedJsonSource passwordSanitizedJsonSource = new PasswordSanitizedJsonSource(Type.CONFIG, + 'path', instanceProvider) + File exportJsonFile = temporaryFolder.newFile() + + when: 'a database is exported' + passwordSanitizedJsonSource.generate(exportJsonFile) + + then: 'the sensitive class names are passed to the externalizer to be excluded' + 1 * externalizer.export(_, _) >> { output, excludedClasses -> + assert excludedClasses == ['api_key', 'usertoken_record'] as Set + output.write('{"records": []}'.bytes) + } + } + + def 'Excludes sensitive fields from the export.'() { + given: 'a PasswordSanitizedJsonSource' + DatabaseInstance instance = Mock(DatabaseInstance) + DatabaseExternalizer externalizer = Mock(DatabaseExternalizer) + externalizer.export(_, _) >> { + output, excludedClassNames -> + output.write(('{"secret": "secret-key", "applicationPassword": "password", ' + + '"systemPassword": "password", "password": "password"}').bytes) + } + Provider instanceProvider = Mock(Provider) + instanceProvider.get() >> instance + instance.externalizer() >> externalizer + PasswordSanitizedJsonSource passwordSanitizedJsonSource = new PasswordSanitizedJsonSource(Type.CONFIG, + 'path', instanceProvider) + File exportJsonFile = temporaryFolder.newFile() + + when: 'a database is exported' + passwordSanitizedJsonSource.generate(exportJsonFile) + + then: 'the sensitive files are replaced in the exported JSON' + def result = new JsonSlurper().parse(exportJsonFile) + assert result.secret == PasswordSanitizedJsonSource.REPLACEMENT + assert result.applicationPassword == PasswordSanitizedJsonSource.REPLACEMENT + assert result.systemPassword == PasswordSanitizedJsonSource.REPLACEMENT + assert result.password == PasswordSanitizedJsonSource.REPLACEMENT + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/security/anonymous/AnonymousManagerImplTest.java b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/security/anonymous/AnonymousManagerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c61458abaf8e1b2132a1b45777db967843c67ba3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/security/anonymous/AnonymousManagerImplTest.java @@ -0,0 +1,116 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.anonymous; + +import javax.inject.Provider; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.security.anonymous.AnonymousConfiguration; +import org.sonatype.nexus.security.anonymous.AnonymousConfigurationChangedEvent; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class AnonymousManagerImplTest + extends TestSupport +{ + @Mock + private EventManager eventManager; + + @Mock + private AnonymousConfigurationStore store; + + @Mock + private AnonymousConfiguration storeConfig; + + @Mock + private AnonymousConfiguration storeConfigCopy; + + @Mock + private Provider defaults; + + @Mock + private AnonymousConfiguration defaultConfig; + + @Mock + private AnonymousConfiguration defaultConfigCopy; + + @Mock + private AnonymousConfigurationEvent configurationEvent; + + private AnonymousManagerImpl manager; + + @Before + public void setUp() { + when(defaults.get()).thenReturn(defaultConfig); + when(storeConfig.copy()).thenReturn(storeConfigCopy); + when(defaultConfig.copy()).thenReturn(defaultConfigCopy); + manager = new AnonymousManagerImpl(eventManager, store, defaults); + } + + @Test + public void testGetConfiguration_FromDefaults() { + assertThat(manager.getConfiguration(), is(defaultConfigCopy)); + } + + @Test + public void testGetConfiguration_FromStore() { + when(store.load()).thenReturn(storeConfig); + assertThat(manager.getConfiguration(), is(storeConfigCopy)); + verifyZeroInteractions(defaults); + } + + @Test + public void testSetConfiguration() { + manager.setConfiguration(storeConfig); + verify(store).save(storeConfigCopy); + ArgumentCaptor eventCaptor = ArgumentCaptor + .forClass(AnonymousConfigurationChangedEvent.class); + verify(eventManager).post(eventCaptor.capture()); + assertThat(eventCaptor.getValue().getConfiguration(), is(storeConfigCopy)); + } + + @Test + public void testHandleConfigurationEvent_FromLocalNode() { + when(configurationEvent.isLocal()).thenReturn(true); + when(store.load()).thenReturn(defaultConfig, storeConfig); + assertThat(manager.getConfiguration(), is(defaultConfigCopy)); + manager.onStoreChanged(configurationEvent); + assertThat(manager.getConfiguration(), is(defaultConfigCopy)); + verify(store).load(); + verify(eventManager, never()).post(any(AnonymousConfigurationChangedEvent.class)); + } + + @Test + public void testHandleConfigurationEvent_FromRemoteNode() { + when(configurationEvent.isLocal()).thenReturn(false); + when(store.load()).thenReturn(defaultConfig, storeConfig); + assertThat(manager.getConfiguration(), is(defaultConfigCopy)); + manager.onStoreChanged(configurationEvent); + assertThat(manager.getConfiguration(), is(storeConfigCopy)); + verify(store, times(2)).load(); + verify(eventManager).post(any(AnonymousConfigurationChangedEvent.class)); + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/security/model/ConcurrentCleanupTest.groovy b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/security/model/ConcurrentCleanupTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..6818c11948cc150b8f65e0cae845309c0c8a1428 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/security/model/ConcurrentCleanupTest.groovy @@ -0,0 +1,220 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.model + +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule +import org.sonatype.nexus.security.config.CPrivilege +import org.sonatype.nexus.security.config.CRole +import org.sonatype.nexus.security.config.CUserRoleMapping +import org.sonatype.nexus.security.config.SecurityConfiguration +import org.sonatype.nexus.security.config.SecurityConfigurationCleaner +import org.sonatype.nexus.security.config.StaticSecurityConfigurationSource +import org.sonatype.nexus.security.internal.SecurityConfigurationCleanerImpl + +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +import static org.hamcrest.MatcherAssert.assertThat +import static org.hamcrest.Matchers.hasSize + +/** + * Parallel security cleanup UTs. + */ +class ConcurrentCleanupTest +extends TestSupport +{ + + private static final Logger log = LoggerFactory.getLogger(ConcurrentCleanupTest) + + private static final int NUMBER_OF_THREADS = 9 + + private static final int NUMBER_OF_MAPPING_UPDATE_THREADS = 3 + + private static final int NUMBER_OF_ROLE_UPDATE_THREADS = 3 + + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory('security') + + private OrientSecurityConfigurationSource source + + private SecurityConfiguration configuration + + private SecurityConfigurationCleaner cleaner + + @Before + public void prepare() throws Exception { + source = new OrientSecurityConfigurationSource( + database.instanceProvider, + new StaticSecurityConfigurationSource(), + new CUserEntityAdapter(), + new CRoleEntityAdapter(), + new CPrivilegeEntityAdapter(), + new CUserRoleMappingEntityAdapter() + ) + source.start() + source.loadConfiguration() + configuration = source.configuration + cleaner = new SecurityConfigurationCleanerImpl() + } + + @After + public void shutdown() throws Exception { + if (source) { + source.stop() + } + } + + /** + * Verify that cleaning up privileges and roles in parallel does not write each other changes. + */ + @Test + public void cleanup() throws Exception { + loadTestData() + + final CountDownLatch startSignal = new CountDownLatch(1) + final CountDownLatch doneSignal = new CountDownLatch( + NUMBER_OF_THREADS * 2 + NUMBER_OF_MAPPING_UPDATE_THREADS + NUMBER_OF_ROLE_UPDATE_THREADS + ) + + (1..NUMBER_OF_THREADS).each { i -> + final String id = 'test-' + i + + new Thread(new Worker(startSignal: startSignal, doneSignal: doneSignal, toDo: { + try { + cleaner.privilegeRemoved(configuration, id) + log.info('cleaned privilege {}', id) + } + catch (Exception e) { + log.error('cleaning privilege {} failed: {}', id, e.getMessage()) + } + })).start() + + new Thread(new Worker(startSignal: startSignal, doneSignal: doneSignal, toDo: { + try { + cleaner.roleRemoved(configuration, id) + log.info('cleaned role {}', id) + } + catch (Exception e) { + log.error('cleaning role {} failed: {}', id, e.getMessage()) + } + })).start() + } + + // have a number of threads updating the mappings + (1..NUMBER_OF_MAPPING_UPDATE_THREADS).each { i -> + new Thread(new Worker(startSignal: startSignal, doneSignal: doneSignal, toDo: { + CUserRoleMapping mapping = configuration.getUserRoleMapping('test', 'default') + try { + configuration.updateUserRoleMapping(mapping) + log.info('{} mapping update', i) + } + catch (Exception e) { + log.error('{} mapping update failed: {}', i, e.getMessage()) + } + })).start() + } + + // have a number of threads updating the roles + (1..NUMBER_OF_ROLE_UPDATE_THREADS).each { i -> + new Thread(new Worker(startSignal: startSignal, doneSignal: doneSignal, toDo: { + CRole role = configuration.getRole('test') + try { + configuration.updateRole(role) + log.info('{} role update', i) + } + catch (Exception e) { + log.error('{} role update failed: {}', i, e.getMessage()) + } + })).start() + } + + startSignal.countDown() + doneSignal.await(5, TimeUnit.MINUTES) + + assertThat(configuration.getUserRoleMapping('test', 'default').getRoles(), hasSize(0)) + assertThat(configuration.getRole('test').getRoles(), hasSize(0)) + assertThat(configuration.getRole('test').getPrivileges(), hasSize(0)) + } + + private static class Worker + implements Runnable + { + CountDownLatch startSignal + CountDownLatch doneSignal + Closure toDo + + @Override + public void run() { + try { + startSignal.await() + toDo.call() + } + catch (Exception ignore) { + // do nothing + } + finally { + doneSignal.countDown() + } + } + } + + private void loadTestData() throws Exception { + configuration.addRole(new CRole( + id: 'test', + name: 'test' + )) + configuration.addUserRoleMapping(new CUserRoleMapping( + userId: 'test', + source: 'default' + )) + + // create privileges and assign them to test role + (1..NUMBER_OF_THREADS).each { i -> + configuration.addPrivilege(new CPrivilege( + id: "test-${i}", + type: 'target', + name: "test-${i}" + )) + + CRole role = configuration.getRole('test') + role.addPrivilege("test-${i}") + configuration.updateRole(role) + } + + // create roles and assign then to test user and test role + (1..NUMBER_OF_THREADS).each { i -> + configuration.addRole(new CRole( + id: "test-${i}", + name: "test-${i}" + )) + + CRole role = configuration.getRole('test') + role.addRole("test-${i}") + configuration.updateRole(role) + + CUserRoleMapping mapping = configuration.getUserRoleMapping('test', 'default') + mapping.addRole("test-${i}") + configuration.updateUserRoleMapping(mapping) + } + } + +} + diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/security/model/OrientSecurityConfigurationSourceTest.groovy b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/security/model/OrientSecurityConfigurationSourceTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..62d17d6523f4e043a451bf5e420c56cf2c9bae69 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/security/model/OrientSecurityConfigurationSourceTest.groovy @@ -0,0 +1,163 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.security.model + +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule +import org.sonatype.nexus.security.config.CPrivilege +import org.sonatype.nexus.security.config.CRole +import org.sonatype.nexus.security.config.CUser +import org.sonatype.nexus.security.config.CUserRoleMapping +import org.sonatype.nexus.security.config.StaticSecurityConfigurationSource +import org.sonatype.nexus.security.privilege.NoSuchPrivilegeException +import org.sonatype.nexus.security.role.NoSuchRoleException +import org.sonatype.nexus.security.user.NoSuchRoleMappingException +import org.sonatype.nexus.security.user.UserNotFoundException + +import org.junit.Rule +import spock.lang.Specification + +/** + * Tests for {@link OrientSecurityConfigurationSource}. + */ +class OrientSecurityConfigurationSourceTest + extends Specification +{ + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory('security') + + OrientSecurityConfigurationSource source + + def setup() { + source = new OrientSecurityConfigurationSource( + database.instanceProvider, + new StaticSecurityConfigurationSource(), + new CUserEntityAdapter(), + new CRoleEntityAdapter(), + new CPrivilegeEntityAdapter(), + new CUserRoleMappingEntityAdapter() + ) + source.start() + source.loadConfiguration() + } + + def 'updateUser should persist user and prevent concurrent modification'() { + given: + def admin = source.configuration.getUser('admin') + def newUser = new CUser(id: 'new') + + when: 'updateUser is called' + admin.firstName = 'foo' + source.configuration.updateUser(admin, [] as Set) + + then: 'user is persisted' + source.configuration.getUser('admin').firstName == 'foo' + + when: 'updateUser is called again on old version' + admin.firstName = 'bar' + source.configuration.updateUser(admin, [] as Set) + + then: 'exception is thrown' + thrown(ConcurrentModificationException) + source.configuration.getUser('admin').firstName == 'foo' + + when: 'updateUser is called on user that doesnt hasnt been saved' + source.configuration.updateUser(newUser, [] as Set) + + then: 'exception is thrown' + thrown(UserNotFoundException) + } + + def 'updatePrivilege should persist privilege and prevent concurrent modification'() { + given: + source.configuration.addPrivilege(new CPrivilege(id: 'test', name: 'test', type: 'test')) + def privilege = source.configuration.getPrivilege('test') + def newPrivilege = new CPrivilege(id: 'new', name: 'new', type: 'test') + + when: 'updatePrivilege is called' + privilege.name = 'foo' + source.configuration.updatePrivilege(privilege) + + then: 'privilege is persisted' + source.configuration.getPrivilege('test').name == 'foo' + + when: 'updatePrivilege is called again on old version' + privilege.name = 'bar' + source.configuration.updatePrivilege(privilege) + + then: 'exception is thrown' + thrown(ConcurrentModificationException) + source.configuration.getPrivilege('test').name == 'foo' + + when: 'updatePrivilege is called on privilege that hasnt been saved' + source.configuration.updatePrivilege(newPrivilege) + + then: 'exception is thrown' + thrown(NoSuchPrivilegeException) + } + + def 'updateRole should persist role and prevent concurrent modification'() { + given: + source.configuration.addRole(new CRole(id: 'test', name: 'test')) + def role = source.configuration.getRole('test') + def newRole = new CRole(id: 'new', name: 'new') + + when: 'updateRole is called' + role.name = 'foo' + source.configuration.updateRole(role) + + then: 'role is persisted' + source.configuration.getRole('test').name == 'foo' + + when: 'updateRole is called again on old version' + role.name = 'bar' + source.configuration.updateRole(role) + + then: 'exception is thrown' + thrown(ConcurrentModificationException) + source.configuration.getRole('test').name == 'foo' + + when: 'updateRole is called on role that hasnt been saved' + source.configuration.updateRole(newRole) + + then: + thrown(NoSuchRoleException) + } + + def 'updateUserRoleMapping should persist user role mapping and prevent concurrent modification'() { + given: + def adminMapping = source.configuration.getUserRoleMapping('admin', 'default') + assert adminMapping.roles == ['nx-admin'] as Set + def newUserRoleMapping = new CUserRoleMapping(userId: 'badid', source: 'badsource', roles: [] as Set) + + when: 'updateUserRoleMapping is called' + adminMapping.roles = [] as Set + source.configuration.updateUserRoleMapping(adminMapping) + + then: 'user role mapping is persisted' + source.configuration.getUserRoleMapping('admin', 'default').roles == [] as Set + + when: 'updateUserRoleMapping is called again on old version' + adminMapping.roles = ['nx-admin'] as Set + source.configuration.updateUserRoleMapping(adminMapping) + + then: 'exception is thrown' + thrown(ConcurrentModificationException) + source.configuration.getUserRoleMapping('admin', 'default').roles == [] as Set + + when: 'updateUserRoleMapping is called on user role mapping that hasnt been saved' + source.configuration.updateUserRoleMapping(newUserRoleMapping) + + then: 'exception is thrown' + thrown(NoSuchRoleMappingException) + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/web/ErrorPageServletTest.java b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/web/ErrorPageServletTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3d60da6e6328a5369fe3afcbde0240604e8299db --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/web/ErrorPageServletTest.java @@ -0,0 +1,125 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.web; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.app.ApplicationVersionSupport; +import org.sonatype.nexus.common.app.BaseUrlHolder; +import org.sonatype.nexus.common.template.TemplateHelper; +import org.sonatype.nexus.internal.template.TemplateHelperImpl; + +import org.apache.http.StatusLine; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.apache.velocity.app.VelocityEngine; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ErrorPageErrorHandler; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static java.util.Arrays.asList; +import static org.eclipse.jetty.servlet.ErrorPageErrorHandler.GLOBAL_ERROR_PAGE; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.stringContainsInOrder; + +/** + * Tests for {@link ErrorPageServlet} + * + */ +public class ErrorPageServletTest + extends TestSupport +{ + Server server; + + int port; + + @Before + public void setUp() throws Exception { + TemplateHelper templateHelper = new TemplateHelperImpl(new ApplicationVersionSupport() + { + @Override + public String getEdition() { + return "Test"; + } + }, new VelocityEngine()); + + ServletContextHandler context = new ServletContextHandler(); + context.addServlet(new ServletHolder(new ErrorPageServlet(templateHelper)), "/error.html"); + context.addServlet(new ServletHolder(new BadServlet()), "/bad/*"); + + ErrorPageErrorHandler errorHandler = new ErrorPageErrorHandler(); + errorHandler.addErrorPage(GLOBAL_ERROR_PAGE, "/error.html"); + context.setErrorHandler(errorHandler); + + BaseUrlHolder.set("http://127.0.0.1"); + + server = new Server(0); + server.setHandler(context); + server.start(); + + port = ((ServerConnector) server.getConnectors()[0]).getLocalPort(); + } + + @After + public void tearDown() throws Exception { + if (server != null) { + server.stop(); + server = null; + } + } + + private static class BadServlet + extends HttpServlet + { + @Override + protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws IOException { + String path = request.getPathInfo().substring(1); + String[] codeAndMsg = path.split("/", 2); + int code = Integer.valueOf(codeAndMsg[0]); + String msg = codeAndMsg[1]; + response.sendError(code, msg); + } + } + + @Test + public void customStatusMessageIsMaintained() throws Exception { + String request = "http://127.0.0.1:" + port + "/bad/403/You%20can%27t%20see%20this"; + + try (CloseableHttpClient client = HttpClientBuilder.create().build()) { + try (CloseableHttpResponse response = client.execute(new HttpGet(request))) { + StatusLine statusLine = response.getStatusLine(); + + assertThat(statusLine.getStatusCode(), is(403)); + assertThat(statusLine.getReasonPhrase(), is("You can't see this")); + + String body = EntityUtils.toString(response.getEntity()); + + assertThat(body, stringContainsInOrder(asList("403", "Forbidden", "You can't see this"))); + } + } + } +} diff --git a/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/web/HeaderPatternFilterTest.java b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/web/HeaderPatternFilterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8ca112e86fd479d59f76f12f779fd5300a61acd8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-base/src/test/java/org/sonatype/nexus/internal/web/HeaderPatternFilterTest.java @@ -0,0 +1,201 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.web; + +import java.util.Collections; + +import javax.servlet.FilterConfig; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link HeaderPatternFilter} + * + */ +public class HeaderPatternFilterTest + extends TestSupport +{ + + @Mock + HttpServletRequest request; + + @Mock + HttpServletResponse response; + + @Mock + FilterConfig filterConfig; + + @Mock + javax.servlet.FilterChain filterChain; + + HeaderPatternFilter filter; + + @Before + public void setUp() throws Exception { + filter = new HeaderPatternFilter(); + when(filterConfig.getInitParameter(anyString())).thenReturn(null); + filter.init(filterConfig); + } + + @Test + public void testFilter_badValue() throws Exception { + testHeaderHostValue(">"); + verify(response).setStatus(HttpServletResponse.SC_BAD_REQUEST); + } + + @Test + public void testFilter_otherBadValue() throws Exception { + testHeaderHostValue("not a legit hostname"); + verify(response).setStatus(HttpServletResponse.SC_BAD_REQUEST); + } + + @Test + public void testFilter_goodValue() throws Exception { + testHeaderHostValue("example.com"); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_localhost() throws Exception { + testHeaderHostValue("localhost"); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_nonroutableIp() throws Exception { + testHeaderHostValue("10.0.0.1"); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_localhostWithPort() throws Exception { + testHeaderHostValue("localhost:8080"); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_goodValueWithPort() throws Exception { + testHeaderHostValue("example.com:8080"); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_nonroutableWithPort() throws Exception { + testHeaderHostValue("10.0.0.1:8080"); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_ipv6() throws Exception { + testHeaderHostValue("[1762:0:0:0:0:B03:1:AF18]"); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_ipv6WithPort() throws Exception { + testHeaderHostValue("[1762:0:0:0:0:B03:1:AF18]:8080"); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_JustProperProto() throws Exception { + testHeaderForwardedValue("proto=http"); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_ProperProto() throws Exception { + testHeaderForwardedValue("for=192.0.2.60;proto=http;by=203.0.113.43"); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_AcceptsQuotedProto() throws Exception { + testHeaderForwardedValue("for=192.0.2.60;proto=\"http\";by=203.0.113.43"); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_NoProtoIP4() throws Exception { + testHeaderForwardedValue("for=192.0.2.43, for=198.51.100.17"); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_NoProtoIP6() throws Exception { + testHeaderForwardedValue("For=\"[2001:db8:cafe::17]:4711\""); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_ProtoAtStart() throws Exception { + testHeaderForwardedValue("proto=http; by=203.0.113.43;for=192.0.2.60;"); + verify(filterChain).doFilter(request, response); + } + + @Test + public void testFilter_ProtoAtStartWithSpace() throws Exception { + testHeaderForwardedValue(" proto=https; by=203.0.113.43;for=192.0.2.60;"); + verify(filterChain).doFilter(request, response); + } + + + @Test + public void testFilter_JustInvalidProto() throws Exception { + testHeaderForwardedValue("proto= + + + + + + + + + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/art/base-trigger.pxm b/thirdparty-bundles/components/nexus-rapture/src/main/art/base-trigger.pxm new file mode 100644 index 0000000000000000000000000000000000000000..f340ca52a0c9569a190dc8ae60cd747a8d0b54df Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/art/base-trigger.pxm differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/art/fa-times-circle-trigger.psd b/thirdparty-bundles/components/nexus-rapture/src/main/art/fa-times-circle-trigger.psd new file mode 100644 index 0000000000000000000000000000000000000000..7df043904ae485ceb611b7a743cfc90d248be9f2 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/art/fa-times-circle-trigger.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/art/loading-1024x768.pxm b/thirdparty-bundles/components/nexus-rapture/src/main/art/loading-1024x768.pxm new file mode 100644 index 0000000000000000000000000000000000000000..f63130d490cb6dacd21d8597e3ea1917f00771b1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/art/loading-1024x768.pxm differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/art/milestone.png b/thirdparty-bundles/components/nexus-rapture/src/main/art/milestone.png new file mode 100644 index 0000000000000000000000000000000000000000..7d58cffdbf66ba997dcfc7629616b263d713fc4f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/art/milestone.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/art/nexus-milestone.pxm b/thirdparty-bundles/components/nexus-rapture/src/main/art/nexus-milestone.pxm new file mode 100644 index 0000000000000000000000000000000000000000..67251a5cd0267930b25cd29ac29b9a04b687a5f0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/art/nexus-milestone.pxm differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/build-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/build-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..e9d332f0e6bcb14d6ac7d9844a6f5841ffdf029c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/build-impl.xml @@ -0,0 +1,503 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + compass watch ${app.sass.dir} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Using Sencha Cmd from ${cmd.dir} for ${ant.file} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/build.properties b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/build.properties new file mode 100644 index 0000000000000000000000000000000000000000..5a2916e09c14d69f15921d213345e66d146ac1e7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/build.properties @@ -0,0 +1,10 @@ +app.name=baseapp + +enable.ext42.themes=true +build.output.markuponly=true + +# cmd needs this for some reason, not sure why, changing to avoid collisions with proper index.html +app.indexHtmlPath=ignore.html + +build.dir=${workspace.dir}/../../../src/main/resources/static/rapture +build.temp.dir=${workspace.build.dir}/cmd-temp/${build.environment} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/codegen.json b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/codegen.json new file mode 100644 index 0000000000000000000000000000000000000000..596c4d28de19a77387ce3e51b49ad061368bab4d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/codegen.json @@ -0,0 +1,340 @@ +{ + "sources": { + "build.properties.merge": { + "d4613dc19be3ddb60e7ff5716e28c8b15d954f3a": "eJytUstOw0AMvPMVI/WGoB+AxAFx4sBD0B9ws05idbOOdp2E/j1OH6pEEKfuzR57POP1Co/XfDcrbFopqCUy+qyjBC6gBB05Zw/QqyRDrRmBaxqiYaQstI1e5xlJHCAJ1nJhZ4s6cXYm0Sy2P/CWh5uVI8A9+NvWPqXnbMLllLxdn5jLEvsTmUXrRSHhBO6xpeJyNGE7SAxrTqNkTR27A0nFmAIoBFzIYOpsmhhaHz2glab9x4L3hqEy0bRUa1xMUrMEEpmMvMz3VO2o4d/uXl4/3j83T28bL/niVLWE5y5gkhjRcW4Yex0yPJ8a9zCJtRAr0CkhDNklYOibTP6X6+OyaFRx32q+CqEzS6WpjlJ55xHvaDf3RnLwDmVi7uf4POewKzufy8x83Vv8AQy16fs\u003d" + }, + "native.properties.merge": { + "2c8c063f9588eecff09624782d7369a8a000d61c": "eJytkEsOwjAMRPecYkT3vQF3YMEFguJSi2BXjgni9rgfFQ5AdrEm743T4fTPc+hwGbli4EKYTBtnqkgCbWQWF0zK4hjUkGlIz+JoyThdS+RiwkIZLAiQJOdG/RarfeAmMmeqfUioLoJtgmQElfIGPyY1D8hrJMH1ySWz3AI3K30kHFfuESSNTeVB4v2hi8T5i/tp4vs+nu6zk9XY38tGK/+nWQx87hK0pU3RlAMzu3fv8qaG8s9f/wFV3ZB9" + }, + "package.properties.merge": { + "c6c1fb2fb1dda28480ad0f6e5caffc9b97d196ae": "eJytkUFygzAMRfc5hSZsOxygM11223aRCwgsQBNH8tiCTG5fGWjDAcLOsnjvS27g45XfqYHLxAUGjgQp68KBCqCALpSzHyApi8GgGQINOEeDBTNjF73PKywUgAUclLC/4kjt3lda5yXKxlRat1BZDXsFMBOoxAfwLWk2p9wnEuhmjoFldF512kRw3sFnIFk4q9xIrD013vLz5B2y2P9EhtcqZc1sj3WmTXCI5gWrYZy2xomKwTFV/hSvP5XN+fV9+Xzftjb7SDoc+jClWJmmIGi80N9SwnbVe1HlDUTdWfNvt1togpGEMkYoLP1K7tVfIHKXfeVU/S9+/V9rlbyW" + }, + "production.properties.merge": { + "cbdf9712e9b5d37cecb4d0530ba1fccd0ed004a3": "eJytkUEOwjAMBO+8YkXv/QF/4MAHAnGpRbArxy3i97gFQbk3t6ycmbXS4LDl2TU49VzRcSEMphNnqkgCncgsLhiUxdGpIVOXxuKYknE6l5iLhIUyWBCgeJ7Hi7NK+xmtbWQDmTPVNkRUF8knQTKCSnmC74OaB+jRk+A8csks10DOWu8J+x97D5KJTeVO4u2uianjD7lq5N+9PN1mL6uxP5fN3o5Vuwh87hO0pVHRlAMz+//cy7sa2o2/4QV6LJW9" + }, + "sencha.cfg.tpl.merge": { + "8b88dc946a59c6ad6edb77744966e548250b33a3": "eJytVE1vEzEQvedXjASHVmo3vSEhwgFuHKASHLk43tmNU6+9sr35oMp/542dbDZN4QKRktge+703n2/ox4rJqY7JN5SwVn1vjVbJeDfDuhLb4ll+D7PZm6vrTcBm68MTDZFrWu5fgtAN7xLNKflBr24z5Phm8Twuv04JepVWN/EWby6Q1mqjog6mTxT9EDRHutG+6xRF7lVQietCoK2KUUAWb38ODw/vPslhbcJhjsXdK2fVOgr3tyH1QyLrj4SNDxcCloOxNamQTKN0ipkrnwnQiUu8Abnms+kwn3BehDJ4n3I88wtxGGGk7YoddbhsXEtRx0iBTx7jyjpS9pAjUIzLEY/Yz2PQpFxdNhsVCNyskw+Gi1gxVCPdJK2PCJZAywUKg2WquTHOlDAYC2LtA1T03tWiCne/IB3fSzqOcqozCaS8moCTzitSyDVq+e/EwPkzMYxXxM3gdGaTyHVmZ468E0xOf3EGxrmytpI8TcFR9kEhlmiJ4gfOjNN2qBn/2EjaWnacC7do0d41pq3C8g5AH8Aj9fGx8Iw22hpracknNFTkkHyHEtXQsSfTUI+AsUvZpdrnOnFSaIzLYMbb2LM2jeG6gm10VNRmT19rdZTMk2pZRCaFFEkycIwv7uQqlX45tn8OXjYtEIj7vLrP2TK6gJsImb7nkPaklRNRna+zJtGoV8q1GR1+SleKL740aC8yJKgEpDx3jl2AadDLcZ4Krq4wfNr3MNa8q1Tsd3fH9Tr2VFUVlXEhcGXOFesqdVY0nrlHwvN92V0VxGQ/gh4K1NTdU/AjKXoxwMiamCTikoY4iTVALoaA9v2ejlVU5tKp2fdZ5OlylYGulI5YULf4rx8I/TxEFCQ9Fn/Fz3t6tDLgdLaYX3meRuTc+m0JjjUuzz+18QZ9yKFlIEnRY/ymiKJHWznecqANhyjPZ78BlO9Yig\u003d\u003d" + }, + "testing.properties.merge": { + "360e715956c81757e53736789fe20be045acb544": "eJytkMENwjAMRe+d4oveuwE7cGCBoLjUItiVY4q6PW6pKAM0t1g/732nxfnI07S4DlzRcyGMphNnqkgCncgsLhiVxdGrIVOfXsUxJeN0K5GLCQtlsCBATtVZ7t2Wq13wRjJnql1YqK6GbYJkBJUyg5+jmgflPZDg9uKSgxK8xekD4bSBTyCZ2FSeJN41bUQuO++vi/828vRYpKzGPq87fQV/1WLgS5mgrXWKphyYRb6L10c1nAf//gdqIpHi" + }, + "app.js.tpl.merge": { + "adb7999f937a224ece5dc57252db7048652e6616": "eJxNj81uwjAQhO9+irnxIxTu9FQhrlxaVeK4iYfEUmJHzroBId69xkiFPXhH3v1m7O3aINd35yacXU/k3tIzitJCvEUabdH1FV/0TSfYD7bCKSQ04kHrFPqPy1T8PGkzcw4R15AiZBx714i64Deo04PgxAJPmF3fo5NfQgNqYmBsS2CxeoVi7ujze9oo1vm2MuutMYeLVm/uy9szXwbusLjlyTHL+2Jjyj0vSm/fJ9XnC85bj6VySNKwj8xf/3GcxxB1B42J5r76MH/syWTb" + }, + "app.json.tpl.merge": { + "5c35fa135e5c429faf9348c096b567a3079ad83b": "eJyr5lIAAqW8xNxUJSsFpWoQo1ZJhwsiXJRaWJpZlFoMlIoGi8TCZDJTwMpL8zILS1M9U2qVuGoByK4TZA\u003d\u003d" + }, + "index.html.tpl.merge": { + "f036c68a34ae0bc941832df08c406a00545dd9f6": "eJx9kT0PwiAQhnd/BbIjqwPt4kccNHaogyPSM1SpNHDRGuN/F4tRa1SWC/fe8x53iP54OcrX2YTM8sU87QmNlbkHkEXaI+GIClASpaXzgAld5VM2pA8JSzSQXg6ygqvg8RaVPmNENEzZqi5DkjAWhXdxYy16dLLuym2JKQ974sAk1OPZgNcASIl2sE3okxso7+kH6JUrayTeqYReti687GTdPpOorxwaZAUcB7tACR4r/+CvPt+BOAf/OUjHTNbfbJ4WnUUJHtcvNrY4B6IN92z7NzfHY30y" + }, + "config.rb.tpl.merge": { + "33f446bd02c3fd24eb27891582eff6a2e789796b": "eJxLLi2KT8ksUrBVcMvMSdUDMvMSc1M14uPdPH1c4+M1ufJLSwpKS+KLSypzUoGqrPJSi0tSU7gALskTcA\u003d\u003d" + }, + "theme.html.tpl.merge": { + "79e5bc8b4cebd921ec602e9a67c0493c70d2d5ce": "eJydVFFv0zAQfs+vuPkJEE66wQPqkr6UIUBITLQ88Ogm18arEwffNW017b/PTsLWjSKmWlGi+LvvvvPns9Ozj9+n81/XV1ByZSZReiZlBABT2+ydXpUMr/LXcDE6v5D+9S6GGdZ5qeBLnceRlJ4w8EpUxSQwIWXNBidXO4avM5iXWCF8Vq5GojTpsT6uQlZelhuJvze6zcTU1ow1y/m+QQF5/5cJxh0nQeYSvLQj5Ozn/JP8ICZRn2ioGb5ZVYAyBlxI6LAAo+s1gaoLoNzphqmLC3V3xAD7YJMJ4r1BKhFZAHv5QTUnElA6XGYCd6pqDMbdVDIk6LMeMm5Uq/pZAeTyTMRx4p/bpVMVbq1bXysu7xIfKgts4xsSkzTpCS/PubCWiZ1qnvGP2MElwszoHJ031OFxH14g6bAu0P1fL9hfqVovkXhwvrSO880JmoN1jcrXaoX0A82jeRz6Si4UYUKKKBm2J/mjfaKzp0g+LPBEzXxDbKuj1nZt2WcKI97qYoUsw9FQuvZ7evuAhVEpt9L1GM5Hze7yCeKJXI7h/egvpLGkWVvP8gdBsW7xKW5bdEtjt2NoNemFOYDvosfKdpK6LpPsa0B++08E3jwr2nqzNe991YeZu+Unw/rT5OB6Wdhi743qPgHp7p972o53iQ\u003d\u003d" + }, + "{controllerFileName}.js.tpl.merge": { + "b092f4aad11d2f7eaab5ce3e2dcc2fc3134f3188": "eJxzrSjRS0lNy8xL1VCvTs7PKynKz8lJLfJLzE0tLkhMTq3VQxOtVddRqOZSAILUipLUvBQrBXVXoBmJBQV6znCF6ly1mtZcACwSIF0\u003d" + } + }, + "targets": { + ".sencha/app/build.properties": { + "source": "build.properties.merge", + "version": "d4613dc19be3ddb60e7ff5716e28c8b15d954f3a", + "parameters": { + "viewNamespace": "baseapp.view", + "frameworkName": "ext", + "appName": "baseapp", + "controllerNamespace": "baseapp.controller", + "frameworkPath": "ext", + "packagesRelPath": "ext/packages", + "appModels": "", + "themeName": "default", + "library": "all", + "viewName": "Main", + "viewFileName": "Main", + "senchadir": ".sencha", + "controllerName": "Main", + "appStores": "", + "name": "baseapp", + "appViews": "", + "appControllers": "", + "controllerFileName": "Main", + "modelNamespace": "baseapp.model", + "uniqueId": "6d86402c-70ea-4a01-94f7-8349a2418015" + } + }, + ".sencha/app/native.properties": { + "source": "native.properties.merge", + "version": "2c8c063f9588eecff09624782d7369a8a000d61c", + "parameters": { + "viewNamespace": "baseapp.view", + "frameworkName": "ext", + "appName": "baseapp", + "controllerNamespace": "baseapp.controller", + "frameworkPath": "ext", + "packagesRelPath": "ext/packages", + "appModels": "", + "themeName": "default", + "library": "all", + "viewName": "Main", + "viewFileName": "Main", + "senchadir": ".sencha", + "controllerName": "Main", + "appStores": "", + "name": "baseapp", + "appViews": "", + "appControllers": "", + "controllerFileName": "Main", + "modelNamespace": "baseapp.model", + "uniqueId": "6d86402c-70ea-4a01-94f7-8349a2418015" + } + }, + ".sencha/app/package.properties": { + "source": "package.properties.merge", + "version": "c6c1fb2fb1dda28480ad0f6e5caffc9b97d196ae", + "parameters": { + "viewNamespace": "baseapp.view", + "frameworkName": "ext", + "appName": "baseapp", + "controllerNamespace": "baseapp.controller", + "frameworkPath": "ext", + "packagesRelPath": "ext/packages", + "appModels": "", + "themeName": "default", + "library": "all", + "viewName": "Main", + "viewFileName": "Main", + "senchadir": ".sencha", + "controllerName": "Main", + "appStores": "", + "name": "baseapp", + "appViews": "", + "appControllers": "", + "controllerFileName": "Main", + "modelNamespace": "baseapp.model", + "uniqueId": "6d86402c-70ea-4a01-94f7-8349a2418015" + } + }, + ".sencha/app/production.properties": { + "source": "production.properties.merge", + "version": "cbdf9712e9b5d37cecb4d0530ba1fccd0ed004a3", + "parameters": { + "viewNamespace": "baseapp.view", + "frameworkName": "ext", + "appName": "baseapp", + "controllerNamespace": "baseapp.controller", + "frameworkPath": "ext", + "packagesRelPath": "ext/packages", + "appModels": "", + "themeName": "default", + "library": "all", + "viewName": "Main", + "viewFileName": "Main", + "senchadir": ".sencha", + "controllerName": "Main", + "appStores": "", + "name": "baseapp", + "appViews": "", + "appControllers": "", + "controllerFileName": "Main", + "modelNamespace": "baseapp.model", + "uniqueId": "6d86402c-70ea-4a01-94f7-8349a2418015" + } + }, + ".sencha/app/sencha.cfg": { + "source": "sencha.cfg.tpl.merge", + "version": "8b88dc946a59c6ad6edb77744966e548250b33a3", + "parameters": { + "viewNamespace": "baseapp.view", + "frameworkName": "ext", + "appName": "baseapp", + "controllerNamespace": "baseapp.controller", + "frameworkPath": "ext", + "packagesRelPath": "ext/packages", + "appModels": "", + "themeName": "default", + "library": "all", + "viewName": "Main", + "viewFileName": "Main", + "senchadir": ".sencha", + "controllerName": "Main", + "appStores": "", + "name": "baseapp", + "appViews": "", + "appControllers": "", + "controllerFileName": "Main", + "modelNamespace": "baseapp.model", + "uniqueId": "6d86402c-70ea-4a01-94f7-8349a2418015" + } + }, + ".sencha/app/testing.properties": { + "source": "testing.properties.merge", + "version": "360e715956c81757e53736789fe20be045acb544", + "parameters": { + "viewNamespace": "baseapp.view", + "frameworkName": "ext", + "appName": "baseapp", + "controllerNamespace": "baseapp.controller", + "frameworkPath": "ext", + "packagesRelPath": "ext/packages", + "appModels": "", + "themeName": "default", + "library": "all", + "viewName": "Main", + "viewFileName": "Main", + "senchadir": ".sencha", + "controllerName": "Main", + "appStores": "", + "name": "baseapp", + "appViews": "", + "appControllers": "", + "controllerFileName": "Main", + "modelNamespace": "baseapp.model", + "uniqueId": "6d86402c-70ea-4a01-94f7-8349a2418015" + } + }, + "app.js": { + "source": "app.js.tpl.merge", + "version": "adb7999f937a224ece5dc57252db7048652e6616", + "parameters": { + "viewNamespace": "baseapp.view", + "frameworkName": "ext", + "appName": "baseapp", + "controllerNamespace": "baseapp.controller", + "frameworkPath": "ext", + "packagesRelPath": "ext/packages", + "appModels": "", + "themeName": "default", + "library": "all", + "viewName": "Main", + "viewFileName": "Main", + "senchadir": ".sencha", + "controllerName": "Main", + "appStores": "", + "name": "baseapp", + "appViews": "", + "appControllers": "", + "controllerFileName": "Main", + "modelNamespace": "baseapp.model", + "uniqueId": "6d86402c-70ea-4a01-94f7-8349a2418015" + } + }, + "app.json": { + "source": "app.json.tpl.merge", + "version": "5c35fa135e5c429faf9348c096b567a3079ad83b", + "parameters": { + "viewNamespace": "baseapp.view", + "frameworkName": "ext", + "appName": "baseapp", + "controllerNamespace": "baseapp.controller", + "frameworkPath": "ext", + "packagesRelPath": "ext/packages", + "appModels": "", + "themeName": "default", + "library": "all", + "viewName": "Main", + "viewFileName": "Main", + "senchadir": ".sencha", + "controllerName": "Main", + "appStores": "", + "name": "baseapp", + "appViews": "", + "appControllers": "", + "controllerFileName": "Main", + "modelNamespace": "baseapp.model", + "uniqueId": "6d86402c-70ea-4a01-94f7-8349a2418015" + } + }, + "index.html": { + "source": "index.html.tpl.merge", + "version": "f036c68a34ae0bc941832df08c406a00545dd9f6", + "parameters": { + "viewNamespace": "baseapp.view", + "frameworkName": "ext", + "appName": "baseapp", + "controllerNamespace": "baseapp.controller", + "frameworkPath": "ext", + "packagesRelPath": "ext/packages", + "appModels": "", + "themeName": "default", + "library": "all", + "viewName": "Main", + "viewFileName": "Main", + "senchadir": ".sencha", + "controllerName": "Main", + "appStores": "", + "name": "baseapp", + "appViews": "", + "appControllers": "", + "controllerFileName": "Main", + "modelNamespace": "baseapp.model", + "uniqueId": "6d86402c-70ea-4a01-94f7-8349a2418015" + } + }, + "sass/config.rb": { + "source": "config.rb.tpl.merge", + "version": "33f446bd02c3fd24eb27891582eff6a2e789796b", + "parameters": { + "viewNamespace": "baseapp.view", + "frameworkName": "ext", + "appName": "baseapp", + "controllerNamespace": "baseapp.controller", + "frameworkPath": "ext", + "packagesRelPath": "ext/packages", + "appModels": "", + "themeName": "default", + "library": "all", + "viewName": "Main", + "viewFileName": "Main", + "senchadir": ".sencha", + "controllerName": "Main", + "appStores": "", + "name": "baseapp", + "appViews": "", + "appControllers": "", + "controllerFileName": "Main", + "modelNamespace": "baseapp.model", + "uniqueId": "6d86402c-70ea-4a01-94f7-8349a2418015" + } + }, + "sass/example/theme.html": { + "source": "theme.html.tpl.merge", + "version": "79e5bc8b4cebd921ec602e9a67c0493c70d2d5ce", + "parameters": { + "viewNamespace": "baseapp.view", + "frameworkName": "ext", + "appName": "baseapp", + "controllerNamespace": "baseapp.controller", + "frameworkPath": "ext", + "packagesRelPath": "ext/packages", + "appModels": "", + "themeName": "default", + "library": "all", + "viewName": "Main", + "viewFileName": "Main", + "senchadir": ".sencha", + "controllerName": "Main", + "appStores": "", + "name": "baseapp", + "appViews": "", + "appControllers": "", + "controllerFileName": "Main", + "modelNamespace": "baseapp.model", + "uniqueId": "6d86402c-70ea-4a01-94f7-8349a2418015" + } + }, + "app/controller/Main.js": { + "source": "{controllerFileName}.js.tpl.merge", + "version": "b092f4aad11d2f7eaab5ce3e2dcc2fc3134f3188", + "parameters": { + "library": "all", + "appName": "baseapp", + "controllerName": "Main", + "name": "baseapp", + "controllerNamespace": "baseapp.controller", + "controllerFileName": "Main" + } + } + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/defaults.properties b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/defaults.properties new file mode 100644 index 0000000000000000000000000000000000000000..408b91e91a60a13918d91f7f61222b211dabdc4f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/defaults.properties @@ -0,0 +1,592 @@ +# ============================================================================= +# This file defines properties used by build-impl.xml and the associated +# *-impl.xml files (sass-impl.xml, js-impl.xml, etc.), which are the core of +# the applications build process. +# +# This file represents the lowest priority file for defining these properties +# as well as the place to look for documentation and learning what properties +# exist. +# +# The full set of these files is as follows (in priority order): +# +# - One of these (based on build.environment): +# - production.properties +# - testing.properties +# - native.properties +# - package.properties +# +# - build.properties +# +# - One of these (based on app.framework): +# - ext.properties +# - touch.properties +# +# - One of these (based on build.environment): +# - production.defaults.properties +# - testing.defaults.properties +# - native.defaults.properties +# - package.defaults.properties +# +# - defaults.properties +# +# Properties are controlled by the first file in the above list to define the +# value. Values from all levels, however, can reference each other via the +# property expansion. +# +# IMPORTANT - This file should not be modified by an app as it is overwritten +# during each app upgrade. +# ============================================================================= + +# ***************************************************************************** +# Global Build Properties +# these are cross-concern properties used by many build phases +# ***************************************************************************** + +# the default build enviornment type (production, testing, native, package) +# NOTE: this is only a default and will typically be set before this file is +# loaded, typically by the 'sencha app build" command. +# See "sencha help app build" for details. +# +# The corresponding properies files: +# (production.properties, testing.properties, etc.) provide examples of +# overriding sets of properties depending on the selected environment +# NOTE: this replaces the deprecated args.environment +build.environment=production + +# the directory to place built application files +build.dir=${workspace.build.dir}/${build.environment}/${app.name} + +# a temporary output directory used for staging intermediate build artifacts +build.temp.dir=${workspace.build.dir}/temp/${build.environment}/${app.name} + +# the directory under the output folder for placing resources +build.resources.dir=${build.dir}/resources + +# ***************************************************************************** +# JS +# these properties control various aspects of output js code construction +# ***************************************************************************** + +# the output js file that contains all needed js code +build.classes.name=app.js +build.classes.file=${build.dir}/${build.classes.name} + +# the output js file for framework code, if the framework +# classes are not included in the default all-classes.js file +build.framework.name=framework.js +build.framework.file=${build.dir}/${build.framework.name} + +# Don't use these - they are deprecated +build.options.debug.enable=debug:true +build.options.debug.disable=debug:false +build.options.logger.enable=logger:yes +build.options.logger.disable=logger:no + +# This property enables/disables blocks in js output, see build.options +build.options.logger=no + +# This property enables/disables blocks in js output, see build.options +build.options.debug=false + +# This property can be used to pass custom build options in addition to any of the other +# build.options flags. When overlapping, these options take priority, see build.options +build.options.custom= + +# This value is specified by the framework +build.options.default= + +# This property contains the framework ("product") used for filtering of JavaScript using +# the preprocessor. This is set by either ext.properties or touch.properties. +# +#build.options.product=touch + +# This property contains the desired API level used for preprocessor filtering of JavaScript. +# This is set by either ext.properties or touch.properties. +# +#build.options.minVersion=2.1 + +# This property holds the set of js preprocessor options in the form: +# +# name1:value1,name2:value2,... +# +# (used by -init-compiler in init-impl.xml) +# +# This property is not likely to be set directly. Rather, you should set one of the +# contributing properties that are combined to form this one: +# +# build.options.debug +# build.options.logger +# build.options.custom +# +# The other properties that contribute to this are typically not needing to be set: +# +# build.options.product +# build.options.minVersion +# +build.options=logger:${build.options.logger},debug:${build.options.debug},product:${build.options.product},minVersion:${build.options.minVersion},${build.options.default},${build.options.custom} + +# This property can be modified to change general build options +# such as excluding files from the set. The format expects newlines +# for each argument, for example: +# +# build.operations=\ +# exclude\n \ +# -namespace=Ext\n +# +# NOTE: modifications to build.operations are intended to be +# placed in an override of the "-after-init" target, where it +# can be calculated based on other +# ant properties +# +# build.operations= + +# enables / disables the full class optimizer during js builds +# (used by the -compile-* targets in js-impl.xml) +build.optimize.enable=\ + optimize\n \ + -define-rewrite\n +build.optimize.disable= +build.optimize=${build.optimize.disable} + +# enables / disables yui compression +build.compression.yui=0 + +# enables / disables closure compression +build.compression.closure=0 + +# enables / disables uglify compression +build.compression.ugilfy=0 + +build.compile.temp.dir=${build.temp.dir}/sencha-compiler + +# controles whether to keep the temp compile dir after the build +build.compile.temp.dir.keep=true + +# ------------------------------------------ +# DOC ONLY - Do Not Set +# this variable will be set to the appropriate compressor +# option, and is calculated in init-impl.xml, but may be overridded in +# app.properties, .properties, or via command line +# +# build.compression= +# ------------------------------------------ + +# ***************************************************************************** +# Page +# these variables control aspects of building the output markup page +# ***************************************************************************** + +# controls whether the output will be microloader-enabled, or markup only +build.output.markuponly=false + +# controls whether the microloader content will be embedded in the output +# markup, or left as a separate resource +build.enable.embedded.microloader=true + +# whether to include the page's manifest.json code with the +# microloader content. Production.properties files should set this to +# false to have app.json exist as a server resource. +build.enable.embedded.manifest=true + +# enables / disables delta patch generation +enable.deltas=false + +# enables / disables generation of cache manifest +enable.cache.manifest=false + +# enables / disables compression of resources referenced in app.json / package.json +# js and css entries +enable.resource.compression=false + +# defaults to index.html, but may be overridden in app.json +app.indexHtmlPath=index.html + +# the input page file for the application +app.page.name=${app.indexHtmlPath} +app.page.file=${app.dir}/${app.page.name} + +# the output page file +build.page.name=${app.page.name} +build.page.file=${build.dir}/${build.page.name} + +# the directory where the microloader files may be found +app.microloader.dir=${app.config.dir}/microloader + +# the file names of the individual microloaders +app.microloader.development=development.js +app.microloader.testing=testing.js +app.microloader.production=production.js + +# the target microloader to use for builds +app.microloader.name=${app.microloader.development} +app.microloader.path=${app.microloader.dir}/${app.microloader.name} + +# specifies how to embed the microloader code into the output markup +# {0} is replaced with the content of the microloader file specified +# by app.microloader.path +build.microloader.code.tpl={0} + +# the template to use when generating a stand-alone json manifest file +build.microloader.json.tpl.standalone={0} + +# the template to use when embedding the manifest json directly next to the +# microloader in the output microloader content +build.microloader.json.tpl.embedded=Ext.blink({0}); + +# the template to use in the output microloader content when supplying +# the manifest json as a separate server-side resource ('production' builds) +build.microloader.json.tpl.external=Ext.blink('{'id:''${app.id}'''}'); + +# the template string to use when embedding the microloader content +# into the output markup +build.embedded.microloader.tpl= + +# the compressor to use when embedding the microloader into a page +# can be -closure or -yui, or leave empty to disable compression +build.embedded.microloader.compressor= + +# the name of the output microloader file +build.microloader.name=microloader.js + +# the path to the microloader content file, if external to the outpout markup +build.microloader.path=${build.dir}/${build.microloader.name} + +# the inner markup to embed into the output markup when not including +# the microloader content directly into the output markup +build.embedded.microloader.src=${build.microloader.name} +build.external.microloader.markup= + +# a flag indicating which mode the microloader should run in (production, testing, etc.) +# currently unused : is a placeholder for future microloader interactions +build.microloader.mode=${build.environment} + +# the tag name to use when generating the compiler save set for +# the page's js code +build.tag.name=full-page + +# the name of the archive folder containing source versions for +# delta patch generation +build.archive.name=archive +build.out.archive.path=${workspace.build.dir}/${build.archive.name}/${app.name} + +# the name of the output folder for calculated delta patches +build.deltas.name=deltas +build.out.delta.path=${build.dir}/${build.deltas.name} + +# the output cache manifest file +build.manifest.name=cache.appcache +build.manifest.path=${build.dir}/${build.manifest.name} + +# the path to the output markup page +build.out.page.path=${build.dir}/${app.page.name} + +# the name of the manifest json file +build.json.name=app.json + +# the full path to the manifest json file +build.out.json.path=${build.dir}/${build.json.name} + +# Defines the file that will contain Ext.setVersion calls for each used package. +build.out.package.versions=${build.compile.temp.dir}/cmd-packages.js + +# a temp directory for managing extracted resources during the page build +build.app.temp.dir=${build.compile.temp.dir}/app + +# controls the format of checksum headers injected into microloaded content +# either comment style, or code style for js and css files +delta.comment.checksums=false + +# ***************************************************************************** +# Refresh +# these properties are used for generating bootstrap js and css +# files to support dev-time interaction with the app +# ***************************************************************************** + +# the base path to use for generating / calculating bootstrap info +app.bootstrap.base.path=${app.dir} + +# these control the name of the bootstrap js file +# note: there will be corresponding entries in either the index page +# or app.json that reference these names +app.bootstrap.js.name=bootstrap.js +app.bootstrap.js=${app.bootstrap.base.path}/${app.bootstrap.js.name} + +# these control the name of the bootstrap css file (for ext 4.2+ apps) +# note: there will be corresponding entries in either the index page +# or app.json that reference these names +app.bootstrap.css.name=bootstrap.css +app.bootstrap.css=${app.bootstrap.base.path}/${app.bootstrap.css.name} + +# the microloader to use for bootstrapping operations +app.microloader.bootstrap=${app.microloader.dir}/${app.microloader.development} + +# the name of the bootstrap microloader manifest +build.json.bootstrap.name=bootstrap.json + +# the full path to the bootstrap microloader manifest +build.json.bootstrap.path=${app.dir}/${build.json.bootstrap.name} + +# ***************************************************************************** +# Sass / Css +# properties for controling features of sass generation and compilation +# ***************************************************************************** + +# controls the ruby command that is used to execute compasss +# a full path to ruby may be specified rather than allowing the system +# shell to resolve the command +build.ruby.path=ruby + +# -------------------- +# these control properties select the mode used to build the app's styling +# see sass-impl.xml for how then are used + +# enables theme builds for apps using ext 41 style themes +enable.ext41.themes=false + +# enables theme builds for apps using ext 42 style themes +enable.ext42.themes=false + +# enables theme builds for apps using touch style themes +enable.touch.themes=false +# -------------------- + +# selector count threshold to use when +# splitting a single css file into multiple +# css files (IE selector limit workaround) +# +# NOTE: applies only to ext js 4.2+ style theme management, currently +# see the above theme control variables for details +build.css.selector.limit=4095 + +# enables / disable css preprocessor (enable.ext42.themes only) +build.css.preprocess=true + +# sets the css preprocessor options, in the form: +# name1:value1,name2:value2,... +build.css.preprocessor.opts= + +# enables / disable css compressor (enable.ext42.themes only) +build.css.compress=true + +# controls the directory used to generate the output app scss file +# for apps that use theme packages +build.sass.dir=${build.temp.dir}/sass + +# Specify the name for the individual resource dirs in the app +# (enable.touch.themes only) +app.sass.name=sass + +# Specify the sass path in the app +# (enable.touch.themes only) +app.sass.dir=${app.dir}/resources/${app.sass.name} + +# name prefix to use for output css / sass files +app.out.base=${app.name}-all +app.out.base.debug=${app.out.base} + +# the output sass file to generate (used with enable.ext42.themes) +app.out.scss=${build.sass.dir}/${app.out.base.debug}.scss +# the output ruby compass config file to generate (used with enable.ext42.themes) +app.out.ruby=${build.sass.dir}/config.rb + +# output css file prefix +app.out.css.prefix=${app.out.base.debug} + +# output css file name +app.out.css.name=${app.out.css.prefix}.css + +# output css file path (relative to build directory root +app.out.css.rel=resources/${app.out.css.name} + +# output css file path (full path) +app.out.css=${build.dir}/${app.out.css.rel} + +# separate file name to use for generating a compressed copy +# of the output css file (this default will compress the file in-place) +app.out.css.compressed=${build.dir}/resources/${app.out.base}.css + +# the directory containing sass files for compass to compile +compass.sass.dir=${build.sass.dir} + +# the output directory where compass should place built css files +compass.css.dir=${build.dir}/resources + +# the directory containing the ruby config file for compass +compass.config.file=${app.out.ruby} + +# enables / disables console highlighting for compass +compass.compile.boring=false + +# enables / disables forced rebuilds for compass +compass.compile.force=true + +# enables / disables stack traces in compass failure output +compass.compile.trace=true + +# the directory that will be the current working directory of the compass +# process (controls the location of .sass-cache folder creation) +# NOTE: this directory will also typically need to contain the config.rb file +# used for compass invocation, so it is ideal to set build.sass.dir instead of this +# variable, as that will control both the config.rb location as well as the +# .sass-cache location +compass.working.dir=${build.sass.dir} + +# --------------------------------------------------- +# Legacy properties for ext41 theme directories +# Specify the resources path in the app +app.packages.dir=${app.dir}/packages + +# Specify the theme path in the app (this directory contains the themes) +app.theme.dir=${app.packages.dir} + +# the currently selected ext 41 theme name +theme.name=default +# --------------------------------------------------- + +# ***************************************************************************** +# Slice +# these properties control features of the theme slice build phase +# ***************************************************************************** + +# the resources directory of the application +# note: this property is currently only used for building ext 4.1 style themes +# (used by x-build-theme and x-copy-resources in slice-impl.xml) +app.resources.dir=${app.dir}/resources + +# the directory containing the slicer widget example page +app.example.dir=${app.dir}/sass/example + +# properties to control the recirect css file that is +# generated for the slicer example page +app.example.css.name=example.css +app.example.css.file=${app.example.dir}/${app.example.css.name} + +# the base path for generating the bootstrap code for the +# slicer page +bootstrap.base.path=${app.example.dir} + +# the full file name of the slicer page's bootstrap js file +bootstrap.example.js=${app.example.dir}/bootstrap.js + +# this is the directory used for intermediate build artifacts used +# by the slicer for generating theme images +app.example.build.dir=${build.temp.dir}/slicer-temp + +# the name of the intermediate screenshot file used for image slicing +build.capture.png=${app.example.build.dir}/theme-capture.png + +# the name of the intermediate widget manifest file used for image slicing +build.capture.json=${app.example.build.dir}/theme-capture.json + +# the location of the slicer widget page +app.example.theme.html.name=theme.html +app.example.theme.html=${app.example.dir}/${app.example.theme.html.name} + +# a name prefix used for slicer page temporary artifacts +app.example.base=${app.name}-example + +# the special slicer page scss file name to generate +app.example.scss=${app.example.build.dir}/${app.example.base}.scss + +# the relative path from the slicer css file to the slicer html file +app.example.css.rel=${app.example.base}.css + +# the path to the css file that will be built for the slicer page +app.example.css=${app.example.build.dir}/${app.example.css.rel} + +# the ruby compass config file to generate for slicer page scss +app.example.out.ruby=${app.example.build.dir}/config.rb +app.example.compass.config=${app.example.out.ruby} + +# legacy ext 41 theme property indicating the name of the +# slicer example page contained in the theme directory +theme.page.name=theme.html + +# Options to pass to the "sencha fs slice" command. +build.slice.options= + +# ***************************************************************************** +# Packager +# these properties control features of the native packaging phase of the +# build process +# ***************************************************************************** + +# enables packaging the built application with the Sencha Desktop Packager +# NOTE: currently unsupported +enable.desktop.packager=false + +# skips packaging the built application with sencha mobile packager (stbuild) or cordova/phonegap +skip.native-package=true + +# a property that controls whether a standalone manifest.json file will be +# generated for the native packaged application +enable.standalone.manifest=false + +# these set the name of the mobile native packager's config file +build.mobile.packager.name=packager.json +build.mobile.packager.file=${app.dir}/${build.mobile.packager.name} + +# the default mobile packager config to use when specifying the autorun argument +# with "sencha app build -run native" +build.mobile.packager.default.name=packager.json +build.mobile.packager.default.file=${app.dir}/${build.mobile.packager.default.name} + +# these set the name of the mobile native packager's temporary config file +# that will have the input and output path properties updated +build.mobile.packager.temp.name=packager.temp.json +build.mobile.packager.temp.file=${app.dir}/${build.mobile.packager.temp.name} + +# the input directory for the mobile native packager that contains the +# built Sencha Cmd application +build.mobile.packager.in.dir=${build.dir} + +# the output location of the mobile native packaged application +build.mobile.packager.out.dir.name=native-package-mobile +build.mobile.packager.out.dir=${workspace.build.dir}/${build.mobile.packager.out.dir.name}/${app.name} + +# ***************************************************************************** +# Resolve +# these properties control aspects of the dynamic dependency resolver, which +# uses phantomjs to load the applicaiton and extract Ext.Loader class load +# history. +# ***************************************************************************** + +# enables / disables dynamic dependency resolution +skip.resolve=true + +# enables the local web server. this may be disabled to load the application's +# page from an existing web server. +skip.web-start=false + +# the port number to start the local web server on +build.web.port=54321 + +# the directory representing the root web folder +build.web.root=${workspace.dir} + +# the base url to access the local web server +build.resolve.url=http://localhost:${build.web.port} + +# a template string used to format the detected dynamic dependencies +build.resolve.tpl={0} + +# the mode to use when formatting the detected dynamic dependencies +build.resolve.mode=references + +# the output file for the detected dynamic dependencies +build.resolve.file=${build.temp.dir}/resolve.json + +# controls whether unmatched external references in the specified file will +# generate build warnings or build failures +build.resolve.allow.unmatched=true + +# ***************************************************************************** +# Watch +# these properties adjust the behavior of the app watch process. +# ***************************************************************************** + +# the default set of actions to run when triggering a rebuild +build.trigger.targets=-refresh,-resources,-compass-compile + +# the watcher targets to run that monitor for code changes +build.watcher.targets=-watch-compiler diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/ext.properties b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/ext.properties new file mode 100644 index 0000000000000000000000000000000000000000..68a0e2e1754add41df6e37748da7519c00e2621f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/ext.properties @@ -0,0 +1,28 @@ +# ============================================================================= +# This file defines default property values that apply to all builds based on +# Ext JS 4.2.x framework. +# +# Please use build.properties to customize these properties. +# +# To override a property based on build.environment instead add properties to +# one of these higher priority files: +# +# - production.properties +# - testing.properties +# - native.properties +# - package.properties +# +# The properties defined in this file take priority over defaults.properties +# and *.defaults.properties. +# +# IMPORTANT - This file should not be modified by an app as it is overwritten +# during each app upgrade. +# ============================================================================= + +enable.ext42.themes=true + +build.output.markuponly=true + +build.options.product=ext + +build.options.minVersion=4.2 diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/find-cmd-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/find-cmd-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..55d6826ce7a826c49c52a47d1aa903536c0621b7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/find-cmd-impl.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + source ~/.bash_profile; sencha which -p cmd.dir -o '$cmddir$' + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/init-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/init-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..9d51916dcba164b003059214773099e822e30079 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/init-impl.xml @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/js-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/js-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..1583bcedfaaf562bb4d9f19b0a9a22b85de72fdb --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/js-impl.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/microloader/development.js b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/microloader/development.js new file mode 100644 index 0000000000000000000000000000000000000000..a183a7b4e1465ec64cec7505d99084b85b8b027e --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/.sencha/app/microloader/development.js @@ -0,0 +1,189 @@ +/** + * Sencha Blink - Development + * @author Jacky Nguyen + */ +(function() { + var head = document.head || document.getElementsByTagName('head')[0]; + + function write(content) { + document.write(content); + } + + function addMeta(name, content) { + var meta = document.createElement('meta'); + + meta.setAttribute('name', name); + meta.setAttribute('content', content); + head.appendChild(meta); + } + + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'bootstrap.json', false); + xhr.send(null); + + var options = eval("(" + xhr.responseText + ")"), + scripts = options.js || [], + styleSheets = options.css || [], + i, ln, path, platform, theme, exclude; + + if(options.platform && options.platforms && options.platforms[options.platform] && options.platforms[options.platform].js) { + scripts = options.platforms[options.platform].js.concat(scripts); + } + + if (navigator.userAgent.match(/IEMobile\/10\.0/)) { + var msViewportStyle = document.createElement("style"); + msViewportStyle.appendChild( + document.createTextNode( + "@media screen and (orientation: portrait) {" + + "@-ms-viewport {width: 320px !important;}" + + "}" + + "@media screen and (orientation: landscape) {" + + "@-ms-viewport {width: 560px !important;}" + + "}" + ) + ); + document.getElementsByTagName("head")[0].appendChild(msViewportStyle); + } + + addMeta('viewport', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no'); + addMeta('apple-mobile-web-app-capable', 'yes'); + addMeta('apple-touch-fullscreen', 'yes'); + + if (!window.Ext) { + window.Ext = {}; + } + Ext.microloaded = true; + + var filterPlatform = window.Ext.filterPlatform = function(platform) { + var profileMatch = false, + ua = navigator.userAgent, + j, jln; + + platform = [].concat(platform); + + function isPhone(ua) { + var isMobile = /Mobile(\/|\s)/.test(ua); + + // Either: + // - iOS but not iPad + // - Android 2 + // - Android with "Mobile" in the UA + + return /(iPhone|iPod)/.test(ua) || + (!/(Silk)/.test(ua) && (/(Android)/.test(ua) && (/(Android 2)/.test(ua) || isMobile))) || + (/(BlackBerry|BB)/.test(ua) && isMobile) || + /(Windows Phone)/.test(ua); + } + + function isTablet(ua) { + return !isPhone(ua) && (/iPad/.test(ua) || /Android|Silk/.test(ua) || /(RIM Tablet OS)/.test(ua) || + (/MSIE 10/.test(ua) && /; Touch/.test(ua))); + } + + // Check if the ?platform parameter is set in the URL + var paramsString = window.location.search.substr(1), + paramsArray = paramsString.split("&"), + params = {}, + testPlatform, i; + + for (i = 0; i < paramsArray.length; i++) { + var tmpArray = paramsArray[i].split("="); + params[tmpArray[0]] = tmpArray[1]; + } + + testPlatform = params.platform; + if (testPlatform) { + return platform.indexOf(testPlatform) != -1; + } + + for (j = 0, jln = platform.length; j < jln; j++) { + switch (platform[j]) { + case 'phone': + profileMatch = isPhone(ua); + break; + case 'tablet': + profileMatch = isTablet(ua); + break; + case 'desktop': + profileMatch = !isPhone(ua) && !isTablet(ua); + break; + case 'ios': + profileMatch = /(iPad|iPhone|iPod)/.test(ua); + break; + case 'android': + profileMatch = /(Android|Silk)/.test(ua); + break; + case 'blackberry': + profileMatch = /(BlackBerry|BB)/.test(ua); + break; + case 'safari': + profileMatch = /Safari/.test(ua) && !(/(BlackBerry|BB)/.test(ua)); + break; + case 'chrome': + profileMatch = /Chrome/.test(ua); + break; + case 'ie10': + profileMatch = /MSIE 10/.test(ua); + break; + case 'windows': + profileMatch = /MSIE 10/.test(ua) || /Trident/.test(ua); + break; + case 'tizen': + profileMatch = /Tizen/.test(ua); + break; + case 'firefox': + profileMatch = /Firefox/.test(ua); + } + if (profileMatch) { + return true; + } + } + return false; + }; + + + for (i = 0,ln = styleSheets.length; i < ln; i++) { + path = styleSheets[i]; + + if (typeof path != 'string') { + platform = path.platform; + exclude = path.exclude; + theme = path.theme; + path = path.path; + } + + if (platform) { + if (!filterPlatform(platform) || filterPlatform(exclude)) { + continue; + } + + if(!Ext.theme) { + Ext.theme = {}; + } + if(!Ext.theme.name) { + Ext.theme.name = theme || 'Default'; + } + } + + write(''); + } + + for (i = 0,ln = scripts.length; i < ln; i++) { + path = scripts[i]; + + if (typeof path != 'string') { + platform = path.platform; + exclude = path.exclude; + path = path.path; + } + + if (platform) { + if (!filterPlatform(platform) || filterPlatform(exclude)) { + continue; + } + } + + write(' + + + + + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/build-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/build-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..e242abd617d18ca7067345ecce1862e619fb774b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/build-impl.xml @@ -0,0 +1,358 @@ + + + + + + + + Using Sencha Cmd from ${cmd.dir} for ${ant.file} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/build.properties b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/build.properties new file mode 100644 index 0000000000000000000000000000000000000000..83e7e5f2a08400863394010646358cf4d411fba9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/build.properties @@ -0,0 +1,8 @@ +# ============================================================================= +# This file provides an override point for default variables defined in +# defaults.properties. +# +# IMPORTANT - Sencha Cmd will merge your changes with its own during upgrades. +# To avoid potential merge conflicts avoid making large, sweeping changes to +# this file. +# ============================================================================= diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/codegen.json b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/codegen.json new file mode 100644 index 0000000000000000000000000000000000000000..5869a34c765ef7e8e0b0ebe90d1fdfa7fa49b529 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/codegen.json @@ -0,0 +1,118 @@ +{ + "sources": { + "custom.js.merge": { + "199e99bbd15c3c0415569425cb21e77c95e9042a": "eJxlj0FOxDAMRfdziq9ZwUjTHIAlYslquIAncdtA4lSxC8PtSdoREmIV6f/4+dmdDjjhbY6KMSZGeycWrmQcQAqCGlWLMmEpUQzXb1xY/Ex4zgFnRMNXTAlSWseovCTybbbUDl6XsJHa1FH3sYX8B03cqqlS4OPQ//2V8CQ7K5fPriEBNjPU17gYjCZE6UnmYbacfj/GsaUNslUIhbVzu5lwq/2qVjIohGixCCVkkjiyWrOFzqWaXw0sViPr0IRYGVQ7yq+55X2HdObg7meo45udt4XnKyk7Je0Z5SWxqyyB6/Cu/Uh3ODj3crNhN28ar/f1D49P/7rLXUd7+QPuPI9g" + }, + "all.scss.merge": { + "da39a3ee5e6b4b0d3255bfef95601890afd80709": "eJwDAAAAAAE\u003d" + }, + "build.properties.merge": { + "8b81315dbe73ce9c08478f4c1d2df84f456efcf5": "eJytkEtOxEAMRPdzipKyhbkBCzQrFnzE5AKetJNYdOzI3UnE7XGA3GC8K5f9/GnwdM84NWhHKeglM2a3VRIXkMJWdg+B2UQrenMk7mnJFSu50C1HXWREOUEUAfr3yzk4M3sVLudTE8bL68f7Z/v81uIRV9ZuJFymhE1yxsQ+ML5tcUReh6BuUkdILbBNkRYXHbDMg1P6BaI10GqSYrXKWoUOSmfaZ+mi88+f6GvvzRTmA8rGPO/6mFMtYPW4fiff97U/al6C1w\u003d\u003d" + }, + "sencha.cfg.tpl.merge": { + "6d1982cce48163a98dc46012d1d0cdfa209fbda6": "eJzFVdFq2zAUfc9XXNzBWmicvg0GgXZlD9vDWsge96LIcqJFljRJTuqN/vuOJNvJmqbbYLASim1dnXPuuVdXZ/R5LUizRpCpKeDZMr5hK0FT8mvTqooaFvg6LRUxriDrjBUudCQ1lbM+vvzqjZ6cTYbXGDr/YTerT3h4nEzORiKPEEHB0G4tE7D0A+lrT4ubxYK4cQ5xRle+TPsqUbNWBdoy1UalgCveP4SCGsG0BwYLSWEtlfBZl2fez7zjdM50NbxvmbvoE+IKH1IwwOLehOeMCXuVJX3QZLDoaGdc5S8Ta2mZFqq8j/+hgFkIMADZc85SxCxFlJ57X2I5WmDIh06Jkbx2piGmuz3lJXkRsimwaKmY3kBFnb/gl75cZsk6hmTAY12daSE6FtC2IduaUj1QiT3PKR1rGAPLUdkc4bmOWUn0jJumYVMvLHMsiIqU9CE2Um1UJZxHiYXbZ4uaykqkinoBpOF702LXUpB4sEpyGVSH0G+tdECMJggo56qt8IrSRt5lK1V1KDVBWRbW81df2qurN++GhUq6x5js3yrHo9kK5yAYPXij1L63YimAFtloJ7ECgSu5RTkYBbaKW4ue/m6AKPpuBztrg0ELSs6U6oAzpgroldAi62EWbbXs8pGEcULz/ogCQkkdPYlBOHdv4cMZEc1m1z0WXT/lP7BqTOtZq8bVp4ZlY/qKpnYqYGvRFzWd0xzsBXN8jRRq42hxu1gAKHuXjul+nHxkW7bgTtowdkLrsa/PGsmhG2CU0UdNCebTxe7b+w8SwDjw/ykDUL+cAQLGDCL5UeuQbKxxITUOTkkkTFmls4GehHjRp3jELsJv/EPADCRpJJwwEv3XT3BDVsWZXsQuXbM8zeMdkqQUZNqAGXQoIp9f0D2rIK0OV0bPlpznRgcmMfDFA2us+sXhc3+R6nV4nxxS9lv8SdYh4EVi3y6n/S4/HscBhu40yoI7SubWKWqH4YmbY1OkeZEv1tDZX4QBEtfkaV0DHXTN/+kfNN1i8uIGus83ukROU7pPteRpRX5P3mISCWV2B8MHNWdbgzwb4VZplhtdoxQBM19iLmqxg3eYJT5un/wEixPeXA\u003d\u003d" + }, + "testing.properties.merge": { + "e65f969c42eb4f355c850fc58fea852582f20db8": "eJyVkUFywyAQBO9+xVb5oIutH/gX+QCCkbUOAooFOf59FsmqpHKKOFEwOzM0Z7r9f53O9DGx0Mge5DBygFDKMSEX1m0VOBpepLqhsndXnpPvv2Z/oefEdiKdLRNoMAJqdyqMI5lAJiXP1hSOQbbZ5msh0mskmuOvnDHHWY32JjbmDEkxOCqxBai6K5DC4d693RAWzjHMCOVCkmB5ZLhW9EWdINjJtBJv9T7cU0vXsk/2rWwxn9AisHA6AooLcgNhqi8riYXdimAn0P+07vXsCOuD8rNimLWaiDKkmBrK7UOUyR0B2RRQdzXedyp+CMVaUi0rQn3ninMxvurPspjBQ/54jjHvYLbHycGKG5Fm2SIf0u/ut9M3l43NIg\u003d\u003d" + }, + "theme.html.tpl.merge": { + "79ec5194c052d6cc313e842c4e2763fa562c7b70": "eJydVE1vEzEQve+vmO4JEF6nhQNKd3MJRYCQqEg4cHS8k9jFay/2JE1U9b/X3l1KWoKIYln74Tdv3vh55PLs/dfp/Mf1FShqzCQrzxjLAGDq2p3XK0XwQr6Ei9H5BYuPNwXM0Eol4JOVRcZYJAw8haKeJCaUpMng5GpL8HkGc4UNwkfhLYZQ8h7r4xokEWWpZfhrrTdVPnWW0BKb71rMQfZ/VU64JZ5kLiFK+4BUfZ9/YO/ySdYnGmqGL07UIIwBnxJ6rMFo+zOAsDUE6XVLoYtLdXfEBMdgU+WBdgaDQqQcKMoPqjKEHJTHZZXjVjStwaJb4kOCPus+40ZsRL+aQ/CyyouCx3kXsW9orgWpex6/WY2L9aq4Cfmk5H388SkXzlEgL9pn/ANukEKYGS3RRz89HrbhCEmPtkb/f73kfiOsXmKgwXjlPMn1CZq9c3Emvyj1EVuIgDyIEPhwHPy32IlWHqXxuIUTReQ6kGsOmtf1XZ8pjeJW1ysklnpfaBtP7e4RS6MRfqXtGM5H7fbyCRKJpMbwdvQX0rqgSbvIip0uSG/wKe426JfG3Y5ho4NemD34PvtT2ZaFro8YxRqQXv8TgVfPinatkJp2ser9zN32+bD/ku/dHwtX76JR3Ssh3QXzACi8aeE\u003d" + }, + "package.json.tpl.merge": { + "12322b2f0769f491000df8ec0e012dd2d78a7eaf": "eJx1zrEKwjAQgOG9TxE6S9HV2VFcFBdxONJrDW1y8XIRSum7m2pTJzMd/3cHGQuVXunAYrlX5ei79pTmqdx8QQa/wiXNK2hGEOLZwJEbLMWQLURrgYfZzg9iUTksXqOA6bE+YNBsvBhy8+6RXKvqX1PUKA+6gxbz5Qs5LNu7altt19+Q9SDXP9oQW5BPzqknDX0qwhGXxPiMhjGkersXU/EGNatRVA\u003d\u003d" + }, + "config.rb.tpl.merge": { + "33f446bd02c3fd24eb27891582eff6a2e789796b": "eJxLLi2KT8ksUrBVcMvMSdUDMvMSc1M14uPdPH1c4+M1ufJLSwpKS+KLSypzUoGqrPJSi0tSU7gALskTcA\u003d\u003d" + } + }, + "targets": { + ".sencha/package/sencha.cfg": { + "source": "sencha.cfg.tpl.merge", + "version": "6d1982cce48163a98dc46012d1d0cdfa209fbda6", + "parameters": { + "pkgType": "code", + "touchRelPath": "../../${touch.dir}", + "senchadir": ".sencha", + "pkgName": "font-awesome", + "extRelPath": "../../ext" + } + }, + "sass/config.rb": { + "source": "config.rb.tpl.merge", + "version": "33f446bd02c3fd24eb27891582eff6a2e789796b", + "parameters": { + "pkgType": "code", + "touchRelPath": "../../${touch.dir}", + "senchadir": ".sencha", + "pkgName": "font-awesome", + "extRelPath": "../../ext" + } + }, + "sass/etc/all.scss": { + "source": "all.scss.merge", + "version": "da39a3ee5e6b4b0d3255bfef95601890afd80709", + "parameters": { + "pkgType": "code", + "touchRelPath": "../../${touch.dir}", + "senchadir": ".sencha", + "pkgName": "font-awesome", + "extRelPath": "../../ext" + } + }, + "package.json": { + "source": "package.json.tpl.merge", + "version": "12322b2f0769f491000df8ec0e012dd2d78a7eaf", + "parameters": { + "pkgType": "code", + "touchRelPath": "../../${touch.dir}", + "senchadir": ".sencha", + "pkgName": "font-awesome", + "extRelPath": "../../ext" + } + }, + ".sencha/package/testing.properties": { + "source": "testing.properties.merge", + "version": "e65f969c42eb4f355c850fc58fea852582f20db8", + "parameters": { + "pkgType": "code", + "touchRelPath": "../../${touch.dir}", + "senchadir": ".sencha", + "pkgName": "font-awesome", + "extRelPath": "../../ext" + } + }, + "sass/example/custom.js": { + "source": "custom.js.merge", + "version": "199e99bbd15c3c0415569425cb21e77c95e9042a", + "parameters": { + "pkgType": "code", + "touchRelPath": "../../${touch.dir}", + "senchadir": ".sencha", + "pkgName": "font-awesome", + "extRelPath": "../../ext" + } + }, + "sass/example/theme.html": { + "source": "theme.html.tpl.merge", + "version": "79ec5194c052d6cc313e842c4e2763fa562c7b70", + "parameters": { + "pkgType": "code", + "touchRelPath": "../../${touch.dir}", + "senchadir": ".sencha", + "pkgName": "font-awesome", + "extRelPath": "../../ext" + } + }, + ".sencha/package/build.properties": { + "source": "build.properties.merge", + "version": "8b81315dbe73ce9c08478f4c1d2df84f456efcf5", + "parameters": { + "pkgType": "code", + "touchRelPath": "../../${touch.dir}", + "senchadir": ".sencha", + "pkgName": "font-awesome", + "extRelPath": "../../ext" + } + } + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/defaults.properties b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/defaults.properties new file mode 100644 index 0000000000000000000000000000000000000000..dab58b17eefefdc8ea7be04d47579b343994c89d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/defaults.properties @@ -0,0 +1,155 @@ +# ============================================================================= +# This file defines properties used by build-impl.xml and the associated +# *-impl.xml files (sass-impl.xml, js-impl.xml, etc.), which are the core of +# the applications build process. +# +# IMPORTANT - This file is not modifiable by a package, and will be overwritten +# during each app upgrade. Please use build.properties for defining package +# customizations to these properties. +# ============================================================================= + +# =========================================== +# properties defining various directory +# locations +# =========================================== +build.dir=${package.build.dir} +build.resources.dir=${build.dir}/resources +package.resources.dir=${package.dir}/resources +package.sass.dir=${package.dir}/sass +package.licenses.dir=${package.dir}/licenses + +# =========================================== +# definitions of various file name patterns +# used for output artifacts +# =========================================== +build.name.prefix=${build.dir}/${package.name} +build.name.css.prefix=${build.resources.dir}/${package.name} +build.name.ruby=config.rb + +build.debug.suffix=-debug +build.all.suffix=-all +build.rtl.suffix=-rtl + +build.all.debug.suffix=${build.all.suffix}${build.debug.suffix} +build.all.rtl.suffix=${build.all.suffix}${build.rtl.suffix} +build.all.rtl.debug.suffix=${build.all.suffix}${build.rtl.suffix}${build.debug.suffix} + +# =========================================== +# define the output js file names for dev, +# debug, and compressed (no suffix) +# =========================================== +build.all.js=${build.name.prefix}.js +build.all.debug.js=${build.name.prefix}${build.debug.suffix}.js + +# =========================================== +# output file names for the scss files +# =========================================== +build.all.scss=${build.name.prefix}${build.all.debug.suffix}.scss +build.all.rtl.scss=${build.name.prefix}${build.all.rtl.debug.suffix}.scss + +# =========================================== +# output file names for the css files +# generated from the scss files by running +# a compass compilation +# =========================================== +build.all.css.debug.prefix=${package.name}${build.all.debug.suffix} +build.all.css.debug=${build.resources.dir}/${build.all.css.debug.prefix}.css +build.all.rtl.css.debug.prefix=${package.name}${build.all.rtl.debug.suffix} +build.all.rtl.css.debug=${build.resources.dir}/${build.all.rtl.css.debug.prefix}.css +build.all.css.prefix=${package.name}${build.all.suffix} +build.all.css=${build.resources.dir}/${build.all.css.prefix}.css +build.all.rtl.css.prefix=${package.name}${build.all.rtl.suffix} +build.all.rtl.css=${build.resources.dir}/${build.all.rtl.css.prefix}.css + +build.all.ruby=${build.dir}/${build.name.ruby} + +# =========================================== +# options to pass to the 'sencha fs slice' command +# =========================================== +build.slice.options= + +# =========================================== +# preprocessor options used when generating +# concatenated js output files +# =========================================== +build.compile.js.debug.options=debug:true +build.compile.js.options=debug:false + +# enables / disables removing text references from +# package js build files +build.remove.references=false + +# This property can be modified to change general build options +# such as excluding files from the set. The format expects newlines +# for each argument, for example: +# +# build.operations=\ +# exclude\n \ +# -namespace=Ext\n +# +# NOTE: modifications to build.operations are intended to be +# placed in an override of the "-after-init" target, where it +# can be calculated based on other +# ant properties +# +# build.operations= + +# =========================================== +# compression option used to generate '-all' +# js output file +# =========================================== +build.compile.js.compress=+yui + +# =========================================== +# selector count threshold to use when +# splitting a single css file into multiple +# css files (IE selector limit workaround) +# =========================================== +build.css.selector.limit=4095 + +# controls the ruby command used to execute compass. a full path +# to ruby may be specified rather than allowing the system shell +# to resolve the command +build.ruby.path=ruby + +# controls the working directory of the child compass process +# and the output location for the .sass-cache folder +compass.working.dir=${build.dir} + +# enables / disables console highlighting for compass +compass.compile.boring=false + +# enables / disables forced rebuilds for compass +compass.compile.force=true + +# enables / disables stack traces in compass failure output +compass.compile.trace=true + +# =========================================== +# Options for sub-packages + +# Set to true/1 to enable build.version inheritance by sub-pacakges +build.subpkgs.inherit.version=0 + +# =========================================== +# theme slicing example page settings +# =========================================== +package.example.dir=${package.dir}/sass/example +package.example.base=${build.all.rtl.css.debug.prefix} +package.example.css.rel=resources/${package.example.base}.css +package.example.css=${build.dir}/${package.example.css.rel} +package.example.scss=${build.dir}/${package.example.base}.scss +package.example.theme.html=${package.example.dir}/theme.html + +bootstrap.base.path=${package.example.dir} +bootstrap.example.js=${package.example.dir}/bootstrap.js + + +# =========================================== +# options controlling output packaging +# operations for output '.pkg' file +# =========================================== +pkg.build.dir=${workspace.build.dir}/${package.name} +pkg.file.name=${package.name}.pkg +pkg.includes=**/* +pkg.excludes=package.json diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/find-cmd-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/find-cmd-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..55d6826ce7a826c49c52a47d1aa903536c0621b7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/find-cmd-impl.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + source ~/.bash_profile; sencha which -p cmd.dir -o '$cmddir$' + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/init-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/init-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..a56164966159edd5afe7bd59b31d533770419743 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/init-impl.xml @@ -0,0 +1,266 @@ + + + + + + + + + + + + + + + + + + + Switch package version to ${build.version} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/js-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/js-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..50e6992c9767bfdf7f1cf62d9c89cc720cc3bdfa --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/js-impl.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/plugin.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/plugin.xml new file mode 100644 index 0000000000000000000000000000000000000000..d57eba8747bb042f7626cadf1b8cae62e0a1a3dc --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/plugin.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/resources-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/resources-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..19e2d4852d57699d9b6bb2ea98f296791e346355 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/resources-impl.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + Merging resources from base package ${base.path} + + + + + + + + + + + + + + + + Merging resources from current package ${package.resources.dir} + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/sass-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/sass-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..d86e2d611dc19f945c960250af3cf0712c885b15 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/sass-impl.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/sencha.cfg b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/sencha.cfg new file mode 100644 index 0000000000000000000000000000000000000000..177fe5facfa17cb19fff1f793057317793228ab8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/sencha.cfg @@ -0,0 +1,60 @@ +# The name of the package - should match the "name" property in ./package.json +# +package.name=font-awesome + +# The namespace to which this package's SASS corresponds. The default value of +# "Ext" means that the files in ./sass/src (and ./sass/var) match classes in +# the Ext" root namespace. In other words, "Ext.panel.Panel" maps to +# ./sass/src/panel/Panel.scss. +# +# To style classes from any namespace, set this to blank. If this is blank, +# then to style "Ext.panel.Panel" you would put SASS in +# ./sass/src/Ext/panel/Panel.scss. +# +package.sass.namespace=Ext + +# This is the comma-separated list of folders where classes reside. These +# classes must be explicitly required to be included in the build. +# +package.classpath=${package.dir}/src + +# This is the comma-separated list of folders of overrides. All files in this +# path will be given a tag of "packageOverrides" which is automatically +# required in generated apps by the presence of this line in app.js: +# +# //@require @packageOverrides +# +package.overrides=${package.dir}/overrides + +# This is the folder where SASS "src" resides. This is searched for SCSS +# files that match the JavaScript classes used by the application. +# +package.sass.srcpath=${package.dir}/sass/src + +# This is the folder where SASS "vars" resides. This is searched for SCSS +# files that match the JavaScript classes used by the application. +# +package.sass.varpath=${package.dir}/sass/var + +# This file is automatically imported into the SASS build before "vars". +# +package.sass.etcpath=${package.dir}/sass/etc/all.scss + +# This is the folder in which to place "sencha packaage build" output. +# +package.build.dir=${package.dir}/build + +# The folder that contains example application(s) for this package. +# +package.examples.dir=${package.dir}/examples + +# The folder that contains sub-packages of this package. Only valid for "framework" +# package type. +# +package.subpkgs.dir=${package.dir}/packages + +#============================================================================== +# Custom Properties - Place customizations below this line to avoid merge +# conflicts with newer versions + +package.cmd.version=4.0.4.84 diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/slice-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/slice-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..8ca45dca951e45a97556df76ecc206d26e3ec24f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/slice-impl.xml @@ -0,0 +1,111 @@ + + + + +/** + * This file is generated by Sencha Cmd and should NOT be edited. It is + * provided to support globbing requires, custom xtypes, and other + * metadata-driven class system features + */ + + + + + + + + + + + +/* + * This file is generated by Sencha Cmd and should NOT be edited. It redirects + * to the most recently built CSS file for the application to allow theme.html + * to load properly for image slicing (required to support non-CSS3 browsers + * such as IE9 and below). + */ +@import '${package.example.css.path}'; + + + + + + Capture theme image to ${build.dir}/theme-capture.png + + + + + + + Slicing theme images to ${build.resources.dir} + + + + + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/sub-builds.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/sub-builds.xml new file mode 100644 index 0000000000000000000000000000000000000000..90f648e362a0a0afcdf240431173ff6c2d6b0e33 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/sub-builds.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + package + upgrade + + + + + + + + + + Building example in @{example-dir} + + + + + + + + + Upgrading example in @{example-dir} + + + app + upgrade + + + + + + + + + + Cleaning example in @{example-dir} + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/testing.properties b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/testing.properties new file mode 100644 index 0000000000000000000000000000000000000000..60749a30dbd1357878336fb45589373f33f12107 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/.sencha/package/testing.properties @@ -0,0 +1,17 @@ +# =========================================== +# This file defines properties used by +# build-impl.xml, which is the base impl +# of an applications build process. The +# properties from this file correspond to the +# 'testing' build environment, specified +# by 'sencha app build testing'. These will +# take precedence over defaults provided by +# build.properties. +# =========================================== + +# =========================================== +# compression option used to generate '-all' +# js output file. this value disables +# compression for testing builds +# =========================================== +build.compile.js.compress= diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/build.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/build.xml new file mode 100644 index 0000000000000000000000000000000000000000..f29a5a36f19f8248351a4d83c42f7287181a0883 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/build.xml @@ -0,0 +1,37 @@ + + + + + + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/package.json b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/package.json new file mode 100644 index 0000000000000000000000000000000000000000..a2f356115bfda59ba306dd1aec2ea704b18ee55c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/package.json @@ -0,0 +1,12 @@ +{ + "name": "font-awesome", + "type": "code", + "creator": "Sonatype", + "summary": "Font-Awesome", + "detailedDescription": "Font-Awesome http://fortawesome.github.io/Font-Awesome/", + "version": "4.5.0", + "compatVersion": "4.5.0", + "format": "1", + "local": true, + "requires": [] +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/FontAwesome.otf b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..3ed7f8b48ad9bfab52eb03822fefcd6b77d2e680 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/FontAwesome.otf differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.eot b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..9b6afaedc0fd7aaf927a07f82da9c11022251b8b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.eot differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.svg b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.svg new file mode 100644 index 0000000000000000000000000000000000000000..d05688e9e28e1bee95662f3b668f410d37dfa214 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.svg @@ -0,0 +1,655 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.ttf b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..26dea7951a73079223b50653c455c5adf46a4648 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.ttf differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.woff b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..dc35ce3c2cf688c89b0bd0d4a82bc4be82b14c40 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.woff differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.woff2 b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..500e5172534171f678e01f7569d66f9257036a09 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/resources/fontawesome-webfont.woff2 differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/config.rb b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/config.rb new file mode 100644 index 0000000000000000000000000000000000000000..2c32c0a25f790936b40982fc7d9397abee1cba51 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/config.rb @@ -0,0 +1,2 @@ +cur_dir = File.dirname(__FILE__) +output_style = :nested diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_animated.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_animated.scss new file mode 100644 index 0000000000000000000000000000000000000000..8a020dbfff7822bf57c7217eafdaa4884b8aa943 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_animated.scss @@ -0,0 +1,34 @@ +// Spinning Icons +// -------------------------- + +.#{$fa-css-prefix}-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} + +.#{$fa-css-prefix}-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); +} + +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_bordered-pulled.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_bordered-pulled.scss new file mode 100644 index 0000000000000000000000000000000000000000..d4b85a02f24adad8890ad69f7a1db6c7e3ec8a7d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_bordered-pulled.scss @@ -0,0 +1,25 @@ +// Bordered & Pulled +// ------------------------- + +.#{$fa-css-prefix}-border { + padding: .2em .25em .15em; + border: solid .08em $fa-border-color; + border-radius: .1em; +} + +.#{$fa-css-prefix}-pull-left { float: left; } +.#{$fa-css-prefix}-pull-right { float: right; } + +.#{$fa-css-prefix} { + &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } + &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } +} + +/* Deprecated as of 4.4.0 */ +.pull-right { float: right; } +.pull-left { float: left; } + +.#{$fa-css-prefix} { + &.pull-left { margin-right: .3em; } + &.pull-right { margin-left: .3em; } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_core.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_core.scss new file mode 100644 index 0000000000000000000000000000000000000000..7425ef85fc80ce6b035065906fc27490715e3733 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_core.scss @@ -0,0 +1,12 @@ +// Base Class Definition +// ------------------------- + +.#{$fa-css-prefix} { + display: inline-block; + font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration + font-size: inherit; // can't have font-size inherit on line above, so need to override + text-rendering: auto; // optimizelegibility throws things off #1094 + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_fixed-width.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_fixed-width.scss new file mode 100644 index 0000000000000000000000000000000000000000..b221c98133a4d4a8449c848ccb69bf631d1c3e5d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_fixed-width.scss @@ -0,0 +1,6 @@ +// Fixed Width Icons +// ------------------------- +.#{$fa-css-prefix}-fw { + width: (18em / 14); + text-align: center; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_icons.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_icons.scss new file mode 100644 index 0000000000000000000000000000000000000000..6f9375989a3518cc8705f8ac89ea08b35913f665 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_icons.scss @@ -0,0 +1,697 @@ +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ + +.#{$fa-css-prefix}-glass:before { content: $fa-var-glass; } +.#{$fa-css-prefix}-music:before { content: $fa-var-music; } +.#{$fa-css-prefix}-search:before { content: $fa-var-search; } +.#{$fa-css-prefix}-envelope-o:before { content: $fa-var-envelope-o; } +.#{$fa-css-prefix}-heart:before { content: $fa-var-heart; } +.#{$fa-css-prefix}-star:before { content: $fa-var-star; } +.#{$fa-css-prefix}-star-o:before { content: $fa-var-star-o; } +.#{$fa-css-prefix}-user:before { content: $fa-var-user; } +.#{$fa-css-prefix}-film:before { content: $fa-var-film; } +.#{$fa-css-prefix}-th-large:before { content: $fa-var-th-large; } +.#{$fa-css-prefix}-th:before { content: $fa-var-th; } +.#{$fa-css-prefix}-th-list:before { content: $fa-var-th-list; } +.#{$fa-css-prefix}-check:before { content: $fa-var-check; } +.#{$fa-css-prefix}-remove:before, +.#{$fa-css-prefix}-close:before, +.#{$fa-css-prefix}-times:before { content: $fa-var-times; } +.#{$fa-css-prefix}-search-plus:before { content: $fa-var-search-plus; } +.#{$fa-css-prefix}-search-minus:before { content: $fa-var-search-minus; } +.#{$fa-css-prefix}-power-off:before { content: $fa-var-power-off; } +.#{$fa-css-prefix}-signal:before { content: $fa-var-signal; } +.#{$fa-css-prefix}-gear:before, +.#{$fa-css-prefix}-cog:before { content: $fa-var-cog; } +.#{$fa-css-prefix}-trash-o:before { content: $fa-var-trash-o; } +.#{$fa-css-prefix}-home:before { content: $fa-var-home; } +.#{$fa-css-prefix}-file-o:before { content: $fa-var-file-o; } +.#{$fa-css-prefix}-clock-o:before { content: $fa-var-clock-o; } +.#{$fa-css-prefix}-road:before { content: $fa-var-road; } +.#{$fa-css-prefix}-download:before { content: $fa-var-download; } +.#{$fa-css-prefix}-arrow-circle-o-down:before { content: $fa-var-arrow-circle-o-down; } +.#{$fa-css-prefix}-arrow-circle-o-up:before { content: $fa-var-arrow-circle-o-up; } +.#{$fa-css-prefix}-inbox:before { content: $fa-var-inbox; } +.#{$fa-css-prefix}-play-circle-o:before { content: $fa-var-play-circle-o; } +.#{$fa-css-prefix}-rotate-right:before, +.#{$fa-css-prefix}-repeat:before { content: $fa-var-repeat; } +.#{$fa-css-prefix}-refresh:before { content: $fa-var-refresh; } +.#{$fa-css-prefix}-list-alt:before { content: $fa-var-list-alt; } +.#{$fa-css-prefix}-lock:before { content: $fa-var-lock; } +.#{$fa-css-prefix}-flag:before { content: $fa-var-flag; } +.#{$fa-css-prefix}-headphones:before { content: $fa-var-headphones; } +.#{$fa-css-prefix}-volume-off:before { content: $fa-var-volume-off; } +.#{$fa-css-prefix}-volume-down:before { content: $fa-var-volume-down; } +.#{$fa-css-prefix}-volume-up:before { content: $fa-var-volume-up; } +.#{$fa-css-prefix}-qrcode:before { content: $fa-var-qrcode; } +.#{$fa-css-prefix}-barcode:before { content: $fa-var-barcode; } +.#{$fa-css-prefix}-tag:before { content: $fa-var-tag; } +.#{$fa-css-prefix}-tags:before { content: $fa-var-tags; } +.#{$fa-css-prefix}-book:before { content: $fa-var-book; } +.#{$fa-css-prefix}-bookmark:before { content: $fa-var-bookmark; } +.#{$fa-css-prefix}-print:before { content: $fa-var-print; } +.#{$fa-css-prefix}-camera:before { content: $fa-var-camera; } +.#{$fa-css-prefix}-font:before { content: $fa-var-font; } +.#{$fa-css-prefix}-bold:before { content: $fa-var-bold; } +.#{$fa-css-prefix}-italic:before { content: $fa-var-italic; } +.#{$fa-css-prefix}-text-height:before { content: $fa-var-text-height; } +.#{$fa-css-prefix}-text-width:before { content: $fa-var-text-width; } +.#{$fa-css-prefix}-align-left:before { content: $fa-var-align-left; } +.#{$fa-css-prefix}-align-center:before { content: $fa-var-align-center; } +.#{$fa-css-prefix}-align-right:before { content: $fa-var-align-right; } +.#{$fa-css-prefix}-align-justify:before { content: $fa-var-align-justify; } +.#{$fa-css-prefix}-list:before { content: $fa-var-list; } +.#{$fa-css-prefix}-dedent:before, +.#{$fa-css-prefix}-outdent:before { content: $fa-var-outdent; } +.#{$fa-css-prefix}-indent:before { content: $fa-var-indent; } +.#{$fa-css-prefix}-video-camera:before { content: $fa-var-video-camera; } +.#{$fa-css-prefix}-photo:before, +.#{$fa-css-prefix}-image:before, +.#{$fa-css-prefix}-picture-o:before { content: $fa-var-picture-o; } +.#{$fa-css-prefix}-pencil:before { content: $fa-var-pencil; } +.#{$fa-css-prefix}-map-marker:before { content: $fa-var-map-marker; } +.#{$fa-css-prefix}-adjust:before { content: $fa-var-adjust; } +.#{$fa-css-prefix}-tint:before { content: $fa-var-tint; } +.#{$fa-css-prefix}-edit:before, +.#{$fa-css-prefix}-pencil-square-o:before { content: $fa-var-pencil-square-o; } +.#{$fa-css-prefix}-share-square-o:before { content: $fa-var-share-square-o; } +.#{$fa-css-prefix}-check-square-o:before { content: $fa-var-check-square-o; } +.#{$fa-css-prefix}-arrows:before { content: $fa-var-arrows; } +.#{$fa-css-prefix}-step-backward:before { content: $fa-var-step-backward; } +.#{$fa-css-prefix}-fast-backward:before { content: $fa-var-fast-backward; } +.#{$fa-css-prefix}-backward:before { content: $fa-var-backward; } +.#{$fa-css-prefix}-play:before { content: $fa-var-play; } +.#{$fa-css-prefix}-pause:before { content: $fa-var-pause; } +.#{$fa-css-prefix}-stop:before { content: $fa-var-stop; } +.#{$fa-css-prefix}-forward:before { content: $fa-var-forward; } +.#{$fa-css-prefix}-fast-forward:before { content: $fa-var-fast-forward; } +.#{$fa-css-prefix}-step-forward:before { content: $fa-var-step-forward; } +.#{$fa-css-prefix}-eject:before { content: $fa-var-eject; } +.#{$fa-css-prefix}-chevron-left:before { content: $fa-var-chevron-left; } +.#{$fa-css-prefix}-chevron-right:before { content: $fa-var-chevron-right; } +.#{$fa-css-prefix}-plus-circle:before { content: $fa-var-plus-circle; } +.#{$fa-css-prefix}-minus-circle:before { content: $fa-var-minus-circle; } +.#{$fa-css-prefix}-times-circle:before { content: $fa-var-times-circle; } +.#{$fa-css-prefix}-check-circle:before { content: $fa-var-check-circle; } +.#{$fa-css-prefix}-question-circle:before { content: $fa-var-question-circle; } +.#{$fa-css-prefix}-info-circle:before { content: $fa-var-info-circle; } +.#{$fa-css-prefix}-crosshairs:before { content: $fa-var-crosshairs; } +.#{$fa-css-prefix}-times-circle-o:before { content: $fa-var-times-circle-o; } +.#{$fa-css-prefix}-check-circle-o:before { content: $fa-var-check-circle-o; } +.#{$fa-css-prefix}-ban:before { content: $fa-var-ban; } +.#{$fa-css-prefix}-arrow-left:before { content: $fa-var-arrow-left; } +.#{$fa-css-prefix}-arrow-right:before { content: $fa-var-arrow-right; } +.#{$fa-css-prefix}-arrow-up:before { content: $fa-var-arrow-up; } +.#{$fa-css-prefix}-arrow-down:before { content: $fa-var-arrow-down; } +.#{$fa-css-prefix}-mail-forward:before, +.#{$fa-css-prefix}-share:before { content: $fa-var-share; } +.#{$fa-css-prefix}-expand:before { content: $fa-var-expand; } +.#{$fa-css-prefix}-compress:before { content: $fa-var-compress; } +.#{$fa-css-prefix}-plus:before { content: $fa-var-plus; } +.#{$fa-css-prefix}-minus:before { content: $fa-var-minus; } +.#{$fa-css-prefix}-asterisk:before { content: $fa-var-asterisk; } +.#{$fa-css-prefix}-exclamation-circle:before { content: $fa-var-exclamation-circle; } +.#{$fa-css-prefix}-gift:before { content: $fa-var-gift; } +.#{$fa-css-prefix}-leaf:before { content: $fa-var-leaf; } +.#{$fa-css-prefix}-fire:before { content: $fa-var-fire; } +.#{$fa-css-prefix}-eye:before { content: $fa-var-eye; } +.#{$fa-css-prefix}-eye-slash:before { content: $fa-var-eye-slash; } +.#{$fa-css-prefix}-warning:before, +.#{$fa-css-prefix}-exclamation-triangle:before { content: $fa-var-exclamation-triangle; } +.#{$fa-css-prefix}-plane:before { content: $fa-var-plane; } +.#{$fa-css-prefix}-calendar:before { content: $fa-var-calendar; } +.#{$fa-css-prefix}-random:before { content: $fa-var-random; } +.#{$fa-css-prefix}-comment:before { content: $fa-var-comment; } +.#{$fa-css-prefix}-magnet:before { content: $fa-var-magnet; } +.#{$fa-css-prefix}-chevron-up:before { content: $fa-var-chevron-up; } +.#{$fa-css-prefix}-chevron-down:before { content: $fa-var-chevron-down; } +.#{$fa-css-prefix}-retweet:before { content: $fa-var-retweet; } +.#{$fa-css-prefix}-shopping-cart:before { content: $fa-var-shopping-cart; } +.#{$fa-css-prefix}-folder:before { content: $fa-var-folder; } +.#{$fa-css-prefix}-folder-open:before { content: $fa-var-folder-open; } +.#{$fa-css-prefix}-arrows-v:before { content: $fa-var-arrows-v; } +.#{$fa-css-prefix}-arrows-h:before { content: $fa-var-arrows-h; } +.#{$fa-css-prefix}-bar-chart-o:before, +.#{$fa-css-prefix}-bar-chart:before { content: $fa-var-bar-chart; } +.#{$fa-css-prefix}-twitter-square:before { content: $fa-var-twitter-square; } +.#{$fa-css-prefix}-facebook-square:before { content: $fa-var-facebook-square; } +.#{$fa-css-prefix}-camera-retro:before { content: $fa-var-camera-retro; } +.#{$fa-css-prefix}-key:before { content: $fa-var-key; } +.#{$fa-css-prefix}-gears:before, +.#{$fa-css-prefix}-cogs:before { content: $fa-var-cogs; } +.#{$fa-css-prefix}-comments:before { content: $fa-var-comments; } +.#{$fa-css-prefix}-thumbs-o-up:before { content: $fa-var-thumbs-o-up; } +.#{$fa-css-prefix}-thumbs-o-down:before { content: $fa-var-thumbs-o-down; } +.#{$fa-css-prefix}-star-half:before { content: $fa-var-star-half; } +.#{$fa-css-prefix}-heart-o:before { content: $fa-var-heart-o; } +.#{$fa-css-prefix}-sign-out:before { content: $fa-var-sign-out; } +.#{$fa-css-prefix}-linkedin-square:before { content: $fa-var-linkedin-square; } +.#{$fa-css-prefix}-thumb-tack:before { content: $fa-var-thumb-tack; } +.#{$fa-css-prefix}-external-link:before { content: $fa-var-external-link; } +.#{$fa-css-prefix}-sign-in:before { content: $fa-var-sign-in; } +.#{$fa-css-prefix}-trophy:before { content: $fa-var-trophy; } +.#{$fa-css-prefix}-github-square:before { content: $fa-var-github-square; } +.#{$fa-css-prefix}-upload:before { content: $fa-var-upload; } +.#{$fa-css-prefix}-lemon-o:before { content: $fa-var-lemon-o; } +.#{$fa-css-prefix}-phone:before { content: $fa-var-phone; } +.#{$fa-css-prefix}-square-o:before { content: $fa-var-square-o; } +.#{$fa-css-prefix}-bookmark-o:before { content: $fa-var-bookmark-o; } +.#{$fa-css-prefix}-phone-square:before { content: $fa-var-phone-square; } +.#{$fa-css-prefix}-twitter:before { content: $fa-var-twitter; } +.#{$fa-css-prefix}-facebook-f:before, +.#{$fa-css-prefix}-facebook:before { content: $fa-var-facebook; } +.#{$fa-css-prefix}-github:before { content: $fa-var-github; } +.#{$fa-css-prefix}-unlock:before { content: $fa-var-unlock; } +.#{$fa-css-prefix}-credit-card:before { content: $fa-var-credit-card; } +.#{$fa-css-prefix}-feed:before, +.#{$fa-css-prefix}-rss:before { content: $fa-var-rss; } +.#{$fa-css-prefix}-hdd-o:before { content: $fa-var-hdd-o; } +.#{$fa-css-prefix}-bullhorn:before { content: $fa-var-bullhorn; } +.#{$fa-css-prefix}-bell:before { content: $fa-var-bell; } +.#{$fa-css-prefix}-certificate:before { content: $fa-var-certificate; } +.#{$fa-css-prefix}-hand-o-right:before { content: $fa-var-hand-o-right; } +.#{$fa-css-prefix}-hand-o-left:before { content: $fa-var-hand-o-left; } +.#{$fa-css-prefix}-hand-o-up:before { content: $fa-var-hand-o-up; } +.#{$fa-css-prefix}-hand-o-down:before { content: $fa-var-hand-o-down; } +.#{$fa-css-prefix}-arrow-circle-left:before { content: $fa-var-arrow-circle-left; } +.#{$fa-css-prefix}-arrow-circle-right:before { content: $fa-var-arrow-circle-right; } +.#{$fa-css-prefix}-arrow-circle-up:before { content: $fa-var-arrow-circle-up; } +.#{$fa-css-prefix}-arrow-circle-down:before { content: $fa-var-arrow-circle-down; } +.#{$fa-css-prefix}-globe:before { content: $fa-var-globe; } +.#{$fa-css-prefix}-wrench:before { content: $fa-var-wrench; } +.#{$fa-css-prefix}-tasks:before { content: $fa-var-tasks; } +.#{$fa-css-prefix}-filter:before { content: $fa-var-filter; } +.#{$fa-css-prefix}-briefcase:before { content: $fa-var-briefcase; } +.#{$fa-css-prefix}-arrows-alt:before { content: $fa-var-arrows-alt; } +.#{$fa-css-prefix}-group:before, +.#{$fa-css-prefix}-users:before { content: $fa-var-users; } +.#{$fa-css-prefix}-chain:before, +.#{$fa-css-prefix}-link:before { content: $fa-var-link; } +.#{$fa-css-prefix}-cloud:before { content: $fa-var-cloud; } +.#{$fa-css-prefix}-flask:before { content: $fa-var-flask; } +.#{$fa-css-prefix}-cut:before, +.#{$fa-css-prefix}-scissors:before { content: $fa-var-scissors; } +.#{$fa-css-prefix}-copy:before, +.#{$fa-css-prefix}-files-o:before { content: $fa-var-files-o; } +.#{$fa-css-prefix}-paperclip:before { content: $fa-var-paperclip; } +.#{$fa-css-prefix}-save:before, +.#{$fa-css-prefix}-floppy-o:before { content: $fa-var-floppy-o; } +.#{$fa-css-prefix}-square:before { content: $fa-var-square; } +.#{$fa-css-prefix}-navicon:before, +.#{$fa-css-prefix}-reorder:before, +.#{$fa-css-prefix}-bars:before { content: $fa-var-bars; } +.#{$fa-css-prefix}-list-ul:before { content: $fa-var-list-ul; } +.#{$fa-css-prefix}-list-ol:before { content: $fa-var-list-ol; } +.#{$fa-css-prefix}-strikethrough:before { content: $fa-var-strikethrough; } +.#{$fa-css-prefix}-underline:before { content: $fa-var-underline; } +.#{$fa-css-prefix}-table:before { content: $fa-var-table; } +.#{$fa-css-prefix}-magic:before { content: $fa-var-magic; } +.#{$fa-css-prefix}-truck:before { content: $fa-var-truck; } +.#{$fa-css-prefix}-pinterest:before { content: $fa-var-pinterest; } +.#{$fa-css-prefix}-pinterest-square:before { content: $fa-var-pinterest-square; } +.#{$fa-css-prefix}-google-plus-square:before { content: $fa-var-google-plus-square; } +.#{$fa-css-prefix}-google-plus:before { content: $fa-var-google-plus; } +.#{$fa-css-prefix}-money:before { content: $fa-var-money; } +.#{$fa-css-prefix}-caret-down:before { content: $fa-var-caret-down; } +.#{$fa-css-prefix}-caret-up:before { content: $fa-var-caret-up; } +.#{$fa-css-prefix}-caret-left:before { content: $fa-var-caret-left; } +.#{$fa-css-prefix}-caret-right:before { content: $fa-var-caret-right; } +.#{$fa-css-prefix}-columns:before { content: $fa-var-columns; } +.#{$fa-css-prefix}-unsorted:before, +.#{$fa-css-prefix}-sort:before { content: $fa-var-sort; } +.#{$fa-css-prefix}-sort-down:before, +.#{$fa-css-prefix}-sort-desc:before { content: $fa-var-sort-desc; } +.#{$fa-css-prefix}-sort-up:before, +.#{$fa-css-prefix}-sort-asc:before { content: $fa-var-sort-asc; } +.#{$fa-css-prefix}-envelope:before { content: $fa-var-envelope; } +.#{$fa-css-prefix}-linkedin:before { content: $fa-var-linkedin; } +.#{$fa-css-prefix}-rotate-left:before, +.#{$fa-css-prefix}-undo:before { content: $fa-var-undo; } +.#{$fa-css-prefix}-legal:before, +.#{$fa-css-prefix}-gavel:before { content: $fa-var-gavel; } +.#{$fa-css-prefix}-dashboard:before, +.#{$fa-css-prefix}-tachometer:before { content: $fa-var-tachometer; } +.#{$fa-css-prefix}-comment-o:before { content: $fa-var-comment-o; } +.#{$fa-css-prefix}-comments-o:before { content: $fa-var-comments-o; } +.#{$fa-css-prefix}-flash:before, +.#{$fa-css-prefix}-bolt:before { content: $fa-var-bolt; } +.#{$fa-css-prefix}-sitemap:before { content: $fa-var-sitemap; } +.#{$fa-css-prefix}-umbrella:before { content: $fa-var-umbrella; } +.#{$fa-css-prefix}-paste:before, +.#{$fa-css-prefix}-clipboard:before { content: $fa-var-clipboard; } +.#{$fa-css-prefix}-lightbulb-o:before { content: $fa-var-lightbulb-o; } +.#{$fa-css-prefix}-exchange:before { content: $fa-var-exchange; } +.#{$fa-css-prefix}-cloud-download:before { content: $fa-var-cloud-download; } +.#{$fa-css-prefix}-cloud-upload:before { content: $fa-var-cloud-upload; } +.#{$fa-css-prefix}-user-md:before { content: $fa-var-user-md; } +.#{$fa-css-prefix}-stethoscope:before { content: $fa-var-stethoscope; } +.#{$fa-css-prefix}-suitcase:before { content: $fa-var-suitcase; } +.#{$fa-css-prefix}-bell-o:before { content: $fa-var-bell-o; } +.#{$fa-css-prefix}-coffee:before { content: $fa-var-coffee; } +.#{$fa-css-prefix}-cutlery:before { content: $fa-var-cutlery; } +.#{$fa-css-prefix}-file-text-o:before { content: $fa-var-file-text-o; } +.#{$fa-css-prefix}-building-o:before { content: $fa-var-building-o; } +.#{$fa-css-prefix}-hospital-o:before { content: $fa-var-hospital-o; } +.#{$fa-css-prefix}-ambulance:before { content: $fa-var-ambulance; } +.#{$fa-css-prefix}-medkit:before { content: $fa-var-medkit; } +.#{$fa-css-prefix}-fighter-jet:before { content: $fa-var-fighter-jet; } +.#{$fa-css-prefix}-beer:before { content: $fa-var-beer; } +.#{$fa-css-prefix}-h-square:before { content: $fa-var-h-square; } +.#{$fa-css-prefix}-plus-square:before { content: $fa-var-plus-square; } +.#{$fa-css-prefix}-angle-double-left:before { content: $fa-var-angle-double-left; } +.#{$fa-css-prefix}-angle-double-right:before { content: $fa-var-angle-double-right; } +.#{$fa-css-prefix}-angle-double-up:before { content: $fa-var-angle-double-up; } +.#{$fa-css-prefix}-angle-double-down:before { content: $fa-var-angle-double-down; } +.#{$fa-css-prefix}-angle-left:before { content: $fa-var-angle-left; } +.#{$fa-css-prefix}-angle-right:before { content: $fa-var-angle-right; } +.#{$fa-css-prefix}-angle-up:before { content: $fa-var-angle-up; } +.#{$fa-css-prefix}-angle-down:before { content: $fa-var-angle-down; } +.#{$fa-css-prefix}-desktop:before { content: $fa-var-desktop; } +.#{$fa-css-prefix}-laptop:before { content: $fa-var-laptop; } +.#{$fa-css-prefix}-tablet:before { content: $fa-var-tablet; } +.#{$fa-css-prefix}-mobile-phone:before, +.#{$fa-css-prefix}-mobile:before { content: $fa-var-mobile; } +.#{$fa-css-prefix}-circle-o:before { content: $fa-var-circle-o; } +.#{$fa-css-prefix}-quote-left:before { content: $fa-var-quote-left; } +.#{$fa-css-prefix}-quote-right:before { content: $fa-var-quote-right; } +.#{$fa-css-prefix}-spinner:before { content: $fa-var-spinner; } +.#{$fa-css-prefix}-circle:before { content: $fa-var-circle; } +.#{$fa-css-prefix}-mail-reply:before, +.#{$fa-css-prefix}-reply:before { content: $fa-var-reply; } +.#{$fa-css-prefix}-github-alt:before { content: $fa-var-github-alt; } +.#{$fa-css-prefix}-folder-o:before { content: $fa-var-folder-o; } +.#{$fa-css-prefix}-folder-open-o:before { content: $fa-var-folder-open-o; } +.#{$fa-css-prefix}-smile-o:before { content: $fa-var-smile-o; } +.#{$fa-css-prefix}-frown-o:before { content: $fa-var-frown-o; } +.#{$fa-css-prefix}-meh-o:before { content: $fa-var-meh-o; } +.#{$fa-css-prefix}-gamepad:before { content: $fa-var-gamepad; } +.#{$fa-css-prefix}-keyboard-o:before { content: $fa-var-keyboard-o; } +.#{$fa-css-prefix}-flag-o:before { content: $fa-var-flag-o; } +.#{$fa-css-prefix}-flag-checkered:before { content: $fa-var-flag-checkered; } +.#{$fa-css-prefix}-terminal:before { content: $fa-var-terminal; } +.#{$fa-css-prefix}-code:before { content: $fa-var-code; } +.#{$fa-css-prefix}-mail-reply-all:before, +.#{$fa-css-prefix}-reply-all:before { content: $fa-var-reply-all; } +.#{$fa-css-prefix}-star-half-empty:before, +.#{$fa-css-prefix}-star-half-full:before, +.#{$fa-css-prefix}-star-half-o:before { content: $fa-var-star-half-o; } +.#{$fa-css-prefix}-location-arrow:before { content: $fa-var-location-arrow; } +.#{$fa-css-prefix}-crop:before { content: $fa-var-crop; } +.#{$fa-css-prefix}-code-fork:before { content: $fa-var-code-fork; } +.#{$fa-css-prefix}-unlink:before, +.#{$fa-css-prefix}-chain-broken:before { content: $fa-var-chain-broken; } +.#{$fa-css-prefix}-question:before { content: $fa-var-question; } +.#{$fa-css-prefix}-info:before { content: $fa-var-info; } +.#{$fa-css-prefix}-exclamation:before { content: $fa-var-exclamation; } +.#{$fa-css-prefix}-superscript:before { content: $fa-var-superscript; } +.#{$fa-css-prefix}-subscript:before { content: $fa-var-subscript; } +.#{$fa-css-prefix}-eraser:before { content: $fa-var-eraser; } +.#{$fa-css-prefix}-puzzle-piece:before { content: $fa-var-puzzle-piece; } +.#{$fa-css-prefix}-microphone:before { content: $fa-var-microphone; } +.#{$fa-css-prefix}-microphone-slash:before { content: $fa-var-microphone-slash; } +.#{$fa-css-prefix}-shield:before { content: $fa-var-shield; } +.#{$fa-css-prefix}-calendar-o:before { content: $fa-var-calendar-o; } +.#{$fa-css-prefix}-fire-extinguisher:before { content: $fa-var-fire-extinguisher; } +.#{$fa-css-prefix}-rocket:before { content: $fa-var-rocket; } +.#{$fa-css-prefix}-maxcdn:before { content: $fa-var-maxcdn; } +.#{$fa-css-prefix}-chevron-circle-left:before { content: $fa-var-chevron-circle-left; } +.#{$fa-css-prefix}-chevron-circle-right:before { content: $fa-var-chevron-circle-right; } +.#{$fa-css-prefix}-chevron-circle-up:before { content: $fa-var-chevron-circle-up; } +.#{$fa-css-prefix}-chevron-circle-down:before { content: $fa-var-chevron-circle-down; } +.#{$fa-css-prefix}-html5:before { content: $fa-var-html5; } +.#{$fa-css-prefix}-css3:before { content: $fa-var-css3; } +.#{$fa-css-prefix}-anchor:before { content: $fa-var-anchor; } +.#{$fa-css-prefix}-unlock-alt:before { content: $fa-var-unlock-alt; } +.#{$fa-css-prefix}-bullseye:before { content: $fa-var-bullseye; } +.#{$fa-css-prefix}-ellipsis-h:before { content: $fa-var-ellipsis-h; } +.#{$fa-css-prefix}-ellipsis-v:before { content: $fa-var-ellipsis-v; } +.#{$fa-css-prefix}-rss-square:before { content: $fa-var-rss-square; } +.#{$fa-css-prefix}-play-circle:before { content: $fa-var-play-circle; } +.#{$fa-css-prefix}-ticket:before { content: $fa-var-ticket; } +.#{$fa-css-prefix}-minus-square:before { content: $fa-var-minus-square; } +.#{$fa-css-prefix}-minus-square-o:before { content: $fa-var-minus-square-o; } +.#{$fa-css-prefix}-level-up:before { content: $fa-var-level-up; } +.#{$fa-css-prefix}-level-down:before { content: $fa-var-level-down; } +.#{$fa-css-prefix}-check-square:before { content: $fa-var-check-square; } +.#{$fa-css-prefix}-pencil-square:before { content: $fa-var-pencil-square; } +.#{$fa-css-prefix}-external-link-square:before { content: $fa-var-external-link-square; } +.#{$fa-css-prefix}-share-square:before { content: $fa-var-share-square; } +.#{$fa-css-prefix}-compass:before { content: $fa-var-compass; } +.#{$fa-css-prefix}-toggle-down:before, +.#{$fa-css-prefix}-caret-square-o-down:before { content: $fa-var-caret-square-o-down; } +.#{$fa-css-prefix}-toggle-up:before, +.#{$fa-css-prefix}-caret-square-o-up:before { content: $fa-var-caret-square-o-up; } +.#{$fa-css-prefix}-toggle-right:before, +.#{$fa-css-prefix}-caret-square-o-right:before { content: $fa-var-caret-square-o-right; } +.#{$fa-css-prefix}-euro:before, +.#{$fa-css-prefix}-eur:before { content: $fa-var-eur; } +.#{$fa-css-prefix}-gbp:before { content: $fa-var-gbp; } +.#{$fa-css-prefix}-dollar:before, +.#{$fa-css-prefix}-usd:before { content: $fa-var-usd; } +.#{$fa-css-prefix}-rupee:before, +.#{$fa-css-prefix}-inr:before { content: $fa-var-inr; } +.#{$fa-css-prefix}-cny:before, +.#{$fa-css-prefix}-rmb:before, +.#{$fa-css-prefix}-yen:before, +.#{$fa-css-prefix}-jpy:before { content: $fa-var-jpy; } +.#{$fa-css-prefix}-ruble:before, +.#{$fa-css-prefix}-rouble:before, +.#{$fa-css-prefix}-rub:before { content: $fa-var-rub; } +.#{$fa-css-prefix}-won:before, +.#{$fa-css-prefix}-krw:before { content: $fa-var-krw; } +.#{$fa-css-prefix}-bitcoin:before, +.#{$fa-css-prefix}-btc:before { content: $fa-var-btc; } +.#{$fa-css-prefix}-file:before { content: $fa-var-file; } +.#{$fa-css-prefix}-file-text:before { content: $fa-var-file-text; } +.#{$fa-css-prefix}-sort-alpha-asc:before { content: $fa-var-sort-alpha-asc; } +.#{$fa-css-prefix}-sort-alpha-desc:before { content: $fa-var-sort-alpha-desc; } +.#{$fa-css-prefix}-sort-amount-asc:before { content: $fa-var-sort-amount-asc; } +.#{$fa-css-prefix}-sort-amount-desc:before { content: $fa-var-sort-amount-desc; } +.#{$fa-css-prefix}-sort-numeric-asc:before { content: $fa-var-sort-numeric-asc; } +.#{$fa-css-prefix}-sort-numeric-desc:before { content: $fa-var-sort-numeric-desc; } +.#{$fa-css-prefix}-thumbs-up:before { content: $fa-var-thumbs-up; } +.#{$fa-css-prefix}-thumbs-down:before { content: $fa-var-thumbs-down; } +.#{$fa-css-prefix}-youtube-square:before { content: $fa-var-youtube-square; } +.#{$fa-css-prefix}-youtube:before { content: $fa-var-youtube; } +.#{$fa-css-prefix}-xing:before { content: $fa-var-xing; } +.#{$fa-css-prefix}-xing-square:before { content: $fa-var-xing-square; } +.#{$fa-css-prefix}-youtube-play:before { content: $fa-var-youtube-play; } +.#{$fa-css-prefix}-dropbox:before { content: $fa-var-dropbox; } +.#{$fa-css-prefix}-stack-overflow:before { content: $fa-var-stack-overflow; } +.#{$fa-css-prefix}-instagram:before { content: $fa-var-instagram; } +.#{$fa-css-prefix}-flickr:before { content: $fa-var-flickr; } +.#{$fa-css-prefix}-adn:before { content: $fa-var-adn; } +.#{$fa-css-prefix}-bitbucket:before { content: $fa-var-bitbucket; } +.#{$fa-css-prefix}-bitbucket-square:before { content: $fa-var-bitbucket-square; } +.#{$fa-css-prefix}-tumblr:before { content: $fa-var-tumblr; } +.#{$fa-css-prefix}-tumblr-square:before { content: $fa-var-tumblr-square; } +.#{$fa-css-prefix}-long-arrow-down:before { content: $fa-var-long-arrow-down; } +.#{$fa-css-prefix}-long-arrow-up:before { content: $fa-var-long-arrow-up; } +.#{$fa-css-prefix}-long-arrow-left:before { content: $fa-var-long-arrow-left; } +.#{$fa-css-prefix}-long-arrow-right:before { content: $fa-var-long-arrow-right; } +.#{$fa-css-prefix}-apple:before { content: $fa-var-apple; } +.#{$fa-css-prefix}-windows:before { content: $fa-var-windows; } +.#{$fa-css-prefix}-android:before { content: $fa-var-android; } +.#{$fa-css-prefix}-linux:before { content: $fa-var-linux; } +.#{$fa-css-prefix}-dribbble:before { content: $fa-var-dribbble; } +.#{$fa-css-prefix}-skype:before { content: $fa-var-skype; } +.#{$fa-css-prefix}-foursquare:before { content: $fa-var-foursquare; } +.#{$fa-css-prefix}-trello:before { content: $fa-var-trello; } +.#{$fa-css-prefix}-female:before { content: $fa-var-female; } +.#{$fa-css-prefix}-male:before { content: $fa-var-male; } +.#{$fa-css-prefix}-gittip:before, +.#{$fa-css-prefix}-gratipay:before { content: $fa-var-gratipay; } +.#{$fa-css-prefix}-sun-o:before { content: $fa-var-sun-o; } +.#{$fa-css-prefix}-moon-o:before { content: $fa-var-moon-o; } +.#{$fa-css-prefix}-archive:before { content: $fa-var-archive; } +.#{$fa-css-prefix}-bug:before { content: $fa-var-bug; } +.#{$fa-css-prefix}-vk:before { content: $fa-var-vk; } +.#{$fa-css-prefix}-weibo:before { content: $fa-var-weibo; } +.#{$fa-css-prefix}-renren:before { content: $fa-var-renren; } +.#{$fa-css-prefix}-pagelines:before { content: $fa-var-pagelines; } +.#{$fa-css-prefix}-stack-exchange:before { content: $fa-var-stack-exchange; } +.#{$fa-css-prefix}-arrow-circle-o-right:before { content: $fa-var-arrow-circle-o-right; } +.#{$fa-css-prefix}-arrow-circle-o-left:before { content: $fa-var-arrow-circle-o-left; } +.#{$fa-css-prefix}-toggle-left:before, +.#{$fa-css-prefix}-caret-square-o-left:before { content: $fa-var-caret-square-o-left; } +.#{$fa-css-prefix}-dot-circle-o:before { content: $fa-var-dot-circle-o; } +.#{$fa-css-prefix}-wheelchair:before { content: $fa-var-wheelchair; } +.#{$fa-css-prefix}-vimeo-square:before { content: $fa-var-vimeo-square; } +.#{$fa-css-prefix}-turkish-lira:before, +.#{$fa-css-prefix}-try:before { content: $fa-var-try; } +.#{$fa-css-prefix}-plus-square-o:before { content: $fa-var-plus-square-o; } +.#{$fa-css-prefix}-space-shuttle:before { content: $fa-var-space-shuttle; } +.#{$fa-css-prefix}-slack:before { content: $fa-var-slack; } +.#{$fa-css-prefix}-envelope-square:before { content: $fa-var-envelope-square; } +.#{$fa-css-prefix}-wordpress:before { content: $fa-var-wordpress; } +.#{$fa-css-prefix}-openid:before { content: $fa-var-openid; } +.#{$fa-css-prefix}-institution:before, +.#{$fa-css-prefix}-bank:before, +.#{$fa-css-prefix}-university:before { content: $fa-var-university; } +.#{$fa-css-prefix}-mortar-board:before, +.#{$fa-css-prefix}-graduation-cap:before { content: $fa-var-graduation-cap; } +.#{$fa-css-prefix}-yahoo:before { content: $fa-var-yahoo; } +.#{$fa-css-prefix}-google:before { content: $fa-var-google; } +.#{$fa-css-prefix}-reddit:before { content: $fa-var-reddit; } +.#{$fa-css-prefix}-reddit-square:before { content: $fa-var-reddit-square; } +.#{$fa-css-prefix}-stumbleupon-circle:before { content: $fa-var-stumbleupon-circle; } +.#{$fa-css-prefix}-stumbleupon:before { content: $fa-var-stumbleupon; } +.#{$fa-css-prefix}-delicious:before { content: $fa-var-delicious; } +.#{$fa-css-prefix}-digg:before { content: $fa-var-digg; } +.#{$fa-css-prefix}-pied-piper:before { content: $fa-var-pied-piper; } +.#{$fa-css-prefix}-pied-piper-alt:before { content: $fa-var-pied-piper-alt; } +.#{$fa-css-prefix}-drupal:before { content: $fa-var-drupal; } +.#{$fa-css-prefix}-joomla:before { content: $fa-var-joomla; } +.#{$fa-css-prefix}-language:before { content: $fa-var-language; } +.#{$fa-css-prefix}-fax:before { content: $fa-var-fax; } +.#{$fa-css-prefix}-building:before { content: $fa-var-building; } +.#{$fa-css-prefix}-child:before { content: $fa-var-child; } +.#{$fa-css-prefix}-paw:before { content: $fa-var-paw; } +.#{$fa-css-prefix}-spoon:before { content: $fa-var-spoon; } +.#{$fa-css-prefix}-cube:before { content: $fa-var-cube; } +.#{$fa-css-prefix}-cubes:before { content: $fa-var-cubes; } +.#{$fa-css-prefix}-behance:before { content: $fa-var-behance; } +.#{$fa-css-prefix}-behance-square:before { content: $fa-var-behance-square; } +.#{$fa-css-prefix}-steam:before { content: $fa-var-steam; } +.#{$fa-css-prefix}-steam-square:before { content: $fa-var-steam-square; } +.#{$fa-css-prefix}-recycle:before { content: $fa-var-recycle; } +.#{$fa-css-prefix}-automobile:before, +.#{$fa-css-prefix}-car:before { content: $fa-var-car; } +.#{$fa-css-prefix}-cab:before, +.#{$fa-css-prefix}-taxi:before { content: $fa-var-taxi; } +.#{$fa-css-prefix}-tree:before { content: $fa-var-tree; } +.#{$fa-css-prefix}-spotify:before { content: $fa-var-spotify; } +.#{$fa-css-prefix}-deviantart:before { content: $fa-var-deviantart; } +.#{$fa-css-prefix}-soundcloud:before { content: $fa-var-soundcloud; } +.#{$fa-css-prefix}-database:before { content: $fa-var-database; } +.#{$fa-css-prefix}-file-pdf-o:before { content: $fa-var-file-pdf-o; } +.#{$fa-css-prefix}-file-word-o:before { content: $fa-var-file-word-o; } +.#{$fa-css-prefix}-file-excel-o:before { content: $fa-var-file-excel-o; } +.#{$fa-css-prefix}-file-powerpoint-o:before { content: $fa-var-file-powerpoint-o; } +.#{$fa-css-prefix}-file-photo-o:before, +.#{$fa-css-prefix}-file-picture-o:before, +.#{$fa-css-prefix}-file-image-o:before { content: $fa-var-file-image-o; } +.#{$fa-css-prefix}-file-zip-o:before, +.#{$fa-css-prefix}-file-archive-o:before { content: $fa-var-file-archive-o; } +.#{$fa-css-prefix}-file-sound-o:before, +.#{$fa-css-prefix}-file-audio-o:before { content: $fa-var-file-audio-o; } +.#{$fa-css-prefix}-file-movie-o:before, +.#{$fa-css-prefix}-file-video-o:before { content: $fa-var-file-video-o; } +.#{$fa-css-prefix}-file-code-o:before { content: $fa-var-file-code-o; } +.#{$fa-css-prefix}-vine:before { content: $fa-var-vine; } +.#{$fa-css-prefix}-codepen:before { content: $fa-var-codepen; } +.#{$fa-css-prefix}-jsfiddle:before { content: $fa-var-jsfiddle; } +.#{$fa-css-prefix}-life-bouy:before, +.#{$fa-css-prefix}-life-buoy:before, +.#{$fa-css-prefix}-life-saver:before, +.#{$fa-css-prefix}-support:before, +.#{$fa-css-prefix}-life-ring:before { content: $fa-var-life-ring; } +.#{$fa-css-prefix}-circle-o-notch:before { content: $fa-var-circle-o-notch; } +.#{$fa-css-prefix}-ra:before, +.#{$fa-css-prefix}-rebel:before { content: $fa-var-rebel; } +.#{$fa-css-prefix}-ge:before, +.#{$fa-css-prefix}-empire:before { content: $fa-var-empire; } +.#{$fa-css-prefix}-git-square:before { content: $fa-var-git-square; } +.#{$fa-css-prefix}-git:before { content: $fa-var-git; } +.#{$fa-css-prefix}-y-combinator-square:before, +.#{$fa-css-prefix}-yc-square:before, +.#{$fa-css-prefix}-hacker-news:before { content: $fa-var-hacker-news; } +.#{$fa-css-prefix}-tencent-weibo:before { content: $fa-var-tencent-weibo; } +.#{$fa-css-prefix}-qq:before { content: $fa-var-qq; } +.#{$fa-css-prefix}-wechat:before, +.#{$fa-css-prefix}-weixin:before { content: $fa-var-weixin; } +.#{$fa-css-prefix}-send:before, +.#{$fa-css-prefix}-paper-plane:before { content: $fa-var-paper-plane; } +.#{$fa-css-prefix}-send-o:before, +.#{$fa-css-prefix}-paper-plane-o:before { content: $fa-var-paper-plane-o; } +.#{$fa-css-prefix}-history:before { content: $fa-var-history; } +.#{$fa-css-prefix}-circle-thin:before { content: $fa-var-circle-thin; } +.#{$fa-css-prefix}-header:before { content: $fa-var-header; } +.#{$fa-css-prefix}-paragraph:before { content: $fa-var-paragraph; } +.#{$fa-css-prefix}-sliders:before { content: $fa-var-sliders; } +.#{$fa-css-prefix}-share-alt:before { content: $fa-var-share-alt; } +.#{$fa-css-prefix}-share-alt-square:before { content: $fa-var-share-alt-square; } +.#{$fa-css-prefix}-bomb:before { content: $fa-var-bomb; } +.#{$fa-css-prefix}-soccer-ball-o:before, +.#{$fa-css-prefix}-futbol-o:before { content: $fa-var-futbol-o; } +.#{$fa-css-prefix}-tty:before { content: $fa-var-tty; } +.#{$fa-css-prefix}-binoculars:before { content: $fa-var-binoculars; } +.#{$fa-css-prefix}-plug:before { content: $fa-var-plug; } +.#{$fa-css-prefix}-slideshare:before { content: $fa-var-slideshare; } +.#{$fa-css-prefix}-twitch:before { content: $fa-var-twitch; } +.#{$fa-css-prefix}-yelp:before { content: $fa-var-yelp; } +.#{$fa-css-prefix}-newspaper-o:before { content: $fa-var-newspaper-o; } +.#{$fa-css-prefix}-wifi:before { content: $fa-var-wifi; } +.#{$fa-css-prefix}-calculator:before { content: $fa-var-calculator; } +.#{$fa-css-prefix}-paypal:before { content: $fa-var-paypal; } +.#{$fa-css-prefix}-google-wallet:before { content: $fa-var-google-wallet; } +.#{$fa-css-prefix}-cc-visa:before { content: $fa-var-cc-visa; } +.#{$fa-css-prefix}-cc-mastercard:before { content: $fa-var-cc-mastercard; } +.#{$fa-css-prefix}-cc-discover:before { content: $fa-var-cc-discover; } +.#{$fa-css-prefix}-cc-amex:before { content: $fa-var-cc-amex; } +.#{$fa-css-prefix}-cc-paypal:before { content: $fa-var-cc-paypal; } +.#{$fa-css-prefix}-cc-stripe:before { content: $fa-var-cc-stripe; } +.#{$fa-css-prefix}-bell-slash:before { content: $fa-var-bell-slash; } +.#{$fa-css-prefix}-bell-slash-o:before { content: $fa-var-bell-slash-o; } +.#{$fa-css-prefix}-trash:before { content: $fa-var-trash; } +.#{$fa-css-prefix}-copyright:before { content: $fa-var-copyright; } +.#{$fa-css-prefix}-at:before { content: $fa-var-at; } +.#{$fa-css-prefix}-eyedropper:before { content: $fa-var-eyedropper; } +.#{$fa-css-prefix}-paint-brush:before { content: $fa-var-paint-brush; } +.#{$fa-css-prefix}-birthday-cake:before { content: $fa-var-birthday-cake; } +.#{$fa-css-prefix}-area-chart:before { content: $fa-var-area-chart; } +.#{$fa-css-prefix}-pie-chart:before { content: $fa-var-pie-chart; } +.#{$fa-css-prefix}-line-chart:before { content: $fa-var-line-chart; } +.#{$fa-css-prefix}-lastfm:before { content: $fa-var-lastfm; } +.#{$fa-css-prefix}-lastfm-square:before { content: $fa-var-lastfm-square; } +.#{$fa-css-prefix}-toggle-off:before { content: $fa-var-toggle-off; } +.#{$fa-css-prefix}-toggle-on:before { content: $fa-var-toggle-on; } +.#{$fa-css-prefix}-bicycle:before { content: $fa-var-bicycle; } +.#{$fa-css-prefix}-bus:before { content: $fa-var-bus; } +.#{$fa-css-prefix}-ioxhost:before { content: $fa-var-ioxhost; } +.#{$fa-css-prefix}-angellist:before { content: $fa-var-angellist; } +.#{$fa-css-prefix}-cc:before { content: $fa-var-cc; } +.#{$fa-css-prefix}-shekel:before, +.#{$fa-css-prefix}-sheqel:before, +.#{$fa-css-prefix}-ils:before { content: $fa-var-ils; } +.#{$fa-css-prefix}-meanpath:before { content: $fa-var-meanpath; } +.#{$fa-css-prefix}-buysellads:before { content: $fa-var-buysellads; } +.#{$fa-css-prefix}-connectdevelop:before { content: $fa-var-connectdevelop; } +.#{$fa-css-prefix}-dashcube:before { content: $fa-var-dashcube; } +.#{$fa-css-prefix}-forumbee:before { content: $fa-var-forumbee; } +.#{$fa-css-prefix}-leanpub:before { content: $fa-var-leanpub; } +.#{$fa-css-prefix}-sellsy:before { content: $fa-var-sellsy; } +.#{$fa-css-prefix}-shirtsinbulk:before { content: $fa-var-shirtsinbulk; } +.#{$fa-css-prefix}-simplybuilt:before { content: $fa-var-simplybuilt; } +.#{$fa-css-prefix}-skyatlas:before { content: $fa-var-skyatlas; } +.#{$fa-css-prefix}-cart-plus:before { content: $fa-var-cart-plus; } +.#{$fa-css-prefix}-cart-arrow-down:before { content: $fa-var-cart-arrow-down; } +.#{$fa-css-prefix}-diamond:before { content: $fa-var-diamond; } +.#{$fa-css-prefix}-ship:before { content: $fa-var-ship; } +.#{$fa-css-prefix}-user-secret:before { content: $fa-var-user-secret; } +.#{$fa-css-prefix}-motorcycle:before { content: $fa-var-motorcycle; } +.#{$fa-css-prefix}-street-view:before { content: $fa-var-street-view; } +.#{$fa-css-prefix}-heartbeat:before { content: $fa-var-heartbeat; } +.#{$fa-css-prefix}-venus:before { content: $fa-var-venus; } +.#{$fa-css-prefix}-mars:before { content: $fa-var-mars; } +.#{$fa-css-prefix}-mercury:before { content: $fa-var-mercury; } +.#{$fa-css-prefix}-intersex:before, +.#{$fa-css-prefix}-transgender:before { content: $fa-var-transgender; } +.#{$fa-css-prefix}-transgender-alt:before { content: $fa-var-transgender-alt; } +.#{$fa-css-prefix}-venus-double:before { content: $fa-var-venus-double; } +.#{$fa-css-prefix}-mars-double:before { content: $fa-var-mars-double; } +.#{$fa-css-prefix}-venus-mars:before { content: $fa-var-venus-mars; } +.#{$fa-css-prefix}-mars-stroke:before { content: $fa-var-mars-stroke; } +.#{$fa-css-prefix}-mars-stroke-v:before { content: $fa-var-mars-stroke-v; } +.#{$fa-css-prefix}-mars-stroke-h:before { content: $fa-var-mars-stroke-h; } +.#{$fa-css-prefix}-neuter:before { content: $fa-var-neuter; } +.#{$fa-css-prefix}-genderless:before { content: $fa-var-genderless; } +.#{$fa-css-prefix}-facebook-official:before { content: $fa-var-facebook-official; } +.#{$fa-css-prefix}-pinterest-p:before { content: $fa-var-pinterest-p; } +.#{$fa-css-prefix}-whatsapp:before { content: $fa-var-whatsapp; } +.#{$fa-css-prefix}-server:before { content: $fa-var-server; } +.#{$fa-css-prefix}-user-plus:before { content: $fa-var-user-plus; } +.#{$fa-css-prefix}-user-times:before { content: $fa-var-user-times; } +.#{$fa-css-prefix}-hotel:before, +.#{$fa-css-prefix}-bed:before { content: $fa-var-bed; } +.#{$fa-css-prefix}-viacoin:before { content: $fa-var-viacoin; } +.#{$fa-css-prefix}-train:before { content: $fa-var-train; } +.#{$fa-css-prefix}-subway:before { content: $fa-var-subway; } +.#{$fa-css-prefix}-medium:before { content: $fa-var-medium; } +.#{$fa-css-prefix}-yc:before, +.#{$fa-css-prefix}-y-combinator:before { content: $fa-var-y-combinator; } +.#{$fa-css-prefix}-optin-monster:before { content: $fa-var-optin-monster; } +.#{$fa-css-prefix}-opencart:before { content: $fa-var-opencart; } +.#{$fa-css-prefix}-expeditedssl:before { content: $fa-var-expeditedssl; } +.#{$fa-css-prefix}-battery-4:before, +.#{$fa-css-prefix}-battery-full:before { content: $fa-var-battery-full; } +.#{$fa-css-prefix}-battery-3:before, +.#{$fa-css-prefix}-battery-three-quarters:before { content: $fa-var-battery-three-quarters; } +.#{$fa-css-prefix}-battery-2:before, +.#{$fa-css-prefix}-battery-half:before { content: $fa-var-battery-half; } +.#{$fa-css-prefix}-battery-1:before, +.#{$fa-css-prefix}-battery-quarter:before { content: $fa-var-battery-quarter; } +.#{$fa-css-prefix}-battery-0:before, +.#{$fa-css-prefix}-battery-empty:before { content: $fa-var-battery-empty; } +.#{$fa-css-prefix}-mouse-pointer:before { content: $fa-var-mouse-pointer; } +.#{$fa-css-prefix}-i-cursor:before { content: $fa-var-i-cursor; } +.#{$fa-css-prefix}-object-group:before { content: $fa-var-object-group; } +.#{$fa-css-prefix}-object-ungroup:before { content: $fa-var-object-ungroup; } +.#{$fa-css-prefix}-sticky-note:before { content: $fa-var-sticky-note; } +.#{$fa-css-prefix}-sticky-note-o:before { content: $fa-var-sticky-note-o; } +.#{$fa-css-prefix}-cc-jcb:before { content: $fa-var-cc-jcb; } +.#{$fa-css-prefix}-cc-diners-club:before { content: $fa-var-cc-diners-club; } +.#{$fa-css-prefix}-clone:before { content: $fa-var-clone; } +.#{$fa-css-prefix}-balance-scale:before { content: $fa-var-balance-scale; } +.#{$fa-css-prefix}-hourglass-o:before { content: $fa-var-hourglass-o; } +.#{$fa-css-prefix}-hourglass-1:before, +.#{$fa-css-prefix}-hourglass-start:before { content: $fa-var-hourglass-start; } +.#{$fa-css-prefix}-hourglass-2:before, +.#{$fa-css-prefix}-hourglass-half:before { content: $fa-var-hourglass-half; } +.#{$fa-css-prefix}-hourglass-3:before, +.#{$fa-css-prefix}-hourglass-end:before { content: $fa-var-hourglass-end; } +.#{$fa-css-prefix}-hourglass:before { content: $fa-var-hourglass; } +.#{$fa-css-prefix}-hand-grab-o:before, +.#{$fa-css-prefix}-hand-rock-o:before { content: $fa-var-hand-rock-o; } +.#{$fa-css-prefix}-hand-stop-o:before, +.#{$fa-css-prefix}-hand-paper-o:before { content: $fa-var-hand-paper-o; } +.#{$fa-css-prefix}-hand-scissors-o:before { content: $fa-var-hand-scissors-o; } +.#{$fa-css-prefix}-hand-lizard-o:before { content: $fa-var-hand-lizard-o; } +.#{$fa-css-prefix}-hand-spock-o:before { content: $fa-var-hand-spock-o; } +.#{$fa-css-prefix}-hand-pointer-o:before { content: $fa-var-hand-pointer-o; } +.#{$fa-css-prefix}-hand-peace-o:before { content: $fa-var-hand-peace-o; } +.#{$fa-css-prefix}-trademark:before { content: $fa-var-trademark; } +.#{$fa-css-prefix}-registered:before { content: $fa-var-registered; } +.#{$fa-css-prefix}-creative-commons:before { content: $fa-var-creative-commons; } +.#{$fa-css-prefix}-gg:before { content: $fa-var-gg; } +.#{$fa-css-prefix}-gg-circle:before { content: $fa-var-gg-circle; } +.#{$fa-css-prefix}-tripadvisor:before { content: $fa-var-tripadvisor; } +.#{$fa-css-prefix}-odnoklassniki:before { content: $fa-var-odnoklassniki; } +.#{$fa-css-prefix}-odnoklassniki-square:before { content: $fa-var-odnoklassniki-square; } +.#{$fa-css-prefix}-get-pocket:before { content: $fa-var-get-pocket; } +.#{$fa-css-prefix}-wikipedia-w:before { content: $fa-var-wikipedia-w; } +.#{$fa-css-prefix}-safari:before { content: $fa-var-safari; } +.#{$fa-css-prefix}-chrome:before { content: $fa-var-chrome; } +.#{$fa-css-prefix}-firefox:before { content: $fa-var-firefox; } +.#{$fa-css-prefix}-opera:before { content: $fa-var-opera; } +.#{$fa-css-prefix}-internet-explorer:before { content: $fa-var-internet-explorer; } +.#{$fa-css-prefix}-tv:before, +.#{$fa-css-prefix}-television:before { content: $fa-var-television; } +.#{$fa-css-prefix}-contao:before { content: $fa-var-contao; } +.#{$fa-css-prefix}-500px:before { content: $fa-var-500px; } +.#{$fa-css-prefix}-amazon:before { content: $fa-var-amazon; } +.#{$fa-css-prefix}-calendar-plus-o:before { content: $fa-var-calendar-plus-o; } +.#{$fa-css-prefix}-calendar-minus-o:before { content: $fa-var-calendar-minus-o; } +.#{$fa-css-prefix}-calendar-times-o:before { content: $fa-var-calendar-times-o; } +.#{$fa-css-prefix}-calendar-check-o:before { content: $fa-var-calendar-check-o; } +.#{$fa-css-prefix}-industry:before { content: $fa-var-industry; } +.#{$fa-css-prefix}-map-pin:before { content: $fa-var-map-pin; } +.#{$fa-css-prefix}-map-signs:before { content: $fa-var-map-signs; } +.#{$fa-css-prefix}-map-o:before { content: $fa-var-map-o; } +.#{$fa-css-prefix}-map:before { content: $fa-var-map; } +.#{$fa-css-prefix}-commenting:before { content: $fa-var-commenting; } +.#{$fa-css-prefix}-commenting-o:before { content: $fa-var-commenting-o; } +.#{$fa-css-prefix}-houzz:before { content: $fa-var-houzz; } +.#{$fa-css-prefix}-vimeo:before { content: $fa-var-vimeo; } +.#{$fa-css-prefix}-black-tie:before { content: $fa-var-black-tie; } +.#{$fa-css-prefix}-fonticons:before { content: $fa-var-fonticons; } +.#{$fa-css-prefix}-reddit-alien:before { content: $fa-var-reddit-alien; } +.#{$fa-css-prefix}-edge:before { content: $fa-var-edge; } +.#{$fa-css-prefix}-credit-card-alt:before { content: $fa-var-credit-card-alt; } +.#{$fa-css-prefix}-codiepie:before { content: $fa-var-codiepie; } +.#{$fa-css-prefix}-modx:before { content: $fa-var-modx; } +.#{$fa-css-prefix}-fort-awesome:before { content: $fa-var-fort-awesome; } +.#{$fa-css-prefix}-usb:before { content: $fa-var-usb; } +.#{$fa-css-prefix}-product-hunt:before { content: $fa-var-product-hunt; } +.#{$fa-css-prefix}-mixcloud:before { content: $fa-var-mixcloud; } +.#{$fa-css-prefix}-scribd:before { content: $fa-var-scribd; } +.#{$fa-css-prefix}-pause-circle:before { content: $fa-var-pause-circle; } +.#{$fa-css-prefix}-pause-circle-o:before { content: $fa-var-pause-circle-o; } +.#{$fa-css-prefix}-stop-circle:before { content: $fa-var-stop-circle; } +.#{$fa-css-prefix}-stop-circle-o:before { content: $fa-var-stop-circle-o; } +.#{$fa-css-prefix}-shopping-bag:before { content: $fa-var-shopping-bag; } +.#{$fa-css-prefix}-shopping-basket:before { content: $fa-var-shopping-basket; } +.#{$fa-css-prefix}-hashtag:before { content: $fa-var-hashtag; } +.#{$fa-css-prefix}-bluetooth:before { content: $fa-var-bluetooth; } +.#{$fa-css-prefix}-bluetooth-b:before { content: $fa-var-bluetooth-b; } +.#{$fa-css-prefix}-percent:before { content: $fa-var-percent; } diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_larger.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_larger.scss new file mode 100644 index 0000000000000000000000000000000000000000..41e9a8184aa287c5970cc8415e3c5a6310dc9f79 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_larger.scss @@ -0,0 +1,13 @@ +// Icon Sizes +// ------------------------- + +/* makes the font 33% larger relative to the icon container */ +.#{$fa-css-prefix}-lg { + font-size: (4em / 3); + line-height: (3em / 4); + vertical-align: -15%; +} +.#{$fa-css-prefix}-2x { font-size: 2em; } +.#{$fa-css-prefix}-3x { font-size: 3em; } +.#{$fa-css-prefix}-4x { font-size: 4em; } +.#{$fa-css-prefix}-5x { font-size: 5em; } diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_list.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_list.scss new file mode 100644 index 0000000000000000000000000000000000000000..7d1e4d54d6c293333eb638aa56feba7b62e15564 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_list.scss @@ -0,0 +1,19 @@ +// List Icons +// ------------------------- + +.#{$fa-css-prefix}-ul { + padding-left: 0; + margin-left: $fa-li-width; + list-style-type: none; + > li { position: relative; } +} +.#{$fa-css-prefix}-li { + position: absolute; + left: -$fa-li-width; + width: $fa-li-width; + top: (2em / 14); + text-align: center; + &.#{$fa-css-prefix}-lg { + left: -$fa-li-width + (4em / 14); + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_mixins.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_mixins.scss new file mode 100644 index 0000000000000000000000000000000000000000..f96719b6a03f2e90a2e6452b108bd8bee2323484 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_mixins.scss @@ -0,0 +1,26 @@ +// Mixins +// -------------------------- + +@mixin fa-icon() { + display: inline-block; + font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration + font-size: inherit; // can't have font-size inherit on line above, so need to override + text-rendering: auto; // optimizelegibility throws things off #1094 + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + +} + +@mixin fa-icon-rotate($degrees, $rotation) { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); + -webkit-transform: rotate($degrees); + -ms-transform: rotate($degrees); + transform: rotate($degrees); +} + +@mixin fa-icon-flip($horiz, $vert, $rotation) { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); + -webkit-transform: scale($horiz, $vert); + -ms-transform: scale($horiz, $vert); + transform: scale($horiz, $vert); +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_path.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_path.scss new file mode 100644 index 0000000000000000000000000000000000000000..bb457c23a8e4e0688ebd9383e34ab9f2b3acecab --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_path.scss @@ -0,0 +1,15 @@ +/* FONT PATH + * -------------------------- */ + +@font-face { + font-family: 'FontAwesome'; + src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); + src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), + url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), + url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), + url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), + url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); +// src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts + font-weight: normal; + font-style: normal; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_rotated-flipped.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_rotated-flipped.scss new file mode 100644 index 0000000000000000000000000000000000000000..a3558fd09ca7cb968166d5445f4df1a0bc2d5a7e --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_rotated-flipped.scss @@ -0,0 +1,20 @@ +// Rotated & Flipped Icons +// ------------------------- + +.#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } +.#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } +.#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } + +.#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } +.#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } + +// Hook for IE8-9 +// ------------------------- + +:root .#{$fa-css-prefix}-rotate-90, +:root .#{$fa-css-prefix}-rotate-180, +:root .#{$fa-css-prefix}-rotate-270, +:root .#{$fa-css-prefix}-flip-horizontal, +:root .#{$fa-css-prefix}-flip-vertical { + filter: none; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_stacked.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_stacked.scss new file mode 100644 index 0000000000000000000000000000000000000000..aef7403660c9a2ccc02a264c62c6b105f7d8d532 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_stacked.scss @@ -0,0 +1,20 @@ +// Stacked Icons +// ------------------------- + +.#{$fa-css-prefix}-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.#{$fa-css-prefix}-stack-1x { line-height: inherit; } +.#{$fa-css-prefix}-stack-2x { font-size: 2em; } +.#{$fa-css-prefix}-inverse { color: $fa-inverse; } diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_variables.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_variables.scss new file mode 100644 index 0000000000000000000000000000000000000000..0a471102c4fdf39c7da19a53d8a1ec0a9995452c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/_variables.scss @@ -0,0 +1,708 @@ +// Variables +// -------------------------- + +$fa-font-path: "../fonts" !default; +$fa-font-size-base: 14px !default; +$fa-line-height-base: 1 !default; +//$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.5.0/fonts" !default; // for referencing Bootstrap CDN font files directly +$fa-css-prefix: fa !default; +$fa-version: "4.5.0" !default; +$fa-border-color: #eee !default; +$fa-inverse: #fff !default; +$fa-li-width: (30em / 14) !default; + +$fa-var-500px: "\f26e"; +$fa-var-adjust: "\f042"; +$fa-var-adn: "\f170"; +$fa-var-align-center: "\f037"; +$fa-var-align-justify: "\f039"; +$fa-var-align-left: "\f036"; +$fa-var-align-right: "\f038"; +$fa-var-amazon: "\f270"; +$fa-var-ambulance: "\f0f9"; +$fa-var-anchor: "\f13d"; +$fa-var-android: "\f17b"; +$fa-var-angellist: "\f209"; +$fa-var-angle-double-down: "\f103"; +$fa-var-angle-double-left: "\f100"; +$fa-var-angle-double-right: "\f101"; +$fa-var-angle-double-up: "\f102"; +$fa-var-angle-down: "\f107"; +$fa-var-angle-left: "\f104"; +$fa-var-angle-right: "\f105"; +$fa-var-angle-up: "\f106"; +$fa-var-apple: "\f179"; +$fa-var-archive: "\f187"; +$fa-var-area-chart: "\f1fe"; +$fa-var-arrow-circle-down: "\f0ab"; +$fa-var-arrow-circle-left: "\f0a8"; +$fa-var-arrow-circle-o-down: "\f01a"; +$fa-var-arrow-circle-o-left: "\f190"; +$fa-var-arrow-circle-o-right: "\f18e"; +$fa-var-arrow-circle-o-up: "\f01b"; +$fa-var-arrow-circle-right: "\f0a9"; +$fa-var-arrow-circle-up: "\f0aa"; +$fa-var-arrow-down: "\f063"; +$fa-var-arrow-left: "\f060"; +$fa-var-arrow-right: "\f061"; +$fa-var-arrow-up: "\f062"; +$fa-var-arrows: "\f047"; +$fa-var-arrows-alt: "\f0b2"; +$fa-var-arrows-h: "\f07e"; +$fa-var-arrows-v: "\f07d"; +$fa-var-asterisk: "\f069"; +$fa-var-at: "\f1fa"; +$fa-var-automobile: "\f1b9"; +$fa-var-backward: "\f04a"; +$fa-var-balance-scale: "\f24e"; +$fa-var-ban: "\f05e"; +$fa-var-bank: "\f19c"; +$fa-var-bar-chart: "\f080"; +$fa-var-bar-chart-o: "\f080"; +$fa-var-barcode: "\f02a"; +$fa-var-bars: "\f0c9"; +$fa-var-battery-0: "\f244"; +$fa-var-battery-1: "\f243"; +$fa-var-battery-2: "\f242"; +$fa-var-battery-3: "\f241"; +$fa-var-battery-4: "\f240"; +$fa-var-battery-empty: "\f244"; +$fa-var-battery-full: "\f240"; +$fa-var-battery-half: "\f242"; +$fa-var-battery-quarter: "\f243"; +$fa-var-battery-three-quarters: "\f241"; +$fa-var-bed: "\f236"; +$fa-var-beer: "\f0fc"; +$fa-var-behance: "\f1b4"; +$fa-var-behance-square: "\f1b5"; +$fa-var-bell: "\f0f3"; +$fa-var-bell-o: "\f0a2"; +$fa-var-bell-slash: "\f1f6"; +$fa-var-bell-slash-o: "\f1f7"; +$fa-var-bicycle: "\f206"; +$fa-var-binoculars: "\f1e5"; +$fa-var-birthday-cake: "\f1fd"; +$fa-var-bitbucket: "\f171"; +$fa-var-bitbucket-square: "\f172"; +$fa-var-bitcoin: "\f15a"; +$fa-var-black-tie: "\f27e"; +$fa-var-bluetooth: "\f293"; +$fa-var-bluetooth-b: "\f294"; +$fa-var-bold: "\f032"; +$fa-var-bolt: "\f0e7"; +$fa-var-bomb: "\f1e2"; +$fa-var-book: "\f02d"; +$fa-var-bookmark: "\f02e"; +$fa-var-bookmark-o: "\f097"; +$fa-var-briefcase: "\f0b1"; +$fa-var-btc: "\f15a"; +$fa-var-bug: "\f188"; +$fa-var-building: "\f1ad"; +$fa-var-building-o: "\f0f7"; +$fa-var-bullhorn: "\f0a1"; +$fa-var-bullseye: "\f140"; +$fa-var-bus: "\f207"; +$fa-var-buysellads: "\f20d"; +$fa-var-cab: "\f1ba"; +$fa-var-calculator: "\f1ec"; +$fa-var-calendar: "\f073"; +$fa-var-calendar-check-o: "\f274"; +$fa-var-calendar-minus-o: "\f272"; +$fa-var-calendar-o: "\f133"; +$fa-var-calendar-plus-o: "\f271"; +$fa-var-calendar-times-o: "\f273"; +$fa-var-camera: "\f030"; +$fa-var-camera-retro: "\f083"; +$fa-var-car: "\f1b9"; +$fa-var-caret-down: "\f0d7"; +$fa-var-caret-left: "\f0d9"; +$fa-var-caret-right: "\f0da"; +$fa-var-caret-square-o-down: "\f150"; +$fa-var-caret-square-o-left: "\f191"; +$fa-var-caret-square-o-right: "\f152"; +$fa-var-caret-square-o-up: "\f151"; +$fa-var-caret-up: "\f0d8"; +$fa-var-cart-arrow-down: "\f218"; +$fa-var-cart-plus: "\f217"; +$fa-var-cc: "\f20a"; +$fa-var-cc-amex: "\f1f3"; +$fa-var-cc-diners-club: "\f24c"; +$fa-var-cc-discover: "\f1f2"; +$fa-var-cc-jcb: "\f24b"; +$fa-var-cc-mastercard: "\f1f1"; +$fa-var-cc-paypal: "\f1f4"; +$fa-var-cc-stripe: "\f1f5"; +$fa-var-cc-visa: "\f1f0"; +$fa-var-certificate: "\f0a3"; +$fa-var-chain: "\f0c1"; +$fa-var-chain-broken: "\f127"; +$fa-var-check: "\f00c"; +$fa-var-check-circle: "\f058"; +$fa-var-check-circle-o: "\f05d"; +$fa-var-check-square: "\f14a"; +$fa-var-check-square-o: "\f046"; +$fa-var-chevron-circle-down: "\f13a"; +$fa-var-chevron-circle-left: "\f137"; +$fa-var-chevron-circle-right: "\f138"; +$fa-var-chevron-circle-up: "\f139"; +$fa-var-chevron-down: "\f078"; +$fa-var-chevron-left: "\f053"; +$fa-var-chevron-right: "\f054"; +$fa-var-chevron-up: "\f077"; +$fa-var-child: "\f1ae"; +$fa-var-chrome: "\f268"; +$fa-var-circle: "\f111"; +$fa-var-circle-o: "\f10c"; +$fa-var-circle-o-notch: "\f1ce"; +$fa-var-circle-thin: "\f1db"; +$fa-var-clipboard: "\f0ea"; +$fa-var-clock-o: "\f017"; +$fa-var-clone: "\f24d"; +$fa-var-close: "\f00d"; +$fa-var-cloud: "\f0c2"; +$fa-var-cloud-download: "\f0ed"; +$fa-var-cloud-upload: "\f0ee"; +$fa-var-cny: "\f157"; +$fa-var-code: "\f121"; +$fa-var-code-fork: "\f126"; +$fa-var-codepen: "\f1cb"; +$fa-var-codiepie: "\f284"; +$fa-var-coffee: "\f0f4"; +$fa-var-cog: "\f013"; +$fa-var-cogs: "\f085"; +$fa-var-columns: "\f0db"; +$fa-var-comment: "\f075"; +$fa-var-comment-o: "\f0e5"; +$fa-var-commenting: "\f27a"; +$fa-var-commenting-o: "\f27b"; +$fa-var-comments: "\f086"; +$fa-var-comments-o: "\f0e6"; +$fa-var-compass: "\f14e"; +$fa-var-compress: "\f066"; +$fa-var-connectdevelop: "\f20e"; +$fa-var-contao: "\f26d"; +$fa-var-copy: "\f0c5"; +$fa-var-copyright: "\f1f9"; +$fa-var-creative-commons: "\f25e"; +$fa-var-credit-card: "\f09d"; +$fa-var-credit-card-alt: "\f283"; +$fa-var-crop: "\f125"; +$fa-var-crosshairs: "\f05b"; +$fa-var-css3: "\f13c"; +$fa-var-cube: "\f1b2"; +$fa-var-cubes: "\f1b3"; +$fa-var-cut: "\f0c4"; +$fa-var-cutlery: "\f0f5"; +$fa-var-dashboard: "\f0e4"; +$fa-var-dashcube: "\f210"; +$fa-var-database: "\f1c0"; +$fa-var-dedent: "\f03b"; +$fa-var-delicious: "\f1a5"; +$fa-var-desktop: "\f108"; +$fa-var-deviantart: "\f1bd"; +$fa-var-diamond: "\f219"; +$fa-var-digg: "\f1a6"; +$fa-var-dollar: "\f155"; +$fa-var-dot-circle-o: "\f192"; +$fa-var-download: "\f019"; +$fa-var-dribbble: "\f17d"; +$fa-var-dropbox: "\f16b"; +$fa-var-drupal: "\f1a9"; +$fa-var-edge: "\f282"; +$fa-var-edit: "\f044"; +$fa-var-eject: "\f052"; +$fa-var-ellipsis-h: "\f141"; +$fa-var-ellipsis-v: "\f142"; +$fa-var-empire: "\f1d1"; +$fa-var-envelope: "\f0e0"; +$fa-var-envelope-o: "\f003"; +$fa-var-envelope-square: "\f199"; +$fa-var-eraser: "\f12d"; +$fa-var-eur: "\f153"; +$fa-var-euro: "\f153"; +$fa-var-exchange: "\f0ec"; +$fa-var-exclamation: "\f12a"; +$fa-var-exclamation-circle: "\f06a"; +$fa-var-exclamation-triangle: "\f071"; +$fa-var-expand: "\f065"; +$fa-var-expeditedssl: "\f23e"; +$fa-var-external-link: "\f08e"; +$fa-var-external-link-square: "\f14c"; +$fa-var-eye: "\f06e"; +$fa-var-eye-slash: "\f070"; +$fa-var-eyedropper: "\f1fb"; +$fa-var-facebook: "\f09a"; +$fa-var-facebook-f: "\f09a"; +$fa-var-facebook-official: "\f230"; +$fa-var-facebook-square: "\f082"; +$fa-var-fast-backward: "\f049"; +$fa-var-fast-forward: "\f050"; +$fa-var-fax: "\f1ac"; +$fa-var-feed: "\f09e"; +$fa-var-female: "\f182"; +$fa-var-fighter-jet: "\f0fb"; +$fa-var-file: "\f15b"; +$fa-var-file-archive-o: "\f1c6"; +$fa-var-file-audio-o: "\f1c7"; +$fa-var-file-code-o: "\f1c9"; +$fa-var-file-excel-o: "\f1c3"; +$fa-var-file-image-o: "\f1c5"; +$fa-var-file-movie-o: "\f1c8"; +$fa-var-file-o: "\f016"; +$fa-var-file-pdf-o: "\f1c1"; +$fa-var-file-photo-o: "\f1c5"; +$fa-var-file-picture-o: "\f1c5"; +$fa-var-file-powerpoint-o: "\f1c4"; +$fa-var-file-sound-o: "\f1c7"; +$fa-var-file-text: "\f15c"; +$fa-var-file-text-o: "\f0f6"; +$fa-var-file-video-o: "\f1c8"; +$fa-var-file-word-o: "\f1c2"; +$fa-var-file-zip-o: "\f1c6"; +$fa-var-files-o: "\f0c5"; +$fa-var-film: "\f008"; +$fa-var-filter: "\f0b0"; +$fa-var-fire: "\f06d"; +$fa-var-fire-extinguisher: "\f134"; +$fa-var-firefox: "\f269"; +$fa-var-flag: "\f024"; +$fa-var-flag-checkered: "\f11e"; +$fa-var-flag-o: "\f11d"; +$fa-var-flash: "\f0e7"; +$fa-var-flask: "\f0c3"; +$fa-var-flickr: "\f16e"; +$fa-var-floppy-o: "\f0c7"; +$fa-var-folder: "\f07b"; +$fa-var-folder-o: "\f114"; +$fa-var-folder-open: "\f07c"; +$fa-var-folder-open-o: "\f115"; +$fa-var-font: "\f031"; +$fa-var-fonticons: "\f280"; +$fa-var-fort-awesome: "\f286"; +$fa-var-forumbee: "\f211"; +$fa-var-forward: "\f04e"; +$fa-var-foursquare: "\f180"; +$fa-var-frown-o: "\f119"; +$fa-var-futbol-o: "\f1e3"; +$fa-var-gamepad: "\f11b"; +$fa-var-gavel: "\f0e3"; +$fa-var-gbp: "\f154"; +$fa-var-ge: "\f1d1"; +$fa-var-gear: "\f013"; +$fa-var-gears: "\f085"; +$fa-var-genderless: "\f22d"; +$fa-var-get-pocket: "\f265"; +$fa-var-gg: "\f260"; +$fa-var-gg-circle: "\f261"; +$fa-var-gift: "\f06b"; +$fa-var-git: "\f1d3"; +$fa-var-git-square: "\f1d2"; +$fa-var-github: "\f09b"; +$fa-var-github-alt: "\f113"; +$fa-var-github-square: "\f092"; +$fa-var-gittip: "\f184"; +$fa-var-glass: "\f000"; +$fa-var-globe: "\f0ac"; +$fa-var-google: "\f1a0"; +$fa-var-google-plus: "\f0d5"; +$fa-var-google-plus-square: "\f0d4"; +$fa-var-google-wallet: "\f1ee"; +$fa-var-graduation-cap: "\f19d"; +$fa-var-gratipay: "\f184"; +$fa-var-group: "\f0c0"; +$fa-var-h-square: "\f0fd"; +$fa-var-hacker-news: "\f1d4"; +$fa-var-hand-grab-o: "\f255"; +$fa-var-hand-lizard-o: "\f258"; +$fa-var-hand-o-down: "\f0a7"; +$fa-var-hand-o-left: "\f0a5"; +$fa-var-hand-o-right: "\f0a4"; +$fa-var-hand-o-up: "\f0a6"; +$fa-var-hand-paper-o: "\f256"; +$fa-var-hand-peace-o: "\f25b"; +$fa-var-hand-pointer-o: "\f25a"; +$fa-var-hand-rock-o: "\f255"; +$fa-var-hand-scissors-o: "\f257"; +$fa-var-hand-spock-o: "\f259"; +$fa-var-hand-stop-o: "\f256"; +$fa-var-hashtag: "\f292"; +$fa-var-hdd-o: "\f0a0"; +$fa-var-header: "\f1dc"; +$fa-var-headphones: "\f025"; +$fa-var-heart: "\f004"; +$fa-var-heart-o: "\f08a"; +$fa-var-heartbeat: "\f21e"; +$fa-var-history: "\f1da"; +$fa-var-home: "\f015"; +$fa-var-hospital-o: "\f0f8"; +$fa-var-hotel: "\f236"; +$fa-var-hourglass: "\f254"; +$fa-var-hourglass-1: "\f251"; +$fa-var-hourglass-2: "\f252"; +$fa-var-hourglass-3: "\f253"; +$fa-var-hourglass-end: "\f253"; +$fa-var-hourglass-half: "\f252"; +$fa-var-hourglass-o: "\f250"; +$fa-var-hourglass-start: "\f251"; +$fa-var-houzz: "\f27c"; +$fa-var-html5: "\f13b"; +$fa-var-i-cursor: "\f246"; +$fa-var-ils: "\f20b"; +$fa-var-image: "\f03e"; +$fa-var-inbox: "\f01c"; +$fa-var-indent: "\f03c"; +$fa-var-industry: "\f275"; +$fa-var-info: "\f129"; +$fa-var-info-circle: "\f05a"; +$fa-var-inr: "\f156"; +$fa-var-instagram: "\f16d"; +$fa-var-institution: "\f19c"; +$fa-var-internet-explorer: "\f26b"; +$fa-var-intersex: "\f224"; +$fa-var-ioxhost: "\f208"; +$fa-var-italic: "\f033"; +$fa-var-joomla: "\f1aa"; +$fa-var-jpy: "\f157"; +$fa-var-jsfiddle: "\f1cc"; +$fa-var-key: "\f084"; +$fa-var-keyboard-o: "\f11c"; +$fa-var-krw: "\f159"; +$fa-var-language: "\f1ab"; +$fa-var-laptop: "\f109"; +$fa-var-lastfm: "\f202"; +$fa-var-lastfm-square: "\f203"; +$fa-var-leaf: "\f06c"; +$fa-var-leanpub: "\f212"; +$fa-var-legal: "\f0e3"; +$fa-var-lemon-o: "\f094"; +$fa-var-level-down: "\f149"; +$fa-var-level-up: "\f148"; +$fa-var-life-bouy: "\f1cd"; +$fa-var-life-buoy: "\f1cd"; +$fa-var-life-ring: "\f1cd"; +$fa-var-life-saver: "\f1cd"; +$fa-var-lightbulb-o: "\f0eb"; +$fa-var-line-chart: "\f201"; +$fa-var-link: "\f0c1"; +$fa-var-linkedin: "\f0e1"; +$fa-var-linkedin-square: "\f08c"; +$fa-var-linux: "\f17c"; +$fa-var-list: "\f03a"; +$fa-var-list-alt: "\f022"; +$fa-var-list-ol: "\f0cb"; +$fa-var-list-ul: "\f0ca"; +$fa-var-location-arrow: "\f124"; +$fa-var-lock: "\f023"; +$fa-var-long-arrow-down: "\f175"; +$fa-var-long-arrow-left: "\f177"; +$fa-var-long-arrow-right: "\f178"; +$fa-var-long-arrow-up: "\f176"; +$fa-var-magic: "\f0d0"; +$fa-var-magnet: "\f076"; +$fa-var-mail-forward: "\f064"; +$fa-var-mail-reply: "\f112"; +$fa-var-mail-reply-all: "\f122"; +$fa-var-male: "\f183"; +$fa-var-map: "\f279"; +$fa-var-map-marker: "\f041"; +$fa-var-map-o: "\f278"; +$fa-var-map-pin: "\f276"; +$fa-var-map-signs: "\f277"; +$fa-var-mars: "\f222"; +$fa-var-mars-double: "\f227"; +$fa-var-mars-stroke: "\f229"; +$fa-var-mars-stroke-h: "\f22b"; +$fa-var-mars-stroke-v: "\f22a"; +$fa-var-maxcdn: "\f136"; +$fa-var-meanpath: "\f20c"; +$fa-var-medium: "\f23a"; +$fa-var-medkit: "\f0fa"; +$fa-var-meh-o: "\f11a"; +$fa-var-mercury: "\f223"; +$fa-var-microphone: "\f130"; +$fa-var-microphone-slash: "\f131"; +$fa-var-minus: "\f068"; +$fa-var-minus-circle: "\f056"; +$fa-var-minus-square: "\f146"; +$fa-var-minus-square-o: "\f147"; +$fa-var-mixcloud: "\f289"; +$fa-var-mobile: "\f10b"; +$fa-var-mobile-phone: "\f10b"; +$fa-var-modx: "\f285"; +$fa-var-money: "\f0d6"; +$fa-var-moon-o: "\f186"; +$fa-var-mortar-board: "\f19d"; +$fa-var-motorcycle: "\f21c"; +$fa-var-mouse-pointer: "\f245"; +$fa-var-music: "\f001"; +$fa-var-navicon: "\f0c9"; +$fa-var-neuter: "\f22c"; +$fa-var-newspaper-o: "\f1ea"; +$fa-var-object-group: "\f247"; +$fa-var-object-ungroup: "\f248"; +$fa-var-odnoklassniki: "\f263"; +$fa-var-odnoklassniki-square: "\f264"; +$fa-var-opencart: "\f23d"; +$fa-var-openid: "\f19b"; +$fa-var-opera: "\f26a"; +$fa-var-optin-monster: "\f23c"; +$fa-var-outdent: "\f03b"; +$fa-var-pagelines: "\f18c"; +$fa-var-paint-brush: "\f1fc"; +$fa-var-paper-plane: "\f1d8"; +$fa-var-paper-plane-o: "\f1d9"; +$fa-var-paperclip: "\f0c6"; +$fa-var-paragraph: "\f1dd"; +$fa-var-paste: "\f0ea"; +$fa-var-pause: "\f04c"; +$fa-var-pause-circle: "\f28b"; +$fa-var-pause-circle-o: "\f28c"; +$fa-var-paw: "\f1b0"; +$fa-var-paypal: "\f1ed"; +$fa-var-pencil: "\f040"; +$fa-var-pencil-square: "\f14b"; +$fa-var-pencil-square-o: "\f044"; +$fa-var-percent: "\f295"; +$fa-var-phone: "\f095"; +$fa-var-phone-square: "\f098"; +$fa-var-photo: "\f03e"; +$fa-var-picture-o: "\f03e"; +$fa-var-pie-chart: "\f200"; +$fa-var-pied-piper: "\f1a7"; +$fa-var-pied-piper-alt: "\f1a8"; +$fa-var-pinterest: "\f0d2"; +$fa-var-pinterest-p: "\f231"; +$fa-var-pinterest-square: "\f0d3"; +$fa-var-plane: "\f072"; +$fa-var-play: "\f04b"; +$fa-var-play-circle: "\f144"; +$fa-var-play-circle-o: "\f01d"; +$fa-var-plug: "\f1e6"; +$fa-var-plus: "\f067"; +$fa-var-plus-circle: "\f055"; +$fa-var-plus-square: "\f0fe"; +$fa-var-plus-square-o: "\f196"; +$fa-var-power-off: "\f011"; +$fa-var-print: "\f02f"; +$fa-var-product-hunt: "\f288"; +$fa-var-puzzle-piece: "\f12e"; +$fa-var-qq: "\f1d6"; +$fa-var-qrcode: "\f029"; +$fa-var-question: "\f128"; +$fa-var-question-circle: "\f059"; +$fa-var-quote-left: "\f10d"; +$fa-var-quote-right: "\f10e"; +$fa-var-ra: "\f1d0"; +$fa-var-random: "\f074"; +$fa-var-rebel: "\f1d0"; +$fa-var-recycle: "\f1b8"; +$fa-var-reddit: "\f1a1"; +$fa-var-reddit-alien: "\f281"; +$fa-var-reddit-square: "\f1a2"; +$fa-var-refresh: "\f021"; +$fa-var-registered: "\f25d"; +$fa-var-remove: "\f00d"; +$fa-var-renren: "\f18b"; +$fa-var-reorder: "\f0c9"; +$fa-var-repeat: "\f01e"; +$fa-var-reply: "\f112"; +$fa-var-reply-all: "\f122"; +$fa-var-retweet: "\f079"; +$fa-var-rmb: "\f157"; +$fa-var-road: "\f018"; +$fa-var-rocket: "\f135"; +$fa-var-rotate-left: "\f0e2"; +$fa-var-rotate-right: "\f01e"; +$fa-var-rouble: "\f158"; +$fa-var-rss: "\f09e"; +$fa-var-rss-square: "\f143"; +$fa-var-rub: "\f158"; +$fa-var-ruble: "\f158"; +$fa-var-rupee: "\f156"; +$fa-var-safari: "\f267"; +$fa-var-save: "\f0c7"; +$fa-var-scissors: "\f0c4"; +$fa-var-scribd: "\f28a"; +$fa-var-search: "\f002"; +$fa-var-search-minus: "\f010"; +$fa-var-search-plus: "\f00e"; +$fa-var-sellsy: "\f213"; +$fa-var-send: "\f1d8"; +$fa-var-send-o: "\f1d9"; +$fa-var-server: "\f233"; +$fa-var-share: "\f064"; +$fa-var-share-alt: "\f1e0"; +$fa-var-share-alt-square: "\f1e1"; +$fa-var-share-square: "\f14d"; +$fa-var-share-square-o: "\f045"; +$fa-var-shekel: "\f20b"; +$fa-var-sheqel: "\f20b"; +$fa-var-shield: "\f132"; +$fa-var-ship: "\f21a"; +$fa-var-shirtsinbulk: "\f214"; +$fa-var-shopping-bag: "\f290"; +$fa-var-shopping-basket: "\f291"; +$fa-var-shopping-cart: "\f07a"; +$fa-var-sign-in: "\f090"; +$fa-var-sign-out: "\f08b"; +$fa-var-signal: "\f012"; +$fa-var-simplybuilt: "\f215"; +$fa-var-sitemap: "\f0e8"; +$fa-var-skyatlas: "\f216"; +$fa-var-skype: "\f17e"; +$fa-var-slack: "\f198"; +$fa-var-sliders: "\f1de"; +$fa-var-slideshare: "\f1e7"; +$fa-var-smile-o: "\f118"; +$fa-var-soccer-ball-o: "\f1e3"; +$fa-var-sort: "\f0dc"; +$fa-var-sort-alpha-asc: "\f15d"; +$fa-var-sort-alpha-desc: "\f15e"; +$fa-var-sort-amount-asc: "\f160"; +$fa-var-sort-amount-desc: "\f161"; +$fa-var-sort-asc: "\f0de"; +$fa-var-sort-desc: "\f0dd"; +$fa-var-sort-down: "\f0dd"; +$fa-var-sort-numeric-asc: "\f162"; +$fa-var-sort-numeric-desc: "\f163"; +$fa-var-sort-up: "\f0de"; +$fa-var-soundcloud: "\f1be"; +$fa-var-space-shuttle: "\f197"; +$fa-var-spinner: "\f110"; +$fa-var-spoon: "\f1b1"; +$fa-var-spotify: "\f1bc"; +$fa-var-square: "\f0c8"; +$fa-var-square-o: "\f096"; +$fa-var-stack-exchange: "\f18d"; +$fa-var-stack-overflow: "\f16c"; +$fa-var-star: "\f005"; +$fa-var-star-half: "\f089"; +$fa-var-star-half-empty: "\f123"; +$fa-var-star-half-full: "\f123"; +$fa-var-star-half-o: "\f123"; +$fa-var-star-o: "\f006"; +$fa-var-steam: "\f1b6"; +$fa-var-steam-square: "\f1b7"; +$fa-var-step-backward: "\f048"; +$fa-var-step-forward: "\f051"; +$fa-var-stethoscope: "\f0f1"; +$fa-var-sticky-note: "\f249"; +$fa-var-sticky-note-o: "\f24a"; +$fa-var-stop: "\f04d"; +$fa-var-stop-circle: "\f28d"; +$fa-var-stop-circle-o: "\f28e"; +$fa-var-street-view: "\f21d"; +$fa-var-strikethrough: "\f0cc"; +$fa-var-stumbleupon: "\f1a4"; +$fa-var-stumbleupon-circle: "\f1a3"; +$fa-var-subscript: "\f12c"; +$fa-var-subway: "\f239"; +$fa-var-suitcase: "\f0f2"; +$fa-var-sun-o: "\f185"; +$fa-var-superscript: "\f12b"; +$fa-var-support: "\f1cd"; +$fa-var-table: "\f0ce"; +$fa-var-tablet: "\f10a"; +$fa-var-tachometer: "\f0e4"; +$fa-var-tag: "\f02b"; +$fa-var-tags: "\f02c"; +$fa-var-tasks: "\f0ae"; +$fa-var-taxi: "\f1ba"; +$fa-var-television: "\f26c"; +$fa-var-tencent-weibo: "\f1d5"; +$fa-var-terminal: "\f120"; +$fa-var-text-height: "\f034"; +$fa-var-text-width: "\f035"; +$fa-var-th: "\f00a"; +$fa-var-th-large: "\f009"; +$fa-var-th-list: "\f00b"; +$fa-var-thumb-tack: "\f08d"; +$fa-var-thumbs-down: "\f165"; +$fa-var-thumbs-o-down: "\f088"; +$fa-var-thumbs-o-up: "\f087"; +$fa-var-thumbs-up: "\f164"; +$fa-var-ticket: "\f145"; +$fa-var-times: "\f00d"; +$fa-var-times-circle: "\f057"; +$fa-var-times-circle-o: "\f05c"; +$fa-var-tint: "\f043"; +$fa-var-toggle-down: "\f150"; +$fa-var-toggle-left: "\f191"; +$fa-var-toggle-off: "\f204"; +$fa-var-toggle-on: "\f205"; +$fa-var-toggle-right: "\f152"; +$fa-var-toggle-up: "\f151"; +$fa-var-trademark: "\f25c"; +$fa-var-train: "\f238"; +$fa-var-transgender: "\f224"; +$fa-var-transgender-alt: "\f225"; +$fa-var-trash: "\f1f8"; +$fa-var-trash-o: "\f014"; +$fa-var-tree: "\f1bb"; +$fa-var-trello: "\f181"; +$fa-var-tripadvisor: "\f262"; +$fa-var-trophy: "\f091"; +$fa-var-truck: "\f0d1"; +$fa-var-try: "\f195"; +$fa-var-tty: "\f1e4"; +$fa-var-tumblr: "\f173"; +$fa-var-tumblr-square: "\f174"; +$fa-var-turkish-lira: "\f195"; +$fa-var-tv: "\f26c"; +$fa-var-twitch: "\f1e8"; +$fa-var-twitter: "\f099"; +$fa-var-twitter-square: "\f081"; +$fa-var-umbrella: "\f0e9"; +$fa-var-underline: "\f0cd"; +$fa-var-undo: "\f0e2"; +$fa-var-university: "\f19c"; +$fa-var-unlink: "\f127"; +$fa-var-unlock: "\f09c"; +$fa-var-unlock-alt: "\f13e"; +$fa-var-unsorted: "\f0dc"; +$fa-var-upload: "\f093"; +$fa-var-usb: "\f287"; +$fa-var-usd: "\f155"; +$fa-var-user: "\f007"; +$fa-var-user-md: "\f0f0"; +$fa-var-user-plus: "\f234"; +$fa-var-user-secret: "\f21b"; +$fa-var-user-times: "\f235"; +$fa-var-users: "\f0c0"; +$fa-var-venus: "\f221"; +$fa-var-venus-double: "\f226"; +$fa-var-venus-mars: "\f228"; +$fa-var-viacoin: "\f237"; +$fa-var-video-camera: "\f03d"; +$fa-var-vimeo: "\f27d"; +$fa-var-vimeo-square: "\f194"; +$fa-var-vine: "\f1ca"; +$fa-var-vk: "\f189"; +$fa-var-volume-down: "\f027"; +$fa-var-volume-off: "\f026"; +$fa-var-volume-up: "\f028"; +$fa-var-warning: "\f071"; +$fa-var-wechat: "\f1d7"; +$fa-var-weibo: "\f18a"; +$fa-var-weixin: "\f1d7"; +$fa-var-whatsapp: "\f232"; +$fa-var-wheelchair: "\f193"; +$fa-var-wifi: "\f1eb"; +$fa-var-wikipedia-w: "\f266"; +$fa-var-windows: "\f17a"; +$fa-var-won: "\f159"; +$fa-var-wordpress: "\f19a"; +$fa-var-wrench: "\f0ad"; +$fa-var-xing: "\f168"; +$fa-var-xing-square: "\f169"; +$fa-var-y-combinator: "\f23b"; +$fa-var-y-combinator-square: "\f1d4"; +$fa-var-yahoo: "\f19e"; +$fa-var-yc: "\f23b"; +$fa-var-yc-square: "\f1d4"; +$fa-var-yelp: "\f1e9"; +$fa-var-yen: "\f157"; +$fa-var-youtube: "\f167"; +$fa-var-youtube-play: "\f16a"; +$fa-var-youtube-square: "\f166"; + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/all.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/all.scss new file mode 100644 index 0000000000000000000000000000000000000000..77e8c868909bb090eb37abc6fdd6834e03d03b6a --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/all.scss @@ -0,0 +1,23 @@ +$fa-font-path: 'font-awesome'; + +// The "x-fa" class should be used in Ext JS applications instead +// of "fa". The "fa" class sets some properties that may collide +// with theme properties, whereas "x-fa" sets only the font-family. +.x-fa:before { + font-family: FontAwesome; +} + +//@import "_variables"; +//@import "_mixins"; +//@import "_path"; +//@import "_core"; +//@import "_larger"; +//@import "_fixed-width"; +//@import "_list"; +//@import "_bordered-pulled"; +//@import "_animated"; +//@import "_rotated-flipped"; +//@import "_stacked"; +//@import "_icons"; + +@import "font-awesome"; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/font-awesome.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/font-awesome.scss new file mode 100644 index 0000000000000000000000000000000000000000..f4668a53c7fe0ae7db53874bfa559ce9ef8051c6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/font-awesome/sass/etc/font-awesome.scss @@ -0,0 +1,17 @@ +/*! + * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ + +@import "variables"; +@import "mixins"; +@import "path"; +@import "core"; +@import "larger"; +@import "fixed-width"; +@import "list"; +@import "bordered-pulled"; +@import "animated"; +@import "rotated-flipped"; +@import "stacked"; +@import "icons"; diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/build-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/build-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..e242abd617d18ca7067345ecce1862e619fb774b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/build-impl.xml @@ -0,0 +1,358 @@ + + + + + + + + Using Sencha Cmd from ${cmd.dir} for ${ant.file} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/build.properties b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/build.properties new file mode 100644 index 0000000000000000000000000000000000000000..83e7e5f2a08400863394010646358cf4d411fba9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/build.properties @@ -0,0 +1,8 @@ +# ============================================================================= +# This file provides an override point for default variables defined in +# defaults.properties. +# +# IMPORTANT - Sencha Cmd will merge your changes with its own during upgrades. +# To avoid potential merge conflicts avoid making large, sweeping changes to +# this file. +# ============================================================================= diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/codegen.json b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/codegen.json new file mode 100644 index 0000000000000000000000000000000000000000..e49161a28c2ea64a657c5990d0afa3fdb6e83e54 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/codegen.json @@ -0,0 +1,118 @@ +{ + "sources": { + "package.json.tpl.merge": { + "12322b2f0769f491000df8ec0e012dd2d78a7eaf": "eJx1zrEKwjAQgOG9TxE6S9HV2VFcFBdxONJrDW1y8XIRSum7m2pTJzMd/3cHGQuVXunAYrlX5ei79pTmqdx8QQa/wiXNK2hGEOLZwJEbLMWQLURrgYfZzg9iUTksXqOA6bE+YNBsvBhy8+6RXKvqX1PUKA+6gxbz5Qs5LNu7altt19+Q9SDXP9oQW5BPzqknDX0qwhGXxPiMhjGkersXU/EGNatRVA\u003d\u003d" + }, + "theme.html.tpl.merge": { + "79ec5194c052d6cc313e842c4e2763fa562c7b70": "eJydVE1vEzEQve+vmO4JEF6nhQNKd3MJRYCQqEg4cHS8k9jFay/2JE1U9b/X3l1KWoKIYln74Tdv3vh55PLs/dfp/Mf1FShqzCQrzxjLAGDq2p3XK0XwQr6Ei9H5BYuPNwXM0Eol4JOVRcZYJAw8haKeJCaUpMng5GpL8HkGc4UNwkfhLYZQ8h7r4xokEWWpZfhrrTdVPnWW0BKb71rMQfZ/VU64JZ5kLiFK+4BUfZ9/YO/ySdYnGmqGL07UIIwBnxJ6rMFo+zOAsDUE6XVLoYtLdXfEBMdgU+WBdgaDQqQcKMoPqjKEHJTHZZXjVjStwaJb4kOCPus+40ZsRL+aQ/CyyouCx3kXsW9orgWpex6/WY2L9aq4Cfmk5H388SkXzlEgL9pn/ANukEKYGS3RRz89HrbhCEmPtkb/f73kfiOsXmKgwXjlPMn1CZq9c3Emvyj1EVuIgDyIEPhwHPy32IlWHqXxuIUTReQ6kGsOmtf1XZ8pjeJW1ysklnpfaBtP7e4RS6MRfqXtGM5H7fbyCRKJpMbwdvQX0rqgSbvIip0uSG/wKe426JfG3Y5ho4NemD34PvtT2ZaFro8YxRqQXv8TgVfPinatkJp2ser9zN32+bD/ku/dHwtX76JR3Ssh3QXzACi8aeE\u003d" + }, + "build.properties.merge": { + "8b81315dbe73ce9c08478f4c1d2df84f456efcf5": "eJytkEtOxEAMRPdzipKyhbkBCzQrFnzE5AKetJNYdOzI3UnE7XGA3GC8K5f9/GnwdM84NWhHKeglM2a3VRIXkMJWdg+B2UQrenMk7mnJFSu50C1HXWREOUEUAfr3yzk4M3sVLudTE8bL68f7Z/v81uIRV9ZuJFymhE1yxsQ+ML5tcUReh6BuUkdILbBNkRYXHbDMg1P6BaI10GqSYrXKWoUOSmfaZ+mi88+f6GvvzRTmA8rGPO/6mFMtYPW4fiff97U/al6C1w\u003d\u003d" + }, + "config.rb.tpl.merge": { + "33f446bd02c3fd24eb27891582eff6a2e789796b": "eJxLLi2KT8ksUrBVcMvMSdUDMvMSc1M14uPdPH1c4+M1ufJLSwpKS+KLSypzUoGqrPJSi0tSU7gALskTcA\u003d\u003d" + }, + "all.scss.merge": { + "da39a3ee5e6b4b0d3255bfef95601890afd80709": "eJwDAAAAAAE\u003d" + }, + "custom.js.merge": { + "199e99bbd15c3c0415569425cb21e77c95e9042a": "eJxlj0FOxDAMRfdziq9ZwUjTHIAlYslquIAncdtA4lSxC8PtSdoREmIV6f/4+dmdDjjhbY6KMSZGeycWrmQcQAqCGlWLMmEpUQzXb1xY/Ex4zgFnRMNXTAlSWseovCTybbbUDl6XsJHa1FH3sYX8B03cqqlS4OPQ//2V8CQ7K5fPriEBNjPU17gYjCZE6UnmYbacfj/GsaUNslUIhbVzu5lwq/2qVjIohGixCCVkkjiyWrOFzqWaXw0sViPr0IRYGVQ7yq+55X2HdObg7meo45udt4XnKyk7Je0Z5SWxqyyB6/Cu/Uh3ODj3crNhN28ar/f1D49P/7rLXUd7+QPuPI9g" + }, + "testing.properties.merge": { + "e65f969c42eb4f355c850fc58fea852582f20db8": "eJyVkUFywyAQBO9+xVb5oIutH/gX+QCCkbUOAooFOf59FsmqpHKKOFEwOzM0Z7r9f53O9DGx0Mge5DBygFDKMSEX1m0VOBpepLqhsndXnpPvv2Z/oefEdiKdLRNoMAJqdyqMI5lAJiXP1hSOQbbZ5msh0mskmuOvnDHHWY32JjbmDEkxOCqxBai6K5DC4d693RAWzjHMCOVCkmB5ZLhW9EWdINjJtBJv9T7cU0vXsk/2rWwxn9AisHA6AooLcgNhqi8riYXdimAn0P+07vXsCOuD8rNimLWaiDKkmBrK7UOUyR0B2RRQdzXedyp+CMVaUi0rQn3ninMxvurPspjBQ/54jjHvYLbHycGKG5Fm2SIf0u/ut9M3l43NIg\u003d\u003d" + }, + "sencha.cfg.tpl.merge": { + "6d1982cce48163a98dc46012d1d0cdfa209fbda6": "eJzFVdFq2zAUfc9XXNzBWmicvg0GgXZlD9vDWsge96LIcqJFljRJTuqN/vuOJNvJmqbbYLASim1dnXPuuVdXZ/R5LUizRpCpKeDZMr5hK0FT8mvTqooaFvg6LRUxriDrjBUudCQ1lbM+vvzqjZ6cTYbXGDr/YTerT3h4nEzORiKPEEHB0G4tE7D0A+lrT4ubxYK4cQ5xRle+TPsqUbNWBdoy1UalgCveP4SCGsG0BwYLSWEtlfBZl2fez7zjdM50NbxvmbvoE+IKH1IwwOLehOeMCXuVJX3QZLDoaGdc5S8Ta2mZFqq8j/+hgFkIMADZc85SxCxFlJ57X2I5WmDIh06Jkbx2piGmuz3lJXkRsimwaKmY3kBFnb/gl75cZsk6hmTAY12daSE6FtC2IduaUj1QiT3PKR1rGAPLUdkc4bmOWUn0jJumYVMvLHMsiIqU9CE2Um1UJZxHiYXbZ4uaykqkinoBpOF702LXUpB4sEpyGVSH0G+tdECMJggo56qt8IrSRt5lK1V1KDVBWRbW81df2qurN++GhUq6x5js3yrHo9kK5yAYPXij1L63YimAFtloJ7ECgSu5RTkYBbaKW4ue/m6AKPpuBztrg0ELSs6U6oAzpgroldAi62EWbbXs8pGEcULz/ogCQkkdPYlBOHdv4cMZEc1m1z0WXT/lP7BqTOtZq8bVp4ZlY/qKpnYqYGvRFzWd0xzsBXN8jRRq42hxu1gAKHuXjul+nHxkW7bgTtowdkLrsa/PGsmhG2CU0UdNCebTxe7b+w8SwDjw/ykDUL+cAQLGDCL5UeuQbKxxITUOTkkkTFmls4GehHjRp3jELsJv/EPADCRpJJwwEv3XT3BDVsWZXsQuXbM8zeMdkqQUZNqAGXQoIp9f0D2rIK0OV0bPlpznRgcmMfDFA2us+sXhc3+R6nV4nxxS9lv8SdYh4EVi3y6n/S4/HscBhu40yoI7SubWKWqH4YmbY1OkeZEv1tDZX4QBEtfkaV0DHXTN/+kfNN1i8uIGus83ukROU7pPteRpRX5P3mISCWV2B8MHNWdbgzwb4VZplhtdoxQBM19iLmqxg3eYJT5un/wEixPeXA\u003d\u003d" + } + }, + "targets": { + ".sencha/package/testing.properties": { + "source": "testing.properties.merge", + "version": "e65f969c42eb4f355c850fc58fea852582f20db8", + "parameters": { + "pkgName": "rapture-theme", + "senchadir": ".sencha", + "touchRelPath": "../../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "theme" + } + }, + ".sencha/package/build.properties": { + "source": "build.properties.merge", + "version": "8b81315dbe73ce9c08478f4c1d2df84f456efcf5", + "parameters": { + "pkgName": "rapture-theme", + "senchadir": ".sencha", + "touchRelPath": "../../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "theme" + } + }, + "package.json": { + "source": "package.json.tpl.merge", + "version": "12322b2f0769f491000df8ec0e012dd2d78a7eaf", + "parameters": { + "pkgName": "rapture-theme", + "senchadir": ".sencha", + "touchRelPath": "../../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "theme" + } + }, + "sass/example/theme.html": { + "source": "theme.html.tpl.merge", + "version": "79ec5194c052d6cc313e842c4e2763fa562c7b70", + "parameters": { + "pkgName": "rapture-theme", + "senchadir": ".sencha", + "touchRelPath": "../../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "theme" + } + }, + "sass/example/custom.js": { + "source": "custom.js.merge", + "version": "199e99bbd15c3c0415569425cb21e77c95e9042a", + "parameters": { + "pkgName": "rapture-theme", + "senchadir": ".sencha", + "touchRelPath": "../../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "theme" + } + }, + "sass/etc/all.scss": { + "source": "all.scss.merge", + "version": "da39a3ee5e6b4b0d3255bfef95601890afd80709", + "parameters": { + "pkgName": "rapture-theme", + "senchadir": ".sencha", + "touchRelPath": "../../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "theme" + } + }, + "sass/config.rb": { + "source": "config.rb.tpl.merge", + "version": "33f446bd02c3fd24eb27891582eff6a2e789796b", + "parameters": { + "pkgName": "rapture-theme", + "senchadir": ".sencha", + "touchRelPath": "../../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "theme" + } + }, + ".sencha/package/sencha.cfg": { + "source": "sencha.cfg.tpl.merge", + "version": "6d1982cce48163a98dc46012d1d0cdfa209fbda6", + "parameters": { + "pkgName": "rapture-theme", + "senchadir": ".sencha", + "touchRelPath": "../../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "theme" + } + } + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/defaults.properties b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/defaults.properties new file mode 100644 index 0000000000000000000000000000000000000000..ec39984d62e71e98bc2146b3791f3237637a072e --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/defaults.properties @@ -0,0 +1,118 @@ +# ============================================================================= +# This file defines properties used by build-impl.xml and the associated +# *-impl.xml files (sass-impl.xml, js-impl.xml, etc.), which are the core of +# the applications build process. +# +# IMPORTANT - This file is not modifiable by a package, and will be overwritten +# during each app upgrade. Please use build.properties for defining package +# customizations to these properties. +# ============================================================================= + +# =========================================== +# properties defining various directory +# locations +# =========================================== +build.dir=${package.build.dir} +build.resources.dir=${build.dir}/resources +package.resources.dir=${package.dir}/resources +package.sass.dir=${package.dir}/sass +package.licenses.dir=${package.dir}/licenses + +# =========================================== +# definitions of various file name patterns +# used for output artifacts +# =========================================== +build.name.prefix=${build.dir}/${package.name} +build.name.css.prefix=${build.resources.dir}/${package.name} +build.name.ruby=config.rb + +build.debug.suffix=-debug +build.all.suffix=-all +build.rtl.suffix=-rtl + +build.all.debug.suffix=${build.all.suffix}${build.debug.suffix} +build.all.rtl.suffix=${build.all.suffix}${build.rtl.suffix} +build.all.rtl.debug.suffix=${build.all.suffix}${build.rtl.suffix}${build.debug.suffix} + +# =========================================== +# define the output js file names for dev, +# debug, and compressed (no suffix) +# =========================================== +build.all.js=${build.name.prefix}.js +build.all.debug.js=${build.name.prefix}${build.debug.suffix}.js + +# =========================================== +# output file names for the scss files +# =========================================== +build.all.scss=${build.name.prefix}${build.all.debug.suffix}.scss +build.all.rtl.scss=${build.name.prefix}${build.all.rtl.debug.suffix}.scss + +# =========================================== +# output file names for the css files +# generated from the scss files by running +# a compass compilation +# =========================================== +build.all.css.debug.prefix=${package.name}${build.all.debug.suffix} +build.all.css.debug=${build.resources.dir}/${build.all.css.debug.prefix}.css +build.all.rtl.css.debug.prefix=${package.name}${build.all.rtl.debug.suffix} +build.all.rtl.css.debug=${build.resources.dir}/${build.all.rtl.css.debug.prefix}.css +build.all.css.prefix=${package.name}${build.all.suffix} +build.all.css=${build.resources.dir}/${build.all.css.prefix}.css +build.all.rtl.css.prefix=${package.name}${build.all.rtl.suffix} +build.all.rtl.css=${build.resources.dir}/${build.all.rtl.css.prefix}.css + +build.all.ruby=${build.dir}/${build.name.ruby} + +# =========================================== +# options to pass to the 'sencha fs slice' command +# =========================================== +build.slice.options= + +# =========================================== +# preprocessor options used when generating +# concatenated js output files +# =========================================== +build.compile.js.debug.options=debug:true +build.compile.js.options=debug:false + +# =========================================== +# compression option used to generate '-all' +# js output file +# =========================================== +build.compile.js.compress=+yui + +# =========================================== +# selector count threshold to use when +# splitting a single css file into multiple +# css files (IE selector limit workaround) +# =========================================== +build.css.selector.limit=4095 + +# =========================================== +# Options for sub-packages + +# Set to true/1 to enable build.version inheritance by sub-pacakges +build.subpkgs.inherit.version=0 + +# =========================================== +# theme slicing example page settings +# =========================================== +package.example.dir=${package.dir}/sass/example +package.example.base=${build.all.rtl.css.debug.prefix} +package.example.css.rel=resources/${package.example.base}.css +package.example.css=${build.dir}/${package.example.css.rel} +package.example.scss=${build.dir}/${package.example.base}.scss +package.example.theme.html=${package.example.dir}/theme.html + +bootstrap.base.path=${package.example.dir} +bootstrap.example.js=${package.example.dir}/bootstrap.js + + +# =========================================== +# options controlling output packaging +# operations for output '.pkg' file +# =========================================== +pkg.build.dir=${workspace.build.dir}/${package.name} +pkg.file.name=${package.name}.pkg +pkg.includes=**/* +pkg.excludes=package.json diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/find-cmd-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/find-cmd-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..55d6826ce7a826c49c52a47d1aa903536c0621b7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/find-cmd-impl.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + source ~/.bash_profile; sencha which -p cmd.dir -o '$cmddir$' + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/init-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/init-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..20d6e9287152b6e5c5e2f93fc6f1375b88063c12 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/init-impl.xml @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + Switch package version from ${package.version} to ${build.version} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/js-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/js-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..285e58567088b9e8cde102661c380bcee53f28b9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/js-impl.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/plugin.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/plugin.xml new file mode 100644 index 0000000000000000000000000000000000000000..d57eba8747bb042f7626cadf1b8cae62e0a1a3dc --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/plugin.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/resources-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/resources-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..19e2d4852d57699d9b6bb2ea98f296791e346355 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/resources-impl.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + Merging resources from base package ${base.path} + + + + + + + + + + + + + + + + Merging resources from current package ${package.resources.dir} + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/sass-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/sass-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..dd803c5ac8aa53278fa9b2d713f6d095c5f8f698 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/sass-impl.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/sencha.cfg b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/sencha.cfg new file mode 100644 index 0000000000000000000000000000000000000000..00b4b678032b2e82bb59c716f545a4a9d54e5bb0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/sencha.cfg @@ -0,0 +1,62 @@ +# The name of the package - should match the "name" property in ./package.json +# +package.name=rapture-theme + +# The namespace to which this package's SASS corresponds. The default value of +# "Ext" means that the files in ./sass/src (and ./sass/var) match classes in +# the Ext" root namespace. In other words, "Ext.panel.Panel" maps to +# ./sass/src/panel/Panel.scss. +# +# To style classes from any namespace, set this to blank. If this is blank, +# then to style "Ext.panel.Panel" you would put SASS in +# ./sass/src/Ext/panel/Panel.scss. +# +package.sass.namespace= + +# This is the comma-separated list of folders where classes reside. These +# classes must be explicitly required to be included in the build. +# +package.classpath=${package.dir}/src + +# This is the comma-separated list of folders of overrides. All files in this +# path will be given a tag of "packageOverrides" which is automatically +# required in generated apps by the presence of this line in app.js: +# +# //@require @packageOverrides +# +package.overrides=${package.dir}/overrides + +# This is the folder where SASS "src" resides. This is searched for SCSS +# files that match the JavaScript classes used by the application. +# +package.sass.srcpath=${package.dir}/sass/src + +# This is the folder where SASS "vars" resides. This is searched for SCSS +# files that match the JavaScript classes used by the application. +# +package.sass.varpath=${package.dir}/sass/var + +# This file is automatically imported into the SASS build before "vars". +# +package.sass.etcpath=${package.dir}/sass/etc/all.scss + +# This is the folder in which to place "sencha packaage build" output. +# +package.build.dir=${package.dir}/build + +# The folder that contains example application(s) for this package. +# +package.examples.dir=${package.dir}/examples + +# The folder that contains sub-packages of this package. Only valid for "framework" +# package type. +# +package.subpkgs.dir=${package.dir}/packages + +#============================================================================== +# Custom Properties - Place customizations below this line to avoid merge +# conflicts with newer versions + +package.cmd.version=4.0.0.203 + +package.framework=ext diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/slice-impl.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/slice-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..8ca45dca951e45a97556df76ecc206d26e3ec24f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/slice-impl.xml @@ -0,0 +1,111 @@ + + + + +/** + * This file is generated by Sencha Cmd and should NOT be edited. It is + * provided to support globbing requires, custom xtypes, and other + * metadata-driven class system features + */ + + + + + + + + + + + +/* + * This file is generated by Sencha Cmd and should NOT be edited. It redirects + * to the most recently built CSS file for the application to allow theme.html + * to load properly for image slicing (required to support non-CSS3 browsers + * such as IE9 and below). + */ +@import '${package.example.css.path}'; + + + + + + Capture theme image to ${build.dir}/theme-capture.png + + + + + + + Slicing theme images to ${build.resources.dir} + + + + + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/sub-builds.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/sub-builds.xml new file mode 100644 index 0000000000000000000000000000000000000000..90f648e362a0a0afcdf240431173ff6c2d6b0e33 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/sub-builds.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + package + upgrade + + + + + + + + + + Building example in @{example-dir} + + + + + + + + + Upgrading example in @{example-dir} + + + app + upgrade + + + + + + + + + + Cleaning example in @{example-dir} + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/testing.properties b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/testing.properties new file mode 100644 index 0000000000000000000000000000000000000000..60749a30dbd1357878336fb45589373f33f12107 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/.sencha/package/testing.properties @@ -0,0 +1,17 @@ +# =========================================== +# This file defines properties used by +# build-impl.xml, which is the base impl +# of an applications build process. The +# properties from this file correspond to the +# 'testing' build environment, specified +# by 'sencha app build testing'. These will +# take precedence over defaults provided by +# build.properties. +# =========================================== + +# =========================================== +# compression option used to generate '-all' +# js output file. this value disables +# compression for testing builds +# =========================================== +build.compile.js.compress= diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/build.xml b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/build.xml new file mode 100644 index 0000000000000000000000000000000000000000..d808256ef77798be418f46d1d0a0e27b8f59ab2f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/build.xml @@ -0,0 +1,37 @@ + + + + + + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/package.json b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/package.json new file mode 100644 index 0000000000000000000000000000000000000000..7182d840db2444b023a8dad1f47335ef595f2688 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/package.json @@ -0,0 +1,13 @@ +{ + "name": "rapture-theme", + "type": "theme", + "creator": "Sonatype", + "summary": "Nexus Rapture Theme", + "detailedDescription": "Provides the theme for use in Nexus Rapture", + "version": "1.0.0", + "compatVersion": "1.0.0", + "format": "1", + "local": true, + "requires": [], + "extend": "ext-theme-neptune" +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..941afc58a085358d91b7054a79b7707056e5d4a9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ae051d953f8e69fe5beab0596aec5d33b87f17ac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/danger-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fc67c0c3522a9b722cd602e09b1919b30834f9da Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..6a0c17dbaf88c5dae3cef565cd062732f32cf8d0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..f818b67cdbe4550f7733589686ea03db0ba4f326 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..2a4c3b10bbb25b7ace76e8aaf4ca4f1838677ec1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..c13635e28011c4c8645cbd8d65215ad5789bc24c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..8c743180fd8e97f9ad569aa3f4cec79c95245b95 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-large-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..5f3acb5da8cba8ba5e154503ff59ad39edcbc85b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..735595c4bedbe0a353853ba10c087eec46717eac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..01d24ca03cf689ddedb97a1e98eaaeaec29006e1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..248743bb86b47a50448fbc0a16b634cf9bc5f0ce Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..799218dc4e2a3049da07b4e7e1d0fa5aef8ba8b6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a437e2e664e853f7ebdd333b86d334c7a07c8587 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ea14ed550d5376cf802e5c26886e30d9cfa48024 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-arrow-rtl.psd b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-arrow-rtl.psd new file mode 100644 index 0000000000000000000000000000000000000000..59e1183936ac275ea7564a0375e7f47bbf31ea59 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-arrow-rtl.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..b7ad1b8b0f11265f55cc4d2c2e301c5f2d0f1bf0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-arrow.psd b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-arrow.psd new file mode 100644 index 0000000000000000000000000000000000000000..c310763b515ad9b349e418f3f8a94680e9f152c4 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-arrow.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..bb6ac1103148e7cfed939420479288d5855dc786 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..6a432cca33a6c7a8d0630eb1a947942011464fe9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2f77cf08b977d13808873a6601626f70c9c6c9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9a65e8e163d0a707619f11d099103308bcedece1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..7ae10261d70c2e0d5cff97548b86fc388a58c147 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..de5242a4fd5ef3ec27a8249e67392192d6613f6b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..a357f5d9e4ce93dedc335190f080cee7d7244f3a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..3f903bebe4d660ba4e94022e4a898b99401f1868 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ea815642073d2809ef6c7731088cec34fcc99b37 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..8fd013ada2991435bd1903dce351674916af153a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-large-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..b1830bd4bbd44b9d7e06d4023a8c31b78abbbfc7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..e94f50fa624f88cf41f1758a1f277f573104fdfc Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..8e51018f6b54b0456e3d988e52f4b2e8e2aadaf6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..404c14d33c24e0ab3ba0ed28759001b9ff4850e3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11e63f47859a175a71e4d6b9830856dc1255690e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..3ef559d452dcd29d2963787bd74f56df3219669d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..7bff9239fc0dcac881694fa4c01230f7017baa1c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..0858a9316071da2469600177b6ddb3342bdf4d01 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..bb6ac1103148e7cfed939420479288d5855dc786 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..6a432cca33a6c7a8d0630eb1a947942011464fe9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2f77cf08b977d13808873a6601626f70c9c6c9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9a65e8e163d0a707619f11d099103308bcedece1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/default-toolbar-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..5f3acb5da8cba8ba5e154503ff59ad39edcbc85b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..735595c4bedbe0a353853ba10c087eec46717eac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..01d24ca03cf689ddedb97a1e98eaaeaec29006e1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..248743bb86b47a50448fbc0a16b634cf9bc5f0ce Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..799218dc4e2a3049da07b4e7e1d0fa5aef8ba8b6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a437e2e664e853f7ebdd333b86d334c7a07c8587 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/header-toolbar-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..5f3acb5da8cba8ba5e154503ff59ad39edcbc85b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..735595c4bedbe0a353853ba10c087eec46717eac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..01d24ca03cf689ddedb97a1e98eaaeaec29006e1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..248743bb86b47a50448fbc0a16b634cf9bc5f0ce Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..799218dc4e2a3049da07b4e7e1d0fa5aef8ba8b6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a437e2e664e853f7ebdd333b86d334c7a07c8587 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/headermode-toolbar-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/loading-spinner.gif b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/loading-spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..55f59960f8065f1043eaddf807e1cd0403239ac4 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/loading-spinner.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/loading.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/loading.png new file mode 100644 index 0000000000000000000000000000000000000000..f9486e882ca746e9e28ab785e7377b5e757e99a8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/loading.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nexus-milestone.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nexus-milestone.png new file mode 100644 index 0000000000000000000000000000000000000000..c19e9bf2067d44168ea83552ae70f1d499fe0769 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nexus-milestone.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-danger-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-danger-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-danger-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-danger-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-danger-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-danger-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-danger-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-danger-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-danger-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-danger-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-danger-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-danger-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-large-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-large-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..6a0c17dbaf88c5dae3cef565cd062732f32cf8d0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-large-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-large-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-large-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..2a4c3b10bbb25b7ace76e8aaf4ca4f1838677ec1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-large-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-large-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-large-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..c13635e28011c4c8645cbd8d65215ad5789bc24c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-large-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-large-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-large-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..8c743180fd8e97f9ad569aa3f4cec79c95245b95 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-large-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..735595c4bedbe0a353853ba10c087eec46717eac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..248743bb86b47a50448fbc0a16b634cf9bc5f0ce Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..799218dc4e2a3049da07b4e7e1d0fa5aef8ba8b6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a437e2e664e853f7ebdd333b86d334c7a07c8587 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-toolbar-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-toolbar-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..0858a9316071da2469600177b6ddb3342bdf4d01 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-toolbar-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-toolbar-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-toolbar-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..6a432cca33a6c7a8d0630eb1a947942011464fe9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-toolbar-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-toolbar-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-toolbar-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2f77cf08b977d13808873a6601626f70c9c6c9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-toolbar-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-toolbar-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-toolbar-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9a65e8e163d0a707619f11d099103308bcedece1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-drilldown-toolbar-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-header-toolbar-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-header-toolbar-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..735595c4bedbe0a353853ba10c087eec46717eac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-header-toolbar-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-header-toolbar-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-header-toolbar-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..248743bb86b47a50448fbc0a16b634cf9bc5f0ce Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-header-toolbar-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-header-toolbar-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-header-toolbar-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..799218dc4e2a3049da07b4e7e1d0fa5aef8ba8b6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-header-toolbar-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-header-toolbar-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-header-toolbar-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a437e2e664e853f7ebdd333b86d334c7a07c8587 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-header-toolbar-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-plain-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-plain-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..0858a9316071da2469600177b6ddb3342bdf4d01 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-plain-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-plain-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-plain-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..6a432cca33a6c7a8d0630eb1a947942011464fe9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-plain-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-plain-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-plain-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2f77cf08b977d13808873a6601626f70c9c6c9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-plain-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-plain-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-plain-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9a65e8e163d0a707619f11d099103308bcedece1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-plain-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-primary-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-primary-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-primary-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-primary-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-primary-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-primary-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-primary-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-primary-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-primary-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-primary-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-primary-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-primary-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-success-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-success-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-success-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-success-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-success-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-success-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-success-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-success-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-success-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-success-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-success-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-success-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-warning-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-warning-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-warning-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-warning-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-warning-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-warning-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-warning-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-warning-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-warning-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-warning-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-warning-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/nx-warning-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..7bff9239fc0dcac881694fa4c01230f7017baa1c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..0858a9316071da2469600177b6ddb3342bdf4d01 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..bb6ac1103148e7cfed939420479288d5855dc786 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..6a432cca33a6c7a8d0630eb1a947942011464fe9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2f77cf08b977d13808873a6601626f70c9c6c9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9a65e8e163d0a707619f11d099103308bcedece1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..7ae10261d70c2e0d5cff97548b86fc388a58c147 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..de5242a4fd5ef3ec27a8249e67392192d6613f6b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..a357f5d9e4ce93dedc335190f080cee7d7244f3a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..3f903bebe4d660ba4e94022e4a898b99401f1868 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ea815642073d2809ef6c7731088cec34fcc99b37 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..8fd013ada2991435bd1903dce351674916af153a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-large-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..b1830bd4bbd44b9d7e06d4023a8c31b78abbbfc7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..e94f50fa624f88cf41f1758a1f277f573104fdfc Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..8e51018f6b54b0456e3d988e52f4b2e8e2aadaf6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..404c14d33c24e0ab3ba0ed28759001b9ff4850e3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11e63f47859a175a71e4d6b9830856dc1255690e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..3ef559d452dcd29d2963787bd74f56df3219669d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..7bff9239fc0dcac881694fa4c01230f7017baa1c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..0858a9316071da2469600177b6ddb3342bdf4d01 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..bb6ac1103148e7cfed939420479288d5855dc786 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..6a432cca33a6c7a8d0630eb1a947942011464fe9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2f77cf08b977d13808873a6601626f70c9c6c9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9a65e8e163d0a707619f11d099103308bcedece1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/plain-toolbar-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..941afc58a085358d91b7054a79b7707056e5d4a9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ae051d953f8e69fe5beab0596aec5d33b87f17ac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/primary-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/s.gif b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/s.gif new file mode 100644 index 0000000000000000000000000000000000000000..1d11fa9ada9e93505b3d736acb204083f45d5fbf Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/s.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..941afc58a085358d91b7054a79b7707056e5d4a9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ae051d953f8e69fe5beab0596aec5d33b87f17ac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/success-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..941afc58a085358d91b7054a79b7707056e5d4a9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ae051d953f8e69fe5beab0596aec5d33b87f17ac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/button/warning-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/datepicker/arrow-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/datepicker/arrow-left.png new file mode 100644 index 0000000000000000000000000000000000000000..25f5b0874a1409259283fc43c839228c354b7196 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/datepicker/arrow-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/datepicker/arrow-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/datepicker/arrow-right.png new file mode 100644 index 0000000000000000000000000000000000000000..70653fc62852efe074dbdc2baea107425c7bdfc7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/datepicker/arrow-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/datepicker/month-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/datepicker/month-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..f724135422e3dfb27a3b0b4ec65151670ccad363 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/datepicker/month-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/dd/drop-add.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/dd/drop-add.png new file mode 100644 index 0000000000000000000000000000000000000000..9f66b4709474a66fad9def9c410b5aae715ba291 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/dd/drop-add.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/dd/drop-no.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/dd/drop-no.png new file mode 100644 index 0000000000000000000000000000000000000000..9a0f571821171d95ec44d9953aec867de9530f3a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/dd/drop-no.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/dd/drop-yes.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/dd/drop-yes.png new file mode 100644 index 0000000000000000000000000000000000000000..9f66b4709474a66fad9def9c410b5aae715ba291 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/dd/drop-yes.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/editor/tb-sprite.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/editor/tb-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..40ec064287569a0fd182c99cdf706c1d2d16247e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/editor/tb-sprite.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/fieldset/collapse-tool.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/fieldset/collapse-tool.png new file mode 100644 index 0000000000000000000000000000000000000000..6363d95bc60a59be841f30959bbafd668ba7907a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/fieldset/collapse-tool.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/checkbox.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/checkbox.png new file mode 100644 index 0000000000000000000000000000000000000000..4c840e484639471206b0b214bf9b2ff0e205ae0d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/checkbox.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/clear-trigger-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/clear-trigger-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe28b6b7876de2328d69ad5625ca0d21ed9ee531 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/clear-trigger-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/clear-trigger.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/clear-trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..fe28b6b7876de2328d69ad5625ca0d21ed9ee531 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/clear-trigger.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/date-trigger-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/date-trigger-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..1d9dfa679a1b72235006f9736eb7b87534febb89 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/date-trigger-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/date-trigger.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/date-trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..1d9dfa679a1b72235006f9736eb7b87534febb89 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/date-trigger.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/exclamation.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/exclamation.png new file mode 100644 index 0000000000000000000000000000000000000000..37d5573dde6f8cee203dc1ac0d0ab06fd1a43020 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/exclamation.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/radio.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/radio.png new file mode 100644 index 0000000000000000000000000000000000000000..7c06a2c820229bf2a3944ccfe3fa2386ab8d9c06 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/radio.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/search-trigger-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/search-trigger-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ad1cee9d777a547bcff1159da71bb28f23585080 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/search-trigger-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/search-trigger.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/search-trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..ad1cee9d777a547bcff1159da71bb28f23585080 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/search-trigger.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/spinner-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/spinner-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..e6958fe275a81e70defdc8c618018c87d40c444e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/spinner-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/spinner.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/spinner.png new file mode 100644 index 0000000000000000000000000000000000000000..e6958fe275a81e70defdc8c618018c87d40c444e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/spinner.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/trigger-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/trigger-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..a1fbe0973fd78001fb8eba6e61f09a563246dcd9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/trigger-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/trigger.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..a1fbe0973fd78001fb8eba6e61f09a563246dcd9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/form/trigger.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/col-move-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/col-move-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..38a244d4c60b4ba3e0c616dbcd62da98b4388908 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/col-move-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/col-move-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/col-move-top.png new file mode 100644 index 0000000000000000000000000000000000000000..9c6fa851ef07741c4f66afc216b617e226372616 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/col-move-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/columns.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/columns.png new file mode 100644 index 0000000000000000000000000000000000000000..bb256e7eab44594a19d9731719b7ff75cc1bf99a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/columns.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/dd-insert-arrow-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/dd-insert-arrow-left.png new file mode 100644 index 0000000000000000000000000000000000000000..846bc8d4808cd8fc363e61a7a657120da93c31fb Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/dd-insert-arrow-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/dd-insert-arrow-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/dd-insert-arrow-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c039a2b324a1c4d0c577151eb1f0a6c5ef81d559 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/dd-insert-arrow-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/dirty-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/dirty-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..7fc4374404bf1aa064c3a30c52e8ee08cf4f2294 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/dirty-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/dirty.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/dirty.png new file mode 100644 index 0000000000000000000000000000000000000000..3f181ed16518dd2a107a7f450cc114c376f457c7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/dirty.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/drop-no.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/drop-no.png new file mode 100644 index 0000000000000000000000000000000000000000..9a0f571821171d95ec44d9953aec867de9530f3a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/drop-no.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/drop-yes.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/drop-yes.png new file mode 100644 index 0000000000000000000000000000000000000000..9f66b4709474a66fad9def9c410b5aae715ba291 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/drop-yes.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/group-by.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/group-by.png new file mode 100644 index 0000000000000000000000000000000000000000..e941346df4528d4c9418d7fac835872dc88c3a0c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/group-by.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/group-collapse.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/group-collapse.png new file mode 100644 index 0000000000000000000000000000000000000000..2244563a7c966d4607931eda328dde7dc9b35ed9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/group-collapse.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/group-expand-sprite.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/group-expand-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..1e2b3939b7d25e4557feb24e2c1408dbe3c31e28 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/group-expand-sprite.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/group-expand.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/group-expand.png new file mode 100644 index 0000000000000000000000000000000000000000..d1e315ead43c0dbbc82e563b5f7fb8ed3fdccd79 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/group-expand.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hd-pop.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hd-pop.png new file mode 100644 index 0000000000000000000000000000000000000000..51355a5f483a46e30a886ff5be1b1125e3749063 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hd-pop.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hmenu-asc.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hmenu-asc.png new file mode 100644 index 0000000000000000000000000000000000000000..b8e2ba271fb60bdf30ca95c9333fcfcef7ab994d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hmenu-asc.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hmenu-desc.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hmenu-desc.png new file mode 100644 index 0000000000000000000000000000000000000000..c8074541dd543168920fb4989334434e82dadc66 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hmenu-desc.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hmenu-lock.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hmenu-lock.png new file mode 100644 index 0000000000000000000000000000000000000000..ca8602415204560d7a40e54560f2c2a0a4356c7d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hmenu-lock.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hmenu-unlock.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hmenu-unlock.png new file mode 100644 index 0000000000000000000000000000000000000000..cf3ffd2f792f46b5a2c536560d8b0fa3b50fa692 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/hmenu-unlock.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/loading.gif b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..81b0f125790bc94311f43e08dee4bb851729adc2 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/loading.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-first.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-first.png new file mode 100644 index 0000000000000000000000000000000000000000..f5e047b7d6944318c48b48ab139ca3b3564cb376 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-first.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-first.psd b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-first.psd new file mode 100644 index 0000000000000000000000000000000000000000..52180a75bcf867b704f5d77e55ae61222e3b637c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-first.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-last.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-last.png new file mode 100644 index 0000000000000000000000000000000000000000..e267a17834949591f85384cc70bd7b10628e3f62 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-last.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-last.psd b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-last.psd new file mode 100644 index 0000000000000000000000000000000000000000..c25e5b840e77b1b9410aa033295d25d7711e7841 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-last.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-next.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-next.png new file mode 100644 index 0000000000000000000000000000000000000000..444d320f296483607ff07d236b497bd2406aacbf Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-next.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-next.psd b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-next.psd new file mode 100644 index 0000000000000000000000000000000000000000..fa5802cd45e66f20a2a8199c91362b95b819c54b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-next.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-prev.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-prev.png new file mode 100644 index 0000000000000000000000000000000000000000..ad680da71dc41816813e4c370f8fc0214443b12e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-prev.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-prev.psd b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-prev.psd new file mode 100644 index 0000000000000000000000000000000000000000..73cc7fa14d6309672d3680962099bfb06c8249fd Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/page-prev.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/pick-button.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/pick-button.png new file mode 100644 index 0000000000000000000000000000000000000000..077ca114954eceff79ea30baaeb18387e7cb77e1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/pick-button.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/refresh.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..9c124654b28fc8b62c2ffc0f21e6e848021add15 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/refresh.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/refresh.psd b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/refresh.psd new file mode 100644 index 0000000000000000000000000000000000000000..af33d503eff0f97f9492449d62aa914aa0d141e6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/refresh.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/sort_asc.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/sort_asc.png new file mode 100644 index 0000000000000000000000000000000000000000..b8e2ba271fb60bdf30ca95c9333fcfcef7ab994d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/sort_asc.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/sort_desc.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/sort_desc.png new file mode 100644 index 0000000000000000000000000000000000000000..c8074541dd543168920fb4989334434e82dadc66 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/grid/sort_desc.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/loadmask/loading.gif b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/loadmask/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..8471b4f0b2570c79d4139779176012357865045a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/loadmask/loading.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/checked.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/checked.png new file mode 100644 index 0000000000000000000000000000000000000000..7dc0d1317ec2a2e75011858be29c31370521cf92 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/checked.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/group-checked.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/group-checked.png new file mode 100644 index 0000000000000000000000000000000000000000..c51fc306abf1191b38ba2a957ee07fe66d113853 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/group-checked.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/menu-parent-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/menu-parent-left.png new file mode 100644 index 0000000000000000000000000000000000000000..11351fc4691e109f2073728c7fae16ba2e8f1d23 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/menu-parent-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/menu-parent.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/menu-parent.png new file mode 100644 index 0000000000000000000000000000000000000000..74069eeceef97704ee335cf3ecb631a6068fe39c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/menu-parent.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..46f26f12d8f8693e9cb9624d35cd1d7e6950ca65 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..7f4a51dd53afa9f48c3164f26c95140b7432c69f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/unchecked.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/unchecked.png new file mode 100644 index 0000000000000000000000000000000000000000..73665913ff9f9eca24693608891b2d68e89cbb7e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/menu/unchecked.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/shared/icon-error.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/shared/icon-error.png new file mode 100644 index 0000000000000000000000000000000000000000..3ba9b069b92c086b3cfbf1270b8d10ff6c4c1ad4 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/shared/icon-error.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/shared/icon-info.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/shared/icon-info.png new file mode 100644 index 0000000000000000000000000000000000000000..3360eac6c4556ed6b69170d6d21c7b3512662677 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/shared/icon-info.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/shared/icon-question.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/shared/icon-question.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0551d1299e7116a4015de6bac182a3c8227dc1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/shared/icon-question.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/shared/icon-warning.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/shared/icon-warning.png new file mode 100644 index 0000000000000000000000000000000000000000..96b21e43a29cc1f2997406df6e0cb90e501fac25 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/shared/icon-warning.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/e-handle.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/e-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..8876d59a61eb479193ea406361cc5f24bf3f7f90 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/e-handle.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/ne-handle.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/ne-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..48399a6b08f567e4d0286ce210b0d60d99eef68d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/ne-handle.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/nw-handle.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/nw-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..b3a1f5926c64e6c7bfd98ebf95f74852296d8dd2 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/nw-handle.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/s-handle.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/s-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..d0f300fe4958b7ee586dc382804c700ce6cfc50d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/s-handle.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/se-handle.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/se-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..56a95e4a19427d81b51363fdb4059b21fff8e87a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/se-handle.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/sw-handle.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/sw-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..580239c2aebb842f533a68666cb50a010e4b8bfb Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/sizer/sw-handle.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/slider/slider-bg.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/slider/slider-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..c3de7ebd30160d3707bbee6cbe6f065965509c34 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/slider/slider-bg.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/slider/slider-thumb.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/slider/slider-thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..9840c4e88613d5fa9dd34c72a9cbd62c6567fb18 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/slider/slider-thumb.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/slider/slider-v-bg.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/slider/slider-v-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..55361b351eb8ddc95deba6558b133b2e1a5a1552 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/slider/slider-v-bg.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/slider/slider-v-thumb.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/slider/slider-v-thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..9840c4e88613d5fa9dd34c72a9cbd62c6567fb18 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/slider/slider-v-thumb.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-plain-scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-plain-scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..725b5fa395eb5e8cb23c02796777e36bc9c21701 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-plain-scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-plain-scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-plain-scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..98215df606775d3227717ad71b659977e3e5cff0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-plain-scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-plain-scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-plain-scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d8481df01837165eba4ea0deb93b37191783dca3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-plain-scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-plain-scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-plain-scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..cda4bc58ac692d2c586f6e3ec10fa5fe0821e361 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-plain-scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..5259c5eba332fc47c1eee747936cbbb44ff3d1ab Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..5c9844265730fbf168020e3e5b062c1882f11aad Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..8d1ef6c37e952219a89f4afc583b6190b7c63603 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..f2831ed4fd6fe3f390663e95b3dc23c5b116b970 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/default-scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-plain-scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-plain-scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..725b5fa395eb5e8cb23c02796777e36bc9c21701 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-plain-scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-plain-scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-plain-scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..98215df606775d3227717ad71b659977e3e5cff0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-plain-scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-plain-scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-plain-scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d8481df01837165eba4ea0deb93b37191783dca3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-plain-scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-plain-scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-plain-scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..cda4bc58ac692d2c586f6e3ec10fa5fe0821e361 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-plain-scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..725b5fa395eb5e8cb23c02796777e36bc9c21701 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..98215df606775d3227717ad71b659977e3e5cff0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d8481df01837165eba4ea0deb93b37191783dca3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..cda4bc58ac692d2c586f6e3ec10fa5fe0821e361 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/light-scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-plain-scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-plain-scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..725b5fa395eb5e8cb23c02796777e36bc9c21701 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-plain-scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-plain-scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-plain-scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..98215df606775d3227717ad71b659977e3e5cff0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-plain-scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-plain-scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-plain-scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d8481df01837165eba4ea0deb93b37191783dca3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-plain-scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-plain-scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-plain-scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..cda4bc58ac692d2c586f6e3ec10fa5fe0821e361 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-plain-scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..725b5fa395eb5e8cb23c02796777e36bc9c21701 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..98215df606775d3227717ad71b659977e3e5cff0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d8481df01837165eba4ea0deb93b37191783dca3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..cda4bc58ac692d2c586f6e3ec10fa5fe0821e361 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab-bar/nx-light-scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab/tab-default-close.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab/tab-default-close.png new file mode 100644 index 0000000000000000000000000000000000000000..320caa48a5a93434158fb1e933dc5cfd31345107 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab/tab-default-close.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab/tab-light-close.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab/tab-light-close.png new file mode 100644 index 0000000000000000000000000000000000000000..320caa48a5a93434158fb1e933dc5cfd31345107 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab/tab-light-close.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab/tab-nx-light-close.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab/tab-nx-light-close.png new file mode 100644 index 0000000000000000000000000000000000000000..320caa48a5a93434158fb1e933dc5cfd31345107 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tab/tab-nx-light-close.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/toolbar/more.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/toolbar/more.png new file mode 100644 index 0000000000000000000000000000000000000000..4c884a9edd1d8920a426b5fa18bec1cba13621df Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/toolbar/more.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/toolbar/scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/toolbar/scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..ce5f0c0cd3ce36d6766bc25c66cc868ccf5eeed7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/toolbar/scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/toolbar/scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/toolbar/scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..721a683a0e8186434c43099e2fd7ccd69fc6b1ce Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/toolbar/scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tools/tool-sprites-dark.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tools/tool-sprites-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..4e7fb18784f24c337867b7ce0fcc3db03e3a8254 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tools/tool-sprites-dark.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tools/tool-sprites.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tools/tool-sprites.png new file mode 100644 index 0000000000000000000000000000000000000000..4c21701adcc3f1268ac631b23a4c2a8b72ed333e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tools/tool-sprites.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/arrows-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/arrows-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..df41161335c114f0675a7e95edff67b1411d768f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/arrows-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/arrows.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/arrows.png new file mode 100644 index 0000000000000000000000000000000000000000..0e2b8b1f20e6cd0840d51b1f0894a5d507c21e4c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/arrows.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-above.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-above.png new file mode 100644 index 0000000000000000000000000000000000000000..ca89bfa626b94ae008aa088fc608d4e55693d928 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-above.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-add.gif b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-add.gif new file mode 100644 index 0000000000000000000000000000000000000000..b22cd1448efa13c47ad6d3b75bdea8b4031c31e9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-add.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-append.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-append.png new file mode 100644 index 0000000000000000000000000000000000000000..ca89bfa626b94ae008aa088fc608d4e55693d928 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-append.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-below.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-below.png new file mode 100644 index 0000000000000000000000000000000000000000..ca89bfa626b94ae008aa088fc608d4e55693d928 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-below.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-between.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-between.png new file mode 100644 index 0000000000000000000000000000000000000000..ca89bfa626b94ae008aa088fc608d4e55693d928 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-between.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-no.gif b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-no.gif new file mode 100644 index 0000000000000000000000000000000000000000..9d9c6a9ce1307c5ba072f08bf77d998bb1b716cb Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-no.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-over.gif b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-over.gif new file mode 100644 index 0000000000000000000000000000000000000000..30d1ca7107816233884d23239dd76fce79237fe5 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-over.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-under.gif b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-under.gif new file mode 100644 index 0000000000000000000000000000000000000000..85f66b1e584aece5a5d6d4cf062b8c1f63edce97 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-under.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-yes.gif b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-yes.gif new file mode 100644 index 0000000000000000000000000000000000000000..8aacb307e89d690f46853e01f5c4726bd5d94e31 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/drop-yes.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-minus-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-minus-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..c8b7ff57fa9471098646f1bb0e5e84f0639f02a8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-minus-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-minus.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-minus.png new file mode 100644 index 0000000000000000000000000000000000000000..0902284a27d8be55431da8a4e76ef3bf1eded246 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-minus.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-plus-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-plus-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..a0d0e36cc63f061b757969ceb54548fd86c73c34 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-plus-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-plus.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-plus.png new file mode 100644 index 0000000000000000000000000000000000000000..19a3faadc815a526a7f9e5d5299e58a6ea611ed8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-plus.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..1e51148e17c6b15ee1a65f5ae8072358ccdf1c37 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end.png new file mode 100644 index 0000000000000000000000000000000000000000..80a897f775d6bc1bb733e5f4a47bf63dcacaa4ba Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-end.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-line-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-line-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ee273b6502615db08de372ed2035fa41526d3863 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-line-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-line.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-line.png new file mode 100644 index 0000000000000000000000000000000000000000..b655cbe1cc4a1eca9f4309db0507246a62cde2ab Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-line.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-minus-nl-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-minus-nl-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fdc2afeb58ad97917c3098348e853efe462197ab Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-minus-nl-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-minus-nl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-minus-nl.png new file mode 100644 index 0000000000000000000000000000000000000000..2057568dcc890f7884428cfc27afa5208a1f4f5a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-minus-nl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-minus-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-minus-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..9ce1e710c242570a8d9b66cd94afb108469fbefe Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-minus-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-minus.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-minus.png new file mode 100644 index 0000000000000000000000000000000000000000..7d95b21efca23b054df1faef765b53f606e6163a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-minus.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-plus-nl-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-plus-nl-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..c1100898e602cb4b2e6819e345cc20842a82a685 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-plus-nl-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-plus-nl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-plus-nl.png new file mode 100644 index 0000000000000000000000000000000000000000..860a688c26c4f6b78e98b680d70ba81819534d2b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-plus-nl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-plus-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-plus-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..c6d638b6e4976d384a1146c6281230414cc120b1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-plus-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-plus.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-plus.png new file mode 100644 index 0000000000000000000000000000000000000000..7d32dba1714e5ea434784b14e981ce29f81e57bf Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-plus.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..52571b124c4d8ae21336fd3417264692d5980fe5 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow.png new file mode 100644 index 0000000000000000000000000000000000000000..ea022f4bdbc36884a1b93289850a178d1c7038a0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/elbow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/folder-open-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/folder-open-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ff5e878b21d061eb72465b8358d6f4996615b637 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/folder-open-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/folder-open.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/folder-open.png new file mode 100644 index 0000000000000000000000000000000000000000..d94c2751ad2bebcbb3dad76a20e7e4fdc6695ee5 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/folder-open.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/folder-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/folder-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..3b4f117e64567dbb2c2647e3da65b9add59d5445 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/folder-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/folder.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..3b4f117e64567dbb2c2647e3da65b9add59d5445 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/folder.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/leaf-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/leaf-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..2e4420c07abaf552181e2c3119b97e2020fb8d3e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/leaf-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/leaf.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/leaf.png new file mode 100644 index 0000000000000000000000000000000000000000..34528c5125908d81530934df20a2eafd27514b92 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/leaf.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/loading.gif b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..81b0f125790bc94311f43e08dee4bb851729adc2 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/tree/loading.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/util/splitter/mini-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/util/splitter/mini-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..5144896cb9d2b9c0a0c1a10b65808bfd5831177b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/util/splitter/mini-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/util/splitter/mini-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/util/splitter/mini-left.png new file mode 100644 index 0000000000000000000000000000000000000000..17faaed459a163a91f79901e1e612a92125b52e6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/util/splitter/mini-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/util/splitter/mini-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/util/splitter/mini-right.png new file mode 100644 index 0000000000000000000000000000000000000000..94320746677d41d55dfe4d658972267e1365049f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/util/splitter/mini-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/util/splitter/mini-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/util/splitter/mini-top.png new file mode 100644 index 0000000000000000000000000000000000000000..61f1c6b2a3cac3b4bc63c7b20830a6276ce1844e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/resources/images/util/splitter/mini-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/config.rb b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/config.rb new file mode 100644 index 0000000000000000000000000000000000000000..086229e9a4603f34a2e8e082a85d682ed86097e8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/config.rb @@ -0,0 +1,5 @@ +cur_dir = File.dirname(__FILE__) +output_style = :nested + +# HACK: workaround to issue setting line_comment=false from require'd config inclusion +::Compass.configuration.line_comments = false \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/etc/all.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/etc/all.scss new file mode 100644 index 0000000000000000000000000000000000000000..4b698fc5193144beb2f2465e17d4ab94e84c74c9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/etc/all.scss @@ -0,0 +1,53 @@ +// +// Utility SCSS styles for entire theme. These are included before var/... and src/... files. +// + +@import 'colors'; +@import 'fonts'; + +/** + * Links + */ + +a:not([class*="x-"]) { + color: $color-navy-blue; + text-decoration: none; + + &:visited { + color: $color-dark-denim; + } + + &:focus, &:hover { + color: $color-dark-cerulean; + text-decoration: underline; + } + + &:active { + color: $color-cornflower; + } +} + +/** + * Paragraphs + */ + +p { + margin-top: 0; + margin-bottom: 1em; +} + +/** + * Tables + */ + +th { + text-align: left; +} + +td:not([class|=x]) { + padding-right: 5px; +} + +td:not([class|=x]):last-child { + padding-right: 0; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/etc/colors.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/etc/colors.scss new file mode 100644 index 0000000000000000000000000000000000000000..aae21b2a2f017139a59c4dff1023c3205423c2e4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/etc/colors.scss @@ -0,0 +1,85 @@ +/** + * Colors + */ + +// Transparency +$color-none: rgba(0, 0, 0, 0); + +// Shell +$color-black: #000000; +$color-night-rider: #333333; +$color-charcoal: #444444; +$color-dark-gray: #777777; +$color-gray: #AAAAAA; +$color-light-gray: #CBCBCB; +$color-gainsboro: #DDDDDD; +$color-smoke: #EBEBEB; +$color-light-smoke: #F4F4F4; +$color-white: #FFFFFF; + +// Severity +$color-cerise: #DB2852; +$color-sun: #F2862F; +$color-energy-yellow: #F5C649; +$color-cobalt: #0047B2; +$color-cerulean-blue: #2476C3; + +// Forms +$color-citrus: #84C900; +$color-free-speech-red: #C70000; + +// Tooltips +$color-energy-yellow: #F5C649; +$color-floral-white: #FFFAEE; + +// Dashboard +$color-pigment-green: #0B9743; +$color-madang: #B6E9AB; +$color-venetian-red: #BC0430; +$color-beauty-bush: #EDB2AF; +$color-navy-blue: #006BBF; +$color-cornflower: #96CAEE; +$color-affair: #875393; +$color-east-side: #B087B9; +$color-blue-chalk: #DAC5DF; + +// Font Awesome Icons +$color-navy-blue: #006BBF; +$color-smalt: #014E8A; +$color-prussian-blue: #013A68; +$color-white: #FFFFFF; +$color-gainsboro: #DDDDDD; +$color-gray: #AAAAAA; + +// Buttons +$color-white: #FFFFFF; +$color-light-gainsboro: #E6E6E6; +$color-light-gray: #CBCBCB; +$color-silver: #B8B8B8; +$color-suva-gray: #919191; +$color-gray: #808080; +$color-denim: #197AC5; +$color-light-cobalt: #0161AD; +$color-dark-denim: #14629E; +$color-smalt: #014E8A; +$color-dark-cerulean: #0F4976; +$color-prussian-blue: #013A68; +$color-light-cerise: #DE3D63; +$color-alert-purple: #953EA9; +$color-brick-red: #C6254B; +$color-old-rose: #B2314F; +$color-fire-brick: #9E1E3C; +$color-shiraz: #85253B; +$color-falu-red: #77162D; +$color-sea-buckthorn: #F39244; +$color-tahiti-gold: #DA792B; +$color-zest: #C17536; +$color-rich-gold: #AE6122; +$color-afghan-tan: #925829; +$color-russet: #83491A; +$color-elf-green: #23A156; +$color-dark-pigment-green: #0B893D; +$color-salem: #1C8145; +$color-jewel: #096E31; +$color-fun-green: #156134; +$color-dark-jewel: #0C4F26; diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/etc/fonts.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/etc/fonts.scss new file mode 100644 index 0000000000000000000000000000000000000000..6a7cf6a7b86bb29a5c6b2a5ab87cee35e1fcc45e --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/etc/fonts.scss @@ -0,0 +1,28 @@ +/** + * Fonts + */ + +$font-regular: 'Proxima Nova', 'Helvetica Neue', Helvetica, Arial, sans-serif !default; +$font-monospace: "Courier New", Courier, monospace; + +/** + * Type styles + */ + +$font-size-h1: 20px; +$font-size-h2: 26px; +$font-size-h3: 22px; +$font-size-h4: 18px; +$font-size-h5: 13px; +$font-size-body: 13px; // Should be 16px eventually, work towards that end +$font-size-code: 13px; +$font-size-utility: 10px; + +$font-weight-h1: 100; +$font-weight-h2: bold; +$font-weight-h3: bold; +$font-weight-h4: bold; +$font-weight-h5: bold; +$font-weight-body: normal; +$font-weight-code: normal; +$font-weight-utility: normal; diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/example/custom.js b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/example/custom.js new file mode 100644 index 0000000000000000000000000000000000000000..4b38faf34e51382346b97910504b5dd21a0bbbb9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/example/custom.js @@ -0,0 +1,12 @@ +/* + * This file is generated as a starting point by Sencha Cmd - it will not be replaced or + * updated by "sencha package upgrade". + * + * This file can be removed and the script tag in theme.html removed if this theme does + * not need custom additional manifest or shortcut entries. These are documented in + * ./packages/ext-theme-base/sass/example/render.js. + */ + +//Ext.theme.addManifest(); + +//Ext.theme.addShortcuts(); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/example/render.js b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/example/render.js new file mode 100644 index 0000000000000000000000000000000000000000..e65f4e8cd46beef61044ca151907aadd4f2068d2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/example/render.js @@ -0,0 +1,423 @@ +/* + * This file is generated by Sencha Cmd and should NOT be edited. It will be replaced + * during an upgrade. + */ + +Ext.require([ + 'Ext.layout.Context', +]); + +Ext.theme = { + /** + * The array of all component manifests. These objects have the following set of + * properties recognized by the slicer: + * @private + */ + _manifest: [], + + /** + * The collection of shortcuts for a given alias (e.g., 'widget.panel'). This is an + * object keyed by alias whose values are arrays of shortcut definitions. + * @private + */ + _shortcuts: {}, + + doRequire : function(xtype) { + if(xtype.indexOf("widget.") != 0) { + xtype = "widget." + xtype; + } + Ext.require([xtype]); + }, + + /** + * Adds one ore more component entries to the theme manifest. These entries will be + * instantiated by the `Ext.theme.render` method when the page is ready. + * + * Usage: + * + * Ext.theme.addManifest({ + * xtype: 'widget.menu', + * folder: 'menu', + * delegate: '.x-menu-item-link', + * filename: 'menu-item-active', + * config: { + * floating: false, + * width: 200, + * items: [{ + * text: 'test', + * cls: 'x-menu-item-active' + * }] + * } + * },{ + * //... + * }); + * + * @param manifest {Object} An object with type of component, slicing information and + * component configuration. If this parameter is an array, each element is treated as + * a manifest entry. Otherwise, each argument passed is treated as a manifest entry. + * + * @param manifest.xtype {String} The xtype ('grid') or alias ('widget.grid'). This + * is used to specify the type of component to create as well as a potential key to + * any `shortcuts` defined for the xtype. + * + * @param manifest.config {Object} The component configuration object. The properties + * of this depend on the `xtype` of the component. + * + * @param [manifest.delegate] {String} The DOM query to use to select the element to + * slice. The default is to slice the primary element of the component. + * + * @param [manifest.parentCls] An optional CSS class to add to the parent of the + * component. + * + * @param [manifest.setup] {Function} An optional function to be called to initialize + * the component. + * @param manifest.setup.component {Ext.Component} The component instance + * @param manifest.setup.container {Element} The component's container. + * + * @param [manifest.folder] {String} The folder in to which to produce image slices. + * Only applies to Ext JS 4.1 (removed in 4.2). + * + * @param [manifest.filename] {String} The base filename for slices. + * Only applies to Ext JS 4.1 (removed in 4.2). + * + * @param [manifest.reverse] {Boolean} True to position the slices for linear gradient + * background at then opposite "end" (right or bottom) and apply the stretch to the + * area before it (left or top). Only applies to Ext JS 4.1 (removed in 4.2). + */ + addManifest: function (manifest) { + var all = Ext.theme._manifest; + var add = Ext.isArray(manifest) ? manifest : arguments; + + if(manifest.xtype) { + Ext.theme.doRequire(manifest.xtype); + } + + for (var i = 0, n = add.length; i < n; ++i) { + all.push(add[i]); + } + }, + + /** + * Adds one or more shortcuts to the rendering process. A `shortcut` is an object that + * looks the same as a `manifest` entry. These are combined by copying the properties + * from the shortcut over those of the manifest entry. In basic terms: + * + * var config = Ext.apply(Ext.apply({}, manfiest.config), shortcut.config); + * var entry = Ext.apply(Ext.apply({}, manfiest), shortcut); + * entry.config = config; + * + * This is not exactly the process, but the idea is the same. The difference is that + * the `ui` of the manifest entry is used to replace any `"{ui}"` substrings found in + * any string properties of the shortcut or its `config` object. + * + * Usage: + * + * Ext.theme.addShortcuts({ + * 'widget.foo': [{ + * config: { + * } + * },{ + * config: { + * } + * }], + * + * 'widget.bar': [ ... ] + * }); + */ + addShortcuts: function (shortcuts) { + var all = Ext.theme._shortcuts; + + for (var key in shortcuts) { + + var add = shortcuts[key]; + var xtype = Ext.theme.addWidget(key); + var existing = all[xtype]; + + Ext.theme.doRequire(xtype); + for(var i=0; i < add.length; i++) { + var config = add[i]; + if(config.xtype) { + Ext.theme.doRequire(config.xtype); + } + } + + if (!existing) { + all[xtype] = existing = []; + } + + existing.push.apply(existing, add); + } + }, + + /** + * This method ensures that a given string has the specified prefix (e.g., "widget."). + * @private + */ + addPrefix: function (prefix, s) { + if (!s || (s.length > prefix.length && s.substring(0,prefix.length) === prefix)) { + return s; + } + return prefix + s; + }, + + /** + * This method returns the given string with "widget." added to the front if that is + * not already present. + * @private + */ + addWidget: function (str) { + return Ext.theme.addPrefix('widget.', str); + }, + + /** + * This method accepts an manifest entry and a shortcut entry and returns the merged + * version. + * @private + */ + applyShortcut: function (manifestEntry, shortcut) { + var ui = manifestEntry.ui; + var config = Ext.theme.copyProps({}, manifestEntry.config); + var entry = Ext.theme.copyProps({}, manifestEntry); + + if (ui && !config.ui) { + config.ui = ui; + } + if (shortcut) { + var tpl = { ui: ui }; + Ext.theme.copyProps(entry, shortcut, tpl); + Ext.theme.copyProps(config, shortcut.config, tpl); + } + + entry.xtype = Ext.theme.addWidget(entry.xtype); + entry.config = config; // both guys have "config" so smash merged one on now... + return entry; + }, + + /** + * This method copies property from a `src` object to a `dest` object and reaplces + * `"{foo}"` fragments of any string properties as defined in the `tpl` object. + * + * var obj = Ext.theme.copyProps({}, { + * foo: 'Hello-{ui}' + * }, { + * ui: 'World' + * }); + * + * console.log('obj.foo: ' + obj.foo); // logs "Hello-World" + * + * @return {Object} The `dest` object or a new object (if `dest` was null). + * @private + */ + copyProps: function (dest, src, tpl) { + var out = dest || {}; + var replacements = []; + var token; + + if (src) { + if (tpl) { + for (token in tpl) { + replacements.push({ + re: new RegExp('\\{' + token + '\\}', 'g'), + value: tpl[token] + }); + } + } + + for (var key in src) { + var val = src[key]; + if (tpl && typeof val === 'string') { + for (var i = 0; i < replacements.length; ++ i) { + val = val.replace(replacements[i].re, replacements[i].value); + } + } + out[key] = val; + } + } + + return out; + }, + + /** + * Renders a component given its manifest and shortcut entries. + * @private + */ + renderWidget: function (manifestEntry, shortcut) { + var entry = Ext.theme.applyShortcut(manifestEntry, shortcut); + var config = entry.config; + var widget = Ext.create(entry.xtype, config); + var ct = Ext.fly(document.body).createChild({ cls: 'widget-container' }); + + Ext.theme.currentWidget = widget; + + if (widget.floating === true) { + widget.floating = { shadow: false }; + } + if (widget.floating) { + widget.focusOnToFront = false; + } + + if (entry.setup) { + entry.setup.call(widget, widget, ct); + } else { + widget.render(ct); + if (widget.floating) { + widget.showAt(0, 0); + ct.setHeight(widget.getHeight()); + } + } + + var el = widget.el; + if (entry.delegate) { + el = el.down(entry.delegate); + } + + el.addCls('x-slicer-target'); // this is what generateSlicerManifest looks for + + if (entry.over) { + widget.addOverCls(); + } + if (config.parentCls) { + el.parent().addCls(config.parentCls); + } + + if (Ext.theme.legacy) { + // The 4.1 approach has some interesting extra pieces + // + var data = {}; + if (entry.reverse) { + data.reverse = true; + } + if (entry.filename) { + data.filename = entry.filename; + } + if (entry.folder) { + data.folder = entry.folder; + } + if (entry.offsets) { + data.offsets = entry.offsets; + } + + Ext.theme.setData(el.dom, data); + } + + Ext.theme.currentWidget = null; + }, + + /** + * Renders all of the components that have been added to the manifest. + * @private + */ + render: function () { + var manifest = Ext.theme._manifest; + var shortcuts = Ext.theme._shortcuts; + + for (var k = 0, n = manifest ? manifest.length : 0; k < n; ++k) { + var manifestEntry = manifest[k]; + var xtype = Ext.theme.addWidget(manifestEntry.xtype); + var widgetShortcuts = xtype ? shortcuts[xtype] : null; + + if (xtype && manifestEntry.ui && widgetShortcuts) { + for (var i = 0; i < widgetShortcuts.length; i++) { + Ext.theme.renderWidget(manifestEntry, widgetShortcuts[i]); + } + } else { + Ext.theme.renderWidget(manifestEntry); + } + } + }, + + /** + * Renders all components (see `render`) and notifies the Slicer that things are ready. + * @private + */ + run: function () { + var extjsVer = Ext.versions.extjs; + var globalData = {}; + + if (Ext.layout.Context) { + Ext.override(Ext.layout.Context, { + run: function () { + var ok = this.callParent(), + widget = Ext.theme.currentWidget; + if (!ok && widget) { + Ext.Error.raise("Layout run failed: " + widget.id); + } + return ok; + } + }); + } + + // Previous to Ext JS 4.2, themes and their manifests where defined differently. + // So pass this along if we are hosting a pre-4.2 theme. + // + if (extjsVer && extjsVer.isLessThan(new Ext.Version("4.2"))) { + globalData.format = "1.0"; // tell the Slicer tool + Ext.theme.legacy = true; // not for our own data collection + + // Check for the Cmd3.0/ExtJS4.1 variables: + // + if (Ext.manifest && Ext.manifest.widgets) { + Ext.theme.addManifest(Ext.manifest.widgets); + } + if (Ext.shortcuts) { + Ext.theme.addShortcuts(Ext.shortcuts); + } + if (Ext.userManifest && Ext.userManifest.widgets) { + Ext.theme.addManifest(Ext.userManifest.widgets); + } + } + + Ext.theme.setData(document.body, globalData); + + Ext.theme.render(); + + // This function is defined by slicer.js (the framework-independent piece) + Ext.defer(generateSlicerManifest, 1); + }, + + /** + * Sets the `data-slicer` attribute to the JSON-encoded value of the provided data. + * @private + */ + setData: function (el, data) { + if (data) { + var json = Ext.encode(data); + if (json !== '{}') { + el.setAttribute('data-slicer', json); + } + } + }, + + /** + * This used to be `loadExtStylesheet`. + * @private + */ + loadCss: function (src, callback) { + var xhr = new XMLHttpRequest(); + + xhr.open('GET', src); + + xhr.onload = function() { + var css = xhr.responseText, + head = document.getElementsByTagName('head')[0], + style = document.createElement('style'); + + // There's bugginess in the next gradient syntax in WebKit r84622 + // This might be fixed in a later WebKit, but for now we're going to + // strip it out here since compass generates it. + // + // TODO: Upgrade to later WebKit revision + css = css.replace(/background(-image)?: ?-webkit-linear-gradient(?:.*?);/g, ''); + + style.type = 'text/css'; + style.innerText = css; + + head.appendChild(style); + callback(); + }; + + xhr.send(null); + } +}; + +Ext.onReady(Ext.theme.run, Ext.theme); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/example/theme.html b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/example/theme.html new file mode 100644 index 0000000000000000000000000000000000000000..55a1993e2200378a2dad2707b7e767cfee1589d5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/example/theme.html @@ -0,0 +1,44 @@ + + + + + Ext JS Theme Harness + + + + + + + + + + + + + + + + + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/button/Button.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/button/Button.scss new file mode 100644 index 0000000000000000000000000000000000000000..99afb3a989c90a904ff46f7204b3256a687d0b6b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/button/Button.scss @@ -0,0 +1,155 @@ +/** + * @class Ext.button.Button + */ + +/** + * ui: 'default' + * + * This is not namespaced, because 'default' is the theme a component + * receives when 'ui' is not set. + */ + +@include extjs-button-small-ui( + $ui: 'default', + + $background-gradient: $color-white $color-light-gainsboro, + $background-gradient-over: $color-light-gray $color-silver, + $background-gradient-focus: $color-white $color-light-gainsboro, + $background-gradient-pressed: $color-suva-gray $color-gray, + $background-gradient-disabled: $color-white $color-light-gainsboro, + + $color: $color-night-rider, + $color-over: $color-night-rider, + $color-focus: $color-night-rider, + $color-pressed: $color-white, + $color-disabled: $color-dark-gray, + + $glyph-color: $color-night-rider, + $glyph-opacity: 1 +); + +/** + * ui: 'nx-plain' + */ + +@include extjs-button-small-ui( + $ui: 'nx-plain', + + $border-color: $color-gainsboro, + $border-color-over: $color-gainsboro, + $border-color-pressed: $color-gainsboro, + $border-color-disabled: $color-gainsboro, + + $background-color: $color-white, + $background-color-over: $color-light-gray, + $background-color-focus: $color-white, + $background-color-pressed: $color-suva-gray, + $background-color-disabled: $color-white, + + $background-gradient: $color-white $color-light-gainsboro, + $background-gradient-over: $color-light-gray $color-silver, + $background-gradient-focus: $color-white $color-light-gainsboro, + $background-gradient-pressed: $color-suva-gray $color-gray, + $background-gradient-disabled: $color-white $color-light-gainsboro, + + $color: $color-night-rider, + $color-over: $color-night-rider, + $color-focus: $color-night-rider, + $color-pressed: $color-white, + $color-disabled: $color-dark-gray, + + $glyph-color: $color-night-rider, + $glyph-opacity: 1 +); + +/** + * ui: 'nx-primary' + */ + +@include extjs-button-small-ui( + $ui: 'nx-primary', + + $background-color: $color-denim, + $background-color-over: $color-dark-denim, + $background-color-focus: $color-denim, + $background-color-pressed: $color-dark-cerulean, + $background-color-disabled: $color-denim, + + $background-gradient: $color-denim $color-light-cobalt, + $background-gradient-over: $color-dark-denim $color-smalt, + $background-gradient-focus: $color-denim $color-light-cobalt, + $background-gradient-pressed: $color-dark-cerulean $color-prussian-blue, + $background-gradient-disabled: $color-denim $color-light-cobalt +); + +/** + * ui: 'nx-danger' + */ + +@include extjs-button-small-ui( + $ui: 'nx-danger', + + $background-color: $color-light-cerise, + $background-color-over: $color-old-rose, + $background-color-focus: $color-light-cerise, + $background-color-pressed: $color-shiraz, + $background-color-disabled: $color-light-cerise, + + $background-gradient: $color-light-cerise $color-brick-red, + $background-gradient-over: $color-old-rose $color-fire-brick, + $background-gradient-focus: $color-light-cerise $color-brick-red, + $background-gradient-pressed: $color-shiraz $color-falu-red, + $background-gradient-disabled: $color-light-cerise $color-brick-red +); + +/** + * ui: 'nx-warning' + */ + +@include extjs-button-small-ui( + $ui: 'nx-warning', + + $background-color: $color-sea-buckthorn, + $background-color-over: $color-zest, + $background-color-focus: $color-sea-buckthorn, + $background-color-pressed: $color-afghan-tan, + $background-color-disabled: $color-sea-buckthorn, + + $background-gradient: $color-sea-buckthorn $color-tahiti-gold, + $background-gradient-over: $color-zest $color-rich-gold, + $background-gradient-focus: $color-sea-buckthorn $color-tahiti-gold, + $background-gradient-pressed: $color-afghan-tan $color-russet, + $background-gradient-disabled: $color-sea-buckthorn $color-tahiti-gold +); + +/** + * ui: 'nx-success' + */ + +@include extjs-button-small-ui( + $ui: 'nx-success', + + $background-color: $color-elf-green, + $background-color-over: $color-salem, + $background-color-focus: $color-elf-green, + $background-color-pressed: $color-fun-green, + $background-color-disabled: $color-elf-green, + + $background-gradient: $color-elf-green $color-dark-pigment-green, + $background-gradient-over: $color-salem $color-jewel, + $background-gradient-focus: $color-elf-green $color-dark-pigment-green, + $background-gradient-pressed: $color-fun-green $color-dark-jewel, + $background-gradient-disabled: $color-elf-green $color-dark-pigment-green +); + +// Fix glyph font size so the top of the icon isn’t cropped +.x-btn-default-small, +.x-btn-plain-small, +.x-btn-primary-small, +.x-btn-danger-small, +.x-btn-warning-small, +.x-btn-success-small { + .x-btn-glyph { + font-size: $font-size-body; + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/container/Container.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/container/Container.scss new file mode 100644 index 0000000000000000000000000000000000000000..690bb71d3400b776a263c679ae6372c5a532d98c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/container/Container.scss @@ -0,0 +1,35 @@ +/** + * @class Ext.container.Container + */ + +/** + * Styles a scrollable container which contains multiple nx-subsection containers + * + * cls: 'nx-inset' + */ +.nx-inset { + padding: 12px 12px 12px 12px; +} + +/** + * Styles a subsection inside an nx-inset + * + * cls: 'nx-subsection' + */ +.nx-subsection { + padding: 12px 12px 12px 12px; + background-color: $color-white; + border-top: 1px solid $color-gainsboro !important; + border-right: 1px solid $color-gainsboro !important; + border-bottom: 1px solid $color-gainsboro !important; + border-left: 1px solid $color-gainsboro !important; +} + +/** + * Styles a section as an important message + * cls: 'nx-form-important-msg' + */ +.nx-form-important-msg { + font-size: 10px; + font-weight: bold; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/draw/Text.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/draw/Text.scss new file mode 100644 index 0000000000000000000000000000000000000000..e95f906d2ccf8704325d39de2c10604dbf5f4231 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/draw/Text.scss @@ -0,0 +1,7 @@ +/** + * @class Ext.draw.Text + */ + +.nx-table-header-label { + font-weight: bold; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/form/CheckboxGroup.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/form/CheckboxGroup.scss new file mode 100644 index 0000000000000000000000000000000000000000..81c21103327802e5c55ab62e224645463d74c1ea --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/form/CheckboxGroup.scss @@ -0,0 +1,7 @@ +/** + * @class Ext.form.CheckboxGroup + */ + +.x-form-checkboxgroup-body { + padding: 0; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/form/Fieldset.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/form/Fieldset.scss new file mode 100644 index 0000000000000000000000000000000000000000..5508106743cbe895441706855d69f40767132af8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/form/Fieldset.scss @@ -0,0 +1,21 @@ +/** + * @class Ext.form.Fieldset + */ + +fieldset.nx-form-section { + margin-bottom: 0; + padding-bottom: 20px; + + .x-fieldset-body { + padding-left: 10px; + } + + &.nx-no-title { + padding-top: 0; + } +} + +fieldset.nx-form-section > legend > span > div > .x-fieldset-header-text { + font-size: $font-size-h4; + line-height: 22px; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/form/Label.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/form/Label.scss new file mode 100644 index 0000000000000000000000000000000000000000..be5fcd6ffadfee3e5931d86903313d9fda4069b6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/form/Label.scss @@ -0,0 +1,10 @@ +/** + * @class Ext.form.Label + */ + +.nx-monospace-field { + textarea, input { + font-family: $font-monospace; + font-size: $font-size-code; + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/form/field/Base.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/form/field/Base.scss new file mode 100644 index 0000000000000000000000000000000000000000..2a923c2f08e1e34a81039fd38b9f62aefaad0be9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/form/field/Base.scss @@ -0,0 +1,67 @@ +/** + * @class Ext.form.field.Base + */ + +/** + * Style for read-only fields + * + * cls: 'x-form-readonly' + */ + +@mixin nx-form-field-readonly { + color: $color-dark-gray; + background-color: $color-smoke; + opacity: 1; +} + +.x-form-field[readonly].x-form-field:not([role="combobox"]) { + @include nx-form-field-readonly; +} + +/** + * editable = false on a combo sets readonly already, so use a custom class to achieve the same effect + */ +.nx-combo-disabled { + input { + @include nx-form-field-readonly; + } +} + +.x-form-fieldcontainer .x-mask { + background-color: $color-gainsboro; + opacity: 0.6; +} + +/** + * Styles which allow for horizontal alignment of form fields + */ + +.nx-float-left { + position: relative; + float: left !important; + margin-right: 5px; +} + +.nx-interstitial-label { + font-size: 13px; + padding-top: 5px; +} + +.nx-clear-both { + clear: both; +} + +/** + * Styling for the boxlabel of form fields + */ +.x-field .nx-boxlabel { + font-size: $font-size-utility; +} + +/** + * IE10 adds a second 'x' icon to text fields. Hide this. + */ +::-ms-clear { + width: 0; + height: 0; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/grid/Panel.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/grid/Panel.scss new file mode 100644 index 0000000000000000000000000000000000000000..b6fdfb8d8ea75dede9134fd34ae894a3f7f1e36b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/grid/Panel.scss @@ -0,0 +1,40 @@ +/** + * @class Ext.grid.Panel + */ + +/** + * ui: 'borderless' + */ +@include extjs-panel-ui( + $ui: 'borderless', + + $ui-body-border-width: 0, + $ui-padding: 5px 0 0 0 +); + +/** + * cls: 'nx-rowexpander' + */ +.nx-rowexpander { + padding: 5px; + + tbody { + td:first-child { + padding-right: 5px; + font-weight: bold; + } + } +} + +/** + * cls: 'nx-disabled-row' + */ +.nx-disabled-row { + td { + color: $color-dark-gray; + + button { + color: $color-dark-gray; + } + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/grid/column/Column.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/grid/column/Column.scss new file mode 100644 index 0000000000000000000000000000000000000000..f2e58b02474e7c3f57eed01d975ffb6c6069ec6c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/grid/column/Column.scss @@ -0,0 +1,7 @@ +/** + * @class Ext.grid.column.Column + */ + +.nx-middle-align { + vertical-align: middle; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/panel/Panel.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/panel/Panel.scss new file mode 100644 index 0000000000000000000000000000000000000000..77ae7d88e1f50a4f5c6dbb9887a43d9aa52cfbe3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/panel/Panel.scss @@ -0,0 +1,145 @@ +/** + * @class Ext.panel.Panel + */ + +/** + * ui: 'nx-inset' + */ +@include extjs-panel-ui( + $ui: 'nx-inset', + + $ui-padding: 12px 12px 0 12px +); + +.x-panel-nx-inset { + .x-panel-body { + .x-toolbar { + padding: 6px 0 6px 0; + } + } + + .x-panel-nx-subsection-framed { + margin: 0 0 10px 0; + } +} + +/** + * Style for the global "license warning" header + */ +@include extjs-panel-ui( + $ui: 'nx-license-warning', + $ui-header-background-color: $color-charcoal +); + +.x-panel-nx-license-warning .x-panel-header { + padding: 5px 16px; +} +.x-panel-nx-license-warning { + color: $color-white; +} + +.x-panel-nx-license-warning a, +.x-panel-nx-license-warning a:visited, +.x-panel-nx-license-warning a:active { + color: $color-gainsboro; +} + +.x-panel-nx-license-warning a:hover { + color: $color-white; + text-decoration: underline; +} + +/** + * Style for the global "node frozen warning" header + */ +.x-panel-nx-database-freeze-warning { + @extend .x-panel-nx-license-warning; + background-color: $color-alert-purple; +} + +.x-panel-header-text-container-nx-database-freeze-warning { + margin-left: 6px; + font-weight: bold; +} + +/** + * Style for the global "file descriptor warning" header + */ +.x-panel-nx-file-descriptor-warning { + @extend .x-panel-nx-license-warning; + background-color: $color-alert-purple; +} + +.x-panel-header-text-container-nx-file-descriptor-warning { + margin-left: 6px; + font-weight: bold; +} + +/** +* Style for the "transparent" inter-page panels +* +* ui: 'subsection' +*/ + +@include extjs-panel-ui( + $ui: 'nx-subsection', + + $ui-header-color: $color-night-rider, + $ui-header-font-size: $font-size-h4, + $ui-header-background-color: $color-white, + $ui-header-line-height: 22px +); + +@include extjs-panel-ui( + $ui: 'nx-subsection-framed', + + $ui-header-color: $color-night-rider, + $ui-header-font-size: $font-size-h4, + $ui-header-background-color: $color-white, + $ui-header-line-height: 22px, + $ui-body-background-color: $color-white +); + +.x-panel-nx-subsection > .x-panel-header { + padding: 10px 0 5px 0 !important; +} + +.x-panel-nx-subsection-framed { + border: $color-gainsboro 1px solid; +} + +.x-panel-nx-subsection-framed > .x-panel-header { + padding: 10px 9px 5px 9px !important; +} + +.x-panel-nx-subsection-framed > .x-panel-body { + padding: 9px 9px 10px 9px !important; +} + +/** + * cls: 'nx-hr' + */ +.nx-hr { + border-top: 1px solid $color-gainsboro !important; +} + +/** + * Style for the 'too many results' panel on the search page + */ +@include extjs-panel-ui( + $ui: 'nx-info-message', + + $ui-header-background-color: $color-white, + $ui-header-color: $color-night-rider +); +.x-panel-nx-info-message .x-header { + padding: 0 10px 10px 10px; +} +.nx-search-result-list { + border-top: 1px solid $color-gainsboro; +} + +// for rows that are repeated and identical +.nx-repeated-row { + margin-bottom: 5px; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/tab/Panel.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/tab/Panel.scss new file mode 100644 index 0000000000000000000000000000000000000000..0ab6b7f0a2467f4b5469ef16e94d7ab4d6feaab1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/tab/Panel.scss @@ -0,0 +1,103 @@ +/** + * @class Ext.tab.Panel + */ + +/** + * ui: 'light' + */ + +@include extjs-panel-ui( + $ui: 'nx-light', + + $ui-header-color: $color-night-rider, + $ui-header-background-color: $color-light-smoke, + $ui-header-font-size: 16px, + $ui-header-line-height: 20px + + // FIXME: Unable to control icon spacing, unsure why + //$ui-header-icon-spacing: 6px, + //$ui-header-icon-height: 16px, + //$ui-header-icon-width: 16px, + //$ui-header-icon-background-position: $panel-header-icon-background-position, // center center + + // FIXME: Unable to control glyph colors here, unsure why + //$ui-header-glyph-color: $color-smalt, + //$ui-header-glyph-opacity: 1 +); + +@include extjs-tab-ui( + $ui: 'nx-light', + + $ui-background-color: $color-light-smoke, + $ui-background-color-over: $color-smoke, + $ui-background-color-active: $color-white, + $ui-background-color-disabled: $color-light-smoke, + + $ui-margin: 0 2px, + $ui-padding: 5px 12px, + $ui-text-padding: 0, + $ui-font-size: $font-size-body, + $ui-font-weight: $font-weight-body, + $ui-font-weight-over: $font-weight-body, + $ui-font-weight-active: $font-weight-body, + $ui-font-weight-disabled: $font-weight-body, + $ui-border-color: $color-light-smoke, + $ui-border-color-over: $color-gainsboro, + $ui-border-color-active: $color-white, + $ui-border-color-disabled: $color-light-smoke, + + $ui-border-radius: 6px, + $ui-border-width: 1px, + + $ui-color: $color-navy-blue, + $ui-color-over: $color-navy-blue, + $ui-color-active: $color-night-rider, + $ui-color-disabled: $color-night-rider +); + +@include extjs-tab-bar-ui( + $ui: 'nx-light', + + $ui-strip-height: 1px, + $ui-strip-border-width: 1px, + $ui-strip-plain-border-width: 1px, + $ui-strip-border-color: $color-light-gray, + $ui-strip-background-color: $color-light-smoke, + + $ui-padding: 5px 0 2px 0, + $ui-background-color: $color-light-smoke, + $ui-background-gradient: $tabbar-background-gradient +); + +// FIXME: Only apply for top, right and left tabs need additional tweaks + +// add few pixel to keep tabs separated from surrounding container +.x-tab-bar-body-nx-light-top { + padding-left: 5px; +} + +// Set bottom border of tab-strip +.x-tab-bar-strip-nx-light-top { + border-bottom: 1px solid $color-gainsboro; + border-top: none; +} + +// Override default treatment of the bottom borders +.x-tab-nx-light-top, +.x-tab-nx-light-top-disabled { + border-bottom: 1px solid $color-light-smoke; +} + +.x-tab-nx-light-top-active, +.x-tab-nx-light-top-active.x-tab-nx-light-top-over { + border-bottom: 1px solid $color-light-smoke; +} + +.x-tab-nx-light-top-over { + border-bottom: 1px solid $color-gainsboro; +} + +// Force the tab panel body to sit underneath the tab bar +.x-tab-bar + .x-panel-body { + z-index: 0; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/ux/IFrame.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/ux/IFrame.scss new file mode 100644 index 0000000000000000000000000000000000000000..053d3b86ba13550900ff5195dd48e19d40041ebd --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/ux/IFrame.scss @@ -0,0 +1,7 @@ +/** + * @class Ext.ux.IFrame + */ + +.nx-iframe-full { + background-color: $color-white; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/ux/form/ItemSelector.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/ux/form/ItemSelector.scss new file mode 100644 index 0000000000000000000000000000000000000000..88e20e3c99cd383eabe5672323777396f8970cfc --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/ux/form/ItemSelector.scss @@ -0,0 +1,13 @@ +/** +* @class Ext.ux.form.ItemSelector +*/ + +.nx-itemselector-disabled { + .x-form-item-label { + opacity: 1; + } +} + +.x-form-itemselector-body .x-form-item { + margin: 0; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/ux/form/MultiSelect.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/ux/form/MultiSelect.scss new file mode 100644 index 0000000000000000000000000000000000000000..acfce4888828e7989234a2688f13f8bb9e53b2bc --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/ux/form/MultiSelect.scss @@ -0,0 +1,38 @@ +/** +* @class Ext.ux.form.MultiSelect +*/ + +.nx-multiselect { + .x-panel-header { + background-color: transparent; + border: none; + padding-left: 0; + padding-bottom: 9px; + padding-right: 0; + + .x-header-text { + color: $color-night-rider; + font-weight: normal; + } + } + + .x-form-item .x-form-trigger-wrap { + margin-bottom: 9px; + } + + .x-panel-body { + border: 1px $color-gainsboro solid !important; + } +} + +.x-form-fieldcontainer.nx-invalid { + .nx-multiselect:nth-child(3) { + .x-panel-body { + border: 1px $color-cerise solid !important; + } + } +} + +.x-form-multiselect-body .x-boundlist .x-mask { + background: none; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/window/Window.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/window/Window.scss new file mode 100644 index 0000000000000000000000000000000000000000..0fe96769f0dc1a2bbd9a53156b557f9161b19618 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/src/Ext/window/Window.scss @@ -0,0 +1,81 @@ +/** + * @class Ext.window.Window + */ + +/** + * ui: 'nx-inset' + */ +@include extjs-window-ui( + $ui: 'nx-inset', + + $ui-padding: 12px 12px 0 12px, + $ui-header-padding: 10px 10px 8px 14px +); + +@include extjs-window-ui( + $ui: 'nx-message-default', + + $ui-padding: 10px, + $ui-header-line-height: 15px, + $ui-header-font-size: $font-size-h5, + $ui-header-font-weight: $font-weight-h5 +); + +@include extjs-window-ui( + $ui: 'nx-message-primary', + + $ui-padding: 10px, + $ui-header-line-height: 15px, + $ui-header-font-size: $font-size-h5, + $ui-header-font-weight: $font-weight-h5, + $ui-tool-background-image: 'tools/tool-sprites', + $ui-header-color: $color-white, + $ui-header-background-color: $color-cobalt +); + +@include extjs-window-ui( + $ui: 'nx-message-danger', + + $ui-padding: 10px, + $ui-header-line-height: 15px, + $ui-header-font-size: $font-size-h5, + $ui-header-font-weight: $font-weight-h5, + $ui-tool-background-image: 'tools/tool-sprites', + $ui-header-color: $color-white, + $ui-header-background-color: $color-cerise +); + +@include extjs-window-ui( + $ui: 'nx-message-warning', + + $ui-padding: 10px, + $ui-header-line-height: 15px, + $ui-header-font-size: $font-size-h5, + $ui-header-font-weight: $font-weight-h5, + $ui-tool-background-image: 'tools/tool-sprites', + $ui-header-color: $color-white, + $ui-header-background-color: $color-sun +); + +@include extjs-window-ui( + $ui: 'nx-message-success', + + $ui-padding: 10px, + $ui-header-line-height: 15px, + $ui-header-font-size: $font-size-h5, + $ui-header-font-weight: $font-weight-h5, + $ui-tool-background-image: 'tools/tool-sprites', + $ui-header-color: $color-white, + $ui-header-background-color: $color-pigment-green +); + + +// Fixed modals for the visual style guide +.fixed-modal { + top: 0 !important; +} + +// Remove footer toolbar spacing +.x-window-body .x-toolbar-footer { + padding-left: 0 !important; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/Component.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/Component.scss new file mode 100644 index 0000000000000000000000000000000000000000..53cb47b31accc956c6c7c630c64337dc5c9d6575 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/Component.scss @@ -0,0 +1,10 @@ +/** + * @class Ext.Component + */ + +// Sets the overall charcoal-like tone of the application +$base-color: $color-charcoal !default; + +// Use the same font-family for everything +$font-family: $font-regular; +$color: $color-night-rider; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/LoadMask.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/LoadMask.scss new file mode 100644 index 0000000000000000000000000000000000000000..67f13dcd0a258dd0c104df9c96f378c661421906 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/LoadMask.scss @@ -0,0 +1,6 @@ +/** + * @class Ext.LoadMask + */ + +$loadmask-background-color: $color-black; +$loadmask-opacity: 0.3; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/button/Button.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/button/Button.scss new file mode 100644 index 0000000000000000000000000000000000000000..2b2ba34459d15274b63d528be0fc9901c49a2b02 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/button/Button.scss @@ -0,0 +1,55 @@ +/** + * @class Ext.button.Button + */ + +$button-default-color: $color-white; +$button-default-color-over: $color-white; +$button-default-color-focus: $color-white; +$button-default-color-pressed: $color-white; +$button-default-color-disabled: $color-white; + +$button-default-border-color: $color-night-rider; +$button-default-border-color-over: $color-night-rider; +$button-default-border-color-focus: $color-cornflower; +$button-default-border-color-pressed: $color-night-rider; +$button-default-border-color-disabled: $color-night-rider; + +$button-small-font-size: $font-size-body; +$button-small-font-size-over: $font-size-body; +$button-small-font-size-focus: $font-size-body; +$button-small-font-size-pressed: $font-size-body; +$button-small-font-size-disabled: $font-size-body; + +$button-small-font-weight: $font-weight-body; +$button-small-font-weight-over: $font-weight-body; +$button-small-font-weight-focus: $font-weight-body; +$button-small-font-weight-pressed: $font-weight-body; +$button-small-font-weight-disabled: $font-weight-body; + +$button-medium-font-weight: $font-weight-body; +$button-medium-font-weight-over: $font-weight-body; +$button-medium-font-weight-focus: $font-weight-body; +$button-medium-font-weight-pressed: $font-weight-body; +$button-medium-font-weight-disabled: $font-weight-body; + +$button-default-glyph-color: $color-white; +$button-default-glyph-opacity: 1; + +$button-toolbar-color: $color-night-rider; +$button-toolbar-color-over: $color-night-rider; +$button-toolbar-color-focus: $color-night-rider; +$button-toolbar-color-pressed: $color-white; +$button-toolbar-color-disabled: $color-night-rider; + +$button-toolbar-border-color-focus: $color-cornflower; + +$button-toolbar-glyph-color: $color-night-rider; +$button-toolbar-glyph-opacity: 1; + +$button-disabled-opacity: 0.5; + +$button-toolbar-background-gradient: $color-white $color-light-gainsboro; +$button-toolbar-background-gradient-over: $color-light-gray $color-silver; +$button-toolbar-background-gradient-focus: $color-white $color-light-gainsboro; +$button-toolbar-background-gradient-pressed: $color-suva-gray $color-gray; +$button-toolbar-background-gradient-disabled: $color-white $color-light-gainsboro; diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/form/Fieldset.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/form/Fieldset.scss new file mode 100644 index 0000000000000000000000000000000000000000..3c91a663eb6566c344eb3ab9307929ef94101f9f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/form/Fieldset.scss @@ -0,0 +1,9 @@ +/** + * @class Ext.form.Fieldset + */ + +$fieldset-header-font-size: $font-size-h5; +$fieldset-header-font-weight: $font-weight-h5; +$fieldset-padding: 10px 0 25px 0px; +$fieldset-header-padding: 0 8px 0 0; +$fieldset-border-width: 0; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/form/field/Base.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/form/field/Base.scss new file mode 100644 index 0000000000000000000000000000000000000000..22fe6e93aad387b935287373194dc3af436de742 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/form/field/Base.scss @@ -0,0 +1,5 @@ +/** + * @class Ext.form.field.Base + */ + +$form-field-focus-border-color: $color-cornflower !important; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/panel/Panel.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/panel/Panel.scss new file mode 100644 index 0000000000000000000000000000000000000000..1debd2b26e13c7b0c8e37b921a3a9d2f1a5a0d0b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/panel/Panel.scss @@ -0,0 +1,5 @@ +/** + * @class Ext.panel.Panel + */ + +$panel-body-background-color: transparent; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/tip/QuickTip.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/tip/QuickTip.scss new file mode 100644 index 0000000000000000000000000000000000000000..dafa5945db0005f8d343f0aa5d67a225bcbfb152 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/tip/QuickTip.scss @@ -0,0 +1,6 @@ +/** + * @class Ext.tip.QuickTip + */ + +$tip-border-color: $color-energy-yellow; +$tip-background-color: $color-floral-white; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/tip/ToolTip.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/tip/ToolTip.scss new file mode 100644 index 0000000000000000000000000000000000000000..94fcb8aa59a409bd82a119afafd975240aecf3c8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/tip/ToolTip.scss @@ -0,0 +1,5 @@ +/** + * @class Ext.tip.ToolTip + */ + +$tip-body-link-color: $color-navy-blue; diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/toolbar/Toolbar.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/toolbar/Toolbar.scss new file mode 100644 index 0000000000000000000000000000000000000000..5136359c3b07d77cd684387496cd9a73126e871b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/toolbar/Toolbar.scss @@ -0,0 +1,6 @@ +/** + * @class Ext.toolbar.Toolbar + */ + +$toolbar-background-color: transparent; +$toolbar-footer-background-color: transparent; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/window/Window.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/window/Window.scss new file mode 100644 index 0000000000000000000000000000000000000000..a9ade57bb44feae496c02541a7c81302c68c802f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/packages/rapture-theme/sass/var/Ext/window/Window.scss @@ -0,0 +1,13 @@ +/** + * @class Ext.window.Window + */ + +$panel-frame-border-width: 2px; +$window-header-background-color: $color-smoke; +$window-header-color: $color-night-rider; +$window-header-border-width: 0; +$window-header-padding: 10px; +$window-header-line-height: 22px; +$window-header-font-size: $font-size-h4; +$window-header-font-weight: $font-weight-h4; +$window-tool-background-image: 'tools/tool-sprites-dark'; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/config.rb b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/config.rb new file mode 100644 index 0000000000000000000000000000000000000000..086229e9a4603f34a2e8e082a85d682ed86097e8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/config.rb @@ -0,0 +1,5 @@ +cur_dir = File.dirname(__FILE__) +output_style = :nested + +# HACK: workaround to issue setting line_comment=false from require'd config inclusion +::Compass.configuration.line_comments = false \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/etc/all.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/etc/all.scss new file mode 100644 index 0000000000000000000000000000000000000000..bcda4b4fc5d86dce1936219cf42462336d5db15b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/etc/all.scss @@ -0,0 +1,5 @@ +// +// SCSS utility styles. These are included before var/... and src/... files. +// + +// placeholder \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/example/bootstrap.css b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/example/bootstrap.css new file mode 100644 index 0000000000000000000000000000000000000000..b29582fe85b9be2dcb1b17e3f8eec9e95ec7aad0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/example/bootstrap.css @@ -0,0 +1,7 @@ +/** + * This file is generated by Sencha Cmd and should NOT be edited. It will + * redirect to the most recently built example css file for the application to + * support capture of theme background, frame, and corner images for non-CSS3 + * browsers. + */ +@import '../../bootstrap.css'; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/example/custom.js b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/example/custom.js new file mode 100644 index 0000000000000000000000000000000000000000..4b38faf34e51382346b97910504b5dd21a0bbbb9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/example/custom.js @@ -0,0 +1,12 @@ +/* + * This file is generated as a starting point by Sencha Cmd - it will not be replaced or + * updated by "sencha package upgrade". + * + * This file can be removed and the script tag in theme.html removed if this theme does + * not need custom additional manifest or shortcut entries. These are documented in + * ./packages/ext-theme-base/sass/example/render.js. + */ + +//Ext.theme.addManifest(); + +//Ext.theme.addShortcuts(); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/example/render.js b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/example/render.js new file mode 100644 index 0000000000000000000000000000000000000000..7b04405dbcee84a70919ce30b26646b47bd1781c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/example/render.js @@ -0,0 +1,426 @@ +/* + * This file is generated by Sencha Cmd and should NOT be edited. It will be replaced + * during an upgrade. + */ + +Ext.require([ + 'Ext.layout.Context', +]); + +Ext.theme = { + /** + * The array of all component manifests. These objects have the following set of + * properties recognized by the slicer: + * @private + */ + _manifest: [], + + /** + * The collection of shortcuts for a given alias (e.g., 'widget.panel'). This is an + * object keyed by alias whose values are arrays of shortcut definitions. + * @private + */ + _shortcuts: {}, + + doRequire : function(xtype) { + if(xtype.indexOf("widget.") != 0) { + xtype = "widget." + xtype; + } + Ext.require([xtype]); + }, + + /** + * Adds one ore more component entries to the theme manifest. These entries will be + * instantiated by the `Ext.theme.render` method when the page is ready. + * + * Usage: + * + * Ext.theme.addManifest({ + * xtype: 'widget.menu', + * folder: 'menu', + * delegate: '.x-menu-item-link', + * filename: 'menu-item-active', + * config: { + * floating: false, + * width: 200, + * items: [{ + * text: 'test', + * cls: 'x-menu-item-active' + * }] + * } + * },{ + * //... + * }); + * + * @param manifest {Object} An object with type of component, slicing information and + * component configuration. If this parameter is an array, each element is treated as + * a manifest entry. Otherwise, each argument passed is treated as a manifest entry. + * + * @param manifest.xtype {String} The xtype ('grid') or alias ('widget.grid'). This + * is used to specify the type of component to create as well as a potential key to + * any `shortcuts` defined for the xtype. + * + * @param manifest.config {Object} The component configuration object. The properties + * of this depend on the `xtype` of the component. + * + * @param [manifest.delegate] {String} The DOM query to use to select the element to + * slice. The default is to slice the primary element of the component. + * + * @param [manifest.parentCls] An optional CSS class to add to the parent of the + * component. + * + * @param [manifest.setup] {Function} An optional function to be called to initialize + * the component. + * @param manifest.setup.component {Ext.Component} The component instance + * @param manifest.setup.container {Element} The component's container. + * + * @param [manifest.folder] {String} The folder in to which to produce image slices. + * Only applies to Ext JS 4.1 (removed in 4.2). + * + * @param [manifest.filename] {String} The base filename for slices. + * Only applies to Ext JS 4.1 (removed in 4.2). + * + * @param [manifest.reverse] {Boolean} True to position the slices for linear gradient + * background at then opposite "end" (right or bottom) and apply the stretch to the + * area before it (left or top). Only applies to Ext JS 4.1 (removed in 4.2). + */ + addManifest: function (manifest) { + var all = Ext.theme._manifest; + var add = Ext.isArray(manifest) ? manifest : arguments; + + if(manifest.xtype) { + Ext.theme.doRequire(manifest.xtype); + } + + for (var i = 0, n = add.length; i < n; ++i) { + if(add[i].xtype) { + Ext.theme.doRequire(add[i].xtype); + } + all.push(add[i]); + } + }, + + /** + * Adds one or more shortcuts to the rendering process. A `shortcut` is an object that + * looks the same as a `manifest` entry. These are combined by copying the properties + * from the shortcut over those of the manifest entry. In basic terms: + * + * var config = Ext.apply(Ext.apply({}, manfiest.config), shortcut.config); + * var entry = Ext.apply(Ext.apply({}, manfiest), shortcut); + * entry.config = config; + * + * This is not exactly the process, but the idea is the same. The difference is that + * the `ui` of the manifest entry is used to replace any `"{ui}"` substrings found in + * any string properties of the shortcut or its `config` object. + * + * Usage: + * + * Ext.theme.addShortcuts({ + * 'widget.foo': [{ + * config: { + * } + * },{ + * config: { + * } + * }], + * + * 'widget.bar': [ ... ] + * }); + */ + addShortcuts: function (shortcuts) { + var all = Ext.theme._shortcuts; + + for (var key in shortcuts) { + + var add = shortcuts[key]; + var xtype = Ext.theme.addWidget(key); + var existing = all[xtype]; + + Ext.theme.doRequire(xtype); + for(var i=0; i < add.length; i++) { + var config = add[i]; + if(config.xtype) { + Ext.theme.doRequire(config.xtype); + } + } + + if (!existing) { + all[xtype] = existing = []; + } + + existing.push.apply(existing, add); + } + }, + + /** + * This method ensures that a given string has the specified prefix (e.g., "widget."). + * @private + */ + addPrefix: function (prefix, s) { + if (!s || (s.length > prefix.length && s.substring(0,prefix.length) === prefix)) { + return s; + } + return prefix + s; + }, + + /** + * This method returns the given string with "widget." added to the front if that is + * not already present. + * @private + */ + addWidget: function (str) { + return Ext.theme.addPrefix('widget.', str); + }, + + /** + * This method accepts an manifest entry and a shortcut entry and returns the merged + * version. + * @private + */ + applyShortcut: function (manifestEntry, shortcut) { + var ui = manifestEntry.ui; + var config = Ext.theme.copyProps({}, manifestEntry.config); + var entry = Ext.theme.copyProps({}, manifestEntry); + + if (ui && !config.ui) { + config.ui = ui; + } + if (shortcut) { + var tpl = { ui: ui }; + Ext.theme.copyProps(entry, shortcut, tpl); + Ext.theme.copyProps(config, shortcut.config, tpl); + } + + entry.xtype = Ext.theme.addWidget(entry.xtype); + entry.config = config; // both guys have "config" so smash merged one on now... + return entry; + }, + + /** + * This method copies property from a `src` object to a `dest` object and reaplces + * `"{foo}"` fragments of any string properties as defined in the `tpl` object. + * + * var obj = Ext.theme.copyProps({}, { + * foo: 'Hello-{ui}' + * }, { + * ui: 'World' + * }); + * + * console.log('obj.foo: ' + obj.foo); // logs "Hello-World" + * + * @return {Object} The `dest` object or a new object (if `dest` was null). + * @private + */ + copyProps: function (dest, src, tpl) { + var out = dest || {}; + var replacements = []; + var token; + + if (src) { + if (tpl) { + for (token in tpl) { + replacements.push({ + re: new RegExp('\\{' + token + '\\}', 'g'), + value: tpl[token] + }); + } + } + + for (var key in src) { + var val = src[key]; + if (tpl && typeof val === 'string') { + for (var i = 0; i < replacements.length; ++ i) { + val = val.replace(replacements[i].re, replacements[i].value); + } + } + out[key] = val; + } + } + + return out; + }, + + /** + * Renders a component given its manifest and shortcut entries. + * @private + */ + renderWidget: function (manifestEntry, shortcut) { + var entry = Ext.theme.applyShortcut(manifestEntry, shortcut); + var config = entry.config; + var widget = Ext.create(entry.xtype, config); + var ct = Ext.fly(document.body).createChild({ cls: 'widget-container' }); + + Ext.theme.currentWidget = widget; + + if (widget.floating === true) { + widget.floating = { shadow: false }; + } + if (widget.floating) { + widget.focusOnToFront = false; + } + + if (entry.setup) { + entry.setup.call(widget, widget, ct); + } else { + widget.render(ct); + if (widget.floating) { + widget.showAt(0, 0); + ct.setHeight(widget.getHeight()); + } + } + + var el = widget.el; + if (entry.delegate) { + el = el.down(entry.delegate); + } + + el.addCls('x-slicer-target'); // this is what generateSlicerManifest looks for + + if (entry.over) { + widget.addOverCls(); + } + if (config.parentCls) { + el.parent().addCls(config.parentCls); + } + + if (Ext.theme.legacy) { + // The 4.1 approach has some interesting extra pieces + // + var data = {}; + if (entry.reverse) { + data.reverse = true; + } + if (entry.filename) { + data.filename = entry.filename; + } + if (entry.folder) { + data.folder = entry.folder; + } + if (entry.offsets) { + data.offsets = entry.offsets; + } + + Ext.theme.setData(el.dom, data); + } + + Ext.theme.currentWidget = null; + }, + + /** + * Renders all of the components that have been added to the manifest. + * @private + */ + render: function () { + var manifest = Ext.theme._manifest; + var shortcuts = Ext.theme._shortcuts; + + for (var k = 0, n = manifest ? manifest.length : 0; k < n; ++k) { + var manifestEntry = manifest[k]; + var xtype = Ext.theme.addWidget(manifestEntry.xtype); + var widgetShortcuts = xtype ? shortcuts[xtype] : null; + + if (xtype && manifestEntry.ui && widgetShortcuts) { + for (var i = 0; i < widgetShortcuts.length; i++) { + Ext.theme.renderWidget(manifestEntry, widgetShortcuts[i]); + } + } else { + Ext.theme.renderWidget(manifestEntry); + } + } + }, + + /** + * Renders all components (see `render`) and notifies the Slicer that things are ready. + * @private + */ + run: function () { + var extjsVer = Ext.versions.extjs; + var globalData = {}; + + if (Ext.layout.Context) { + Ext.override(Ext.layout.Context, { + run: function () { + var ok = this.callParent(), + widget = Ext.theme.currentWidget; + if (!ok && widget) { + Ext.Error.raise("Layout run failed: " + widget.id); + } + return ok; + } + }); + } + + // Previous to Ext JS 4.2, themes and their manifests where defined differently. + // So pass this along if we are hosting a pre-4.2 theme. + // + if (extjsVer && extjsVer.isLessThan(new Ext.Version("4.2"))) { + globalData.format = "1.0"; // tell the Slicer tool + Ext.theme.legacy = true; // not for our own data collection + + // Check for the Cmd3.0/ExtJS4.1 variables: + // + if (Ext.manifest && Ext.manifest.widgets) { + Ext.theme.addManifest(Ext.manifest.widgets); + } + if (Ext.shortcuts) { + Ext.theme.addShortcuts(Ext.shortcuts); + } + if (Ext.userManifest && Ext.userManifest.widgets) { + Ext.theme.addManifest(Ext.userManifest.widgets); + } + } + + Ext.theme.setData(document.body, globalData); + + Ext.theme.render(); + + // This function is defined by slicer.js (the framework-independent piece) + Ext.defer(generateSlicerManifest, 1); + }, + + /** + * Sets the `data-slicer` attribute to the JSON-encoded value of the provided data. + * @private + */ + setData: function (el, data) { + if (data) { + var json = Ext.encode(data); + if (json !== '{}') { + el.setAttribute('data-slicer', json); + } + } + }, + + /** + * This used to be `loadExtStylesheet`. + * @private + */ + loadCss: function (src, callback) { + var xhr = new XMLHttpRequest(); + + xhr.open('GET', src); + + xhr.onload = function() { + var css = xhr.responseText, + head = document.getElementsByTagName('head')[0], + style = document.createElement('style'); + + // There's bugginess in the next gradient syntax in WebKit r84622 + // This might be fixed in a later WebKit, but for now we're going to + // strip it out here since compass generates it. + // + // TODO: Upgrade to later WebKit revision + css = css.replace(/background(-image)?: ?-webkit-linear-gradient(?:.*?);/g, ''); + + style.type = 'text/css'; + style.innerText = css; + + head.appendChild(style); + callback(); + }; + + xhr.send(null); + } +}; + +Ext.onReady(Ext.theme.run, Ext.theme); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/example/theme.html b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/example/theme.html new file mode 100644 index 0000000000000000000000000000000000000000..b01ee9d3b42199f7c54969f92fcfc6a33ce94a36 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/example/theme.html @@ -0,0 +1,44 @@ + + + + + Ext JS Theme Harness + + + + + + + + + + + + + + + + + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/Application.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/Application.scss new file mode 100644 index 0000000000000000000000000000000000000000..fd8ae5aca6f7868fac62d2161dcc8dc89d78fd80 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/Application.scss @@ -0,0 +1,39 @@ +// +// Ordered application SCSS files to include. +// + +@import 'NX/ext/form/OptionalFieldSet'; +@import 'NX/ext/Mask'; + +@import 'NX/view/AboutWindow'; +@import 'NX/view/Authenticate'; +@import 'NX/view/Unlicensed'; +@import 'NX/view/ExpireSession'; +@import 'NX/view/UnsupportedBrowser'; +@import 'NX/view/header/Mode'; +@import 'NX/view/header/QuickSearch'; +@import 'NX/view/header/Panel'; +@import 'NX/view/footer/Panel'; +@import 'NX/view/feature/Menu'; +@import 'NX/view/feature/Content'; +@import 'NX/view/feature/Group'; +@import 'NX/view/feature/NotFound'; +@import 'NX/view/feature/NotVisible'; +@import 'NX/view/dev/Panel'; +@import 'NX/view/info/Entry'; +@import 'NX/view/drilldown/Actions'; +@import 'NX/view/drilldown/Details'; +@import 'NX/view/drilldown/Drilldown'; +@import 'NX/wizard/Panel'; + +@import 'NX/coreui/view/capability/CapabilityList'; +@import 'NX/coreui/view/component/ComponentDetails'; +@import 'NX/coreui/view/component/ComponentAssetInfo'; +@import 'NX/coreui/view/component/ComponentInfo'; +@import 'NX/coreui/view/component/AssetAttributes'; +@import 'NX/coreui/view/logging/LogViewer'; +@import 'NX/coreui/view/search/SearchFeature'; +@import 'NX/coreui/view/support/Metrics'; + +@import 'NX/proui/view/usertoken/UserTokenDetails'; + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/capability/CapabilityList.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/capability/CapabilityList.scss new file mode 100644 index 0000000000000000000000000000000000000000..f3b62457ce4cd5092382e93dac0be50dc240338f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/capability/CapabilityList.scss @@ -0,0 +1,7 @@ +/** + * @class NX.coreui.view.capability.CapabilityList + */ + +.nx-red-marker td { + color: $color-cerise; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/component/AssetAttributes.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/component/AssetAttributes.scss new file mode 100644 index 0000000000000000000000000000000000000000..4ac5dd6ccb92c7adf44d6d438c80be5280d6c383 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/component/AssetAttributes.scss @@ -0,0 +1,10 @@ +/** + * @class NX.coreui.view.component.AssetAttributes + */ + +.nx-coreui-component-assetattributes { + .attribute-value { + word-wrap: break-word; + white-space:normal !important; + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/component/ComponentAssetInfo.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/component/ComponentAssetInfo.scss new file mode 100644 index 0000000000000000000000000000000000000000..1fffb984de9189d9533940903904aed59ba486f0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/component/ComponentAssetInfo.scss @@ -0,0 +1,17 @@ +/** + * @class NX.coreui.view.component.ComponentAssetInfo + */ + +.nx-coreui-component-componentassetinfo { + border-top: 1px solid silver; + > .x-panel-header { + background-color: white; + * { + font-weight: normal; + font-family: $font-regular; + font-size: 18px; + line-height: 20px; + color: black; + } + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/component/ComponentDetails.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/component/ComponentDetails.scss new file mode 100644 index 0000000000000000000000000000000000000000..ffb90901eb1c7d30d52f53a8922962ec90401598 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/component/ComponentDetails.scss @@ -0,0 +1,8 @@ +/** + * @class NX.coreui.view.component.ComponentDetails + */ + +.nx-coreui-component-details { + @extend .nx-inset; + background-color: $color-white; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/component/ComponentInfo.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/component/ComponentInfo.scss new file mode 100644 index 0000000000000000000000000000000000000000..2c7ae7f6fa0ef2b536a2b320431229579fea6137 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/component/ComponentInfo.scss @@ -0,0 +1,17 @@ +/** + * @class NX.coreui.view.component.ComponentInfo + */ + +.nx-coreui-component-componentinfo { + border-top: 1px solid silver; + > .x-panel-header { + background-color: white; + * { + font-weight: normal; + font-family: $font-regular; + font-size: 18px; + line-height: 20px; + color: black; + } + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/logging/LogViewer.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/logging/LogViewer.scss new file mode 100644 index 0000000000000000000000000000000000000000..09cfa29a1b9d17753e6a6635dd61f1a5de9f0a8d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/logging/LogViewer.scss @@ -0,0 +1,8 @@ +/** + * @class NX.coreui.view.logging.LogViewer + */ + +.nx-log-viewer-field, +.nx-log-viewer-field textarea { + background-color: $color-white !important; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/search/SearchFeature.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/search/SearchFeature.scss new file mode 100644 index 0000000000000000000000000000000000000000..b618e258c799cf4ba265d8647580676ff0876d18 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/search/SearchFeature.scss @@ -0,0 +1,16 @@ +/** + * @class NX.coreui.view.search.SearchFeature + */ + +.nx-coreui-searchfeature { + .criteria { + background-color: $color-white; + + padding: 0 0 10px 10px; + + // FIXME: find a saner way to get 'more criteria' button to line up with criteria input fields + .more-criteria { + margin: 26px 0 0 0; + } + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/support/Metrics.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/support/Metrics.scss new file mode 100644 index 0000000000000000000000000000000000000000..d343da8f74fbc23c55e5e42eb9401dd559b5d640 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/coreui/view/support/Metrics.scss @@ -0,0 +1,9 @@ +/** + * @class NX.coreui.view.support.Metrics + */ + +.nx-coreui-support-metrics { + .metricwidget { + margin: 0 20px 20px 0; + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/ext/Mask.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/ext/Mask.scss new file mode 100644 index 0000000000000000000000000000000000000000..046dc568f184f10e121e042dbde1b5c64159bddf --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/ext/Mask.scss @@ -0,0 +1,4 @@ +.nx-mask-without-spinner .x-mask-msg-text { + background: transparent !important; + padding: 5px !important; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/ext/form/OptionalFieldSet.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/ext/form/OptionalFieldSet.scss new file mode 100644 index 0000000000000000000000000000000000000000..bcc3a2cba24450099a1f49ad5fe316d9d0240f74 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/ext/form/OptionalFieldSet.scss @@ -0,0 +1,21 @@ +/** + * @class NX.ext.form.OptionalFieldSet + */ + +.nx-optionalfieldset { + margin: 10px 25px 0 0; + + .x-fieldset-body { + background-color: $color-light-smoke; + border: 1px solid $color-light-gray; + padding: 5px 20px 10px 20px; + position: relative; + margin-left: 25px; + width: auto !important; + } + + // Give child elements a background opacity instead of a color + .nx-optionalfieldset .x-fieldset-body { + background-color: rgba(0, 0, 0, 0.05); + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/proui/view/usertoken/UserTokenDetails.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/proui/view/usertoken/UserTokenDetails.scss new file mode 100644 index 0000000000000000000000000000000000000000..652446192c6075b461cd7e2220121e2488e6a474 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/proui/view/usertoken/UserTokenDetails.scss @@ -0,0 +1,9 @@ +/** + * @class NX.proui.view.usertoken.UserTokenDetails + */ + +.nx-user-token-field { + padding: 2px; + font-family: $font-monospace; + font-size: $font-size-code; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/AboutWindow.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/AboutWindow.scss new file mode 100644 index 0000000000000000000000000000000000000000..753401ae127e971bf5661830105c4ecbf802e361 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/AboutWindow.scss @@ -0,0 +1,17 @@ +/** + * @class NX.view.AboutWindow + */ + +.nx-aboutwindow { + .x-window { + padding: 0; + } + + .summary { + background-color: $color-light-smoke; + } + + .logo { + margin: 10px; + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/Authenticate.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/Authenticate.scss new file mode 100644 index 0000000000000000000000000000000000000000..783de2873cb22c1cb4e75a8ff72c2d7eb2f639b1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/Authenticate.scss @@ -0,0 +1,9 @@ +/** + * @class NX.view.Authenticate + */ + +.nx-authenticate { + .message { + margin-bottom: 10px; + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/ExpireSession.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/ExpireSession.scss new file mode 100644 index 0000000000000000000000000000000000000000..8275972bd700b1e9cd641c8c50b312f9c5e31a38 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/ExpireSession.scss @@ -0,0 +1,11 @@ +/** + * @class NX.view.ExpireSession + */ + +.nx-expire-session { + #expire { + color: $color-cerise; + font-size: 20px; + margin: 12px; + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/Unlicensed.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/Unlicensed.scss new file mode 100644 index 0000000000000000000000000000000000000000..36b17482d7ba2e3516f972cb4b26a9906a28a365 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/Unlicensed.scss @@ -0,0 +1,19 @@ +/** + * @class NX.view.Unlicensed + */ + +.nx-unlicensed { + background-color: $color-white; + + .title { + color: $color-night-rider; + font-size: 20px; + font-weight: bold; + text-align: center; + padding: 20px; + } + + .description { + font-size: 16px; + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/UnsupportedBrowser.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/UnsupportedBrowser.scss new file mode 100644 index 0000000000000000000000000000000000000000..5af3e482328219048c59093724cc5bf9c0066ed1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/UnsupportedBrowser.scss @@ -0,0 +1,23 @@ +/** + * @class NX.view.UnsupportedBrowser + */ + +.nx-unsupported-browser { + background-color: $color-white; + + .title { + color: $color-night-rider; + font-size: 20px; + font-weight: bold; + text-align: center; + padding: 20px; + } + + .description { + font-size: 16px; + } + + .icons { + padding: 20px 0 50px 0; + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/dev/Panel.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/dev/Panel.scss new file mode 100644 index 0000000000000000000000000000000000000000..1206b65d9c0f4f8072cae3f01e4eecd0aeea8919 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/dev/Panel.scss @@ -0,0 +1,363 @@ +/** + * @class NX.view.dev.Panel + */ + +/** + * ui: 'nx-developer' + */ +@include extjs-panel-ui( + $ui: 'nx-developer', + $ui-header-padding: 4px, + $ui-header-color: $color-white, + $ui-header-background-color: $color-smalt, + $ui-body-background-color: $color-white +); + +.x-panel-nx-developer { + // Let the developer panes scroll as needed + .x-panel-body .x-tabpanel-child .x-box-inner { + overflow-y: auto; + } + + /* + * Meta styles for the visual style sheet + */ + + .nx-hbox { + float: left; + margin-right: 20px; + } + + .nx-vbox { + margin-bottom: 20px; + } + + .nx-section-header { + font-size: $font-size-h5; + padding-bottom: 5px; + text-transform: uppercase; + } + + table thead th { + font-size: $font-size-h5; + text-transform: uppercase; + text-align: left; + } + + table tbody tr .nx-color { + height: 10px; + width: 10px; + } + + /* + * Visual style sheet fonts + */ + + .nx-proxima-nova-regular { + font-family: $font-regular; + } + + .nx-proxima-nova-bold { + font-family: $font-regular; + font-weight: bold; + } + + .nx-courier-new-regular { + font-family: $font-monospace; + } + + /* + * Visual style sheet type styles + */ + + .nx-sample-h1 { + font-size: $font-size-h1; + font-weight: $font-weight-h1; + } + + .nx-sample-h2 { + font-size: $font-size-h2; + font-weight: $font-weight-h2; + } + + .nx-sample-h3 { + font-size: $font-size-h3; + font-weight: $font-weight-h3; + } + + .nx-sample-h4 { + font-size: $font-size-h4; + font-weight: $font-weight-h4; + } + + .nx-sample-h5 { + font-size: $font-size-h5; + font-weight: $font-weight-h5; + } + + .nx-sample-body { + font-size: $font-size-body; + font-weight: $font-weight-body; + } + + .nx-sample-code { + font-size: $font-size-code; + font-weight: $font-weight-code; + font-family: $font-monospace; + } + + .nx-sample-utility { + font-size: $font-size-utility; + font-weight: $font-weight-utility; + } + + /* + * Visual style sheet colors + */ + + .nx-color { + border: 1px $color-gainsboro solid; + margin: 0 3px 3px 0 !important; + height: 40px; + width: 80px; + + // Shell colors + &.black { + background-color: $color-black; + } + + &.night-rider { + background-color: $color-night-rider; + } + + &.charcoal { + background-color: $color-charcoal; + } + + &.dark-gray { + background-color: $color-dark-gray; + } + + &.gray { + background-color: $color-gray; + } + + &.light-gray { + background-color: $color-light-gray; + } + + &.gainsboro { + background-color: $color-gainsboro; + } + + &.smoke { + background-color: $color-smoke; + } + + &.light-smoke { + background-color: $color-light-smoke; + } + + // Severity + + &.cerise { + background-color: $color-cerise; + } + + &.sun { + background-color: $color-sun; + } + + &.energy-yellow { + background-color: $color-energy-yellow; + } + + &.cobalt { + background-color: $color-cobalt; + } + + &.cerulean-blue { + background-color: $color-cerulean-blue; + } + + // Forms + + &.citrus { + background-color: $color-citrus; + } + + &.free-speech-red { + background-color: $color-free-speech-red; + } + + // Tooltips + + &.energy-yellow { + background-color: $color-energy-yellow; + } + + &.floral-white { + background-color: $color-floral-white; + } + + // Dashboard + + &.pigment-green { + background-color: $color-pigment-green; + } + + &.madang { + background-color: $color-madang; + } + + &.venetian-red { + background-color: $color-venetian-red; + } + + &.beauty-bush { + background-color: $color-beauty-bush; + } + + &.navy-blue { + background-color: $color-navy-blue; + } + + &.cornflower { + background-color: $color-cornflower; + } + + &.affair { + background-color: $color-affair; + } + + &.east-side { + background-color: $color-east-side; + } + + &.blue-chalk { + background-color: $color-blue-chalk; + } + + // Buttons + + &.white { + background-color: $color-white; + } + + &.light-gainsboro { + background-color: $color-light-gainsboro; + } + + &.light-gray { + background-color: $color-light-gray; + } + + &.silver { + background-color: $color-silver; + } + + &.suva-gray { + background-color: $color-suva-gray; + } + + &.gray { + background-color: $color-gray; + } + + &.denim { + background-color: $color-denim; + } + + &.light-cobalt { + background-color: $color-light-cobalt; + } + + &.dark-denim { + background-color: $color-smalt; + } + + &.smalt { + background-color: $color-dark-cerulean; + } + + &.dark-cerulean { + background-color: $color-dark-cerulean; + } + + &.prussian-blue { + background-color: $color-prussian-blue; + } + + &.light-cerise { + background-color: $color-light-cerise; + } + + &.brick-red { + background-color: $color-brick-red; + } + + &.old-rose { + background-color: $color-old-rose; + } + + &.fire-brick { + background-color: $color-fire-brick; + } + + &.shiraz { + background-color: $color-shiraz; + } + + &.falu-red { + background-color: $color-falu-red; + } + + &.sea-buckthorn { + background-color: $color-sea-buckthorn; + } + + &.tahiti-gold { + background-color: $color-tahiti-gold; + } + + &.zest { + background-color: $color-zest; + } + + &.rich-gold { + background-color: $color-rich-gold; + } + + &.afghan-tan { + background-color: $color-afghan-tan; + } + + &.russet { + background-color: $color-russet; + } + + &.elf-green { + background-color: $color-elf-green; + } + + &.dark-pigment-green { + background-color: $color-dark-pigment-green; + } + + &.salem { + background-color: $color-salem; + } + + &.jewel { + background-color: $color-jewel; + } + + &.fun-green { + background-color: $color-fun-green; + } + + &.dark-jewel { + background-color: $color-dark-jewel; + } + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/drilldown/Actions.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/drilldown/Actions.scss new file mode 100644 index 0000000000000000000000000000000000000000..ceb45fc08399b6701aba1d597dc69de431474d1d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/drilldown/Actions.scss @@ -0,0 +1,8 @@ +/** + * @class NX.view.drilldown.Actions + */ + +.nx-actions { + background-color: $color-white; + border-style: none; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/drilldown/Details.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/drilldown/Details.scss new file mode 100644 index 0000000000000000000000000000000000000000..f1cc39b4f087032419e7f2915af0b4013f28ccae --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/drilldown/Details.scss @@ -0,0 +1,43 @@ +/** + * @class NX.view.drilldown.Details + */ + +@include extjs-panel-ui( + $ui: 'nx-drilldown-message', + $ui-header-color: $color-night-rider +); + +.x-panel-nx-drilldown-message { + padding-left: 8px !important; + padding-right: 8px !important; + background-color: $color-white; + + .x-panel-header { + padding: 8px; + border: 1px solid !important; + height: 32px; + border-radius: 8px; + + img { + margin-right: 5px !important; + } + } +} + +.x-panel-nx-drilldown-message.nx-drilldown-warning { + .x-panel-header { + border-color: $color-sun !important; + background-color: $color-light-smoke; + } +} + +.x-panel-nx-drilldown-message.nx-drilldown-info { + .x-panel-header { + border-color: $color-cerulean-blue !important; + background-color: $color-light-smoke; + } +} + +.x-docked-top.nx-drilldown-info { + padding: 10px 0 0 0; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/drilldown/Drilldown.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/drilldown/Drilldown.scss new file mode 100644 index 0000000000000000000000000000000000000000..b3ba4e228518193d3fd93c907b3c7e7e4d538fc4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/drilldown/Drilldown.scss @@ -0,0 +1,122 @@ +/** + * @class NX.view.drilldown.Drilldown + */ + +// FIXME: Sort out where these should go, referenced by NX.controller.Drilldown + +@include extjs-button-large-ui( + $ui: 'nx-drilldown', + $text-padding: 0, + $color: $color-navy-blue, + $color-over: $color-dark-cerulean, + $color-focus: $color-dark-cerulean, + $color-pressed: $color-cornflower, + $color-disabled: $color-night-rider, + $font-size: $font-size-h2, + $background-gradient: transparent, + $background-gradient-over: transparent, + $background-gradient-focus: transparent, + $background-gradient-pressed: transparent, + $background-gradient-disabled: transparent, + $background-color: transparent, + $background-color-over: transparent, + $background-color-focus: transparent, + $background-color-pressed: transparent, + $background-color-disabled: transparent, + $border-width: 0, + $border-color: transparent, + $border-color-over: transparent, + $border-color-focus: transparent, + $border-color-pressed: transparent, + $border-color-disabled: transparent, + $opacity-disabled: 1, + $inner-opacity-disabled: 1 +); + +.x-btn-nx-drilldown-large { + padding: 0 !important; +} + +@include extjs-button-medium-ui( + $ui: 'nx-drilldown', + $text-padding: 0, + $color: $color-navy-blue, + $color-over: $color-dark-cerulean, + $color-focus: $color-dark-cerulean, + $color-pressed: $color-cornflower, + $color-disabled: $color-night-rider, + $font-size: $font-size-h4, + $background-gradient: transparent, + $background-gradient-over: transparent, + $background-gradient-focus: transparent, + $background-gradient-pressed: transparent, + $background-gradient-disabled: transparent, + $background-color: transparent, + $background-color-over: transparent, + $background-color-focus: transparent, + $background-color-pressed: transparent, + $background-color-disabled: transparent, + $border-width: 0, + $border-color: transparent, + $border-color-over: transparent, + $border-color-focus: transparent, + $border-color-pressed: transparent, + $border-color-disabled: transparent, + $opacity-disabled: 1, + $inner-opacity-disabled: 1 +); + +.x-btn-nx-drilldown-medium { + padding: 0 2px 0 6px; + + .x-btn-inner { + text-overflow: ellipsis; + } +} + +.nx-feature-content .x-panel-header .x-btn-nx-drilldown-medium { + top: 7px !important; +} + +@include extjs-button-toolbar-small-ui( + $ui: 'nx-drilldown', + $text-padding: 0, + $color: $color-navy-blue, + $color-over: $color-dark-cerulean, + $color-focus: $color-dark-cerulean, + $color-pressed: $color-cornflower, + $color-disabled: $color-night-rider, + $font-size: $font-size-body, + $background-gradient: transparent, + $background-gradient-over: transparent, + $background-gradient-focus: transparent, + $background-gradient-pressed: transparent, + $background-gradient-disabled: transparent, + $background-color: transparent, + $background-color-over: transparent, + $background-color-focus: transparent, + $background-color-pressed: transparent, + $background-color-disabled: transparent, + $border-color: transparent, + $border-color-over: transparent, + $border-color-focus: transparent, + $border-color-pressed: transparent, + $border-color-disabled: transparent, + $glyph-color: $color-navy-blue, + $opacity-disabled: 1, + $inner-opacity-disabled: 1 +); + +// FIXME: Sort out where these should go, referenced by NX.controller.Drilldown + +.nx-breadcrumb-separator { + font-size: $font-size-h4; + top: 7px !important; + color: $color-gray; + padding-left: 12px; +} + +.nx-breadcrumb-icon { + top: 9px !important; + margin-left: 13px; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/Content.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/Content.scss new file mode 100644 index 0000000000000000000000000000000000000000..c2c20be4e1576ca8b36eb489b84d1df89a2936d5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/Content.scss @@ -0,0 +1,53 @@ +/** + * @class NX.view.feature.Content + */ + +@include extjs-panel-ui( + $ui: 'nx-feature-content', + $ui-header-padding: 10px 8px 10px 10px, + $ui-header-background-color: $color-white, + $ui-body-background-color: $color-light-smoke, + $ui-header-icon-height: 32px, + $ui-header-icon-width: 32px, + $ui-wrap-border-width: 0, + $ui-header-text-transform: $panel-light-header-text-transform, + $ui-tool-background-image: $panel-light-tool-background-image +); + +.nx-feature-content { + .nx-feature-group { + border-top: 1px $color-gainsboro solid !important; + margin: 0 !important; + } +} + +.nx-feature-content .x-panel-body .x-toolbar { + padding: 12px 8px 0 0; + /*.x-toolbar-item { + margin: 100px 8px 0 0; + }*/ + &.x-toolbar-footer { + background-color: $color-white; + } +} + +.nx-feature-content .x-panel-body .x-panel-nx-subsection-framed > div + .x-toolbar, +.nx-feature-content .x-panel-body .nx-actions.x-toolbar { + padding: 0 8px 0 0; + .x-toolbar-item { + margin: 6px 0 6px 8px; + } +} + +.nx-feature-name { + font-size: $font-size-h2; + font-weight: $font-weight-h2; +} + +.nx-feature-description { + font-size: $font-size-body; + font-weight: $font-weight-body; + top: 12px !important; + padding-left: 7px; +} + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/Group.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/Group.scss new file mode 100644 index 0000000000000000000000000000000000000000..c98b01c70766ca5cddb696c60310c36ca7aaba8a --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/Group.scss @@ -0,0 +1,38 @@ +/** + * @class NX.view.feature.Group + */ + +.nx-feature-group { + margin-top: 16px; + margin-left: 16px; + + .item-wrap { + border: 1px solid $color-none; + float: left; + width: 200px; + height: 42px; + margin: 5px; + padding: 5px; + cursor: pointer; + } + + .x-item-over { + border: 1px solid $color-light-gray; + background: $color-smoke; + border-radius: 5px; + -moz-border-radius: 5px; + } + + .x-item-selected { + border: 1px solid $color-gray; + background: $color-light-gray; + border-radius: 5px; + -moz-border-radius: 5px; + } + + img { + height: 32px; + width: 32px; + margin-right: 5px; + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/Menu.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/Menu.scss new file mode 100644 index 0000000000000000000000000000000000000000..c56343bb2ec166c8c2fc3e9a26d5ddf607953225 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/Menu.scss @@ -0,0 +1,32 @@ +/** + * @class NX.view.feature.Menu + */ + +@include extjs-panel-ui( + $ui: 'nx-feature-menu', + $ui-header-padding: 4px, + $ui-body-background-color: $color-gainsboro +); + +.x-panel-nx-feature-menu { + border-width: 0 2px 0 0; + border-color: $color-light-gray; + border-style: solid; + + .x-grid-header-ct { + border: none; + } + + .x-panel-body-nx-feature-menu { + .x-grid-cell { + background-color: $color-gainsboro; + } + .x-grid-row-over .x-grid-td { + background-color: $color-smoke; + } + .x-grid-row-selected .x-grid-td { + color: $color-white; + background-color: $color-cerulean-blue; + } + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/NotFound.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/NotFound.scss new file mode 100644 index 0000000000000000000000000000000000000000..a506aa2ca5eea78924f43b777c2b4e21446e7807 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/NotFound.scss @@ -0,0 +1,19 @@ +/** + * @class NX.view.feature.NotFound + */ + +.nx-feature-notfound { + background-color: $color-white; + + .title { + color: $color-night-rider; + font-size: 20px; + font-weight: bold; + text-align: center; + padding: 20px; + } + + .description { + font-size: 16px; + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/NotVisible.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/NotVisible.scss new file mode 100644 index 0000000000000000000000000000000000000000..e0186a4857f6ca65d47eef6536420fdcfbb45780 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/feature/NotVisible.scss @@ -0,0 +1,19 @@ +/** + * @class NX.view.feature.NotVisible + */ + +.nx-feature-notvisible { + background-color: $color-light-smoke; + + .title { + color: $color-night-rider; + font-size: 20px; + font-weight: bold; + text-align: center; + padding: 20px; + } + + .description { + font-size: 16px; + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/footer/Panel.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/footer/Panel.scss new file mode 100644 index 0000000000000000000000000000000000000000..cb01be5668b7094318880aeb9815551b54723372 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/footer/Panel.scss @@ -0,0 +1,14 @@ +/** + * @class NX.view.footer.Panel + */ + +.nx-footer { + background-color: $color-charcoal; + + .copyright { + color: $color-light-gray; + font-size: 8px; + text-align: right; + padding: 1px 2px 0 0; + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/header/Mode.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/header/Mode.scss new file mode 100644 index 0000000000000000000000000000000000000000..797fb109279021fe3773a5205f5ff96ed47d45e3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/header/Mode.scss @@ -0,0 +1,60 @@ +/** + * @class NX.view.header.Mode + */ + +.nx-modebutton { + padding: 6px 0 0 0; + color: $color-white; + + .x-btn-glyph { + font-size: 24px; + } + + &.x-icon-text-left { + .x-btn-inner { + line-height: 25px !important; + font-size: 15px; + padding-left: 36px; + padding-right: 10px; + } + + .x-btn-glyph { + text-align: left; + padding-left: 10px; + } + } +} + +.nx-modebutton + .nx-caret { + border-left: 5px solid transparent; + border-right: 5px solid transparent; +} + +.nx-modebutton:hover, +.nx-modebutton:focus { + background-color: $color-gainsboro; + + .x-btn-inner { + color: $color-navy-blue; + } + + .x-btn-glyph { + color: $color-navy-blue; + } +} + +.nx-modebutton.x-pressed { + background-color: $color-navy-blue; + + .x-btn-inner { + color: $color-white; + } + + .x-btn-glyph { + color: $color-white; + } +} + +.nx-modebutton.x-pressed + .nx-caret { + border-bottom: 5px solid $color-white; +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/header/Panel.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/header/Panel.scss new file mode 100644 index 0000000000000000000000000000000000000000..38f676815ffaf7319b8f6fc36d3103a4c2ba4d27 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/header/Panel.scss @@ -0,0 +1,57 @@ +/** + * @class NX.view.header.Panel + */ + +.nx-header-panel { + .x-toolbar { + background-color: $color-black; + padding: 0 0 0 16px; + } + + .productname { + color: $color-white; + font-family: 'Proxima Nova Thin', 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-size: 20px; + line-height: 1.2em; + } + + .productspec { + color: $color-white; + font-family: 'Proxima Nova Semibold', 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-size: 11px; + line-height: 1.1em; + letter-spacing: 0.05em; + } +} + +@include extjs-button-toolbar-medium-ui( + $ui: 'nx-header', + $background-color: transparent, + $background-color-over: $color-salem, + $background-color-focus: $color-salem, + $background-color-pressed: $color-jewel, + $background-color-disabled: transparent, + $color: $color-white, + $color-over: $color-white, + $color-focus: $color-white, + $color-pressed: $color-white, + $color-disabled: $color-white, + $border-width: 0, + $background-gradient: transparent, + $background-gradient-over: transparent, + $background-gradient-focus: transparent, + $background-gradient-pressed: transparent, + $background-gradient-disabled: transparent, + $glyph-color: $color-white, + $glyph-opacity: 1 +); + +.x-btn-nx-header-toolbar-medium { + height: 39px; + border-radius: 0; + padding: 3px 6px; + + .x-btn-wrap { + margin-top: 4px; + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/header/QuickSearch.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/header/QuickSearch.scss new file mode 100644 index 0000000000000000000000000000000000000000..bec706ed0b324b3aa21d6fc2fab7dc43cb6cfdf4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/header/QuickSearch.scss @@ -0,0 +1,28 @@ +/** + * @class NX.view.header.QuickSearch + */ + +/** + * Style for the main header search field. + */ +.nx-quicksearch .x-form-trigger-wrap { + border-radius: 11px; + padding-left: 3px; + padding-right: 0px; + background-color: $color-charcoal; + border-color: $color-night-rider; + + input.x-form-field, + .x-trigger-cell, + .x-form-trigger, + .x-form-trigger-input-cell { + background-color: $color-none; + } + + input.x-form-field, + .x-trigger-cell, + .x-form-trigger, + .x-form-text { + color: $color-white; + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/info/Entry.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/info/Entry.scss new file mode 100644 index 0000000000000000000000000000000000000000..71e1df47e184dff04e080d7a77798672adb3d633 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/view/info/Entry.scss @@ -0,0 +1,37 @@ +/** + * @class NX.view.info.Entry + */ + +.nx-info { + table { + border-spacing: 5px; + } +} + +// FIXME: refine scss syntax + +.nx-info { + overflow: auto; +} + +.nx-info-entry td { + padding-bottom: 4px; + vertical-align: top; +} + +.nx-info-entry-name { + padding-left: 0; + padding-right: 6px; + font-weight: bold; + white-space: nowrap; +} + +.nx-info-entry-value { + padding-left: 0; + padding-right: 25px; + word-break: break-all; +} + +.nx-info-entry-value pre { + margin: 0; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/wizard/Panel.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/wizard/Panel.scss new file mode 100644 index 0000000000000000000000000000000000000000..eb671fdc3d82c22cabe8158f2d8e109727f3c03d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/src/NX/wizard/Panel.scss @@ -0,0 +1,34 @@ +/** + * @class NX.wizard.Panel + */ + +.nx-wizard-panel { + padding: 12px 12px 12px 12px; + border-top: 1px solid $color-gainsboro !important; + + .screencontainer, .screenheader { + background-color: $color-white; + border: 1px solid $color-gainsboro !important; + } + + .screencontainer { + padding: 12px 12px 12px 12px; + border-top: 0 !important; + } + + .screenheader { + padding: 7px 8px 0 8px !important; + border-bottom: 0 !important; + + .title { + font-size: $font-size-h3; + font-weight: $font-weight-h3; + } + + .progress { + font-size: $font-size-h5; + font-weight: $font-weight-h5; + color: $color-dark-gray; + } + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/var/Application.scss b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/var/Application.scss new file mode 100644 index 0000000000000000000000000000000000000000..0235dc1d688a1092b4ade28054438c1f0c5c31cd --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/baseapp/sass/var/Application.scss @@ -0,0 +1,5 @@ +// +// Ordered application SCSS files to include. These are included _before_ inclusion of files from src/... +// + +// placeholder \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/PasswordPlaceholder.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/PasswordPlaceholder.java new file mode 100644 index 0000000000000000000000000000000000000000..62e12308a084794610a842dc0f01dbffb4d77a8e --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/PasswordPlaceholder.java @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture; + +import javax.annotation.Nullable; + +import com.google.common.annotations.VisibleForTesting; + +/** + * Helper for password-placeholder data. + * + * @since 3.0 + */ +public class PasswordPlaceholder +{ + private PasswordPlaceholder() { + // empty + } + + /** + * Token used for passwords that are defined, but which are not transmitted. + */ + @VisibleForTesting + static final String VALUE = "#~NEXUS~PLACEHOLDER~PASSWORD~#"; + + /** + * Returns password placeholder. + */ + public static String get() { + return VALUE; + } + + /** + * Returns fake password placeholder unless value is {@code null}. + */ + @Nullable + public static String get(@Nullable final String value) { + if (value != null) { + return VALUE; + } + return null; + } + + /** + * Determine if given value is a password placeholder. + */ + public static boolean is(@Nullable final String value) { + return VALUE.equals(value); + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/StateContributor.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/StateContributor.java new file mode 100644 index 0000000000000000000000000000000000000000..070692eeee29e9e470a42523c83d1495a9832582 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/StateContributor.java @@ -0,0 +1,31 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture; + +import java.util.Map; + +import javax.annotation.Nullable; + +/** + * Implemented by components that provide state values/commands. + * + * @since 3.0 + */ +public interface StateContributor +{ + /** + * Returns mapping of state-id to state-value (state-value can be null). + */ + @Nullable + Map getState(); +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/UiPluginDescriptor.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/UiPluginDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..12510d7ae40f57de873a28d8a5d87169a9ee0b0d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/UiPluginDescriptor.java @@ -0,0 +1,58 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture; + +import javax.annotation.Nullable; + +/** + * Rapture UI plugin descriptor. + * + * Using component priority to determine inclusion order. Ordering is important to properly load the UI. + * + * Plugins are required to define these resources for "debug" mode: + *
    + *
  • /static/rapture/resources/plugin-id-debug.css
  • + *
+ * + * ... and for "prod" mode: + *
    + *
  • /static/rapture/plugin-id-prod.js
  • + *
  • /static/rapture/resources/plugin-id-prod.css
  • + *
+ * + * @since 3.0 + */ +public interface UiPluginDescriptor +{ + /** + * The plugin identifier. This is normally the POM artifactId. + * This is used to generate references to Javascript and CSS sources. + */ + String getPluginId(); + + boolean hasStyle(); + + boolean hasScript(); + + /** + * Extjs application plugin namespace. + */ + @Nullable + String getNamespace(); + + /** + * The Extjs class name of the {@code PluginConfig} for the plugin. + */ + @Nullable + String getConfigClassName(); +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/UiPluginDescriptorSupport.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/UiPluginDescriptorSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..d165053c05640747ddab297b3fbf08459a81fd69 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/UiPluginDescriptorSupport.java @@ -0,0 +1,89 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.common.ComponentSupport; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Support for {@link UiPluginDescriptor} implementations. + * + * @since 3.0 + */ +public class UiPluginDescriptorSupport + extends ComponentSupport + implements UiPluginDescriptor +{ + /** + * Artifact ID of UI-contributing plugin. Used to resolve the location of its contributed web-resources. + */ + private final String pluginId; + + private boolean hasStyle = true; + + private boolean hasScript = true; + + private String namespace; + + private String configClassName; + + public UiPluginDescriptorSupport(final String artifactId) { + this.pluginId = checkNotNull(artifactId); + } + + @Override + public String getPluginId() { + return pluginId; + } + + @Override + public boolean hasStyle() { + return hasStyle; + } + + public void setHasStyle(final boolean hasStyle) { + this.hasStyle = hasStyle; + } + + @Override + public boolean hasScript() { + return hasScript; + } + + public void setHasScript(final boolean hasScript) { + this.hasScript = hasScript; + } + + @Override + @Nullable + public String getNamespace() { + return namespace; + } + + public void setNamespace(@Nullable final String namespace) { + this.namespace = namespace; + } + + @Override + @Nullable + public String getConfigClassName() { + return configClassName; + } + + public void setConfigClassName(final String configClassName) { + this.configClassName = configClassName; + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/RaptureCapabilitiesBooter.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/RaptureCapabilitiesBooter.java new file mode 100644 index 0000000000000000000000000000000000000000..76b5df9c5c5895d33324345e81882c116b48985c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/RaptureCapabilitiesBooter.java @@ -0,0 +1,43 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.capability.CapabilityBooterSupport; +import org.sonatype.nexus.capability.CapabilityRegistry; +import org.sonatype.nexus.rapture.internal.settings.SettingsCapabilityConfiguration; +import org.sonatype.nexus.rapture.internal.settings.SettingsCapabilityDescriptor; + +/** + * Automatically create Rapture capabilities. + * + * @since 3.0 + */ +@Named +@Singleton +public class RaptureCapabilitiesBooter + extends CapabilityBooterSupport +{ + @Override + protected void boot(final CapabilityRegistry registry) throws Exception { + maybeAddCapability( + registry, + SettingsCapabilityDescriptor.TYPE, + true, // enabled + null, // no notes + new SettingsCapabilityConfiguration().asMap() + ); + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/RaptureModule.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/RaptureModule.java new file mode 100644 index 0000000000000000000000000000000000000000..74ef379f4557b0287552773f10c1c5167972b783 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/RaptureModule.java @@ -0,0 +1,63 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal; + +import javax.inject.Named; + +import org.sonatype.nexus.rapture.internal.security.SessionAuthenticationFilter; +import org.sonatype.nexus.rapture.internal.security.SessionServlet; +import org.sonatype.nexus.security.CookieFilter; +import org.sonatype.nexus.security.FilterChainModule; +import org.sonatype.nexus.security.SecurityFilter; + +import com.google.inject.AbstractModule; +import com.google.inject.servlet.ServletModule; + +import static org.sonatype.nexus.security.FilterProviderSupport.filterKey; + +/** + * Rapture Guice module. + * + * @since 3.0 + */ +@Named +public class RaptureModule + extends AbstractModule +{ + private static final String MOUNT_POINT = "/service/rapture"; + + private static final String SESSION_MP = MOUNT_POINT + "/session"; + + @Override + protected void configure() { + bind(filterKey(SessionAuthenticationFilter.NAME)).to(SessionAuthenticationFilter.class); + + install(new ServletModule() + { + @Override + protected void configureServlets() { + serve(SESSION_MP).with(SessionServlet.class); + filter(SESSION_MP).through(SecurityFilter.class); + filter(SESSION_MP).through(CookieFilter.class); + } + }); + + install(new FilterChainModule() + { + @Override + protected void configure() { + addFilterChain(SESSION_MP, SessionAuthenticationFilter.NAME); + } + }); + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/RaptureWebResourceBundle.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/RaptureWebResourceBundle.java new file mode 100644 index 0000000000000000000000000000000000000000..adf3d045854b887004018942cf5286623b588d4c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/RaptureWebResourceBundle.java @@ -0,0 +1,350 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; +import javax.servlet.http.HttpServletRequest; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.app.ApplicationVersion; +import org.sonatype.nexus.common.app.BaseUrlHolder; +import org.sonatype.nexus.common.template.TemplateAccessible; +import org.sonatype.nexus.common.template.TemplateHelper; +import org.sonatype.nexus.common.template.TemplateParameters; +import org.sonatype.nexus.rapture.UiPluginDescriptor; +import org.sonatype.nexus.rapture.internal.state.StateComponent; +import org.sonatype.nexus.servlet.ServletHelper; +import org.sonatype.nexus.webresources.GeneratedWebResource; +import org.sonatype.nexus.webresources.WebResource; +import org.sonatype.nexus.webresources.WebResourceBundle; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Rapture {@link WebResourceBundle}. + * + * Provides resources: + *
    + *
  • {@code /index.html}
  • + *
  • {@code /static/rapture/bootstrap.js}
  • + *
  • {@code /static/rapture/app.js}
  • + *
+ * + * @since 3.0 + */ +@Named +@Singleton +public class RaptureWebResourceBundle + extends ComponentSupport + implements WebResourceBundle +{ + private final ApplicationVersion applicationVersion; + + private final Provider servletRequestProvider; + + private final Provider stateComponentProvider; + + private final TemplateHelper templateHelper; + + private final List pluginDescriptors; + + private final Gson gson; + + @Inject + public RaptureWebResourceBundle(final ApplicationVersion applicationVersion, + final Provider servletRequestProvider, + final Provider stateComponentProvider, + final TemplateHelper templateHelper, + final List pluginDescriptors) + { + this.applicationVersion = checkNotNull(applicationVersion); + this.servletRequestProvider = checkNotNull(servletRequestProvider); + this.stateComponentProvider = checkNotNull(stateComponentProvider); + this.templateHelper = checkNotNull(templateHelper); + this.pluginDescriptors = checkNotNull(pluginDescriptors); + + log.info("UI plugin descriptors:"); + for (UiPluginDescriptor descriptor : pluginDescriptors) { + log.info(" {}", descriptor.getPluginId()); + } + + gson = new GsonBuilder().setPrettyPrinting().create(); + } + + // + // FIXME: optimize so that we dont duplicate things like isDebug() over and over each request + // FIXME: for now we simply do a bit more work than is needed :-( + // + + @Override + public List getResources() { + return ImmutableList.of( + index_html(), + bootstrap_js(), + app_js() + ); + } + + private abstract class TemplateWebResource + extends GeneratedWebResource + { + protected byte[] render(final String template, final TemplateParameters parameters) throws IOException { + log.trace("Rendering template: {}, with params: {}", template, parameters); + URL url = getClass().getResource(template); + return templateHelper.render(url, parameters).getBytes(); + } + } + + @TemplateAccessible + public static class TemplateUtil + { + /** + * Helper to return the filename for a URI. + */ + public String fileName(final URI uri) { + String path = uri.getPath(); + int i = path.lastIndexOf('/'); + return path.substring(i + 1, path.length()); + } + } + + /** + * The index.html resource. + */ + private WebResource index_html() { + return new TemplateWebResource() + { + @Override + public String getPath() { + return "/index.html"; + } + + @Override + public String getContentType() { + return HTML; + } + + @Override + protected byte[] generate() throws IOException { + return render("index.vm", new TemplateParameters() + .set("baseUrl", BaseUrlHolder.get()) + .set("debug", isDebug()) + .set("urlSuffix", generateUrlSuffix()) + .set("styles", getStyles()) + .set("scripts", getScripts()) + .set("util", new TemplateUtil()) + ); + } + }; + } + + /** + * The bootstrap.js resource. + */ + private WebResource bootstrap_js() { + return new TemplateWebResource() + { + @Override + public String getPath() { + return "/static/rapture/bootstrap.js"; + } + + @Override + public String getContentType() { + return JAVASCRIPT; + } + + @Override + protected byte[] generate() throws IOException { + return render("bootstrap.vm", new TemplateParameters() + .set("baseUrl", BaseUrlHolder.get()) + .set("debug", isDebug()) + .set("urlSuffix", generateUrlSuffix()) + .set("namespaces", getNamespaces()) + ); + } + }; + } + + /** + * The app.js resource. + */ + private WebResource app_js() { + return new TemplateWebResource() + { + @Override + public String getPath() { + return "/static/rapture/app.js"; + } + + @Override + public String getContentType() { + return JAVASCRIPT; + } + + @Override + protected byte[] generate() throws IOException { + return render("app.vm", new TemplateParameters() + .set("baseUrl", BaseUrlHolder.get()) + .set("debug", isDebug()) + .set("state", gson.toJson(getState())) + .set("pluginConfigs", getPluginConfigs()) + ); + } + }; + } + + /** + * Generate URL suffix to use on all requests when loading the index. + */ + private String generateUrlSuffix() { + StringBuilder buff = new StringBuilder(); + String version = applicationVersion.getVersion(); + buff.append("_v=").append(version); + + // if version is a SNAPSHOT, then append additional timestamp to disable cache + if (version.endsWith("SNAPSHOT")) { + buff.append("&_dc=").append(System.currentTimeMillis()); + } + + // when debug, add parameter + if (isDebug()) { + buff.append("&debug=true"); + } + + return buff.toString(); + } + + /** + * Check if ?debug parameter is given on the request. + */ + private boolean isDebug() { + HttpServletRequest request = servletRequestProvider.get(); + return ServletHelper.isDebug(request); + } + + /** + * Returns the initial state for the application. + */ + private Map getState() { + return stateComponentProvider.get().getState(Maps.newHashMap()); + } + + /** + * Find all plugin configs. + */ + private List getPluginConfigs() { + List classNames = Lists.newArrayList(); + for (UiPluginDescriptor descriptor : pluginDescriptors) { + String className = descriptor.getConfigClassName(); + if (className != null) { + classNames.add(className); + } + } + return classNames; + } + + /** + * Determine all plugin namespaces. + */ + private List getNamespaces() { + List namespaces = Lists.newArrayList(); + for (UiPluginDescriptor descriptor : pluginDescriptors) { + String ns = descriptor.getNamespace(); + if (ns != null) { + namespaces.add(ns); + } + } + return namespaces; + } + + /** + * Replaces "{mode}" in given path with either "prod" or "debug". + */ + private String mode(final String path) { + String mode = isDebug() ? "debug" : "prod"; + return path.replaceAll("\\{mode\\}", mode); + } + + /** + * Generate a URI for the given path. + */ + private URI uri(final String path) { + try { + return new URI(String.format("%s/static/rapture/%s", BaseUrlHolder.get(), path)); + } + catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + /** + * Generate the list of CSS styles to include in the index.html. + */ + private List getStyles() { + List styles = Lists.newArrayList(); + styles.add(uri(mode("resources/loading-{mode}.css"))); + styles.add(uri(mode("resources/baseapp-{mode}.css"))); + + // add all plugin styles + for (UiPluginDescriptor descriptor : pluginDescriptors) { + if (descriptor.hasStyle()) { + String path = String.format("resources/%s-{mode}.css", descriptor.getPluginId()); + styles.add(uri(mode(path))); + } + } + + return styles; + } + + /** + * Generate the list of javascript sources to include in the index.html. + */ + private List getScripts() { + List scripts = Lists.newArrayList(); + + scripts.add(uri(mode("baseapp-{mode}.js"))); + scripts.add(uri(mode("extdirect-{mode}.js"))); + scripts.add(uri("bootstrap.js")); + scripts.add(uri("d3.v4.min.js")); + + // add all "prod" plugin scripts if debug is not enabled + if (!isDebug()) { + for (UiPluginDescriptor descriptor : pluginDescriptors) { + if (descriptor.hasScript()) { + String path = String.format("%s-prod.js", descriptor.getPluginId()); + scripts.add(uri(path)); + } + } + } + + scripts.add(uri("app.js")); + return scripts; + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/UiPluginDescriptorImpl.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/UiPluginDescriptorImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..7be77580dabfa61ee7fe6399fcb5ba5d555d1b30 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/UiPluginDescriptorImpl.java @@ -0,0 +1,40 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.rapture.UiPluginDescriptor; +import org.sonatype.nexus.rapture.UiPluginDescriptorSupport; + +import org.eclipse.sisu.Priority; + +/** + * Rapture {@link UiPluginDescriptor} for {@code nexus-rapture}. + * + * @since 3.0 + */ +@Named +@Singleton +@Priority(Integer.MAX_VALUE) // always load first +public class UiPluginDescriptorImpl + extends UiPluginDescriptorSupport +{ + @Inject + public UiPluginDescriptorImpl() { + super("nexus-rapture"); + setConfigClassName("NX.app.PluginConfig"); + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/Branding.groovy b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/Branding.groovy new file mode 100644 index 0000000000000000000000000000000000000000..5fb1e020b228c6eb3710530a10c76d3671ab144c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/Branding.groovy @@ -0,0 +1,70 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.branding + +import java.util.regex.Matcher + +import javax.annotation.Nullable +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.nexus.common.app.BaseUrlHolder +import org.sonatype.nexus.rapture.StateContributor + +import groovy.transform.PackageScope + +/** + * Branding state contributor. + * + * @since 3.0 + */ +@Named +@Singleton +class Branding + implements StateContributor +{ + private BrandingCapabilityConfiguration config + + // FIXME: This will perform interpolation (when configured) each poll, + // FIXME: ... but is needed due to interpolation of baseUrl which can change + + @Override + Map getState() { + if (config) { + return ['branding': new BrandingXO( + headerEnabled: config.headerEnabled, + headerHtml: interpolate(config.headerHtml), + footerEnabled: config.footerEnabled, + footerHtml: interpolate(config.footerHtml) + )] + } + return null + } + + @PackageScope + @Nullable + String interpolate(@Nullable final String html) { + if (html != null) { + return html.replaceAll(Matcher.quoteReplacement('$baseUrl'), BaseUrlHolder.get()) + } + return null + } + + void set(final BrandingCapabilityConfiguration config) { + this.config = config + } + + void reset() { + this.config = null + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/BrandingCapability.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/BrandingCapability.java new file mode 100644 index 0000000000000000000000000000000000000000..17cb32ea34f8f6116be81303dde4e752b82c55de --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/BrandingCapability.java @@ -0,0 +1,67 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.branding; + +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.nexus.capability.CapabilitySupport; +import org.sonatype.nexus.capability.Condition; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Branding capability. + * + * @since 3.0 + */ +@Named(BrandingCapabilityDescriptor.TYPE_ID) +public class BrandingCapability + extends CapabilitySupport +{ + + private final Branding branding; + + @Inject + public BrandingCapability(final Branding branding) { + this.branding = checkNotNull(branding); + } + + @Override + protected BrandingCapabilityConfiguration createConfig(final Map properties) { + return new BrandingCapabilityConfiguration(properties); + } + + @Override + protected void onActivate(final BrandingCapabilityConfiguration config) throws Exception { + branding.set(config); + } + + @Override + protected void onPassivate(final BrandingCapabilityConfiguration config) throws Exception { + branding.reset(); + } + + @Override + protected void onRemove(final BrandingCapabilityConfiguration config) throws Exception { + branding.reset(); + } + + @Override + public Condition activationCondition() { + return conditions().capabilities().passivateCapabilityDuringUpdate(); + } + +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/BrandingCapabilityConfiguration.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/BrandingCapabilityConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..a8585fbb0879d239f18ad8b351c2c2f07eaf1b4d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/BrandingCapabilityConfiguration.java @@ -0,0 +1,68 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.branding; + +import java.util.Map; + +import org.sonatype.nexus.common.text.Strings2; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Configuration adapter for {@link BrandingCapability}. + * + * @since 3.0 + */ +public class BrandingCapabilityConfiguration + extends BrandingXO +{ + + public static final String HEADER_ENABLED = "headerEnabled"; + + public static final String HEADER_HTML = "headerHtml"; + + public static final String FOOTER_ENABLED = "footerEnabled"; + + public static final String FOOTER_HTML = "footerHtml"; + + public BrandingCapabilityConfiguration(final Map properties) { + checkNotNull(properties); + setHeaderEnabled(parseBoolean(properties.get(HEADER_ENABLED), false)); + setHeaderHtml(parseString(properties.get(HEADER_HTML))); + setFooterEnabled(parseBoolean(properties.get(FOOTER_ENABLED), false)); + setFooterHtml(parseString(properties.get(FOOTER_HTML))); + } + + private boolean parseBoolean(final String value, final boolean defaultValue) { + if (!Strings2.isEmpty(value)) { + return Boolean.parseBoolean(value); + } + return defaultValue; + } + + private String parseString(final String value) { + if (!Strings2.isEmpty(value)) { + return value; + } + return null; + } + + @Override + public String toString() { + return getClass().getSimpleName() + + "{" + + "headerEnabled=" + getHeaderEnabled() + + ", footerEnabled=" + getFooterEnabled() + + "}"; + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/BrandingCapabilityDescriptor.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/BrandingCapabilityDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..936c17b86d860515bcaa51ff48e71cd89b00b340 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/BrandingCapabilityDescriptor.java @@ -0,0 +1,149 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.branding; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.i18n.I18N; +import org.sonatype.goodies.i18n.MessageBundle; +import org.sonatype.nexus.capability.CapabilityDescriptorSupport; +import org.sonatype.nexus.capability.CapabilityType; +import org.sonatype.nexus.capability.Tag; +import org.sonatype.nexus.capability.Taggable; +import org.sonatype.nexus.formfields.CheckboxFormField; +import org.sonatype.nexus.formfields.FormField; +import org.sonatype.nexus.formfields.TextAreaFormField; + +import com.google.common.collect.Lists; + +/** + * {@link BrandingCapability} descriptor. + * + * @since 3.0 + */ +@Named(BrandingCapabilityDescriptor.TYPE_ID) +@Singleton +public class BrandingCapabilityDescriptor + extends CapabilityDescriptorSupport + implements Taggable +{ + public static final String TYPE_ID = "rapture.branding"; + + public static final CapabilityType TYPE = CapabilityType.capabilityType(TYPE_ID); + + private interface Messages + extends MessageBundle + { + @DefaultMessage("UI: Branding") + String name(); + + @DefaultMessage("Enable header") + String headerEnabledLabel(); + + @DefaultMessage("Enable branding header HTML snippet.") + String headerEnabledHelp(); + + @DefaultMessage("Header HTML snippet") + String headerHtmlLabel(); + + @DefaultMessage( + "An HTML snippet to be included in branding header.
" + + "Use '$baseUrl' to insert the base URL of the server (e.g. to reference an image)" + ) + String headerHtmlHelp(); + + @DefaultMessage("Enable footer") + String footerEnabledLabel(); + + @DefaultMessage("Enable branding footer HTML snippet.") + String footerEnabledHelp(); + + @DefaultMessage("Footer HTML snippet") + String footerHtmlLabel(); + + @DefaultMessage( + "An HTML snippet to be included in branding footer.
" + + "Use '$baseUrl' to insert the base URL of the server (e.g. to reference an image)" + ) + String footerHtmlHelp(); + } + + private static final Messages messages = I18N.create(Messages.class); + + private final List formFields; + + public BrandingCapabilityDescriptor() { + formFields = Lists.newArrayList( + new CheckboxFormField( + BrandingCapabilityConfiguration.HEADER_ENABLED, + messages.headerEnabledLabel(), + messages.headerEnabledHelp(), + FormField.OPTIONAL + ).withInitialValue(true), + new TextAreaFormField( + BrandingCapabilityConfiguration.HEADER_HTML, + messages.headerHtmlLabel(), + messages.headerHtmlHelp(), + FormField.OPTIONAL + ), + new CheckboxFormField( + BrandingCapabilityConfiguration.FOOTER_ENABLED, + messages.footerEnabledLabel(), + messages.footerEnabledHelp(), + FormField.OPTIONAL + ).withInitialValue(true), + new TextAreaFormField( + BrandingCapabilityConfiguration.FOOTER_HTML, + messages.footerHtmlLabel(), + messages.footerHtmlHelp(), + FormField.OPTIONAL + ) + ); + } + + @Override + public CapabilityType type() { + return TYPE; + } + + @Override + public String name() { + return messages.name(); + } + + @Override + public List formFields() { + return formFields; + } + + @Override + protected BrandingCapabilityConfiguration createConfig(final Map properties) { + return new BrandingCapabilityConfiguration(properties); + } + + @Override + protected String renderAbout() throws Exception { + return render(TYPE_ID + "-about.vm"); + } + + @Override + public Set getTags() { + return Tag.tags(Tag.categoryTag("UI")); + } + +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/BrandingXO.groovy b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/BrandingXO.groovy new file mode 100644 index 0000000000000000000000000000000000000000..627f2bc1b84b9fb20bc774574136e7c5925c01d5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/branding/BrandingXO.groovy @@ -0,0 +1,32 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.branding + +import groovy.transform.ToString + +/** + * Branding exchange object. + * + * @since 3.0 + */ +@ToString(includePackage = false, includeNames = true) +class BrandingXO +{ + Boolean headerEnabled + + String headerHtml + + Boolean footerEnabled + + String footerHtml +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/logging/LogEventComponent.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/logging/LogEventComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..3c5d371b35cc6eb3c1186599ecd863e96b18a61b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/logging/LogEventComponent.java @@ -0,0 +1,65 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.logging; + +import java.util.Map; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.gossip.Level; +import org.sonatype.nexus.extdirect.DirectComponentSupport; + +import com.codahale.metrics.annotation.ExceptionMetered; +import com.codahale.metrics.annotation.Timed; +import com.google.common.collect.ImmutableMap; +import com.softwarementors.extjs.djn.config.annotations.DirectAction; +import com.softwarementors.extjs.djn.config.annotations.DirectMethod; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * LogEvent component. + * + * @since 3.0 + */ +@Named +@Singleton +@DirectAction(action = "rapture_LogEvent") +public class LogEventComponent + extends DirectComponentSupport +{ + private static final Map levels = ImmutableMap.of( + "trace", Level.TRACE, + "debug", Level.DEBUG, + "info", Level.INFO, + "warn", Level.WARN, + "error", Level.ERROR + ); + + @DirectMethod + @Timed + @ExceptionMetered + public void recordEvent(final LogEventXO event) { + checkNotNull(event); + + Level level = levels.get(event.getLevel()); + checkState(level != null, "Invalid level: %s", event.getLevel()); + + Logger logger = LoggerFactory.getLogger(event.getLogger()); + level.log(logger, event.getMessage()); + } +} diff --git a/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/SimpleApiResponse.groovy b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/logging/LogEventXO.groovy similarity index 56% rename from components/nexus-rest/src/main/java/org/sonatype/nexus/rest/SimpleApiResponse.groovy rename to thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/logging/LogEventXO.groovy index c04ec45605b002040cc579e0223e03c457ebe77b..cdcea2a132f7147cd20e3d08c105add94bb9e30b 100644 --- a/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/SimpleApiResponse.groovy +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/logging/LogEventXO.groovy @@ -10,36 +10,23 @@ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the * Eclipse Foundation. All other trademarks are the property of their respective owners. */ -package org.sonatype.nexus.rest +package org.sonatype.nexus.rapture.internal.logging -import javax.ws.rs.core.Response - -import groovy.transform.CompileStatic import groovy.transform.ToString -import static javax.ws.rs.core.MediaType.APPLICATION_JSON -import static javax.ws.rs.core.Response.Status.OK - /** - * Simple API response object. Contains attributes for HTTP status, message, and a data map + * LogEvent exchange-object. * - * @since 3.next + * @since 3.0 */ -@CompileStatic @ToString(includePackage = false, includeNames = true) -class SimpleApiResponse +class LogEventXO { - int status + Long timestamp - String message + String logger - Map data + String level - static Response ok(final String message, final Map data) { - final SimpleApiResponse ok = new SimpleApiResponse() - ok.setStatus(OK.getStatusCode()) - ok.setMessage(message) - ok.setData(data) - return Response.status(OK).entity(ok).type(APPLICATION_JSON).build() - } + String message } diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/PermissionXO.groovy b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/PermissionXO.groovy new file mode 100644 index 0000000000000000000000000000000000000000..e9686c9b015dce4585ffb379419483de2a798447 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/PermissionXO.groovy @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.security + +import groovy.transform.ToString + +/** + * Permission exchange object. + * + * @since 3.0 + */ +@ToString(includePackage = false, includeNames = true) +class PermissionXO +{ + String id +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/SecurityComponent.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/SecurityComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..79ddde7cc9c5b44b89e6183ad91b3342393c2042 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/SecurityComponent.java @@ -0,0 +1,234 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.security; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.text.Strings2; +import org.sonatype.nexus.common.wonderland.AuthTicketService; +import org.sonatype.nexus.extdirect.DirectComponentSupport; +import org.sonatype.nexus.rapture.StateContributor; +import org.sonatype.nexus.security.SecuritySystem; +import org.sonatype.nexus.security.anonymous.AnonymousConfiguration; +import org.sonatype.nexus.security.anonymous.AnonymousManager; +import org.sonatype.nexus.security.authz.WildcardPermission2; +import org.sonatype.nexus.security.privilege.Privilege; +import org.sonatype.nexus.validation.Validate; + +import com.codahale.metrics.annotation.ExceptionMetered; +import com.codahale.metrics.annotation.Timed; +import com.softwarementors.extjs.djn.config.annotations.DirectAction; +import com.softwarementors.extjs.djn.config.annotations.DirectMethod; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.authz.Permission; +import org.apache.shiro.subject.Subject; +import org.hibernate.validator.constraints.NotEmpty; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * Security Ext.Direct component. + * + * @since 3.0 + */ +@Named +@Singleton +@DirectAction(action = "rapture_Security") +public class SecurityComponent + extends DirectComponentSupport + implements StateContributor +{ + private final SecuritySystem securitySystem; + + private final AnonymousManager anonymousManager; + + private final AuthTicketService authTickets; + + @Inject + public SecurityComponent(final SecuritySystem securitySystem, + final AnonymousManager anonymousManager, + final AuthTicketService authTickets) + { + this.securitySystem = checkNotNull(securitySystem); + this.anonymousManager = checkNotNull(anonymousManager); + this.authTickets = checkNotNull(authTickets); + } + + // FIXME: Move authenticate to session servlet + + @DirectMethod + @Timed + @ExceptionMetered + @Validate + public UserXO authenticate(@NotEmpty final String base64Username, @NotEmpty final String base64Password) + throws Exception + { + Subject subject = securitySystem.getSubject(); + + // FIXME: Subject is not nullable, but we have code that checks for nulls, likely from testing setups, verify and simplify + checkState(subject != null); + + try { + subject.login(new UsernamePasswordToken( + Strings2.decodeBase64(base64Username), + Strings2.decodeBase64(base64Password), + false + )); + } + catch (Exception e) { + throw new Exception("Authentication failed", e); + } + + return getUser(); + } + + @DirectMethod + @Timed + @ExceptionMetered + @Validate + public String authenticationToken(@NotEmpty final String base64Username, @NotEmpty final String base64Password) + throws Exception + { + Subject subject = securitySystem.getSubject(); + if (subject == null || !subject.isAuthenticated()) { + authenticate(base64Username, base64Password); + } + + String username = Strings2.decodeBase64(base64Username); + String password = Strings2.decodeBase64(base64Password); + log.debug("Authenticate w/username: {}, password: {}", username, Strings2.mask(password)); + + // Require current user to be the requested user to authenticate + subject = securitySystem.getSubject(); + if (!subject.getPrincipal().toString().equals(username)) { + throw new Exception("Username mismatch"); + } + + // Ask the sec-manager to authenticate, this won't alter the current subject + try { + SecurityUtils.getSecurityManager().authenticate(new UsernamePasswordToken(username, password)); + } + catch (AuthenticationException e) { + throw new Exception("Authentication failed", e); + } + + // At this point we should be authenticated, return a new ticket + return authTickets.createTicket(); + } + + @DirectMethod + @Timed + @ExceptionMetered + public UserXO getUser() { + UserXO userXO = null; + + Subject subject = securitySystem.getSubject(); + if (isLoggedIn(subject)) { + userXO = new UserXO(); + userXO.setAuthenticated(subject.isAuthenticated()); + + // HACK: roles for the current user are not exposed to the UI. + // HACK: but we need to know if user is admin or not for some things (like outreach) + if (subject.isPermitted(new WildcardPermission2("nexus:*"))) { + userXO.setAdministrator(true); + } + + Object principal = subject.getPrincipal(); + if (principal != null) { + userXO.setId(principal.toString()); + AnonymousConfiguration anonymousConfiguration = anonymousManager.getConfiguration(); + if (anonymousConfiguration.isEnabled() && userXO.getId().equals(anonymousConfiguration.getUserId())) { + userXO = null; + } + } + } + return userXO; + } + + @DirectMethod + @Timed + @ExceptionMetered + public List getPermissions() { + List permissions = null; + Subject subject = securitySystem.getSubject(); + if (isLoggedIn(subject)) { + permissions = calculatePermissions(subject); + } + return permissions; + } + + @Override + public Map getState() { + Map state = new HashMap<>(); + state.put("user", getUser()); + state.put("permissions", getPermissions()); + + AnonymousConfiguration anonymousConfiguration = anonymousManager.getConfiguration(); + state.put("anonymousUsername", anonymousConfiguration.isEnabled() ? anonymousConfiguration.getUserId() : null); + return state; + } + + private boolean isLoggedIn(final Subject subject) { + return subject != null && (subject.isRemembered() || subject.isAuthenticated()); + } + + // FIXME: Avoid calculating permissions for every poll request + + private List calculatePermissions(final Subject subject) { + log.debug("Calculating permissions"); + + List granted = new ArrayList<>(); + List result = new ArrayList<>(); + + // find all privileges which we expose the UI, , which we can deconstruct and evaluate + for (Privilege privilege : securitySystem.listPrivileges()) { + // only WildcardPermission2 presently is supported due to toString() implementation + if (privilege.getPermission() instanceof WildcardPermission2) { + granted.add(privilege.getPermission()); + } + } + + // determine which of the exposed privilege permissions the current subject is granted + boolean[] boolResults = subject.isPermitted(granted); + for (int i = 0; i < granted.size(); i++) { + if (boolResults[i]) { + PermissionXO entry = new PermissionXO(); + entry.setId(granted.get(i).toString()); + result.add(entry); + } + } + + // FIXME: Permissions must be sorted for state-hash calculation :-( + Collections.sort(result, new Comparator() + { + @Override + public int compare(final PermissionXO o1, final PermissionXO o2) { + return o1.getId().compareTo(o2.getId()); + } + }); + + return result; + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/SessionAuthenticationFilter.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/SessionAuthenticationFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..074b71fa4a3236bddba640e8b211e997abc56c59 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/SessionAuthenticationFilter.java @@ -0,0 +1,141 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.security; + +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.sonatype.nexus.common.text.Strings2; + +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.web.filter.authc.AuthenticatingFilter; +import org.apache.shiro.web.util.WebUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Session authentication filter for {@link SessionServlet}. + * + * Provides (very) basic {@code x-www-form-urlencoded} authentication support. + * + * @since 3.0 + */ +@Named +@Singleton +public class SessionAuthenticationFilter + extends AuthenticatingFilter +{ + private static final Logger log = LoggerFactory.getLogger(SessionAuthenticationFilter.class); + + public static final String NAME = "nx-session-authc"; + + public static final String P_USERNAME = "username"; + + public static final String P_PASSWORD = "password"; + + public static final String DELETE_METHOD = "DELETE"; + + /** + * Allow if authenticated or if logout request. + */ + @Override + protected boolean isAccessAllowed(final ServletRequest request, + final ServletResponse response, + final Object mappedValue) + { + Subject subject = getSubject(request, response); + return subject.isAuthenticated() || isLogoutRequest(request); + } + + /** + * Fill in denied response. + */ + private void denied(final ServletResponse response) { + if (response instanceof HttpServletResponse) { + // using 403 here as 401 implies use of "WWW-Authenticate" scheme which is not employed here + WebUtils.toHttp(response).setStatus(HttpServletResponse.SC_FORBIDDEN); + } + } + + @Override + protected boolean onAccessDenied(final ServletRequest request, final ServletResponse response) throws Exception { + boolean authenticated = false; + if (isLoginRequest(request, response)) { + log.trace("Attempting authentication"); + authenticated = executeLogin(request, response); + } + + if (!authenticated) { + log.trace("Access denied"); + denied(response); + } + + return authenticated; + } + + @Override + protected boolean isLoginRequest(final ServletRequest request, final ServletResponse response) { + return (request instanceof HttpServletRequest) && + WebUtils.toHttp(request).getMethod().equalsIgnoreCase(POST_METHOD); + } + + private boolean isLogoutRequest(final ServletRequest request) { + return (request instanceof HttpServletRequest) && + WebUtils.toHttp(request).getMethod().equalsIgnoreCase(DELETE_METHOD); + } + + private String decodeBase64Param(final ServletRequest request, final String name) { + String encoded = WebUtils.getCleanParam(request, name); + if (encoded != null) { + return Strings2.decodeBase64(encoded); + } + return null; + } + + @Override + protected AuthenticationToken createToken(final ServletRequest request, final ServletResponse response) + throws Exception + { + String username = decodeBase64Param(request, P_USERNAME); + String password = decodeBase64Param(request, P_PASSWORD); + return createToken(username, password, request, response); + } + + @Override + protected boolean onLoginSuccess(final AuthenticationToken token, + final Subject subject, + final ServletRequest request, + final ServletResponse response) + throws Exception + { + log.debug("Success: token={}, subject={}", token, subject); + return true; + } + + @Override + protected boolean onLoginFailure(final AuthenticationToken token, + final AuthenticationException e, + final ServletRequest request, + final ServletResponse response) + { + log.debug("Failure: token={}", token, e); + denied(response); + return false; + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/SessionServlet.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/SessionServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..11e9a62082e9ea5211a41677e068d335cd9e77a0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/SessionServlet.java @@ -0,0 +1,82 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.security; + +import java.io.IOException; + +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkState; +import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT; + +/** + * Session servlet, to expose end-point for configuration of Shiro authentication filter to + * establish a user session. + * + * @since 3.0 + * + * @see SessionAuthenticationFilter + */ +@Named +@Singleton +public class SessionServlet + extends HttpServlet +{ + private static final Logger log = LoggerFactory.getLogger(SessionServlet.class); + + /** + * Create session. + */ + @Override + protected void doPost(final HttpServletRequest request, final HttpServletResponse response) + throws ServletException, IOException + { + Subject subject = SecurityUtils.getSubject(); + log.info("Created session for user: {}", subject.getPrincipal()); + + // sanity check + checkState(subject.isAuthenticated()); + checkState(subject.getSession(false) != null); + + response.setStatus(SC_NO_CONTENT); + } + + /** + * Delete session. + */ + @Override + protected void doDelete(final HttpServletRequest request, final HttpServletResponse response) + throws ServletException, IOException + { + Subject subject = SecurityUtils.getSubject(); + log.info("Deleting session for user: {}", subject.getPrincipal()); + subject.logout(); + + // sanity check + checkState(!subject.isAuthenticated()); + checkState(!subject.isRemembered()); + checkState(subject.getSession(false) == null); + + response.setStatus(SC_NO_CONTENT); + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/UserXO.groovy b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/UserXO.groovy new file mode 100644 index 0000000000000000000000000000000000000000..9f8b402987a5c57238a03cd44e662aa32e0ae480 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/security/UserXO.groovy @@ -0,0 +1,35 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.security + +import groovy.transform.ToString + +// TODO: Rename to CurrentUserXO? + +/** + * User exchange object. + * + * @since 3.0 + */ +@ToString(includePackage = false, includeNames = true) +class UserXO +{ + String id + + boolean authenticated + + /** + * True if the current user has general administrator privileges (ie. admin role). + */ + boolean administrator +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/Rapture.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/Rapture.java new file mode 100644 index 0000000000000000000000000000000000000000..3fde300c82662581f644423c56c1c6a19aecb159 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/Rapture.java @@ -0,0 +1,47 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.settings; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; + +import static com.google.common.base.Preconditions.checkNotNull; + +// TODO: rename to SettingsManager? + +/** + * Rapture. + * + * @since 3.0 + */ +@Named +@Singleton +public class Rapture + extends ComponentSupport +{ + private RaptureSettings settings = new RaptureSettings(); + + public RaptureSettings getSettings() { + return settings; + } + + public void setSettings(final RaptureSettings settings) { + this.settings = checkNotNull(settings); + } + + public void resetSettings() { + this.settings = new RaptureSettings(); + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/RaptureSettings.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/RaptureSettings.java new file mode 100644 index 0000000000000000000000000000000000000000..5c4e7823aa8f93bdc7d8d04b7aa056b6ff80810c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/RaptureSettings.java @@ -0,0 +1,95 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.settings; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.constraints.NotBlank; + +/** + * Rapture settings. + * + * @since 3.0 + */ +public class RaptureSettings +{ + public static final String DEFAULT_TITLE = "Nexus Repository Manager"; + + public static final boolean DEFAULT_DEBUG_ALLOWED = true; + + // FIXME: Use Goodies Time + + public static final int DEFAULT_STATUS_INTERVAL_AUTHENTICATED = 5; // seconds + + public static final int DEFAULT_STATUS_INTERVAL_ANONYMOUS = 60; // seconds + + public static final int DEFAULT_SESSION_TIMEOUT = 30; // minutes + + private boolean debugAllowed = DEFAULT_DEBUG_ALLOWED; + + @NotNull + @Min(0) + private Integer statusIntervalAuthenticated = DEFAULT_STATUS_INTERVAL_AUTHENTICATED; + + @NotNull + @Min(0) + private Integer statusIntervalAnonymous = DEFAULT_STATUS_INTERVAL_ANONYMOUS; + + @NotNull + @Min(0) + private Integer sessionTimeout = DEFAULT_SESSION_TIMEOUT; + + @NotBlank + private String title; + + public boolean isDebugAllowed() { + return debugAllowed; + } + + public void setDebugAllowed(final boolean debugAllowed) { + this.debugAllowed = debugAllowed; + } + + public int getStatusIntervalAuthenticated() { + return statusIntervalAuthenticated; + } + + public void setStatusIntervalAuthenticated(final int statusInterval) { + this.statusIntervalAuthenticated = statusInterval; + } + + public int getStatusIntervalAnonymous() { + return statusIntervalAnonymous; + } + + public void setStatusIntervalAnonymous(final int statusInterval) { + this.statusIntervalAnonymous = statusInterval; + } + + public int getSessionTimeout() { + return sessionTimeout; + } + + public void setSessionTimeout(final int sessionTimeout) { + this.sessionTimeout = sessionTimeout; + } + + public String getTitle() { + return title; + } + + public void setTitle(final String title) { + this.title = title; + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/SettingsCapability.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/SettingsCapability.java new file mode 100644 index 0000000000000000000000000000000000000000..eb4069d7758d6e166144fac174538dd3fbde9f09 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/SettingsCapability.java @@ -0,0 +1,67 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.settings; + +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.nexus.capability.CapabilitySupport; +import org.sonatype.nexus.capability.Condition; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Rapture Settings capability. + * + * @since 3.0 + */ +@Named(SettingsCapabilityDescriptor.TYPE_ID) +public class SettingsCapability + extends CapabilitySupport +{ + + private final Rapture rapture; + + @Inject + public SettingsCapability(final Rapture rapture) { + this.rapture = checkNotNull(rapture); + } + + @Override + protected SettingsCapabilityConfiguration createConfig(final Map properties) { + return new SettingsCapabilityConfiguration(properties); + } + + @Override + protected void onActivate(final SettingsCapabilityConfiguration config) throws Exception { + rapture.setSettings(config); + } + + @Override + protected void onPassivate(final SettingsCapabilityConfiguration config) throws Exception { + rapture.resetSettings(); + } + + @Override + protected void onRemove(final SettingsCapabilityConfiguration config) throws Exception { + rapture.resetSettings(); + } + + @Override + public Condition activationCondition() { + return conditions().capabilities().passivateCapabilityDuringUpdate(); + } + +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/SettingsCapabilityConfiguration.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/SettingsCapabilityConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..327907e09a2002e9ff6c5e5f30eec9962f191238 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/SettingsCapabilityConfiguration.java @@ -0,0 +1,105 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.settings; + +import java.util.Map; + +import org.sonatype.nexus.common.text.Strings2; + +import com.google.common.collect.Maps; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Configuration adapter for {@link SettingsCapability}. + * + * @since 3.0 + */ +public class SettingsCapabilityConfiguration + extends RaptureSettings +{ + + public static final String DEBUG_ALLOWED = "debugAllowed"; + + public static final String STATUS_INTERVAL_AUTHENTICATED = "statusIntervalAuthenticated"; + + public static final String STATUS_INTERVAL_ANONYMOUS = "statusIntervalAnonymous"; + + public static final String SESSION_TIMEOUT = "sessionTimeout"; + + public static final String TITLE = "title"; + + public SettingsCapabilityConfiguration() { + this(Maps.newHashMap()); + } + + public SettingsCapabilityConfiguration(final Map properties) { + checkNotNull(properties); + setDebugAllowed(parseBoolean(properties.get(DEBUG_ALLOWED), DEFAULT_DEBUG_ALLOWED)); + setStatusIntervalAuthenticated( + parseInteger(properties.get(STATUS_INTERVAL_AUTHENTICATED), DEFAULT_STATUS_INTERVAL_AUTHENTICATED) + ); + setStatusIntervalAnonymous( + parseInteger(properties.get(STATUS_INTERVAL_ANONYMOUS), DEFAULT_STATUS_INTERVAL_ANONYMOUS) + ); + setSessionTimeout(parseInteger(properties.get(SESSION_TIMEOUT), DEFAULT_SESSION_TIMEOUT)); + setTitle(parseString(properties.get(TITLE), DEFAULT_TITLE)); + } + + public Map asMap() { + final Map props = Maps.newHashMap(); + props.put(DEBUG_ALLOWED, Boolean.toString(isDebugAllowed())); + props.put(STATUS_INTERVAL_AUTHENTICATED, Integer.toString(getStatusIntervalAuthenticated())); + props.put(STATUS_INTERVAL_ANONYMOUS, Integer.toString(getStatusIntervalAnonymous())); + props.put(SESSION_TIMEOUT, Integer.toString(getSessionTimeout())); + props.put(TITLE, getTitle()); + return props; + } + + private boolean parseBoolean(final String value, final boolean defaultValue) { + if (!isEmpty(value)) { + return Boolean.parseBoolean(value); + } + return defaultValue; + } + + private int parseInteger(final String value, final int defaultValue) { + if (!isEmpty(value)) { + return Integer.parseInt(value); + } + return defaultValue; + } + + private String parseString(final String value, final String defaultValue) { + if (!isEmpty(value)) { + return value; + } + return defaultValue; + } + + private boolean isEmpty(final String value) { + return Strings2.isEmpty(value); + } + + @Override + public String toString() { + return getClass().getSimpleName() + + "{" + + "title=" + getTitle() + + ", debugAllowed=" + isDebugAllowed() + + ", statusIntervalAuthenticated=" + getStatusIntervalAuthenticated() + + ", statusIntervalAnonymous=" + getStatusIntervalAnonymous() + + ", sessionTimeout=" + getSessionTimeout() + + "}"; + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/SettingsCapabilityDescriptor.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/SettingsCapabilityDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..df254569dea410e2786c1a61a5a5d49ce7a8ab13 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/SettingsCapabilityDescriptor.java @@ -0,0 +1,158 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.settings; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.i18n.I18N; +import org.sonatype.goodies.i18n.MessageBundle; +import org.sonatype.nexus.capability.CapabilityDescriptorSupport; +import org.sonatype.nexus.capability.CapabilityType; +import org.sonatype.nexus.capability.Tag; +import org.sonatype.nexus.capability.Taggable; +import org.sonatype.nexus.formfields.CheckboxFormField; +import org.sonatype.nexus.formfields.FormField; +import org.sonatype.nexus.formfields.NumberTextFormField; +import org.sonatype.nexus.formfields.StringTextFormField; + +import com.google.common.collect.Lists; + +/** + * {@link SettingsCapability} descriptor. + * + * @since 3.0 + */ +@Named(SettingsCapabilityDescriptor.TYPE_ID) +@Singleton +public class SettingsCapabilityDescriptor + extends CapabilityDescriptorSupport + implements Taggable +{ + public static final String TYPE_ID = "rapture.settings"; + + public static final CapabilityType TYPE = CapabilityType.capabilityType(TYPE_ID); + + private interface Messages + extends MessageBundle + { + @DefaultMessage("UI: Settings") + String name(); + + @DefaultMessage("Debug allowed") + String debugAllowedLabel(); + + @DefaultMessage("Allow developer debugging") + String debugAllowedHelp(); + + @DefaultMessage("Authenticated user status interval") + String statusIntervalAuthenticatedLabel(); + + @DefaultMessage("Interval between status requests for authenticated users (seconds)") + String statusIntervalAuthenticatedHelp(); + + @DefaultMessage("Anonymous user status interval") + String statusIntervalAnonymousLabel(); + + @DefaultMessage("Interval between status requests for anonymous user (seconds)") + String statusIntervalAnonymousHelp(); + + @DefaultMessage("Session timeout") + String sessionTimeoutLabel(); + + @DefaultMessage( + "Period of inactivity before session times out (minutes). A value of 0 will mean that a session never expires." + ) + String sessionTimeoutHelp(); + + @DefaultMessage("Title") + String titleLabel(); + + @DefaultMessage("Browser page title") + String titleHelp(); + } + + private static final Messages messages = I18N.create(Messages.class); + + private final List formFields; + + public SettingsCapabilityDescriptor() { + formFields = Lists.newArrayList( + new StringTextFormField( + SettingsCapabilityConfiguration.TITLE, + messages.titleLabel(), + messages.titleHelp(), + FormField.MANDATORY + ).withInitialValue(RaptureSettings.DEFAULT_TITLE), + new CheckboxFormField( + SettingsCapabilityConfiguration.DEBUG_ALLOWED, + messages.debugAllowedLabel(), + messages.debugAllowedHelp(), + FormField.OPTIONAL + ).withInitialValue(RaptureSettings.DEFAULT_DEBUG_ALLOWED), + new NumberTextFormField( + SettingsCapabilityConfiguration.STATUS_INTERVAL_AUTHENTICATED, + messages.statusIntervalAuthenticatedLabel(), + messages.statusIntervalAuthenticatedHelp(), + FormField.MANDATORY + ).withInitialValue(RaptureSettings.DEFAULT_STATUS_INTERVAL_AUTHENTICATED), + new NumberTextFormField( + SettingsCapabilityConfiguration.STATUS_INTERVAL_ANONYMOUS, + messages.statusIntervalAnonymousLabel(), + messages.statusIntervalAnonymousHelp(), + FormField.MANDATORY + ).withInitialValue(RaptureSettings.DEFAULT_STATUS_INTERVAL_ANONYMOUS), + new NumberTextFormField( + SettingsCapabilityConfiguration.SESSION_TIMEOUT, + messages.sessionTimeoutLabel(), + messages.sessionTimeoutHelp(), + FormField.MANDATORY + ).withInitialValue(RaptureSettings.DEFAULT_SESSION_TIMEOUT) + ); + } + + @Override + public CapabilityType type() { + return TYPE; + } + + @Override + public String name() { + return messages.name(); + } + + @Override + public List formFields() { + return formFields; + } + + @Override + protected SettingsCapabilityConfiguration createConfig(final Map properties) { + return new SettingsCapabilityConfiguration(properties); + } + + @Override + protected String renderAbout() throws Exception { + return render(TYPE_ID + "-about.vm"); + } + + @Override + public Set getTags() { + return Tag.tags(Tag.categoryTag("UI")); + } + +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/UiSettingsStateContributor.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/UiSettingsStateContributor.java new file mode 100644 index 0000000000000000000000000000000000000000..7f40afed4df41acd30314e6a36c1e691dc76cadb --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/settings/UiSettingsStateContributor.java @@ -0,0 +1,58 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.settings; + +import java.util.Map; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.rapture.StateContributor; + +import com.google.common.collect.ImmutableMap; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Contributes {@code uiSettings} state. + * + * @since 3.0 + */ +@Named +@Singleton +public class UiSettingsStateContributor + extends ComponentSupport + implements StateContributor +{ + private static final String STATE_ID = "uiSettings"; + + private final Rapture rapture; + + @Inject + public UiSettingsStateContributor(final Rapture rapture) { + this.rapture = checkNotNull(rapture); + } + + @Nullable + @Override + public Map getState() { + return ImmutableMap.of(STATE_ID, calculateSettings()); + } + + private Object calculateSettings() { + return rapture.getSettings(); + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/ActiveBundlesStateContributor.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/ActiveBundlesStateContributor.java new file mode 100644 index 0000000000000000000000000000000000000000..c04d05985c9fa5a99ccee8a2c7e41b7e84f91fde --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/ActiveBundlesStateContributor.java @@ -0,0 +1,82 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.state; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.rapture.StateContributor; + +import com.google.common.collect.ImmutableMap; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.util.tracker.BundleTracker; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Contributes {@code activeBundles} state. + * + * @since 3.0 + */ +@Named +@Singleton +public class ActiveBundlesStateContributor + extends ComponentSupport + implements StateContributor +{ + public static final String STATE_ID = "activeBundles"; + + private final Set activeBundles = new ConcurrentSkipListSet<>(); + + @Inject + public ActiveBundlesStateContributor(final BundleContext bundleContext) { + checkNotNull(bundleContext); + + // install tracker to invalidate cache + new BundleTracker(bundleContext, Bundle.ACTIVE, null) + { + @Override + public String addingBundle(final Bundle bundle, final BundleEvent event) { + String name = bundle.getSymbolicName(); + String location = bundle.getLocation(); + + // ignore wrap bundles, and stop tracking them + if (location.startsWith("wrap:mvn")) { + return null; + } + + activeBundles.add(name); + return name; + } + + @Override + public void removedBundle(final Bundle bundle, final BundleEvent event, final String name) { + activeBundles.remove(name); + } + }.open(); + } + + @Override + public Map getState() { + return ImmutableMap.of(STATE_ID, (Object)activeBundles); + } + +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/CommandXO.groovy b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/CommandXO.groovy new file mode 100644 index 0000000000000000000000000000000000000000..a05bdf33c129eaa558bafdda2d705f6d2ca0c0a4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/CommandXO.groovy @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.state + +import groovy.transform.ToString + +/** + * Command exchange object. + * + * @since 3.0 + */ +@ToString(includePackage = false, includeNames = true) +class CommandXO +{ + String type + + Object data +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/DatabaseStateContributor.groovy b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/DatabaseStateContributor.groovy new file mode 100644 index 0000000000000000000000000000000000000000..093de743ed0c367cd21ca4499bd4f8cba3cc8c76 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/DatabaseStateContributor.groovy @@ -0,0 +1,52 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.state + +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.goodies.common.ComponentSupport +import org.sonatype.nexus.orient.freeze.DatabaseFreezeService +import org.sonatype.nexus.rapture.StateContributor + +import static com.google.common.base.Preconditions.checkNotNull +import static com.google.common.collect.ImmutableMap.of + +@Named +@Singleton +class DatabaseStateContributor + extends ComponentSupport + implements StateContributor +{ + private static final String STATE_ID = 'db' + + private final DatabaseFreezeService databaseFreezeService + + @Inject + DatabaseStateContributor(final DatabaseFreezeService databaseFreezeService) { + this.databaseFreezeService = checkNotNull(databaseFreezeService) + } + + @Override + Map getState() { + def state = databaseFreezeService.getReadOnlyState() + return of(STATE_ID, + of( + 'dbFrozen', state.isFrozen(), + 'system', state.isSystemInitiated(), + 'reason', state.getSummaryReason() + ) + ) + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/FileDescriptorCheckValueContributor.groovy b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/FileDescriptorCheckValueContributor.groovy new file mode 100644 index 0000000000000000000000000000000000000000..0eabc44c37cc669e4e6f65eaa439bab3ba26b29c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/FileDescriptorCheckValueContributor.groovy @@ -0,0 +1,44 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.state + +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.goodies.common.ComponentSupport +import org.sonatype.nexus.common.system.FileDescriptorService +import org.sonatype.nexus.rapture.StateContributor + +@Named +@Singleton +class FileDescriptorCheckValueContributor + extends ComponentSupport + implements StateContributor +{ + private final FileDescriptorService fileDescriptorService + + @Inject + FileDescriptorCheckValueContributor(final FileDescriptorService fileDescriptorService) { + this.fileDescriptorService = fileDescriptorService; + } + + @Override + Map getState() { + return [file_descriptor_limit: [ + limitOk : fileDescriptorService.fileDescriptorLimitOk, + count : fileDescriptorService.fileDescriptorCount, + recommended: fileDescriptorService.fileDescriptorRecommended + ]] + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/LicenseStateContributor.groovy b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/LicenseStateContributor.groovy new file mode 100644 index 0000000000000000000000000000000000000000..08486d61682cb4a6834970fe41cd83e402cb8212 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/LicenseStateContributor.groovy @@ -0,0 +1,74 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.state + +import javax.annotation.Nullable +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.goodies.common.ComponentSupport +import org.sonatype.nexus.common.app.ApplicationLicense +import org.sonatype.nexus.rapture.StateContributor + +import com.google.common.collect.ImmutableMap + +import static com.google.common.base.Preconditions.checkNotNull + +/** + * Contributes {@code license} state. + * + * @since 3.0 + */ +@Named +@Singleton +public class LicenseStateContributor + extends ComponentSupport + implements StateContributor +{ + private static final String STATE_ID = "license" + + private final ApplicationLicense applicationLicense + + @Inject + public LicenseStateContributor(final ApplicationLicense applicationLicense) { + this.applicationLicense = checkNotNull(applicationLicense) + } + + @Nullable + @Override + public Map getState() { + return ImmutableMap.of(STATE_ID, calculateLicense()) + } + + private Object calculateLicense() { + LicenseXO result = new LicenseXO() + result.setRequired(applicationLicense.isRequired()) + result.setInstalled(applicationLicense.isInstalled()) + result.setValid(applicationLicense.isValid()) + Map attributes = applicationLicense.getAttributes() + if (attributes && attributes.get(ApplicationLicense.Attributes.EXPIRATION_DATE.getKey())) { + use(groovy.time.TimeCategory) { + def duration = attributes.get(ApplicationLicense.Attributes.EXPIRATION_DATE.getKey()) - new Date() + result.setDaysToExpiry(duration.days) + } + } + if (attributes && attributes.get(ApplicationLicense.Attributes.FEATURES.getKey())) { + result.setFeatures(attributes.get(ApplicationLicense.Attributes.FEATURES.getKey())) + } + else { + result.setFeatures(Collections.emptyList()) + } + return result + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/LicenseXO.groovy b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/LicenseXO.groovy new file mode 100644 index 0000000000000000000000000000000000000000..d3e74cbd2d5ccab0142a320b0e19d638a222dd3e --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/LicenseXO.groovy @@ -0,0 +1,34 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.state + +import groovy.transform.ToString + +/** + * License exchange object. + * + * @since 3.0 + */ +@ToString(includePackage = false, includeNames = true) +class LicenseXO +{ + boolean required + + boolean installed + + boolean valid + + int daysToExpiry + + List features +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/StateComponent.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/StateComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..3e9394f032a3f528c78a5da952aba009b2ef285c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/StateComponent.java @@ -0,0 +1,147 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.state; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.common.text.Strings2; +import org.sonatype.nexus.extdirect.DirectComponentSupport; +import org.sonatype.nexus.rapture.StateContributor; + +import com.codahale.metrics.annotation.ExceptionMetered; +import com.codahale.metrics.annotation.Timed; +import com.google.common.base.Objects; +import com.google.common.hash.Hashing; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.softwarementors.extjs.djn.config.annotations.DirectAction; +import com.softwarementors.extjs.djn.config.annotations.DirectPollMethod; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * State Ext.Direct component. + * + * @since 3.0 + */ +@Named +@Singleton +@DirectAction(action = "rapture_State") +public class StateComponent + extends DirectComponentSupport +{ + private static final Logger log = LoggerFactory.getLogger(StateComponent.class); + + /** + * Randomly generated identifier on each boot to identify the running instance of the server and detect server reboots. + */ + private final String serverId; + + private final List> stateContributors; + + @Inject + public StateComponent(final List> stateContributors, final NodeAccess nodeAccess) { + this.stateContributors = checkNotNull(stateContributors); + // special key on serverId hints UI event listeners to ignore serverId changes + final String prefix = checkNotNull(nodeAccess).isClustered() ? "ignore." : ""; + this.serverId = prefix + String.valueOf(System.nanoTime()); + } + + @Timed + @ExceptionMetered + @DirectPollMethod(event = "rapture_State_get") + public Map getState(final Map hashes) { + HashMap values = new HashMap<>(); + + // First add an entry for each hash we got. + // If state will not contribute a value for it, the state will be send back as null and such will be removed in UI + for (String key : hashes.keySet()) { + values.put(key, null); + } + + for (Provider contributor : stateContributors) { + try { + Map stateValues = contributor.get().getState(); + if (stateValues != null) { + for (Entry entry : stateValues.entrySet()) { + if (!Strings2.isBlank(entry.getKey())) { + maybeSend(values, hashes, entry.getKey(), entry.getValue()); + } + else { + log.warn("Blank state-id returned by {} (ignored)", contributor.getClass().getName()); + } + } + } + } + catch (Exception e) { + log.warn("Failed to get state from {} (ignored)", contributor.getClass().getName(), e); + } + } + + maybeSend(values, hashes, "serverId", serverId); + + return values; + } + + /** + * Include value in state unless its hash is unchanged. + */ + private void maybeSend(final Map values, + final Map hashes, + final String key, + final Object value) + { + values.remove(key); + String hash = hash(value); + if (!Objects.equal(hash, hashes.get(key))) { + StateValueXO data = new StateValueXO(); + data.setHash(hash); + data.setValue(value); + values.put(key, data); + } + } + + /** + * Gson instance used to calculate hashes. + */ + private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); // pretty for debugging + + /** + * Calculate (opaque) hash for given non-null value. + */ + @Nullable + private static String hash(@Nullable final Object value) { + if (value != null) { + // TODO: consider using Object.hashCode() and getting state contributors to ensure values have proper impls? + // TODO: ... or something else which is more efficient than object->gson->sha1? + String json = gson.toJson(value); + log.trace("Hashing state: {}", json); + return Hashing.sha1().hashString(json, StandardCharsets.UTF_8).toString(); + } + return null; + } + +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/StateValueXO.groovy b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/StateValueXO.groovy new file mode 100644 index 0000000000000000000000000000000000000000..0a0ef58524e98f2c3e90fc9d278743027e06d80a --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/StateValueXO.groovy @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.state + +import groovy.transform.ToString + +/** + * State value exchange object. + * + * @since 3.0 + */ +@ToString(includePackage = false, includeNames = true) +class StateValueXO +{ + String hash + + Object value +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/StatusStateContributor.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/StatusStateContributor.java new file mode 100644 index 0000000000000000000000000000000000000000..7c83663f7958528687bdc648a837c2482b7bd502 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/StatusStateContributor.java @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.state; + +import java.util.Map; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.app.ApplicationVersion; +import org.sonatype.nexus.rapture.StateContributor; + +import com.google.common.collect.ImmutableMap; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Contributes {@code status} state. + * + * @since 3.0 + */ +@Named +@Singleton +public class StatusStateContributor + extends ComponentSupport + implements StateContributor +{ + private static final String STATE_ID = "status"; + + private final ApplicationVersion applicationVersion; + + @Inject + public StatusStateContributor(final ApplicationVersion applicationVersion) { + this.applicationVersion = checkNotNull(applicationVersion); + } + + @Nullable + @Override + public Map getState() { + return ImmutableMap.of(STATE_ID, calculateStatus()); + } + + private Object calculateStatus() { + StatusXO result = new StatusXO(); + result.setVersion(applicationVersion.getVersion()); + result.setEdition(applicationVersion.getEdition()); + result.setBuildRevision(applicationVersion.getBuildRevision()); + result.setBuildTimestamp(applicationVersion.getBuildTimestamp()); + return result; + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/StatusXO.groovy b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/StatusXO.groovy new file mode 100644 index 0000000000000000000000000000000000000000..5ab8fa1c0f917e648e02e9c38800d4e2c722bd9b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/internal/state/StatusXO.groovy @@ -0,0 +1,32 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.state + +import groovy.transform.ToString + +/** + * Status exchange object. + * + * @since 3.0 + */ +@ToString(includePackage = false, includeNames = true) +class StatusXO +{ + String edition + + String version + + String buildRevision + + String buildTimestamp +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/package-info.java b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..c7b545a8130a28cdf696935fbb9b06591260a53c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/java/org/sonatype/nexus/rapture/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * UI framework. + * + * @since 3.0 + */ +package org.sonatype.nexus.rapture; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources-filtered/static/rapture/resources/baseapp-debug.css b/thirdparty-bundles/components/nexus-rapture/src/main/resources-filtered/static/rapture/resources/baseapp-debug.css new file mode 100644 index 0000000000000000000000000000000000000000..d42a0c9fa7d2b1f19a6c487356b82f5399672112 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources-filtered/static/rapture/resources/baseapp-debug.css @@ -0,0 +1,3 @@ +@import 'baseapp-debug_01.css${resource.url.suffix}'; +@import 'baseapp-debug_02.css${resource.url.suffix}'; +@import 'baseapp-debug_03.css${resource.url.suffix}'; diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources-filtered/static/rapture/resources/baseapp-prod.css b/thirdparty-bundles/components/nexus-rapture/src/main/resources-filtered/static/rapture/resources/baseapp-prod.css new file mode 100644 index 0000000000000000000000000000000000000000..dab1047d00f6ed5102cc3a20e3e60e4e8d2e5ae1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources-filtered/static/rapture/resources/baseapp-prod.css @@ -0,0 +1 @@ +@import 'baseapp-prod_01.css${resource.url.suffix}';@import 'baseapp-prod_02.css${resource.url.suffix}';@import 'baseapp-prod_03.css${resource.url.suffix}'; diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/app.vm b/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/app.vm new file mode 100644 index 0000000000000000000000000000000000000000..9ccdf4f0d493866e8fbf65a307c333febc8e0576 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/app.vm @@ -0,0 +1,28 @@ +#* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + *### +## require all plugins +#foreach($pluginConfig in $pluginConfigs) + Ext.require('$pluginConfig'); +#end + +## load the application +Ext.require('NX.app.Loader', function(Loader) { + new Loader().load({ + pluginConfigs: [ + #foreach($pluginConfig in $pluginConfigs) + '$pluginConfig'#if($foreach.hasNext),#end + #end + ], + state: $state + }); +}) diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/bootstrap.vm b/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/bootstrap.vm new file mode 100644 index 0000000000000000000000000000000000000000..16415e2f88f01dbe8872151de65b78231203e7df --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/bootstrap.vm @@ -0,0 +1,45 @@ +#* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + *### +## Dynamic loader configuration +Ext.Loader.setConfig({ + #if($debug) + enabled: true, + paths: { + NX: 'static/rapture/NX' + } + #else + enabled: false + #end +}); + +## add plugin namespaces needed for loading +#foreach($ns in $namespaces) + Ext.app.addNamespaces('$ns'); +#end + +## provide tool-agnostic reference to the global/window object +Ext.ns('NX'); +NX.global = (function() { + if (window !== undefined) { + return window; + } + if (global !== undefined) { + return global; + } + Ext.Error.raise('Unable to determine global object'); +}()); + +## HACK: Needed by NX.util.Url in class-def, should update that to reference dynamically and move this to loader state config +Ext.ns('NX.app'); +NX.app.baseUrl = '$baseUrl'; +NX.app.urlSuffix = '$urlSuffix'; diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/branding/rapture.branding-about.vm b/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/branding/rapture.branding-about.vm new file mode 100644 index 0000000000000000000000000000000000000000..cbf50d9f58414e1467d2d614163c6beb11f0ec59 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/branding/rapture.branding-about.vm @@ -0,0 +1,13 @@ +#* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + *### +Configure custom branding of the user-interface. diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/index.vm b/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/index.vm new file mode 100644 index 0000000000000000000000000000000000000000..4583857135d46bf03486daec21fbe44c0c01803b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/index.vm @@ -0,0 +1,108 @@ +#* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + *### +#set($raptureUrl="$baseUrl/static/rapture") + + + + + Nexus Repository Manager + + + + +#* START favicon links and meta + * favicon set for multiple browsers, OS shortcuts, etc. + * Generated at Real Favicon Generator, https://realfavicongenerator.net/ + * + * Associated files not referenced explicitly in link or meta tags: + * mstile-*.png - Win8+ desktop support + * browserconfig.xml - Win8+ desktop support + * apple-touch-icon.png - OSX Safari desktop support +*# +## IE, early versions (comment required) + +## Safari on MacOS and iOS + + +## classic favicon, shown in browser tabs + +## IE, later versions + +## Win8+ + + +## END favicon links + + + #macro(style $src) + + #end + + ## Load all styles + #foreach($uri in $styles) + #style("$uri") + #end + + ## Loading progress helpers + + #macro(message $text) + + #end + + + +
+
+
+ + +
+ + Loading ... +
+
+ + ## Code loading +
+ #macro(script $src) + + #end + + ## Load all scripts + #foreach($uri in $scripts) + #message("Loading ${util.fileName($uri)}") + #script("$uri") + #end + + ## Finished + #message("Initializing ...") +
+
+ +## Fields required for history management +
+ + +
+ + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/settings/rapture.settings-about.vm b/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/settings/rapture.settings-about.vm new file mode 100644 index 0000000000000000000000000000000000000000..beee7d63f0f40634728ebea296dee1f2fc762ef4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/org/sonatype/nexus/rapture/internal/settings/rapture.settings-about.vm @@ -0,0 +1,13 @@ +#* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + *### +Customize user-interface settings. \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_15227.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_15227.js new file mode 100644 index 0000000000000000000000000000000000000000..9ea852d826d3809467f07f1290d604dd45de189c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_15227.js @@ -0,0 +1,73 @@ +/* + Any code delivered as a result of the subject support + incident is delivered as is and is not officially supported. + Any such code is subject to the standard license agreement + between the customer and Sencha. It is the customer’s + responsibility to implement and maintain the workaround/override. + In addition, the customer will be responsible for removing the + workaround or override at the proper time when the appropriate + official release is available. + + BY USING THIS CODE YOU AGREE THAT IT IS DELIVERED "AS IS", + WITH NO WARRANTIES WHATSOEVER, INCLUDING, BUT NOT LIMITED TO, + IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, TITLE AND NON-INFRINGEMENT. + */ + +Ext.define('Ext.patch.Ticket_15227', { + override: 'Ext.view.Table', + 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]); + } + else { + // add a pixel to fix Chrome + widthAdjust += 1; + } + 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.titleEl.setStyle('text-overflow', 'clip'); + + // 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.titleEl.setStyle('text-overflow', ''); + + 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? + maxWidth = max(maxWidth, 40); + + // Set column width back to original width + columnSizer.setWidth(originalWidth); + + return maxWidth; + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_17866.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_17866.js new file mode 100644 index 0000000000000000000000000000000000000000..53a8cf721a94bfbf7f6d9f6293416723e3dad1c9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_17866.js @@ -0,0 +1,36 @@ +// Fixes bug where hover on a submenu causes it to disappear +// +// NOTE: This is a bug specifically in Chrome v43. Once v44 is released, check to see if this is fixed. +// https://www.sencha.com/forum/showthread.php?301116-Submenus-disappear-in-Chrome-43-beta +// +Ext.define('Ext.patch.Ticket_17866', { + override: 'Ext.menu.Menu', + onMouseLeave: function(e) { + var me = this; + + + // BEGIN FIX + var visibleSubmenu = false; + me.items.each(function(item) { + if(item.menu && item.menu.isVisible()) { + visibleSubmenu = true; + } + }); + if(visibleSubmenu) { + //console.log('apply fix hide submenu'); + return; + } + // END FIX + + + me.deactivateActiveItem(); + + + if (me.disabled) { + return; + } + + + me.fireEvent('mouseleave', me, e); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_18960.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_18960.js new file mode 100644 index 0000000000000000000000000000000000000000..2a3ca8736455c20d2b19914cb9f56cf755d7d486 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_18960.js @@ -0,0 +1,42 @@ +/** + * https://support.sencha.com/index.php#ticket-18960 + */ +Ext.define('Ext.patch.Ticket_18960', { + override: 'Ext.grid.RowEditor', + + 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, + scope = column.usingDefaultRenderer && !column.scope ? column : column.scope, + metaData, + rowIdx, + colIdx; + + // 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); + field.resetOriginalValue(); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_18964.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_18964.js new file mode 100644 index 0000000000000000000000000000000000000000..2e9c3c0dca07d05b65fc8bbea167285d98c80231 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_18964.js @@ -0,0 +1,21 @@ +/** + * https://support.sencha.com/index.php#ticket-18964 + */ +Ext.define('Ext.patch.Ticket_18964', { + override: 'Ext.view.BoundList', + + getRefItems: function() { + var me = this, + result = []; + + if (me.pagingToolbar) { + result.push(me.pagingToolbar); + } + // HACK: Disable including this, seems this value is 'true' for itemselector + // HACK: which totally messes up component queries + //if (me.loadMask) { + // result.push(me.loadMask); + //} + return result; + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_21425.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_21425.js new file mode 100644 index 0000000000000000000000000000000000000000..9405d0f8b993162034db26d68b3694b560b91bd8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_21425.js @@ -0,0 +1,13 @@ +/** + * https://support.sencha.com/index.php#ticket-21425 + */ +Ext.define('Ext.patch.Ticket_21425', { + override: 'Ext.util.History', + + getHash: function() { + // HACK: Firefox decodes the hash when accessed directly, so we need to use + // location.href to get it instead + // return this.win.location.hash.substr(1); + return location.href.split('#').splice(1).join('#'); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_22557_1.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_22557_1.js new file mode 100644 index 0000000000000000000000000000000000000000..fab05cc90a550fece50444b1265fae170371970b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_22557_1.js @@ -0,0 +1,10 @@ +/** + * https://support.sencha.com/index.php#ticket-22557 + */ +Ext.define('Ext.patch.Ticket_22557_1', { + override : 'Ext.view.Table', + + getMaxContentWidth: function(header) { + return this.callParent(arguments) + 4; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_22557_2.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_22557_2.js new file mode 100644 index 0000000000000000000000000000000000000000..dd2853a1c24fadb0a63f25f76731155a4e56ead7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/patch/Ticket_22557_2.js @@ -0,0 +1,35 @@ +/** + * https://support.sencha.com/index.php#ticket-22557 + */ +Ext.define('Ext.patch.Ticket_22557_2', { + override: 'Ext.grid.header.Container', + onHeaderCtEvent: function(e, t) { + var me = this, + headerEl = e.getTarget('.' + Ext.grid.column.Column.prototype.baseCls), + header, + targetEl, + isTriggerClick; + + if (headerEl && !me.ddLock) { + header = Ext.getCmp(headerEl.id); + if (header) { + targetEl = header[header.clickTargetName]; + if (e.within(targetEl)) { + if (e.type === 'click') { + isTriggerClick = header.onTitleElClick(e, targetEl); + if (isTriggerClick) { + me.onHeaderTriggerClick(header, e, t); + } else { + me.onHeaderClick(header, e, t); + } + } + else if (e.type === 'contextmenu') { + me.onHeaderContextMenu(header, e, t); + } else if (e.type === 'dblclick') { + header.onTitleElDblClick(e, targetEl.dom); + } + } + } + } + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/ux/ActivityMonitor.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/ux/ActivityMonitor.js new file mode 100644 index 0000000000000000000000000000000000000000..0339ccf0d04e8c2bb17dfe9ca866c9a7b9bf065c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/ux/ActivityMonitor.js @@ -0,0 +1,115 @@ +/*global Ext, window*/ + +/** + * @class Ext.ux.ActivityMonitor + * @author Arthur Kay (http://www.akawebdesign.com) + * @singleton + * @version 1.0 + * + * GitHub Project: https://github.com/arthurakay/ExtJS-Activity-Monitor + * + * The MIT License (MIT) + * + * Copyright (c) <2011> Arthur Kay + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +Ext.define('Ext.ux.ActivityMonitor', { + + ui: null, + runner: null, + task: null, + lastActive: null, + + ready: false, + verbose: false, + interval: (1000 * 60 * 1), //1 minute + maxInactive: (1000 * 60 * 5), //5 minutes + + constructor: function (config) { + if (!config) { + config = {}; + } + + Ext.apply(this, config, { + runner: new Ext.util.TaskRunner(), + ui: Ext.getBody(), + task: { + run: this.monitorUI, + interval: config.interval || this.interval, + scope: this + } + }); + + this.callParent(arguments); + }, + + isActive: Ext.emptyFn, + isInactive: Ext.emptyFn, + + start: function () { + this.ui.on('mousemove', this.captureActivity, this); + this.ui.on('mousedown', this.captureActivity, this); + this.ui.on('keydown', this.captureActivity, this); + + this.lastActive = new Date(); + this.log('ActivityMonitor has been started.'); + + this.runner.start(this.task); + }, + + stop: function () { + this.runner.stop(this.task); + this.lastActive = null; + + this.ui.un('mousemove', this.captureActivity); + this.ui.un('mousedown', this.captureActivity, this); + this.ui.un('keydown', this.captureActivity); + + this.log('ActivityMonitor has been stopped.'); + }, + + captureActivity: function (eventObj, el, eventOptions) { + this.lastActive = new Date(); + }, + + monitorUI: function () { + var now = new Date(), + inactive = (now - this.lastActive); + + if (inactive >= this.maxInactive) { + this.log('MAXIMUM INACTIVE TIME HAS BEEN REACHED'); + this.stop(); //remove event listeners + + this.isInactive(); + } + else { + this.log('CURRENTLY INACTIVE FOR ' + inactive + ' (ms)'); + this.isActive(); + } + }, + + log: function (msg) { + if (this.verbose) { + window.console.log(msg); + } + } + +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/ux/window/Notification.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/ux/window/Notification.js new file mode 100644 index 0000000000000000000000000000000000000000..8b88e6803cb4ae023540b0ae18a82782061edea5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/Ext/ux/window/Notification.js @@ -0,0 +1,497 @@ +// NOTE: Electing MIT license +/*global Ext*/ + +/* + * 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. + * + */ + +Ext.define('Ext.ux.window.Notification', { + extend: 'Ext.window.Window', + alias: 'widget.uxNotification', + + cls: 'ux-notification-window', + autoClose: true, + autoHeight: true, + plain: false, + draggable: false, + shadow: false, + focus: Ext.emptyFn, + + // For alignment and to store array of rendered notifications. Defaults to document if not set. + manager: null, + + useXAxis: false, + + // Options: br, bl, tr, tl, t, l, b, r + position: 'br', + + // Pixels between each notification + spacing: 6, + + // Pixels from the managers borders to start the first notification + paddingX: 30, + paddingY: 10, + + slideInAnimation: 'easeIn', + slideBackAnimation: 'bounceOut', + slideInDuration: 1500, + slideBackDuration: 1000, + hideDuration: 500, + autoCloseDelay: 7000, + stickOnClick: true, + stickWhileHover: true, + + // 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, + + statics: { + defaultManager: { + el: null + } + }, + + initComponent: function() { + var me = this; + + // Backwards compatibility + if (Ext.isDefined(me.corner)) { + me.position = me.corner; + } + if (Ext.isDefined(me.slideDownAnimation)) { + me.slideBackAnimation = me.slideDownAnimation; + } + if (Ext.isDefined(me.autoDestroyDelay)) { + me.autoCloseDelay = me.autoDestroyDelay; + } + if (Ext.isDefined(me.autoHideDelay)) { + me.autoCloseDelay = me.autoHideDelay; + } + if (Ext.isDefined(me.autoHide)) { + me.autoClose = me.autoHide; + } + if (Ext.isDefined(me.slideInDelay)) { + me.slideInDuration = me.slideInDelay; + } + if (Ext.isDefined(me.slideDownDelay)) { + me.slideBackDuration = me.slideDownDelay; + } + if (Ext.isDefined(me.fadeDelay)) { + me.hideDuration = me.fadeDelay; + } + + // 'bc', lc', 'rc', 'tc' compatibility + me.position = me.position.replace(/c/, ''); + + me.updateAlignment(me.position); + + me.setManager(me.manager); + + me.callParent(arguments); + }, + + onRender: function() { + var me = this; + me.callParent(arguments); + + me.el.hover( + function () { + me.mouseIsOver = true; + }, + function () { + me.mouseIsOver = false; + if (me.closeOnMouseOut) { + me.closeOnMouseOut = false; + me.close(); + } + }, + me + ); + + }, + + updateAlignment: function (position) { + var me = this; + + switch (position) { + case 'br': + me.paddingFactorX = -1; + me.paddingFactorY = -1; + me.siblingAlignment = "br-br"; + if (me.useXAxis) { + me.managerAlignment = "bl-br"; + } else { + me.managerAlignment = "tr-br"; + } + break; + case 'bl': + me.paddingFactorX = 1; + me.paddingFactorY = -1; + me.siblingAlignment = "bl-bl"; + if (me.useXAxis) { + me.managerAlignment = "br-bl"; + } else { + me.managerAlignment = "tl-bl"; + } + break; + case 'tr': + me.paddingFactorX = -1; + me.paddingFactorY = 1; + me.siblingAlignment = "tr-tr"; + if (me.useXAxis) { + me.managerAlignment = "tl-tr"; + } else { + me.managerAlignment = "br-tr"; + } + break; + case 'tl': + me.paddingFactorX = 1; + me.paddingFactorY = 1; + me.siblingAlignment = "tl-tl"; + if (me.useXAxis) { + me.managerAlignment = "tr-tl"; + } else { + me.managerAlignment = "bl-tl"; + } + break; + case 'b': + me.paddingFactorX = 0; + me.paddingFactorY = -1; + me.siblingAlignment = "b-b"; + me.useXAxis = 0; + me.managerAlignment = "t-b"; + break; + case 't': + me.paddingFactorX = 0; + me.paddingFactorY = 1; + me.siblingAlignment = "t-t"; + me.useXAxis = 0; + me.managerAlignment = "b-t"; + break; + case 'l': + me.paddingFactorX = 1; + me.paddingFactorY = 0; + me.siblingAlignment = "l-l"; + me.useXAxis = 1; + me.managerAlignment = "r-l"; + break; + case 'r': + me.paddingFactorX = -1; + me.paddingFactorY = 0; + me.siblingAlignment = "r-r"; + me.useXAxis = 1; + me.managerAlignment = "l-r"; + break; + } + }, + + getXposAlignedToManager: function () { + var me = this; + + var xPos = 0; + + // Avoid error messages if the manager does not have a dom element + if (me.manager && me.manager.el && me.manager.el.dom) { + if (!me.useXAxis) { + // Element should already be aligned vertically + return me.el.getLeft(); + } else { + // Using getAnchorXY instead of getTop/getBottom should give a correct placement when document is used + // as the manager but is still 0 px high. Before rendering the viewport. + if (me.position == 'br' || me.position == 'tr' || me.position == 'r') { + xPos += me.manager.el.getAnchorXY('r')[0]; + xPos -= (me.el.getWidth() + me.paddingX); + } else { + xPos += me.manager.el.getAnchorXY('l')[0]; + xPos += me.paddingX; + } + } + } + + return xPos; + }, + + getYposAlignedToManager: function () { + var me = this; + + var yPos = 0; + + // Avoid error messages if the manager does not have a dom element + if (me.manager && me.manager.el && me.manager.el.dom) { + if (me.useXAxis) { + // Element should already be aligned horizontally + return me.el.getTop(); + } else { + // Using getAnchorXY instead of getTop/getBottom should give a correct placement when document is used + // as the manager but is still 0 px high. Before rendering the viewport. + if (me.position == 'br' || me.position == 'bl' || me.position == 'b') { + yPos += me.manager.el.getAnchorXY('b')[1]; + yPos -= (me.el.getHeight() + me.paddingY); + } else { + yPos += me.manager.el.getAnchorXY('t')[1]; + yPos += me.paddingY; + } + } + } + + return yPos; + }, + + getXposAlignedToSibling: function (sibling) { + var me = this; + + if (me.useXAxis) { + if (me.position == 'tl' || me.position == 'bl' || me.position == 'l') { + // Using sibling's width when adding + return (sibling.xPos + sibling.el.getWidth() + sibling.spacing); + } else { + // Using own width when subtracting + return (sibling.xPos - me.el.getWidth() - me.spacing); + } + } else { + return me.el.getLeft(); + } + + }, + + getYposAlignedToSibling: function (sibling) { + var me = this; + + if (me.useXAxis) { + return me.el.getTop(); + } else { + if (me.position == 'tr' || me.position == 'tl' || me.position == 't') { + // Using sibling's width when adding + return (sibling.yPos + sibling.el.getHeight() + sibling.spacing); + } else { + // Using own width when subtracting + return (sibling.yPos - me.el.getHeight() - sibling.spacing); + } + } + }, + + getNotifications: function (alignment) { + var me = this; + + if (!me.manager.notifications[alignment]) { + me.manager.notifications[alignment] = []; + } + + return me.manager.notifications[alignment]; + }, + + setManager: function (manager) { + var me = this; + + me.manager = manager; + + if (typeof me.manager == 'string') { + me.manager = Ext.getCmp(me.manager); + } + + // If no manager is provided or found, then the static object is used and the el property pointed to the body document. + if (!me.manager) { + me.manager = me.statics().defaultManager; + + if (!me.manager.el) { + me.manager.el = Ext.getBody(); + } + } + + if (typeof me.manager.notifications == 'undefined') { + me.manager.notifications = {}; + } + }, + + beforeShow: function () { + var me = this; + + if (me.stickOnClick) { + if (me.body && me.body.dom) { + Ext.fly(me.body.dom).on('click', function () { + me.cancelAutoClose(); + me.addCls('notification-fixed'); + }, me); + } + } + + if (me.autoClose) { + me.task = new Ext.util.DelayedTask(me.doAutoClose, me); + me.task.delay(me.autoCloseDelay); + } + + // Shunting offscreen to avoid flicker + me.el.setX(-10000); + me.el.setOpacity(1); + + }, + + afterShow: function () { + var me = this; + + me.callParent(arguments); + + var notifications = me.getNotifications(me.managerAlignment); + + if (notifications.length) { + me.el.alignTo(notifications[notifications.length - 1].el, me.siblingAlignment, [0, 0]); + me.xPos = me.getXposAlignedToSibling(notifications[notifications.length - 1]); + me.yPos = me.getYposAlignedToSibling(notifications[notifications.length - 1]); + } else { + me.el.alignTo(me.manager.el, me.managerAlignment, [(me.paddingX * me.paddingFactorX), (me.paddingY * me.paddingFactorY)], false); + me.xPos = me.getXposAlignedToManager(); + me.yPos = me.getYposAlignedToManager(); + } + + Ext.Array.include(notifications, me); + + // Repeating from coordinates makes sure the windows does not flicker into the center of the viewport during animation + me.el.animate({ + from: { + x: me.el.getX(), + y: me.el.getY() + }, + to: { + x: me.xPos, + y: me.yPos, + opacity: 1 + }, + easing: me.slideInAnimation, + duration: me.slideInDuration, + dynamic: true + }); + + }, + + slideBack: function () { + var me = this; + + var notifications = me.getNotifications(me.managerAlignment); + var index = Ext.Array.indexOf(notifications, me) + + // Not animating the element if it already started to hide itself or if the manager is not present in the dom + if (!me.isHiding && me.el && me.manager && me.manager.el && me.manager.el.dom && me.manager.el.isVisible()) { + + if (index) { + me.xPos = me.getXposAlignedToSibling(notifications[index - 1]); + me.yPos = me.getYposAlignedToSibling(notifications[index - 1]); + } else { + me.xPos = me.getXposAlignedToManager(); + me.yPos = me.getYposAlignedToManager(); + } + + me.stopAnimation(); + + me.el.animate({ + to: { + x: me.xPos, + y: me.yPos + }, + easing: me.slideBackAnimation, + duration: me.slideBackDuration, + dynamic: true + }); + } + }, + + cancelAutoClose: function() { + var me = this; + + if (me.autoClose) { + me.task.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; + } + }, + + removeFromManager: function () { + var me = this; + + if (me.manager) { + var notifications = me.getNotifications(me.managerAlignment); + var index = Ext.Array.indexOf(notifications, me); + if (index != -1) { + // Requires Ext JS 4.0.2 + Ext.Array.erase(notifications, index, 1); + + // Slide "down" all notifications "above" the hidden one + for (;index < notifications.length; index++) { + notifications[index].slideBack(); + } + } + } + }, + + hide: function () { + var me = this; + + if (me.isHiding) { + if (!me.isFading) { + me.callParent(arguments); + // Must come after callParent() since it will pass through hide() again triggered by destroy() + 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 (me.el) { + me.el.fadeOut({ + opacity: 0, + easing: 'easeIn', + duration: me.hideDuration, + remove: me.destroyAfterHide, + listeners: { + afteranimate: function () { + me.isFading = false; + me.removeCls('notification-fixed'); + me.removeFromManager(); + me.hide(me.animateTarget, me.doClose, me); + } + } + }); + } + } + + return me; + }, + + destroy: function () { + var me = this; + if (!me.hidden) { + me.destroyAfterHide = true; + me.hide(me.animateTarget, me.doClose, me); + } else { + me.callParent(arguments); + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX.js new file mode 100644 index 0000000000000000000000000000000000000000..221c805c8ad86ff0761559948e983b4c6d54e36d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX.js @@ -0,0 +1,52 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +// NOTE: This is not a real class, but exists for jsdoc purposes ONLY +// NOTE: Namespace is created via bootstrap.js and app.js + +/** + * NX singleton. + * + * @class NX + * @singleton + * @since 3.0 + */ +(function () { + /** + * Global reference. + * + * @public + * @property {Object} global + * @readonly + */ + + /** + * Application reference. + * + * @internal + * @property {NX.app.Application} application + * @readonly + */ + + /** + * Container for bootstrap cruft. + * + * @internal + * @property {Object} app + * @property {Boolean} app.debug Flag if debug is enabled. + * @property {String} app.baseUrl Initial application base-url. + * @property {Object} app.state Initial application state. + * @readonly + */ +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Assert.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Assert.js new file mode 100644 index 0000000000000000000000000000000000000000..08cc6cf23fcecd8057d1e8e16f87730f16d7484d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Assert.js @@ -0,0 +1,52 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Assertion helper. + * + * @since 3.0 + */ +Ext.define('NX.Assert', { + singleton: true, + requires: [ + 'NX.Console' + ], + + /** + * Set to true to disable all assertions. + * + * @public + * @property {Boolean} + */ + disable: false, + + /** + * @public + * @param {Boolean} expression + * @param {String...} message + */ + assert: function() { + // + if (this.disable) { + return; + } + var args = Array.prototype.slice.call(arguments), + expression = args.shift(); + if (!expression) { + args.unshift('Assertion failure:'); + NX.Console.error.apply(NX.Console, args); + } + // + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Bookmark.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Bookmark.js new file mode 100644 index 0000000000000000000000000000000000000000..cbe590eeeb229d976f89556528a9e3de03ef9858 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Bookmark.js @@ -0,0 +1,113 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * A bookmark. + * + * @since 3.0 + */ +Ext.define('NX.Bookmark', { + + config: { + /** + * Bookmark token. + * + * @cfg {String} + */ + token: undefined + }, + + /** + * @private + */ + segments: undefined, + + /** + * @constructor + */ + constructor: function (config) { + this.initConfig(config); + }, + + /** + * Validates token to be a String and calculates segments. + * + * @private + * @param token to apply + * @returns {String} token + */ + applyToken: function (token) { + var me = this; + if (token && !Ext.isString(token)) { + throw Ext.Error.raise('Invalid token'); + } + if (token && (token.trim().length === 0)) { + token = undefined; + } + // avoid nulls + if (!token) { + token = undefined; + } + me.segments = []; + if (token) { + me.segments = token.split(':'); + } + return token; + }, + + /** + * @public + * @param {Number} index of segment + * @returns {String} segment at index if defined + */ + getSegment: function (index) { + return this.segments[index]; + }, + + /** + * @public + * @returns {Array} list of all segments in this bookmarks + */ + getSegments: function() { + return this.segments; + }, + + /** + * Appends a segment to current segment. + * + * @param {String/String[]} segments to append + * @returns {NX.Bookmark} itself + */ + appendSegments: function (segments) { + var me = this; + + if (!segments) { + throw Ext.Error.raise('Invalid segment: ' + segment); + } + if (!Ext.isArray(segments)) { + segments = [segments]; + } + Ext.each(segments, function (segment) { + if (!segment || !Ext.isString(segment)) { + throw Ext.Error.raise('Invalid segment: ' + segment); + } + me.segments.push(segment); + }); + + me.setToken(me.segments.join(':')); + + return me; + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Bookmarks.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Bookmarks.js new file mode 100644 index 0000000000000000000000000000000000000000..58170e14ff0b5e8c8f405e68bd32c53709d6e87e --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Bookmarks.js @@ -0,0 +1,106 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Helpers to interact with **{@link NX.controller.Bookmarking}** controller. + * + * @since 3.0 + */ +Ext.define('NX.Bookmarks', { + singleton: true, + requires: [ + 'NX.Bookmark' + ], + + /** + * @private + * @returns {NX.controller.Bookmarking} + */ + controller: function () { + return NX.getApplication().getBookmarkingController(); + }, + + /** + * @see NX.controller.Bookmarking#getBookmark + */ + getBookmark: function () { + return this.controller().getBookmark(); + }, + + /** + * @see NX.controller.Bookmarking#bookmark + */ + bookmark: function (bookmark, caller) { + return this.controller().bookmark(bookmark, caller); + }, + + /** + * @see NX.controller.Bookmarking#navigateTo + */ + navigateTo: function (bookmark, caller) { + return this.controller().navigateTo(bookmark, caller); + }, + + /** + * Navigate back by removing one or more segments from the given bookmark. + * @param bookmark + * @param segments + * @param caller + * @returns {*} + */ + navigateBackSegments: function(bookmark, segments, caller) { + return this.controller().navigateTo(NX.Bookmarks.fromSegments(bookmark.getSegments().slice(0, -segments)), caller); + }, + + /** + * Creates a new bookmark. + * + * @public + * @param {String} token bookmark token + * @returns {NX.Bookmark} created bookmark + */ + fromToken: function (token) { + return Ext.create('NX.Bookmark', { token: token }); + }, + + /** + * Creates a new bookmark from provided segments. + * + * @public + * @param {String[]} segments bookmark segments + * @returns {NX.Bookmark} created bookmark + */ + fromSegments: function (segments) { + var token; + if (Ext.isDefined(segments)) { + token = Ext.Array.from(segments).join(':'); + } + return Ext.create('NX.Bookmark', { token: token }); + }, + + /** + * Encodes the value suitable to be used as a bookmark token. + * (eliminate spaces and lower case) + * + * @param value to be encoded + * @returns {String} encoded value + */ + encode: function (value) { + if (!Ext.isString(value)) { + throw Ext.Error.raise('Value to be encoded must be a String'); + } + return value.replace(/\s/g, ''); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Conditions.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Conditions.js new file mode 100644 index 0000000000000000000000000000000000000000..186565316204359b1e2f23a5ed097243f0776b91 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Conditions.js @@ -0,0 +1,104 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Access point for available {NX.util.condition.Condition}s. + * + * @since 3.0 + */ +Ext.define('NX.Conditions', { + singleton: true, + requires: [ + 'NX.util.condition.Conjunction', + 'NX.util.condition.Disjunction', + 'NX.util.condition.FormHasRecord', + 'NX.util.condition.GridHasSelection', + 'NX.util.condition.IsPermitted', + 'NX.util.condition.StoreHasRecords', + 'NX.util.condition.WatchState', + 'NX.util.condition.NeverSatisfied' + ], + + /** + * @param {String} permission permission + * @returns {NX.util.condition.IsPermitted} + */ + isPermitted: function (permission) { + return Ext.create('NX.util.condition.IsPermitted', { permission: permission }); + }, + + /** + * @param {String} store id of store that should have records + * @returns {NX.util.condition.StoreHasRecords} + */ + storeHasRecords: function (store) { + return Ext.create('NX.util.condition.StoreHasRecords', { store: store }); + }, + + /** + * @param {String} grid a grid selector as specified by {@link Ext.ComponentQuery#query} + * @param {Function} [fn] to be called when grid has a selection to perform additional checks on the passed in model + * @returns {NX.util.condition.GridHasSelection} + */ + gridHasSelection: function (grid, fn) { + return Ext.create('NX.util.condition.GridHasSelection', { grid: grid, fn: fn }); + }, + + /** + * @param {String} form a {@link NX.view.SettingsForm} selector as specified by {@link Ext.ComponentQuery#query} + * @param {Function} [fn] to be called when form has a record to perform additional checks on the passed in model + * @returns {NX.util.condition.FormHasRecord} + */ + formHasRecord: function (form, fn) { + return Ext.create('NX.util.condition.FormHasRecord', { form: form, fn: fn }); + }, + + /** + * @param {String} key state value key + * @param {Function} [fn] An optional function to be called when a state value changes. If not specified, a boolean + * check against value will be performed + * @returns {NX.util.condition.WatchState} + */ + watchState: function (key, fn) { + return Ext.create('NX.util.condition.WatchState', { key: key, fn: fn }); + }, + + /** + * Takes as parameter {@link NX.util.condition.Condition}s to be AND-ed. + * + * @returns {NX.util.condition.Conjunction} + */ + and: function () { + return Ext.create('NX.util.condition.Conjunction', { conditions: Array.prototype.slice.call(arguments) }); + }, + + /** + * Takes as parameter {@link NX.util.condition.Condition}s to be OR-ed. + * + * @returns {NX.util.condition.Disjunction} + */ + or: function () { + return Ext.create('NX.util.condition.Disjunction', { conditions: Array.prototype.slice.call(arguments) }); + }, + + /** + * No-op condition that is never satisfied. + * + * @returns {NX.util.condition.NeverSatisfied} + */ + never: function() { + return Ext.create('NX.util.condition.NeverSatisfied'); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Console.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Console.js new file mode 100644 index 0000000000000000000000000000000000000000..a75385b597c4843e7bd7f554ab19be2ed690fd96 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Console.js @@ -0,0 +1,168 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Console helper. + * + * @since 3.0 + */ +Ext.define('NX.Console', { + singleton: true, + + /** + * @private + */ + console: undefined, + + /** + * Disable all application console output. + * + * @public + * @property {Boolean} + */ + disable: false, + + /** + * Set to true to enable console 'trace'. + * + * @public + * @property {Boolean} + */ + traceEnabled: false, + + /** + * Set to false to disable console 'debug'. + * + * @public + * @property {Boolean} + */ + debugEnabled: true, + + /** + * Set up the console environment. + */ + constructor: function () { + this.console = NX.global.console || {}; + + // apply default empty functions to console if missing + Ext.applyIf(this.console, { + log: Ext.emptyFn, + info: Ext.emptyFn, + warn: Ext.emptyFn, + error: Ext.emptyFn + }); + + // use ?debug to enable + this.debugEnabled = NX.global.location.href.search("[?&]debug") > -1; + + // use ?debug&trace to enable + this.traceEnabled = NX.global.location.href.search("[?&]trace") > -1; + }, + + /** + * Output a message to console at given level. + * + * @public + * @param {String} level + * @param {Array} args + */ + log: function (level, args) { + var c = this.console; + switch (level) { + case 'trace': + if (this.traceEnabled) { + c.log.apply(c, args); + } + break; + + case 'debug': + if (this.debugEnabled) { + c.log.apply(c, args); + } + break; + + case 'info': + c.info.apply(c, args); + break; + + case 'warn': + c.warn.apply(c, args); + break; + + case 'error': + c.error.apply(c, args); + break; + } + }, + + /** + * Outputs a trace message. + * + * @public + * @param {String/Object/Array} message + */ + trace: function() { + this.log('trace', Array.prototype.slice.call(arguments)); + }, + + /** + * Outputs a debug message. + * + * @public + * @param {String/Object/Array} message + */ + debug: function () { + this.log('debug', Array.prototype.slice.call(arguments)); + }, + + /** + * Outputs an info message. + * + * @public + * @param {String/Object/Array} message + */ + info: function () { + this.log('info', Array.prototype.slice.call(arguments)); + }, + + /** + * Outputs a warn message. + * + * @public + * @param {String/Object/Array} message + */ + warn: function () { + this.log('warn', Array.prototype.slice.call(arguments)); + }, + + /** + * Outputs an error message. + * + * @public + * @param {String/Object/Array} message + */ + error: function () { + this.log('error', Array.prototype.slice.call(arguments)); + }, + + /** + * Helper to record event details to console. + * + * @internal + * @param event + */ + recordEvent: function(event) { + this.log(event.level, [event.level, event.logger, event.message.join(' ')]); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Dialogs.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Dialogs.js new file mode 100644 index 0000000000000000000000000000000000000000..846577715a37b2dcbce2c8c733e25398729d44f7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Dialogs.js @@ -0,0 +1,91 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Helpers to show dialog boxes. + * + * @since 3.0 + */ +Ext.define('NX.Dialogs', { + singleton: true, + requires: [ + 'NX.I18n' + ], + + /** + * Show information dialog. + * + * @public + */ + showInfo: function (title, message, options) { + options = options || {}; + + // set default configuration + Ext.applyIf(options, { + title: title || NX.I18n.get('Dialogs_Info_Title'), + msg: message, + buttons: Ext.Msg.OK, + icon: Ext.MessageBox.INFO, + closable: true + }); + + Ext.Msg.show(options); + }, + + /** + * Show error dialog. + * + * @public + */ + showError: function (title, message, options) { + options = options || {}; + + // set default configuration + Ext.applyIf(options, { + title: title || NX.I18n.get('Dialogs_Error_Title'), + msg: message || NX.I18n.get('Dialogs_Error_Message'), + buttons: Ext.Msg.OK, + icon: Ext.MessageBox.ERROR, + closable: true + }); + + Ext.Msg.show(options); + }, + + /** + * Show confirmation dialog. + * + * @public + */ + askConfirmation: function (title, message, onYesFn, options) { + options = options || {}; + Ext.Msg.show({ + title: title, + msg: message, + buttons: Ext.Msg.YESNO, + icon: Ext.MessageBox.QUESTION, + closable: false, + animEl: options.animEl, + buttonText: options.buttonText, + fn: function (buttonName) { + if (buttonName === 'yes' || buttonName === 'ok') { + if (Ext.isDefined(onYesFn)) { + onYesFn.call(options.scope); + } + } + } + }); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/I18n.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/I18n.js new file mode 100644 index 0000000000000000000000000000000000000000..07eafb3d8702077c8eeb9eef073aabb7527185f0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/I18n.js @@ -0,0 +1,160 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * I18n helper. + * + * @since 3.0 + */ +Ext.define('NX.I18n', { + singleton: true, + mixins: { + logAware: 'NX.LogAware' + }, + + /** + * @private + * @property {Object} + */ + keys: {}, + + /** + * @private + * @property {Object} + */ + bundles: {}, + + /** + * @public + * @param {Object} strings + */ + register: function (strings) { + Ext.apply(this.keys, strings.keys); + Ext.apply(this.bundles, strings.bundles); + }, + + /** + * Resolves a string from a key. + * + * If the key begins with '@' then the remainder is assumed to be a reference to another key and will be resolved. + * + * @public + * @param {String} key + */ + get: function (key) { + var text = this.keys[key]; + if (text === null || text === undefined) { + this.logWarn('Missing I18n key:', key); + return 'MISSING_I18N:' + key; + } + // resolve references + if (text.charAt(0) === '@') { + return this.get(text.substring(1, text.length)); + } + else { + return text; + } + }, + + /** + * @public + * @param {String} key + * @param {Object...} values + * @returns {String} + */ + format: function (key) { + var text = this.get(key); + if (text) { + var params = Array.prototype.slice.call(arguments); + // replace first element with text + params.shift(); + params.unshift(text); + text = Ext.String.format.apply(this, params); + } + return text; + }, + + /** + * Render a bundle string. + * + * @param {Object/String} bundle + * @param {String} key + * @param {Object...} [params] + * @returns {String} + */ + render: function (bundle, key) { + var resources, text, params; + + // resolve bundle + if (Ext.isObject(bundle)) { + bundle = Ext.getClassName(bundle); + } + + // + this.logTrace('Resolving bundle:', bundle, 'key:', key); + // + + resources = this.bundles[bundle]; + if (resources === undefined) { + this.logWarn('Missing I18n bundle:', bundle); + return 'MISSING_I18N:' + bundle + ':' + key; + } + + // resolve text + text = resources[key]; + if (text === undefined) { + // handle bundle extension + var extend = resources['$extend']; + if (extend !== undefined) { + params = Array.prototype.slice.call(arguments, 1); + params.unshift(extend); + return this.render.apply(this, params); + } + + this.logWarn('Missing I18n bundle key:', bundle, ':', key); + return 'MISSING_I18N:' + bundle + ':' + key; + } + + // resolve references + if (text.charAt(0) === '@') { + if (text.indexOf(':') !== -1) { + // bundle ref :[key] + var items = text.substring(1, text.length).split(':', 2); + + // default to given key if ref missing key + if (items[1] === '') { + items[1] = key; + } + + params = Array.prototype.slice.call(arguments, 2); + params.unshift(items[1]); + params.unshift(items[0]); + return this.render.apply(this, params); + } + else { + // key ref + text = this.get(text.substring(1, text.length)); + } + } + + // optionally format with parameters + if (arguments.length > 2) { + params = Array.prototype.slice.call(arguments, 2); + params.unshift(text); + text = Ext.String.format.apply(this, params); + } + + return text; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Icons.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Icons.js new file mode 100644 index 0000000000000000000000000000000000000000..a032a2ae3aa785af1d3f72d92b555d79f8e385e9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Icons.js @@ -0,0 +1,92 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Helpers to interact with Icon controller. + * + * @since 3.0 + */ +Ext.define('NX.Icons', { + singleton: true, + requires: [ + 'Ext.DomHelper', + 'NX.util.Url' + ], + mixins: { + logAware: 'NX.LogAware' + }, + + /** + * Helper to get the CSS class for a named icon with optional variant. + * + * @public + */ + cls: function (name, variant) { + // translate . -> _ to avoid problems with CSS selector syntax muck + var cls = 'nx-icon-' + name.replace('.', '_'); + if (variant) { + cls += '-' + variant; + } + return cls; + }, + + /** + * Helper to get html text for a named icon with variant. + * + * @public + */ + img: function(name, variant) { + return Ext.DomHelper.markup({ + tag: 'img', + src: Ext.BLANK_IMAGE_URL, + cls: this.cls(name, variant) + }); + }, + + /** + * Helper to get a cache-busted URL for an icon name + variant + optional extension. + * + * @param name + * @param [variant] + * @param [ext] The file extension to use, png if not set. + * @returns {string} + */ + url: function(name, variant, ext) { + var file = name; + + if (ext === undefined) { + ext = 'png'; + } + file += '.' + ext; + + return this.url2(file, variant); + }, + + /** + * Helper to get a cache-busted URL for an icon file + optional variant. + * + * @param file + * @param [variant] + * @returns {string} + */ + url2: function(file, variant) { + var url = NX.util.Url.baseUrl + '/static/rapture/resources/icons/'; + if (variant) { + url += variant + '/'; + } + url += file; + return NX.util.Url.cacheBustingUrl(url); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Log.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Log.js new file mode 100644 index 0000000000000000000000000000000000000000..dce0a5d93ce3597dbcd4a81a1af95cb5d15d07f3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Log.js @@ -0,0 +1,86 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Global logging helper. + * + * @since 3.0 + */ +Ext.define('NX.Log', { + singleton: true, + requires: [ + 'NX.Console' + ], + + /** + * Reference to attached logging controller. + * + * @private + * @property {NX.controller.Logging} + */ + controller: undefined, + + /** + * Queue of events logged before controller is attached. + * This is deleted upon attachment after events are passed to the controller. + * + * @private + */ + eventQueue: [], + + /** + * Attach to the logging controller. + * + * @internal + * @param {NX.controller.Logging} controller + */ + attach: function (controller) { + var me = this; + me.controller = controller; + + // reply queued events and clear + Ext.each(me.eventQueue, function (event) { + me.controller.recordEvent(event); + }); + delete me.eventQueue; + }, + + /** + * Record a log event. + * + * @public + * @param {String} level + * @param {String} logger + * @param {String/Array} message + */ + recordEvent: function (level, logger, message) { + var me = this, + event = { + timestamp: Date.now(), + level: level, + logger: logger, + message: message + }; + + // if controller is attached, delegate to record the event + if (me.controller) { + me.controller.recordEvent(event); + } + else { + // else queue the event and emit to console + me.eventQueue.push(event); + NX.Console.recordEvent(event); + } + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/LogAware.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/LogAware.js new file mode 100644 index 0000000000000000000000000000000000000000..797cac618cfb6de10956c61fe6cc209d2e519f2b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/LogAware.js @@ -0,0 +1,96 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Adds logging support helpers to objects. + * + * @since 3.0 + */ +Ext.define('NX.LogAware', { + requires: [ + 'NX.Log' + ], + + /** + * Log a message at the given level. + * + * @param {String} level + * @param {Array} args + */ + log: function (level, args) { + // + NX.Log.recordEvent(level, Ext.getClassName(this), args); + // + }, + + /** + * Log a trace message. + * + * @public + * @param {String/Object/Array} message + */ + logTrace: function () { + // + this.log('trace', Array.prototype.slice.call(arguments)); + // + }, + + /** + * Log a debug message. + * + * @public + * @param {String/Object/Array} message + */ + logDebug: function () { + // + this.log('debug', Array.prototype.slice.call(arguments)); + // + }, + + /** + * Log an info message. + * + * @public + * @param {String/Object/Array} message + */ + logInfo: function () { + // + this.log('info', Array.prototype.slice.call(arguments)); + // + }, + + /** + * Log a warn message. + * + * @public + * @param {String/Object/Array} message + */ + logWarn: function () { + // + this.log('warn', Array.prototype.slice.call(arguments)); + // + }, + + /** + * Log an error message. + * + * @public + * @param {String/Object/Array} message + */ + logError: function () { + // + this.log('error', Array.prototype.slice.call(arguments)); + // + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Messages.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Messages.js new file mode 100644 index 0000000000000000000000000000000000000000..8b0d340982474c0422958b4226e960a1437e3ade --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Messages.js @@ -0,0 +1,102 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Helpers to interact with Message controller. + * + * @since 3.0 + */ +Ext.define('NX.Messages', { + singleton: true, + + /** + * Add a new custom message. + * + * @public + * @param {Object} message + * @param {boolean} indicates that the message is already encoded + */ + add: function (message, encoded) { + message.text = encoded ? message.text : Ext.util.Format.htmlEncode(message.text); + NX.getApplication().getMessageController().addMessage(message); + }, + + /** + * Check if a message with a matching type already exists + * @public + * @param {Object} message + */ + messageExists: function(message) { + return NX.getApplication().getMessageController().messageExists(message); + }, + + // + // High-level helpers + // + + /** + * Add a notice message. + * + * @public + * @param {string} message + * @param {boolean} indicates that the message is already encoded + */ + notice: function (message, encoded) { + this.add({type: 'default', text: message}, encoded); + }, + + /** + * Add an info message. + * + * @public + * @param {string} message + * @param {boolean} indicates that the message is already encoded + */ + info: function (message, encoded) { + this.add({type: 'primary', text: message}, encoded); + }, + + /** + * Add an error message. + * + * @public + * @param {string} message + * @param {boolean} indicates that the message is already encoded + */ + error: function (message, encoded) { + this.add({type: 'danger', text: message}, encoded); + }, + + /** + * Add a warning message. + * + * @public + * @param {string} message + * @param {boolean} indicates that the message is already encoded + */ + warning: function (message, encoded) { + this.add({type: 'warning', text: message}, encoded); + }, + + /** + * Add a success message. + * + * @public + * @param {string} message + * @param {boolean} indicates that the message is already encoded + */ + success: function (message, encoded) { + this.add({type: 'success', text: message}, encoded); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Permissions.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Permissions.js new file mode 100644 index 0000000000000000000000000000000000000000..4b553b2992ee37e049fef33aae421e7a1acbebf0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Permissions.js @@ -0,0 +1,211 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Permissions helper. + * + * @since 3.0 + */ +Ext.define('NX.Permissions', { + singleton: true, + requires: [ + 'NX.Assert', + 'NX.util.Array' + ], + mixins: { + logAware: 'NX.LogAware' + }, + + /** + * Map between permission and permitted boolean value. + * + * @private + */ + permissions: undefined, + + /** + * Map of permission to implied calculated value. + * + * @private + */ + impliedCache: undefined, + + /** + * @public + * @returns {boolean} True, if permissions had been set (loaded from server) + */ + available: function() { + return Ext.isDefined(this.permissions); + }, + + /** + * Sets permissions. + * + * @public + * @param {Object} permissions + */ + setPermissions: function(permissions) { + var me = this; + + // defensive copy + me.permissions = Ext.clone(permissions); + + // reset implied cache + me.impliedCache = {}; + + // + me.logDebug('Permissions installed'); + // + }, + + /** + * Resets all permissions. + * + * @public + */ + resetPermissions: function() { + var me = this; + + // + me.logDebug('Resetting permissions'); + // + + delete me.permissions; + delete me.impliedCache; + }, + + /** + * Check if the current subject has the expected permission. + * + * @public + * @param {String} expectedPermission + * @returns {boolean} True if user is authorized for expected permission. + */ + check: function(expectedPermission) { + var me = this, + hasPermission = false; + + // + NX.Assert.assert(expectedPermission.search('undefined') === -1, 'Invalid permission check:', expectedPermission); + // + + // short-circuit if permissions are not installed + if (!me.available()) { + return false; + } + + // check for exact match first + if (me.permissions[expectedPermission] !== undefined) { + return me.permissions[expectedPermission]; + } + + // or use cached implied if we know it + if (me.impliedCache[expectedPermission] !== undefined) { + // + me.logTrace('Using cached implied permission:', expectedPermission, 'is:', me.impliedCache[expectedPermission]); + // + hasPermission = me.impliedCache[expectedPermission]; + } + else { + // otherwise calculate if permission is implied or not + Ext.Object.each(me.permissions, function (permission, permitted) { + if (permitted && me.implies(permission, expectedPermission)) { + hasPermission = true; + return false; // break + } + return true; // continue + }); + + // cache calculated implied + me.impliedCache[expectedPermission] = hasPermission; + + // + me.logTrace('Cached implied permission:', expectedPermission, 'is:', hasPermission); + // + } + + return hasPermission; + }, + + /** + * Returns true if permission1 implies permission2 using same logic as WildcardPermission. + * + * @private + * @param {string} permission1 Granted permission + * @param {string} permission2 Permission under-test + * @return {boolean} + */ + implies: function(permission1, permission2) { + var parts1 = permission1.split(':'), + parts2 = permission2.split(':'), + part1, part2, i; + + // + this.logTrace('Checking if:', permission1, 'implies:', permission2); + // + + for (i = 0; i < parts2.length; i++) { + // If this permission has less parts than the other permission, everything after the number of parts contained + // in this permission is automatically implied, so return true + if (parts1.length - 1 < i) { + return true; + } + else { + part1 = parts1[i].split(','); + part2 = parts2[i].split(','); + if (!Ext.Array.contains(part1, '*') && !NX.util.Array.containsAll(part1, part2)) { + return false; + } + } + } + + // If this permission has more parts than the other parts, only imply it if all of the other parts are wildcards + for (; i < parts1.length; i++) { + part1 = parts1[i].split(','); + if (!Ext.Array.contains(part1, '*')) { + return false; + } + } + + return true; + }, + + /** + * Check if any permission exists with given prefix and is permitted. + * + * This does not perform any implied checking, use with caution. + * + * @public + * @param {string} prefix + */ + checkExistsWithPrefix: function(prefix) { + var me = this, + exists = false; + + // short-circuit if permissions are not installed + if (!me.available()) { + return false; + } + + Ext.Object.each(me.permissions, function(permission, permitted) { + if (Ext.String.startsWith(permission, prefix) && permitted === true) { + exists = true; + return false; // break + } + return true; // continue + }); + + return exists; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Security.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Security.js new file mode 100644 index 0000000000000000000000000000000000000000..ea1e84d473890ef525ed2c160a642a7e55e2f5c9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Security.js @@ -0,0 +1,74 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Helpers to interact with **{@link NX.controller.User}** controller. + * + * @since 3.0 + */ +Ext.define('NX.Security', { + singleton: true, + requires: [ + 'NX.controller.User' + ], + + /** + * @private + * @returns {NX.controller.User} + */ + controller: function () { + return NX.getApplication().getController('User'); + }, + + /** + * @see NX.controller.User#hasUser + */ + hasUser: function () { + var me = this; + if (me.controller()) { + return me.controller().hasUser(); + } + }, + + /** + * @see NX.controller.User#askToAuthenticate + */ + askToAuthenticate: function (message, options) { + var me = this; + if (me.controller()) { + me.controller().askToAuthenticate(message, options); + } + }, + + /** + * @see NX.controller.User#doWithAuthenticationToken + */ + doWithAuthenticationToken: function (message, options) { + var me = this; + if (me.controller()) { + me.controller().doWithAuthenticationToken(message, options); + } + }, + + /** + * @see NX.controller.User#signOut + */ + signOut: function () { + var me = this; + if (me.controller()) { + me.controller().signOut(); + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/State.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/State.js new file mode 100644 index 0000000000000000000000000000000000000000..64cd025e99429f479a6e19871997b68da4ed27a4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/State.js @@ -0,0 +1,222 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Helpers to interact with **{@link NX.controller.State}** controller. + * + * @since 3.0 + */ +Ext.define('NX.State', { + singleton: true, + requires: [ + 'Ext.Version' + ], + mixins: { + observable: 'Ext.util.Observable', + logAware: 'NX.LogAware' + }, + + /** + * @constructor + * @param {Object} config + */ + constructor: function (config) { + var me = this; + + me.mixins.observable.constructor.call(me, config); + + me.addEvents( + /** + * Fires when any of application context values changes. + * + * @event changed + * @param {NX.State} this + */ + 'changed' + ); + }, + + /** + * @public + * @returns {boolean} true, if browser is supported + */ + isBrowserSupported: function () { + return this.getValue('browserSupported') === true; + }, + + /** + * @public + * @param {boolean} value true, if browser is supported + */ + setBrowserSupported: function (value) { + this.setValue('browserSupported', value === true); + }, + + /** + * @public + * @returns {boolean} true, if license is required + */ + requiresLicense: function () { + return this.getValue('license', {})['required'] === true; + }, + + /** + * @public + * @returns {boolean} true, if license is installed + */ + isLicenseInstalled: function () { + return this.getValue('license', {})['installed'] === true; + }, + + /** + * @public + * @returns {boolean} true, if license is installed and valid + */ + isLicenseValid: function() { + return this.isLicenseInstalled() && this.getValue('license', {})['valid'] === true; + }, + + /** + * @public + * @returns {number} of days until license expires, may be null + */ + getDaysToLicenseExpiry: function() { + return this.getValue('license', {})['daysToExpiry']; + }, + + /** + * @public + * @param {string} feature name + * @returns {boolean} true, if feature exists + */ + hasFeature: function(feature) { + var features = this.getValue('license', {})['features']; + if (features) { + return features.indexOf(feature) !== -1; + } + return false; + }, + + /** + * @public + * @returns {Object} current user, if any + */ + getUser: function () { + return this.getValue('user'); + }, + + /** + * @public + * @param {Object} [user] current user to be set + * @returns {*} + */ + setUser: function (user) { + this.setValue('user', user); + }, + + /** + * Return status.version + * + * @public + * @returns {string} + */ + getVersion: function() { + return this.getValue('status')['version']; + }, + + /** + * Returns major.minor parts of status.version. + * + * @public + * @returns {string} + */ + getVersionMajorMinor: function() { + // Ext.Version doesn't fully support our version scheme, but the major.minor bits it handles fine + var v = Ext.create('Ext.Version', this.getVersion()); + return v.getMajor() + '.' + v.getMinor(); + }, + + /** + * Return status.edition. + * + * @public + * @returns {string} + */ + getEdition: function() { + return this.getValue('status')['edition']; + }, + + /** + * Return status.edition and status.version suitable for branded display. + * + * @public + * @returns {string} + */ + getBrandedEditionAndVersion: function() { + var edition = this.getEdition(), + version = this.getVersion(); + + return edition + ' ' + version; + }, + + /** + * Return status.buildRevision. + * + * @public + * @returns {string} + */ + getBuildRevision: function() { + return this.getValue('status')['buildRevision']; + }, + + /** + * Return status.buildTimestamp. + * + * @public + * @returns {string} + */ + getBuildTimestamp: function() { + return this.getValue('status')['buildTimestamp']; + }, + + /** + * Return whether or not we're receiving from the server. + * + * @returns {boolean} + */ + isReceiving: function() { + return this.getValue('receiving'); + }, + + getValue: function (key, defaultValue) { + return this.controller().getValue(key, defaultValue); + }, + + setValue: function (key, value) { + this.controller().setValue(key, value); + }, + + setValues: function (values) { + this.controller().setValues(values); + }, + + /** + * @private + * @returns {NX.controller.State} + */ + controller: function () { + return NX.getApplication().getStateController(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Windows.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Windows.js new file mode 100644 index 0000000000000000000000000000000000000000..e4ede0d0f7ea0dda118e3ec5ee2b3b0165c53785 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/Windows.js @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Helpers to open browser windows. + * + * @since 3.0 + */ +Ext.define('NX.Windows', { + singleton: true, + requires: [ + 'NX.Messages', + 'NX.I18n' + ], + mixins: { + logAware: 'NX.LogAware' + }, + + /** + * Open a new browser window. + * + * @public + * @return Browser window object or {@code null} if unable to open. + */ + open: function(url, name, specs, replace) { + var win; + + // apply default window specs if omitted, helps keep windows user-controllable on most browsers + if (specs === undefined) { + specs = 'menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes'; + } + + // + this.logDebug('Opening window: url=' + url + ', name=' + name + ', specs=' + specs + ', replace=' + replace); + // + + win = NX.global.open(url, name, specs, replace); + if (win === null) { + NX.Messages.add({text: NX.I18n.get('Windows_Popup_Message'), type: 'danger'}); + } + return win; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/Application.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/Application.js new file mode 100644 index 0000000000000000000000000000000000000000..84aa84169b8e15da75979c4587db6b198895603e --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/Application.js @@ -0,0 +1,427 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Nexus application. + * + * @since 3.0 + */ +Ext.define('NX.app.Application', { + extend: 'Ext.app.Application', + + requires: [ + 'Ext.Ajax', + 'Ext.Error', + 'Ext.direct.Manager', + 'Ext.state.Manager', + 'Ext.state.LocalStorageProvider', + 'Ext.util.LocalStorage', + 'NX.view.Viewport', + 'NX.util.Url', + 'NX.I18n', + 'NX.State' + ], + + mixins: { + logAware: 'NX.LogAware' + }, + + uses: [ + // framework patches + 'Ext.patch.Ticket_15227', + 'Ext.patch.Ticket_17866', + 'Ext.patch.Ticket_18960', + 'Ext.patch.Ticket_18964', + 'Ext.patch.Ticket_21425', + 'Ext.patch.Ticket_22557_1', + 'Ext.patch.Ticket_22557_2', + + // direct overrides + 'NX.ext.direct.RemotingProvider', + 'NX.ext.form.action.DirectLoad', + 'NX.ext.form.action.DirectSubmit', + + // form overrides + 'NX.ext.form.FieldContainer', + 'NX.ext.form.field.Base', + 'NX.ext.form.field.Checkbox', + 'NX.ext.form.field.Display', + 'NX.ext.form.field.Number', + + // custom form fields + 'NX.ext.form.OptionalFieldSet', + 'NX.ext.form.field.Email', + 'NX.ext.form.field.Password', + 'NX.ext.form.field.RegExp', + 'NX.ext.form.field.Url', + 'NX.ext.form.field.ClearableComboBox', + 'NX.ext.form.field.DateDisplayField', + 'NX.ext.form.field.ValueSet', + 'NX.ext.SearchBox', + 'Ext.ux.form.ItemSelector', + + // grid plugins + 'NX.ext.grid.plugin.FilterBox', + 'NX.ext.grid.plugin.RemoteFilterBox', + 'NX.ext.grid.plugin.Filtering', + + // grid overrides + 'NX.ext.grid.column.Date', + + // custom grid columns + 'NX.ext.grid.column.Icon', + 'NX.ext.grid.column.CopyLink' + ], + + name: 'NX', + + /** + * Store application instance in "NX.application". + */ + appProperty: 'application', + + /** + * Relative to /index.html + */ + appFolder: 'static/rapture/NX', + + paths: { + 'Ext.ux': 'static/rapture/Ext/ux', + 'Ext.patch': 'static/rapture/Ext/patch' + }, + + /** + * Always active controllers. + */ + controllers: [ + 'Copy', + 'Logging', + 'State', + 'Bookmarking', + 'ExtDirect', + 'Features', + 'Icon', + 'KeyNav', + 'Message', + 'Permissions' + ], + + /** + * Managed controller configurations. + * + * @private + * @property {Ext.util.MixedCollection} + */ + managedControllers: undefined, + + statics: { + alwaysActive: function () { + return true; + }, + defaultActivation: function () { + return NX.app.Application.supportedBrowser(); + }, + supportedBrowser: function () { + return NX.State.isBrowserSupported(); + }, + unsupportedBrowser: function () { + return !NX.app.Application.supportedBrowser(); + }, + licensed: function () { + return !NX.State.requiresLicense() || NX.State.isLicenseValid(); + }, + unlicensed: function () { + return !NX.app.Application.licensed(); + }, + licenseExpired: function() { + var daysToLicenseExpiry = NX.State.getDaysToLicenseExpiry(); + return NX.app.Application.licensed() && daysToLicenseExpiry ? daysToLicenseExpiry < 0 : false; + }, + debugMode: function () { + return NX.State.getValue('debug') === true; + }, + bundleActive: function (symbolicName) { + return NX.State.getValue('activeBundles').indexOf(symbolicName) > -1; + } + }, + + /** + * Flag to indicate that app is ready. + * + * @public + * @property {Boolean} + * @readonly + */ + ready: false, + + /** + * @override + * @param {NX.app.Application} app this class + */ + init: function (app) { + var me = this; + + // + me.logInfo('Initializing'); + me.logDebug(me.managedControllers.getCount(), 'managed controllers'); + // + + // Configure blank image URL + Ext.BLANK_IMAGE_URL = NX.util.Url.baseUrl + '/static/rapture/resources/images/s.gif'; + + Ext.Ajax.defaultHeaders = { + // HACK: Setting request header to allow analytics to tell if the request came from the UI or not + // HACK: This has some issues, will only catch ajax requests, etc... but may be fine for now + 'X-Nexus-UI': 'true' + }; + + app.initErrorHandler(); + app.initDirect(); + app.initState(); + }, + + /** + * Hook into browser error handling (in order to log them). + * + * @private + */ + initErrorHandler: function () { + var me = this, + originalOnError = NX.global.onerror; + + // FIXME: This needs further refinement, seems like javascript errors are lost in Firefox (but show up fine in Chrome) + + // pass unhandled errors to application error handler + Ext.Error.handle = function (err) { + me.handleError(err); + }; + + // FIXME: This will catch more errors, but duplicates messages for ext errors + // FIXME: Without this however some javascript errors will go unhandled + NX.global.onerror = function (msg, url, line) { + me.handleError({ msg: msg + ' (' + url + ':' + line + ')' }); + + // maybe delegate to original onerror handler? + if (originalOnError) { + originalOnError(msg, url, line); + } + }; + + // + me.logDebug('Configured error handling'); + // + }, + + /** + * Handle application error. + * + * @private + */ + handleError: function (error) { + NX.Messages.add({ + type: 'danger', + text: this.errorAsString(error) + }); + }, + + /** + * Customize error to-string handling. + * + * Ext.Error.toString() assumes instance, but raise(String) makes anonymous object. + * + * @private + */ + errorAsString: function (error) { + var className = error.sourceClass || '', + methodName = error.sourceMethod ? '.' + error.sourceMethod + '(): ' : '', + msg = error.msg || '(No description provided)'; + return className + methodName + msg; + }, + + /** + * Initialize Ex.Direct remote providers. + * + * @private + */ + initDirect: function () { + var remotingProvider; + + remotingProvider = Ext.direct.Manager.addProvider(NX.direct.api.REMOTING_API); + + // configure Ext.Direct buffer-window milliseconds + remotingProvider.enableBuffer = 10; + + // disable retry + remotingProvider.maxRetries = 0; + + // default request timeout to 60 seconds + remotingProvider.timeout = 60 * 1000; + + // + this.logDebug('Configured Ext.Direct'); + // + }, + + /** + * Initialize state manager. + * + * @private + */ + initState: function () { + var me = this, + provider; + + // If local storage is supported install state provider + if (Ext.util.LocalStorage.supported) { + provider = Ext.create('Ext.state.LocalStorageProvider'); + Ext.state.Manager.setProvider(provider); + // + me.logDebug('Configured state provider: local'); + // + } + else { + // + me.logWarn('Local storage not supported; state management not supported'); + // + } + + // + if (provider) { + provider.on('statechange', function (provider, key, value, opts) { + me.logTrace('State changed:', key, '->', (value ? value : '(deleted)')); + }); + } + // + }, + + /** + * Starts the application. + * + * @public + */ + start: function () { + var me = this, becomeReady; + + // + me.logInfo('Starting'); + // + + Ext.create('NX.view.Viewport'); + + me.syncManagedControllers(); + me.listen({ + controller: { + '#State': { + changed: me.syncManagedControllers + } + } + }); + + becomeReady = function () { + // hide the loading mask after we have loaded + Ext.get('loading').remove(); + Ext.fly('loading-mask').animate({ opacity: 0, remove: true }); + + // mark app as ready + me.logInfo('Ready'); + me.ready = true; + }; + + // FIXME: Need a better way to know when the UI is actually rendered so we can hide the mask + // HACK: for now increasing delay slightly to cope with longer loading times + Ext.defer(becomeReady, 500); + }, + + /** + * Fired when synchronizing controllers and changes were detected. + * + * @event controllerschanged + */ + + /** + * Create / Destroy managed controllers based on their active status. + * + * @private + */ + syncManagedControllers: function () { + var me = this, + ref, initializedControllers = [], + changes = false; + + // + me.logDebug('Refreshing controllers'); + // + + // destroy all controllers that are become inactive + me.managedControllers.eachKey(function (key) { + ref = me.managedControllers.get(key); + if (!ref.active()) { + if (ref.controller) { + changes = true; + + // + me.logDebug('Destroying controller:', key); + // + + ref.controller.fireEvent('destroy', ref.controller); + + // private reference to controller.eventbus + ref.controller.eventbus.unlisten(ref.controller.id); + + if (Ext.isFunction(ref.controller.onDestroy)) { + ref.controller.onDestroy(); + } + me.controllers.remove(ref.controller); + ref.controller.clearManagedListeners(); + if (Ext.isFunction(ref.controller.destroy)) { + ref.controller.destroy(); + } + delete ref.controller; + } + } + }); + + // create & init all controllers that become active + me.managedControllers.eachKey(function (key) { + ref = me.managedControllers.get(key); + if (ref.active()) { + if (!ref.controller) { + changes = true; + + // + me.logDebug('Initializing controller:', key); + // + + ref.controller = me.getController(key); + initializedControllers.push(ref.controller); + } + } + }); + + // launch any initialized controller + Ext.each(initializedControllers, function (controller) { + controller.onLaunch(me); + }); + // finish init on any initialized controller + Ext.each(initializedControllers, function (controller) { + controller.finishInit(me); + }); + + if (changes) { + // TODO shall we do this on each refresh? + me.getIconController().installStylesheet(); + me.fireEvent('controllerschanged'); + } + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/Controller.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/Controller.js new file mode 100644 index 0000000000000000000000000000000000000000..3cca853bcdc1fbd43b37be855eb062dddd118cde --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/Controller.js @@ -0,0 +1,49 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Nexus managed controller. + * + * @since 3.0 + * @see NX.app.Application#syncManagedControllers + */ +Ext.define('NX.app.Controller', { + extend: 'Ext.app.Controller', + mixins: { + logAware: 'NX.LogAware' + }, + + /** + * Event fired when a controller is being destroyed. + * + * @event destroy + * @param {NX.app.Controller} controller The controller being destroyed. + */ + + /** + * Optional callback to invoke when a controller is being destroyed. + * + * @protected + * @property {Function} + */ + onDestroy: undefined, + + /** + * Optional callback to invoke when a controller has fully destroyed. + * + * @protected + * @property {Function} + */ + destroy: undefined +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/Loader.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/Loader.js new file mode 100644 index 0000000000000000000000000000000000000000..c2fe61643df304ab8f2f514c31ae7147427c0ed9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/Loader.js @@ -0,0 +1,195 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Nexus application loader. + * + * @since 3.0 + */ +Ext.define('NX.app.Loader', { + requires: [ + 'NX.app.Application', + 'Ext.app.Controller', + 'Ext.util.MixedCollection' + ], + mixins: { + logAware: 'NX.LogAware' + }, + + /** + * Discovered plugin controllers. + * + * @private + * @property {Ext.util.MixedCollection} + */ + controllers: undefined, + + /** + * Load the application. + * + * @public + */ + load: function (config) { + var me = this, App; + + // + me.logInfo('Loading'); + // + + // sanity check config + if (!Ext.isArray(config.pluginConfigs)) { + Ext.Error.raise("Invalid config property 'pluginConfigs' (expected array): " + config.pluginConfigs); + } + if (!Ext.isObject(config.state)) { + Ext.Error.raise("Invalid config property: 'state' (expected object): " + config.state); + } + + // + me.logDebug('ExtJS version:', Ext.getVersion('extjs')); + // + + me.controllers = Ext.create('Ext.util.MixedCollection'); + + // attach initial application state + NX.app.state = config.state; + NX.app.debug = false; + if (NX.global.location.search === '?debug') { + if (NX.app.state.uiSettings.value.debugAllowed) { + NX.app.debug = true; + + // + me.logInfo('Debug mode enabled'); + // + } + else { + me.logWarn('Debug mode disallowed'); + } + } + + // apply all plugin configurations + // + me.logDebug('Plugin configs:', config.pluginConfigs); + // + + Ext.each(config.pluginConfigs, function (className) { + me.applyPluginConfig(className); + }); + + // launch the application + App = Ext.ClassManager.get('NX.app.Application'); + Ext.onReady(function () { + // + me.logDebug('Received Ext.ready event'); + // + + Ext.app.Application.instance = new App({ + managedControllers: me.controllers + }); + + // + me.logInfo('Application loaded'); + // + + Ext.app.Application.instance.start(); + + // + me.logInfo('Application started'); + // + }); + }, + + /** + * Apply plugin customizations. + * + * @private + * @param {String} className + */ + applyPluginConfig: function (className) { + var me = this, + config; + + // + me.logDebug('Applying plugin config:', className); + // + + config = Ext.create(className); + + // find all controllers + if (config.controllers) { + Ext.each(config.controllers, function (config) { + var controller = me.defineController(config); + me.controllers.add(controller); + }); + } + + // resolve all controllers + if (me.controllers) { + // + me.logDebug(me.controllers.getCount(), 'plugin controllers:'); + // + + me.controllers.each(function (controller) { + // attach full class-name to controller defs + controller.type = Ext.app.Controller.getFullName(controller.id, 'controller', 'NX').absoluteName; + + // + me.logDebug(' + ' + controller.id + (controller.id !== controller.type ? ' (' + controller.type + ')' : '')); + // + + // require all controllers, to avoid warning in console + Ext.require(controller.type); + }); + } + }, + + /** + * Define controller from configuration. + * + * @private + * @param {String|Object} config + * @return {Object} controller configuration + */ + defineController: function(config) { + // simple definition of controller class-name + if (Ext.isString(config)) { + return { + id: config, + active: NX.app.Application.defaultActivation + }; + } + + // advanced configuration of controller + if (!Ext.isObject(config)) { + Ext.Error.raise('Invalid controller definition: ' + config); + } + + // require 'id' parameter + if (!Ext.isString(config.id) || config.id.length === 0) { + Ext.Error.raise('Invalid controller definition: ' + config + '; required property: id'); + } + + // require or normalize 'active' parameter + if (Ext.isBoolean(config.active)) { + var flag = config.active; + config.active = function() { + return flag; + }; + } + else if (!Ext.isFunction(config.active)) { + Ext.Error.raise('Invalid controller definition: ' + config.id + '; required property: active (boolean or function)'); + } + + return config; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/PluginConfig.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/PluginConfig.js new file mode 100644 index 0000000000000000000000000000000000000000..dd98ef6a84cb5af649741d943ae78b41cd457bea --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/PluginConfig.js @@ -0,0 +1,90 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Core plugin configuration. + * + * @since 3.0 + */ +Ext.define('NX.app.PluginConfig', { + '@aggregate_priority': 100, + + requires: [ + 'NX.app.PluginStrings' + ], + + controllers: [ + 'Content', + 'Dashboard', + 'Help', + 'Main', + 'Menu', + 'MenuGroup', + 'Refresh', + 'SettingsForm', + 'UiSessionTimeout', + 'User', + + { + id: 'Branding', + // branding is active in also when we are unlicensed or browser is not supported + active: true + }, + { + id: 'Unlicensed', + active: function () { + return NX.app.Application.supportedBrowser() && + (NX.app.Application.unlicensed() || NX.app.Application.licenseExpired()); + } + }, + { + id: 'UnsupportedBrowser', + active: function () { + return NX.app.Application.unsupportedBrowser(); + } + }, + + // dev controllers (visible when ?debug and rapture capability debugAllowed = true) + { + id: 'dev.Conditions', + active: function () { + return NX.app.Application.debugMode; + } + }, + { + id: 'dev.Developer', + active: function () { + return NX.app.Application.debugMode; + } + }, + { + id: 'dev.Permissions', + active: function () { + return NX.app.Application.debugMode; + } + }, + { + id: 'dev.Stores', + active: function () { + return NX.app.Application.debugMode; + } + }, + { + id: 'dev.Logging', + active: function () { + return NX.app.Application.debugMode; + } + } + ] +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/PluginStrings.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/PluginStrings.js new file mode 100644 index 0000000000000000000000000000000000000000..a0536e47f855ea20edcb883078af1b3329111329 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/app/PluginStrings.js @@ -0,0 +1,185 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Application strings + * + * @since 3.0 + */ +Ext.define('NX.app.PluginStrings', { + '@aggregate_priority': 90, + + singleton: true, + + requires: [ + 'NX.I18n' + ], + + // + // Note: Symbols follow the following naming convention: + // __ + // + + keys: { + // Buttons + Button_Back: 'Back', + Button_Cancel: 'Cancel', + Button_Close: 'Close', + Button_Create: 'Create', + Button_Discard: 'Discard', + Button_Next: 'Next', + Button_Save: 'Save', + + // Header + Header_Panel_Logo_Text: 'Nexus Repository Manager', + Header_BrowseMode_Title: 'Browse', + Header_BrowseMode_Tooltip: 'Browse server contents', + Header_AdminMode_Title: 'Administration', + Header_AdminMode_Tooltip: 'Server administration and configuration', + Header_QuickSearch_Empty: 'Search components', + Header_QuickSearch_Tooltip: 'Quick component keyword search', + Header_Refresh_Tooltip: 'Refresh current view and data', + Refresh_Message: 'Refreshed', + Header_UserMode_Title: 'User', + User_Tooltip: 'Hi, {0}. Manage your user account.', + Header_SignIn_Text: 'Sign in', + Header_SignIn_Tooltip: 'Sign in', + Header_SignOut_Text: 'Sign out', + Header_SignOut_Tooltip: 'Sign out', + Header_Help_Tooltip: 'Help', + Help_Feature_Text: 'Help for: ', + Header_Help_Feature_Tooltip: 'Help and documentation for the currently selected feature', + Header_Help_About_Text: 'About', + Header_Help_About_Tooltip: 'About Nexus Repository Manager', + Header_Help_Documentation_Text: 'Documentation', + Header_Help_Documentation_Tooltip: 'Product documentation', + Header_Help_KB_Text: 'Knowledge base', + Header_Help_KB_Tooltip: 'Knowledge base', + Header_Help_Community_Text: 'Community', + Header_Help_Community_Tooltip: 'Community information', + Header_Help_Issues_Text: 'Issue tracker', + Header_Help_Issues_Tooltip: 'Issue and bug tracker', + Header_Help_Support_Text: 'Support', + Header_Help_Support_Tooltip: 'Product support', + + // Footer + Footer_Panel_HTML: 'Copyright © 2008-present, Sonatype Inc. All rights reserved.', + + // Sign in + SignIn_Title: 'Sign In', + User_SignIn_Mask: 'Signing in…', + SignIn_Username_Empty: 'Username', + SignIn_Password_Empty: 'Password', + SignIn_Submit_Button: 'Sign in', + SignIn_Cancel_Button: '@Button_Cancel', + + // Filter box + Grid_Plugin_FilterBox_Empty: 'Filter', + + // Dialogs + Dialogs_Info_Title: 'Information', + Dialogs_Error_Title: 'Error', + Dialogs_Error_Message: 'Operation failed', + Add_Submit_Button: '@Button_Create', + Add_Cancel_Button: '@Button_Cancel', + ChangeOrderWindow_Submit_Button: '@Button_Save', + ChangeOrderWindow_Cancel_Button: '@Button_Cancel', + + // Server + User_ConnectFailure_Message: 'Operation failed as server could not be contacted', + State_Reconnected_Message: 'Server reconnected', + State_Disconnected_Message: 'Server disconnected', + UiSessionTimeout_Expire_Message: 'Session is about to expire', + UiSessionTimeout_Expired_Message: 'Session expired after being inactive for {0} minutes', + User_SignedIn_Message: 'User signed in: {0}', + User_SignedOut_Message: 'User signed out', + User_Credentials_Message: 'Incorrect username or password, or no permission to use the application.', + Util_DownloadHelper_Download_Message: 'Download initiated', + Windows_Popup_Message: 'Window pop-up was blocked!', + + // License + State_Installed_Message: 'License installed', + State_Uninstalled_Message: 'License uninstalled', + State_License_Expiry: 'Your license will expire in {0} days. Contact us to renew.', + State_License_Expired: 'Your license has expired. Contact us to renew.', + State_License_Invalid_Message: 'Your license has been detected as missing or invalid. Upload a valid license to proceed.', + + // About modal + AboutWindow_Title: 'About Nexus Repository Manager', + AboutWindow_About_Title: 'Copyright', + AboutWindow_License_Tab: 'License', + + // Authentication modal + Authenticate_Title: 'Authenticate', + Authenticate_Help_Text: 'You have requested an operation which requires validation of your credentials.', + User_Controller_Authenticate_Mask: 'Authenticate…', + User_View_Authenticate_Submit_Button: 'Authenticate', + User_Retrieving_Mask: 'Retrieving authentication token…', + Authenticate_Cancel_Button: '@Button_Cancel', + + // Expiry modal + ExpireSession_Title: 'Session', + ExpireSession_Help_Text: 'Session is about to expire', + UiSessionTimeout_Expire_Text: 'Session will expire in {0} seconds', + SignedOut_Text: 'Your session has expired. Please sign in.', + ExpireSession_Cancel_Button: '@Button_Cancel', + ExpireSession_SignIn_Button: 'Sign in', + + // Unsaved changes modal + UnsavedChanges_Title: 'Unsaved changes', + UnsavedChanges_Help_HTML: '

Do you want to discard your changes?

', + UnsavedChanges_Discard_Button: 'Discard changes', + UnsavedChanges_Back_Button: 'Go back', + Menu_Browser_Title: 'You will lose your unsaved changes', + + // Unsupported browser + UnsupportedBrowser_Title: 'The browser you are using is not supported', + UnsupportedBrowser_Alternatives_Text: 'Below is a list of alternatives that are supported by this web application', + UnsupportedBrowser_Continue_Button: 'Ignore and continue', + + // 404 + Feature_NotFoundPath_Text: 'Path "{0}" not found', + Feature_NotFound_Text: 'Path not found', + + // Buttons + SettingsForm_Save_Button: '@Button_Save', + SettingsForm_Discard_Button: '@Button_Discard', + Ldap_LdapServerConnectionAdd_Text: '@Button_Next', + + // Item selector + Form_Field_ItemSelector_Empty: 'Filter', + + // Settings form + SettingsForm_Load_Message: 'Loading', + SettingsForm_Submit_Message: 'Saving', + + // Browse -> Welcome + Dashboard_Title: 'Welcome', + Dashboard_Description: 'Welcome to Nexus Repository Manager!', + + // Field validation messages + Util_Validator_Text: 'Only letters, digits, underscores(_), hyphens(-), and dots(.) are allowed and may not start with underscore or dot.', + Util_Validator_Hostname: 'Hostname must be valid', + Util_Validator_Trim: 'A Role ID may not start or end with a space.', + + // Wizard + Wizard_Next: '@Button_Next', + Wizard_Back: '@Button_Back', + Wizard_Cancel: '@Button_Cancel', + Wizard_Screen_Progress: '{0} of {1}' + } +}, function(obj) { + NX.I18n.register(obj); +}); + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Bookmarking.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Bookmarking.js new file mode 100644 index 0000000000000000000000000000000000000000..b5692839ea47e968e478e4adf5dea1e0af0a439d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Bookmarking.js @@ -0,0 +1,154 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Bookmarking controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.Bookmarking', { + extend: 'NX.app.Controller', + requires: [ + 'Ext.History', + 'NX.Bookmark', + 'NX.Bookmarks' + ], + + /** + * If this controller had been launched. Becomes true after onLaunch() method is called by ExtJS. + */ + launched: false, + + /** + * @override + */ + init: function () { + var me = this; + + // The only requirement for this to work is that you must have a hidden field and + // an iframe available in the page with ids corresponding to Ext.History.fieldId + // and Ext.History.iframeId. See history.html for an example. + Ext.History.useTopWindow = false; + Ext.History.init(); + + me.bindToHistory(); + + me.addEvents( + /** + * Fires when user navigates to a new bookmark. + * + * @event navigate + * @param {String} bookmark value + */ + 'navigate' + ); + }, + + /** + * @public + * @returns {NX.Bookmark} current bookmark + */ + getBookmark: function () { + return NX.Bookmarks.fromToken(Ext.History.bookmark || Ext.History.getToken()); + }, + + /** + * Sets bookmark to a specified value. + * + * @public + * @param {NX.Bookmark} bookmark new bookmark + * @param {Object} [caller] whom is asking to bookmark + */ + bookmark: function (bookmark, caller) { + var me = this, + oldValue = me.getBookmark().getToken(); + + if (!me.launched) { + return; + } + + if (bookmark && oldValue !== bookmark.getToken()) { + // + me.logDebug('Bookmark:', bookmark.getToken(), (caller ? '(' + caller.self.getName() + ')' : '')); + // + + Ext.History.bookmark = bookmark.getToken(); + Ext.History.add(bookmark.getToken()); + } + }, + + /** + * Sets bookmark to a specified value and navigates to it. + * + * @public + * @param {NX.Bookmark} bookmark to navigate to + * @param {Object} [caller] whom is asking to navigate + */ + navigateTo: function (bookmark, caller) { + var me = this; + + if (!me.launched) { + return; + } + + if (bookmark) { + // + me.logDebug('Navigate to:', bookmark.getToken(), (caller ? '(' + caller.self.getName() + ')' : '')); + // + + me.bookmark(bookmark, caller); + me.fireEvent('navigate', bookmark); + } + }, + + /** + * Navigate to current bookmark. + * + * @override + */ + onLaunch: function () { + var me = this; + + me.launched = true; + + me.navigateTo(me.getBookmark(), me); + }, + + /** + * Sets bookmark to a specified value and navigates to it. + * + * @private + * @param {String} token to navigate to + */ + onNavigate: function (token) { + var me = this; + + if (token !== Ext.History.bookmark) { + delete Ext.History.bookmark; + me.navigateTo(NX.Bookmarks.fromToken(token), me); + } + }, + + /** + * Start listening to **{@link Ext.History}** change events. + * + * @private + */ + bindToHistory: function () { + var me = this; + + Ext.History.on('change', me.onNavigate, me); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Branding.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Branding.js new file mode 100644 index 0000000000000000000000000000000000000000..d2ca735e0e144b2eb41e433175e134dd59170717 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Branding.js @@ -0,0 +1,109 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Branding controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.Branding', { + extend: 'NX.app.Controller', + requires: [ + 'NX.State' + ], + + views: [ + 'header.Branding' + ], + + refs: [ + { ref: 'viewport', selector: 'viewport' }, + { ref: 'headerBranding', selector: 'nx-header-branding' }, + { ref: 'footerBranding', selector: 'nx-footer-branding' } + ], + + /** + * @override + */ + init: function() { + var me = this; + + me.listen({ + controller: { + '#State': { + brandingchanged: me.onBrandingChanged + } + }, + component: { + 'nx-header-branding': { + afterrender: me.renderHeaderBranding + }, + 'nx-footer-branding': { + afterrender: me.renderFooterBranding + } + } + }); + }, + + /** + * Render header/footer branding when branding configuration changes. + * + * @private + */ + onBrandingChanged: function() { + this.renderHeaderBranding(); + this.renderFooterBranding(); + }, + + /** + * Render header branding. + * + * @private + */ + renderHeaderBranding: function() { + var branding = NX.State.getValue('branding'), + headerBranding = this.getHeaderBranding(); + + if (headerBranding) { + if (branding && branding['headerEnabled']) { + headerBranding.update(branding['headerHtml']); + headerBranding.show(); + } + else { + headerBranding.hide(); + } + } + }, + + /** + * Render footer branding. + * + * @private + */ + renderFooterBranding: function() { + var branding = NX.State.getValue('branding'), + footerBranding = this.getFooterBranding(); + + if (footerBranding) { + if (branding && branding['footerEnabled']) { + footerBranding.update(branding['footerHtml']); + footerBranding.show(); + } + else { + footerBranding.hide(); + } + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Content.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Content.js new file mode 100644 index 0000000000000000000000000000000000000000..774f034cdef739c178fbb866d5b4e0065b783ff9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Content.js @@ -0,0 +1,127 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Content (features area) controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.Content', { + extend: 'NX.app.Controller', + requires: [ + 'NX.Icons', + 'NX.State' + ], + + views: [ + 'feature.Content' + ], + + refs: [ + { + ref: 'featureContent', + selector: 'nx-feature-content' + } + ], + + /** + * @override + */ + init: function () { + var me = this; + + me.listen({ + controller: { + '#Menu': { + featureselected: me.onFeatureSelected + } + }, + component: { + 'nx-feature-content': { + resize: function (obj) { + var drilldown; + if (obj) { + drilldown = obj.down('nx-drilldown'); + if (drilldown) { + drilldown.fireEvent('syncsize'); + } + } + } + } + } + }); + }, + + /** + * Update content to selected feature view. + * + * @private + * @param {NX.model.Feature} feature selected feature + */ + onFeatureSelected: function (feature) { + var me = this, + content = me.getFeatureContent(), + view = feature.get('view'), + text = feature.get('text'), + iconName = feature.get('iconName'), + description = feature.get('description'), + cmp; + + // create new view and replace any current view + if (Ext.isString(view)) { + cmp = me.getView(view).create({}); + } + else { + cmp = Ext.widget(view); + } + me.mon(cmp, 'destroy', function () { + // + me.logTrace('Destroyed:', cmp.self.getName()); + // + }); + + // remove the current contents + content.removeAll(); + + // update title and icon + content.setTitle(text); + content.setIconCls(NX.Icons.cls(iconName, 'x32')); + + // Reset unsaved changes flag + content.resetUnsavedChangesFlag(); + + // set browser title + NX.global.document.title = text + ' - ' + NX.State.getValue('uiSettings').title; + + // update description + if (description === undefined) { + description = ''; + } + content.setDescription(description); + + // Update the breadcrumb + content.showRoot(); + + // install new feature view + content.add(cmp); + + // fire activate event to view component + cmp.fireEvent('activate', cmp); + + // + me.logInfo('Content changed to:', text, 'class:', cmp.self.getName()); + // + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Copy.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Copy.js new file mode 100644 index 0000000000000000000000000000000000000000..02ff1b333f862ee0ffd0972dd424af5037a2ebac --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Copy.js @@ -0,0 +1,52 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Copy window controller + * + * @since 3.0 + */ +Ext.define('NX.controller.Copy', { + extend: 'NX.app.Controller', + + views: [ + 'CopyWindow' + ], + + refs: [ + { + ref: 'copyModal', + selector: 'nx-copywindow' + } + ], + + /** + * @override + */ + init: function () { + var me = this; + + me.listen({ + component: { + 'nx-copywindow button[action=close]': { + click: me.copyToClipboard + } + } + }); + }, + + copyToClipboard: function() { + this.getCopyModal().close(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Dashboard.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Dashboard.js new file mode 100644 index 0000000000000000000000000000000000000000..da034d2ae570d1567779bbc08ace71f6e25a9fdc --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Dashboard.js @@ -0,0 +1,49 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Dashboard controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.Dashboard', { + extend: 'NX.app.Controller', + requires: [ + 'NX.I18n' + ], + + views: [ + 'dashboard.Welcome' + ], + + /** + * @override + */ + init: function () { + this.getApplication().getFeaturesController().registerFeature({ + path: '/Welcome', + mode: 'browse', + view: 'NX.view.dashboard.Welcome', + text: NX.I18n.get('Dashboard_Title'), + description: NX.I18n.get('Dashboard_Description'), + iconConfig: { + file: 'sonatype.png', + variants: ['x16', 'x32'] + }, + weight: 10, + authenticationRequired: false + }); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Drilldown.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Drilldown.js new file mode 100644 index 0000000000000000000000000000000000000000..55cecde872fdba0e1800ccfb4dcd5bbfda4d4fda --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Drilldown.js @@ -0,0 +1,917 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Abstract Master/Detail controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.Drilldown', { + extend: 'NX.app.Controller', + requires: [ + 'NX.Conditions', + 'NX.Dialogs', + 'NX.Bookmarks', + 'NX.view.drilldown.Drilldown', + 'NX.view.drilldown.Item', + 'NX.State' + ], + + views: [ + 'drilldown.Drilldown', + 'drilldown.Details' + ], + + permission: undefined, + + /** + * @protected + * Get the human-readable name of a model + */ + getDescription: Ext.emptyFn, + + /** + * @override + * An array of xtypes which represent the masters available to this drilldown + */ + masters: null, + + /** + * @override + * The xtype of the detail panel used by this drilldown. Only needed when no masters exist. + */ + detail: null, + + /** + * @cfg {Function} optional function to be called on delete + */ + deleteModel: undefined, + + /** + * @override + */ + onLaunch: function () { + this.getApplication().getIconController().addIcons({ + 'drilldown-info': { + file: 'information.png', + variants: ['x16', 'x32'] + }, + 'drilldown-warning': { + file: 'warning.png', + variants: ['x16', 'x32'] + } + }); + }, + + /** + * @override + */ + init: function () { + var me = this, + componentListener = {}; + + // Normalize lists into an array + if (!me.masters) { + me.masters = []; + } + + // Add event handlers to each list + for (var i = 0; i < me.masters.length; ++i) { + componentListener[me.masters[i]] = { + selection: me.onSelection, + cellclick: me.onCellClick + }; + } + + // Drilldown + componentListener[(me.masters[0] || me.detail) + ' ^ nx-drilldown'] = { + syncsize: me.syncSizeToOwner, + activate: function() { + me.currentIndex = 0; + me.reselect(); + me.syncSizeToOwner(); + } + }; + + // New button + componentListener[me.masters[0] + ' ^ nx-drilldown button[action=new]'] = { + afterrender: me.bindNewButton + }; + + // Delete button + componentListener[me.masters[0] + ' ^ nx-drilldown button[action=delete]'] = { + afterrender: me.bindDeleteButton, + click: me.onDelete + }; + + // Back button + componentListener[(me.masters[0] || me.detail) + ' ^ nx-drilldown nx-addpanel button[action=back]'] = { + click: function() { + me.showChild(0, true); + } + }; + + me.listen({ + component: componentListener, + controller: { + '#Bookmarking': { + navigate: me.onNavigate + } + } + }); + + if (me.icons) { + me.getApplication().getIconController().addIcons(me.icons); + } + if (me.features) { + me.getApplication().getFeaturesController().registerFeature(me.features, me); + } + }, + + /** + * @private + */ + getDrilldown: function() { + return Ext.ComponentQuery.query('#nx-drilldown')[0]; + }, + + /** + * @private + */ + getDrilldownItems: function() { + return Ext.ComponentQuery.query('nx-drilldown-item'); + }, + + /** + * @private + */ + getDrilldownDetails: function() { + return Ext.ComponentQuery.query('nx-drilldown-details')[0]; + }, + + /** + * @public + * Load all of the stores associated with this controller + */ + loadStores: function () { + var me = this; + if (this.getFeature()) { + Ext.each(this.stores, function(store){ + // + me.logDebug('Loading Drilldown store: ', store); + // + me.getStore(store).load(); + }); + } + }, + + /** + * @public + */ + reselect: function () { + var lists = Ext.ComponentQuery.query('nx-drilldown-master'); + + if (lists.length) { + this.navigateTo(NX.Bookmarks.getBookmark()); + } + }, + + /** + * @private + * When a list item is clicked, display the new view and update the bookmark + */ + onCellClick: function(view, td, cellIndex, model, tr, rowIndex, e) { + var index = Ext.ComponentQuery.query('nx-drilldown-master').indexOf(view.up('grid')); + + //if the cell target is a link, let it do it's thing + if(e && e.getTarget('a')) { + return false; + } + this.loadView(index + 1, true, model); + }, + + /** + * @public + * A model changed, focus on the new row and update the name of the related drilldown + */ + onModelChanged: function (index, model) { + var me = this, + lists = Ext.ComponentQuery.query('nx-drilldown-master'), + view, firstCell, firstCellImgs; + + // If the list hasn’t loaded, don't do anything + if (!lists[index]) { + return; + } + + view = lists[index].getView(); + firstCell = view.getCellByPosition({row:view.getRowId(model), column:0}); + firstCellImgs = firstCell ? firstCell.dom.getElementsByTagName('img') : null; + + lists[index].getSelectionModel().select([model], false, true); + me.setItemName(index + 1, me.getDescription(model)); + if (firstCellImgs && firstCellImgs.length) { + this.setItemClass(index + 1, firstCellImgs[0].className); + } + }, + + /** + * @public + * Make the detail view appear + * + * @param index The zero-based view to load + * @param animate Whether to animate the panel into view + * @param model An optional record to select + */ + loadView: function (index, animate, model) { + var me = this, + lists = Ext.ComponentQuery.query('nx-drilldown-master'); + + // Don’t load the view if the feature is not ready + if (!me.getFeature()) { + return; + } + + // Model specified, select it in the previous list + if (model && index > 0) { + lists[index - 1].fireEvent('selection', lists[index - 1], model); + me.onModelChanged(index - 1, model); + } + // Set all child bookmarks + for (var i = 0; i <= index; ++i) { + me.setItemBookmark(i, NX.Bookmarks.fromSegments(NX.Bookmarks.getBookmark().getSegments().slice(0, i + 1)), me); + } + + // Show the next view in line + me.showChild(index, animate); + me.bookmark(index, model); + }, + + /** + * @public + * Make the create wizard appear + * + * @param index The zero-based step in the create wizard + * @param animate Whether to animate the panel into view + * @param cmp An optional component to load + */ + loadCreateWizard: function (index, animate, cmp) { + var me = this; + + // Reset all non-root bookmarks + for (var i = 1; i <= index; ++i) { + me.setItemBookmark(i, null); + } + + // Show the specified step in the wizard + me.showCreateWizard(index, animate, cmp); + }, + + /** + * @private + * Bookmark specified model + */ + bookmark: function (index, model) { + var lists = Ext.ComponentQuery.query('nx-drilldown-master'), + bookmark = NX.Bookmarks.getBookmark().getSegments(), + segments = [], + i = 0; + + // Add the root element of the bookmark + segments.push(bookmark.shift()); + + // Find all parent models and add them to the bookmark array + while (i < lists.length && i < index - 1) { + segments.push(bookmark.shift()); + ++i; + } + + // Add the currently selected model to the bookmark array + if (model) { + segments.push(encodeURIComponent(model.getId())); + } + + // Set the bookmark + NX.Bookmarks.bookmark(NX.Bookmarks.fromSegments(segments), this); + }, + + /** + * Reselect on user navigation. + * + * @protected + */ + onNavigate: function () { + this.reselect(); + }, + + /** + * @public + * @param {NX.Bookmark} bookmark to navigate to + */ + navigateTo: function (bookmark) { + var me = this, + lists = Ext.ComponentQuery.query('nx-drilldown-master'), + list_ids = bookmark.getSegments().slice(1), + index, modelId, store; + + // Don’t navigate if the feature view hasn’t loaded + if (!me.getFeature || !me.getFeature()) { + return; + } + + if (lists.length && list_ids.length) { + // + me.logDebug('Navigate to: ' + bookmark.getSegments().join(':')); + // + + modelId = decodeURIComponent(list_ids.pop()); + index = list_ids.length; + store = lists[index].getStore(); + + if (store.isLoading()) { + // The store hasn’t yet loaded, load it when ready + me.mon(store, 'load', function() { + me.selectModelById(index, modelId); + me.mun(store, 'load'); + }); + } else { + me.selectModelById(index, modelId); + } + } else { + me.loadView(0, false); + } + }, + + /** + * @private + * @param index of the list which owns the model + * @param modelId to select + */ + selectModelById: function (index, modelId) { + var me = this, + lists = Ext.ComponentQuery.query('nx-drilldown-master'), + store, model; + + // If the list hasn’t loaded, don't do anything + if (!lists[index]) { + return; + } + + // If the store doesn't have any records in it, do nothing + store = lists[index].getStore(); + if (!store.getCount()) { + return; + } + + // getById() throws an error if a model ID is found, but not cached, check for content first + model = me.getById(store, modelId); + if (model === null) { + // check for integer model id + model = me.getById(store, parseInt(modelId)); + } + if (model === null) { + if (Ext.isFunction(me.findAndSelectModel)) { + me.findAndSelectModel(index, modelId); + } + return; + } + + me.selectModel(index, model); + }, + + /** + * Selects a raw in specified list or loads the model in settings panel. + * + * @protected + * @param index of the list which owns the model + * @param model to select + */ + selectModel: function (index, model) { + var me = this, + lists = Ext.ComponentQuery.query('nx-drilldown-master'); + + if (index + 1 !== me.currentIndex) { + me.loadView(index + 1, false, model); + } + else { + lists[index].fireEvent('selection', lists[index], model); + me.onModelChanged(index, model); + me.refreshBreadcrumb(); + } + }, + + /** + * Finds the model using other means that local store cache and selects the model if found. + * Should be overridden by subclasses, usually to call into server to get the model by id. + * + * @protected + * @param index of the list which owns the model + * @param modelId to find and select + */ + findAndSelectModel: function(index, modelId) { + var me = this, + lists = Ext.ComponentQuery.query('nx-drilldown-master'), + store = lists[index].getStore(), + modelType = store.model.modelName.replace(/^.*?model\./, '').replace(/\-.*$/, ''); + + NX.Messages.add({ + text: modelType + " (" + modelId + ") not found", + type: 'warning' + }); + }, + + /** + * @private + * Get a model from the specified store with the specified ID. Avoids exceptions + * that arise from using Ext.data.Store.getById() with buffered stores. + */ + getById: function (store, modelId) { + var index = store.findBy(function(record) { + return record.getId() === modelId; + }); + + if (index !== -1) { + return store.getAt(index); + } + + return null; + }, + + /** + * @private + */ + onDelete: function () { + var me = this, + selection = Ext.ComponentQuery.query('nx-drilldown-master')[0].getSelectionModel().getSelection(), + description; + + if (Ext.isDefined(selection) && selection.length > 0) { + description = me.getDescription(selection[0]); + NX.Dialogs.askConfirmation('Confirm deletion?', description, function () { + me.deleteModel(selection[0]); + + // Reset the bookmark + NX.Bookmarks.bookmark(NX.Bookmarks.fromToken(NX.Bookmarks.getBookmark().getSegment(0))); + }, {scope: me}); + } + }, + + /** + * @protected + * Enable 'New' when user has 'create' permission. + */ + bindNewButton: function (button) { + button.mon( + NX.Conditions.isPermitted(this.permission + ':create'), + { + satisfied: button.enable, + unsatisfied: button.disable, + scope: button + } + ); + }, + + /** + * @protected + * Enable 'Delete' when user has 'delete' permission. + */ + bindDeleteButton: function (button) { + button.mon( + NX.Conditions.isPermitted(this.permission + ':delete'), + { + satisfied: button.enable, + unsatisfied: button.disable, + scope: button + } + ); + }, + + // Constants which represent card indexes + BROWSE_INDEX: 0, + CREATE_INDEX: 1, + BLANK_INDEX: 2, + + /** + * @private + * Given N drilldown items, this panel should have a width of N times the current screen width + */ + syncSizeToOwner: function () { + var me = this, + drilldown = me.getDrilldown(), + owner = drilldown.ownerCt.body.el, + container = drilldown.down('container'); + + container.setSize(owner.getWidth() * container.items.length, owner.getHeight()); + me.slidePanels(me.currentIndex, false); + }, + + /** + * @public + * Shift this panel to display the referenced step in the create wizard + * + * @param index The index of the create wizard to display + * @param animate Set to “true†if the view should slide into place, “false†if it should just appear + * @param cmp An optional component to load into the panel + */ + showCreateWizard: function (index, animate, cmp) { + var me = this, + drilldown = me.getDrilldown(), + items = me.padItems(index), // Pad the drilldown + createContainer; + + // Add a component to the specified drilldown item (if specified) + if (cmp) { + createContainer = drilldown.down('#create' + index); + createContainer.removeAll(); + createContainer.add(cmp); + } + + // Show the proper card + items[index].setCardIndex(me.CREATE_INDEX); + + me.slidePanels(index, animate); + }, + + /** + * @public + * Shift this panel to display the referenced master or detail panel + * + * @param index The index of the master/detail panel to display + * @param animate Set to “true†if the view should slide into place, “false†if it should just appear + */ + showChild: function (index, animate) { + var me = this, + items = me.getDrilldownItems(), + item = items[index], + createContainer; + + // Show the proper card + item.setCardIndex(me.BROWSE_INDEX); + + // Destroy any create wizard panels + for (var i = 0; i < items.length; ++i) { + createContainer = items[i].down('#create' + i); + createContainer.removeAll(); + } + + me.slidePanels(index, animate); + }, + + /** + * @private + * Hide all except the specified panel. Focus on a default form field, if available. + * + * This is needed to restrict focus to the visible panel only. + */ + hideAllExceptAndFocus: function (index) { + var me = this, + items = me.getDrilldownItems(), + form; + + // Hide everything that’s not the specified panel + for (var i = 0; i < items.length; ++i) { + if (i != index) { + items[i].getLayout().setActiveItem(me.BLANK_INDEX); + } + } + + // Set focus on the default field (if available) or the panel itself + form = items[index].down('nx-addpanel[defaultFocus]'); + if (form) { + form.down('[name=' + form.defaultFocus + ']').focus(); + } else { + me.getDrilldown().focus(); + } + }, + + /** + * @private + * Slide the drilldown to reveal the specified panel + */ + slidePanels: function (index, animate) { + var me = this, + drilldown = me.getDrilldown(), + feature = drilldown.up('nx-feature-content'), + items = me.getDrilldownItems(), + item = items[index]; + + if (item && item.el) { + + // Restore the current card + me.currentIndex = index; + item.getLayout().setActiveItem(item.cardIndex); + + var left = feature.el.getX() - (index * feature.el.getWidth()); + if (animate) { + // Suspend layouts until the drilldown animation is complete + Ext.suspendLayouts(); + + drilldown.animate({ + easing: 'easeInOut', + duration: NX.State.getValue('animateDuration', 200), + to: { + x: left + }, + callback: function() { + // Update the breadcrumb + me.refreshBreadcrumb(); + + // Put focus on the panel we’re navigating to + me.hideAllExceptAndFocus(me.currentIndex); + + // Destroy any create wizard panels after current + for (var i = index + 1; i < items.length; ++i) { + items[i].down('#create' + i).removeAll(); + } + + // Resume layouts + Ext.resumeLayouts(true); + + // Resize the breadcrumb to fit the window + me.resizeBreadcrumb(); + } + }); + } else { + // Show the requested panel, without animation + drilldown.setX(left, false); + + // Update the breadcrumb + me.refreshBreadcrumb(); + me.resizeBreadcrumb(); + + // Put focus on the panel we’re navigating to + me.hideAllExceptAndFocus(index); + + // Destroy any create wizard panels after current + for (var i = index + 1; i < items.length; ++i) { + items[i].down('#create' + i).removeAll(); + } + } + } + }, + + /** + * @private + * Pad the number of items in this drilldown to the specified index + */ + padItems: function (index) { + var me = this, + drilldown = me.getDrilldown(), + items = me.getDrilldownItems(), + itemContainer; + + // Create new drilldown items (if needed) + if (index > items.length - 1) { + itemContainer = drilldown.down('container'); + + // Create empty panels if index > items.length + for (var i = items.length; i <= index; ++i) { + itemContainer.add(drilldown.createDrilldownItem(i, undefined, undefined)); + } + + // Resize the panel + me.syncSizeToOwner(); + } + + return me.getDrilldownItems(); + }, + + /** + * @private + * Update the breadcrumb based on the itemName and itemClass of drilldown items + */ + refreshBreadcrumb: function() { + var me = this, + content = me.getDrilldown().up('#feature-content'), + breadcrumb = content.down('#breadcrumb'), + items = me.getDrilldownItems(), + objs = []; + + if (me.currentIndex == 0) { + // Feature's home page, no breadcrumb required + content.showRoot(); + } else { + // Make a breadcrumb (including icon and 'home' link) + objs.push({ + xtype: 'button', + scale: 'large', + ui: 'nx-drilldown', + text: content.currentTitle, + handler: function() { + me.slidePanels(0, true); + + // Set the bookmark + var bookmark = items[0].itemBookmark; + if (bookmark) { + NX.Bookmarks.bookmark(bookmark.obj, bookmark.scope); + } + } + } + ); + + // Create the rest of the links + for (var i = 1; i <= me.currentIndex && i < items.length; ++i) { + // do no create breadcrumb for items that do not have a name + if (!items[i].itemName) { + return; + } + objs.push( + // Separator + { + xtype: 'label', + cls: 'nx-breadcrumb-separator', + text: '/' + }, + { + xtype: 'image', + height: 16, + width: 16, + cls: 'nx-breadcrumb-icon ' + items[i].itemClass + }, + + // Create a closure within a closure to decouple 'i' from the current context + (function(j) { + return { + xtype: 'button', + scale: 'medium', + ui: 'nx-drilldown', + // Disabled if it’s the last item in the breadcrumb + disabled: (i === me.currentIndex ? true : false), + text: items[j].itemName, + handler: function() { + var bookmark = items[j].itemBookmark; + if (bookmark) { + NX.Bookmarks.bookmark(bookmark.obj, bookmark.scope); + } + me.slidePanels(j, true); + } + }; + })(i) + ); + } + + breadcrumb.removeAll(); + breadcrumb.add(objs); + } + }, + + /* + * @private + * Resize the breadcrumb, truncate individual elements with ellipses as needed + */ + resizeBreadcrumb: function() { + var me = this, + padding = 60, // Prevent truncation from happening too late + parent = me.getDrilldown().ownerCt, + breadcrumb = me.getDrilldown().up('#feature-content').down('#breadcrumb'), + buttons, availableWidth, minimumWidth; + + // Is the breadcrumb clipped? + if (parent && breadcrumb.getWidth() + padding > parent.getWidth()) { + + // Yes. Take measurements and get a list of buttons sorted by length (longest first) + buttons = breadcrumb.query('button').splice(1); + availableWidth = parent.getWidth(); + + // What is the width of the breadcrumb, sans buttons? + minimumWidth = breadcrumb.getWidth() + padding; + for (var i = 0; i < buttons.length; ++i) { + minimumWidth -= buttons[i].getWidth(); + } + + // Reduce the size of the longest button, until all buttons fit in the specified width + me.reduceButtonWidth(buttons, availableWidth - minimumWidth); + } + }, + + /* + * @private + * Reduce the width of a set of buttons to fit a specified target width. The buttons are processed in + * ascending order by width until the first button with a width > the remaining target width / remaining buttons + * (i.e. equalPartsButtonWidth) is found. The equalPartsButtonWidth is then applied to all remaining buttons. + * + * @param buttons The list of buttons to resize + * @param targetWidth The desired resize width (sum of all buttons) + */ + reduceButtonWidth: function(buttons, targetWidth) { + var currentButtonWidth, + equalPartsButtonWidth, + adjustedButtonWidth, + totalButtons = buttons.length, + byWidthAscending = function(button, anotherButton) { + return button.getWidth() - anotherButton.getWidth(); + }; + + buttons.sort(byWidthAscending).forEach(function(button, buttonIndex) { + if (adjustedButtonWidth) { + button.setWidth(adjustedButtonWidth); + return; + } + + equalPartsButtonWidth = Math.floor(targetWidth / (totalButtons - buttonIndex)); + currentButtonWidth = button.getWidth(); + targetWidth -= currentButtonWidth; + + if (equalPartsButtonWidth < currentButtonWidth) { + adjustedButtonWidth = equalPartsButtonWidth; + button.setWidth(adjustedButtonWidth); + } + }); + }, + + /** + * @public + * Set the name of the referenced drilldown item + */ + setItemName: function (index, text) { + var me = this, + items = me.padItems(index); + + items[index].setItemName(text); + }, + + /** + * @public + * Set the icon class of the referenced drilldown item + */ + setItemClass: function (index, cls) { + var me = this, + items = me.padItems(index); + + items[index].setItemClass(cls); + }, + + /** + * @public + * Set the bookmark of the breadcrumb segment associated with the referenced drilldown item + */ + setItemBookmark: function (index, bookmark, scope) { + var me = this, + items = me.padItems(index); + + items[index].setItemBookmark(bookmark, scope); + }, + + /** + * @public + */ + showInfo: function (message) { + this.getDrilldownDetails().showInfo(message); + }, + + /** + * @public + */ + clearInfo: function () { + this.getDrilldownDetails().clearInfo(); + }, + + /** + * @public + */ + showWarning: function (message) { + this.getDrilldownDetails().showWarning(message); + }, + + /** + * @public + */ + clearWarning: function () { + this.getDrilldownDetails().clearWarning(); + }, + + /** + * Add a tab to the default detail panel + * + * Note: this will have no effect if a custom detail panel has been specified + */ + addTab: function (tab) { + var me = this; + if (!me.detail) { + me.getDrilldownDetails().addTab(tab); + } + }, + + /** + * Remove a panel from the default detail panel + * + * Note: this will have no effect if a custom detail panel has been specified + */ + removeTab: function (tab) { + var me = this; + if (!me.detail) { + me.getDrilldownDetails().removeTab(tab); + } + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/ExtDirect.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/ExtDirect.js new file mode 100644 index 0000000000000000000000000000000000000000..2e3deeb758d5773bb8ed5d33d63d69e6b61202e0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/ExtDirect.js @@ -0,0 +1,96 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Watches over Ext.Direct communication. + * + * @since 3.0 + */ +Ext.define('NX.controller.ExtDirect', { + extend: 'NX.app.Controller', + requires: [ + 'NX.Security', + 'NX.Messages', + 'NX.I18n' + ], + + /** + * @override + */ + init: function() { + var me = this; + + me.listen({ + direct: { + '*': { + beforecallback: me.checkResponse + } + } + }); + }, + + /** + * Checks Ext.Direct response and automatically show warning messages if an error occurred. + * If response specifies that authentication is required, will show the sign-in window. + * + * @private + */ + checkResponse: function(provider, transaction, options) { + var result = transaction.result, + message; + + // FIXME: Anything that does logging here can cause Ext.Direct log event remoting to spin out of control + + if (options && options.callbackOptions && options.callbackOptions.skipResultCheck) { + return; + } + + if (Ext.isDefined(result)) { + if (Ext.isDefined(result.success) && result.success === false) { + + if (Ext.isDefined(result.authenticationRequired) && result.authenticationRequired === true) { + message = result.message; + NX.Security.askToAuthenticate(); + } + else if (Ext.isDefined(result.message)) { + message = result.message; + } + else if (Ext.isDefined(result.messages)) { + message = Ext.Array.from(result.messages).join('
'); + } + } + + if (Ext.isDefined(transaction.serverException)) { + message = transaction.serverException.exception.message; + } + } + else { + message = NX.I18n.get('User_ConnectFailure_Message'); + } + + if (message) { + NX.Messages.add({text: message, type: 'warning'}); + } + + // HACK: disabled for now as this causes problems remoting LogEvents + //// + //var logMsg = transaction.action + ':' + transaction.method + " -> " + (message ? 'Failed: ' + message : 'OK'); + //if (Ext.isDefined(result) && result.errors) { + // logMsg += (' Errors: ' + Ext.encode(result.errors)); + //} + //this.logDebug(logMsg); + //// + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Features.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Features.js new file mode 100644 index 0000000000000000000000000000000000000000..8bf8973e1989702e73d1c4b77978f40753b9a6c7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Features.js @@ -0,0 +1,181 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Features registration controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.Features', { + extend: 'NX.app.Controller', + + models: [ + 'Feature' + ], + stores: [ + 'Feature', + 'FeatureMenu' + ], + + statics: { + /** + * Always returns true. + * + * @returns {boolean} + */ + alwaysVisible: function () { + return true; + }, + + /** + * Always returns false. + * + * @returns {boolean} + */ + alwaysHidden: function () { + return false; + } + }, + + /** + * Registers features. + * + * @param {Array/Object} features to be registered + * @param {Ext.util.Observable} [owner] to be watched to automatically unregister the features if owner is destroyed + */ + registerFeature: function (features, owner) { + var me = this; + + if (features) { + if (owner) { + owner.on('destroy', Ext.pass(me.unregisterFeature, [features], me), me); + } + Ext.each(Ext.Array.from(features), function (feature) { + var clonedFeature = Ext.clone(feature), + path; + + if (!clonedFeature.path) { + throw Ext.Error.raise('Feature missing path'); + } + + if (!clonedFeature.mode) { + clonedFeature.mode = 'admin'; + } + + if (!clonedFeature.view && clonedFeature.group === true) { + clonedFeature.view = 'NX.view.feature.Group'; + } + + // complain if there is no view configuration + if (!clonedFeature.view) { + me.logError('Missing view configuration for feature at path:', clonedFeature.path); + } + + path = clonedFeature.path; + if (path.charAt(0) === '/') { + path = path.substr(1, path.length); + } + + me.configureIcon(path, clonedFeature); + + path = clonedFeature.mode + '/' + path; + clonedFeature.path = '/' + path; + + // auto-set bookmark + if (!clonedFeature.bookmark) { + clonedFeature.bookmark = NX.Bookmarks.encode(path).toLowerCase(); + } + + // generate default context-help keyword + if (!clonedFeature.helpKeyword) { + clonedFeature.helpKeyword = path.replace(/\//g, ' ').toLowerCase(); + } + + if (Ext.isDefined(clonedFeature.visible)) { + if (!Ext.isFunction(clonedFeature.visible)) { + if (clonedFeature.visible) { + clonedFeature.visible = NX.controller.Features.alwaysVisible; + } + else { + clonedFeature.visible = NX.controller.Features.alwaysHidden; + } + } + } + else { + clonedFeature.visible = NX.controller.Features.alwaysVisible; + } + + me.getStore('Feature').addSorted(me.getFeatureModel().create(clonedFeature)); + }); + } + }, + + /** + * Un-registers features. + * + * @param {Object[]/Object} features to be unregistered + */ + unregisterFeature: function (features) { + var me = this; + + if (features) { + Ext.each(Ext.Array.from(features), function (feature) { + var clonedFeature = Ext.clone(feature), + path, model; + + if (!clonedFeature.mode) { + clonedFeature.mode = 'admin'; + } + path = clonedFeature.path; + if (path.charAt(0) === '/') { + path = path.substr(1, path.length); + } + path = clonedFeature.mode + '/' + path; + clonedFeature.path = '/' + path; + + model = me.getStore('Feature').getById(clonedFeature.path); + if (model) { + me.getStore('Feature').remove(model); + } + }); + } + }, + + /** + * @private + * @param feature + */ + configureIcon: function (path, feature) { + var defaultIconName = 'feature-' + feature.mode + '-' + path.toLowerCase().replace(/\//g, '-').replace(/\s/g, ''); + + // inline icon registration for feature + if (feature.iconConfig) { + var icon = feature.iconConfig; + delete feature.iconConfig; + if (icon.name) { + feature.iconName = icon.name; + } + else { + icon.name = defaultIconName; + } + this.getApplication().getIconController().addIcon(icon); + } + + // default icon name if not set + if (!feature.iconName) { + feature.iconName = defaultIconName; + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Help.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Help.js new file mode 100644 index 0000000000000000000000000000000000000000..09354e8db054791006f5dd756c6548d817b83f00 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Help.js @@ -0,0 +1,203 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Help controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.Help', { + extend: 'NX.app.Controller', + requires: [ + 'NX.Icons', + 'NX.I18n', + 'NX.Windows' + ], + + views: [ + 'header.Help', + 'AboutWindow' + ], + + refs: [ + { + ref: 'featureHelp', + selector: 'nx-header-help menuitem[action=feature]' + } + ], + + /** + * The base-url for help links. + * + * @private + * @property {String} + * @readonly + */ + baseUrl: 'http://links.sonatype.com/products/nexus', + + /** + * @private + * @property {NX.model.Feature} + */ + selectedFeature: undefined, + + /** + * @override + */ + init: function () { + var me = this; + + me.getApplication().getIconController().addIcons({ + 'help-support': { + file: 'support.png', + variants: ['x16', 'x32'] + }, + 'help-issues': { + file: 'bug.png', + variants: ['x16', 'x32'] + }, + 'help-manual': { + file: 'book_picture.png', + variants: ['x16', 'x32'] + }, + 'help-community': { + file: 'users_4.png', + variants: ['x16', 'x32'] + }, + 'help-kb': { + file: 'brain_trainer.png', + variants: ['x16', 'x32'] + } + }); + + me.listen({ + controller: { + '#Menu': { + featureselected: me.onFeatureSelected + } + }, + component: { + 'nx-header-help menuitem[action=feature]': { + click: me.onFeatureHelp + }, + 'nx-header-help menuitem[action=about]': { + click: me.onAbout + }, + 'nx-header-help menuitem[action=docs]': { + click: me.onDocs + }, + 'nx-header-help menuitem[action=support]': { + click: me.onSupport + }, + 'nx-header-help menuitem[action=issues]': { + click: me.onIssues + }, + 'nx-header-help menuitem[action=community]': { + click: me.onCommunity + }, + 'nx-header-help menuitem[action=kb]': { + click: me.onKnowledgeBase + } + } + }); + }, + + /** + * Update help menu content. + * + * @private + * @param {NX.model.Feature} feature selected feature + */ + onFeatureSelected: function (feature) { + var me = this, + text = feature.get('text'), + iconName = feature.get('iconName'), + featureHelp = me.getFeatureHelp(); + + me.selectedFeature = feature; + + featureHelp.setText(NX.I18n.get('Help_Feature_Text') + text); + featureHelp.setIconCls(NX.Icons.cls(iconName, 'x16')); + }, + + /** + * @private + * @param {String} section + */ + openUrl: function(section) { + NX.Windows.open(this.baseUrl + '/' + section); + }, + + /** + * Create a help url for the given keyword. + * @param keyword + * @returns {string} + */ + createUrl: function(keyword) { + return this.baseUrl + '/docs-search/' + NX.State.getVersionMajorMinor() + '/' + keyword; + }, + + /** + * @private + */ + onFeatureHelp: function() { + var me = this, + keyword = me.selectedFeature.get('helpKeyword'), + url = me.createUrl(keyword); + + NX.Windows.open(url); + }, + + /** + * @private + */ + onAbout: function() { + Ext.widget('nx-aboutwindow'); + }, + + /** + * @private + */ + onDocs: function() { + NX.Windows.open(this.baseUrl + '/docs/' + NX.State.getVersionMajorMinor()); + }, + + /** + * @private + */ + onSupport: function() { + this.openUrl('support'); + }, + + /** + * @private + */ + onIssues: function() { + this.openUrl('issues'); + }, + + /** + * @private + */ + onCommunity: function() { + this.openUrl('community'); + }, + + /** + * @private + */ + onKnowledgeBase: function() { + this.openUrl('kb'); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Icon.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Icon.js new file mode 100644 index 0000000000000000000000000000000000000000..faa728b4d3e0a9a9c94091e3cc05b13576fda9ec --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Icon.js @@ -0,0 +1,237 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX, Image*/ + +/** + * Main uber mode controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.Icon', { + extend: 'NX.app.Controller', + requires: [ + 'Ext.Error', + 'Ext.util.CSS', + 'NX.util.Url', + 'NX.Icons' + ], + + models: [ + 'Icon' + ], + + stores: [ + 'Icon' + ], + + /** + * @private + */ + stylesheet: undefined, + + /** + * @override + */ + onLaunch: function() { + var me = this; + + // install stylesheet after all other controllers have had a chance to init & add icons. + me.installStylesheet(); + + // HACK: preload some additional image resources + me.preloadImage(NX.util.Url.cacheBustingUrl(NX.util.Url.baseUrl + '/static/rapture/resources/images/shared/icon-error.png')); + me.preloadImage(NX.util.Url.cacheBustingUrl(NX.util.Url.baseUrl + '/static/rapture/resources/images/shared/icon-info.png')); + me.preloadImage(NX.util.Url.cacheBustingUrl(NX.util.Url.baseUrl + '/static/rapture/resources/images/shared/icon-question.png')); + me.preloadImage(NX.util.Url.cacheBustingUrl(NX.util.Url.baseUrl + '/static/rapture/resources/images/shared/icon-warning.png')); + }, + + /** + * @private + * @param {String} url + */ + preloadImage: function(url) { + var img; + + // + this.logTrace('Preloading:', url); + // + + img = new Image(); + img.src = url; + }, + + /** + * Generate and install stylesheet for icons when the applications is launching. + * + * @private + */ + installStylesheet: function () { + var me = this, + styles = []; + + // + me.logDebug('Installing stylesheet'); + // + + // build styles for each icon in store + me.getStore('Icon').each(function (record) { + var img, style = me.buildIconStyle(record.data); + //me.logDebug('Adding style: ' + style); + styles.push(style); + + // Optionally pre-load icon + if (record.data.preload) { + me.preloadImage(record.data.url); + } + }); + + // create the style sheet + me.stylesheet = Ext.util.CSS.createStyleSheet(styles.join(' '), 'nx-icons'); + + // + me.logDebug('Stylesheet installed with', me.stylesheet.cssRules.length, 'rules'); + // + }, + + /** + * Build style for given icon. + * + * @private + */ + buildIconStyle: function (icon) { + var style; + + style = '.' + icon.cls + ' {'; + style += 'background: url(' + icon.url + ') no-repeat center center !important;'; + style += 'height: ' + icon.height + 'px;'; + style += 'width: ' + icon.width + 'px;'; + // needed to get iconCls lined up in trees when height/width is set + style += 'vertical-align: middle;'; + style += '}'; + + return style; + }, + + /** + * Add new icons. + * + * @public + * @param icons Array or object. + */ + addIcons: function (icons) { + var me = this; + if (Ext.isArray(icons)) { + Ext.Array.each(icons, function (icon) { + me.addIcon(icon); + }); + } + else if (Ext.isObject(icons)) { + Ext.Object.each(icons, function (key, value) { + var copy = Ext.clone(value); + copy.name = key; + me.addIcon(copy); + }); + } + else { + Ext.Error.raise('Expected array or object, found: ' + icons); + } + }, + + /** + * Add a new icon. + * + * @public + */ + addIcon: function (icon) { + var me = this; + + // If icon contains 'variants' field then create an icon for each variant + if (Ext.isArray(icon.variants)) { + var copy = Ext.clone(icon); + delete copy.variants; + Ext.each(icon.variants, function (variant) { + copy.variant = variant; + me.addIcon(copy); + }); + return; + } + + me.configureIcon(icon); + + // complain if height/width are missing as this could cause the image not to display + if (!icon.height) { + me.logWarn('Icon missing height:', icon.css); + } + if (!icon.width) { + me.logWarn('Icon missing width:', icon.css); + } + + // TODO: complain if we are overwriting an icon + + me.getStore('Icon').add(icon); + }, + + /** + * Apply basic icon configuration. + * + * @private + */ + configureIcon: function (icon) { + var variant = icon.variant; + + // automatically apply 'x' + if (Ext.isString(variant)) { + if (variant.charAt(0) === 'x' && variant.length > 1) { + var size = Ext.Number.from(variant.substring(1), -1); + if (size === -1) { + throw Ext.Error.raise('Invalid variant format: ' + variant); + } + icon.height = icon.width = size; + } + } + + icon.url = NX.Icons.url2(icon.file, icon.variant); + icon.cls = NX.Icons.cls(icon.name, icon.variant); + }, + + /** + * Find an icon by name with optional variant. + * + * @public + */ + findIcon: function (name, variant) { + var store = this.getStore('Icon'), + recordId; + + recordId = store.findBy(function (record, id) { + // find matching icon name + if (name === record.get('name')) { + // if icon has a variant match that too + if (variant) { + if (variant === record.get('variant')) { + // match + return true; + } + } + } + + // no match + return false; + }); + + if (recordId === -1) { + return null; + } + return store.getAt(recordId); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/KeyNav.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/KeyNav.js new file mode 100644 index 0000000000000000000000000000000000000000..69abb5c988680a8b4a6c45f0c2139de852d5cfb0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/KeyNav.js @@ -0,0 +1,121 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * KeyNav controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.KeyNav', { + extend: 'NX.app.Controller', + requires: [ + 'Ext.util.KeyNav' + ], + + /** + * @override + */ + init: function () { + var me = this; + + me.listen({ + component: { + 'form button[bindToEnter=true]': { + afterrender: me.installEnterKey + } + } + }); + + me.disableBackspaceNav(); + }, + + /** + * Install a key nav that will trigger click on any form buttons marked with "bindToEnter: true", + * (usually submit button) on ENTER. + * + * @private + */ + installEnterKey: function (button) { + var form = button.up('form'); + + button.keyNav = Ext.create('Ext.util.KeyNav', form.el, { + enter: function () { + if (!button.isDisabled()) { + button.fireEvent('click', button); + } + } + }); + }, + + /** + * Disable backspace as a means for navigating back. Allow backspace when an enabled + * input field has focus. + * + * @private + */ + disableBackspaceNav: function() { + var parent = Ext.isIE ? document : window; + Ext.EventManager.on(parent, 'keydown', function (e, focused) { + // Check for least-likely to most-likely conditions in order to avoid costly evaluations - fail fast to increase performance + var isBackspace = e.getKey() === e.BACKSPACE; + if ( isBackspace && !isBackspaceAllowed() ) { + e.stopEvent(); + } + + /** + * Returns true if a backspace should be allowed. + * + * @inner + * @private + * @returns {boolean} + */ + function isBackspaceAllowed() { + // isEditable is false if focused.readOnly is undefined; this traps the case where no field has focus, + // and the short-circuit avoids costly field checking + var isEditable = (focused.readOnly !== undefined && !focused.readOnly), + isEnabled = !focused.disabled, + isTypingAllowed = isEditable && isEnabled; + + return isTypingAllowed && isFieldAllowed(); + } + + /** + * Returns true if the field should allow backspaces. + * + * ExtJS use the role attribute to map to multiple UI field types: + * textbox ==> normal text field (`input[type=text]`); multi-line text area (`textarea`) + * spinbutton ==> normal text field ('input[type=text]`) with accompanying up/down arrows for selecting numeric values + * combobox ==> text field that allows typing in addition to list selection (`input[type=text]`); + * text field that only allows list selection (`input[type=text,readOnly=readOnly]`) + * + * Field types disallowed by exclusion: + * checkbox ==> rendered by ExtJS as a button with accompanying label (`input[type=button]`) + * + * @inner + * @private + * @returns {boolean} + */ + function isFieldAllowed() { + var roleAttribute = focused.attributes["role"], + role = roleAttribute && roleAttribute.value, + rolesAllowed = ['textbox', 'spinbutton', 'combobox'], + rePattern = '^' + rolesAllowed.join('|') + '$', + re = new RegExp(rePattern, 'i'); + + return re.test(role); + } + }); + } + +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Logging.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Logging.js new file mode 100644 index 0000000000000000000000000000000000000000..0e20e6cf271425cc4c3a46e9a7fc2a06ab50d750 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Logging.js @@ -0,0 +1,188 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Logging controller. + * + * @since 3.0 + * @see NX.util.log.Sink + */ +Ext.define('NX.controller.Logging', { + extend: 'NX.app.Controller', + requires: [ + 'NX.Log', + 'NX.util.log.StoreSink', + 'NX.util.log.ConsoleSink', + 'NX.util.log.RemoteSink' + ], + mixins: { + stateful: 'Ext.state.Stateful' + }, + + stores: [ + 'LogEvent' + ], + + /** + * Map of named sinks. + * + * @private + * @property {Object} + * @readonly + */ + sinks: {}, + + /** + * Array of configured sinks. + * + * Mirrors {@link #sinks} values, but in array form for faster evaluation. + * + * @private + * @property {NX.util.log.Sink[]} + * @readonly + */ + sinkRefs: undefined, + + /** + * Logging threshold. + * + * @private + * @property {String} + */ + threshold: 'debug', + + /** + * @constructor + */ + constructor: function () { + this.mixins.stateful.constructor.call(this, { + stateful: true, + stateId: this.self.getName() + }); + + this.callParent(arguments); + this.initState(); + }, + + /** + * @override + */ + init: function () { + this.sinks = { + store: Ext.create('NX.util.log.StoreSink', this.getStore('LogEvent')), + console: Ext.create('NX.util.log.ConsoleSink'), + remote: Ext.create('NX.util.log.RemoteSink') + }; + // build array of all sink objects for faster evaluation + this.sinkRefs = Ext.Object.getValues(this.sinks); + }, + + /** + * Attach to {@link NX.Log} helper. + * + * @override + */ + onLaunch: function () { + NX.Log.attach(this); + this.logInfo('Attached'); + }, + + /** + * @override + * @return {Object} + */ + getState: function() { + return { + threshold: this.threshold + }; + }, + + /** + * Returns sink by name, or undefined. + * + * @public + * @param {String} name + */ + getSink: function(name) { + return this.sinks[name]; + }, + + /** + * Get the logging threshold. + * + * @public + * @returns {String} + */ + getThreshold: function () { + return this.threshold; + }, + + /** + * Set the logging threshold. + * + * @public + * @param {String} threshold + */ + setThreshold: function (threshold) { + this.threshold = threshold; + this.saveState(); + }, + + /** + * Mapping of {@link NX.model.LogLevel} weights. + * + * @private + * @property {Object} + */ + levelWeights: { + all: 1, + trace: 2, + debug: 3, + info: 4, + warn: 5, + error: 6, + off: 7 + }, + + /** + * Check if given level exceeds configured threshold. + * + * @private + * @param {String} level + * @return {Boolean} + */ + exceedsThreshold: function (level) { + return this.levelWeights[level] >= this.levelWeights[this.threshold]; + }, + + /** + * Record a log-event. + * + * @public + * @param event + */ + recordEvent: function (event) { + // ignore events that do not exceed threshold + if (!this.exceedsThreshold(event.level)) { + return; + } + + // pass events to all enabled sinks + for (var i=0; i + me.logDebug('Showing main view'); + // + + viewport.add({ xtype: 'nx-main' }); + } + }, + + /** + * Removes {@link NX.view.Main} view from {@link Ext.container.Viewport}. + * + * @override + */ + onDestroy: function () { + var me = this, + viewport = me.getViewport(); + + if (viewport) { + // + me.logDebug('Removing main view'); + // + + viewport.remove(me.getMain()); + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Menu.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Menu.js new file mode 100644 index 0000000000000000000000000000000000000000..b7bf482da073691e0c44c1ab8de7adc18bd6203d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Menu.js @@ -0,0 +1,881 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Menu controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.Menu', { + extend: 'NX.app.Controller', + requires: [ + 'NX.Bookmarks', + 'NX.controller.User', + 'NX.controller.Features', + 'NX.Permissions', + 'NX.Security', + 'NX.State', + 'NX.view.header.Mode', + 'NX.I18n' + ], + + views: [ + 'feature.Menu', + 'feature.NotFound', + 'feature.NotVisible', + 'UnsavedChanges' + ], + + models: [ + 'Feature' + ], + stores: [ + 'Feature', + 'FeatureMenu', + 'FeatureGroup' + ], + + refs: [ + { + ref: 'featureMenu', + selector: 'nx-feature-menu' + }, + { + ref: 'featureContent', + selector: 'nx-feature-content' + }, + { + ref: 'headerPanel', + selector: 'nx-header-panel' + } + ], + + /** + * Currently selected mode name. + * + * @private + * @type {String} + */ + mode: undefined, + + /** + * All available {@link NX.view.header.Mode modes}. + * + * @private + * @type {Ext.util.MixedCollection} + */ + availableModes: undefined, + + /** + * @private + * @type {Boolean} + */ + bookmarkingEnabled: true, + + /** + * Current selected path. + * + * @private {String} + */ + currentSelectedPath: undefined, + + /** + * True if menu should auto navigate to first available feature. + * + * @private {Boolean} + */ + navigateToFirstFeature: false, + + /** + * @override + */ + init: function () { + var me = this; + + // initialize privates + me.availableModes = Ext.create('Ext.util.MixedCollection'); + + me.getApplication().getIconController().addIcons({ + 'feature-notfound': { + file: 'exclamation.png', + variants: ['x16', 'x32'] + } + }); + + me.listen({ + controller: { + '#Permissions': { + changed: me.refreshMenu + }, + '#State': { + changed: me.onStateChange + }, + '#Bookmarking': { + navigate: me.navigateTo + }, + '#User': { + beforesignout: me.warnBeforeSignOut, + signout: me.onSignOut + }, + '#Refresh': { + beforerefresh: me.warnBeforeRefresh + } + }, + component: { + 'nx-feature-menu': { + select: me.onSelection, + itemclick: me.onItemClick, + afterrender: me.onAfterRender, + beforecellclick: me.warnBeforeMenuSelect + }, + 'nx-main #quicksearch': { + beforesearch: me.warnBeforeSearch + }, + '#breadcrumb button': { + click: me.warnBeforeButtonClick + }, + 'nx-actions button[handler]': { + click: me.warnBeforeButtonClick + }, + 'nx-actions menuitem[handler]': { + click: me.warnBeforeButtonClick + }, + 'nx-header-mode': { + afterrender: me.registerMode, + destroy: me.unregisterMode, + selected: me.warnBeforeModeSelect + } + }, + store: { + '#Feature': { + update: me.refreshMenu + } + } + }); + + me.addEvents( + /** + * Fires when a feature is selected. + * + * @event featureselected + * @param {NX.model.Feature} selected feature + */ + 'featureselected' + ); + + // Warn people about refreshing or closing their browser when there are unsaved changes + me.warnBeforeUnload(); + }, + + /** + * Unregister Application listener. + * + * @override + */ + destroy: function() { + var me = this; + + me.getApplication().un('controllerschanged', me.refreshMenu, me); + + me.callParent(arguments); + }, + + /** + * Register as Application listener and rebuild menu. + */ + onAfterRender: function () { + var me = this; + + me.getApplication().on('controllerschanged', me.refreshMenu, me); + me.refreshMenu(); + }, + + /** + * @public + * @returns {NX.Bookmark} a bookmark for current selected feature (if any) + */ + getBookmark: function () { + var me = this, + selection = me.getFeatureMenu().getSelectionModel().getSelection(); + + return NX.Bookmarks.fromToken(selection.length ? selection[0].get('bookmark') : me.mode); + }, + + /** + * Select a feature when the associated menu item is clicked + * + * @private + */ + onItemClick: function (panel, featureMenuModel) { + this.selectMenuItem(featureMenuModel, true); + }, + + /** + * Select a feature when the associated menu item is selected. This differs + * from onItemClick in that an already selected feature will not be reselected. + * + * @private + */ + onSelection: function (panel, featureMenuModel) { + this.selectMenuItem(featureMenuModel, false); + }, + + /** + * (Re)select a feature + * + * @private + */ + selectMenuItem: function (featureMenuModel, reselect) { + var me = this, + path = featureMenuModel.get('path'); + + if (reselect || path !== me.currentSelectedPath || featureMenuModel.get('group')) { + me.currentSelectedPath = path; + + // + me.logInfo('Selected feature:', path); + // + + if (me.bookmarkingEnabled) { + me.bookmark(featureMenuModel); + } + me.selectFeature(me.getStore('Feature').getById(featureMenuModel.get('path'))); + me.populateFeatureGroupStore(featureMenuModel); + } + }, + + /** + * @private + */ + selectFeature: function (featureModel) { + var path; + + if (featureModel) { + path = featureModel.get('path'); + if (path && path.length > 0) { + this.fireEvent('featureselected', featureModel); + } + } + }, + + /** + * Updates the {@link NX.store.FeatureGroup} store with children of selected feature. + * + * @private + * @param {NX.model.FeatureMenu} record + */ + populateFeatureGroupStore: function (record) { + var me = this, + features = [], + featureStore = me.getStore('Feature'); + + // add all children of the record to the group store, but do not include the node for the current record + record.eachChild(function (node) { + node.cascadeBy(function (child) { + features.push(featureStore.getById(child.get('path'))); + }); + }); + + me.getStore('FeatureGroup').loadData(features); + }, + + /** + * @private + */ + navigateTo: function (bookmark) { + var me = this, + node, mode, feature, menuBookmark, queryIndex; + + if (bookmark) { + // Get the path (minus an optional filter string) + if (bookmark.getSegments().length) { + queryIndex = bookmark.getSegment(0).indexOf('='); + if (queryIndex !== -1) { + menuBookmark = bookmark.getSegment(0).slice(0, bookmark.getSegment(0).indexOf('=')); + } + else { + menuBookmark = bookmark.getSegment(0); + } + } + + // + me.logInfo('Navigate to:', menuBookmark); + // + + mode = me.getMode(bookmark); + // if we are navigating to a new mode, sync it + if (me.mode !== mode) { + me.mode = mode; + me.refreshModes(); + } + if (menuBookmark) { + node = me.getStore('FeatureMenu').getRootNode().findChild('bookmark', menuBookmark, true); + } + // in case that we do not have a bookmark to navigate to or we have to navigate to first feature, + // find the first feature + if (!node && (!Ext.isDefined(menuBookmark) || me.navigateToFirstFeature)) { + if (!me.mode) { + me.selectFirstAvailableMode(); + me.refreshModes(); + } + node = me.getStore('FeatureMenu').getRootNode().firstChild; + + // + me.logDebug('Automatically selected:', node.get('bookmark')); + // + } + // select the bookmarked feature in menu, if available + if (node) { + me.bookmarkingEnabled = me.navigateToFirstFeature; + me.navigateToFirstFeature = false; + me.getFeatureMenu().selectPath(node.getPath('text'), 'text', undefined, function () { + me.bookmarkingEnabled = true; + }); + } + else { + delete me.currentSelectedPath; + // if the feature to navigate to is not available in menu check out if is hidden (probably no permissions) + if (menuBookmark) { + feature = me.getStore('Feature').findRecord('bookmark', menuBookmark, 0, false, false, true); + } + me.getFeatureMenu().getSelectionModel().deselectAll(); + if (feature) { + if (feature.get('authenticationRequired') && NX.Permissions.available()) { + // + me.logDebug('Asking user to authenticate as feature exists but is not visible'); + // + + NX.Security.askToAuthenticate(); + } + me.selectFeature(me.createNotAvailableFeature(feature)); + } + else { + // as feature does not exist at all, show teh 403 like content + me.selectFeature(me.createNotFoundFeature(menuBookmark)); + } + } + } + }, + + /** + * @private + */ + onSignOut: function () { + this.navigateToFirstFeature = true; + }, + + /** + * On a state change check features visibility and trigger a menu refresh if necessary. + * + * @private + */ + onStateChange: function () { + var me = this, + shouldRefresh = false; + + me.getStore('Feature').each(function (feature) { + var visible, previousVisible; + if (feature.get('mode') === me.mode) { + visible = feature.get('visible')(); + previousVisible = me.getStore('FeatureMenu').getRootNode().findChild('path', feature.get('path'), true) !== null; + shouldRefresh = (visible !== previousVisible); + } + return !shouldRefresh; + }); + + if (shouldRefresh) { + me.refreshMenu(); + } + }, + + /** + * @private + */ + bookmark: function (node) { + var me = this, + bookmark = node.get('bookmark'); + + if (NX.Bookmarks.getBookmark().getToken() !== bookmark) { + NX.Bookmarks.bookmark(NX.Bookmarks.fromToken(bookmark), me); + } + }, + + /** + * Refresh modes and feature menu. + * + * @public + */ + refreshMenu: function () { + var me = this; + + // + me.logDebug('Refreshing menu; mode:', me.mode); + // + + me.refreshVisibleModes(); + me.refreshTree(); + me.navigateTo(NX.Bookmarks.getBookmark()); + }, + + /** + * Find mode switcher widget for given name. + * + * @private + * @param {String} name + * @returns {NX.view.header.Mode|undefined} + */ + findModeSwitcher: function(name) { + return this.availableModes.findBy(function(item) { + return item.name === name; + }); + }, + + /** + * Refreshes modes buttons based on the fact that there are features visible for that mode or not. + * In case that current mode is no longer visible, auto selects a new one. + * + * @private + */ + refreshVisibleModes: function () { + var me = this, + visibleModes = [], + feature; + + me.getStore('Feature').each(function (rec) { + feature = rec.getData(); + if (feature.visible() && !feature.group && visibleModes.indexOf(feature.mode) === -1) { + visibleModes.push(feature.mode); + } + }); + + // + me.logDebug('Visible modes:', visibleModes); + // + + me.availableModes.each(function (mode) { + mode.toggle(false, true); + if (mode.autoHide) { + if (visibleModes.indexOf(mode.name) > -1) { + mode.show(); + } + else { + mode.hide(); + } + } + }); + + me.refreshModeButtons(); + }, + + /** + * @private + */ + refreshModeButtons: function () { + var me = this, + mode; + + me.availableModes.each(function (mode) { + mode.toggle(false, true); + }); + + if (me.mode) { + mode = me.findModeSwitcher(me.mode); + if (!mode || mode.isHidden()) { + delete me.mode; + } + } + if (me.mode) { + mode = me.findModeSwitcher(me.mode); + mode.toggle(true, true); + } + }, + + // NOTE: refreshTree() is only used externally by coreui.controller.Search + + /** + * @public + */ + refreshTree: function () { + var me = this, + menuTitle = me.mode, + groupsToRemove = [], + feature, segments, parent, child, mode; + + // + me.logDebug('Refreshing tree; mode:', me.mode); + // + + Ext.suspendLayouts(); + + mode = me.findModeSwitcher(me.mode); + if (mode && mode.title) { + menuTitle = mode.title; + } + me.getFeatureMenu().setTitle(menuTitle); + + me.getStore('FeatureMenu').getRootNode().removeAll(); + + // create leafs and all parent groups of those leafs + me.getStore('Feature').each(function (rec) { + feature = rec.getData(); + // iterate only visible features + if ((me.mode === feature.mode) && feature.visible()) { + segments = feature.path.split('/'); + parent = me.getStore('FeatureMenu').getRootNode(); + for (var i = 2; i < segments.length; i++) { + child = parent.findChild('path', segments.slice(0, i + 1).join('/'), false); + if (child) { + if (i < segments.length - 1) { + child.data = Ext.apply(child.data, { + leaf: false + }); + } + } + else { + if (i < segments.length - 1) { + // create the group + child = parent.appendChild({ + text: segments[i], + leaf: false, + // expand the menu by default + expanded: true + }); + } + else { + // create the leaf + child = parent.appendChild(Ext.apply(feature, { + leaf: true, + iconCls: NX.Icons.cls(feature.iconName, 'x16'), + qtip: feature.description + })); + } + } + parent = child; + } + } + }); + + // remove all groups without children + me.getStore('FeatureMenu').getRootNode().cascadeBy(function (node) { + if (node.get('group') && !node.hasChildNodes()) { + groupsToRemove.push(node); + } + }); + Ext.Array.each(groupsToRemove, function (node) { + node.parentNode.removeChild(node, true); + }); + + me.getStore('FeatureMenu').sort([ + { property: 'weight', direction: 'ASC' }, + { property: 'text', direction: 'ASC' } + ]); + + Ext.resumeLayouts(true); + }, + + /** + * @private + */ + createNotAvailableFeature: function (feature) { + return this.getFeatureModel().create({ + text: feature.get('text'), + path: feature.get('path'), + description: feature.get('description'), + iconName: feature.get('iconName'), + view: { + xtype: 'nx-feature-notvisible', + // FIXME: i18n + text: feature.get('text') + ' feature is not available as ' + + (NX.State.getValue('user') ? ' you do not have the required permissions' : ' you are not logged in') + }, + visible: NX.controller.Features.alwaysVisible + }); + }, + + /** + * @private + */ + createNotFoundFeature: function (bookmark) { + return this.getFeatureModel().create({ + text: 'Not found', + path: '/Not Found', + description: bookmark, + iconName: 'feature-notfound', + view: { + xtype: 'nx-feature-notfound', + path: bookmark + }, + visible: NX.controller.Features.alwaysVisible + }); + }, + + /** + * @private + */ + getMode: function (bookmark) { + if (bookmark && bookmark.getSegment(0)) { + return bookmark.getSegment(0).split('/')[0]; + } + return undefined; + }, + + /** + * Change mode. + * + * @public + * @param {String} mode to change to + */ + changeMode: function (mode) { + var me = this; + + // + me.logDebug('Mode changed:', mode); + // + + me.mode = mode; + me.refreshTree(); + me.navigateTo(NX.Bookmarks.fromToken(me.getStore('FeatureMenu').getRootNode().firstChild.get('bookmark'))); + NX.Bookmarks.bookmark(me.getBookmark()); + }, + + /** + * Register a mode button. + * + * @private + * @param {NX.view.header.Mode} mode + */ + registerMode: function (mode) { + this.availableModes.add(mode); + }, + + /** + * Unregister a mode button. + * + * @private + * @param {NX.view.header.Mode} mode + */ + unregisterMode: function (mode) { + this.availableModes.remove(mode); + }, + + /** + * @private + */ + selectFirstAvailableMode: function () { + var me = this; + me.availableModes.each(function (mode) { + if (!mode.isHidden()) { + me.mode = mode.name; + return false; + } + return true; + }); + + // + me.logDebug('Auto selecting mode:', me.mode); + // + }, + + /** + * @private + */ + refreshModes: function () { + this.refreshModeButtons(); + this.refreshTree(); + }, + + /** + * Check for unsaved changes before opening a menu item. + * + * @private + */ + warnBeforeMenuSelect: function(tree, td, cellIndex, record) { + var me = this; + + return me.warnBeforeNavigate( + function () { + me.getFeatureMenu().getSelectionModel().select(record); + me.getFeatureMenu().fireEvent('itemclick', me.getFeatureMenu(), record); + } + ); + }, + + /** + * Check for unsaved changes before switching modes. + * + * @private + * @param {NX.view.header.Mode} mode + */ + warnBeforeModeSelect: function(mode) { + var me = this; + + var cb = function() { + mode.toggle(true); + me.changeMode(mode.name); + }; + + if (me.warnBeforeNavigate(cb)) { + me.changeMode(mode.name); + } + else { + mode.toggle(true); + } + }, + + /** + * Check for unsaved changes before doing a search. + * + * @private + */ + warnBeforeSearch: function() { + var me = this, + button = me.getHeaderPanel().down('nx-header-quicksearch'); + + return me.warnBeforeNavigate( + function() { + button.fireEvent('search', button, button.getValue()); + } + ); + }, + + /** + * Check for unsaved changes before clicking a button. + * + * @private + */ + warnBeforeButtonClick: function(button, e) { + return this.warnBeforeNavigate( + function() { + button.handler.call(button.scope, button, e); + } + ); + }, + + /** + * Check for unsaved changes before refreshing the view. + * + * @private + */ + warnBeforeRefresh: function() { + var me = this, + button = me.getHeaderPanel().down('nx-header-refresh'); + + return me.warnBeforeNavigate( + function() { + button.fireEvent('click'); + } + ); + }, + + /** + * Check for unsaved changes before signing out. + * + * @private + */ + warnBeforeSignOut: function() { + return this.warnBeforeNavigate( + function() { + NX.getApplication().getController('User').signOut(); + } + ); + }, + + /** + * Check for unsaved changes. Warn the user, and stop or continue navigation. + * + * @private + * @param {Function} callback + */ + warnBeforeNavigate: function(callback) { + var me = this, + dirty = me.hasDirt(), + content = me.getFeatureContent(); + + // If true, we’ve already warned the user about the unsaved changes. Don’t warn again. + if (content.discardUnsavedChanges) { + // Reset the flag and continue with navigation + content.resetUnsavedChangesFlag(); + return true; + } + + // Load the content, but warn first if there are unsaved changes + if (dirty) { + // Show modal and stop navigation + me.showUnsavedChangesModal(callback); + return false; + + } else { + // Continue with navigation + return true; + } + }, + + /** + * Show warning modal about unsaved changes, and take action. + * + * @private + * @param {Function} callback + */ + showUnsavedChangesModal: function(callback) { + var content = this.getFeatureContent(); + + Ext.create('NX.view.UnsavedChanges', { + content: content, + callback: function() { + // Run the callback + callback(); + + // Reset the unsaved changes flag + content.resetUnsavedChangesFlag(); + } + }); + }, + + /** + * Are any forms dirty? + * + * @private + */ + hasDirt: function() { + var dirty = false, + forms = Ext.ComponentQuery.query('form[settingsForm=true]'); + + // Check for dirty content + if (forms.length !== 0) { + Ext.Array.each(forms, function (form) { + if (form.isDirty()) { + dirty = true; + return false; // break + } + }); + } + + return dirty; + }, + + /** + * Warn people about refreshing or closing their browser when there are unsaved changes. + * + * @private + */ + warnBeforeUnload: function() { + var me = this; + + window.onbeforeunload = function() { + if (me.hasDirt()) { + return NX.I18n.get('Menu_Browser_Title'); + } + }; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/MenuGroup.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/MenuGroup.js new file mode 100644 index 0000000000000000000000000000000000000000..a8bb7cbe319bedd7f5b20147e363a2e97df95d97 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/MenuGroup.js @@ -0,0 +1,61 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Menu group controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.MenuGroup', { + extend: 'NX.app.Controller', + requires: [ + 'NX.Bookmarks' + ], + + views: [ + 'feature.Group' + ], + + /** + * @override + */ + init: function () { + var me = this; + + me.listen({ + component: { + 'nx-feature-group dataview': { + selectionchange: me.onSelection + } + } + }); + }, + + /** + * Invoked when {@link NX.view.feature.Group} item is selected. + * + * @private + * @param {NX.view.feature.Group} view + * @param {NX.model.Feature[]} records + */ + onSelection: function (view, records) { + var feature; + + if (records.length > 0) { + feature = records[0]; + NX.Bookmarks.navigateTo(NX.Bookmarks.fromToken(feature.get('bookmark')), this); + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Message.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Message.js new file mode 100644 index 0000000000000000000000000000000000000000..bc6c9f786b7fba8f3c22cef7b92d94f2a3b74dfb --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Message.js @@ -0,0 +1,114 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Message controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.Message', { + extend: 'NX.app.Controller', + requires: [ + 'NX.Icons' + ], + + views: [ + 'message.Notification' + ], + + /** + * @protected + */ + init: function () { + var me = this; + + me.getApplication().getIconController().addIcons({ + 'message-default': { + file: 'bell.png', + variants: ['x16', 'x32'], + preload: true + }, + 'message-primary': { + file: 'information.png', + variants: ['x16', 'x32'], + preload: true + }, + 'message-danger': { + file: 'exclamation.png', + variants: ['x16', 'x32'], + preload: true + }, + 'message-warning': { + file: 'warning.png', + variants: ['x16', 'x32'], + preload: true + }, + 'message-success': { + file: 'accept.png', + variants: ['x16', 'x32'], + preload: true + } + }); + }, + + /** + * Internal customization of {@link NX.view.message.Notification} window options. + * + * At the moment, mainly intended for use by functional tests that need to + * override default settings. + * + * @internal + * @property {Object} + */ + windowOptions: {}, + + /** + * @public + * @param {object} message + * @param {string} message.type + * @param {string} message.text + */ + addMessage: function (message) { + if (!message.type) { + message.type = 'default'; + } + + message.timestamp = new Date(); + + // show transient message notification + if (!this.messageExists(message)) { + var cfg = Ext.clone(this.windowOptions); + this.getView('message.Notification').create(Ext.apply(cfg, { + ui: 'nx-message-' + message.type, + iconCls: NX.Icons.cls('message-' + message.type, 'x16'), + title: Ext.String.capitalize(message.type), + html: message.text + })); + } + }, + + /** + * Query to see if the message is already displayed so that we can prevent duplicates. + * @private + * @param message + */ + messageExists: function (message) { + var selector = 'nx-message-notification[title=' + Ext.String.capitalize(message.type) + ']'; + var existingMessage = Ext.Array.filter(Ext.ComponentQuery.query(selector), function (foundMessage) { + return Ext.String.trim(foundMessage.body.el.dom.innerText) == message.text; + } + ); + return existingMessage.length > 0; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Permissions.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Permissions.js new file mode 100644 index 0000000000000000000000000000000000000000..416a13c7d40830f3d6c94f8e10bd8b47b20b4dc7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Permissions.js @@ -0,0 +1,152 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Permissions management controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.Permissions', { + extend: 'NX.app.Controller', + requires: [ + 'NX.State', + 'NX.Permissions' + ], + + stores: [ + 'Permission' + ], + + /** + * @override + */ + init: function () { + var me = this; + + me.listen({ + controller: { + '#State': { + userchanged: me.fetchPermissions, + permissionschanged: me.loadPermissions + } + }, + store: { + '#Permission': { + load: me.firePermissionsChanged, + update: me.onUpdate, + remove: me.firePermissionsChanged + } + } + }); + + me.addEvents( + /** + * Fires when permissions change. + * + * @event changed + * @param {NX.Permissions} permissions Permissions helper. + */ + 'changed' + ); + }, + + /** + * Prime initial set of permissions from state. + * + * @override + */ + onLaunch: function () { + var me = this, + rawData = NX.State.getValue('permissions'); + + // + me.logTrace('Initial permissions:', rawData); + // + + me.getStore('Permission').loadRawData(rawData, false); + NX.Permissions.setPermissions(me.getPermissions()); + + // + me.logInfo('Permissions primed'); + // + }, + + /** + * @private + */ + onUpdate: function (store, record, operation) { + if (operation === Ext.data.Model.COMMIT) { + this.firePermissionsChanged(); + } + }, + + /** + * @private + */ + fetchPermissions: function () { + var me = this; + + NX.Permissions.resetPermissions(); + // + me.logDebug('Fetching permissions...'); + // + me.getStore('Permission').load(); + }, + + /** + * @private + */ + loadPermissions: function (permissions) { + var me = this; + + // + me.logDebug('Loading permissions...'); + // + + me.getStore('Permission').loadRawData(permissions, false); + me.firePermissionsChanged(); + }, + + /** + * @private + */ + firePermissionsChanged: function () { + var me = this; + + NX.Permissions.setPermissions(me.getPermissions()); + + // + me.logDebug('Permissions changed; Firing event'); + // + + me.fireEvent('changed', NX.Permissions); + }, + + /** + * @private + * @return {Object} permissions + */ + getPermissions: function () { + var store = this.getStore('Permission'), + perms = {}; + + store.clearFilter(); + store.each(function (rec) { + perms[rec.get('id')] = rec.get('permitted'); + }); + + return perms; + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Refresh.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Refresh.js new file mode 100644 index 0000000000000000000000000000000000000000..feaae99a9b2f2da85e86b27f8cb5c759ea0fecb8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Refresh.js @@ -0,0 +1,79 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Refresh controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.Refresh', { + extend: 'NX.app.Controller', + requires: [ + 'NX.Messages', + 'NX.I18n' + ], + + views: [ + 'header.Refresh' + ], + + /** + * @override + */ + init: function () { + var me = this; + + me.listen({ + component: { + 'nx-header-refresh': { + click: me.refresh + } + } + }); + + me.addEvents( + /** + * Fires before the refresh is performed. + * + * @event beforerefresh + */ + 'beforerefresh', + + /** + * Fires when refresh should be performed. + * + * @event refresh + */ + 'refresh' + ); + }, + + /** + * Fire refresh event. + * + * @public + */ + refresh: function () { + var me = this; + + if (me.fireEvent('beforerefresh')) { + me.fireEvent('refresh'); + + // Show a message here, so that if the current view doesn't actually support + // request that users don't think the feature is broken and spam-click the refresh button + NX.Messages.add({ text: NX.I18n.get('Refresh_Message'), type: 'default' }); + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/SettingsForm.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/SettingsForm.js new file mode 100644 index 0000000000000000000000000000000000000000..1731c6586d314e6d96b222bfd3fc2c28ce80e9dd --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/SettingsForm.js @@ -0,0 +1,252 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Controls forms marked with settingsForm = true by adding save/discard/refresh functionality using form configured + * api. + * + * @since 3.0 + */ +Ext.define('NX.controller.SettingsForm', { + extend: 'NX.app.Controller', + requires: [ + 'Ext.ComponentQuery', + 'NX.Messages' + ], + + /** + * @override + */ + init: function () { + var me = this; + + me.listen({ + controller: { + '#Refresh': { + refresh: me.onRefresh + } + }, + component: { + 'form[settingsForm=true]': { + afterrender: me.loadForm, + load: me.loadForm, + dirtychange: me.updateButtonState, + validitychange: me.updateButtonState + }, + 'form[settingsForm=true][editableCondition]': { + afterrender: me.bindEditableCondition + }, + 'form[settingsForm=true][settingsFormSubmit=true] button[action=add]': { + click: me.submitForm + }, + 'form[settingsForm=true][settingsFormSubmit=true] button[action=submit]': { + click: me.submitForm + }, + 'form[settingsForm=true][settingsFormSubmit=true] button[action=save]': { + click: me.submitForm + }, + 'form[settingsForm=true][settingsFormSubmit=true] button[action=discard]': { + click: me.discardChanges + }, + 'form[settingsForm=true] field[bindGroup]': { + validitychange: me.updateEnableState + } + } + }); + }, + + /** + * @private + */ + onRefresh: function () { + var me = this, + forms = Ext.ComponentQuery.query('form[settingsForm=true]'); + + if (forms) { + Ext.each(forms, function (form) { + me.loadForm(form); + }); + } + }, + + /** + * Loads the form if form's api load function is defined. + * + * @private + */ + loadForm: function (form, options) { + if (!form.isDestroyed && form.rendered) { + if (form.api && form.api.load) { + // Load the form + form.load(Ext.applyIf(options || {}, { + waitMsg: form.settingsFormLoadMessage, + success: function (basicForm, action) { + // Form is valid + form.isValid(); + + form.fireEvent('loaded', form, action); + }, + failure: function (basicForm, action) { + form.isValid(); + } + })); + } + else { + form.isValid(); + } + } + }, + + /** + * Submits the form containing the button, if form's api submit function is defined. + * + * @private + */ + submitForm: function (button) { + var me = this, + form = button.up('form'); + + if (form.api && form.api.submit) { + form.submit({ + waitMsg: form.settingsFormSubmitMessage, + success: function (basicForm, action) { + var title = me.getSettingsFormSuccessMessage(form, action); + if (title) { + NX.Messages.add({ text: title, type: 'success' }); + } + form.fireEvent('submitted', form, action); + me.loadForm(form); + } + }); + } + }, + + /** + * Discard any changes to this form + * + * @private + */ + discardChanges: function (button) { + var form = button.up('form'); + + if (form.api && form.api.load) { + form.fireEvent('load', form); + } + else { + form.getForm().reset(); + form.isValid(); + } + }, + + /** + * Calculates title based on form's {NX.view.SettingsForm#getSettingsFormSuccessMessage}. + * + * @private + * @param {NX.view.SettingsForm} form + * @param {Ext.form.action.Action} action + */ + getSettingsFormSuccessMessage: function (form, action) { + var title; + + if (form.settingsFormSuccessMessage) { + if (Ext.isFunction(form.settingsFormSuccessMessage)) { + title = form.settingsFormSuccessMessage(action.result.data); + } + else { + title = form.settingsFormSuccessMessage.toString(); + } + title = title.replace(/\$action/, action.type.indexOf('submit') > -1 ? 'updated' : 'refreshed'); + } + return title; + }, + + /** + * Toggle editable on settings form when editable condition is satisfied (if specified). + * + * @private + * @param {NX.view.SettingsForm} form + */ + bindEditableCondition: function (form) { + if (Ext.isDefined(form.editableCondition)) { + form.mon( + form.editableCondition, + { + satisfied: function () { + form.setEditable(true); + }, + unsatisfied: function () { + form.setEditable(false); + }, + scope: form + } + ); + } + }, + + /** + * Enable/Disable components marked with a "groupBind" property by checking that all fields marked with "bindGroup" + * that matches, are valid. + * + * @private + * @param {Ext.form.field.Base} field a field with a "bindGroup" property. "bindGroup" can be a space separated list of + * groups + */ + updateEnableState: function(field) { + var form = field.up('form'); + + if (Ext.isString(field['bindGroup'])) { + Ext.Array.each(field['bindGroup'].split(' '), function(group) { + var bindables = form.query('component[groupBind=' + group + ']'), + validatables = form.query('field[bindGroup~=' + group + ']'), + enabled; + + Ext.Array.each(bindables, function(bindable) { + if (!Ext.isDefined(enabled)) { + enabled = true; + Ext.Array.each(validatables, function(validatable) { + return enabled = validatable.isValid(); + }); + } + if (enabled) { + bindable.enable(); + } + else { + bindable.disable(); + } + }); + }); + } + }, + + /** + * Update the state of the add|save|submit and discard buttons based on form being dirty/valid. + * + * @private + */ + updateButtonState: function(form) { + var dirty = form.isDirty(), + valid = form.isValid(), + saveButton = form.owner.down('button[action=save]'), + discardButton = form.owner.down('button[action=discard]'); + + if (saveButton) { + saveButton.setDisabled(!valid || !dirty); + } + + if (discardButton) { + discardButton.setDisabled(!dirty); + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/State.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/State.js new file mode 100644 index 0000000000000000000000000000000000000000..276be21356ee4137f2527348a15d68e51b20ccc4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/State.js @@ -0,0 +1,459 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * State controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.State', { + extend: 'NX.app.Controller', + requires: [ + 'Ext.direct.Manager', + 'NX.Dialogs', + 'NX.Messages', + 'NX.I18n' + ], + + models: [ + 'State' + ], + stores: [ + 'State' + ], + + /** + * @private + */ + disconnectedTimes: 0, + + /** + * Max number of times to show a warning, before disabling the UI. + * + * @private + */ + maxDisconnectWarnings: 3, + + /** + * True when state is received from server. + * + * @private + */ + receiving: false, + + /** + * @override + */ + init: function () { + var me = this; + + me.listen({ + controller: { + '#State': { + userchanged: me.onUserChanged, + uisettingschanged: me.onUiSettingsChanged, + licensechanged: me.onLicenseChanged, + serveridchanged: me.reloadWhenServerIdChanged, + clusteridchanged: me.reloadWhenServerIdChanged + } + }, + store: { + '#State': { + add: me.onEntryAdded, + update: me.onEntryUpdated, + remove: me.onEntryRemoved + } + } + }); + + me.addEvents( + /** + * Fires when any of application context values changes. + * + * @event changed + */ + 'changed' + ); + }, + + /** + * Install initial state, primed from app.js + * + * @override + */ + onLaunch: function () { + var me = this; + + // + me.logTrace('Initial state:', NX.app.state); + // + + var uiSettings = NX.app.state['uiSettings']; + + NX.State.setBrowserSupported( + !Ext.isIE || (Ext.isIE9p && Ext.isIE11m) + ); + NX.State.setValue('debug', NX.app.debug); + NX.State.setValue('receiving', false); + + // set uiSettings by the end so it does not start state pulling till all initial state hashes are known + // this avoids unnecessary sending of state from server + delete NX.app.state['uiSettings']; + NX.State.setValues(NX.app.state); + NX.State.setValues({ uiSettings: uiSettings }); + + // + me.logInfo('State primed'); + // + }, + + /** + * @public + * @returns {Boolean} true when status is being received from server + */ + isReceiving: function () { + return this.receiving; + }, + + getValue: function(key, defaultValue) { + var model = this.getStore('State').getById(key), + value; + + if (model) { + value = model.get('value'); + if (Ext.isDefined(value)) { + return value; + } + } + return defaultValue; + }, + + /** + * @public + * @param {String} key + * @param {Object} value + * @param {String} [hash] + */ + setValue: function (key, value, hash) { + var me = this, + model = me.getStore('State').getById(key); + + if (!model) { + if (Ext.isDefined(value)) { + me.getStore('State').add(me.getStateModel().create({ key: key, value: value, hash: hash })); + } + } + else { + if (Ext.isDefined(value) && value !== null) { + if (!Ext.Object.equals(value, model.get('value'))) { + model.set('value', value); + } + if (!Ext.Object.equals(hash, model.get('hash'))) { + model.set('hash', hash); + } + } + else { + me.getStore('State').remove(model); + } + } + me.getStore('State').commitChanges(); + if (me.statusProvider) { + if (Ext.isDefined(value) && hash) { + me.statusProvider.baseParams[key] = hash; + } + else { + delete me.statusProvider.baseParams[key]; + } + } + }, + + setValues: function (map) { + var me = this, + hash, valueToSet; + + if (map) { + Ext.Object.each(map, function (key, value) { + valueToSet = value; + if (Ext.isObject(value) && Ext.isDefined(value.hash) && Ext.isDefined(value.value)) { + hash = value.hash; + valueToSet = value.value; + } + if (Ext.isDefined(valueToSet)) { + if (!Ext.isPrimitive(valueToSet) && !Ext.isArray(valueToSet) + && Ext.ClassManager.getByAlias('nx.state.' + key)) { + valueToSet = Ext.ClassManager.instantiateByAlias('nx.state.' + key, valueToSet); + } + } + me.setValue(key, valueToSet, hash); + }); + } + }, + + onEntryAdded: function (store, models) { + var me = this; + Ext.each(models, function (model) { + me.notifyChange(model.get('key'), model.get('value')); + }); + }, + + onEntryUpdated: function (store, model, operation, modifiedFieldNames) { + if ((operation === Ext.data.Model.EDIT) && modifiedFieldNames.indexOf('value') > -1) { + this.notifyChange(model.get('key'), model.get('value'), model.modified.value); + } + }, + + onEntryRemoved: function (store, model) { + this.notifyChange(model.get('key'), undefined, model.get('value')); + }, + + notifyChange: function (key, value, oldValue) { + var me = this; + + // + me.logTrace('Changed:', key, '->', (value ? value : '(deleted)')); + // + + me.fireEvent(key.toLowerCase() + 'changed', value, oldValue); + me.fireEvent('changed', key, value, oldValue); + }, + + /** + * Reset state pooling when uiSettings.statusInterval changes. + * + * @private + */ + onUiSettingsChanged: function (uiSettings, oldUiSettings) { + var me = this, + newStatusInterval, oldStatusInterval; + + uiSettings = uiSettings || {}; + oldUiSettings = oldUiSettings || {}; + + if (uiSettings.debugAllowed !== oldUiSettings.debugAllowed) { + NX.State.setValue('debug', uiSettings.debugAllowed && (NX.global.location.search === '?debug')); + } + + if (uiSettings.title !== oldUiSettings.title) { + NX.global.document.title = NX.global.document.title.replace(oldUiSettings.title, uiSettings.title); + } + + if (me.statusProvider) { + oldStatusInterval = me.statusProvider.interval; + } + + newStatusInterval = uiSettings.statusIntervalAnonymous; + if (NX.State.getUser()) { + newStatusInterval = uiSettings.statusIntervalAuthenticated; + } + + if (newStatusInterval > 0) { + if (newStatusInterval !== oldStatusInterval) { + if (me.statusProvider) { + me.statusProvider.disconnect(); + me.receiving = false; + } + me.statusProvider = Ext.direct.Manager.addProvider({ + type: 'polling', + url: NX.direct.api.POLLING_URLS.rapture_State_get, + interval: newStatusInterval * 1000, + baseParams: { + }, + listeners: { + data: me.onServerData, + scope: me + } + }); + + // + me.logDebug('State pooling configured for', newStatusInterval, 'seconds'); + // + } + } + else { + if (me.statusProvider) { + me.statusProvider.disconnect(); + } + + // + me.logDebug('State pooling disabled'); + // + } + }, + + /** + * On sign-in/sign-out update status interval. + * + * @private + */ + onUserChanged: function (user, oldUser) { + var uiSettings; + + if (Ext.isDefined(user) !== Ext.isDefined(oldUser)) { + uiSettings = NX.State.getValue('uiSettings'); + this.onUiSettingsChanged(uiSettings, uiSettings); + } + }, + + /** + * Called when there is new data from state callback. + * + * @private + */ + onServerData: function (provider, event) { + var me = this; + if (event.data) { + me.onSuccess(event); + } + else { + me.onError(event); + } + }, + + /** + * Called when state pooling was successful. + * + * @private + */ + onSuccess: function (event) { + var me = this, + serverId = me.getValue('serverId'), + clusterId = me.getValue('clusterId'), + state = event.data.data, + clustered = Ext.isDefined(state.nodes) && state.nodes.value.enabled, + oldClusterId = state.clusterId ? state.clusterId.value : clusterId, + oldServerId = state.serverId ? state.serverId.value : serverId; + + me.receiving = true; + + // re-enable the UI we are now connected again + if (me.disconnectedTimes > 0) { + me.disconnectedTimes = 0; + NX.Messages.add({text: NX.I18n.get('State_Reconnected_Message'), type: 'success' }); + } + + NX.State.setValue('receiving', true); + + if ( (clustered && !me.reloadWhenServerIdChanged(clusterId, oldClusterId)) || + (!clustered && !me.reloadWhenServerIdChanged(serverId, oldServerId)) ) { + me.setValues(state); + } + // TODO: Fire global refresh event + }, + + /** + * Called when state pooling failed. + * + * @private + */ + onError: function (event) { + var me = this; + + if (event.code === 'xhr') { + if (event.xhr.status === 402) { + NX.State.setValue('license', Ext.apply(Ext.clone(NX.State.getValue('license')), { installed: false })); + } + else { + me.receiving = false; + + // we appear to have lost the server connection + me.disconnectedTimes = me.disconnectedTimes + 1; + + NX.State.setValue('receiving', false); + + if (me.disconnectedTimes <= me.maxDisconnectWarnings) { + NX.Messages.add({ text: NX.I18n.get('State_Disconnected_Message'), type: 'warning' }); + } + + // Give up after a few attempts and disable the UI + if (me.disconnectedTimes > me.maxDisconnectWarnings) { + NX.Messages.add({text: NX.I18n.get('State_Disconnected_Message'), type: 'danger' }); + + // Stop polling + me.statusProvider.disconnect(); + + // FIXME: i18n + // Show the UI with a modal dialog error + NX.Dialogs.showError( + 'Server disconnected', + 'There is a problem communicating with the server', + { + buttonText: { + ok: 'Retry' + }, + + fn: function () { + // retry after the dialog is dismissed + me.statusProvider.connect(); + } + } + ); + } + } + } + else if (event.type === 'exception') { + NX.Messages.add({ text: event.message, type: 'danger' }); + } + }, + + /** + * Refreshes status from server on demand. + * + * @public + */ + refreshNow: function () { + var me = this; + if (me.statusProvider) { + me.statusProvider.disconnect(); + me.statusProvider.connect(); + } + }, + + /** + * Show messages about license. + * + * @private + * @param {Object} license + * @param {Number} license.installed + * @param {Object} oldLicense + * @param {Number} oldLicense.installed + */ + onLicenseChanged: function (license, oldLicense) { + if (license && oldLicense) { + if (license.installed && !oldLicense.installed) { + NX.Messages.add({ text: NX.I18n.get('State_Installed_Message'), type: 'success' }); + } + else if (!license.installed && oldLicense.installed) { + NX.Messages.add({ text: NX.I18n.get('State_Uninstalled_Message'), type: 'warning' }); + } + } + }, + + reloadWhenServerIdChanged: function (serverId, oldServerId) { + if (oldServerId && (serverId !== oldServerId) && !serverId.startsWith('ignore')) { + // FIXME: i18n + NX.Dialogs.showInfo( + 'Server restarted', + 'Application will be reloaded as server has been restarted', + { + fn: function () { + NX.global.location.reload(); + } + } + ); + return true; + } + return false; + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/UiSessionTimeout.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/UiSessionTimeout.js new file mode 100644 index 0000000000000000000000000000000000000000..452bdd1d75603f6c0e54794efd46cb837e0cd365 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/UiSessionTimeout.js @@ -0,0 +1,189 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * UI Session Timeout controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.UiSessionTimeout', { + extend: 'NX.app.Controller', + requires: [ + 'Ext.ux.ActivityMonitor', + 'NX.Messages', + 'NX.Security', + 'NX.State', + 'NX.I18n', + 'NX.State' + ], + + views: [ + 'ExpireSession' + ], + + refs: [ + { + ref: 'expireSessionWindow', + selector: 'nx-expire-session' + } + ], + + SECONDS_TO_EXPIRE: 30, + + activityMonitor: undefined, + + expirationTicker: undefined, + + /** + * @override + */ + init: function () { + var me = this; + + me.listen({ + controller: { + '#State': { + userchanged: me.setupTimeout, + uisettingschanged: me.onUiSettingsChanged, + receivingchanged: me.setupTimeout + } + }, + component: { + 'nx-expire-session': { + afterrender: me.startTicking + }, + 'nx-expire-session button[action=cancel]': { + click: me.setupTimeout + } + } + }); + }, + + /** + * @override + */ + onLaunch: function () { + this.setupTimeout(); + }, + + /** + * Reset UI session timeout when uiSettings.sessionTimeout changes. + * + * @private + * @param {Object} uiSettings + * @param {Number} uiSettings.sessionTimeout + * @param {Object} oldUiSettings + * @param {Number} oldUiSettings.sessionTimeout + */ + onUiSettingsChanged: function (uiSettings, oldUiSettings) { + uiSettings = uiSettings || {}; + oldUiSettings = oldUiSettings || {}; + + if (uiSettings.sessionTimeout !== oldUiSettings.sessionTimeout) { + this.setupTimeout(); + } + }, + + /** + * @private + */ + setupTimeout: function () { + var me = this, + user = NX.State.getUser(), + uiSettings = NX.State.getValue('uiSettings') || {}, + sessionTimeout = user ? uiSettings['sessionTimeout'] : undefined; + + me.cancelTimeout(); + if ((user && NX.State.isReceiving()) && sessionTimeout > 0) { + // + me.logDebug('Session expiration enabled for', sessionTimeout, 'minutes'); + // + + me.activityMonitor = Ext.create('Ext.ux.ActivityMonitor', { + // check every second + interval: 1000, + maxInactive: ((sessionTimeout * 60) - me.SECONDS_TO_EXPIRE) * 1000, + isInactive: Ext.bind(me.showExpirationWindow, me) + }); + me.activityMonitor.start(); + } + }, + + /** + * @private + */ + cancelTimeout: function () { + var me = this, + expireSessionView = me.getExpireSessionWindow(); + + // close the window if the session has not yet expired or if the server is disconnected + if (expireSessionView && (!expireSessionView.sessionExpired() || !NX.State.isReceiving())) { + expireSessionView.close(); + } + + if (me.activityMonitor) { + me.activityMonitor.stop(); + delete me.activityMonitor; + + // + me.logDebug('Activity monitor disabled'); + // + } + + if (me.expirationTicker) { + me.expirationTicker.destroy(); + delete me.expirationTicker; + + // + me.logDebug('Session expiration disabled'); + // + } + }, + + /** + * @private + */ + showExpirationWindow: function () { + NX.Messages.add({text: NX.I18n.get('UiSessionTimeout_Expire_Message'), type: 'warning'}); + this.getExpireSessionView().create(); + }, + + /** + * @private + */ + startTicking: function (win) { + var me = this; + + me.expirationTicker = Ext.util.TaskManager.newTask({ + run: function (count) { + win.down('label').setText(NX.I18n.format('UiSessionTimeout_Expire_Text', me.SECONDS_TO_EXPIRE - count)); + if (count === me.SECONDS_TO_EXPIRE) { + win.down('label').setText(NX.I18n.get('SignedOut_Text')); + win.down('button[action=close]').show(); + win.down('button[action=signin]').show(); + win.down('button[action=cancel]').hide(); + NX.Messages.add({ + text: NX.I18n.format('UiSessionTimeout_Expired_Message', NX.State.getValue('uiSettings')['sessionTimeout']), + type: 'warning' + }); + NX.Security.signOut(); + } + }, + interval: 1000, + repeat: me.SECONDS_TO_EXPIRE + }); + me.expirationTicker.start(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Unlicensed.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Unlicensed.js new file mode 100644 index 0000000000000000000000000000000000000000..e860e21cb3a186ea76360ebf7b0b60a8009a2edf --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/Unlicensed.js @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Unlicensed uber mode controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.Unlicensed', { + extend: 'NX.app.Controller', + requires: [ + 'NX.Bookmarks', + 'NX.Messages', + 'NX.I18n' + ], + + + /** + * Show {@link NX.view.Unlicensed} view from {@link Ext.container.Viewport}. + * + * @override + */ + onLaunch: function () { + var me = this; + // + me.logDebug('Adding unlicensed listeners'); + // + Ext.History.on('change', me.forceLicensing); + me.forceLicensing(); + }, + + /** + * Removes {@link NX.view.Unlicensed} view from {@link Ext.container.Viewport}. + * + * @override + */ + onDestroy: function () { + var me = this; + // + me.logDebug('Removing unlicensed listeners'); + // + Ext.History.un('change', me.forceLicensing); + }, + + /** + * Show a message and force navigation to the Licensing page, preventing all other navigation in the UI. + */ + forceLicensing: function () { + NX.Messages.add({text: NX.I18n.get('State_License_Invalid_Message'), type: 'danger'}); + NX.Bookmarks.navigateTo(NX.Bookmarks.fromToken('admin/system/licensing')); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/UnsupportedBrowser.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/UnsupportedBrowser.js new file mode 100644 index 0000000000000000000000000000000000000000..3255077bf37b85cb0cceb73d62118a502ff22777 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/UnsupportedBrowser.js @@ -0,0 +1,104 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Unsupported browser uber mode controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.UnsupportedBrowser', { + extend: 'NX.app.Controller', + requires: [ + 'NX.State' + ], + + views: [ + 'UnsupportedBrowser', + 'header.Panel', + 'header.Branding', + 'header.Logo', + 'footer.Panel', + 'footer.Branding' + ], + + refs: [ + { + ref: 'viewport', + selector: 'viewport' + }, + { + ref: 'unsupportedBrowser', + selector: 'nx-unsupported-browser' + } + ], + + /** + * @override + */ + init: function () { + var me = this; + + me.listen({ + component: { + 'viewport': { + afterrender: me.onLaunch + }, + 'nx-unsupported-browser button[action=continue]': { + click: me.onContinue + } + } + }); + }, + + /** + * Show {@link NX.view.UnsupportedBrowser} view from {@link Ext.container.Viewport}. + * + * @override + */ + onLaunch: function () { + var me = this, + viewport = me.getViewport(); + + if (viewport) { + // + me.logDebug('Showing unsupported browser view'); + // + + viewport.add({ xtype: 'nx-unsupported-browser' }); + } + }, + + /** + * Removes {@link NX.view.UnsupportedBrowser} view from {@link Ext.container.Viewport}. + * + * @override + */ + onDestroy: function () { + var me = this, + viewport = me.getViewport(); + + if (viewport) { + // + me.logDebug('Removing unsupported browser view'); + // + + viewport.remove(me.getUnsupportedBrowser()); + } + }, + + onContinue: function () { + NX.State.setBrowserSupported(true); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/User.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/User.js new file mode 100644 index 0000000000000000000000000000000000000000..f71a191e173a96ad81af71a987f55613e7928cf6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/User.js @@ -0,0 +1,402 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * User controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.User', { + extend: 'NX.app.Controller', + requires: [ + 'NX.util.Base64', + 'NX.Messages', + 'NX.State', + 'NX.I18n', + 'NX.view.header.Mode' + ], + + views: [ + 'header.SignIn', + 'header.SignOut', + 'Authenticate', + 'SignIn', + 'ExpireSession' + ], + + refs: [ + { + ref: 'signInButton', + selector: 'nx-header-signin' + }, + { + ref: 'signOutButton', + selector: 'nx-header-signout' + }, + { + ref: 'userMode', + selector: 'nx-header-mode[name=user]' + }, + { + ref: 'signIn', + selector: 'nx-signin' + }, + { + ref: 'authenticate', + selector: 'nx-authenticate' + } + ], + + /** + * @override + */ + init: function () { + var me = this; + + me.getApplication().getIconController().addIcons({ + 'authenticate': { + file: 'lock.png', + variants: ['x16', 'x32'] + } + }); + + me.listen({ + controller: { + '#State': { + userchanged: me.onUserChanged + } + }, + component: { + 'nx-header-panel': { + afterrender: me.manageButtons + }, + 'nx-header-signin': { + click: me.showSignInWindow + }, + 'nx-expire-session button[action=signin]': { + click: me.showSignInWindow + }, + 'nx-header-signout': { + click: me.onClickSignOut + }, + 'nx-signin button[action=signin]': { + click: me.signIn + }, + 'nx-authenticate button[action=authenticate]': { + click: me.doAuthenticateAction + } + } + }); + + me.addEvents( + /** + * Fires when a user had been successfully signed-in. + * + * @event signin + * @param {Object} user + */ + 'signin', + + /** + * Fires before a user is signed out. + * + * @event beforesignout + */ + 'beforesignout', + + /** + * Fires when a user had been successfully signed-out. + * + * @event signout + */ + 'signout' + ); + }, + + /** + * @private + */ + onUserChanged: function (user, oldUser) { + var me = this; + + if (user && !oldUser) { + NX.Messages.add({text: NX.I18n.format('User_SignedIn_Message', user.id), type: 'default'}); + me.fireEvent('signin', user); + } + else if (!user && oldUser) { + NX.Messages.add({text: NX.I18n.get('User_SignedOut_Message'), type: 'default'}); + me.fireEvent('signout'); + } + + me.manageButtons(); + }, + + /** + * Returns true if there is an authenticated user. + * + * @public + * @return {boolean} + */ + hasUser: function () { + return Ext.isDefined(NX.State.getUser()); + }, + + /** + * Shows sign-in or authentication window based on the fact that we have an user or not. + * + * @public + * @param {String} [message] Message to be shown in authentication window + * @param {Object} [options] TODO + */ + askToAuthenticate: function (message, options) { + var me = this; + + if (me.hasUser()) { + me.showAuthenticateWindow(message, Ext.apply(options || {}, {authenticateAction: me.authenticate})); + } + else { + me.showSignInWindow(options); + } + }, + + /** + * Shows authentication window in order to retrieve an authentication token. + * + * @public + * @param {String} [message] Message to be shown in authentication window + * @param {Object} [options] TODO + */ + doWithAuthenticationToken: function (message, options) { + var me = this; + + me.showAuthenticateWindow(message, + Ext.apply(options || {}, {authenticateAction: me.retrieveAuthenticationToken}) + ); + }, + + /** + * Shows sign-in window. + * + * @private + * @param {Object} [options] TODO + */ + showSignInWindow: function (options) { + var me = this; + + if (!me.getSignIn()) { + me.getSignInView().create({options: options}); + } + }, + + /** + * Shows authenticate window. + * + * @private + * @param {String} [message] Message to be shown in authentication window + * @param {Object} [options] TODO + */ + showAuthenticateWindow: function (message, options) { + var me = this, + user = NX.State.getUser(), + win; + + if (!me.getAuthenticate()) { + win = me.getAuthenticateView().create({message: message, options: options}); + if (me.hasUser()) { + win.down('form').getForm().setValues({username: user.id}); + win.down('#password').focus(); + } + } + }, + + /** + * @private + */ + signIn: function (button) { + var me = this, + win = button.up('window'), + form = button.up('form'), + values = form.getValues(), + b64username = NX.util.Base64.encode(values.username), + b64password = NX.util.Base64.encode(values.password); + + win.getEl().mask(NX.I18n.get('User_SignIn_Mask')); + + // + me.logDebug('Sign-in user: "', values.username, '" ...'); + // + + me.doSignIn(b64username, b64password, values, button); + }, + + /** + * @private + */ + doAuthenticateAction: function (button) { + var win = button.up('window'); + + // invoke optional authenticateAction callback registered on window + if (win.options && Ext.isFunction(win.options.authenticateAction)) { + win.options.authenticateAction.call(this, button); + } + }, + + // TODO: anything that may change the authentication/session should probably not be + // TODO: done via extjs as it can batch, and the batch operation could impact the + // TODO: sanity of the requests if authentication changes mid execution of batch operations + + doSignIn: function(b64username, b64password, values, button) { + var me = this, + win = button.up('window'); + + Ext.Ajax.request({ + url: NX.util.Url.urlOf('service/rapture/session'), + method: 'POST', + params: { + username: b64username, + password: b64password + }, + scope: me, + suppressStatus: true, + success: function () { + win.getEl().unmask(); + NX.State.setUser({id: values.username}); + win.close(); + + // invoke optional success callback registered on window + if (win.options && Ext.isFunction(win.options.success)) { + win.options.success.call(win.options.scope, win.options); + } + }, + failure: function (response) { + var message = NX.I18n.get('User_Credentials_Message'); + if (response.status === 0) { + message = NX.I18n.get('User_ConnectFailure_Message'); + } + win.getEl().unmask(); + NX.Messages.add({ + text: message, + type: 'warning' + }); + } + }); + }, + + /** + * @private + */ + authenticate: function (button) { + var me = this, + win = button.up('window'), + form = button.up('form'), + user = NX.State.getUser(), + values = Ext.applyIf(form.getValues(), {username: user ? user.id : undefined}), + b64username = NX.util.Base64.encode(values.username), + b64password = NX.util.Base64.encode(values.password); + + win.getEl().mask(NX.I18n.get('User_Controller_Authenticate_Mask')); + + // + this.logDebug('Authenticating user "', values.username, '" ...'); + // + + me.doSignIn(b64username, b64password, values, button); + }, + + /** + * @private + */ + retrieveAuthenticationToken: function (button) { + var win = button.up('window'), + form = button.up('form'), + user = NX.State.getUser(), + values = Ext.applyIf(form.getValues(), {username: user ? user.id : undefined}), + b64username = NX.util.Base64.encode(values.username), + b64password = NX.util.Base64.encode(values.password); + + win.getEl().mask(NX.I18n.get('User_Retrieving_Mask')); + + // + this.logDebug('Retrieving authentication token...'); + // + + NX.direct.rapture_Security.authenticationToken(b64username, b64password, function (response) { + win.getEl().unmask(); + if (Ext.isObject(response) && response.success) { + win.close(); + + // invoke optional success callback registered on window + if (win.options && Ext.isFunction(win.options.success)) { + win.options.success.call(win.options.scope, response.data, win.options); + } + } + }); + }, + + /** + * @private + */ + onClickSignOut: function () { + var me = this; + + if (me.fireEvent('beforesignout')) { + me.signOut(); + } + }, + + /** + * @public + */ + signOut: function () { + var me = this; + + me.logDebug('Sign-out'); + + // TODO: Mask? + + Ext.Ajax.request({ + url: NX.util.Url.urlOf('service/rapture/session'), + method: 'DELETE', + scope: me, + suppressStatus: true, + success: function () { + NX.State.setUser(undefined); + } + }); + }, + + manageButtons: function () { + var me = this, + user = NX.State.getUser(), + signInButton = me.getSignInButton(), + signOutButton = me.getSignOutButton(), + userMode = me.getUserMode(); + + if (signInButton) { + if (user) { + signInButton.hide(); + userMode.show(); + userMode.setText(user.id); + userMode.setTooltip(NX.I18n.format('User_Tooltip', user.id)); + signOutButton.show(); + } + else { + signInButton.show(); + userMode.hide(); + signOutButton.hide(); + } + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Conditions.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Conditions.js new file mode 100644 index 0000000000000000000000000000000000000000..37fe4e1937895d133efc435b79b68eb3fe2cc507 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Conditions.js @@ -0,0 +1,165 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Conditions developer panel controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.dev.Conditions', { + extend: 'NX.app.Controller', + requires: [ + 'Ext.util.Filter' + ], + + stores: [ + 'NX.store.dev.Condition' + ], + views: [ + 'dev.Conditions' + ], + + refs: [ + { + ref: 'showSatisfied', + selector: 'nx-dev-conditions #showSatisfied' + }, + { + ref: 'showUnsatisfied', + selector: 'nx-dev-conditions #showUnsatisfied' + }, + { + ref: 'devPanelTabs', + selector: 'nx-dev-panel tabpanel' + }, + { + ref: 'devConditionsPanel', + selector: 'nx-dev-panel nx-dev-conditions' + } + ], + + /** + * @override + */ + init: function () { + var me = this; + + me.excludeSatisfiedFilter = Ext.create('Ext.util.Filter', { + id: 'excludeSatisfiedFilter', + filterFn: function (record) { + return record.get('satisfied') !== true; + } + }); + me.excludeUnsatisfiedFilter = Ext.create('Ext.util.Filter', { + id: 'excludeUnsatisfiedFilter', + filterFn: function (record) { + return record.get('satisfied') !== false; + } + }); + + me.listen({ + controller: { + '#State': { + conditionboundedchanged: me.boundedChanged, + conditionstatechanged: me.stateChanged + } + }, + component: { + 'nx-dev-conditions #showSatisfied': { + change: me.syncSatisfiedFilter + }, + 'nx-dev-conditions #showUnsatisfied': { + change: me.syncUnsatisfiedFilter + } + } + }); + }, + + /** + * @override + */ + onLaunch: function () { + var devPanelTab = this.getDevPanelTabs(); + + if (devPanelTab) { + devPanelTab.add({ xtype: 'nx-dev-conditions' }); + } + }, + + /** + * @override + */ + onDestroy: function () { + var me = this, + devConditionsPanel = me.getDevConditionsPanel(); + + if (devConditionsPanel) { + me.getDevPanelTabs().remove(devConditionsPanel); + } + }, + + syncSatisfiedFilter: function () { + var me = this, + value = me.getShowSatisfied().getValue(), + store = me.getStore('NX.store.dev.Condition'); + + if (value) { + store.removeFilter(me.excludeSatisfiedFilter); + } + else { + store.addFilter(me.excludeSatisfiedFilter); + } + }, + + syncUnsatisfiedFilter: function () { + var me = this, + value = me.getShowUnsatisfied().getValue(), + store = me.getStore('NX.store.dev.Condition'); + + if (value) { + store.removeFilter(me.excludeUnsatisfiedFilter); + } + else { + store.addFilter(me.excludeUnsatisfiedFilter); + } + }, + + boundedChanged: function (condition) { + var store = this.getStore('NX.store.dev.Condition'), + model; + + if (condition.bounded) { + store.add({ id: condition.id, condition: condition }); + store.filter(); + } + else { + model = store.getById(condition.id); + if (model) { + store.remove(model); + } + } + }, + + stateChanged: function (condition) { + var store = this.getStore('NX.store.dev.Condition'), + model = store.getById(condition.id); + + if (model) { + model.set('satisfied', condition.isSatisfied()); + model.commit(); + store.filter(); + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Developer.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Developer.js new file mode 100644 index 0000000000000000000000000000000000000000..5db5cc57e19267a7cbc68d961fe73bd8ef466a1c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Developer.js @@ -0,0 +1,241 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX, console*/ + +/** + * Developer controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.dev.Developer', { + extend: 'NX.app.Controller', + requires: [ + 'Ext.state.Manager', + 'NX.State', + 'NX.Messages' + ], + + views: [ + 'dev.Panel', + 'dev.Tests', + 'dev.Styles', + 'dev.Icons', + 'dev.Features', + 'dev.State', + 'dev.Stores', + 'dev.Logging' + ], + + refs: [ + { + ref: 'branding', + selector: 'nx-header-branding' + }, + { + ref: 'developer', + selector: 'nx-dev-panel' + } + ], + + /** + * @protected + */ + init: function () { + var me = this; + + me.listen({ + controller: { + '#State': { + debugchanged: me.manageDeveloperPanel + } + }, + component: { + 'nx-dev-panel': { + afterrender: me.manageDeveloperPanel + }, + 'nx-dev-panel tool[type=maximize]': { + click: me.onMaximize + }, + + // Tests actions + 'nx-dev-tests button[action=testError]': { + click: me.testError + }, + 'nx-dev-tests button[action=testExtError]': { + click: me.testExtError + }, + 'nx-dev-tests button[action=testMessages]': { + click: me.testMessages + }, + 'nx-dev-tests button[action=toggleUnsupportedBrowser]': { + click: me.toggleUnsupportedBrowser + }, + 'nx-dev-tests button[action=showLicenseWarning]': { + click: me.showLicenseWarning + }, + 'nx-dev-tests button[action=showQuorumWarning]': { + click: me.showQuorumWarning + }, + 'nx-dev-tests button[action=clearLocalState]': { + click: me.clearLocalState + } + } + }); + }, + + /** + * @override + */ + onLaunch: function () { + var me = this; + Ext.each(Ext.ComponentQuery.query('nx-dev-panel'), function (panel) { + me.manageDeveloperPanel(panel); + }); + }, + + /** + * Show/Hide developer panel based on debug state. + * + * @private + * @param {Ext.Panel} developerPanel + */ + manageDeveloperPanel: function (developerPanel) { + var debug = NX.State.getValue('debug'); + + developerPanel = developerPanel || this.getDeveloper(); + + if (developerPanel) { + if (debug) { + developerPanel.show(); + } + else { + developerPanel.hide(); + } + } + }, + + /** + * Maximimze developer panel. + * + * @private + */ + onMaximize: function(tool) { + var panel = tool.up('nx-dev-panel'), + tabs = panel.down('tabpanel'), + win; + + panel.remove(tabs, false); + + win = Ext.create('Ext.window.Window', { + title: panel.title, + iconCls: panel.iconCls, + glyph: panel.glyph, + + maximized: true, + autoScroll: true, + closable: false, + + layout: 'fit', + items: tabs, + tools: [ + { + type: 'close', + handler: function () { + win.hide(panel, function () { + win.remove(tabs, false); + panel.add(tabs); + win.destroy(); + }); + } + } + ] + }); + + win.show(panel); + }, + + /** + * Attempts to call a object's method that doesn't exist to produce a low-level javascript error. + * + * @private + */ + testError: function () { + console.log_no_such_method(); + }, + + /** + * Raises an Ext.Error so we can see how that behaves. + * + * @private + */ + testExtError: function () { + Ext.Error.raise('simulated error'); + }, + + /** + * Adds messages for each of the major types to view styling, etc. + * + * @private + */ + testMessages: function () { + Ext.each(['default', 'primary', 'danger', 'warning', 'success'], function (type) { + NX.Messages.add({ + type: type, + text: 'test of ' + type + }); + }); + }, + + /** + * Toggle the unsupported browser application state. + * + * @private + */ + toggleUnsupportedBrowser: function() { + NX.State.setBrowserSupported(!NX.State.isBrowserSupported()); + }, + + /** + * Set state such that a License warning element is shown in the UI. + */ + showLicenseWarning : function() { + var me = this, + licenseWarnings = me.getController('NX.proui.controller.LicenseWarnings'); + + licenseWarnings.daysToWarn = 10000; + licenseWarnings.updateLicenseExpiryWarning(); + }, + + /** + * Modify state so that the database quorum warning is shown in the UI. + * + * @private + */ + showQuorumWarning: function () { + NX.State.setValue('quorum', { 'quorumPresent': false}); + }, + + /** + * Clear local browser state. + * + * @private + */ + clearLocalState: function() { + var provider = Ext.state.Manager.getProvider(); + // HACK: provider.state is private + Ext.Object.each(provider.state, function (key, value) { + provider.clear(key); + }); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Logging.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Logging.js new file mode 100644 index 0000000000000000000000000000000000000000..9788b7f2f60e0cf5dd65bd79fc02fdd0b1dbba7f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Logging.js @@ -0,0 +1,145 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Logging dev-panel controller. + * + * @since 3.0 + */ +Ext.define('NX.controller.dev.Logging', { + extend: 'NX.app.Controller', + requires: [ + 'Ext.util.Format', + 'NX.Windows' + ], + + stores: [ + 'LogEvent', + 'LogLevel' + ], + + refs: [ + { + ref: 'panel', + selector: 'nx-dev-logging' + } + ], + + /** + * @override + */ + init: function () { + var me = this; + + // helper to lookup sink by name via Logging controller + function sink(name) { + return me.getController('Logging').getSink(name); + } + + me.listen({ + component: { + 'nx-dev-logging button[action=clear]': { + click: function (button) { + me.getStore('LogEvent').removeAll(); + } + }, + + 'nx-dev-logging combobox[itemId=threshold]': { + afterrender: function (combo) { + combo.select(me.getController('Logging').getThreshold()); + }, + select: function (combo) { + me.getController('Logging').setThreshold(combo.getValue()); + } + }, + + 'nx-dev-logging checkbox[itemId=buffer]': { + afterrender: function (checkbox) { + checkbox.setValue(sink('store').enabled); + }, + change: function (checkbox) { + sink('store').setEnabled(checkbox.getValue()); + } + }, + + 'nx-dev-logging numberfield[itemId=bufferSize]': { + afterrender: function (field) { + field.setValue(sink('store').maxSize); + }, + + // update the max-size when enter or loss of focus + blur: function(field, event) { + sink('store').setMaxSize(field.getValue()); + }, + keypress: function (field, event) { + if (event.getKey() === event.ENTER) { + sink('store').setMaxSize(field.getValue()); + } + } + }, + + 'nx-dev-logging checkbox[itemId=console]': { + afterrender: function (checkbox) { + checkbox.setValue(sink('console').enabled); + }, + change: function (checkbox) { + sink('console').setEnabled(checkbox.getValue()); + } + }, + + 'nx-dev-logging checkbox[itemId=remote]': { + afterrender: function (checkbox) { + checkbox.setValue(sink('remote').enabled); + }, + change: function (checkbox) { + sink('remote').setEnabled(checkbox.getValue()); + } + }, + + 'nx-dev-logging button[action=export]': { + click: me.exportSelection + } + } + }); + }, + + /** + * Export selected rows to window. + * + * @private + */ + exportSelection: function() { + var win, + doc, + selected; + + win = NX.Windows.open('', '', 'width=640,height=480'); + if (win !== null) { + doc = win.document; + selected = Ext.Array.pluck(this.getPanel().getSelectionModel().getSelection(), 'data'); + + doc.write(''); + doc.write('' + Ext.util.Format.plural(selected.length, 'Logging Event') + ''); + doc.write(''); + doc.write('
');
+
+      Ext.Array.each(selected, function(data) {
+        doc.write(data.timestamp + ' ' + data.level + ' ' + data.logger + ' ' + data.message.join(' ') + '
'); + }); + + doc.write('
');
+      doc.write('');
+    }
+  }
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Permissions.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Permissions.js
new file mode 100644
index 0000000000000000000000000000000000000000..945eea137ffcb06cff845a3abdb6b7ef709c2196
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Permissions.js
@@ -0,0 +1,140 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext, NX*/
+
+/**
+ * Permissions developer panel controller.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.controller.dev.Permissions', {
+  extend: 'NX.app.Controller',
+  requires: [
+    'NX.Permissions'
+  ],
+
+  stores: [
+    'Permission'
+  ],
+  models: [
+    'Permission'
+  ],
+  views: [
+    'dev.Permissions'
+  ],
+
+  refs: [
+    {
+      ref: 'grid',
+      selector: 'nx-dev-permissions'
+    }
+  ],
+
+  /**
+   * @protected
+   */
+  init: function () {
+    var me = this;
+
+    me.getApplication().getIconController().addIcons({
+      'permission-granted': {
+        file: 'tick.png',
+        variants: ['x16', 'x32']
+      },
+      'permission-denied': {
+        file: 'cross.png',
+        variants: ['x16', 'x32']
+      }
+    });
+
+    me.listen({
+      component: {
+        'nx-dev-permissions': {
+          beforeedit: me.beforeEdit,
+          canceledit: me.cancelEdit,
+          validateedit: me.update,
+          selectionchange: me.onSelectionChange
+        },
+        'nx-dev-permissions button[action=add]': {
+          click: me.add
+        },
+        'nx-dev-permissions button[action=delete]': {
+          click: me.deleteModel
+        }
+      }
+    });
+  },
+
+  /**
+   * @private
+   */
+  add: function () {
+    var me = this,
+        grid = me.getGrid(),
+        editor = grid.getPlugin('editor'),
+        model = me.getPermissionModel().create();
+
+    editor.cancelEdit();
+    grid.getStore().insert(0, model);
+    editor.startEdit(model, 0);
+  },
+
+  /**
+   * @private
+   */
+  deleteModel : function () {
+    var grid = this.getGrid(),
+        editor = grid.getPlugin('editor');
+
+    editor.cancelEdit();
+    grid.getStore().remove(grid.getSelectionModel().getSelection());
+  },
+
+  /**
+   * @private
+   */
+  beforeEdit: function (editor, context) {
+    var idField = editor.editor.form.findField('id');
+
+    if (context.record.get('id')) {
+      idField.disable();
+    }
+    else {
+      idField.enable();
+    }
+  },
+
+  /**
+   * @private
+   */
+  cancelEdit: function (editor, context) {
+    if (!context.record.get('id')) {
+      context.store.remove(context.record);
+    }
+  },
+
+  /**
+   * @private
+   */
+  update: function (editor, context) {
+    context.record.set('permitted', context.newValues['permitted']);
+    context.record.commit();
+  },
+
+  onSelectionChange: function (selectionModel, records) {
+    var deleteButton = this.getGrid().down('button[action=delete]');
+
+    deleteButton.setDisabled(!records.length);
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Stores.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Stores.js
new file mode 100644
index 0000000000000000000000000000000000000000..07cbdc1c04c9436469e793516e9ad4c89f5911f9
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/controller/dev/Stores.js
@@ -0,0 +1,118 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * Stores developer panel controller.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.controller.dev.Stores', {
+  extend: 'NX.app.Controller',
+  requires: [
+    'Ext.data.StoreManager'
+  ],
+
+  refs: [
+    {
+      ref: 'stores',
+      selector: 'nx-dev-stores'
+    }
+  ],
+
+  /**
+   * @override
+   */
+  init: function () {
+    var me = this;
+
+    me.listen({
+      component: {
+        'nx-dev-stores combobox': {
+          change: me.onStoreSelected
+        },
+        'nx-dev-stores button[action=load]': {
+          click: me.loadStore
+        },
+        'nx-dev-stores button[action=clear]': {
+          click: me.clearStore
+        }
+      }
+    });
+  },
+
+  /**
+   * @private
+   */
+  onStoreSelected: function (combobox) {
+    var storeId = combobox.getValue(),
+        panel = this.getStores(),
+        grid = panel.down('grid'),
+        store, columns = [];
+
+    if (storeId) {
+      store = Ext.data.StoreManager.get(storeId);
+      if (store) {
+        Ext.each(store.model.getFields(), function (field) {
+          columns.push({
+            text: field.name,
+            dataIndex: field.name,
+            renderer: function(value) {
+              if (Ext.isObject(value) || Ext.isArray(value)) {
+                try {
+                  return Ext.encode(value);
+                }
+                catch (e) {
+                  console.error('Failed to encode value:', value, e);
+                }
+              }
+              return value;
+            }
+          });
+        });
+        panel.removeAll(true);
+        panel.add({
+          xtype: 'grid',
+          autoScroll: true,
+          store: store,
+          columns: columns
+        });
+      }
+    }
+  },
+
+  /**
+   * @private
+   */
+  loadStore: function () {
+    var panel = this.getStores(),
+        grid = panel.down('grid');
+
+    if (grid) {
+      grid.getStore().load();
+    }
+  },
+
+  /**
+   * @private
+   */
+  clearStore: function () {
+    var panel = this.getStores(),
+        grid = panel.down('grid');
+
+    if (grid) {
+      grid.getStore().removeAll();
+    }
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/SearchBox.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/SearchBox.js
new file mode 100644
index 0000000000000000000000000000000000000000..1ec4e6bab6a66a78023af5156100b8de49bbb573
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/SearchBox.js
@@ -0,0 +1,175 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * A search box.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.SearchBox', {
+  extend: 'Ext.form.field.Trigger',
+  alias: 'widget.nx-searchbox',
+  requires: [
+    'Ext.util.KeyNav'
+  ],
+
+  emptyText: 'search',
+  submitValue: false,
+
+  /**
+   * Number of milliseconds to trigger searching.
+   *
+   * @cfg {Number}
+   */
+  searchDelay: 1000,
+
+  // TODO: Only show clear trigger if we have text
+  trigger1Cls: 'nx-form-fa-times-circle-trigger',
+
+  /**
+   * @override
+   */
+  initComponent: function () {
+    var me = this;
+
+    Ext.apply(me, {
+      checkChangeBuffer: me.searchDelay
+    });
+
+    me.callParent(arguments);
+
+    me.on('change', me.onValueChange, me);
+
+    me.addEvents(
+        /**
+         * Fires before a search is performed.
+         *
+         * @event beforesearch
+         */
+        'beforesearch',
+
+        /**
+         * Fires when a search values was typed. Fires with a delay of **{@link #searchDelay}**.
+         *
+         * @event search
+         * @param {NX.ext.SearchBox} this search box
+         * @param {String} search value
+         */
+        'search',
+
+        /**
+         * Fires when a search value had been cleared.
+         *
+         * @event searchcleared
+         * @param {NX.ext.SearchBox} this search box
+         */
+        'searchcleared'
+    );
+  },
+
+  /**
+   * @override
+   */
+  initEvents: function () {
+    var me = this;
+
+    me.callParent();
+
+    me.keyNav = new Ext.util.KeyNav(me.inputEl, {
+      esc: {
+        handler: me.clearSearch,
+        scope: me,
+        defaultEventAction: false
+      },
+      enter: {
+        handler: me.onEnter,
+        scope: me,
+        defaultEventAction: false
+      },
+      scope: me,
+      forceKeyDown: true
+    });
+  },
+
+  /**
+   * Clear search.
+   *
+   * @private
+   */
+  onTrigger1Click: function () {
+    this.clearSearch();
+  },
+
+  /**
+   * Search on ENTER.
+   *
+   * @private
+   */
+  onEnter: function () {
+    var me = this;
+
+    me.search(me.getValue());
+  },
+
+  /**
+   * Trigger search.
+   *
+   * @private
+   */
+  onValueChange: function (trigger, value) {
+    var me = this;
+
+    if (value) {
+      me.search(value);
+    }
+    else {
+      me.clearSearch();
+    }
+    me.resetOriginalValue();
+  },
+
+  /**
+   * Search for value and fires a 'search' event.
+   *
+   * @public
+   * @param value to search for
+   */
+  search: function (value) {
+    var me = this;
+
+    if (value !== me.getValue()) {
+      me.setValue(value);
+    }
+    else {
+      if (me.fireEvent('beforesearch', me)) {
+        me.fireEvent('search', me, value);
+      }
+    }
+  },
+
+  /**
+   * Clears the search and fires a 'searchcleared' event.
+   *
+   * @public
+   */
+  clearSearch: function () {
+    var me = this;
+
+    if (me.getValue()) {
+      me.setValue(undefined);
+    }
+    me.fireEvent('searchcleared', me);
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/direct/RemotingProvider.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/direct/RemotingProvider.js
new file mode 100644
index 0000000000000000000000000000000000000000..986945431673fdbd579f2ec2b3cb5fd958fefb33
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/direct/RemotingProvider.js
@@ -0,0 +1,35 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ *  **{@link Ext.direct.RemotingProvider}** overrides.
+ *
+ *  @since 3.0
+ */
+Ext.define('NX.ext.direct.RemotingProvider', {
+  override: 'Ext.direct.RemotingProvider',
+
+  /**
+   * Avoid buffering if "enableBuffer" option is false.
+   *
+   * @override
+   */
+  queueTransaction: function(transaction) {
+    if (transaction.callbackOptions && transaction.callbackOptions.enableBuffer === false) {
+      this.sendRequest(transaction);
+      return;
+    }
+    this.callParent(arguments);
+  }
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/FieldContainer.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/FieldContainer.js
new file mode 100644
index 0000000000000000000000000000000000000000..b7b879b64803e9c49b0350c2dc732196f20796cd
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/FieldContainer.js
@@ -0,0 +1,37 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * **{@link Ext.form.FieldContainer}** override, that changes default label width.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.FieldContainer', {
+  override: 'Ext.form.FieldContainer',
+
+  labelAlign: 'top',
+  labelStyle: 'font-weight: bold;',
+  msgTarget: 'under',
+
+  initComponent: function () {
+    var me = this;
+
+    if (me.helpText) {
+      me.afterLabelTpl = '' + me.helpText + '';
+    }
+
+    me.callParent(arguments);
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/OptionalFieldSet.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/OptionalFieldSet.js
new file mode 100644
index 0000000000000000000000000000000000000000..0c6c6f551365c8c7a455f7b5f72853bf74d623ca
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/OptionalFieldSet.js
@@ -0,0 +1,108 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * A **{@link Ext.form.FieldSet}** that enable/disable contained items on expand/collapse.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.OptionalFieldSet', {
+  extend: 'Ext.form.FieldSet',
+  alias: 'widget.nx-optionalfieldset',
+  cls: 'nx-optionalfieldset',
+
+  /**
+   * @override
+   */
+  initComponent: function () {
+    var me = this;
+
+    me.on('add', me.setupMonitorOnChange, me);
+
+    me.callParent(arguments);
+
+    // When state changes, repeat the evaluation
+    me.on('collapse', me.enableContainedItems, me);
+    me.on('expand', me.enableContainedItems, me);
+    me.on('afterrender', me.enableContainedItems, me);
+  },
+
+  /**
+   * @private
+   */
+  enableContainedItems: function (container, enable) {
+    var me = this;
+
+    if (!Ext.isDefined(enable)) {
+      enable = !container.collapsed;
+    }
+
+    if (container.items) {
+      container.items.each(function (item) {
+        if (enable) {
+          if (!item.disabledOnCollapse && !item.isXType('container')) {
+            item.enable();
+          }
+          delete item.disabledOnCollapse;
+          if (item.isXType('nx-optionalfieldset')) {
+            if (item.collapsedOnCollapse === false) {
+              item.expand();
+            }
+            delete item.collapsedOnCollapse;
+          }
+        }
+        else {
+          if (!Ext.isDefined(item.disabledOnCollapse)) {
+            item.disabledOnCollapse = item.isDisabled();
+          }
+          if (!item.isXType('container')) {
+            item.disable();
+          }
+          if (item.isXType('nx-optionalfieldset')) {
+            if (!Ext.isDefined(item.collapsedOnCollapse)) {
+              item.collapsedOnCollapse = item.collapsed;
+            }
+            item.collapse();
+          }
+        }
+        if (!item.isXType('nx-optionalfieldset')) {
+          me.enableContainedItems(item, enable);
+        }
+        if (Ext.isFunction(item.validate)) {
+          item.validate();
+        }
+      });
+    }
+  },
+
+  /**
+   * @private
+   * Watch for change events for contained components in order to automatically expand the toggle/checkbox.
+   */
+  setupMonitorOnChange: function(container, component) {
+    var me = this;
+
+    if (me === container) {
+      me.mon(component, 'change', function(field, value) {
+        if (value && me.collapsed) {
+          me.expand();
+          if (me.checkboxCmp) {
+            me.checkboxCmp.resetOriginalValue();
+          }
+        }
+      });
+    }
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/action/DirectLoad.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/action/DirectLoad.js
new file mode 100644
index 0000000000000000000000000000000000000000..cb3c142dda21a9f48cc4ed4d63a0b740ab03f9ff
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/action/DirectLoad.js
@@ -0,0 +1,35 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * **{@link Ext.form.action.DirectLoad}** overrides.
+ *
+ * See: https://support.sencha.com/index.php#ticket-17182
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.action.DirectLoad', {
+  override: 'Ext.form.action.DirectLoad',
+
+  /**
+   * @override
+   * Bail out if form was already destroyed.
+   */
+  onComplete: function () {
+    if (!this.form.isDestroyed) {
+      this.callParent(arguments);
+    }
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/action/DirectSubmit.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/action/DirectSubmit.js
new file mode 100644
index 0000000000000000000000000000000000000000..05f6d62ec47048cdb73f96938bf29014600b826c
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/action/DirectSubmit.js
@@ -0,0 +1,79 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext, NX*/
+
+/**
+ * **{@link Ext.form.action.DirectSubmit}** overrides (see inline comments marked with <override/>
+ *
+ * See: https://support.sencha.com/index.php#ticket-16118
+ *
+ * See: https://support.sencha.com/index.php#ticket-16102
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.action.DirectSubmit', {
+  override: 'Ext.form.action.DirectSubmit',
+
+  submitEmptyText: false,
+
+  doSubmit: function () {
+    var me = this,
+        form = me.form,
+        api = form.api,
+        fn = api.submit,
+        callback = Ext.Function.bind(me.onComplete, me),
+        formInfo = me.buildForm(),
+        options, formEl;
+
+    if (!Ext.isFunction(fn)) {
+      //
+      var fnName = fn;
+      //
+
+      api.update = fn = Ext.direct.Manager.parseMethod(fn);
+      // avoid cleanup as that resets values of file upload fields
+      //me.cleanup(formInfo);
+      //
+
+      //
+      if (!Ext.isFunction(fn)) {
+        Ext.Error.raise('Cannot resolve Ext.Direct API method ' + fnName);
+      }
+      //
+    }
+
+    if (me.timeout || form.timeout) {
+      options = {
+        timeout: me.timeout * 1000 || form.timeout * 1000
+      };
+    }
+
+    // call using field values if direct function formHandler = false
+    //fn.call(NX.global, formInfo.formEl, callback, me, options);
+    if (fn.directCfg.method.formHandler) {
+      formEl = formInfo.formEl;
+    }
+    else {
+      formEl = me.getParams(true);
+      Ext.Object.each(formEl, function (key, value) {
+        if (Ext.typeOf(value) === 'date') {
+          formEl[key] = Ext.Date.format(value, 'Y-m-d\\TH:i:s.uP');
+        }
+      });
+    }
+    fn.call(NX.global, formEl, callback, me, options);
+    //
+    me.cleanup(formInfo);
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Base.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Base.js
new file mode 100644
index 0000000000000000000000000000000000000000..0f00195deba545c6ccb4c5cf85aeba0af807a0da
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Base.js
@@ -0,0 +1,62 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * **{@link Ext.form.field.Base}** override, that changes default label width.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.field.Base', {
+  override: 'Ext.form.field.Base',
+
+  // FIXME: This is not the best way to ensure that forms are limited width
+  width: 600,
+  labelAlign: 'top',
+  labelStyle: 'font-weight: bold;',
+  msgTarget: 'under',
+
+  /**
+   * @cfg {boolean} [hideIfUndefined=false]
+   * If field should auto hide in case it has no value. Functionality applies only for read only field.
+   */
+  hideIfUndefined: false,
+
+  // used to track if helpText has already been placed
+  isHelpTextPlaced: false,
+
+  initComponent: function () {
+    var me = this;
+
+    if (me.helpText && !me.isHelpTextPlaced) {
+      me.beforeSubTpl = '' + me.helpText + '';
+      me.isHelpTextPlaced = true;
+    }
+
+    me.callParent(arguments);
+  },
+
+  setValue: function (value) {
+    var me = this;
+    me.callParent(arguments);
+    if (me.readOnly && me.hideIfUndefined) {
+      if (value) {
+        me.show();
+      }
+      else {
+        me.hide();
+      }
+    }
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Checkbox.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Checkbox.js
new file mode 100644
index 0000000000000000000000000000000000000000..2906f6eef5c9de7c4db82d469fc7f57dd1f0294b
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Checkbox.js
@@ -0,0 +1,36 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * **{@link Ext.form.field.Checkbox}** override, that changes default width overridden in {@link Ext.form.field.Base}.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.field.Checkbox', {
+  override: 'Ext.form.field.Checkbox',
+
+  width: undefined,
+
+  initComponent: function () {
+    var me = this;
+
+    if (me.helpText && ! me.isHelpTextPlaced) {
+      me.boxLabel = '' + me.helpText + '';
+      me.isHelpTextPlaced = true;
+    }
+
+    me.callParent(arguments);
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/ClearableComboBox.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/ClearableComboBox.js
new file mode 100644
index 0000000000000000000000000000000000000000..c2dbc4467d8b69d9242a5f2b1e3a8ce649ef97e5
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/ClearableComboBox.js
@@ -0,0 +1,35 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * A **{@link Ext.form.field.ComboBox}** with an extra button that allows value to be cleared.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.field.ClearableComboBox', {
+  extend: 'Ext.form.field.ComboBox',
+  alias: 'widget.nx-clearablecombobox',
+
+  // TODO: Only show clear trigger if we have text
+  trigger2Cls: Ext.baseCSSPrefix + 'form-clear-trigger',
+
+  /**
+   * @private
+   * Clear value.
+   */
+  onTrigger2Click: function () {
+    this.reset();
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/DateDisplayField.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/DateDisplayField.js
new file mode 100644
index 0000000000000000000000000000000000000000..e194053bcfb0985ccd5e0cfb8ae129032e9d807e
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/DateDisplayField.js
@@ -0,0 +1,41 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * An **{@link Ext.form.field.Display}** that converts a date in ISO-8601 format to a date before display.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.field.DateDisplayField', {
+  extend: 'Ext.form.field.Display',
+  alias: 'widget.nx-datedisplayfield',
+
+  config: {
+    /**
+     * @cfg {String} Format for Date output, defaults to ISO 8601.
+     */
+    format: 'c'
+  },
+  
+  /**
+   * @override
+   */
+  setValue: function (value) {
+    if (value) {
+      arguments[0] = Ext.Date.format(Ext.Date.parse(value, 'c'), this.format);
+    }
+    this.callParent(arguments);
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Display.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Display.js
new file mode 100644
index 0000000000000000000000000000000000000000..07bd052ac379632eff4aec3ebd23fb63877af56a
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Display.js
@@ -0,0 +1,25 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * **{@link Ext.form.field.Display}** override, that changes default width overridden in {@link Ext.form.field.Base}.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.field.Display', {
+  override: 'Ext.form.field.Display',
+
+  width: undefined
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Email.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Email.js
new file mode 100644
index 0000000000000000000000000000000000000000..d8c515800aee68e7a67ae01372901c79540e26e6
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Email.js
@@ -0,0 +1,27 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * An email **{@link Ext.form.field.Text}**.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.field.Email', {
+  extend: 'Ext.form.field.Text',
+  alias: 'widget.nx-email',
+
+  vtype: 'nx-email',
+  maxLength: 254
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Hostname.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Hostname.js
new file mode 100644
index 0000000000000000000000000000000000000000..772697815d52f2f9cd39db867c80384d5918266c
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Hostname.js
@@ -0,0 +1,26 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * A hostname **{@link Ext.form.field.Text}**.
+ *
+ * @since 3.3
+ */
+Ext.define('NX.ext.form.field.Hostname', {
+  extend: 'Ext.form.field.Text',
+  alias: 'widget.nx-hostname',
+
+  vtype: 'nx-hostname',
+  maskRe: /\S/
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/ItemOrderer.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/ItemOrderer.js
new file mode 100644
index 0000000000000000000000000000000000000000..7e5de8687d2db199ec2ec58e4cd081e9e0e17e52
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/ItemOrderer.js
@@ -0,0 +1,315 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * An field that allows ordering records in a store.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.field.ItemOrderer', {
+  extend: 'Ext.ux.form.MultiSelect',
+  alias: 'widget.nx-itemorderer',
+  requires: [
+    'Ext.button.Button',
+    'Ext.ux.form.MultiSelect'
+  ],
+
+  /**
+   * @cfg {Boolean} [hideNavIcons=false] True to hide the navigation icons
+   */
+  hideNavIcons: false,
+
+  /**
+   * @cfg {Array} buttons Defines the set of buttons that should be displayed on the right of MultiSelect field.
+   * Defaults to ['top', 'up', 'down', 'bottom']. These names are used to look up the button text labels in
+   * {@link #buttonsText} and the glyph in {@link #buttonsGlyph}.
+   * This can be overridden with a custom Array to change which buttons are displayed or their order.
+   */
+  buttons: ['top', 'up', 'down', 'bottom'],
+
+  /**
+   * @cfg {Object} buttonsText The tooltips for the {@link #buttons}.
+   * Labels for buttons.
+   */
+  buttonsText: {
+    top: 'Move to Top',
+    up: 'Move Up',
+    down: 'Move Down',
+    bottom: 'Move to Bottom'
+  },
+
+  /**
+   * @cfg {Object} buttonsGlyph The glyphs for the {@link #buttons}.
+   * Glyphs for buttons.
+   */
+  buttonsGlyph: {
+    top: 'xf102@FontAwesome' /* fa-angle-double-up */,
+    up: 'xf106@FontAwesome' /* fa-angle-up */,
+    down: 'xf107@FontAwesome' /* fa-angle-down */,
+    bottom: 'xf103@FontAwesome' /* fa-angle-double-down */
+  },
+
+  layout: {
+    type: 'hbox',
+    align: 'stretch'
+  },
+
+  initComponent: function () {
+    var me = this;
+
+    me.ddGroup = me.id + '-dd';
+    me.callParent();
+
+    // bindStore must be called after the orderField has been created because
+    // it copies records from our configured Store into the orderField's Store
+    me.bindStore(me.store);
+  },
+
+  setupItems: function () {
+    var me = this;
+
+    me.orderField = Ext.create('Ext.ux.form.MultiSelect', {
+      // We don't want the multiselects themselves to act like fields,
+      // so override these methods to prevent them from including
+      // any of their values
+      submitValue: false,
+      getSubmitData: function () {
+        return null;
+      },
+      getModelData: function () {
+        return null;
+      },
+      flex: 1,
+      dragGroup: me.ddGroup,
+      dropGroup: me.ddGroup,
+      title: me.title,
+      store: {
+        model: me.store.model,
+        data: []
+      },
+      displayField: me.displayField,
+      valueField: me.valueField,
+      disabled: me.disabled,
+      listeners: {
+        boundList: {
+          scope: me,
+          drop: me.syncValue
+        }
+      }
+    });
+
+    return [
+      me.orderField,
+      {
+        xtype: 'container',
+        margins: '0 4',
+        layout: {
+          type: 'vbox',
+          pack: 'center'
+        },
+        items: me.createButtons()
+      }
+    ];
+  },
+
+  createButtons: function () {
+    var me = this,
+        buttons = [];
+
+    if (!me.hideNavIcons) {
+      Ext.Array.forEach(me.buttons, function (name) {
+        buttons.push({
+          xtype: 'button',
+          tooltip: me.buttonsText[name],
+          glyph: me.buttonsGlyph[name],
+          handler: me['on' + Ext.String.capitalize(name) + 'BtnClick'],
+          navBtn: true,
+          scope: me,
+          margin: '4 0 0 0'
+        });
+      });
+    }
+    return buttons;
+  },
+
+  /**
+   * Get the selected records from the specified list.
+   *
+   * Records will be returned *in store order*, not in order of selection.
+   * @param {Ext.view.BoundList} list The list to read selections from.
+   * @return {Ext.data.Model[]} The selected records in store order.
+   *
+   */
+  getSelections: function (list) {
+    var store = list.getStore();
+
+    return Ext.Array.sort(list.getSelectionModel().getSelection(), function (a, b) {
+      a = store.indexOf(a);
+      b = store.indexOf(b);
+
+      if (a < b) {
+        return -1;
+      }
+      else if (a > b) {
+        return 1;
+      }
+      return 0;
+    });
+  },
+
+  onTopBtnClick: function () {
+    var me = this,
+        list = me.orderField.boundList,
+        store = list.getStore(),
+        selected = me.getSelections(list);
+
+    store.suspendEvents();
+    store.remove(selected, true);
+    store.insert(0, selected);
+    store.resumeEvents();
+    list.refresh();
+    me.syncValue();
+    list.getSelectionModel().select(selected);
+  },
+
+  onBottomBtnClick: function () {
+    var me = this,
+        list = me.orderField.boundList,
+        store = list.getStore(),
+        selected = me.getSelections(list);
+
+    store.suspendEvents();
+    store.remove(selected, true);
+    store.add(selected);
+    store.resumeEvents();
+    list.refresh();
+    me.syncValue();
+    list.getSelectionModel().select(selected);
+  },
+
+  onUpBtnClick: function () {
+    var me = this,
+        list = me.orderField.boundList,
+        store = list.getStore(),
+        selected = me.getSelections(list),
+        rec,
+        i = 0,
+        len = selected.length,
+        index = 0;
+
+    // Move each selection up by one place if possible
+    store.suspendEvents();
+    for (; i < len; ++i, index++) {
+      rec = selected[i];
+      index = Math.max(index, store.indexOf(rec) - 1);
+      store.remove(rec, true);
+      store.insert(index, rec);
+    }
+    store.resumeEvents();
+    list.refresh();
+    me.syncValue();
+    list.getSelectionModel().select(selected);
+  },
+
+  onDownBtnClick: function () {
+    var me = this,
+        list = me.orderField.boundList,
+        store = list.getStore(),
+        selected = me.getSelections(list),
+        rec,
+        i = selected.length - 1,
+        index = store.getCount() - 1;
+
+    // Move each selection down by one place if possible
+    store.suspendEvents();
+    for (; i > -1; --i, index--) {
+      rec = selected[i];
+      index = Math.min(index, store.indexOf(rec) + 1);
+      store.remove(rec, true);
+      store.insert(index, rec);
+    }
+    store.resumeEvents();
+    list.refresh();
+    me.syncValue();
+    list.getSelectionModel().select(selected);
+  },
+
+  syncValue: function () {
+    var me = this;
+    me.mixins.field.setValue.call(me, me.setupValue(me.orderField.store.getRange()));
+  },
+
+  setValue: function (value) {
+    // do nothing as we always show all records, unselected
+  },
+
+  onBindStore: function (store) {
+    var me = this;
+
+    if (me.orderField) {
+      if (store.getCount()) {
+        me.populateStore(store);
+      }
+      else {
+        me.store.on('load', me.populateStore, me);
+      }
+    }
+  },
+
+  populateStore: function (store) {
+    var me = this,
+        orderStore = me.orderField.store;
+
+    me.storePopulated = true;
+
+    orderStore.removeAll();
+    orderStore.add(store.getRange());
+    me.syncValue();
+
+    orderStore.fireEvent('load', orderStore);
+  },
+
+  onEnable: function () {
+    var me = this;
+
+    me.callParent();
+    me.orderField.enable();
+
+    Ext.Array.forEach(me.query('[navBtn]'), function (btn) {
+      btn.enable();
+    });
+  },
+
+  onDisable: function () {
+    var me = this;
+
+    me.callParent();
+    me.orderField.disable();
+
+    Ext.Array.forEach(me.query('[navBtn]'), function (btn) {
+      btn.disable();
+    });
+  },
+
+  onDestroy: function () {
+    var me = this;
+
+    if (me.store) {
+      me.store.un('load', me.populateStore, me);
+    }
+    me.bindStore(null);
+    me.callParent();
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/ItemSelector.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/ItemSelector.js
new file mode 100644
index 0000000000000000000000000000000000000000..9d251df6e53aae4f56b4045561730c9b38c46c9f
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/ItemSelector.js
@@ -0,0 +1,202 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * Extension of Ext.ux.form.ItemSelector to allow better control over button configurations.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.field.ItemSelector', {
+  extend: 'Ext.ux.form.ItemSelector',
+  alias: 'widget.nx-itemselector',
+  requires: [
+    'Ext.ux.form.MultiSelect',
+    'NX.I18n'
+  ],
+
+  // FIXME: This is not the best way to ensure that forms are limited width
+  width: 600,
+  height: 253,
+
+  disabledCls: 'nx-itemselector-disabled',
+  invalidCls: 'nx-invalid',
+
+  /**
+   * Override super *private* impl so we can control the button configuration.
+   *
+   * @override
+   * @private
+   */
+  createButtons: function () {
+    var me = this,
+        buttons = me.callSuper();
+
+    if (!me.hideNavIcons) {
+      var i = 0;
+      Ext.Array.forEach(me.buttons, function (name) {
+        me.customizeButton(name, buttons[i++]);
+      });
+    }
+
+    return buttons;
+  },
+
+  /**
+   * Replace iconCls with glyph.
+   *
+   * @private
+   *
+   * @param name
+   * @param button
+   */
+  customizeButton: function (name, button) {
+    // remove icon
+    delete button.iconCls;
+
+    // replace with glyph
+    switch (name) {
+      case 'top':
+        button.glyph = 'xf102@FontAwesome'; // fa-angle-double-up
+        break;
+      case 'up':
+        button.glyph = 'xf106@FontAwesome'; // fa-angle-up
+        break;
+      case 'add':
+        button.glyph = 'xf105@FontAwesome'; // fa-angle-right
+        break;
+      case 'remove':
+        button.glyph = 'xf104@FontAwesome'; // fa-angle-left
+        break;
+      case 'down':
+        button.glyph = 'xf107@FontAwesome'; // fa-angle-down
+        break;
+      case 'bottom':
+        button.glyph = 'xf103@FontAwesome'; // fa-angle-double-down
+        break;
+    }
+  },
+
+  createList: function (title) {
+    var me = this,
+        tbar;
+
+    // only create filter box for from field
+    if (!me.fromField) {
+      tbar = {
+        xtype: 'nx-searchbox',
+        emptyText: NX.I18n.get('Form_Field_ItemSelector_Empty'),
+        searchDelay: 200,
+        listeners: {
+          search: me.onSearch,
+          searchcleared: me.onSearchCleared,
+          scope: me
+        }
+      };
+    }
+
+    return Ext.create('Ext.ux.form.MultiSelect', {
+      // We don't want the multiselects themselves to act like fields,
+      // so override these methods to prevent them from including
+      // any of their values
+      submitValue: false,
+      isDirty: Ext.emptyFn,
+      getSubmitData: function () {
+        return null;
+      },
+      getModelData: function () {
+        return null;
+      },
+      cls: 'nx-multiselect',
+      flex: 1,
+      dragGroup: me.ddGroup,
+      dropGroup: me.ddGroup,
+      title: title,
+      store: {
+        model: me.store.model,
+        sorters: me.store.getSorters(),
+        data: []
+      },
+      displayField: me.displayField,
+      valueField: me.valueField,
+      disabled: me.disabled,
+      listeners: {
+        boundList: {
+          scope: me,
+          itemdblclick: me.onItemDblClick,
+          drop: me.syncValue
+        }
+      },
+      tbar: tbar
+    });
+  },
+
+  /**
+   * Ext.ux.form.ItemSelector defers setting value if store is not loaded,
+   * which messes up the logic in Ext.form.Basic.setValues()
+   * when Ext.form.Basic.trackResetOnLoad is true.
+   *
+   * @override
+   */
+  setValue: function(value) {
+    this.callParent(arguments);
+
+    // HACK: force original value to reset, to prevent always dirty forms when store has not loaded when form initially sets values.
+    this.resetOriginalValue();
+  },
+
+  // HACK: avoid exceptions when the store is reloaded
+  populateFromStore: function (store) {
+    var me = this,
+        fromStore = me.fromField.store;
+
+    if (fromStore) {
+      fromStore.removeAll();
+    }
+    me.callParent(arguments);
+  },
+
+  /**
+   * @private
+   */
+  onSearch: function (searchbox, value) {
+    var me = this;
+
+    me.fromField.store.filter({ id: 'filter', filterFn: function (model) {
+      var stringValue = model.get(me.displayField);
+      if (stringValue) {
+        stringValue = stringValue.toString();
+        return stringValue.toLowerCase().indexOf(value.toLowerCase()) !== -1;
+      }
+      return false;
+    }});
+  },
+
+  /**
+   * @private
+   */
+  onSearchCleared: function () {
+    this.fromField.store.clearFilter();
+  },
+
+  // HACK: Looks like original item selector forgot to unbind from store which results in NPEs in #populateFromStore
+  onDestroy: function () {
+    var me = this;
+
+    if (me.store) {
+      me.store.un('load', me.populateFromStore, me);
+    }
+    this.callParent();
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Number.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Number.js
new file mode 100644
index 0000000000000000000000000000000000000000..c0498dc299a542f89d6a2c53daac8cc5366304ab
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Number.js
@@ -0,0 +1,25 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * **{@link Ext.form.field.Number}** override, that disables mouse wheel interactions for all numeric fields.
+ *
+ * @since 3.1.0
+ */
+Ext.define('NX.ext.form.field.Number', {
+  override: 'Ext.form.field.Number',
+
+ mouseWheelEnabled: false
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Password.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Password.js
new file mode 100644
index 0000000000000000000000000000000000000000..3d0649091fa72dcc6cb70a4899a144c866fa918c
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Password.js
@@ -0,0 +1,25 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext, NX*/
+
+/**
+ * An password **{@link Ext.form.field.Text}**.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.field.Password', {
+  extend: 'Ext.form.field.Text',
+  alias: 'widget.nx-password',
+
+  inputType: 'password'
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/RegExp.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/RegExp.js
new file mode 100644
index 0000000000000000000000000000000000000000..8b16ee38eac09c704eb81babfb053b4c79f1bf70
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/RegExp.js
@@ -0,0 +1,34 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * An regular expression **{@link Ext.form.field.Text}**.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.field.RegExp', {
+  extend: 'Ext.form.field.Text',
+  alias: 'widget.nx-regexp',
+
+  validator: function (value) {
+    try {
+      new RegExp(value);
+    }
+    catch (err) {
+      return err.message;
+    }
+    return true;
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Url.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Url.js
new file mode 100644
index 0000000000000000000000000000000000000000..23511c94a28cfc3957b304bb2a38636d4b8e0e45
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/Url.js
@@ -0,0 +1,54 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext, NX*/
+
+/**
+ * An URL **{@link Ext.form.field.Text}**.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.field.Url', {
+  extend: 'Ext.form.field.Text',
+  alias: 'widget.nx-url',
+  requires: [ 'NX.util.Validator' ],
+
+  validator: function (value) {
+    var valid = NX.util.Validator.isURL(value, {
+      protocols: ['http', 'https'],
+      require_protocol: true,
+      allow_underscores: true,
+      allow_blank: this.allowBlank
+    });
+
+    if (valid || isHandledByBlankValidation(value)) {
+      return true;
+    }
+
+    function isHandledByBlankValidation(value) {
+      return (Ext.isEmpty(value) && !this.allowBlank);
+    }
+
+    return 'This field should be a URL in the format "http:/' + '/www.example.com"';
+  },
+
+  useTrustStore: function (field) {
+    if (Ext.String.startsWith(field.getValue(), 'https://')) {
+      return {
+        name: 'useTrustStoreFor' + Ext.String.capitalize(field.getName()),
+        url: field
+      };
+    }
+    return undefined;
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/ValueSet.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/ValueSet.js
new file mode 100644
index 0000000000000000000000000000000000000000..03247676b3aa600096962a821dacfe050525b714
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/form/field/ValueSet.js
@@ -0,0 +1,404 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext, NX*/
+
+/**
+ * A form field that allows managing multiple values.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.form.field.ValueSet', {
+  extend: 'Ext.form.FieldContainer',
+    alias: 'widget.nx-valueset',
+  requires: [
+    'Ext.data.SequentialIdGenerator',
+    'Ext.data.Store',
+    'Ext.util.KeyNav',
+    'NX.Icons'
+  ],
+  mixins: {
+    field: 'Ext.form.field.Field'
+  },
+
+  statics: {
+    idGenerator: Ext.create('Ext.data.SequentialIdGenerator'),
+    generateId: function () {
+      return 'nx-valueset-valuefield-' + NX.ext.form.field.ValueSet.idGenerator.generate();
+    }
+  },
+
+  // FIXME: This is not the best way to ensure that forms are limited width
+  width: 600,
+
+  /**
+   * @cfg {Number} [minValues=0] Minimum number of selections allowed.
+   */
+  minValues: 0,
+
+  /**
+   * @cfg {Number} [maxValues=Number.MAX_VALUE] Maximum number of values allowed.
+   */
+  maxValues: Number.MAX_VALUE,
+
+  /**
+   * @cfg {String} [blankText="This field is required"] Default text displayed when the control contains no values.
+   */
+  blankText: 'At least one value is required',
+
+  /**
+   * @cfg {String} [minValuesText="Minimum {0} value(s) required"]
+   * Validation message displayed when {@link #minValues} is not met.
+   * The {0} token will be replaced by the value of {@link #minValues}.
+   */
+  minValuesText: 'Minimum {0} value(s) required',
+
+  /**
+   * @cfg {String} [maxValuesText="Maximum {0} value(s) allowed"]
+   * Validation message displayed when {@link #maxValues} is not met
+   * The {0} token will be replaced by the value of {@link #maxValues}.
+   */
+  maxValuesText: 'Maximum {0} values(s) allowed',
+
+  /**
+   * @cfg {Boolean} [allowBlank=true] `false` to require at least one value, `true` to allow no value.
+   */
+  allowBlank: true,
+
+  /**
+   * @cfg {Boolean} [sorted=false] `true` to sort values, `false` to use adding order
+   */
+  sorted: false,
+
+  /**
+   * @cfg {Ext.form.field.Field} [input=true] Field to be used to add values. If not defined an
+   * {@link Ext.form.field.Text} will be used.
+   */
+  input: undefined,
+
+  /**
+   * The default text to place into an empty field.
+   * See {@link Ext.form.field.Text#emptyText}
+   *
+   * @cfg {String} emptyText
+   */
+  emptyText: undefined,
+
+  converter: {
+    toValues: undefined,
+    fromValues: undefined
+  },
+
+  /**
+   * @cfg {String} [glyphAddButton="xf055@FontAwesome"]
+   */
+  glyphAddButton: 'xf055@FontAwesome' /* fa-plus-circle */,
+
+  /**
+   * @cfg {String} [glyphDeleteButton="xf056@FontAwesome"]
+   */
+  glyphDeleteButton: 'xf056@FontAwesome' /* fa-minus-circle */,
+
+  /**
+   * @private {Ext.data.Store} Stores managed values
+   */
+  store: undefined,
+
+  /**
+   * @override
+   */
+  initComponent: function () {
+    var me = this,
+        valueFieldId = NX.ext.form.field.ValueSet.generateId();
+
+    if (!Ext.isDefined(me.input)) {
+      me.input = {
+        xtype: 'textfield'
+      };
+    }
+    Ext.apply(me.input, {
+      valueFieldId: valueFieldId,
+      submitValue: false,
+      isFormField: false,
+      flex: 1,
+      inputFor: me.name
+    });
+    if (me.emptyText) {
+      me.input.emptyText = me.emptyText;
+    }
+    if (!me.converter) {
+      me.converter = {};
+    }
+    if (!me.converter.toValues || !Ext.isFunction(me.converter.toValues)) {
+      me.converter.toValues = function (values) {
+        return values;
+      };
+    }
+    if (!me.converter.fromValues || !Ext.isFunction(me.converter.fromValues)) {
+      me.converter.fromValues = function (values) {
+        return values;
+      };
+    }
+
+    me.items = [
+      {
+        xtype: 'panel',
+        layout: 'hbox',
+        items: [
+          me.input,
+          {
+            xtype: 'button',
+            listeners: {
+              click: function() {
+                // Add an item to the list of values
+                me.addValue();
+
+                // Unsticky the input field’s error message
+                me.items.items[0].items.items[0].resumeEvents();
+
+                if (me.items.items[0].items.items[0].isValid()) {
+                  me.validate();
+                }
+              },
+              mouseover: function() {
+                // Sticky the input field’s error message
+                me.items.items[0].items.items[0].suspendEvents(false);
+              },
+              mouseout: function() {
+                // Unsticky the input field’s error message
+                me.items.items[0].items.items[0].resumeEvents();
+
+                if (me.items.items[0].items.items[0].isValid()) {
+                  me.validate();
+                }
+              },
+              scope: me
+            },
+            ui: 'nx-plain',
+            glyph: me.glyphAddButton
+          }
+        ]
+      },
+      me.values = {
+        xtype: 'grid',
+        hideHeaders: true,
+        ui: 'nx-borderless',
+        columns: [
+          { text: 'Value', dataIndex: 'value', flex: 1 },
+          {
+            xtype: 'actioncolumn',
+            width: 25,
+            items: [
+              {
+                icon: NX.Icons.url('cross', 'x16'),
+                tooltip: 'Delete',
+                handler: function (grid, rowIndex) {
+                  me.removeValue(rowIndex);
+                }
+              }
+            ]
+          }
+        ],
+        store: me.store = Ext.create('Ext.data.Store', {
+          storeId: valueFieldId,
+          fields: ['value'],
+          idProperty: 'value',
+          sorters: me.sorted ? { property: 'value', direction: 'ASC' } : undefined
+        })
+      }
+    ];
+
+    me.callParent(arguments);
+
+    me.on('afterrender', function () {
+      me.valueField = me.down('component[valueFieldId=' + valueFieldId + ']');
+      me.mon(me.valueField, 'blur', function (input) {
+        if (input.isValid()) {
+          me.validate();
+        }
+      });
+      me.mon(me.valueField, 'change', function (input, newValue) {
+        if (!newValue || newValue === '') {
+          me.validate();
+        }
+      });
+      Ext.create('Ext.util.KeyNav', me.valueField.el, {
+        enter: me.addValue,
+        scope: me
+      });
+    });
+  },
+
+  /**
+   * @private
+   * Add value form input field to set of values.
+   */
+  addValue: function () {
+    var me = this,
+        valueToAdd;
+
+    if (!me.valueField.isValid()) {
+      return;
+    }
+
+    valueToAdd = me.valueField.getValue();
+
+    if (valueToAdd && me.store.find('value', valueToAdd) === -1) {
+      me.store.add({ value: valueToAdd });
+      me.valueField.setValue(undefined);
+    }
+
+    me.valueField.focus();
+
+    me.syncValue();
+  },
+
+  /**
+   * @private
+   * Remove value form input field to set of values.
+   * @param {Number} rowIndex of value to be deleted
+   */
+  removeValue: function (rowIndex) {
+    var me = this;
+
+    me.store.removeAt(rowIndex);
+    me.syncValue();
+  },
+
+  getSubmitData: function () {
+    var me = this,
+        data = null,
+        val;
+
+    if (!me.disabled && me.submitValue && !me.isFileUpload()) {
+      val = me.getSubmitValue();
+      if (val !== null) {
+        data = {};
+        data[me.getName()] = val;
+      }
+    }
+    return data;
+  },
+
+  getSubmitValue: function () {
+    return this.getValue();
+  },
+
+  isValid: function () {
+    var me = this,
+        disabled = me.disabled,
+        validate = me.forceValidation || !disabled;
+
+    return validate ? me.validateValue() : disabled;
+  },
+
+  validateValue: function () {
+    var me = this,
+        errors = me.getErrors(),
+        isValid = Ext.isEmpty(errors);
+
+    if (isValid) {
+      me.clearInvalid();
+    }
+    else {
+      me.markInvalid(errors);
+    }
+
+    return isValid;
+  },
+
+  markInvalid: function (errors) {
+    this.items.items[0].items.items[0].markInvalid(errors);
+  },
+
+  getErrors: function () {
+    var me = this,
+        format = Ext.String.format,
+        errors = [],
+        numValues = me.store.getCount();
+
+    if (!me.allowBlank && numValues < 1) {
+      errors.push(me.blankText);
+    }
+    if (numValues < me.minValues) {
+      errors.push(format(me.minValuesText, me.minValues));
+    }
+    if (numValues > me.maxValues) {
+      errors.push(format(me.maxValuesText, me.maxValues));
+    }
+    return errors;
+  },
+
+  /**
+   * Clear any invalid styles/messages.
+   */
+  clearInvalid: function () {
+    // Clear the message and fire the 'valid' event
+    this.items.items[0].items.items[0].clearInvalid();
+  },
+
+  /**
+   * @inheritdoc Ext.form.field.Field#setValue
+   */
+  setValue: function (value) {
+    var me = this;
+
+    me.loadValues(value);
+    me.syncValue();
+  },
+
+  /**
+   * @private
+   * @returns {Array} store values as an array
+   */
+  getValues: function () {
+    var values = [];
+
+    this.store.each(function (model) {
+      values.push(model.get('value'));
+    });
+
+    return values;
+  },
+
+  /**
+   * @private
+   * Loads values into the store.
+   * @param value to be converted and loaded into store
+   */
+  loadValues: function (value) {
+    var me = this,
+        converted;
+
+    me.store.removeAll();
+    if (value) {
+      converted = me.converter.toValues(value);
+      Ext.each(converted, function (value) {
+        me.store.add({ value: value });
+      });
+    }
+  },
+
+  /**
+   * @private
+   * Synchronizes the submit value with the current state of the store.
+   */
+  syncValue: function () {
+    var me = this;
+    me.mixins.field.setValue.call(me, me.converter.fromValues(me.getValues()));
+  }
+
+}, function () {
+
+  this.borrow(Ext.form.field.Base, ['setError']);
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/Action.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/Action.js
new file mode 100644
index 0000000000000000000000000000000000000000..070d0efe66945d3e7c3b2e8398609d292bf609c2
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/Action.js
@@ -0,0 +1,36 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext, NX*/
+
+/**
+ * Customized actioncolumn.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.grid.column.Action', {
+  extend: 'Ext.grid.column.Action',
+  alias: 'widget.nx-actioncolumn',
+
+  /**
+   * @override
+   */
+  initComponent: function () {
+    var me = this;
+
+    me.handler = function (grid, ri, ci, item, e, record, row) {
+      me.fireEvent('actionclick', me, grid, ri, ci, item, record, row);
+    };
+
+    me.callParent();
+  }
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/CopyLink.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/CopyLink.js
new file mode 100644
index 0000000000000000000000000000000000000000..1b78795f9c21e8e755cf7ec4a8527b0babec7994
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/CopyLink.js
@@ -0,0 +1,60 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext, NX*/
+
+/**
+ * A {@link Ext.grid.column.Column} which renders its value as a link.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.grid.column.CopyLink', {
+  extend: 'Ext.grid.column.Column',
+  alias: ['widget.nx-copylinkcolumn'],
+  requires: [
+    'NX.util.Url'
+  ],
+
+  stateId: 'copylink',
+
+  constructor: function () {
+    var me = this;
+
+    me.listeners = {
+      click: function() {
+        // Prevent drilldown from triggering
+        return false;
+      }
+    };
+
+    me.callParent(arguments);
+  },
+
+  /**
+   * Renders value as a link.
+   */
+  defaultRenderer: function (value) {
+    if (value) {
+      value = value.replace(/\$baseUrl/, NX.util.Url.baseUrl);
+      return NX.util.Url.asCopyWidget(value);
+    }
+    return undefined;
+  },
+
+  /**
+   * @protected
+   */
+  target: function (value) {
+    return value;
+  }
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/Date.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/Date.js
new file mode 100644
index 0000000000000000000000000000000000000000..e2836df6baf36502f7f82381e69050c57f4ffbca
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/Date.js
@@ -0,0 +1,25 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * **{@link Ext.grid.column.Date}** override, that sets format.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.grid.column.Date', {
+  override: 'Ext.grid.column.Date',
+
+  format: 'Y-M-d H:i:s'
+
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/Icon.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/Icon.js
new file mode 100644
index 0000000000000000000000000000000000000000..25df5f7d0b79aebd65440184a13e2e405beb2078
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/Icon.js
@@ -0,0 +1,110 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext, NX*/
+
+/**
+ * Renders icon in a column.
+ *
+ * Icon must be registered first with NX.controller.Icon.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.grid.column.Icon', {
+  extend: 'Ext.grid.column.Column',
+  alias: 'widget.nx-iconcolumn',
+  requires: [
+    'Ext.DomHelper',
+    'NX.Icons'
+  ],
+
+  hideable: false,
+  sortable: false,
+  menuDisabled: true,
+  resizable: false,
+  draggable: false,
+  stateId: 'icon',
+
+  /**
+   * @cfg {String} iconVariant
+   */
+  /**
+   * @cfg {Number} iconHeight
+   */
+  /**
+   * @cfg {Number} iconWidth
+   */
+  /**
+   * @cfg {String} iconNamePrefix
+   */
+
+  /**
+   * @override
+   */
+  defaultRenderer: function(value, meta, record) {
+    var me = this,
+        cls,
+        height = me.iconHeight,
+        width = me.iconWidth,
+        spec;
+
+    cls = me.iconCls(value, meta, record);
+
+    if (me.iconVariant) {
+      switch (me.iconVariant) {
+        case 'x16':
+          height = width = 16;
+          break;
+        case 'x32':
+          height = width = 32;
+          break;
+      }
+    }
+
+    spec = {
+      tag: 'img',
+      // NOTE: Chrome is displaying borders around  w/o src
+      src: Ext.BLANK_IMAGE_URL,
+      cls: cls,
+      alt: me.iconName(value, meta, record)
+    };
+    if (height) {
+      spec.height = height;
+    }
+    if (width) {
+      spec.width = width;
+    }
+
+    return Ext.DomHelper.markup(spec);
+  },
+
+  /**
+   * @protected
+   */
+  iconName: function(value, meta, record) {
+    return value;
+  },
+
+  /**
+   * @protected
+   */
+  iconCls: function(value, meta, record) {
+    var me = this,
+        name = me.iconName(value, meta, record);
+
+    if (me.iconNamePrefix) {
+      name = me.iconNamePrefix + name;
+    }
+
+    return NX.Icons.cls(name, me.iconVariant);
+  }
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/Renderers.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/Renderers.js
new file mode 100644
index 0000000000000000000000000000000000000000..c1eb8a1ee17d9a75b1c8ea9bc99af12b3be2c952
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/column/Renderers.js
@@ -0,0 +1,29 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * Renderer helpers.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.grid.column.Renderers', {
+  singleton: true,
+
+  /**
+   * Renderer which will use no-data glyph if given value is undefined or null.
+   */
+  optionalData: function(value) {
+    return value ? value : '';
+  }
+});
diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/plugin/FilterBox.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/plugin/FilterBox.js
new file mode 100644
index 0000000000000000000000000000000000000000..4cf6092d9d67b822954cb9119825bee2942d37dc
--- /dev/null
+++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/plugin/FilterBox.js
@@ -0,0 +1,124 @@
+/*
+ * Sonatype Nexus (TM) Open Source Version
+ * Copyright (c) 2008-present Sonatype, Inc.
+ * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
+ * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
+ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
+ * Eclipse Foundation. All other trademarks are the property of their respective owners.
+ */
+/*global Ext*/
+
+/**
+ * Filter plugin for grids.
+ *
+ * @since 3.0
+ */
+Ext.define('NX.ext.grid.plugin.FilterBox', {
+  extend: 'NX.ext.grid.plugin.Filtering',
+  alias: 'plugin.gridfilterbox',
+  requires: [
+    'NX.I18n',
+    'NX.util.Filter'
+  ],
+
+  /**
+   * @cfg {String} emptyText Text to be used as grid empty text when no records are matching the filter. If text
+   * contains "${filter}" it will be replaced with filter value.
+   */
+
+  /**
+   * @override
+   */
+  init: function (grid) {
+    var me = this,
+        tbar = grid.getDockedItems('toolbar[dock="top"]')[0],
+        items = [
+          '->',
+          {
+            xtype: 'nx-searchbox',
+            emptyText: NX.I18n.get('Grid_Plugin_FilterBox_Empty'),
+            searchDelay: 200,
+            width: 200,
+            listeners: {
+              search: me.onSearch,
+              searchcleared: me.onSearchCleared,
+              scope: me
+            }
+          }
+        ];
+
+    me.callParent(arguments);
+
+    if (tbar) {
+      tbar.add(items);
+    }
+    else {
+      grid.addDocked([
+        {
+          xtype: 'toolbar',
+          dock: 'top',
+          items: items
+        }
+      ]);
+    }
+
+    me.grid.on('filteringautocleared', me.syncSearchBox, me);
+  },
+
+  /**
+   * Filter grid.
+   *
+   * @private
+   */
+  onSearch: function (searchbox, value) {
+    var me = this;
+
+    if (me.emptyText) {
+      me.grid.getView().emptyText = NX.util.Filter.buildEmptyResult(value, me.emptyText);
+    }
+    me.filter(value);
+  },
+
+  /**
+   * Clear the filter before destroying this plugin.
+   *
+   * @protected
+   */
+  destroy: function() {
+    var me = this;
+
+    me.clearFilter();
+
+    me.callParent();
+  },
+
+  /**
+   * Clear filtering on grid.
+   *
+   * @private
+   */
+  onSearchCleared: function () {
+    var me = this;
+
+    if (me.grid.emptyText) {
+      me.grid.getView().emptyText = '
' + me.grid.emptyText + '
'; + } + me.clearFilter(); + }, + + /** + * Syncs filtering value with search box. + * + * @private + */ + syncSearchBox: function () { + var me = this; + + me.grid.down('nx-searchbox').setValue(me.filterValue); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/plugin/Filtering.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/plugin/Filtering.js new file mode 100644 index 0000000000000000000000000000000000000000..3d0534342342136e5de4efc17187237a5da7eadf --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/plugin/Filtering.js @@ -0,0 +1,307 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * A grid plugins that adds filtering capabilities. + * + * @since 3.0 + */ +Ext.define('NX.ext.grid.plugin.Filtering', { + extend: 'Ext.AbstractPlugin', + alias: 'plugin.gridfiltering', + requires: [ + 'Ext.util.Filter' + ], + mixins: { + logAware: 'NX.LogAware' + }, + + /** + * @private {String} current filtering value + */ + filterValue: undefined, + + /** + * @private {Ext.data.Store} store that should filtered + */ + filteredStore: undefined, + + /** + * @private {Array} array of field ids that should be used for filtering + */ + filteredFields: undefined, + + /** + * @cfg {Function} to be used for filtering (defaults to String contains) + */ + filterFn: function (valueToBeMatched, filterValue) { + var stringValue; + if (valueToBeMatched) { + stringValue = valueToBeMatched.toString(); + if (stringValue) { + return stringValue.toLowerCase().indexOf(filterValue.toLowerCase()) !== -1; + } + } + return false; + }, + + /** + * Returns true if the field value is defined and matches the filtering function. + * + * @protected + * @param filterValue to match + * @param record record that was used to extract the value to be matched + * @param fieldName filter field name that was used to extract the value to be matched + * @param fieldValue to me matched + */ + matches: function (filterValue, record, fieldName, fieldValue) { + return this.filterFn(fieldValue, filterValue); + }, + + /** + * Filters on specified value. + * + * @public + * @param value to filter upon + */ + filter: function (value) { + var me = this; + + me.filterValue = value; + me.applyFilter(); + }, + + /** + * Clears filter. + * + * @public + */ + clearFilter: function () { + var me = this; + + if (me.filterValue) { + me.filterValue = undefined; + me.applyFilter(); + } + }, + + /** + * Filters on current filter value. + * + * @public + */ + applyFilter: function () { + var me = this, + remoteFilter = me.filteredStore.remoteFilter, + filters = me.filteredStore.filters.items; + + // HACK: when remote filter is on store will not be locally filtered, so we have to trick ExtJS into doing local + // filtering by setting remoteFilter to false and temporary remove the other filters (will add them back after local + // filtering is performed) + if (remoteFilter) { + me.filteredStore.filters.clear(); + me.filteredStore.remoteFilter = false; + } + if (me.filterValue) { + // + me.logTrace('Filtering', me.filteredStore.self.getName(), 'on [', me.filterValue, '] using fields:', me.filteredFields); + // + + me.filteredStore.filter(me.filteringFilter); + } + else { + me.filteredStore.removeFilter(me.filteringFilter); + + // + me.logTrace('Filtering cleared on', me.filteredStore.self.getName()); + // + } + if (remoteFilter) { + me.filteredStore.remoteFilter = remoteFilter; + if (filters) { + me.filteredStore.filters.add(filters); + } + } + }, + + /** + * Clear filter value if store is filtered from outside. + * + * @private + */ + syncFilterValue: function (store, filters) { + var me = this, + filteringFilterRemoved = true; + + if (filters) { + Ext.Array.each(filters, function (filter) { + if (filter.id === me.filteringFilter.id) { + filteringFilterRemoved = false; + return false; + } + return true; + }); + } + + if (me.filterValue && filteringFilterRemoved) { + me.clearFilter(); + me.grid.fireEvent('filteringautocleared'); + } + }, + + /** + * Bind plugin to grid. + * + * @private + * @param grid to bind to + */ + init: function (grid) { + var me = this; + + me.grid = grid; + grid.filterable = true; + grid.filter = Ext.Function.bind(me.filter, me); + grid.clearFilter = Ext.Function.bind(me.clearFilter, me); + + me.filteringFilter = Ext.create('Ext.util.Filter', { + id: 'filteringPlugin', + filterFn: function (record) { + for (var i = 0; i < me.filteredFields.length; i++) { + var filteredField = me.filteredFields[i]; + if (filteredField) { + if (me.matches(me.filterValue, record, filteredField, record.data[filteredField])) { + return true; + } + } + } + return false; + } + }); + + grid.mon(grid, { + reconfigure: me.onReconfigure, + scope: me, + beforerender: { + fn: me.onBeforeRender, + single: true, + scope: me + } + }); + }, + + /** + * Handles configuration of grid. + * + * @private + * @param grid that was reconfigure + */ + onBeforeRender: function (grid) { + this.onReconfigure(grid, grid.getStore(), grid.columns); + }, + + /** + * Handles reconfiguration of grid. + * + * @private + * @param grid that was reconfigured + * @param store new store + * @param columns new columns + */ + onReconfigure: function (grid, store, columns) { + var me = this, + store = store || me.grid.getStore(); + + // + me.logTrace('Grid', grid.id, 'reconfigured; binding to new store'); + // + + me.reconfigureStore(store, me.extractColumnsWithDataIndex(columns)); + }, + + /** + * Unbinds from current store and register itself to provided store. + * + * @private + * @param store to register itself to + * @param filteredFields fields to be used while filtering + */ + reconfigureStore: function (store, filteredFields) { + var me = this; + if (me.filteredStore !== store) { + me.unbindFromStore(me.filteredStore); + me.bindToStore(store); + } + me.filteredFields = filteredFields; + me.applyFilter(); + }, + + /** + * Register itself as listener of load events on provided store. + * + * @private + * @param store to register itself to + */ + bindToStore: function (store) { + var me = this; + me.filteredStore = store; + if (store) { + // + me.logTrace('Binding to store', me.filteredStore.self.getName()); + // + + me.grid.mon(store, 'load', me.applyFilter, me); + me.grid.mon(store, 'filterchange', me.syncFilterValue, me); + } + }, + + /** + * Remove itself as listener from provided store. + * + * @private + * @param store to remove itself from + */ + unbindFromStore: function (store) { + var me = this; + if (store) { + // + me.logTrace('Unbinding from store', me.filteredStore.self.getName()); + // + + me.grid.mun(store, 'load', me.applyFilter, me); + me.grid.mun(store, 'filterchange', me.syncFilterValue, me); + } + }, + + /** + * Returns the dataIndex property of all grid columns. + * + * @private + * @returns {Array} of fields names + */ + extractColumnsWithDataIndex: function (columns) { + var filterFieldNames = []; + + if (columns) { + Ext.each(columns, function (column) { + if (column.dataIndex) { + filterFieldNames.push(column.dataIndex); + } + }); + } + + if (filterFieldNames.length > 0) { + return filterFieldNames; + } + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/plugin/RemoteFilterBox.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/plugin/RemoteFilterBox.js new file mode 100644 index 0000000000000000000000000000000000000000..df60a1b54f5511220803535963944a0ed9c831e9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/grid/plugin/RemoteFilterBox.js @@ -0,0 +1,145 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Filter plugin for grids where filtering is handled remotely. + * + * @since 3.0 + */ +Ext.define('NX.ext.grid.plugin.RemoteFilterBox', { + extend: 'Ext.AbstractPlugin', + alias: 'plugin.remotegridfilterbox', + requires: [ + 'NX.I18n', + 'NX.util.Filter' + ], + + /** + * @cfg {String} emptyText Text to be used as grid empty text when no records are matching the filter. If text + * contains "${filter}" it will be replaced with filter value. + */ + + /** + * @override + */ + init: function (grid) { + var me = this, + tbar = grid.getDockedItems('toolbar[dock="top"]')[0], + items = [ + '->', + { + xtype: 'nx-searchbox', + emptyText: NX.I18n.get('Grid_Plugin_FilterBox_Empty'), + searchDelay: 200, + width: 200, + listeners: { + search: me.onSearch, + searchcleared: me.onSearchCleared, + scope: me + } + } + ]; + me.grid = grid; + + me.callParent(arguments); + + if (tbar) { + tbar.add(items); + } + else { + grid.addDocked([ + { + xtype: 'nx-actions', + dock: 'top', + items: items + } + ]); + } + + me.grid.on('filteringautocleared', me.syncSearchBox, me); + }, + + /** + * Syncs filtering value with search box. + * + * @private + */ + syncSearchBox: function () { + var me = this; + + me.grid.down('nx-searchbox').setValue(me.filterValue); + }, + + /** + * Clears the present search. + */ + clearSearch: function() { + var me = this; + + me.grid.down('nx-searchbox').clearSearch(); + }, + + /** + * @private + * Filter grid. + * + * @private + * @param {NX.ext.SearchBox} searchBox component + * @param {String} value to be searched + */ + onSearch: function(searchBox, value) { + var grid = searchBox.up('grid'), + store = grid.getStore(), + emptyText = grid.getView().emptyTextFilter; + + if (!grid.emptyText) { + grid.emptyText = grid.getView().emptyText; + } + grid.getView().emptyText = NX.util.Filter.buildEmptyResult(value, emptyText); + grid.getSelectionModel().deselectAll(); + store.addFilter([ + { + id: 'filter', + property: 'filter', + value: value + } + ]); + }, + + /** + * Clear filtering on grid. + * + * @private + * @param {NX.ext.SearchBox} searchBox component + */ + onSearchCleared: function(searchBox) { + var grid = searchBox.up('grid'), + store = grid.getStore(); + + if (grid.emptyText) { + grid.getView().emptyText = grid.emptyText; + } + grid.getSelectionModel().deselectAll(); + // we have to remove filter directly as store#removeFilter() does not work when store#remoteFilter = true + if (store.filters.removeAtKey('filter')) { + if (store.filters.length) { + store.filter(); + } + else { + store.clearFilter(); + } + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/tab/SortedPanel.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/tab/SortedPanel.js new file mode 100644 index 0000000000000000000000000000000000000000..5526d3ddc5f07b30dbd8d966adcab15af83a8565 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/ext/tab/SortedPanel.js @@ -0,0 +1,71 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * A tab panel that sorts tabs based on weight and title and not show the tab bar if only one tab. + * + * @since 3.0 + */ +Ext.define('NX.ext.tab.SortedPanel', { + extend: 'Ext.tab.Panel', + alias: 'widget.nx-sorted-tabpanel', + + listeners: { + /** + * @private + * Reorders tabs sorting them by weight / title. + * Show the tab bar in case of more then one tab. + * + * @param me this tab panel + * @param component added tab + */ + add: function (me, component) { + var thisTitle = component.title || '', + thisWeight = component.weight || 1000, + position = 0; + + me.suspendEvents(); + me.remove(component, false); + + me.items.each(function (item) { + var thatTitle = item.title || '', + thatWeight = item.weight || 1000; + + if (thisWeight < thatWeight + || (thisWeight === thatWeight && thisTitle < thatTitle)) { + return false; + } + position++; + return true; + }); + + me.insert(position, component); + me.resumeEvents(); + } + }, + + // FIXME: This doesn't belong here, this is styling treatment for master/detail tabs only + /** + * @override + */ + onAdd: function(item, index) { + item.tabConfig = item.tabConfig || {}; + Ext.applyIf(item.tabConfig, { + // HACK: force tabs to follow scss style for borders + border: null + }); + + this.callParent([item, index]); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/Feature.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/Feature.js new file mode 100644 index 0000000000000000000000000000000000000000..b9a9fcb93d836e79ab3a85661891ab40f42029bd --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/Feature.js @@ -0,0 +1,51 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Feature model. + * + * @since 3.0 + */ +Ext.define('NX.model.Feature', { + extend: 'Ext.data.Model', + + idProperty: 'path', + + // FIXME: define types so its clear what this data is! Also consider comments for further clarity. + + fields: [ + { name: 'path' }, + { name: 'text' }, + { + /** + * Mode name. + */ + name: 'mode', + type: 'string', + + // FIXME: why is this defaulting to 'admin'? + defaultValue: 'admin' + }, + { name: 'weight', defaultValue: 100 }, + { name: 'group', defaultValue: false }, + { name: 'view', defaultValue: undefined }, + { name: 'helpKeyword', defaultValue: undefined }, + { name: 'visible', defaultValue: true }, + { name: 'expanded', defaultValue: true }, + { name: 'bookmark', defaultValue: undefined }, + { name: 'iconName', defaultValue: undefined }, + { name: 'description', defaultValue: undefined }, + { name: 'authenticationRequired', defaultValue: true } + ] +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/FeatureMenu.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/FeatureMenu.js new file mode 100644 index 0000000000000000000000000000000000000000..5d4a737757137704aaf4cf532c75a760509badae --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/FeatureMenu.js @@ -0,0 +1,43 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Feature menu tree model. + * + * @since 3.0 + */ +Ext.define('NX.model.FeatureMenu', { + extend: 'Ext.data.TreeModel', + + // FIXME: define types so its clear what this data is! Also consider comments for further clarity. + + // FIXME: Set ID for module... unsure what this should be in a tree though + + fields: [ + { name: 'path' }, + { + /** + * Mode name. + */ + name: 'mode', + type: 'string' + }, + { name: 'text' }, + { name: 'weight', defaultValue: 100 }, + { name: 'group', defaultValue: false }, + { name: 'view' }, + { name: 'bookmark' }, + { name: 'iconName' } + ] +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/Icon.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/Icon.js new file mode 100644 index 0000000000000000000000000000000000000000..d5f7ebeed5d14d74ec8415587bca11c8d3fe5346 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/Icon.js @@ -0,0 +1,34 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Icon model. + * + * @since 3.0 + */ +Ext.define('NX.model.Icon', { + extend: 'Ext.data.Model', + + idProperty: 'cls', + fields: [ + { name: 'cls', type: 'string' }, + { name: 'name', type: 'string' }, + { name: 'file', type: 'string' }, + { name: 'variant', type: 'string' }, + { name: 'height', type: 'int' }, + { name: 'width', type: 'int' }, + { name: 'url', type: 'string' }, + { name: 'preload', type: 'boolean' } + ] +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/LogEvent.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/LogEvent.js new file mode 100644 index 0000000000000000000000000000000000000000..d4455388f7733d9f3211f5f6ace4e417cfcc6e9a --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/LogEvent.js @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Log-event model. + * + * @since 3.0 + */ +Ext.define('NX.model.LogEvent', { + extend: 'Ext.data.Model', + fields: [ + { name: 'timestamp', type: 'int' }, + { name: 'logger', type: 'string' }, + { name: 'level', type: 'string' }, + { name: 'message', type: 'object' } + ], + idgen: 'sequential' +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/LogLevel.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/LogLevel.js new file mode 100644 index 0000000000000000000000000000000000000000..94c4d0b775e3a78cf6953d322521850b48768fed --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/LogLevel.js @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Log-level model. + * + * @since 3.0 + */ +Ext.define('NX.model.LogLevel', { + extend: 'Ext.data.Model', + idProperty: 'name', + fields: [ + { name: 'name', type: 'string' } + ] +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/Permission.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/Permission.js new file mode 100644 index 0000000000000000000000000000000000000000..aa6e9e8a30d939a1f8fdeedbf1023975428a4b6c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/Permission.js @@ -0,0 +1,32 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Permission model. + * + * @since 3.0 + */ +Ext.define('NX.model.Permission', { + extend: 'Ext.data.Model', + requires: [ + 'NX.Permissions' + ], + + idProperty: 'id', + fields: [ + { name: 'id', type: 'string' }, + { name: 'permitted', type: 'bool', defaultValue: true } + ] + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/State.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/State.js new file mode 100644 index 0000000000000000000000000000000000000000..87e57c49bf921709eed869bda279d71546023bce --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/State.js @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * State model. + * + * @since 3.0 + */ +Ext.define('NX.model.State', { + extend: 'Ext.data.Model', + idProperty: 'key', + fields: [ + { name: 'key', type: 'string' }, + { name: 'value', defaultValue: undefined }, + { name: 'hash', type: 'string' } + ] +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/dev/Condition.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/dev/Condition.js new file mode 100644 index 0000000000000000000000000000000000000000..a8f9dfa3cdfdf0723953750c7007679fa8ecd571 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/model/dev/Condition.js @@ -0,0 +1,22 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +Ext.define('NX.model.dev.Condition', { + extend: 'Ext.data.Model', + fields: [ + { name: 'id', type: 'string' }, + { name: 'condition', defaultValue: undefined }, + { name: 'satisfied', type: 'boolean' } + ] +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/Feature.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/Feature.js new file mode 100644 index 0000000000000000000000000000000000000000..b05067f57c3676cb403d2955d105bd5995fbaf4d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/Feature.js @@ -0,0 +1,25 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Feature store. + * + * @since 3.0 + */ +Ext.define('NX.store.Feature', { + extend: 'Ext.data.ArrayStore', + model: 'NX.model.Feature', + + sorters: { property: 'path', direction: 'ASC' } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/FeatureGroup.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/FeatureGroup.js new file mode 100644 index 0000000000000000000000000000000000000000..e3a1fefdf27ff7520e3edcc382296329e80d9ddb --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/FeatureGroup.js @@ -0,0 +1,25 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Store containing {@link NX.model.Feature} records for selected feature group (children of feature node). + * + * @since 3.0 + */ +Ext.define('NX.store.FeatureGroup', { + extend: 'Ext.data.ArrayStore', + model: 'NX.model.Feature', + + sorters: { property: 'path', direction: 'ASC' } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/FeatureMenu.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/FeatureMenu.js new file mode 100644 index 0000000000000000000000000000000000000000..1a39e075269867fbdf2efc418762dbf260a23f35 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/FeatureMenu.js @@ -0,0 +1,30 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Feature menu tree store. + * + * @since 3.0 + */ +Ext.define('NX.store.FeatureMenu', { + extend: 'Ext.data.TreeStore', + model: 'NX.model.FeatureMenu', + + root: { + expanded: true, + text: 'Features', + children: [] + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/Icon.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/Icon.js new file mode 100644 index 0000000000000000000000000000000000000000..fe87d36eba172e8f603709b386f6f0349992d50f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/Icon.js @@ -0,0 +1,23 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Icon store. + * + * @since 3.0 + */ +Ext.define('NX.store.Icon', { + extend: 'Ext.data.Store', + model: 'NX.model.Icon' +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/LogEvent.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/LogEvent.js new file mode 100644 index 0000000000000000000000000000000000000000..6a2a5e6f87a261575ceed12d25d5ebc9b96b9fd4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/LogEvent.js @@ -0,0 +1,23 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * LogEvent store. + * + * @since 3.0 + */ +Ext.define('NX.store.LogEvent', { + extend: 'Ext.data.ArrayStore', + model: 'NX.model.LogEvent' +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/LogLevel.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/LogLevel.js new file mode 100644 index 0000000000000000000000000000000000000000..4a900cca9e48645b7bbbf3c9ac2c325f38d14082 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/LogLevel.js @@ -0,0 +1,32 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Log-level store. + * + * @since 3.0 + */ +Ext.define('NX.store.LogLevel', { + extend: 'Ext.data.Store', + model: 'NX.model.LogLevel', + data: [ + { name: 'all' }, + { name: 'trace' }, + { name: 'debug' }, + { name: 'info' }, + { name: 'warn' }, + { name: 'error' }, + { name: 'off' } + ] +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/Permission.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/Permission.js new file mode 100644 index 0000000000000000000000000000000000000000..8155c7e6be6c1eaa64c8b759c032cf178670202b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/Permission.js @@ -0,0 +1,43 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Permission store. + * + * @since 3.0 + */ +Ext.define('NX.store.Permission', { + extend: 'Ext.data.Store', + model: 'NX.model.Permission', + + proxy: { + type: 'direct', + paramsAsHash: false, + + api: { + read: 'NX.direct.rapture_Security.getPermissions' + }, + + reader: { + type: 'json', + root: 'data', + idProperty: 'id', + successProperty: 'success' + } + }, + + sortOnLoad: true, + sorters: { property: 'id', direction: 'ASC' } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/State.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/State.js new file mode 100644 index 0000000000000000000000000000000000000000..47f04e813b6c6e032fab8a2e644b5a35e6673215 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/State.js @@ -0,0 +1,23 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * State store. + * + * @since 3.0 + */ +Ext.define('NX.store.State', { + extend: 'Ext.data.Store', + model: 'NX.model.State' +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/dev/Condition.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/dev/Condition.js new file mode 100644 index 0000000000000000000000000000000000000000..de47a8b257ea7fcef1c2ada0c2cbde714464b84c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/store/dev/Condition.js @@ -0,0 +1,18 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +Ext.define('NX.store.dev.Condition', { + extend: 'Ext.data.Store', + model: 'NX.model.dev.Condition' +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Array.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Array.js new file mode 100644 index 0000000000000000000000000000000000000000..cb012bbc5425f385ec2351e4fb02b15bc12343c8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Array.js @@ -0,0 +1,43 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Array related utils. + * + * @since 3.0 + */ +Ext.define('NX.util.Array', { + singleton: true, + requires: [ + 'Ext.Array' + ], + + /** + * Check if one array contains all elements from another. + * + * @public + * @param {Array} array1 + * @param {Array} array2 + * @return {boolean} true if array1 contains all elements from array2. + */ + containsAll: function(array1, array2) { + var i; + for (i=0; i> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } + else if (isNaN(chr3)) { + enc4 = 64; + } + + output = output + + this.keyStr.charAt(enc1) + + this.keyStr.charAt(enc2) + + this.keyStr.charAt(enc3) + + this.keyStr.charAt(enc4); + } + + return output; + }, + + /** + * Decode given BASE-64 encoded input string. + * + * @public + */ + decode: function (input) { + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + while (i < input.length) { + enc1 = this.keyStr.indexOf(input.charAt(i++)); + enc2 = this.keyStr.indexOf(input.charAt(i++)); + enc3 = this.keyStr.indexOf(input.charAt(i++)); + enc4 = this.keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if (enc3 !== 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 !== 64) { + output = output + String.fromCharCode(chr3); + } + } + + output = NX.util.Utf8.decode(output); + + return output; + } + +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/DateFormat.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/DateFormat.js new file mode 100644 index 0000000000000000000000000000000000000000..585f526fe720292a234030ed6f541e993c4efa15 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/DateFormat.js @@ -0,0 +1,124 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Date format related utils. + * + * @since 3.0 + */ +Ext.define('NX.util.DateFormat', { + singleton: true, + + mixins: [ + 'NX.LogAware' + ], + + /** + * @private + */ + defaultPatterns: { + date: { + // 2013-Mar-06 + 'short': 'Y-M-d', + + // Wednesday, March 06, 2013 + 'long': 'l, F d, Y' + }, + + time: { + // 15:49:57 + 'short': 'H:i:s', + + // 15:49:57-0700 PST (GMT-0700) + 'long': 'H:i:s T (\\G\\M\\TO)' + }, + + datetime: { + // 2013-Mar-06 15:49:57 + 'short': 'Y-M-d H:i:s', + + // Wednesday, March 06, 2013 15:50:19 PDT (GMT-0700) + 'long': 'l, F d, Y H:i:s T (\\G\\M\\TO)' + } + }, + + /** + * Return the date format object for the given name. + * + * Date format objects currently have a 'short' and 'long' variants. + * + * @example + * var longDatetimePattern = NX.util.DateFormat.forName('datetime')['long']; + * var shortDatePattern = NX.util.DateFormat.forName('date')['short']; + * + * @public + * @param name + * @return {*} Date format object. + */ + forName: function (name) { + var format = this.defaultPatterns[name]; + + // if no format, complain and return the full ISO-8601 format + if (!name) { + this.logWarn('Missing named format:', name); + return 'c'; + } + + // TODO: Eventually let this customizable by user, for now its hardcoded + + return format; + }, + + /** + * Formats the passed timestamp using the specified format pattern. + * + * @public + * @param {Number} value The value to format converted to a date by the Javascript's built-in Date#parse method. + * @param {String} [format] Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}. + * @return {String} The formatted date string + */ + timestamp: function (value, format) { + format = format || NX.util.DateFormat.forName('datetime')['long']; + return value ? Ext.util.Format.date(new Date(value), format) : undefined; + }, + + /** + * Returns a timestamp rendering function that can be reused to apply a date format multiple times efficiently. + * + * @public + * @param {String} format Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}. + * @return {Function} The date formatting function + */ + timestampRenderer: function (format) { + return function (value) { + return NX.util.DateFormat.timestamp(value, format); + }; + }, + + /** + * @public + * @returns {String} time zone + */ + getTimeZone: function () { + var me = this; + + if (!me.timeZone) { + me.timeZone = new Date().toTimeString(); + me.timeZone = me.timeZone.substring(me.timeZone.indexOf(" ")); + } + + return me.timeZone; + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/DownloadHelper.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/DownloadHelper.js new file mode 100644 index 0000000000000000000000000000000000000000..dbcbc8c861a1bb611838fa1dbe8843be57e59ff1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/DownloadHelper.js @@ -0,0 +1,99 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Helper to facilitate browser-based file downloads. + * + * @since 3.0 + */ +Ext.define('NX.util.DownloadHelper', { + singleton: true, + requires: [ + 'NX.Messages', + 'NX.Windows', + 'NX.I18n' + ], + mixins: { + logAware: 'NX.LogAware' + }, + + /** + * ExtJS component identifier for nested iframe. + * + * @private + */ + windowId: 'nx-download-frame', + + /** + * Window names in IE are very picky, using '_' instead of '-' so that its its a valid javascript identifier. + * + * @private + */ + windowName: 'nx_download_frame', + + /** + * Get the hidden download frame. + * + * @private + * @returns {Ext.Element} + */ + getFrame: function() { + var me = this, frame; + + // create the download frame if needed + frame = Ext.get(me.windowId); + if (!frame) { + frame = Ext.getBody().createChild({ + tag: 'iframe', + cls: 'x-hidden', + id: me.windowId, + name: me.windowName + }); + + // + me.logDebug('Created download-frame:', frame); + // + } + + return frame; + }, + + /** + * @public + * @param {String} url URL to download + */ + downloadUrl: function (url) { + var me = this; + + // + me.logDebug('Downloading URL:', url); + // + + // resolve the download frame + me.getFrame(); + + // TODO: Consider changing this to a dynamic form or 'a' link and automatically submit/click + // TODO: ... to make use of html5 download attribute and avoid needing to _open_ more windows + // TODO: ... IE might not like this very much though? + + // TODO: Form method could be handy to GET/POST w/params vs link to just GET? + + // FIXME: This may produce js console warnings "Resource interpreted as Document but transferred with MIME type application/zip" + + // open new window in hidden download-from to initiate download + if (NX.Windows.open(url, me.windowName) !== null) { + NX.Messages.add({text: NX.I18n.get('Util_DownloadHelper_Download_Message'), type: 'success'}); + } + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Filter.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Filter.js new file mode 100644 index 0000000000000000000000000000000000000000..fffc48d60ce38e0c5305632fb83064321a345333 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Filter.js @@ -0,0 +1,34 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Filter + * + * @since 3.3 + */ +Ext.define('NX.util.Filter', { + singleton: true, + + /** + * Util to build a div for empty search results. + * + * @param searchString + * @param emptyTemplate + * @returns {string} + */ + buildEmptyResult: function(searchString, emptyTemplate) { + var encoded = Ext.util.Format.htmlEncode(searchString); + return '
' + emptyTemplate.replace(/\$filter/, encoded) + '
'; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Url.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Url.js new file mode 100644 index 0000000000000000000000000000000000000000..93c3dfc71a203ae7b29876e79eee835715f89a4a --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Url.js @@ -0,0 +1,103 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * URL related utils. + * + * @since 3.0 + */ +Ext.define('NX.util.Url', { + singleton: true, + requires: [ + 'Ext.String' + ], + + /** + * Returns the base URL of the Nexus server. URL never ends with '/'. + * + * @public + * @property {String} + * @readonly + */ + baseUrl: NX.app.baseUrl, + + /** + * Returns a cache-busting urlSuffix provided by the Nexus server. + * + * @public + * @property {String} + * @readonly + */ + urlSuffix: NX.app.urlSuffix, + + /** + * @public + */ + urlOf: function (path) { + var baseUrl = this.baseUrl; + + if (!Ext.isEmpty(path)) { + if (Ext.String.endsWith(baseUrl, '/')) { + baseUrl = baseUrl.substring(0, baseUrl.length - 1); + } + if (!Ext.String.startsWith(path, '/')) { + path = '/' + path; + } + return baseUrl + path; + } + return this.baseUrl; + }, + + /** + * Creates a link. + * + * @public + * @param {String} url to link to + * @param {String} [text] link text. If omitted, defaults to url value. + * @param {String} [target] link target. If omitted, defaults to '_blank' + * @param {String} [id] link id + */ + asLink: function (url, text, target, id) { + target = target || '_blank'; + if (Ext.isEmpty(text)) { + text = url; + } + if (id) { + id = ' id="' + id + '"'; + } else { + id = ''; + } + return '' + text + ''; + }, + + /** + * Allows text to be easily copy/pasted. + * + * @public + * @param {String} value to copy + */ + asCopyWidget: function (value) { + return ''; + }, + + /** + * Helper to append cache busting suffix to given url. + * + * @param {string} url + * @returns {string} + */ + cacheBustingUrl: function(url) { + return url + '?' + this.urlSuffix; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Utf8.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Utf8.js new file mode 100644 index 0000000000000000000000000000000000000000..46c6bf0f05add1f40aadcdf3f8fdd2bcf8060797 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Utf8.js @@ -0,0 +1,93 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ +/*jslint bitwise: true, plusplus: true*/ + +/** + * UTF8 related utils. + * + * @since 3.0 + */ +Ext.define('NX.util.Utf8', { + singleton: true, + + /** + * Encode string as UTF-8. + * + * @public + * @param {String} string + * @return UTF-8 encoded string. + */ + encode: function (string) { + var utftext = "", + c, + n; + + string = string.replace(/\r\n/g, "\n"); + + for (n = 0; n < string.length; n++) { + c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if ((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + } + + return utftext; + }, + + /** + * Decode UTF-8 string. + * + * @public + * @param {String} utftext + * @return String. + */ + decode: function (utftext) { + var string = "", + i = 0, + c = 0, c2 = 0, c3 = 0; + + while (i < utftext.length) { + c = utftext.charCodeAt(i); + + if (c < 128) { + string += String.fromCharCode(c); + i++; + } + else if ((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i + 1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } + else { + c2 = utftext.charCodeAt(i + 1); + c3 = utftext.charCodeAt(i + 2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + } + + return string; + } + +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Validator.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Validator.js new file mode 100644 index 0000000000000000000000000000000000000000..f1400acd02010e8f6f338db5a54056a1a69a1ae7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/Validator.js @@ -0,0 +1,147 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Validation helpers. + * + * @since 3.0 + */ +Ext.define('NX.util.Validator', { + singleton: true, + requires: [ + 'Ext.form.field.VTypes', + 'NX.I18n' + ], + + /** + * @private + */ + default_url_options: { + protocols: ['http', 'https', 'ftp'], + require_tld: false, + require_protocol: false, + allow_underscores: false + }, + + /** + * Changes to this property should be sync'd in: + * components/nexus-validation/src/main/java/org/sonatype/nexus/validation/constraint/NamePatternConstants.java + * @private + */ + nxNameRegex : /^[a-zA-Z0-9\-]{1}[a-zA-Z0-9_\-\.]*$/, + + /** + * Removes the constraint for a maximum of 6 characters in the last element of the domain name, otherwise + * is the same as default ExtJS email vtype. + * @private + */ + nxEmailRegex : /^(")?(?:[^\."])(?:(?:[\.])?(?:[\w\-!#$%&'*+/=?^_`{|}~]))*\1@(\w[\-\w]*\.){1,5}([A-Za-z]){2,60}$/, + + /** + * A regular expression to detect a valid hostname according to RFC 1123. + * See also http-headers-patterns.properties and HostnameValidator.java for other uses of this regex. + * @private + */ + nxRfc1123HostRegex: new RegExp( + "^(((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|" + + "(\\[(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\])|" + + "(\\[((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\])|" + + "(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|" + + "[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9]))(:([0-9]+))?$" + ), + + /** + * A regular expression to detect whether we have leading and trailing white space + * + * @private + */ + nxLeadingAndTrailingWhiteSpaceRegex : /^[ \s]+|[ \s]+$/, + + /** + * @public + * @param vtype {object} + */ + registerVtype: function(vtype) { + Ext.apply(Ext.form.field.VTypes, vtype); + }, + + constructor: function () { + var me = this; + + me.vtypes = [ + { + 'nx-name': function(val) { + return NX.util.Validator.nxNameRegex.test(val); + }, + 'nx-nameText': NX.I18n.get('Util_Validator_Text'), + 'nx-email': function(val) { + return NX.util.Validator.nxEmailRegex.test(val); + }, + 'nx-emailText': Ext.form.field.VTypes.emailText, + 'nx-hostname': function(val) { + return NX.util.Validator.nxRfc1123HostRegex.test(val); + }, + 'nx-hostnameText': NX.I18n.get('Util_Validator_Hostname'), + 'nx-trim': function(val) { + return !NX.util.Validator.nxLeadingAndTrailingWhiteSpaceRegex.test(val); + }, + 'nx-trimText': NX.I18n.get('Util_Validator_Trim') + } + ]; + + Ext.each(me.vtypes, function(vtype) { + me.registerVtype(vtype); + }); + }, + + /** + * Validate if given string is a URL. + * Based on: https://github.com/chriso/validator.js (MIT license) + * + * @public + * @param {String} str + * @param {Object} options (optional) + * @returns {boolean} + */ + isURL: function (str, options) { + + // Apply options + options = options || {}; + options = Ext.applyIf(options, this.default_url_options); + + // Short-circuit when empty + if (Ext.isEmpty(str)) { + return options.allow_blank; + } + + // Ensure that the URL is of proper length + if (str.length >= 2083) { + return false; + } + + // Check the URL syntax + var separators = '-?-?' + (options.allow_underscores ? '_?' : ''); + var url = new RegExp('^(?!mailto:)(?:(?:' + options.protocols.join('|') + ')://)' + + (options.require_protocol ? '' : '?') + + '(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:www.)?)?(?:(?:[a-z\\u00a1-\\uffff0-9]+' + + separators + ')*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+' + separators + + ')*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{1,}))' + (options.require_tld ? '' : '?') + + ')|localhost)(?::(\\d{1,5}))?(?:(?:/|\\?|#)[^\\s]*)?$', 'i'); + var match = str.match(url), + port = match ? match[1] : 0; + + return !!(match && (!port || (port > 0 && port <= 65535))); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/Condition.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/Condition.js new file mode 100644 index 0000000000000000000000000000000000000000..d3f1f1569445f00e0b7d128f766807e026cbac0f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/Condition.js @@ -0,0 +1,224 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ +/*jslint plusplus: true*/ + +/** + * @since 3.0 + */ +Ext.define('NX.util.condition.Condition', { + mixins: { + observable: 'Ext.util.Observable', + logAware: 'NX.LogAware' + }, + + statics: { + counter: 1 + }, + + /** + * Generated id used by event bus. + * + * @property {String} + */ + id: undefined, + + /** + * Number of listeners listening to this condition. + * + * @private + * @property {Number} + */ + listenerCounter: 0, + + /** + * True when this condition is bounded. + * + * @protected + * @property {Boolean} + */ + bounded: false, + + /** + * True when this condition is satisfied. + * + * @private + * @property {Boolean} + */ + satisfied: false, + + /** + * @constructor + * @param {Object} config + */ + constructor: function (config) { + var me = this; + + me.id = me.self.getName() + '-' + NX.util.condition.Condition.counter++; + + me.mixins.observable.constructor.call(me, config); + + me.addEvents( + /** + * Fires when condition is satisfied. + * + * @event satisfied + * @param {NX.util.condition.Condition} this + */ + 'satisfied', + + /** + * Fires when condition is not satisfied. + * + * @event unsatisfied + * @param {NX.util.condition.Condition} this + */ + 'unsatisfied' + ); + }, + + // HACK: comment the following lines to let debug messages flow + /** + * @override + */ + logDebug: function () { + // empty + }, + + /** + * Sets {@link #bounded} = true. + * + * @protected + * @chainable + */ + bind: function () { + var me = this; + + if (!me.bounded) { + me.setBounded(true); + } + + return me; + }, + + /** + * Clears all listeners of this condition and sets {@link #bounded} = false. + * + * @protected + * @chainable + */ + unbind: function () { + var me = this; + + if (me.bounded) { + me.clearListeners(); + Ext.app.EventBus.unlisten(me.id); + me.setBounded(false); + } + + return me; + }, + + /** + * Sets {@link #bounded} = false and makes condition unsatisfied. + * + * @protected + * @param bounded + */ + setBounded: function (bounded) { + var me = this; + if (Ext.isDefined(me.bounded)) { + if (bounded !== me.bounded) { + if (!bounded) { + me.setSatisfied(false); + } + + // + me.logDebug((bounded ? 'Bounded:' : 'Unbounded:'), me); + // + + me.bounded = bounded; + Ext.defer(function () { + NX.getApplication().getStateController().fireEvent('conditionboundedchanged', me); + }, 1); + } + } + else { + me.bounded = bounded; + } + }, + + /** + * @public + * @returns {boolean} true, if condition is satisfied + */ + isSatisfied: function () { + return this.satisfied; + }, + + /** + * Sets {@link #satisfied} = true and fires 'satisfied' / 'unsatisfied' if satisfied changed. + * + * @protected + * @param {boolean} satisfied if condition is satisfied + */ + setSatisfied: function (satisfied) { + var me = this; + if (Ext.isDefined(me.satisfied)) { + if (satisfied !== me.satisfied) { + // + me.logDebug((satisfied ? 'Satisfied:' : 'Unsatisfied:'), me); + // + + me.satisfied = satisfied; + me.fireEvent(satisfied ? 'satisfied' : 'unsatisfied', me); + Ext.defer(function () { + NX.getApplication().getStateController().fireEvent('conditionstatechanged', me); + }, 1); + } + } + else { + me.satisfied = satisfied; + } + }, + + /** + * Additionally, {@link #bind}s when first listener added. + * + * @override + */ + addListener: function (ename, fn, scope, options) { + var me = this; + me.mixins.observable.addListener.call(me, ename, fn, scope, options); + me.listenerCounter++; + if (me.listenerCounter === 1) { + me.bind(); + } + // re-fire event so new listener has the chance to do its job + me.fireEvent(me.satisfied ? 'satisfied' : 'unsatisfied', me); + }, + + /** + * Additionally, {@link #unbind}s when no more listeners. + * + * @override + */ + removeListener: function (ename, fn, scope) { + var me = this; + me.mixins.observable.removeListener.call(me, ename, fn, scope); + me.listenerCounter--; + if (me.listenerCounter === 0) { + me.unbind(); + } + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/Conjunction.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/Conjunction.js new file mode 100644 index 0000000000000000000000000000000000000000..2886b0d655f78fa3ed36edcd9b6a04bef6684129 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/Conjunction.js @@ -0,0 +1,77 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * A {@link NX.util.condition.Condition} that is satisfied when all AND-ed {@link NX.util.condition.Condition}s + * are satisfied. + * + * @since 3.0 + */ +Ext.define('NX.util.condition.Conjunction', { + extend: 'NX.util.condition.Condition', + + /** + * Array of conditions to be AND-ed. + * + * @cfg {NX.util.condition.Condition[]} + */ + conditions: undefined, + + /** + * @override + * @returns {NX.util.condition.Conjunction} + */ + bind: function () { + var me = this; + + if (!me.bounded) { + me.callParent(); + me.evaluate(); + Ext.each(me.conditions, function (condition) { + me.mon(condition, { + satisfied: me.evaluate, + unsatisfied: me.evaluate, + scope: me + }); + }); + } + + return me; + }, + + /** + * @private + */ + evaluate: function () { + var me = this, + satisfied = true; + + if (me.bounded) { + Ext.each(me.conditions, function (condition) { + satisfied = condition.satisfied; + return satisfied; + }); + me.setSatisfied(satisfied); + } + }, + + /** + * @overrdie + * @returns {String} + */ + toString: function () { + return this.conditions.join(' AND '); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/Disjunction.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/Disjunction.js new file mode 100644 index 0000000000000000000000000000000000000000..2b201fb56b0f9526f6662518a4d035bca71c6dbb --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/Disjunction.js @@ -0,0 +1,80 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * A {@link NX.util.condition.Condition} that is satisfied when any of OR-ed {@link NX.util.condition.Condition}s + * is satisfied. + * + * @since 3.0 + */ +Ext.define('NX.util.condition.Disjunction', { + extend: 'NX.util.condition.Condition', + + /** + * Array of conditions to be OR-ed. + * + * @cfg {NX.util.condition.Condition[]} + */ + conditions: undefined, + + /** + * @override + * @returns {NX.util.condition.Disjunction} + */ + bind: function () { + var me = this; + + if (!me.bounded) { + me.callParent(); + me.evaluate(); + Ext.each(me.conditions, function (condition) { + me.mon(condition, { + satisfied: me.evaluate, + unsatisfied: me.evaluate, + scope: me + }); + }); + } + + return me; + }, + + /** + * @private + */ + evaluate: function () { + var me = this, + satisfied = false; + + if (me.bounded) { + Ext.each(me.conditions, function (condition) { + if(condition.satisfied){ + satisfied = true; + return false; + } + return true; + }); + me.setSatisfied(satisfied); + } + }, + + /** + * @override + * @returns {String} + */ + toString: function () { + return this.conditions.join(' OR '); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/FormHasRecord.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/FormHasRecord.js new file mode 100644 index 0000000000000000000000000000000000000000..ec1a0baf039f1d3c9f149e05f023d64b2e15e87f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/FormHasRecord.js @@ -0,0 +1,94 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * A {@link NX.util.condition.Condition} that is satisfied when a {@link NX.view.SettingsForm}, specified by its + * selector, exists and has a record. Optionally, a function could be used to provide additional checking when form has + * a record. + * + * @since 3.0 + */ +Ext.define('NX.util.condition.FormHasRecord', { + extend: 'NX.util.condition.Condition', + + /** + * A form selector as specified by (@link Ext.ComponentQuery#query}. + * + * @cfg {String} + */ + form: undefined, + + /** + * An optional function to be called when form has a record to perform additional checks on the passed in model. + * + * @cfg {Function} + */ + fn: undefined, + + /** + * @override + * @returns {NX.util.condition.FormHasRecord} + */ + bind: function () { + var me = this, + components = {}, queryResult; + + if (!me.bounded) { + components[me.form] = { + afterrender: me.evaluate, + recordloaded: me.evaluate, + destroy: me.evaluate + }; + Ext.app.EventBus.listen({ component: components }, me); + me.callParent(); + queryResult = Ext.ComponentQuery.query(me.form); + if (queryResult && queryResult.length > 0) { + me.evaluate(queryResult[0]); + } + } + + return me; + }, + + /** + * @private + */ + evaluate: function (form) { + var me = this, + satisfied = false, + model; + + if (me.bounded) { + if (Ext.isDefined(form) && form.isXType('form')) { + model = form.getRecord(); + if (model) { + satisfied = true; + if (Ext.isFunction(me.fn)) { + satisfied = me.fn(model) === true; + } + } + } + me.setSatisfied(satisfied); + } + }, + + /** + * @override + * @returns {String} + */ + toString: function () { + return this.self.getName() + '{ form=' + this.form + ' }'; + } + +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/GridHasSelection.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/GridHasSelection.js new file mode 100644 index 0000000000000000000000000000000000000000..dbda0d9ef0cac956131557500a098efdffdccd90 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/GridHasSelection.js @@ -0,0 +1,91 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * A {@link NX.util.condition.Condition} that is satisfied when a grid, specified by its selector, exists and has a + * selection. Optionally, a function could be used to provide additional checking when grid has a selection. + * + * @since 3.0 + */ +Ext.define('NX.util.condition.GridHasSelection', { + extend: 'NX.util.condition.Condition', + + /** + * A grid selector as specified by (@link Ext.ComponentQuery#query}. + * + * @cfg {String} + */ + grid: undefined, + + /** + * An optional function to be called when grid has a selection to perform additional checks on the + * passed in model. + * + * @cfg {Function} + */ + fn: undefined, + + /** + * @override + * @returns {NX.util.condition.GridHasSelection} + */ + bind: function () { + var me = this, + components = {}; + + if (!me.bounded) { + components[me.grid] = { + cellclick: function(list, td, cellIndex, model) { + me.evaluate(list, model); + }, + selection: me.evaluate, + selectionchange: function(list, selected) { + me.evaluate(list, selected ? selected[0] : null); + }, + destroy: me.evaluate + }; + Ext.app.EventBus.listen({ component: components }, me); + me.callParent(); + } + + return me; + }, + + /** + * @private + */ + evaluate: function (cmp, selection) { + var me = this, + satisfied = false; + + if (me.bounded) { + if (selection) { + satisfied = true; + if (Ext.isFunction(me.fn)) { + satisfied = me.fn(selection) === true; + } + } + me.setSatisfied(satisfied); + } + }, + + /** + * @override + * @returns {String} + */ + toString: function () { + return this.self.getName() + '{ grid=' + this.grid + ' }'; + } + +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/IsPermitted.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/IsPermitted.js new file mode 100644 index 0000000000000000000000000000000000000000..1b2fd5ebf3d41283653c11fd3aa813c7d2b6a802 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/IsPermitted.js @@ -0,0 +1,80 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * A {@link NX.util.condition.Condition} that is satisfied when user has a specified permission. + * + * @since 3.0 + */ +Ext.define('NX.util.condition.IsPermitted', { + extend: 'NX.util.condition.Condition', + + /** + * @private + * @property {String} + */ + permission: undefined, + + /** + * @override + * @returns {NX.util.condition.IsPermitted} + */ + bind: function () { + var me = this, + controller; + + if (!me.bounded) { + controller = NX.getApplication().getController('Permissions'); + me.mon(controller, { + changed: me.evaluate, + scope: me + }); + me.callParent(); + me.evaluate(); + } + + return me; + }, + + /** + * @private + */ + evaluate: function () { + var me = this; + + if (me.bounded) { + me.setSatisfied(NX.Permissions.check(me.permission)); + } + }, + + /** + * @override + * @returns {String} + */ + toString: function () { + return this.self.getName() + '{ permission=' + this.permission + ' }'; + }, + + /** + * Sets permission and re-evaluate. + * + * @public + * @param {String} permission + */ + setPermission: function(permission) { + this.permission = permission; + this.evaluate(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/NeverSatisfied.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/NeverSatisfied.js new file mode 100644 index 0000000000000000000000000000000000000000..278dea4331bf1f1352a7413868efb196f1006ed6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/NeverSatisfied.js @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/** + * A {@link NX.util.condition.Condition} that is never satisfied. + * + * @since 3.0 + */ +Ext.define('NX.util.condition.NeverSatisfied', { + extend: 'NX.util.condition.Condition', + + /** + * @override + * @returns {String} + */ + toString: function () { + return this.self.getName() + '{ never satisfied }'; + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/StoreHasRecords.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/StoreHasRecords.js new file mode 100644 index 0000000000000000000000000000000000000000..9be316f214b0d12931ca5d50c4f959a6a319d254 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/StoreHasRecords.js @@ -0,0 +1,71 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * A {@link NX.util.condition.Condition} that is satisfied when specified store has records. + * + * @since 3.0 + */ +Ext.define('NX.util.condition.StoreHasRecords', { + extend: 'NX.util.condition.Condition', + + /** + * Id of store to be monitored. + * + * @cfg {String} + */ + store: undefined, + + /** + * @override + * @returns {NX.util.condition.StoreHasRecords} + */ + bind: function () { + var me = this, + store; + + if (!me.bounded) { + store = NX.getApplication().getStore(me.store); + me.mon(store, { + datachanged: me.evaluate, + beforeload: Ext.pass(me.evaluate, [undefined]), + scope: me + }); + me.callParent(); + me.evaluate(store); + } + + return me; + }, + + /** + * @private + */ + evaluate: function (store) { + var me = this; + + if (me.bounded) { + me.setSatisfied(Ext.isDefined(store) && (store.getCount() > 0)); + } + }, + + /** + * @override + * @returns {String} + */ + toString: function () { + return this.self.getName() + '{ store=' + this.store + ' }'; + } + +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/WatchState.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/WatchState.js new file mode 100644 index 0000000000000000000000000000000000000000..7f2dae98a94a691717550c20ec95efa13407a754 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/condition/WatchState.js @@ -0,0 +1,85 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * A {@link NX.util.condition.Condition} that is satisfied applying a function on a state value. + * + * @since 3.0 + */ +Ext.define('NX.util.condition.WatchState', { + extend: 'NX.util.condition.Condition', + requires: [ + 'NX.State' + ], + + /** + * @cfg {String} + * + * State value key. + */ + key: undefined, + + /** + * An optional function to be called when a state value changes. If not specified, a boolean check + * against value will be performed. + * + * @cfg {Function} + */ + fn: undefined, + + /** + * @override + * @returns {NX.util.condition.WatchState} + */ + bind: function () { + var me = this, + controller, listeners; + + if (!me.bounded) { + if (!Ext.isDefined(me.fn)) { + me.fn = function (value) { + return value; + }; + } + controller = NX.getApplication().getController('State'); + listeners = { scope: me }; + listeners[me.key.toLowerCase() + 'changed'] = me.evaluate; + me.mon(controller, listeners); + me.callParent(); + me.evaluate(NX.State.getValue(me.key)); + } + + return me; + }, + + /** + * @private + */ + evaluate: function (value, oldValue) { + var me = this; + + if (me.bounded) { + me.setSatisfied(me.fn(value, oldValue)); + } + }, + + /** + * @override + * @returns {String} + */ + toString: function () { + return this.self.getName() + '{ key=' + this.key + ' }'; + } + +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/log/ConsoleSink.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/log/ConsoleSink.js new file mode 100644 index 0000000000000000000000000000000000000000..287e74c4c3b35ff3f7aa425d66fb586d22e81a26 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/log/ConsoleSink.js @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Console {@link NX.util.log.Sink}. + * + * Emits events to the browser console. + * + * @since 3.0 + */ +Ext.define('NX.util.log.ConsoleSink', { + extend: 'NX.util.log.Sink', + requires: [ + 'NX.Console' + ], + + // default to disabled + enabled: false, + + /** + * @override + */ + receive: function (event) { + NX.Console.recordEvent(event); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/log/RemoteSink.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/log/RemoteSink.js new file mode 100644 index 0000000000000000000000000000000000000000..4c275b69aa9e9416fc11ac61dbc3abb3830468de --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/log/RemoteSink.js @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Remote {@link NX.util.log.Sink}. + * + * Sends events to server via Ext.Direct. + * + * @since 3.0 + */ +Ext.define('NX.util.log.RemoteSink', { + extend: 'NX.util.log.Sink', + + // default to disabled + enabled: false, + + /** + * @override + */ + receive: function (event) { + // copy event to transform message + var copy = Ext.clone(event); + copy.message = copy.message.join(' '); + NX.direct.rapture_LogEvent.recordEvent(copy); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/log/Sink.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/log/Sink.js new file mode 100644 index 0000000000000000000000000000000000000000..efd96719360f3ed3e33a87d33f13795d37e6e54c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/log/Sink.js @@ -0,0 +1,81 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Allows customizable processing of {@link NX.model.LogEvent}. + * + * @since 3.0 + */ +Ext.define('NX.util.log.Sink', { + mixins: { + stateful: 'Ext.state.Stateful', + logAware: 'NX.LogAware' + }, + + /** + * Sink enabled. + * + * @property {Boolean} + * @readonly + */ + enabled: true, + + /** + * @constructor + */ + constructor: function () { + // setup stateful configuration with class-name, these are not technically singletons but are used as such + this.mixins.stateful.constructor.call(this, { + stateful: true, + stateId: this.self.getName() + }); + + this.callParent(arguments); + this.initState(); + }, + + /** + * @override + * @return {Object} + */ + getState: function() { + return { + enabled: this.enabled + }; + }, + + /** + * Toggle enabled. + * + * @public + * @param {boolean} flag + */ + setEnabled: function (flag) { + this.enabled = flag; + + // + this.logInfo('Enabled:', flag); + // + + this.saveState(); + }, + + /** + * @public + * @param {NX.model.LogEvent} event + */ + receive: function(event) { + throw 'abstract-method'; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/log/StoreSink.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/log/StoreSink.js new file mode 100644 index 0000000000000000000000000000000000000000..161f62853cddf4f11751ea863c751a30a026c8d5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/util/log/StoreSink.js @@ -0,0 +1,120 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Store {@link NX.util.log.Sink} into {@link NX.store.LogEvent} store. + * + * @since 3.0 + */ +Ext.define('NX.util.log.StoreSink', { + extend: 'NX.util.log.Sink', + requires: [ + 'NX.Assert' + ], + + /** + * Reference to the event store. + * + * @private + * @property {NX.store.LogEvent} + */ + store: undefined, + + /** + * Maximum records to retain in the store. + * + * @public + * @property {Number} + * @readonly + */ + maxSize: 200, + + /** + * @constructor + * @param {NX.store.LogEvent} store + */ + constructor: function (store) { + this.store = store; + this.callParent(arguments); + }, + + /** + * Customize state. + * + * @override + * @return {Object} + */ + getState: function() { + return Ext.apply(this.callParent(), { + maxSize: this.maxSize + }); + }, + + /** + * Set the maximum size of the store. + * + * @public + * @param {Number} maxSize + */ + setMaxSize: function (maxSize) { + this.maxSize = maxSize; + + // log here should induce shrinkage, nothing more to do + this.logDebug('Max size:', maxSize); + + this.saveState(); + }, + + /** + * Array of ordered records for shrinking. + * + * @private + * @property {NX.model.LogEvent[]} + */ + records: [], + + /** + * @override + */ + receive: function (event) { + // + NX.Assert.assert(this.store, 'Store not attached'); + // + + // only 1 record, pick off first + var record = this.store.add(event)[0]; + + // maybe shrink + this.shrink(); + + // track records for shrinkage + this.records.push(record); + }, + + /** + * Shrink the store after we breach maximum size. + * + * @private + */ + shrink: function () { + // calculate number of records to purge + var remove = this.records.length - this.maxSize; + + // maybe purge records + if (remove > 0) { + var purged = this.records.splice(0, remove); + this.store.remove(purged); + } + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/AboutWindow.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/AboutWindow.js new file mode 100644 index 0000000000000000000000000000000000000000..569f6c547a45d2e00e66219c5b96e18697569f2a --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/AboutWindow.js @@ -0,0 +1,104 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * About window. + * + * @since 3.0 + */ +Ext.define('NX.view.AboutWindow', { + extend: 'NX.view.ModalDialog', + alias: 'widget.nx-aboutwindow', + requires: [ + 'NX.I18n', + 'NX.Icons', + 'NX.State', + 'NX.util.Url' + ], + + cls: 'nx-aboutwindow', + + /** + * @override + */ + initComponent: function () { + var me = this; + + me.layout = { + type: 'vbox', + align: 'stretch' + }; + + me.height = 480; + me.width = NX.view.ModalDialog.LARGE_MODAL; + + me.title = NX.I18n.get('AboutWindow_Title'); + + me.items = [ + { + xtype: 'container', + cls: 'summary', + layout: { + type: 'hbox', + align: 'stretch' + }, + items: [ + { + xtype: 'component', + cls: 'logo', + html: NX.Icons.img('nexus', 'x100') + }, + { + xtype: 'nx-info', + itemId: 'aboutInfo', + flex: 1 + } + ] + }, + { + xtype: 'tabpanel', + ui: 'nx-light', + flex: 1, + items: [ + { + title: NX.I18n.get('AboutWindow_About_Title'), + xtype: 'uxiframe', + src: NX.util.Url.urlOf('/COPYRIGHT.html') + }, + { + title: NX.I18n.get('AboutWindow_License_Tab'), + xtype: 'uxiframe', + src: NX.util.Url.urlOf('/LICENSE.html') + } + ] + } + ]; + + me.buttons = [ + { text: NX.I18n.get('Button_Close'), action: 'close', ui: 'nx-primary', handler: function () { me.close(); }} + ]; + me.buttonAlign = 'left'; + + me.callParent(); + + // populate initial details + me.down('#aboutInfo').showInfo({ + 'Version': NX.State.getVersion(), + 'Edition': NX.State.getEdition(), + 'Build Revision': NX.State.getBuildRevision(), + 'Build Timestamp': NX.State.getBuildTimestamp() + }); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/AddPanel.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/AddPanel.js new file mode 100644 index 0000000000000000000000000000000000000000..875ed73b9a2c2930a9deeedc3dde6cb3b4ade393 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/AddPanel.js @@ -0,0 +1,65 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Abstract add window. + * + * @since 3.0 + */ +Ext.define('NX.view.AddPanel', { + extend: 'Ext.panel.Panel', + alias: 'widget.nx-addpanel', + requires: [ + 'NX.I18n' + ], + + cls: 'nx-hr', + + layout: { + type: 'vbox', + align: 'stretch' + }, + + autoScroll: true, + + /** + * @override + */ + initComponent: function () { + var me = this; + + // Create default buttons if they do not exist + if (Ext.isDefined(me.settingsForm) && !Ext.isArray(me.settingsForm)) { + if (!me.settingsForm.buttons) { + me.settingsForm.buttons = [ + { text: NX.I18n.get('Add_Submit_Button'), action: 'add', formBind: true, ui: 'nx-primary', bindToEnter: me.items.settingsFormSubmitOnEnter }, + { text: NX.I18n.get('Add_Cancel_Button'), handler: function () { + this.up('nx-drilldown').showChild(0, true); + }} + ]; + } + } + + // Add settings form to the panel + me.items = { + xtype: 'panel', + ui: 'nx-inset', + + items: me.settingsForm + }; + + me.callParent(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/AddWindow.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/AddWindow.js new file mode 100644 index 0000000000000000000000000000000000000000..22f33edf8fa3b0e504f40766f3d31a662772465c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/AddWindow.js @@ -0,0 +1,56 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Abstract add window. + * + * @since 3.0 + */ +Ext.define('NX.view.AddWindow', { + extend: 'Ext.window.Window', + alias: 'widget.nx-addwindow', + requires: [ + 'NX.I18n' + ], + + layout: 'fit', + autoShow: true, + modal: true, + constrain: true, + width: 630, + minWidth: 630, + + /** + * @override + */ + initComponent: function () { + var me = this; + + if (Ext.isDefined(me.items) && !Ext.isArray(me.items)) { + if (!me.items.buttons) { + me.items.buttons = [ + { text: NX.I18n.get('Add_Submit_Button'), action: 'add', formBind: true, ui: 'nx-primary', bindToEnter: me.items.settingsFormSubmitOnEnter }, + { text: NX.I18n.get('Add_Cancel_Button'), handler: function () { + this.up('window').close(); + }} + ]; + } + } + + me.maxHeight = Ext.getBody().getViewSize().height - 100; + + me.callParent(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/Authenticate.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/Authenticate.js new file mode 100644 index 0000000000000000000000000000000000000000..a384bdbd021cc556bcc1e631af319b8437670012 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/Authenticate.js @@ -0,0 +1,97 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Authenticate window. + * + * @since 3.0 + */ +Ext.define('NX.view.Authenticate', { + extend: 'NX.view.ModalDialog', + alias: 'widget.nx-authenticate', + requires: [ + 'NX.Icons', + 'NX.I18n' + ], + + cls: 'nx-authenticate', + + /** + * @cfg message Message to be shown + */ + message: undefined, + + /** + * @protected + */ + initComponent: function () { + var me = this; + + me.ui = 'nx-inset'; + me.title = NX.I18n.get('Authenticate_Title'); + me.defaultFocus = 'password'; + + me.setWidth(NX.view.ModalDialog.SMALL_MODAL); + + if (!me.message) { + me.message = NX.I18n.get('Authenticate_Help_Text'); + } + + Ext.apply(this, { + items: { + xtype: 'form', + defaultType: 'textfield', + defaults: { + anchor: '100%' + }, + items: [ + { + xtype: 'container', + layout: 'hbox', + cls: 'message', + items: [ + { xtype: 'component', html: NX.Icons.img('authenticate', 'x32') }, + { xtype: 'component', html: '
' + me.message + '
' } + ] + }, + { + name: 'username', + itemId: 'username', + emptyText: NX.I18n.get('SignIn_Username_Empty'), + allowBlank: false, + readOnly: true + }, + { + name: 'password', + itemId: 'password', + inputType: 'password', + emptyText: NX.I18n.get('SignIn_Password_Empty'), + allowBlank: false, + // allow cancel to be clicked w/o validating this to be non-blank + validateOnBlur: false + } + ], + + buttonAlign: 'left', + buttons: [ + { text: NX.I18n.get('User_View_Authenticate_Submit_Button'), action: 'authenticate', formBind: true, bindToEnter: true, ui: 'nx-primary' }, + { text: NX.I18n.get('Authenticate_Cancel_Button'), handler: me.close, scope: me } + ] + } + }); + + me.callParent(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/ChangeOrderWindow.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/ChangeOrderWindow.js new file mode 100644 index 0000000000000000000000000000000000000000..c4a5690944fec8abf0b468b0d41a31b2fd0ba977 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/ChangeOrderWindow.js @@ -0,0 +1,63 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Abstract change order window. + * + * @since 3.0 + */ +Ext.define('NX.view.ChangeOrderWindow', { + extend: 'NX.view.ModalDialog', + alias: 'widget.nx-changeorderwindow', + requires: [ + 'NX.ext.form.field.ItemOrderer', + 'NX.I18n' + ], + ui: 'nx-inset', + + displayField: 'name', + valueField: 'id', + + /** + * @override + */ + initComponent: function () { + var me = this; + + me.setWidth(NX.view.ModalDialog.MEDIUM_MODAL); + + me.items = { + xtype: 'form', + items: { + xtype: 'nx-itemorderer', + store: me.store, + displayField: me.displayField, + valueField: me.valueField, + delimiter: null, + height: 400, + width: 400 + }, + buttonAlign: 'left', + buttons: [ + { text: NX.I18n.get('ChangeOrderWindow_Submit_Button'), action: 'save', formBind: true, ui: 'nx-primary' }, + { text: NX.I18n.get('ChangeOrderWindow_Cancel_Button'), handler: function () { + this.up('window').close(); + }} + ] + }; + + me.callParent(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/CopyWindow.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/CopyWindow.js new file mode 100644 index 0000000000000000000000000000000000000000..741f7350d68e2fe8fc6fca0daac242c15d82b9a5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/CopyWindow.js @@ -0,0 +1,95 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * About window. + * + * @since 3.0 + */ +Ext.define('NX.view.CopyWindow', { + extend: 'NX.view.ModalDialog', + alias: 'widget.nx-copywindow', + requires: [ + 'NX.I18n', + 'NX.Icons' + ], + + layout: { + type: 'vbox', + align: 'stretch' + }, + + ui: 'nx-inset', + + /** + * @property + * The text to be selected for copying + */ + copyText: '', + + /** + * @property + * The message to use when prompting the user to copy/paste + */ + defaultMessage: 'Copy to clipboard: #{key}, Enter', + + /** + * @override + */ + initComponent: function () { + var me = this, + message = this.format(this.defaultMessage); + + me.width = NX.view.ModalDialog.MEDIUM_MODAL; + + me.title = message; + me.items = { + xtype: 'form', + defaults: { + anchor: '100%' + }, + items: { + xtype: 'textfield', + name: 'url', + value: me.copyText, + selectOnFocus: true + }, + buttonAlign: 'left', + buttons: [ + { + text: NX.I18n.get('Button_Close'), + action: 'close', + bindToEnter: true, + handler: function () { + me.close(); + } + } + ] + }; + me.defaultFocus = 'textfield'; + + me.callParent(); + }, + + /** + * @private + * @param Substitute the keyboard shortcut for copy, given the current platform + * @returns {string} + */ + format: function (message) { + var copyKey = (/mac os x/i.test(navigator.userAgent) ? '⌘' : 'Ctrl') + '+C'; + return message.replace(/#{\s*key\s*}/g, copyKey); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/ExpireSession.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/ExpireSession.js new file mode 100644 index 0000000000000000000000000000000000000000..95542a7e252d2915e06487268a21eebb70172766 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/ExpireSession.js @@ -0,0 +1,87 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Expire session window. + * + * @since 3.0 + */ +Ext.define('NX.view.ExpireSession', { + extend: 'NX.view.ModalDialog', + requires: [ + 'NX.I18n' + ], + alias: 'widget.nx-expire-session', + + cls: 'nx-expire-session', + + /** + * @override + */ + initComponent: function () { + var me = this; + + me.title = NX.I18n.get('ExpireSession_Title'); + + me.setWidth(NX.view.ModalDialog.MEDIUM_MODAL); + + Ext.apply(me, { + items: [ + { + xtype: 'label', + // FIXME: Why is this using global 'id'? + id: 'expire', + text: NX.I18n.get('ExpireSession_Help_Text') + } + ], + buttonAlign: 'left', + buttons: [ + { text: NX.I18n.get('ExpireSession_Cancel_Button'), action: 'cancel' }, + { + text: NX.I18n.get('ExpireSession_SignIn_Button'), + action: 'signin', + hidden: true, + itemId: 'expiredSignIn', + ui: 'nx-primary', + handler: function() { + // FIXME: simplify, me.close() + this.up('nx-expire-session').close(); + } + }, + { + text: NX.I18n.get('Button_Close'), + action: 'close', + hidden: true, + handler: function() { + // FIXME: simplify, me.close() + this.up('nx-expire-session').close(); + } + } + ] + }); + + me.callParent(); + }, + + /** + * Check to see if the dialog is showing that it is expired. + * + * @public + * @returns {boolean} + */ + sessionExpired: function() { + return this.down('#expiredSignIn').isVisible(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/Main.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/Main.js new file mode 100644 index 0000000000000000000000000000000000000000..102ce2eb0df0eee12089e92b0ee5b3fcf3593bf2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/Main.js @@ -0,0 +1,151 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Main uber mode panel. + * + * @since 3.0 + */ +Ext.define('NX.view.Main', { + extend: 'Ext.panel.Panel', + alias: 'widget.nx-main', + requires: [ + 'NX.I18n', + 'NX.Icons', + 'NX.view.header.QuickSearch' + ], + + layout: 'border', + + /** + * @override + */ + initComponent: function () { + var me = this; + + me.items = [ + { + xtype: 'panel', + layout: { + type: 'vbox', + align: 'stretch' + }, + items: [ + { + xtype: 'panel', + ui: 'nx-database-freeze-warning', + id: 'nx-database-freeze-warning', + iconCls: NX.Icons.cls('drilldown-warning', 'x16'), + hidden: true + }, + { + xtype: 'panel', + ui: 'nx-license-warning', + id: 'nx-license-warning', + iconCls: NX.Icons.cls('drilldown-warning', 'x16'), + hidden: true + }, + { + xtype: 'panel', + ui: 'nx-file-descriptor-warning', + id: 'nx-file-descriptor-warning', + iconCls: NX.Icons.cls('drilldown-warning', 'x16'), + hidden: true + }, + { + xtype: 'nx-header-panel' + } + ], + region: 'north', + collapsible: false + }, + + { + xtype: 'nx-feature-menu', + region: 'west', + border: false, + resizable: true, + resizeHandles: 'e' + }, + + { + xtype: 'nx-feature-content', + region: 'center', + border: true + }, + + { + xtype: 'nx-footer', + region: 'south', + hidden: false + }, + + { + xtype: 'nx-dev-panel', + region: 'south', + collapsible: true, + collapsed: true, + resizable: true, + resizeHandles: 'n', + + // keep initial constraints to prevent huge panels + height: 300, + + // default to hidden, only show if debug enabled + hidden: true + } + ]; + + me.callParent(); + + me.down('nx-header-panel>toolbar').add([ + // 2x pad + ' ', ' ', + { + xtype: 'nx-header-mode', + name: 'browse', + title: NX.I18n.get('Header_BrowseMode_Title'), + tooltip: NX.I18n.get('Header_BrowseMode_Tooltip'), + glyph: 'xf1b2@FontAwesome', /* fa-cube */ + autoHide: true, + collapseMenu: true + }, + { + xtype: 'nx-header-mode', + name: 'admin', + title: NX.I18n.get('Header_AdminMode_Title'), + tooltip: NX.I18n.get('Header_AdminMode_Tooltip'), + glyph: 'xf013@FontAwesome', /* fa-gear */ + autoHide: true, + collapseMenu: false + }, + ' ', + {xtype: 'nx-header-quicksearch', hidden: true}, + '->', + {xtype: 'nx-header-refresh', ui: 'nx-header'}, + {xtype: 'nx-header-help', ui: 'nx-header'}, + { + xtype: 'nx-header-mode', + name: 'user', + title: NX.I18n.get('Header_UserMode_Title'), + glyph: 'xf007@FontAwesome', // fa-user + autoHide: false, + collapseMenu: false + }, + {xtype: 'nx-header-signin', ui: 'nx-header'}, + {xtype: 'nx-header-signout', ui: 'nx-header'} + ]); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/ModalDialog.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/ModalDialog.js new file mode 100644 index 0000000000000000000000000000000000000000..39ea2661d58dbf0fdb6c525ca76b424bddc91b2a --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/ModalDialog.js @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Custom modal dialog window. + * + * @since 3.0 + */ +Ext.define('NX.view.ModalDialog', { + extend: 'Ext.window.Window', + alias: 'widget.nx-modal-dialog', + + layout: 'fit', + autoShow: true, + modal: true, + constrain: true, + closable: true, + resizable: false, + + // Standard modal widths + statics: { + SMALL_MODAL: 320, + MEDIUM_MODAL: 480, + LARGE_MODAL: 640 + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/SettingsForm.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/SettingsForm.js new file mode 100644 index 0000000000000000000000000000000000000000..cdaac4370c165e7148ad9e2ad99a1b184fcccab1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/SettingsForm.js @@ -0,0 +1,248 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Abstract settings form. + * + * @since 3.0 + */ +Ext.define('NX.view.SettingsForm', { + extend: 'Ext.form.Panel', + requires: [ + 'NX.I18n' + ], + alias: 'widget.nx-settingsform', + ui: 'nx-subsection', + frame: true, + + /** + * Set trackResetOnLoad by default. + * + * @private + */ + constructor: function (config) { + config = config || {}; + config.trackResetOnLoad = true; + this.callParent([config]); + }, + + /** + * @cfg {boolean} [settingsForm=true] Marker that we have a settings form + * ({NX.controller.SettingsForm} controller kicks in) + */ + settingsForm: true, + + /** + * @cfg {boolean} [settingsFormSubmit=true] True if settings form should be submitted automatically when 'submit' + * button is clicked. Set this to false if custom processing is needed. + */ + settingsFormSubmit: true, + + /** + * @cfg {boolean} [settingsFormSubmitOnEnter=false] True if form should be submitted on Enter. + */ + settingsFormSubmitOnEnter: false, + + /** + * @cfg {String/Function} Text to be used when displaying submit/load messages. If is a function it will be called + * with submit/load response data as parameter and it should return a String. + * If text contains "${action}", it will be replaced with performed action. + */ + settingsFormSuccessMessage: undefined, + + /** + * @cfg {String} [settingsFormLoadMessage] Text to be used as mask while loading data. + */ + settingsFormLoadMessage: undefined, + + /** + * @cfg {String} [settingsFormSubmitMessage] Text to be used as mask while submitting data. + */ + settingsFormSubmitMessage: undefined, + + /** + * @cfg {NX.util.condition.Condition} The condition to be satisfied in order for this form to be editable. + */ + editableCondition: undefined, + + /** + * @cfg {String} Optional text to be shown in case that form is not editable (condition is not satisfied). + */ + editableMarker: undefined, + + autoScroll: true, + waitMsgTarget: true, + + defaults: { + xtype: 'textfield', + allowBlank: false + }, + + buttonAlign: 'left', + + buttons: 'defaultButtons', + + /** + * @override + */ + initComponent: function () { + var me = this; + + if (me.buttons === 'defaultButtons') { + me.buttons = [ + { + text: NX.I18n.get('SettingsForm_Save_Button'), + action: 'save', + ui: 'nx-primary', + bindToEnter: false + }, + { + text: NX.I18n.get('SettingsForm_Discard_Button'), + action: 'discard' + } + ]; + } + + Ext.applyIf(me, { + settingsFormLoadMessage: NX.I18n.get('SettingsForm_Load_Message'), + settingsFormSubmitMessage: NX.I18n.get('SettingsForm_Submit_Message') + }); + + if (me.buttons && Ext.isArray(me.buttons) && me.buttons[0] && Ext.isDefined(me.buttons[0].bindToEnter)) { + me.buttons[0].bindToEnter = me.settingsFormSubmitOnEnter; + } + + me.callParent(); + + me.addEvents( + /** + * Fires when a record is loaded via {@link Ext.form.Panel#loadRecord}. + * + * @event recordloaded + * @param {Ext.form.Panel} this form + * @param {Ext.data.Model} loaded record + */ + 'recordloaded', + + /** + * Fires after form was loaded via configured api. + * + * @event loaded + * @param {Ext.form.Panel} this form + * @param {Ext.form.action.Action} load action + */ + 'loaded', + + /** + * Fires after form was submitted via configured api. + * + * @event submitted + * @param {Ext.form.Panel} this form + * @param {Ext.form.action.Action} submit action + */ + 'submitted' + ); + }, + + /** + * Fires 'recordloaded' after record was loaded. + * + * @override + */ + loadRecord: function (record) { + var me = this; + + me.callParent(arguments); + me.fireEvent('recordloaded', me, record); + }, + + /** + * Sets the read only state for all fields of this form. + * + * @public + * @param {boolean} editable + */ + setEditable: function (editable) { + var me = this, + itemsToDisable = me.getChildItemsToDisable(), + bottomBar; + + if (editable) { + Ext.Array.each(itemsToDisable, function (item) { + var enable = true, + form; + + if (item.resetEditable) { + if (Ext.isFunction(item.setReadOnly)) { + item.setReadOnly(false); + } + else { + if (Ext.isDefined(item.resetFormBind)) { + item.formBind = item.resetFormBind; + } + if (item.formBind) { + form = item.up('form'); + if (form && !form.isValid()) { + enable = false; + } + } + if (enable) { + item.enable(); + } + } + } + if (Ext.isDefined(item.resetEditable)) { + delete item.resetEditable; + delete item.resetFormBind; + } + }); + } + else { + Ext.Array.each(itemsToDisable, function (item) { + if (Ext.isFunction(item.setReadOnly)) { + if (item.resetEditable !== false && !item.readOnly) { + item.setReadOnly(true); + item.resetEditable = true; + } + } + else { + if (item.resetEditable !== false && !item.disabled) { + item.disable(); + item.resetFormBind = item.formBind; + delete item.formBind; + item.resetEditable = true; + } + } + }); + } + + bottomBar = me.getDockedItems('toolbar[dock="bottom"]')[0]; + if (bottomBar) { + if (bottomBar.editableMarker) { + bottomBar.remove(bottomBar.editableMarker); + bottomBar.editableMarker = undefined; + } + + if (!editable && me.editableMarker) { + bottomBar.editableMarker = Ext.widget({ + xtype: 'label', + text: me.editableMarker, + cls: 'nx-form-important-msg' + }); + bottomBar.add(bottomBar.editableMarker); + } + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/SettingsPanel.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/SettingsPanel.js new file mode 100644 index 0000000000000000000000000000000000000000..2297073fe67fd4f20378118344d35a0eaeecee9b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/SettingsPanel.js @@ -0,0 +1,81 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Abstract settings panel. + * + * @since 3.0 + */ +Ext.define('NX.view.SettingsPanel', { + extend: 'Ext.panel.Panel', + alias: 'widget.nx-settingsPanel', + autoScroll: true, + + cls: 'nx-hr', + + layout: { + type: 'vbox', + align: 'stretch' + }, + + // TODO maxWidth: 1024, + + /** + * @override + */ + initComponent: function() { + var me = this; + + me.items = { + xtype: 'panel', + ui: 'nx-inset', + + items: me.settingsForm || [] + }; + + me.callParent(); + }, + + /** + * @override + * @param form The form to add to this settings panel + */ + addSettingsForm: function(form) { + this.down('panel').add(form); + }, + + /** + * Remove all settings forms from this settings panel. + * + * @override + */ + removeAllSettingsForms: function() { + this.down('panel').removeAll(); + }, + + /** + * Loads an {@link Ext.data.Model} into this form + * (internally just calls {@link NX.view.SettingsForm#loadRecord}). + * + * @public + * @param model The model to load + */ + loadRecord: function(model) { + var settingsForm = this.down('nx-settingsform'); + if (settingsForm) { + settingsForm.loadRecord(model); + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/SignIn.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/SignIn.js new file mode 100644 index 0000000000000000000000000000000000000000..13e5947d6a8207798c3eba459f31e2bb471777a7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/SignIn.js @@ -0,0 +1,77 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Sign-in window. + * + * @since 3.0 + */ +Ext.define('NX.view.SignIn', { + extend: 'NX.view.ModalDialog', + alias: 'widget.nx-signin', + requires: [ + 'NX.I18n' + ], + + /** + * @override + */ + initComponent: function () { + var me = this; + + me.ui = 'nx-inset'; + me.title = NX.I18n.get('SignIn_Title'); + me.defaultFocus = 'username'; + + me.setWidth(NX.view.ModalDialog.SMALL_MODAL); + + Ext.apply(me, { + items: { + xtype: 'form', + defaultType: 'textfield', + defaults: { + anchor: '100%' + }, + items: [ + { + name: 'username', + itemId: 'username', + emptyText: NX.I18n.get('SignIn_Username_Empty'), + allowBlank: false, + // allow cancel to be clicked w/o validating this to be non-blank + validateOnBlur: false + }, + { + name: 'password', + itemId: 'password', + inputType: 'password', + emptyText: NX.I18n.get('SignIn_Password_Empty'), + allowBlank: false, + // allow cancel to be clicked w/o validating this to be non-blank + validateOnBlur: false + } + ], + + buttonAlign: 'left', + buttons: [ + { text: NX.I18n.get('SignIn_Submit_Button'), action: 'signin', formBind: true, bindToEnter: true, ui: 'nx-primary' }, + { text: NX.I18n.get('SignIn_Cancel_Button'), handler: me.close, scope: me } + ] + } + }); + + me.callParent(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/Unlicensed.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/Unlicensed.js new file mode 100644 index 0000000000000000000000000000000000000000..50aa8fe902c4feef6ab55e61a8712a187b9e2824 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/Unlicensed.js @@ -0,0 +1,77 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +// TODO: This is a placeholder view for what to display to the user when a license is required and missing or invalid + +/** + * Unlicensed uber mode panel. + * + * @since 3.0 + */ +Ext.define('NX.view.Unlicensed', { + extend: 'Ext.container.Container', + alias: 'widget.nx-unlicensed', + + cls: 'nx-unlicensed', + layout: 'border', + + items: [ + { + xtype: 'nx-header-panel', + region: 'north', + collapsible: false + }, + { + xtype: 'panel', + region: 'center', + layout: { + type: 'vbox', + align: 'center', + pack: 'center' + }, + items: [ + { + xtype: 'label', + cls: 'title', + html: 'Product License Required' + }, + { + xtype: 'label', + cls: 'description', + text: 'A license is required to use this product.' + } + ] + }, + { + xtype: 'nx-footer', + region: 'south', + hidden: false + }, + { + xtype: 'nx-dev-panel', + region: 'south', + collapsible: true, + collapsed: true, + resizable: true, + resizeHandles: 'n', + + // keep initial constraints to prevent huge panels + height: 300, + + // default to hidden, only show if debug enabled + hidden: true + } + ] + +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/UnsavedChanges.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/UnsavedChanges.js new file mode 100644 index 0000000000000000000000000000000000000000..17bc4d4668367d7bdb712fa2133c8251fd80f641 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/UnsavedChanges.js @@ -0,0 +1,78 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Unsaved changes window. + * + * @since 3.0 + */ +Ext.define('NX.view.UnsavedChanges', { + extend: 'NX.view.ModalDialog', + requires: [ + 'NX.I18n' + ], + alias: 'widget.nx-unsaved-changes', + + /** + * Panel with content to be saved. + * + * @public + */ + content: null, + + /** + * Function to call if content is to be discarded. + * + * @public + */ + callback: Ext.emptyFn, + + /** + * @override + */ + initComponent: function () { + var me = this; + + me.title = NX.I18n.get('UnsavedChanges_Title'); + me.defaultFocus = 'nx-discard'; + + me.setWidth(NX.view.ModalDialog.SMALL_MODAL); + + Ext.apply(me, { + items: { + xtype: 'panel', + ui: 'nx-inset', + html: NX.I18n.get('UnsavedChanges_Help_HTML'), + buttonAlign: 'left', + buttons: [ + { + text: NX.I18n.get('UnsavedChanges_Discard_Button'), + ui: 'nx-primary', + itemId: 'nx-discard', + handler: function () { + // Discard changes and load new content + me.content.resetUnsavedChangesFlag(true); + me.callback(); + me.close(); + } + }, + { text: NX.I18n.get('UnsavedChanges_Back_Button'), handler: me.close, scope: me } + ] + } + }); + + me.callParent(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/UnsupportedBrowser.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/UnsupportedBrowser.js new file mode 100644 index 0000000000000000000000000000000000000000..302506a9dfdfee2679870a067c964597b22ee1bc --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/UnsupportedBrowser.js @@ -0,0 +1,104 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Unsupported browser uber mode panel. + * + * @since 3.0 + */ +Ext.define('NX.view.UnsupportedBrowser', { + extend: 'Ext.container.Container', + alias: 'widget.nx-unsupported-browser', + requires: [ + 'NX.I18n', + 'NX.Icons' + ], + + cls: 'nx-unsupported-browser', + layout: 'border', + + /** + * @override + */ + initComponent: function () { + var me = this; + + me.items = [ + { + xtype: 'nx-header-panel', + region: 'north', + collapsible: false + }, + + { + xtype: 'container', + region: 'center', + layout: { + type: 'vbox', + align: 'center', + pack: 'center' + }, + items: [ + { + xtype: 'label', + cls: 'title', + text: NX.I18n.get('UnsupportedBrowser_Title') + }, + { + xtype: 'label', + cls: 'description', + text: NX.I18n.get('UnsupportedBrowser_Alternatives_Text') + }, + { + xtype: 'container', + cls: 'icons', + layout: { + type: 'hbox' + }, + items: [ + { xtype: 'image', width: 72, height: 72, src: NX.Icons.url('chrome', 'x72') }, + { xtype: 'image', width: 72, height: 72, src: NX.Icons.url('firefox', 'x72') }, + { xtype: 'image', width: 72, height: 72, src: NX.Icons.url('ie', 'x72') }, + { xtype: 'image', width: 72, height: 72, src: NX.Icons.url('opera', 'x72') }, + { xtype: 'image', width: 72, height: 72, src: NX.Icons.url('safari', 'x72') } + ] + }, + { xtype: 'button', text: NX.I18n.get('UnsupportedBrowser_Continue_Button'), action: 'continue' } + ] + }, + { + xtype: 'nx-footer', + region: 'south', + hidden: false + }, + { + xtype: 'nx-dev-panel', + region: 'south', + collapsible: true, + collapsed: true, + resizable: true, + resizeHandles: 'n', + + // keep initial constraints to prevent huge panels + height: 300, + + // default to hidden, only show if debug enabled + hidden: true + } + ]; + + me.callParent(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/Viewport.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/Viewport.js new file mode 100644 index 0000000000000000000000000000000000000000..d30f0942443c46ea9d57c553aa5dcad88580be15 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/Viewport.js @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Nexus viewport. + * + * @since 3.0 + */ +Ext.define('NX.view.Viewport', { + extend: 'Ext.container.Viewport', + + layout: 'fit', + + items: [] +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dashboard/Welcome.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dashboard/Welcome.js new file mode 100644 index 0000000000000000000000000000000000000000..ddab0f1d816797dd2374537f1be1a7f4fc9d9e02 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dashboard/Welcome.js @@ -0,0 +1,30 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Welcome dashboard. + * + * @since 3.0 + */ +Ext.define('NX.view.dashboard.Welcome', { + extend: 'Ext.container.Container', + alias: 'widget.nx-dashboard-welcome', + requires: [ + 'NX.Icons' + ], + + cls: 'nx-iframe-full', + width: '100%', + layout: 'fit' +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Conditions.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Conditions.js new file mode 100644 index 0000000000000000000000000000000000000000..cb75cd077c1aa5eb9270af0c47a76078fcd20658 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Conditions.js @@ -0,0 +1,56 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Developer Conditions grid. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.Conditions', { + extend: 'Ext.grid.Panel', + alias: 'widget.nx-dev-conditions', + + title: 'Conditions', + store: 'NX.store.dev.Condition', + emptyText: 'No condition', + viewConfig: { + deferEmptyText: false + }, + + columns: [ + { text: 'id', dataIndex: 'id', flex: 1 }, + { text: 'condition', dataIndex: 'condition', flex: 3 }, + { + xtype: 'nx-iconcolumn', + text: 'satisfied', + dataIndex: 'satisfied', + width: 80, + align: 'center', + iconVariant: 'x16', + iconName: function (value) { + return value ? 'permission-granted' : 'permission-denied'; + } + } + ], + + plugins: [ + 'gridfilterbox' + ], + + tbar : [ + { xtype: 'checkbox', itemId: 'showSatisfied', boxLabel: 'Show Satisfied', value: true }, + { xtype: 'checkbox', itemId: 'showUnsatisfied', boxLabel: 'Show Unsatisfied', value: true } + ] + +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Features.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Features.js new file mode 100644 index 0000000000000000000000000000000000000000..49b6845298bd778df3d0863d14f8edfa45ba9129 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Features.js @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * List of all known features. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.Features', { + extend: 'Ext.grid.Panel', + alias: 'widget.nx-dev-features', + + title: 'Features', + store: 'Feature', + emptyText: 'No features', + + columns: [ + { text: 'mode', dataIndex: 'mode', editor: 'textfield' }, + { text: 'path', dataIndex: 'path', editor: 'textfield', flex: 1 }, + { text: 'bookmark', dataIndex: 'bookmark', editor: 'textfield', flex: 1 }, + { text: 'weight', dataIndex: 'weight', width: 80, editor: 'textfield' }, + { text: 'view', dataIndex: 'view', editor: 'textfield', hidden: true }, + { text: 'help keyword', dataIndex: 'helpKeyword', editor: 'textfield', flex: 1 }, + { text: 'description', dataIndex: 'description', editor: 'textfield', flex: 1 }, + { text: 'iconName', dataIndex: 'iconName', editor: 'textfield' }, + { + xtype: 'nx-iconcolumn', + dataIndex: 'iconName', + width: 48, + iconVariant: 'x16' + }, + { + xtype: 'nx-iconcolumn', + dataIndex: 'iconName', + width: 48, + iconVariant: 'x32' + } + ], + + plugins: [ + { ptype: 'rowediting', clicksToEdit: 1 }, + 'gridfilterbox' + ], + + viewConfig: { + deferEmptyText: false, + markDirty: false + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Icons.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Icons.js new file mode 100644 index 0000000000000000000000000000000000000000..51ef221def8964693e0a6d4f4b198095c4e216dd --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Icons.js @@ -0,0 +1,55 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * List of all known icons. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.Icons', { + extend: 'Ext.grid.Panel', + alias: 'widget.nx-dev-icons', + + title: 'Icons', + store: 'Icon', + emptyText: 'No icons', + + viewConfig: { + deferEmptyText: false + }, + + columns: [ + { text: 'cls', dataIndex: 'cls', width: 200 }, + { text: 'name', dataIndex: 'name' }, + { text: 'file', dataIndex: 'file' }, + { text: 'variant', dataIndex: 'variant', width: 50 }, + { text: 'size', xtype: 'templatecolumn', tpl: '{height}x{width}', width: 80 }, + { text: 'url', xtype: 'templatecolumn', tpl: '{url}', flex: 1 }, + { text: 'img src', xtype: 'templatecolumn', tpl: '' }, + { + xtype: 'nx-iconcolumn', + text: 'img class', + dataIndex: 'cls', + iconCls: function(value) { + return value; + } + } + ], + + plugins: [ + { ptype: 'rowediting', clicksToEdit: 1 }, + 'gridfilterbox' + ] + +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Logging.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Logging.js new file mode 100644 index 0000000000000000000000000000000000000000..345ee8a327c1d64b9c638cdc74376bfa47bffaa6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Logging.js @@ -0,0 +1,135 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Logging dev-panel. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.Logging', { + extend: 'Ext.grid.Panel', + alias: 'widget.nx-dev-logging', + + title: 'Logging', + store: 'LogEvent', + emptyText: 'No events in buffer', + viewConfig: { + deferEmptyText: false, + // allow browser text selection + enableTextSelection: true + }, + multiSelect: true, + + stateful: true, + stateId: 'nx-dev-logging', + + columns: [ + {text: 'level', dataIndex: 'level'}, + {text: 'logger', dataIndex: 'logger', flex: 1}, + { + text: 'message', + dataIndex: 'message', + flex: 3, + renderer: function(value) { + return value.join(' '); + } + }, + {text: 'timestamp', dataIndex: 'timestamp', width: 130} + ], + + tbar: [ + { + xtype: 'button', + text: 'Clear events', + action: 'clear', + glyph: 'xf12d@FontAwesome' /* fa-eraser */ + }, + { + xtype: 'button', + text: 'Export selection', + action: 'export', + glyph: 'xf019@FontAwesome' /* fa-download */ + }, + '-', + { + xtype: 'label', + text: 'Threshold:' + }, + { + xtype: 'combo', + itemId: 'threshold', + store: 'LogLevel', + width: 80, + displayField: 'name', + valueField: 'name', + queryMode: 'local', + allowBlank: false, + editable: false + }, + '-', + { + xtype: 'checkbox', + itemId: 'buffer', + boxLabel: 'Buffer' + }, + { + xtype: 'numberfield', + itemId: 'bufferSize', + width: 50, + allowDecimals: false, + allowExponential: false, + minValue: -1, + maxValue: 999, + value: 200, + + // listen for key events + enableKeyEvents: true, + + // disable the spinner muck + hideTrigger: true, + mouseWheelEnabled: false, + keyNavEnabled: false + }, + '-', + { + xtype: 'checkbox', + itemId: 'console', + boxLabel: 'Mirror console' + }, + { + xtype: 'checkbox', + itemId: 'remote', + boxLabel: 'Remote events' + } + ], + + plugins: [ + { + ptype: 'rowexpander', + rowBodyTpl: Ext.create('Ext.XTemplate', + '', + '', + '', + '', + '
{[this.render(values)]}
', + { + compiled: true, + render: function (values) { + return Ext.encode(values.message); + } + }) + }, + {ptype: 'gridfilterbox'} + ] +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Panel.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Panel.js new file mode 100644 index 0000000000000000000000000000000000000000..2c299acbf9b1a84bc39bcb821626d9778a296d2b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Panel.js @@ -0,0 +1,73 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * The developer panel. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.Panel', { + extend: 'Ext.panel.Panel', + requires: [ + 'NX.view.dev.Styles' + ], + alias: 'widget.nx-dev-panel', + + title: 'Developer', + glyph: 'xf188@FontAwesome', // fa-bug + ui: 'nx-developer', + stateful: true, + stateId: 'nx-dev-panel', + + tools: [ + { type: 'maximize', tooltip: 'Maximize' } + ], + + layout: 'fit', + items: { + xtype: 'tabpanel', + tabPosition: 'bottom', + + stateful: true, + stateId: 'nx-dev-panel.tabs', + stateEvents: [ 'tabchange' ], + + /** + * @override + */ + getState: function() { + return { + activeTabId: this.items.findIndex('id', this.getActiveTab().id) + }; + }, + + /** + * @override + */ + applyState: function(state) { + this.setActiveTab(state.activeTabId); + }, + + items: [ + { xtype: 'nx-dev-tests' }, + { xtype: 'nx-dev-styles' }, + { xtype: 'nx-dev-icons' }, + { xtype: 'nx-dev-features' }, + { xtype: 'nx-dev-permissions' }, + { xtype: 'nx-dev-state' }, + { xtype: 'nx-dev-stores' }, + { xtype: 'nx-dev-logging' } + ] + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Permissions.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Permissions.js new file mode 100644 index 0000000000000000000000000000000000000000..ec0d36e37289d6550994f5a736bf3ccef2cb6414 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Permissions.js @@ -0,0 +1,61 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * List of permissions. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.Permissions', { + extend: 'Ext.grid.Panel', + requires: [ + 'NX.Permissions' + ], + alias: 'widget.nx-dev-permissions', + + title: 'Permissions', + store: 'Permission', + emptyText: 'No permissions', + + viewConfig: { + deferEmptyText: false, + markDirty: false + }, + + columns: [ + { text: 'permission', dataIndex: 'id', flex: 1, editor: { xtype: 'textfield', allowBlank: false } }, + { + xtype: 'nx-iconcolumn', + text: 'Permitted', + dataIndex: 'permitted', + width: 100, + align: 'center', + editor: 'checkbox', + iconVariant: 'x16', + iconName: function (value) { + return value ? 'permission-granted' : 'permission-denied'; + } + } + ], + + plugins: [ + { pluginId: 'editor', ptype: 'rowediting', clicksToEdit: 1, errorSummary: false }, + 'gridfilterbox' + ], + + tbar: [ + { xtype: 'button', text: 'Add', action: 'add' }, + { xtype: 'button', text: 'Delete', action: 'delete', disabled: true } + ] +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/State.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/State.js new file mode 100644 index 0000000000000000000000000000000000000000..4f37ba318c4d28a6f5b66b647ba9515c3e3a7ef8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/State.js @@ -0,0 +1,40 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * List of current state. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.State', { + extend: 'Ext.grid.Panel', + alias: 'widget.nx-dev-state', + + title: 'State', + store: 'State', + emptyText: 'No values', + viewConfig: { + deferEmptyText: false + }, + + columns: [ + { text: 'key', dataIndex: 'key', width: 250 }, + { text: 'hash', dataIndex: 'hash' }, + { text: 'value', dataIndex: 'value', flex: 1, + renderer: function (value) { + return Ext.JSON.encode(value); + } + } + ] +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Stores.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Stores.js new file mode 100644 index 0000000000000000000000000000000000000000..ca8154a8436a10ac7ec5846bcbf72a23b79924d3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Stores.js @@ -0,0 +1,100 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Stores developer panel controller. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.Stores', { + extend: 'Ext.panel.Panel', + alias: 'widget.nx-dev-stores', + requires: [ + 'Ext.data.Store', + 'Ext.data.StoreManager' + ], + + title: 'Stores', + layout: 'fit', + + /** + * @override + */ + initComponent: function () { + var me = this; + + Ext.apply(me, { + items: [ + { + xtype: 'label', + text: 'No store selected', + padding: '10 10 10 10' + } + ], + + tbar: [ + { + xtype: 'combo', + name: 'storeId', + width: 300, + emptyText: 'select a store', + queryMode: 'local', + displayField: 'id', + valueField: 'id', + trigger2Cls: 'x-form-search-trigger', + onTrigger2Click: function () { + this.getStore().load(); + }, + store: Ext.create('Ext.data.Store', { + fields: ['id'], + data: Ext.data.StoreManager, + proxy: { + type: 'memory', + reader: { + type: 'json', + read: function (data) { + var stores = []; + + data.each(function (store) { + stores.push({ + id: store.storeId + }); + }); + + return this.readRecords(stores); + } + } + }, + sorters: {property: 'id', direction: 'ASC'} + }) + }, + { + xtype: 'button', + text: 'Load store', + action: 'load', + glyph: 'xf0ab@FontAwesome' /* fa-arrow-circle-down */ + }, + { + xtype: 'button', + text: 'Clear store', + action: 'clear', + glyph: 'xf12d@FontAwesome' /* fa-eraser */ + } + ] + }); + + me.callParent(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Styles.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Styles.js new file mode 100644 index 0000000000000000000000000000000000000000..e7a24c116baace3e57bb950e7afb255f8b18f97a --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Styles.js @@ -0,0 +1,104 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Visual style sheet for the application. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.Styles', { + extend: 'Ext.panel.Panel', + alias: 'widget.nx-dev-styles', + requires: [ + 'NX.view.dev.styles.Colors', + 'NX.view.dev.styles.Fonts', + 'NX.view.dev.styles.Buttons', + 'NX.view.dev.styles.Forms', + 'NX.view.dev.styles.Messages', + 'NX.view.dev.styles.Modals', + 'NX.view.dev.styles.Menus', + 'NX.view.dev.styles.Tabs', + 'NX.view.dev.styles.Pickers', + 'NX.view.dev.styles.Tooltips', + 'NX.view.dev.styles.Panels', + 'NX.view.dev.styles.Toolbars', + 'NX.view.dev.styles.Grids', + 'NX.view.dev.styles.Other' + ], + mixins: { + logAware: 'NX.LogAware' + }, + + title: 'Styles', + + layout: { + type: 'vbox', + defaultMargins: {top: 0, right: 4, bottom: 10, left: 0} + }, + + defaults: { + width: '100%' + }, + + /** + * @protected + */ + initComponent: function () { + var me = this; + + // build guide components on activate as this is a heavy view + me.on('activate', function () { + var sections = [ + 'Colors', + 'Fonts', + 'Buttons', + 'Forms', + 'Messages', + 'Modals', + 'Menus', + 'Tooltips', + 'Tabs', + 'Pickers', + 'Panels', + 'Toolbars', + 'Grids', + 'Other' + ]; + + me.logDebug('Creating style guide'); + + // TODO: See if suspending layouts here actually helps anything? + //Ext.AbstractComponent.suspendLayouts(); + //try { + Ext.Array.each(sections, function (section) { + me.add(Ext.create('NX.view.dev.styles.' + section)); + }); + //} + //finally { + // Ext.AbstractComponent.resumeLayouts(true); + //} + + me.logDebug('Style guide ready'); + }); + + // and destroy on deactivate to save memory + me.on('deactivate', function () { + me.removeAll(true); + + me.logDebug('Destroyed style guide'); + }); + + me.callParent(); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Tests.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Tests.js new file mode 100644 index 0000000000000000000000000000000000000000..620055ff5edc3e5fbc97c6eee9b9f339f94cdd5f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/Tests.js @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Contains various buttons to execute actions for development/testing. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.Tests', { + extend: 'Ext.panel.Panel', + alias: 'widget.nx-dev-tests', + + title: 'Tests', + + layout: { + type: 'vbox', + padding: 4, + defaultMargins: {top: 0, right: 0, bottom: 4, left: 0} + }, + + items: [ + { xtype: 'button', text: 'clear local state', action: 'clearLocalState' }, + { xtype: 'button', text: 'javascript error', action: 'testError' }, + { xtype: 'button', text: 'ext error', action: 'testExtError' }, + { xtype: 'button', text: 'message types', action: 'testMessages' }, + { xtype: 'button', text: 'toggle unsupported browser', action: 'toggleUnsupportedBrowser'}, + { xtype: 'button', text: 'show license expiry warning', action: 'showLicenseWarning'}, + { xtype: 'button', text: 'show quorum warning', action: 'showQuorumWarning'} + ] +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Buttons.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Buttons.js new file mode 100644 index 0000000000000000000000000000000000000000..ab18b70afed393def00245f9b396a27581dcfe9a --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Buttons.js @@ -0,0 +1,108 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Button styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Buttons', { + extend: 'NX.view.dev.styles.StyleSection', + requires: [ + 'Ext.XTemplate' + ], + + title: 'Buttons', + layout: { + type: 'vbox', + defaultMargins: {top: 4, right: 0, bottom: 0, left: 0} + }, + + /** + * @protected + */ + initComponent: function () { + var me = this; + + var colorSampleTpl = Ext.create('Ext.XTemplate', + '', + '', + '', + '', + '', + '', + '', + '
$color-{.}
' + ); + + function button(ui, text, disabled, pressed, menu) { + var button = { + xtype: 'button', + text: text, + ui: ui, + margin: "0 10 10 0", + width: 100 + }; + + // Initialize optional button parameters + if (disabled) { + button['disabled'] = true; + } + if (pressed) { + button['pressed'] = true; + button['enableToggle'] = true; + } + if (menu) { + button['menu'] = [ + { text: 'First' }, + '-', + { text: 'Second' } + ]; + } + else { + button['glyph'] = 'xf036@FontAwesome'; + } + + return button; + } + + function buttonStyle(name, colors) { + return { + xtype: 'container', + layout: { + type: 'hbox', + defaultMargins: {top: 0, right: 4, bottom: 0, left: 0} + }, + items: [ + me.label('ui: ' + name, { width: 80 }), + button(name, name, false, false, false), + button(name, name, true, false, false), + button(name, name, false, false, true), + me.html(colorSampleTpl.apply(colors)) + ] + }; + } + + me.items = [ + buttonStyle('default', ['white', 'light-gainsboro', 'light-gray', 'silver', 'suva-gray', 'gray']), + buttonStyle('nx-plain', ['white', 'light-gainsboro', 'light-gray', 'silver', 'suva-gray', 'gray']), + buttonStyle('nx-primary', ['denim', 'light-cobalt', 'dark-denim', 'smalt', 'dark-cerulean', 'prussian-blue']), + buttonStyle('nx-danger', ['light-cerise', 'brick-red', 'old-rose', 'fire-brick', 'shiraz', 'falu-red']), + buttonStyle('nx-warning', ['sea-buckthorn', 'tahiti-gold', 'zest', 'rich-gold', 'afghan-tan', 'russet']), + buttonStyle('nx-success', ['elf-green', 'dark-pigment-green', 'salem', 'jewel', 'fun-green', 'dark-jewel']) + ]; + + me.callParent(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Colors.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Colors.js new file mode 100644 index 0000000000000000000000000000000000000000..1bc7752da671085a5f40738a935f3064b819d7b2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Colors.js @@ -0,0 +1,197 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Color styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Colors', { + extend: 'NX.view.dev.styles.StyleSection', + requires: [ + 'Ext.XTemplate' + ], + + title: 'Colors', + + /** + * @protected + */ + initComponent: function () { + var me = this; + + var rowTemplate = Ext.create('Ext.XTemplate', + '
', + '', + '
{.}
', + '
', + '
' + ); + + var columnTemplate = Ext.create('Ext.XTemplate', + '
', + '', + '
{.}
', + '
', + '
' + ); + + var labelTemplate = Ext.create('Ext.XTemplate', + '{text}' + ); + + var paletteTemplate = Ext.create('Ext.XTemplate', + '
', + '
{.}
', + '
' + ); + + var colorTemplate = Ext.create('Ext.XTemplate', + '
', + '
', + '
{name}
', + '
{value}
', + '
' + ); + + me.items = [ + { + xtype: 'container', + layout: { + type: 'vbox', + padding: 4 + }, + items: [ + me.html(columnTemplate.apply([ + labelTemplate.apply({text: 'Shell', clz: 'nx-section-header' }), + paletteTemplate.apply([ + colorTemplate.apply({clz: 'nx-color black', name: 'Black', value: '#000000'}), + colorTemplate.apply({clz: 'nx-color night-rider', name: 'Night Rider', value: '#333333'}), + colorTemplate.apply({clz: 'nx-color charcoal', name: 'Charcoal', value: '#444444'}), + colorTemplate.apply({clz: 'nx-color dark-gray', name: 'Dark Gray', value: '#777777'}), + colorTemplate.apply({clz: 'nx-color gray', name: 'Gray', value: '#AAAAAA'}), + colorTemplate.apply({clz: 'nx-color light-gray', name: 'Light Gray', value: '#CBCBCB'}), + colorTemplate.apply({clz: 'nx-color gainsboro', name: 'Gainsboro', value: '#DDDDDD'}), + colorTemplate.apply({clz: 'nx-color smoke', name: 'Smoke', value: '#EBEBEB'}), + colorTemplate.apply({clz: 'nx-color light-smoke', name: 'Light Smoke', value: '#F4F4F4'}), + colorTemplate.apply({clz: 'nx-color white', name: 'White', value: '#FFFFFF'}) + ]) + ])), + + me.html(rowTemplate.apply([ + columnTemplate.apply([ + labelTemplate.apply({text: 'Severity', clz: 'nx-section-header' }), + paletteTemplate.apply([ + colorTemplate.apply({clz: 'nx-color cerise', name: 'Cerise', value: '#DB2852'}), + colorTemplate.apply({clz: 'nx-color sun', name: 'Sun', value: '#F2862F'}), + colorTemplate.apply({clz: 'nx-color energy-yellow', name: 'Energy Yellow', value: '#F5C649'}), + colorTemplate.apply({clz: 'nx-color cobalt', name: 'Cobalt', value: '#0047B2'}), + colorTemplate.apply({clz: 'nx-color cerulean-blue', name: 'Cerulean Blue', value: '#2476C3'}) + ]) + ]), + columnTemplate.apply([ + labelTemplate.apply({text: 'Forms', clz: 'nx-section-header' }), + paletteTemplate.apply([ + colorTemplate.apply({clz: 'nx-color citrus', name: 'Citrus', value: '#84C900'}), + colorTemplate.apply({clz: 'nx-color free-speech-red', name: 'Free Speech Red', value: '#C70000'}) + ]) + ]), + columnTemplate.apply([ + labelTemplate.apply({text: 'Tooltip', clz: 'nx-section-header' }), + paletteTemplate.apply([ + colorTemplate.apply({clz: 'nx-color energy-yellow', name: 'Energy Yellow', value: '#F5C649'}), + colorTemplate.apply({clz: 'nx-color floral-white', name: 'Floral White', value: '#FFFAEE'}) + ]) + ]) + ])), + + me.html(columnTemplate.apply([ + labelTemplate.apply({text: 'Dashboard', clz: 'nx-section-header' }), + paletteTemplate.apply([ + colorTemplate.apply({clz: 'nx-color pigment-green', name: 'Pigment Green', value: '#0B9743'}), + colorTemplate.apply({clz: 'nx-color madang', name: 'Madang', value: '#B6E9AB'}), + colorTemplate.apply({clz: 'nx-color venetian-red', name: 'Venetian Red', value: '#BC0430'}), + colorTemplate.apply({clz: 'nx-color beauty-bush', name: 'Beauty Bush', value: '#EDB2AF'}), + colorTemplate.apply({clz: 'nx-color navy-blue', name: 'Navy Blue', value: '#006BBF'}), + colorTemplate.apply({clz: 'nx-color cornflower', name: 'Cornflower', value: '#96CAEE'}), + colorTemplate.apply({clz: 'nx-color east-side', name: 'East Side', value: '#B087B9'}), + colorTemplate.apply({clz: 'nx-color blue-chalk', name: 'Blue Chalk', value: '#DAC5DF'}) + ]) + ])), + + me.html(rowTemplate.apply([ + columnTemplate.apply([ + labelTemplate.apply({text: 'Buttons', clz: 'nx-section-header' }), + paletteTemplate.apply([ + colorTemplate.apply({clz: 'nx-color white', name: 'White', value: '#FFFFFF'}), + colorTemplate.apply({clz: 'nx-color light-gainsboro', name: 'Light Gainsboro', value: '#E6E6E6'}), + colorTemplate.apply({clz: 'nx-color light-gray', name: 'Light Gray', value: '#CBCBCB'}), + colorTemplate.apply({clz: 'nx-color silver', name: 'Silver', value: '#B8B8B8'}), + colorTemplate.apply({clz: 'nx-color suva-gray', name: 'Suva Gray', value: '#919191'}), + colorTemplate.apply({clz: 'nx-color gray', name: 'Gray', value: '#808080'}) + ]), + paletteTemplate.apply([ + colorTemplate.apply({clz: 'nx-color denim', name: 'Denim', value: '#197AC5'}), + colorTemplate.apply({clz: 'nx-color light-cobalt', name: 'Light Cobalt', value: '#0161AD'}), + colorTemplate.apply({clz: 'nx-color dark-denim', name: 'Dark Denim', value: '#14629E'}), + colorTemplate.apply({clz: 'nx-color smalt', name: 'Smalt', value: '#014E8A'}), + colorTemplate.apply({clz: 'nx-color dark-cerulean', name: 'Dark Cerulean', value: '#0F4976'}), + colorTemplate.apply({clz: 'nx-color prussian-blue', name: 'Prussian Blue', value: '#013A68'}) + ]), + paletteTemplate.apply([ + colorTemplate.apply({clz: 'nx-color light-cerise', name: 'Light Cerise', value: '#DE3D63'}), + colorTemplate.apply({clz: 'nx-color brick-red', name: 'Brick Red', value: '#C6254B'}), + colorTemplate.apply({clz: 'nx-color old-rose', name: 'Old Rose', value: '#B2314F'}), + colorTemplate.apply({clz: 'nx-color fire-brick', name: 'Fire Brick', value: '#9E1E3C'}), + colorTemplate.apply({clz: 'nx-color shiraz', name: 'Shiraz', value: '#85253B'}), + colorTemplate.apply({clz: 'nx-color falu-red', name: 'Falu Red', value: '#77162D'}) + ]), + paletteTemplate.apply([ + colorTemplate.apply({clz: 'nx-color sea-buckthorn', name: 'Sea Buckthorn', value: '#F39244'}), + colorTemplate.apply({clz: 'nx-color tahiti-gold', name: 'Tahiti Gold', value: '#DA792B'}), + colorTemplate.apply({clz: 'nx-color zest', name: 'Zest', value: '#C17536'}), + colorTemplate.apply({clz: 'nx-color rich-gold', name: 'Rich Gold', value: '#AE6122'}), + colorTemplate.apply({clz: 'nx-color afghan-tan', name: 'Afghan Tan', value: '#925829'}), + colorTemplate.apply({clz: 'nx-color russet', name: 'Russet', value: '#83491A'}) + ]), + paletteTemplate.apply([ + colorTemplate.apply({clz: 'nx-color elf-green', name: 'Elf Green', value: '#23A156'}), + colorTemplate.apply({clz: 'nx-color dark-pigment-green', name: 'Dark Pigment Green', value: '#0B893D'}), + colorTemplate.apply({clz: 'nx-color salem', name: 'Salem', value: '#1C8145'}), + colorTemplate.apply({clz: 'nx-color jewel', name: 'Jewel', value: '#096E31'}), + colorTemplate.apply({clz: 'nx-color fun-green', name: 'Fun Green', value: '#156134'}), + colorTemplate.apply({clz: 'nx-color dark-jewel', name: 'Dark Jewel', value: '#0C4F26'}) + ]) + ]), + columnTemplate.apply([ + labelTemplate.apply({text: 'Font Awesome Icons', clz: 'nx-section-header' }), + paletteTemplate.apply([ + colorTemplate.apply({clz: 'nx-color navy-blue', name: 'Navy Blue', value: '#006BBF'}), + colorTemplate.apply({clz: 'nx-color smalt', name: 'Smalt', value: '#014E8A'}), + colorTemplate.apply({clz: 'nx-color prussian-blue', name: 'Prussian Blue', value: '#013A68'}) + ]), + paletteTemplate.apply([ + colorTemplate.apply({clz: 'nx-color white', name: 'White', value: '#FFFFFF'}), + colorTemplate.apply({clz: 'nx-color gainsboro', name: 'Gainsboro', value: '#DDDDDD'}), + colorTemplate.apply({clz: 'nx-color gray', name: 'Gray', value: '#AAAAAA'}) + ]) + ]) + ])) + ] + } + ]; + + me.callParent(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Fonts.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Fonts.js new file mode 100644 index 0000000000000000000000000000000000000000..a985067887dff3c1c1f09305223670b9abbeb5f9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Fonts.js @@ -0,0 +1,144 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Font styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Fonts', { + extend: 'NX.view.dev.styles.StyleSection', + requires: [ + 'Ext.XTemplate' + ], + + title: 'Fonts', + layout: { + type: 'vbox', + defaultMargins: {top: 4, right: 0, bottom: 0, left: 0} + }, + + /** + * @protected + */ + initComponent: function () { + var me = this; + + var faceExampleTpl = Ext.create('Ext.XTemplate', + '
', + '{text}', + '

', + 'Trusted applications at the speed of deployment
', + 'abcdefghijklmnopqrstuvwxyz
', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ
', + ',1234567890?¿¡;.:*@#£$%&/()=[]+', + '

', + '
' + ); + + function faceExample(name, clz) { + return me.html(faceExampleTpl.apply({ + text: name, + clz: clz + }) + ); + } + + // Create a table + var tableTemplate = Ext.create('Ext.XTemplate', + '', + '{thead}', + '{tbody}', + '
' + ); + + // Create a table head + var theadTemplate = Ext.create('Ext.XTemplate', + '', + '{.}', + '' + ); + + // Create a table body + var tbodyTemplate = Ext.create('Ext.XTemplate', + '', + '', + '{$}', + '', + '', + '{text}', + '', + '{.}', + '', + '', + '', + '' + ); + + me.items = [ + { + xtype: 'panel', + title: 'Faces', + ui: 'nx-subsection', + layout: { + type: 'hbox', + defaultMargins: {top: 0, right: 20, bottom: 0, left: 0} + }, + items: [ + faceExample('Proxima Nova Regular', 'nx-proxima-nova-regular'), + faceExample('Proxima Nova Bold', 'nx-proxima-nova-bold'), + faceExample('Courier New', 'nx-courier-new-regular') + ] + }, + { + xtype: 'panel', + title: 'Styles', + ui: 'nx-subsection', + items: [ + me.html(tableTemplate.apply({ + thead: theadTemplate.apply(['Name', 'Description', 'Font & Weight', 'Use Cases', 'Pixels', 'Sample']), + tbody: tbodyTemplate.apply({ + 'h1': [ + 'Header', 'Proxima Nova Light', 'Logo', '20', { text: 'Sonatype Nexus', clz: 'nx-sample-h1' } + ], + 'h2': [ + 'Header', 'Proxima Nova Bold', 'Page Title', '26', { text: 'Development', clz: 'nx-sample-h2' } + ], + 'h3': [ + 'Header', 'Proxima Nova Bold', 'Header', '22', { text: 'Development', clz: 'nx-sample-h3' } + ], + 'h4': [ + 'Header', 'Proxima Nova Bold', 'Sub-Header', '18', { text: 'Development', clz: 'nx-sample-h4' } + ], + 'h5': [ + 'Header', 'Proxima Nova Bold', 'Sub-Header', '13', { text: 'Development', clz: 'nx-sample-h5' } + ], + 'p/ul/ol': [ + 'Body', 'Proxima Nova Regular', 'Body text, lists, default size', '13', { text: 'Development', clz: 'nx-sample-body' } + ], + 'code': [ + 'Code', 'Courier New Regular', 'Code examples', '13', { text: 'Development', clz: 'nx-sample-code' } + ], + 'utility': [ + 'Small Text', 'Proxima Nova Regular', 'Labels, Side-Nav', '10', { text: 'Development', clz: 'nx-sample-utility' } + ] + }) + })) + ] + } + ]; + + me.callParent(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Forms.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Forms.js new file mode 100644 index 0000000000000000000000000000000000000000..398818790f7900d6b84c7e4e7a8c198861b1e764 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Forms.js @@ -0,0 +1,213 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Form styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Forms', { + extend: 'NX.view.dev.styles.StyleSection', + + title: 'Forms', + layout: { + type: 'hbox', + defaultMargins: {top: 0, right: 4, bottom: 0, left: 0} + }, + + /** + * @protected + */ + initComponent: function () { + var me = this; + + me.items = [ + // basic form layout + { + xtype: 'form', + items: [ + { xtype: 'textfield', value: 'Text Input', allowBlank: false, fieldLabel: '[Label]', helpText: '[Optional description text]', width: 200 }, + { xtype: 'textarea', value: 'Text Input', allowBlank: false, fieldLabel: '[Label]', helpText: '[Optional description text]', width: 200 }, + { xtype: 'checkbox', boxLabel: 'Checkbox', checked: true, fieldLabel: null, helpText: null }, + { xtype: 'radio', boxLabel: 'Radio Button', checked: true, fieldLabel: null, helpText: null } + ], + buttons: [ + { text: 'Submit', ui: 'nx-primary' }, + { text: 'Discard' } + ] + }, + + // form example from extjs example/themes + { + xtype: 'form', + frame: true, + collapsible: true, + + tools: [ + {type:'toggle'}, + {type:'close'}, + {type:'minimize'}, + {type:'maximize'}, + {type:'restore'}, + {type:'gear'}, + {type:'pin'}, + {type:'unpin'}, + {type:'right'}, + {type:'left'}, + {type:'down'}, + {type:'refresh'}, + {type:'minus'}, + {type:'plus'}, + {type:'help'}, + {type:'search'}, + {type:'save'}, + {type:'print'} + ], + + bodyPadding: '10 20', + + defaults: { + anchor : '98%', + msgTarget : 'side', + allowBlank: false + }, + + items: [ + { + xtype: 'label', + text: 'Plain Label' + }, + { + fieldLabel: 'TextField', + xtype: 'textfield', + name: 'someField', + emptyText: 'Enter a value' + }, + { + fieldLabel: 'ComboBox', + xtype: 'combo', + store: ['Foo', 'Bar'] + }, + { + fieldLabel: 'DateField', + xtype: 'datefield', + name: 'date' + }, + { + fieldLabel: 'TimeField', + name: 'time', + xtype: 'timefield' + }, + { + fieldLabel: 'NumberField', + xtype: 'numberfield', + name: 'number', + emptyText: '(This field is optional)', + allowBlank: true + }, + { + fieldLabel: 'TextArea', + xtype: 'textareafield', + name: 'message', + cls: 'x-form-valid', + value: 'This field is hard-coded to have the "valid" style (it will require some code changes to add/remove this style dynamically)' + }, + { + fieldLabel: 'Checkboxes', + xtype: 'checkboxgroup', + columns: [100, 100], + items: [ + {boxLabel: 'Foo', checked: true, inputId: 'fooChkInput'}, + {boxLabel: 'Bar'} + ] + }, + { + fieldLabel: 'Radios', + xtype: 'radiogroup', + columns: [100, 100], + items: [ + {boxLabel: 'Foo', checked: true, name: 'radios'}, + {boxLabel: 'Bar', name: 'radios'} + ] + }, + { + hideLabel: true, + xtype: 'htmleditor', + name: 'html', + enableColors: false, + value: 'Mouse over toolbar for tooltips.

The HTMLEditor IFrame requires a refresh between a stylesheet switch to get accurate colors.', + height: 110 + }, + { + xtype: 'fieldset', + title: 'Plain Fieldset', + items: [ + { + hideLabel: true, + xtype: 'radiogroup', + items: [ + {boxLabel: 'Radio A', checked: true, name: 'radiogrp2'}, + {boxLabel: 'Radio B', name: 'radiogrp2'} + ] + } + ] + }, + { + xtype: 'fieldset', + title: 'Collapsible Fieldset', + collapsible: true, + items: [ + { xtype: 'checkbox', boxLabel: 'Checkbox 1' }, + { xtype: 'checkbox', boxLabel: 'Checkbox 2' } + ] + }, + { + xtype: 'fieldset', + title: 'Checkbox Fieldset', + checkboxToggle: true, + items: [ + { xtype: 'radio', boxLabel: 'Radio 1', name: 'radiongrp1' }, + { xtype: 'radio', boxLabel: 'Radio 2', name: 'radiongrp1' } + ] + } + ], + + buttons: [ + { + text: 'Toggle Enabled', + handler: function () { + this.up('form').items.each(function (item) { + item.setDisabled(!item.disabled); + }); + } + }, + { + text: 'Reset Form', + handler: function () { + this.up('form').getForm().reset(); + } + }, + { + text: 'Validate', + handler: function () { + this.up('form').getForm().isValid(); + } + } + ] + } + ]; + + me.callParent(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Grids.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Grids.js new file mode 100644 index 0000000000000000000000000000000000000000..6fd2b1cd3e50eb2f12154ecf50a0120e863b5896 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Grids.js @@ -0,0 +1,62 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Grid styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Grids', { + extend: 'NX.view.dev.styles.StyleSection', + requires: [ + 'Ext.data.ArrayStore' + ], + + title: 'Grids', + + /** + * @protected + */ + initComponent: function () { + var me = this, + store; + + store = Ext.create('Ext.data.ArrayStore', { + fields: [ + 'id', + 'name' + ], + data: [ + ['foo', 'Foo'], + ['bar', 'Bar'], + ['baz', 'Baz'] + ] + }); + + me.items = [ + { + xtype: 'grid', + store: store, + height: 200, + width: 200, + columns: [ + { text: 'ID', dataIndex: 'id' }, + { text: 'Name', dataIndex: 'name' } + ] + } + ]; + + me.callParent(); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Menus.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Menus.js new file mode 100644 index 0000000000000000000000000000000000000000..ebf0c2ab0b89935ef0b67b1e6f333b6b53a30b78 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Menus.js @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Menu styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Menus', { + extend: 'NX.view.dev.styles.StyleSection', + + title: 'Menus', + layout: { + type: 'hbox', + defaultMargins: {top: 0, right: 4, bottom: 0, left: 0} + }, + + /** + * @protected + */ + initComponent: function () { + var me = this; + + function menu(text, iconCls, tooltip, action) { + return { + text: text, + iconCls: iconCls, + tooltip: tooltip, + action: action + }; + } + + me.items = [ + { + xtype: 'menu', + floating: false, + items: [ + menu('Help for [Feature]', 'nx-icon-search-default-x16', 'Help for the current feature', 'feature'), + '-', + menu('About', 'nx-icon-nexus-x16', 'About Nexus Repository Manager', 'about'), + menu('Documentation', 'nx-icon-help-manual-x16', 'Product documentation', 'docs'), + menu('Knowledge Base', 'nx-icon-help-kb-x16', 'Knowledge base', 'kb'), + menu('Community', 'nx-icon-help-community-x16', 'Community information', 'community'), + menu('Issue Tracker', 'nx-icon-help-issues-x16', 'Issue and bug tracker', 'issues'), + '-', + menu('Support', 'nx-icon-help-support-x16', 'Product support', 'support') + ] + } + ]; + + me.callParent(); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Messages.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Messages.js new file mode 100644 index 0000000000000000000000000000000000000000..ded49d5b55316676fdadea8727b6f90d3bbfdb55 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Messages.js @@ -0,0 +1,67 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Message notification styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Messages', { + extend: 'NX.view.dev.styles.StyleSection', + requires: [ + 'NX.Icons' + ], + + title: 'Messages', + layout: { + type: 'hbox', + defaultMargins: {top: 0, right: 4, bottom: 0, left: 0} + }, + + /** + * @protected + */ + initComponent: function () { + var me = this; + + function message(type) { + var style = 'nx-message-' + type; + var icon = NX.Icons.cls('message-' + type, 'x16'); + return { + xtype: 'window', + ui: style, + iconCls: icon, + title: type, + html: "ui: '" + style + "'", + hidden: false, + collapsible: false, + floating: false, + closable: false, + draggable: false, + resizable: false, + width: 200 + }; + } + + me.items = [ + message('default'), + message('primary'), + message('danger'), + message('warning'), + message('success') + ]; + + me.callParent(); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Modals.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Modals.js new file mode 100644 index 0000000000000000000000000000000000000000..7c71c2c8a67a3c1e27272fbea601fcae0128f888 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Modals.js @@ -0,0 +1,121 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Modal dialogs styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Modals', { + extend: 'NX.view.dev.styles.StyleSection', + requires: [ + 'NX.I18n' + ], + + title: 'Modals', + + layout: { + type: 'hbox', + defaultMargins: {top: 0, right: 4, bottom: 0, left: 0} + }, + + /** + * @protected + */ + initComponent: function () { + var me = this; + + me.items = [ + { + xtype: 'window', + + title: NX.I18n.get('SignIn_Title'), + + hidden: false, + collapsible: false, + floating: false, + closable: false, + draggable: false, + resizable: false, + width: 320, + cls: 'fixed-modal', + + items: { + xtype: 'form', + ui: 'nx-inset', + defaultType: 'textfield', + defaults: { + anchor: '100%' + }, + items: [ + { + name: 'username', + itemId: 'username', + emptyText: NX.I18n.get('SignIn_Username_Empty'), + allowBlank: false, + // allow cancel to be clicked w/o validating this to be non-blank + validateOnBlur: false + }, + { + name: 'password', + itemId: 'password', + inputType: 'password', + emptyText: NX.I18n.get('SignIn_Password_Empty'), + allowBlank: false, + // allow cancel to be clicked w/o validating this to be non-blank + validateOnBlur: false + } + ], + + buttonAlign: 'left', + buttons: [ + { text: NX.I18n.get('SignIn_Submit_Button'), formBind: true, bindToEnter: true, ui: 'nx-primary' }, + { text: NX.I18n.get('SignIn_Cancel_Button') } + ] + } + }, + { + xtype: 'window', + + title: 'Session', + + hidden: false, + collapsible: false, + floating: false, + closable: false, + draggable: false, + resizable: false, + width: 320, + cls: 'fixed-modal', + + items: [ + { + xtype: 'label', + text: 'Session is about to expire', + style: { + 'color': 'red', + 'font-size': '20px', + 'margin': '10px' + } + } + ], + buttons: [ + { text: 'Cancel' } + ] + } + ]; + + me.callParent(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Other.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Other.js new file mode 100644 index 0000000000000000000000000000000000000000..87551a60428bf514bf3fb3e65d522f096238b075 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Other.js @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Other styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Other', { + extend: 'NX.view.dev.styles.StyleSection', + + title: 'Other', + layout: { + type: 'vbox', + defaultMargins: {top: 4, right: 0, bottom: 0, left: 0} + }, + + /** + * @protected + */ + initComponent: function () { + var me = this; + + me.items = [ + me.label('date picker'), + { + xtype: 'datepicker' + }, + + me.label('sliders'), + { + xtype: 'slider', + hideLabel: true, + value: 50, + margin: '5 0 0 0', + anchor: '100%' + }, + { + xtype: 'slider', + vertical: true, + value: 50, + height: 100, + margin: '5 0 0 0' + } + ]; + + me.callParent(); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Panels.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Panels.js new file mode 100644 index 0000000000000000000000000000000000000000..fedfd336c9aa3d2702521ab41cf1f1d3067cad86 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Panels.js @@ -0,0 +1,85 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Panel styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Panels', { + extend: 'NX.view.dev.styles.StyleSection', + + title: 'Panels', + + layout: { + type: 'vbox', + defaultMargins: {top: 4, right: 0, bottom: 0, left: 0} + }, + + /** + * @protected + */ + initComponent: function () { + var me = this; + + function panelStyle(ui) { + return { + layout: { + type: 'hbox', + defaultMargins: {top: 0, right: 4, bottom: 0, left: 0} + }, + + items: [ + { + xtype: 'panel', + title: ui, + ui: ui, + height: 100, + width: 200, + items: [ + { + xtype: 'container', + html: 'ui: ' + ui + } + ] + }, + { + xtype: 'panel', + title: ui + ' framed', + ui: ui, + frame: true, + height: 100, + width: 200, + items: [ + { + xtype: 'container', + html: 'ui: ' + ui + '; frame: true' + } + ] + } + ] + }; + } + + me.items = [ + panelStyle('default'), + panelStyle('light'), + + // TODO: Consider adding flag to disable 'frame: true' example for subsection + panelStyle('nx-subsection') + ]; + + me.callParent(); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Pickers.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Pickers.js new file mode 100644 index 0000000000000000000000000000000000000000..9d3ef380758982cbdc50dedbd1fbf0ab09f2153c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Pickers.js @@ -0,0 +1,63 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Picker styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Pickers', { + extend: 'NX.view.dev.styles.StyleSection', + requires: [ + 'Ext.data.ArrayStore' + ], + + title: 'Pickers', + + /** + * @protected + */ + initComponent: function () { + var me = this, + store; + + store = Ext.create('Ext.data.ArrayStore', { + fields: [ + 'id', + 'name' + ], + data: [ + [ 'foo', 'Foo' ], + [ 'bar', 'Bar' ], + [ 'baz', 'Baz' ] + ] + }); + + me.items = [ + { + xtype: 'nx-itemselector', + name: 'realms', + buttons: ['up', 'add', 'remove', 'down'], + fromTitle: 'Available', + toTitle: 'Selected', + store: store, + valueField: 'id', + displayField: 'name', + delimiter: null + } + ]; + + me.callParent(); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/StyleSection.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/StyleSection.js new file mode 100644 index 0000000000000000000000000000000000000000..829b7e801a44abe06e9ccd8649ad5fe37e10bfac --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/StyleSection.js @@ -0,0 +1,57 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Support for sections of the style guild. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.StyleSection', { + extend: 'Ext.panel.Panel', + + ui: 'nx-light', + bodyPadding: '5px 5px 5px 5px', + + /** + * Render a block of HTML. + * + * @protected + */ + html: function(html, cfg) { + var obj = { + xtype: 'container', + html: html + }; + if (cfg) { + Ext.apply(obj, cfg); + } + return obj; + }, + + /** + * Render a label with HTML. + * + * @protected + */ + label: function(html, cfg) { + var obj = { + xtype: 'label', + html: html + }; + if (cfg) { + Ext.apply(obj, cfg); + } + return obj; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Tabs.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Tabs.js new file mode 100644 index 0000000000000000000000000000000000000000..fd0c87a90de3c639b8c82408ec9c2510b9e7c542 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Tabs.js @@ -0,0 +1,73 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Tab styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Tabs', { + extend: 'NX.view.dev.styles.StyleSection', + + title: 'Tabs', + layout: { + type: 'vbox', + defaultMargins: {top: 4, right: 0, bottom: 0, left: 0} + }, + + /** + * @protected + */ + initComponent: function () { + var me = this; + + function tabStyle(name) { + var proto = { + xtype: 'tabpanel', + width: 400, + height: 80, + activeTab: 0, + ui: name, + items: [ + { title: 'Settings', items: { xtype: 'panel', html: 'A simple tab', ui: 'nx-inset' } }, + { title: 'Routing', items: { xtype: 'panel', html: 'Another one', ui: 'nx-inset' } }, + { title: 'Smart Proxy', items: { xtype: 'panel', html: 'Yet another', ui: 'nx-inset' } }, + { title: 'Health Check', items: { xtype: 'panel', html: 'And one more', ui: 'nx-inset' } } + ] + }; + + return { + xtype: 'container', + layout: { + type: 'vbox', + defaultMargins: {top: 4, right: 0, bottom: 0, left: 0} + }, + + items: [ + me.label('ui: ' + name), + Ext.clone(proto), + me.label('ui: ' + name + '; plain: true'), + Ext.apply(proto, { plain: true }) + ] + }; + } + + me.items = [ + tabStyle('default'), + tabStyle('nx-light') + ]; + + me.callParent(); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Toolbars.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Toolbars.js new file mode 100644 index 0000000000000000000000000000000000000000..10031367437cc6219df2be07ae78134fd824e50c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Toolbars.js @@ -0,0 +1,107 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Toolbar styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Toolbars', { + extend: 'NX.view.dev.styles.StyleSection', + + title: 'Toolbars', + + /** + * @protected + */ + initComponent: function () { + var me = this; + + function toolbar(scale) { + var obj = { + xtype: 'toolbar', + items: [ + 'text', + { + xtype: 'button', + text: 'plain' + }, + { + xtype: 'button', + text: 'with glyph', + glyph: 'xf1b2@FontAwesome' + }, + { + xtype: 'button', + text: 'with icon', + iconCls: 'nx-icon-help-kb-x16' + }, + ' ', // spacer + { + xtype: 'button', + text: 'button menu', + menu: [ + { text: 'plain' }, + { text: 'with glyph', glyph: 'xf059@FontAwesome' }, + { text: 'with icon', iconCls: 'nx-icon-help-kb-x16'} + ] + }, + '-', // sep + { + xtype: 'splitbutton', + text: 'split button', + menu: Ext.widget('menu', { + items: [ + {text: 'Item 1'}, + {text: 'Item 2'} + ] + }) + }, + { + xtype: 'button', + enableToggle: true, + pressed: true, + text: 'toggle button' + }, + '->', // spring + { + xtype: 'nx-searchbox', + width: 200 + } + ] + }; + + if (scale) { + Ext.apply(obj, { + defaults: { + scale: scale + } + }); + } + + return obj; + } + + me.items = [ + me.label('default'), + toolbar(undefined), + me.label('scale: medium'), + toolbar('medium'), + me.label('scale: large'), + toolbar('large') + ]; + + me.callParent(); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Tooltips.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Tooltips.js new file mode 100644 index 0000000000000000000000000000000000000000..47ca4595ceb6060e798cf8c86ff1484b719cabb1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/dev/styles/Tooltips.js @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Tooltip styles. + * + * @since 3.0 + */ +Ext.define('NX.view.dev.styles.Tooltips', { + extend: 'NX.view.dev.styles.StyleSection', + + title: 'Tooltips', + + /** + * @protected + */ + initComponent: function () { + var me = this; + + me.items = [ + { xtype: 'button', text: 'Mouse over me', tooltip: 'This is a tooltip' } + ]; + + me.callParent(); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Actions.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Actions.js new file mode 100644 index 0000000000000000000000000000000000000000..e9283edcf27ed9412894c9e0a896f320d9be26bc --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Actions.js @@ -0,0 +1,25 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * A toolbar to hold actions for master/detail panels + * + * @since 3.0 + */ +Ext.define('NX.view.drilldown.Actions', { + extend: 'Ext.toolbar.Toolbar', + alias: 'widget.nx-actions', + cls: 'nx-actions', + dock: 'top' +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Details.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Details.js new file mode 100644 index 0000000000000000000000000000000000000000..f81a9a152676834e205a16937529ff01ec370e91 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Details.js @@ -0,0 +1,155 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Master/Detail tabs. + * + * @since 3.0 + */ +Ext.define('NX.view.drilldown.Details', { + extend: 'Ext.panel.Panel', + alias: 'widget.nx-drilldown-details', + requires: [ + 'NX.Icons', + 'NX.Bookmarks', + 'NX.ext.tab.SortedPanel', + 'NX.view.drilldown.Actions' + ], + + /** + * @override + */ + initComponent: function() { + var me = this; + + me.items = [ + { + xtype: 'panel', + itemId: 'info', + ui: 'nx-drilldown-message', + cls: 'nx-drilldown-info', + iconCls: NX.Icons.cls('drilldown-info', 'x16'), + hidden: true + }, + { + xtype: 'panel', + itemId: 'warning', + ui: 'nx-drilldown-message', + cls: 'nx-drilldown-warning', + iconCls: NX.Icons.cls('drilldown-warning', 'x16'), + hidden: true + }, + { + xtype: 'nx-actions', + items: me.actions + }, + { + xtype: 'nx-sorted-tabpanel', + itemId: 'tab', + ui: 'nx-light', + cls: 'nx-hr', + activeTab: 0, + layoutOnTabChange: true, + flex: 1, + items: me.tabs + } + ]; + + me.callParent(); + + me.on('afterrender', me.calculateBookmarks, me); + }, + + showInfo: function(message) { + var infoPanel = this.down('>#info'); + + infoPanel.setTitle(message); + infoPanel.show(); + }, + + clearInfo: function() { + var infoPanel = this.down('>#info'); + + infoPanel.hide(); + }, + + showWarning: function(message) { + var warningPanel = this.down('>#warning'); + + warningPanel.setTitle(message); + warningPanel.show(); + }, + + clearWarning: function() { + var warningPanel = this.down('>#warning'); + + warningPanel.hide(); + }, + + addTab: function(tab) { + var me = this, + tabPanel = me.down('>#tab'); + + tabPanel.add(tab); + me.calculateBookmarks(); + }, + + removeTab: function(tab) { + var me = this, + tabPanel = me.down('>#tab'); + + tabPanel.remove(tab); + me.calculateBookmarks(); + }, + + /** + * @public + * @returns {String} bookmark token of selected tab + */ + getBookmarkOfSelectedTab: function() { + var tabPanel = this.down('>#tab'); + + return tabPanel.getActiveTab().bookmark; + }, + + /** + * @public + * Finds a tab by bookmark & sets it active (if found). + * @param {String} bookmark of tab to be activated + */ + setActiveTabByBookmark: function(bookmark) { + var me = this, + tabPanel = me.down('>#tab'), + tab = me.down('> tabpanel > panel[bookmark=' + bookmark + ']'); + + if (tabPanel && tab) { + tabPanel.setActiveTab(tab); + } + }, + + /** + * @private + * Calculates bookmarks of all tabs based on tab title. + */ + calculateBookmarks: function() { + var tabPanel = this.down('>#tab'); + + tabPanel.items.each(function(tab) { + if (tab.title) { + tab.bookmark = NX.Bookmarks.encode(tab.title).toLowerCase(); + } + }); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Drilldown.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Drilldown.js new file mode 100644 index 0000000000000000000000000000000000000000..7d13e6e017623158c6a18e44408ad3bc1cb51127 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Drilldown.js @@ -0,0 +1,147 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * The foundation class for new drilldowns. Extend this. + * + * @since 3.0 + */ +Ext.define('NX.view.drilldown.Drilldown', { + extend: 'Ext.container.Container', + alias: 'widget.nx-drilldown', + itemId: 'nx-drilldown', + + requires: [ + 'NX.Icons' + ], + + // List of masters to use (xtype objects) + masters: null, + + // List of actions to use in the detail view + actions: null, + + items: [], + + /** + * @override + */ + initComponent: function() { + var me = this; + + me.on('beforerender', me.loadDrilldown); + + me.callParent(); + }, + + /** + * @private + * Initialize the items array of this component + */ + loadDrilldown: function(me) { + var items = [], + views; + + // Normalize the list of masters. Clone the list to avoid memory leaks. + if (!me.masters) { + views = []; + } + else if (!Ext.isArray(me.masters)) { + views = [Ext.clone(me.masters)]; + } + else { + views = Ext.Array.clone(me.masters); + } + + if (!me.skipDetail) { + if (me.detail) { + // Use a custom detail panel + views.push(me.detail); + } + else { + // Use the default tab panel + views.push( + { + xtype: 'nx-drilldown-details', + header: false, + plain: true, + + layout: { + type: 'vbox', + align: 'stretch', + pack: 'start' + }, + + tabs: Ext.clone(me.tabs), + actions: Ext.isArray(me.actions) ? Ext.Array.clone(me.actions) : me.actions + } + ); + } + } + + // Stack all panels onto the items array + for (var i = 0; i < views.length; ++i) { + items.push(me.createDrilldownItem(i, views[i], undefined)); + } + + // Add components to the container + me.add({ + xtype: 'container', + + defaults: { + flex: 1 + }, + + layout: { + type: 'hbox', + align: 'stretch' + }, + + items: items + }); + + // Add resize events + me.addEvents('syncsize'); + }, + + /** + * @private + * Create a new drilldown item + */ + createDrilldownItem: function(index, browsePanel, createPanel) { + return { + xtype: 'nx-drilldown-item', + itemClass: NX.Icons.cls(this.iconName) + (index === 0 ? '-x32' : '-x16'), + items: [ + { + xtype: 'container', + layout: 'fit', + itemId: 'browse' + index, + items: browsePanel + }, + { + xtype: 'container', + layout: 'fit', + itemId: 'create' + index, + items: createPanel + }, + { + type: 'container', + layout: 'fit', + itemId: 'nothin' + index + } + ] + }; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Item.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Item.js new file mode 100644 index 0000000000000000000000000000000000000000..3dc1878b759aa71a88c58b96831ef610d5ccd2cf --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Item.js @@ -0,0 +1,63 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * A single panel in a drilldown series + * + * @since 3.0 + */ + +Ext.define('NX.view.drilldown.Item', { + extend: 'Ext.panel.Panel', + alias: 'widget.nx-drilldown-item', + + itemName: null, + itemClass: null, + itemBookmark: null, + cardIndex: 0, + + layout: 'card', + + /** + * @public + * Set the name of this drilldown item (appears in the breadcrumb) + */ + setItemName: function(text) { + this.itemName = text; + }, + + /** + * @public + * Set the icon class of this drilldown item (appears in the breadcrumb) + */ + setItemClass: function(cls) { + this.itemClass = cls; + }, + + /** + * @public + * Set the page to load when the breadcrumb segment associated with this drilldown item is clicked + */ + setItemBookmark: function(bookmark, scope) { + this.itemBookmark = (bookmark ? { obj: bookmark, scope: scope } : null); + }, + + /** + * @public + * Set the currently selected card (will not change the active index by itself) + */ + setCardIndex: function(index) { + this.cardIndex = index; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Master.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Master.js new file mode 100644 index 0000000000000000000000000000000000000000..0d51a1543f27b203df3d44602a0f5ca91c0831f2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/drilldown/Master.js @@ -0,0 +1,100 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Master/Detail tabs. + * + * @since 3.0 + */ +Ext.define('NX.view.drilldown.Master', { + extend: 'Ext.grid.Panel', + alias: 'widget.nx-drilldown-master', + + maskElement: 'body', + + /** + * @private + */ + initComponent: function() { + var me = this; + + me.callParent(); + + me.on('render', this.loadStore, this); + + // Refresh drilldown affordances on load, and when a column is added + me.on('viewready', function(view) { + view.refreshDrilldown(view.headerCt); + }); + me.headerCt.on('columnschanged', me.refreshDrilldown); + }, + + loadStore: function() { + this.getStore().load(); + }, + + /** + * @private + * Put a drilldown affordance ‘>’ at the end of each item in the list + * + * @param ct The content header for the grid + */ + refreshDrilldown: function(ct) { + var firstIdx, + columns = ct.items.items.filter(function(e, idx) { + if (e.cls && e.cls === 'nx-drilldown-affordance') { + if (!firstIdx) { + firstIdx = idx; + } + return true; + } + return false; + }); + + // skip adding affordance if the column already exists and is teh last one + if (columns.length === 1 && firstIdx + 1 === ct.items.items.length) { + return; + } + + this.suspendEvents(false); + + // Remove drilldown affordance columns + columns.forEach(function(e) { + ct.remove(e); + }); + + // Add a drilldown affordance to the end of the list + ct.add( + { + width: 28, + hideable: false, + sortable: false, + menuDisabled: true, + resizable: false, + draggable: false, + stateId: 'affordance', + cls: 'nx-drilldown-affordance', + + defaultRenderer: function () { + return Ext.DomHelper.markup({ + tag: 'span', + cls: 'x-fa fa-angle-right' + }); + } + } + ); + + this.resumeEvents(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/Content.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/Content.js new file mode 100644 index 0000000000000000000000000000000000000000..787d6f98e5b791d2646c49e2bb8c6e5781624571 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/Content.js @@ -0,0 +1,129 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Content panel. + * + * @since 3.0 + */ +Ext.define('NX.view.feature.Content', { + extend: 'Ext.panel.Panel', + alias: 'widget.nx-feature-content', + + itemId: 'feature-content', + ui: 'nx-feature-content', + cls: 'nx-feature-content', + layout: 'fit', + + /** + * @private + * If false, show a warning modal when you’re about to discard unsaved changes by navigating away + */ + discardUnsavedChanges: false, + + header: { + items: [ + { + xtype: 'panel', + layout: { type: 'hbox' }, + itemId: 'breadcrumb' + } + ] + }, + + listeners: { + afterrender: function(obj) { + obj.rendered = true; + obj.showRoot(); + } + }, + + /** + * Show the feature root (hide the breadcrumb) + */ + showRoot: function() { + var me = this; + var breadcrumb = me.down('#breadcrumb'); + + if (!me.rendered) { + return; + } + + breadcrumb.removeAll(); + breadcrumb.add( + { + xtype: 'label', + cls: 'nx-feature-name', + text: me.currentTitle + }, + { + xtype: 'label', + cls: 'nx-feature-description', + text: me.currentDescription + } + ); + }, + + /** + * The currently set title, so subpanels can access it + * @param text + */ + currentTitle: undefined, + + /** + * Custom handling for title since we are using custom header component. + * + * @override + * @param text + */ + setTitle: function(text) { + var me = this; + + me.callParent(arguments); + + me.currentTitle = text; + }, + + /** + * Set description text. + * + * @public + * @param text + */ + setDescription: function(text) { + this.currentDescription = text; + }, + + /** + * The currently set iconCls, so we can remove it when changed. + * + * @private + */ + currentIconCls: undefined, + + /** + * @public + * Reset the discardUnsavedChanges flag (false by default) + */ + resetUnsavedChangesFlag: function(enable) { + var me = this; + + if (enable) { + me.discardUnsavedChanges = true; + } + else { + me.discardUnsavedChanges = false; + } + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/Group.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/Group.js new file mode 100644 index 0000000000000000000000000000000000000000..363e513b8dc0e5d089f0f71b5511e0abd7a3bac1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/Group.js @@ -0,0 +1,59 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Shows an icon display of features in the {@link NX.store.FeatureGroup} store. + * + * @since 3.0 + */ +Ext.define('NX.view.feature.Group', { + extend: 'Ext.container.Container', + alias: 'widget.nx-feature-group', + requires: [ + 'NX.Icons' + ], + + cls: [ + 'nx-feature-group', + 'nx-inset' + ], + + autoScroll: true, + + items: { + xtype: 'container', + frame: true, + cls: 'nx-subsection', + + items: { + xtype: 'dataview', + + store: 'FeatureGroup', + tpl: [ + '', + '
', + '{[ NX.Icons.img(values.iconName, "x32") ]}', + '{text}', + '
', + '
' + ], + + itemSelector: 'div.item-wrap', + trackOver: true, + overItemCls: 'x-item-over', + selectedItemCls: 'x-item-selected' + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/Menu.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/Menu.js new file mode 100644 index 0000000000000000000000000000000000000000..4fd3d8fc2ce5e3b0a6bc43c77cd20954cd09bfb6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/Menu.js @@ -0,0 +1,33 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Feature menu tree panel. + * + * @since 3.0 + */ +Ext.define('NX.view.feature.Menu', { + extend: 'Ext.tree.Panel', + alias: 'widget.nx-feature-menu', + + width: 220, + ui: 'nx-feature-menu', + + stateful: true, + stateId: 'nx-feature-menu', + + store: 'FeatureMenu', + rootVisible: false, + lines: false +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/NotFound.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/NotFound.js new file mode 100644 index 0000000000000000000000000000000000000000..3e268351c1ed3dd9ee7be36b2cec0bdee6e9297b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/NotFound.js @@ -0,0 +1,61 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Panel shown in case a bookmarked feature is not found (404 like). + * + * @since 3.0 + */ +Ext.define('NX.view.feature.NotFound', { + extend: 'Ext.container.Container', + alias: 'widget.nx-feature-notfound', + requires: [ + 'NX.I18n' + ], + + cls: [ + 'nx-feature-notfound', + 'nx-hr' + ], + + layout: { + type: 'vbox', + align: 'center', + pack: 'center' + }, + + /** + * @override + */ + initComponent: function () { + var me = this; + + me.items = [ + { + xtype: 'label', + cls: 'title', + text: me.path ? NX.I18n.format('Feature_NotFoundPath_Text', me.path) : NX.I18n.get('Feature_NotFound_Text') + }, + { + xtype: 'label', + cls: 'description', + // TODO: i18n + text: 'Sorry the feature you have selected does not exist. Please make another selection.' + } + ]; + + me.callParent(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/NotVisible.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/NotVisible.js new file mode 100644 index 0000000000000000000000000000000000000000..c0694d3c95a19eb9e76fb5df0a8195537a63af8d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/feature/NotVisible.js @@ -0,0 +1,61 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Panel shown in case a bookmarked feature cannot be shown (403 like). + * + * @since 3.0 + */ +Ext.define('NX.view.feature.NotVisible', { + extend: 'Ext.container.Container', + alias: 'widget.nx-feature-notvisible', + requires: [ + 'NX.I18n' + ], + + cls: [ + 'nx-feature-notvisible', + 'nx-hr' + ], + + layout: { + type: 'vbox', + align: 'center', + pack: 'center' + }, + + /** + * @override + */ + initComponent: function () { + var me = this; + + me.items = [ + { + xtype: 'label', + cls: 'title', + text: me.text + }, + { + xtype: 'label', + cls: 'description', + // TODO: i18n + text: 'Sorry you are not permitted to use the feature you selected. Please select another feature.' + } + ]; + + me.callParent(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/footer/Branding.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/footer/Branding.js new file mode 100644 index 0000000000000000000000000000000000000000..24cc3fe287ce45d335283e09fc9fa7e3c9ea4151 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/footer/Branding.js @@ -0,0 +1,25 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Footer branding panel. + * + * @since 3.0 + */ +Ext.define('NX.view.footer.Branding', { + extend: 'Ext.container.Container', + alias: 'widget.nx-footer-branding' + + // intentionally empty, placeholder for where branding footer content is dynamically inserted +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/footer/Panel.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/footer/Panel.js new file mode 100644 index 0000000000000000000000000000000000000000..fd669133652ea847ad8a331009d208af304465cd --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/footer/Panel.js @@ -0,0 +1,52 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Footer panel. + * + * @since 3.0 + */ +Ext.define('NX.view.footer.Panel', { + extend: 'Ext.container.Container', + alias: 'widget.nx-footer', + requires: [ + 'NX.I18n' + ], + + cls: 'nx-footer', + + /** + * @override + */ + initComponent: function() { + Ext.apply(this, { + layout: { + type: 'vbox', + align: 'stretch', + pack: 'start' + }, + + items: [ + { + xtype: 'container', + cls: 'copyright', + html: NX.I18n.get('Footer_Panel_HTML') + }, + { xtype: 'nx-footer-branding', hidden: true } + ] + }); + + this.callParent(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Branding.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Branding.js new file mode 100644 index 0000000000000000000000000000000000000000..a86d2f686d621ba01689a46654e82e0b552a74b2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Branding.js @@ -0,0 +1,25 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Header branding panel. + * + * @since 3.0 + */ +Ext.define('NX.view.header.Branding', { + extend: 'Ext.container.Container', + alias: 'widget.nx-header-branding' + + // intentionally empty, placeholder for where branding header content is dynamically inserted +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Help.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Help.js new file mode 100644 index 0000000000000000000000000000000000000000..1edbfa40bb390a026fee015bd177a3047b5539b2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Help.js @@ -0,0 +1,88 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Help button. + * + * @since 3.0 + */ +Ext.define('NX.view.header.Help', { + extend: 'Ext.button.Button', + alias: 'widget.nx-header-help', + requires: [ + 'NX.I18n' + ], + + /** + * @override + */ + initComponent: function () { + var me = this; + + me.tooltip = NX.I18n.get('Header_Help_Tooltip'); + me.glyph = 'xf059@FontAwesome'; // fa-question-circle + + // hide the menu button arrow + me.arrowCls = ''; + + me.menu = [ + { + // text and iconCls is dynamic + tooltip: NX.I18n.get('Header_Help_Feature_Tooltip'), + action: 'feature' + }, + '-', + { + text: NX.I18n.get('Header_Help_About_Text'), + iconCls: 'nx-icon-nexus-x16', + tooltip: NX.I18n.get('Header_Help_About_Tooltip'), + action: 'about' + }, + { + text: NX.I18n.get('Header_Help_Documentation_Text'), + iconCls: 'nx-icon-help-manual-x16', + tooltip: NX.I18n.get('Header_Help_Documentation_Tooltip'), + action: 'docs' + }, + { + text: NX.I18n.get('Header_Help_KB_Text'), + iconCls: 'nx-icon-help-kb-x16', + tooltip: NX.I18n.get('Header_Help_KB_Tooltip'), + action: 'kb' + }, + { + text: NX.I18n.get('Header_Help_Community_Text'), + iconCls: 'nx-icon-help-community-x16', + tooltip: NX.I18n.get('Header_Help_Community_Tooltip'), + action: 'community' + }, + { + text: NX.I18n.get('Header_Help_Issues_Text'), + iconCls: 'nx-icon-help-issues-x16', + tooltip: NX.I18n.get('Header_Help_Issues_Tooltip'), + action: 'issues' + }, + '-', + { + text: NX.I18n.get('Header_Help_Support_Text'), + iconCls: 'nx-icon-help-support-x16', + tooltip: NX.I18n.get('Header_Help_Support_Tooltip'), + action: 'support' + } + ]; + + me.callParent(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Logo.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Logo.js new file mode 100644 index 0000000000000000000000000000000000000000..965b51ce43baa572455adb175c68324b9f152395 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Logo.js @@ -0,0 +1,38 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Logo image. + * + * @since 3.0 + */ +Ext.define('NX.view.header.Logo', { + extend: 'Ext.Img', + requires: [ + 'NX.Icons' + ], + alias: 'widget.nx-header-logo', + + autoEl: 'span', + height: 32, + width: 32, + + /** + * @protected + */ + initComponent: function() { + this.setSrc(NX.Icons.url('nexus', 'x32')); + this.callParent(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Mode.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Mode.js new file mode 100644 index 0000000000000000000000000000000000000000..0ad221a35163c15db5ea677352aca9b4514d1b2b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Mode.js @@ -0,0 +1,154 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Mode selector widget. + * + * @since 3.0 + */ +Ext.define('NX.view.header.Mode', { + extend: 'Ext.container.Container', + alias: 'widget.nx-header-mode', + + config: { + /** + * Mode name. + * + * @cfg {String} + */ + name: undefined, + + /** + * Mode menu title. + * + * @cfg {String} + */ + title: undefined, + + /** + * Mode button tooltip. + * + * @cfg {String} + */ + tooltip: undefined, + + /** + * Mode button glyph. + * + * @cfg {String} + */ + glyph: undefined, + + /** + * If button should auto hide when no features are available for selected mode. + * + * @cfg {boolean} + */ + autoHide: false, + + /** + * If menu should be collapsed automatically when mode is selected. + * + * @cfg {boolean} + */ + collapseMenu: false + }, + + /** + * Absolute layout for caret positioning over button. + */ + layout: 'absolute', + + /** + * @override + */ + initComponent: function() { + var me = this; + + me.addEvents( + /** + * Fired when mode has been selected. + * + * @event selected + * @param {NX.view.header.Mode} mode + */ + 'selected' + ); + + Ext.apply(me, { + items: [ + { + xtype: 'button', + ui: 'nx-header', + cls: 'nx-modebutton', + scale: 'medium', + height: 39, + // min-width here as the user-mode extends past this with user-name + minWidth: 39, + toggleGroup: 'mode', + allowDepress: false, + tooltip: me.tooltip, + glyph: me.glyph, + handler: function(button) { + me.fireEvent('selected', me); + }, + // copied autoEl from Ext.button.Button + autoEl: { + tag: 'a', + hidefocus: 'on', + unselectable: 'on', + // expose mode name on element for testability to target button by mode name + 'data-name': me.name + } + }, + { + // css magic renders caret look + xtype: 'container', + cls: 'nx-caret', + width: 0, + height: 0, + x: 14, + y: 34 + } + ] + }); + + me.callParent(); + }, + + /** + * @public + * @param {String} text + */ + setText: function(text) { + this.down('button').setText(text); + }, + + /** + * @public + * @param {String} tip + */ + setTooltip: function(tip) { + this.down('button').setTooltip(tip); + }, + + /** + * @public + * @param {boolean} state + * @param {boolean} suppressEvent + */ + toggle: function(state, suppressEvent) { + this.down('button').toggle(state, suppressEvent); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Panel.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Panel.js new file mode 100644 index 0000000000000000000000000000000000000000..606893b26572f5b0624388c6664d8ef40e1b73d9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Panel.js @@ -0,0 +1,83 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Header panel. + * + * @since 3.0 + */ +Ext.define('NX.view.header.Panel', { + extend: 'Ext.container.Container', + alias: 'widget.nx-header-panel', + requires: [ + 'NX.I18n', + 'NX.State' + ], + + cls: 'nx-header-panel', + + layout: { + type: 'vbox', + align: 'stretch', + pack: 'start' + }, + + /** + * @override + */ + initComponent: function () { + var me = this; + + me.items = [ + { xtype: 'nx-header-branding', hidden: true }, + { + xtype: 'toolbar', + + // set height to ensure we have uniform size and not depend on what is in the toolbar + height: 40, + + anchor: '100%', + + defaults: { + scale: 'medium' + }, + + items: [ + { xtype: 'nx-header-logo' }, + { + xtype: 'container', + layout: { + type: 'vbox', + pack: 'center' + }, + items: [ + { + xtype: 'label', + cls: 'productname', + text: NX.I18n.get('Header_Panel_Logo_Text') + }, + { + xtype: 'label', + cls: 'productspec', + text: NX.State.getBrandedEditionAndVersion() + } + ] + } + ] + } + ]; + + me.callParent(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/QuickSearch.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/QuickSearch.js new file mode 100644 index 0000000000000000000000000000000000000000..fa856b2f302b5bc24e54202cca7a58fbcbc6e13c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/QuickSearch.js @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Help button. + * + * @since 3.0 + */ +Ext.define('NX.view.header.QuickSearch', { + extend: 'NX.ext.SearchBox', + alias: 'widget.nx-header-quicksearch', + requires: [ + 'NX.I18n' + ], + + /** + * @override + */ + initComponent: function() { + Ext.apply(this, { + itemId: 'quicksearch', + cls: 'nx-quicksearch', + width: 200, + emptyText: NX.I18n.get('Header_QuickSearch_Empty'), + // field tooltip + inputAttrTpl: "data-qtip='" + NX.I18n.get('Header_QuickSearch_Tooltip') + "'" + }); + + this.callParent(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Refresh.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Refresh.js new file mode 100644 index 0000000000000000000000000000000000000000..c79ecf0c5033c67a55560fadd27b3cb16a6e6461 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/Refresh.js @@ -0,0 +1,38 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Refresh button. + * + * @since 3.0 + */ +Ext.define('NX.view.header.Refresh', { + extend: 'Ext.button.Button', + alias: 'widget.nx-header-refresh', + requires: [ + 'NX.I18n' + ], + + /** + * @override + */ + initComponent: function() { + Ext.apply(this, { + tooltip: NX.I18n.get('Header_Refresh_Tooltip'), + glyph: 'xf021@FontAwesome' // fa-refresh + }); + + this.callParent(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/SignIn.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/SignIn.js new file mode 100644 index 0000000000000000000000000000000000000000..94bb8c847f8b3e1e42e4832b86c8b379cddfb4f4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/SignIn.js @@ -0,0 +1,40 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Sign-in button. + * + * @since 3.0 + */ +Ext.define('NX.view.header.SignIn', { + extend: 'Ext.button.Button', + alias: 'widget.nx-header-signin', + requires: [ + 'NX.I18n' + ], + + /** + * @override + */ + initComponent: function() { + Ext.apply(this, { + text: NX.I18n.get('Header_SignIn_Text'), + tooltip: NX.I18n.get('Header_SignIn_Tooltip'), + glyph: 'xf090@FontAwesome' // fa-sign-in + }); + + this.callParent(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/SignOut.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/SignOut.js new file mode 100644 index 0000000000000000000000000000000000000000..34bf21fd60aeffd987bbcc796bfdefb6de148f83 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/header/SignOut.js @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Sign-out button. + * + * @since 3.0 + */ +Ext.define('NX.view.header.SignOut', { + extend: 'Ext.button.Button', + alias: 'widget.nx-header-signout', + requires: [ + 'NX.I18n' + ], + + /** + * @override + */ + initComponent: function() { + Ext.apply(this, { + text: NX.I18n.get('Header_SignOut_Text'), + tooltip: NX.I18n.get('Header_SignOut_Tooltip'), + glyph: 'xf08b@FontAwesome', // fa-sign-out + hidden: true + }); + + this.callParent(); + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/info/Entry.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/info/Entry.js new file mode 100644 index 0000000000000000000000000000000000000000..9145cd1d3efb7b6112485fa3df036a94230ba863 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/info/Entry.js @@ -0,0 +1,77 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Info entry. + * + * @since 3.0 + */ +Ext.define('NX.view.info.Entry', { + extend: 'Ext.Component', + alias: 'widget.nx-info', + requires: [ + 'Ext.XTemplate' + ], + + /** + * @override + */ + initComponent: function () { + var me = this; + + me.tpl = Ext.create('Ext.XTemplate', [ + '
', + '', + '', + '', + '', + '', + '', + '', + '', + '
{name}{value}
', + '
' + ]); + + me.callParent(); + }, + + /** + * @public + * @param {Object} info + */ + showInfo: function (info) { + var entries = []; + Ext.Object.each(info, function (key, value) { + if (!Ext.isEmpty(value)) { + entries.push( + { + name: key, + value: value + } + ); + } + }); + if (this.getEl()) { + this.tpl.overwrite(this.getEl(), entries); + + // FIXME: what 'panel' is this intended to re-layout? + this.up('panel').doComponentLayout(); + } + else { + this.html = this.tpl.apply(entries); + } + } + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/info/Panel.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/info/Panel.js new file mode 100644 index 0000000000000000000000000000000000000000..e70ee9d328588e15e26ecf96bf20d6014f32eaaa --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/info/Panel.js @@ -0,0 +1,92 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Info panel. + * + * @since 3.0 + */ +Ext.define('NX.view.info.Panel', { + extend: 'Ext.panel.Panel', + alias: 'widget.nx-info-panel', + + // FIXME: What is this for? + titled: null, + + framed: true, + autoScroll: true, + header: false, + + // FIXME: Reduce use of nested panels + + /** + * @private + */ + initComponent: function() { + var me = this, + inset, + subsection; + + subsection = { + xtype: 'panel', + ui: 'nx-subsection', + //if framed the title will come from the inset section + title: me.framed ? undefined : me.titled, + frame: me.framed, + items: { xtype: 'nx-info' } + }; + + inset = { + xtype: 'panel', + ui: 'nx-inset', + title: me.titled, + collapsible: me.collapsible, + + items: subsection + }; + + if (me.framed) { + me.items = inset; + } + else { + me.items = subsection; + } + + me.callParent(); + }, + + /** + * @public + */ + setTitle: function(title) { + var me = this; + me.titled = title; + me.down('panel').down('panel').setTitle(title); + }, + + /** + * @public + */ + showInfo: function (info) { + this.down('nx-info').showInfo(info); + }, + + /** + * @public + * Add an additional component to enhance the info. + */ + addSection: function(component) { + this.down('nx-info').up('panel').add(component); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/message/Notification.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/message/Notification.js new file mode 100644 index 0000000000000000000000000000000000000000..7c551b8d530d6b482e431aee8b57337de128c6fc --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/view/message/Notification.js @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext*/ + +/** + * Notification window. + * + * @since 3.0 + */ +Ext.define('NX.view.message.Notification', { + extend: 'Ext.ux.window.Notification', + alias: 'widget.nx-message-notification', + + minWidth: 200, + maxWidth: 400, + autoShow: true, + + manager: 'default', + + // top-right, but do not obscure the header toolbar + position: 'tr', + paddingX: 10, + paddingY: 55, + + stickWhileHover: true, + slideInDuration: 800, + slideBackDuration: 1500, + autoCloseDelay: 4000, + slideInAnimation: 'elasticIn', + slideBackAnimation: 'elasticIn' +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/Controller.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/Controller.js new file mode 100644 index 0000000000000000000000000000000000000000..d4c1c9f69446ae29fac65927142a27c8676cfcf7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/Controller.js @@ -0,0 +1,465 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Wizard controller support. + * + * @since 3.0 + * @abstract + */ +Ext.define('NX.wizard.Controller', { + extend: 'NX.app.Controller', + requires: [ + 'NX.Assert' + ], + + /** + * Registered steps. + * + * @private + * @type {NX.wizard.Step[]} + */ + steps: undefined, + + /** + * Active step index. + * + * @private + * @type {Number} + */ + activeStepIndex: undefined, + + /** + * Shared context. + * + * @private + * @type {Ext.util.MixedCollection} + */ + context: undefined, + + /** + * @override + */ + init: function () { + var me = this; + + // initialize privates + me.steps = []; + me.activeStepIndex = 0; + me.context = Ext.create('Ext.util.MixedCollection'); + + // listen for events for logging consistency + // + me.context.on({ + add: function(index, value, key) { + me.logDebug('Set', key, '=', value); + }, + remove: function(value, key) { + me.logDebug('Unset', key); + } + }); + // + + me.addRef([ + { + ref: 'content', + selector: 'nx-feature-content' + }, + { + ref: 'panel', + selector: 'nx-wizard-panel' + } + ]); + + me.listen({ + controller: { + '#Refresh': { + // FIXME: handle-global refresh, this is not ideal as this calls all wizards even if not activated + refresh: me.refresh + } + } + }); + + me.callParent(); + }, + + /** + * Reset when controller is destroyed. + * + * @override + */ + onDestroy: function() { + this.reset(); + }, + + /** + * Register a step. + * + * @protected + * @param {String|Object} config + */ + registerStep: function(config) { + var step; + + // + this.logDebug('Register step:', config); + // + + if (Ext.isString(config)) { + step = Ext.create(config); + } + else { + step = Ext.widget(config); + } + + step.attach(this); + this.steps.push(step); + }, + + /** + * Register steps. + * + * @param {String[]|Object[]} configs + */ + registerSteps: function(configs) { + var me = this; + Ext.Array.each(configs, function(config) { + me.registerStep(config); + }); + }, + + /** + * Get a step by name. + * + * @param {String} name + * @return {NX.wizard.Step} Step with name or null if not found. + */ + getStep: function(name) { + var s = this.steps, + i; + + for (i = 0; i < s.length; i++) { + if (name === s[i].getName()) { + return s[i]; + } + } + + this.logWarn('Missing step:', name); + + return null; + }, + + /** + * Return the index of given step. + * + * @param {NX.wizard.Step} step + * @return {number} + */ + getStepIndex: function(step) { + var s = this.steps, + i; + + for (i = 0; i < s.length; i++) { + if (step.getName() === s[i].getName()) { + return i; + } + } + + return -1; + }, + + /** + * Returns the active step. + * + * @return {NX.wizard.Step} + */ + getActiveStep: function() { + return this.steps[this.activeStepIndex]; + }, + + /** + * Load wizard. + * + * @protected + */ + load: function() { + var s = this.steps, + i, + panel; + + // + this.logDebug('Loading'); + // + + panel = this.getPanel().getScreenContainer(); + for (i = 0; i < s.length; i++) { + panel.add(s[i].createScreenCmp()); + } + + this.restore(); + }, + + /** + * @private + */ + refresh: function() { + // + this.logDebug('Refreshing'); + // + + this.getActiveStep().refresh(); + }, + + // + // Context + // + + /** + * Returns shared context. + * + * @returns {Ext.util.MixedCollection} + */ + getContext: function() { + return this.context; + }, + + // + // Navigation + // + + // TODO: fire navigation events? + + /** + * Move to specific step index. + * + * @param {number} index + * @return {boolean} True if moved + */ + moveTo: function(index) { + if (index < 0 || index + 1 > this.steps.length) { + this.logError('Index out of bounds:', index); + return false; + } + + // + this.logDebug('Moving to:', index); + // + + var panel = this.getPanel(), + container, + cards, + screen; + + // panel may not exist if controller being destroyed + if (!panel) { + return false; + } + + container = panel.getScreenContainer(); + cards = container.getLayout(); + + // move and resolve screen component + screen = cards.setActiveItem(index); + if (screen === false) { + screen = cards.getActiveItem(); + } + + this.activeStepIndex = index; + + this.updateScreenHeader(screen, index); + + return true; + }, + + /** + * Update the title and progress for the current screen. + * + * @private + * @param {NX.wizard.Screen} screen + * @param {number} index + */ + updateScreenHeader: function(screen, index) { + var panel = this.getPanel(), + s = this.steps, + enabledSteps = 0, + screenNumber = 1, + i; + + // calculate number of enabled steps and displayed screen number + for (i = 0; i < s.length; i++) { + if (s[i].enabled) { + enabledSteps++; + if (i < index) { + screenNumber++; + } + } + } + + panel.setTitle(screen.getTitle()); + panel.setProgress(screenNumber, enabledSteps); + }, + + /** + * Move to step with given name. + * + * @param {String} name + * @return {boolean} True if moved + */ + moveToStepNamed: function(name) { + var me = this, + step; + + // + me.logDebug('Moving to step with name:', name); + // + + step = me.getStep(name); + + if (!step) { + this.logError('Missing step with name:', name); + return false; + } + + return me.moveTo(me.getStepIndex(step)); + }, + + /** + * Move to the next enabled step. + * + * @return {boolean} True if moved + */ + moveNext: function() { + var current = this.activeStepIndex, + max = this.steps.length, + i; + + if (current + 1 >= max) { + this.logError('No next step'); + return false; + } + + for (i = current + 1; i < max; i++) { + if (this.steps[i].enabled) { + return this.moveTo(i); + } + } + + this.logError('No enabled next step'); + return false; + }, + + /** + * Move to the previous step. + * + * @return {boolean} True if moved + */ + moveBack: function() { + var current = this.activeStepIndex, + i; + + if (current <= 0) { + this.logError('No back step'); + return false; + } + + for (i = current - 1; i >= 0; i--) { + if (this.steps[i].enabled) { + return this.moveTo(i); + } + } + + this.logError('No enabled back step'); + return false; + }, + + /** + * Reset state to initial. + */ + reset: function() { + var s = this.steps, + i; + + // + this.logDebug('Resetting'); + // + + // Reset all steps + for (i = 0; i < s.length; i++) { + s[i].reset(); + } + + // Reset context + this.context.removeAll(); + + // Move to starting position + this.moveTo(0); + + // + this.logDebug('Reset'); + // + }, + + /** + * Cancel and reset. + */ + cancel: function() { + // + this.logDebug('Canceled'); + // + + this.reset(); + }, + + /** + * Finish and reset. + */ + finish: function() { + // + this.logDebug('Finished'); + // + + this.reset(); + }, + + /** + * Restore state to last known. + */ + restore: function() { + this.moveTo(this.activeStepIndex); + + // + this.logDebug('Restored'); + // + }, + + // + // Helpers + // + + /** + * Display screen mask. + * + * @param {string} message + */ + mask: function(message) { + this.getContent().getEl().mask(message); + }, + + /** + * Remove screen mask. + */ + unmask: function() { + this.getContent().getEl().unmask(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/FormScreen.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/FormScreen.js new file mode 100644 index 0000000000000000000000000000000000000000..0ca2253b679bf472ceeebe00c5380e56e0ffdc20 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/FormScreen.js @@ -0,0 +1,33 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Wizard form screen. + * + * @since 3.0 + * @abstract + */ +Ext.define('NX.wizard.FormScreen', { + extend: 'NX.wizard.Screen', + alias: 'widget.nx-wizard-formscreen', + + /** + * Returns the screen form. + * + * @return {Ext.form.Basic} + */ + getForm: function() { + return this.down('form').getForm(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/GridScreen.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/GridScreen.js new file mode 100644 index 0000000000000000000000000000000000000000..285ed4b29c31354f46c983da2d7a50ffacb362fc --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/GridScreen.js @@ -0,0 +1,208 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Wizard grid screen. + * + * @since 3.0 + * @abstract + */ +Ext.define('NX.wizard.GridScreen', { + extend: 'NX.wizard.Screen', + alias: 'widget.nx-wizard-gridscreen', + requires: [ + 'NX.Assert' + ], + + config: { + /** + * @cfg {Object} {@link Ext.grid.Panel} configuration object. + */ + grid: undefined + }, + + /** + * @override + */ + initComponent: function () { + var me = this; + + // + NX.Assert.assert(me.grid, 'Missing required config: grid'); + // + + Ext.applyIf(me.grid, { + xtype: 'grid' + }); + + me.fields = me.fields || []; + me.fields.push(me.grid); + + me.callParent(arguments); + + var view = me.getGrid().getView(); + + // when grid loads sync + view.on('refresh', function (view) { + me.syncSizeToScreen(view); + }); + + // when screen is activated sync + me.on('activate', function () { + me.syncSizeToScreen(view); + }); + + // when wizard panel resizes sync + me.on('added', function () { + var panel = me.up('nx-wizard-panel'); + panel.on('resize', function () { + me.syncSizeToScreen(view); + }); + }); + }, + + /** + * @return {Ext.grid.Panel} + */ + getGrid: function () { + return this.down('grid'); + }, + + /** + * @private + * @param {Ext.view.Table} view + */ + syncSizeToScreen: function (view) { + //console.log('syncing size'); + + Ext.suspendLayouts(); + try { + var table, + contentHeight, + visibleHeight, + maxHeight; + + // ref: Ext.view.Table.hasVerticalScroll() + table = view.getEl().down('table'); + if (!table) { + return; + } + + contentHeight = table.getHeight(); + visibleHeight = view.getHeight(); + maxHeight = this.calculateMaxHeight(view); + + if (contentHeight > maxHeight) { + // content is larger than can display, needs to be set to max visible to scroll + view.setHeight(maxHeight); + view.setAutoScroll(true); + } + else if (visibleHeight < contentHeight) { + // content fits into visible height, use content height + view.setHeight(contentHeight); + view.setAutoScroll(false); + } + else if (contentHeight < visibleHeight) { + // content is smaller than visible, use content height + view.setHeight(contentHeight); + view.setAutoScroll(false); + } + + //console.log('content,visible,max,current', contentHeight, visibleHeight, maxHeight, view.getHeight()); + } + finally { + Ext.resumeLayouts(true); + } + }, + + /** + * @private + * @param {Ext.view.Table} view + * @returns {number} + */ + calculateMaxHeight: function (view) { + var me = this, + panel = me.up('nx-wizard-panel'), + screenContainer = panel.getScreenContainer(), + screenHeader = panel.getScreenHeader(), + buttonsContainer = me.getButtonsContainer(), + max; + + //function logheight(name, comp) { + // var el = comp.getEl(); + // console.log( + // name, + // //me.heightOf(comp), + // 'hf', el.getHeight(false), + // 'ht', el.getHeight(true), + // 'hf-ht', el.getHeight(false) - el.getHeight(true), + // 'p', el.getPadding('tb'), + // 'b', el.getBorderWidth('tb'), + // 'm', el.getMargin('tb') + // ); + //} + + //logheight('panel', panel); + //logheight('view', view); + //logheight('screen-container', screenContainer); + //logheight('screen-header', screenHeader); + //logheight('screen-form', me.down('form')); + //logheight('grid-headers', view.getHeaderCt()); + //logheight('buttons-container', buttonsContainer); + + function h(comp) { + var el = comp.getEl(), + hf = el.getHeight(false), + hc = el.getHeight(true), + m = el.getMargin('tb'); + return hf + (hf - hc) + m; + } + + //Object.is replacement, as not supported in all browsers, derived from + //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is + function is(x, y) { + if (x === y) { + return x !== 0 || 1 / x === 1 / y; + } else { + return x !== x && y !== y; + } + }; + + // calculate the height of all fields (except the grid) + var grid = me.getGrid(); + var fieldsHeight = 0; + me.down('#fields').items.each(function(item) { + if (!is(item, grid)) { + //logheight('field', item); + + fieldsHeight += h(item); + } + }); + + // FIXME: where do these come from? + var mysteryPixels = 6; + var screenContainerEl = screenContainer.getEl(); + + max = panel.getHeight(true) - + h(screenHeader) - + (screenContainerEl.getHeight(false) - screenContainerEl.getHeight(true)) - + fieldsHeight - + h(view.getHeaderCt()) - + h(buttonsContainer) - + mysteryPixels; + + //console.log('max:', max); + return max; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/Panel.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/Panel.js new file mode 100644 index 0000000000000000000000000000000000000000..d1a3d76209651d8eca2fa589f73f2f9c169b35d4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/Panel.js @@ -0,0 +1,94 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Wizard panel. + * + * @since 3.0 + * @abstract + */ +Ext.define('NX.wizard.Panel', { + extend: 'Ext.panel.Panel', + alias: 'widget.nx-wizard-panel', + requires: [ + 'NX.I18n' + ], + + cls: 'nx-wizard-panel', + + autoScroll: true, + layout: { + type: 'vbox', + align: 'stretch' + }, + + items: { + xtype: 'container', + itemId: 'container', + cls: 'screencontainer', + frame: true, + layout: 'card' + }, + + // screen header (title + progress) + dockedItems: { + xtype: 'toolbar', + itemId: 'header', + cls: 'screenheader', + dock: 'top', + items: [ + { + xtype: 'label', + itemId: 'title', + cls: 'title' + }, + '->', + { + xtype: 'label', + itemId: 'progress', + cls: 'progress' + } + ] + }, + + /** + * @returns {Ext.container.Container} + */ + getScreenHeader: function () { + return this.down('#header'); + }, + + /** + * @param {String} title + */ + setTitle: function (title) { + this.getScreenHeader().down('#title').setText(title); + }, + + /** + * @param {number} current Current screen number. + * @param {number} total Total number of screens. + */ + setProgress: function (current, total) { + this.getScreenHeader().down('#progress').setText(NX.I18n.format('Wizard_Screen_Progress', current, total) + ); + }, + + /** + * @returns {Ext.panel.Panel} + */ + getScreenContainer: function () { + return this.down('#container'); + } +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/Screen.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/Screen.js new file mode 100644 index 0000000000000000000000000000000000000000..724328a3dbd5babf5233caf09fcd0f0e2aaccbc9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/Screen.js @@ -0,0 +1,140 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Wizard screen. + * + * @since 3.0 + * @abstract + */ +Ext.define('NX.wizard.Screen', { + extend: 'Ext.container.Container', + alias: 'widget.nx-wizard-screen', + requires: [ + 'NX.I18n' + ], + mixins: { + logAware: 'NX.LogAware' + }, + + config: { + /** + * @cfg {String} + */ + title: undefined, + + /** + * @cfg {String} + */ + description: undefined, + + /** + * @cfg {String[]/Object[]} + */ + buttons: undefined, + + /** + * @cfg {Object[]} + */ + fields: undefined + }, + + layout: 'fit', + + /** + * @override + */ + initComponent: function () { + var me = this, + items = [], + buttons = []; + + // NOTE: title is handled by controller, rendered in NX.wizard.Panel, arguably should be part of step not screen + + // add optional description + if (me.description) { + items.push({ + xtype: 'container', + itemId: 'description', + html: me.description + }); + } + + // add optional form fields + if (me.fields) { + Ext.Array.push(items, me.fields); + } + + // add optional buttons + if (me.buttons) { + Ext.Array.each(me.buttons, function (button) { + if (button === 'next') { + buttons.push({ + text: NX.I18n.get('Wizard_Next'), + action: 'next', + ui: 'nx-primary', + formBind: true + }); + } + else if (button === 'back') { + buttons.push({ + text: NX.I18n.get('Wizard_Back'), + action: 'back', + ui: 'default' + }); + } + else if (button === 'cancel') { + buttons.push({ + text: NX.I18n.get('Wizard_Cancel'), + action: 'cancel', + ui: 'default' + }) + } + else if (Ext.isObject(button)) { + // custom button configuration + buttons.push(button); + } + else { + me.logWarn('Invalid button:', button); + } + }); + } + + Ext.apply(me, { + items: { + xtype: 'form', + itemId: 'fields', + items: items, + buttonAlign: 'left', + buttons: buttons + } + }); + + me.callParent(arguments); + }, + + /** + * @returns {Ext.container.Container} + */ + getDescriptionContainer: function() { + return this.down('#description'); + }, + + /** + * @return {Ext.toolbar.Toolbar} + */ + getButtonsContainer: function() { + return this.down('form').getDockedItems('toolbar[dock="bottom"]')[0]; + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/Step.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/Step.js new file mode 100644 index 0000000000000000000000000000000000000000..5bdd626ab23fadc4cc54fa2e026f72ef3b4918dc --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/NX/wizard/Step.js @@ -0,0 +1,464 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/*global Ext, NX*/ + +/** + * Wizard step. + * + * @since 3.0 + * @abstract + */ +Ext.define('NX.wizard.Step', { + alias: 'widget.nx-wizard-step', + requires: [ + 'NX.Assert' + ], + mixins: { + observable: 'Ext.util.Observable', + logAware: 'NX.LogAware' + }, + + config: { + /** + * Class name of screen. + * + * @cfg {String} + */ + screen: undefined, + + /** + * Automatically reset step when moving back. + * + * @cfg {Boolean} + */ + resetOnBack: false, + + /** + * Set to false to disable step. + * + * @cfg {Boolean} + */ + enabled: true + }, + + /** + * The controller which the step is attached to. + * + * @protected {NX.wizard.Controller} + */ + controller: undefined, + + /** + * Step name. + * + * @private {String} + */ + name: undefined, + + /** + * Screen class. + * + * @private {Ext.Class} + */ + screenClass: undefined, + + /** + * Screen xtype. + * + * @private {String} + */ + screenXtype: undefined, + + /** + * @constructor + */ + constructor: function (config) { + var me = this; + me.mixins.observable.constructor.call(me, config); + me.initConfig(config); + + me.name = Ext.getClassName(me); + me.screenClass = Ext.ClassManager.get(me.screen); + // TODO: Sort out if this is proper api to get xtype from class, this may not be portable to new versions of extjs + me.screenXtype = me.screenClass.xtype; + }, + + /** + * Returns step name. Defaults to class-name. + * + * @return {String} + */ + getName: function () { + return this.name; + }, + + /** + * Returns screen class. + * + * @return {Ext.Class} + */ + getScreenClass: function () { + return this.screenClass; + }, + + /** + * Returns screen xtype. + * + * @return {String} + */ + getScreenXtype: function () { + return this.screenXtype; + }, + + /** + * Returns screen component. + * + * @return {NX.wizard.Screen|undefined} + */ + getScreenCmp: function () { + var xtype = this.screenXtype, + matches = Ext.ComponentQuery.query(xtype); + + if (matches.length === 0) { + return undefined; + } + + // + NX.Assert.assert(matches.length === 1, 'Expected 1 component matching:', xtype, '; found:', matches.length); + // + + return matches[0]; + }, + + /** + * Create screen component or component configuration. + * + * @return {Object|NX.wizard.Screen} + */ + createScreenCmp: function () { + return { + xtype: this.screenXtype + }; + }, + + // + // Lifecycle + // + + /** + * Attach step to controller and initialize step. + * + * @param {NX.wizard.Controller} controller + */ + attach: function (controller) { + var me = this; + + me.controller = controller; + + // TODO: We could probably simplify this by logic in controller for nx-wizard-screen + // TODO: and could probably do with some better eventing between screen + step and avoid this global listener bloat + + // setup core screen event handlers + me.controller.control(me.screenXtype, { + activate: { + fn: me.doActivate, + scope: me + } + }); + + // initialize step + me.init(); + + // + me.logDebug('Attached'); + // + }, + + /** + * Initialize state. + * + * @protected + * @template + */ + init: Ext.emptyFn, + + /** + * @private + * @type {boolean} + */ + prepared: false, + + /** + * @private + */ + doActivate: function() { + var me = this; + + // + me.logDebug('Activate'); + // + + if (!me.prepared) { + // + me.logDebug('Preparing'); + // + + me.prepare(); + me.prepared = true; + } + }, + + /** + * Prepare state. + * + * @protected + * @template + */ + prepare: Ext.emptyFn, + + /** + * Refresh state. + * + * @template + */ + refresh: Ext.emptyFn, + + /** + * Reset state. + */ + reset: function() { + this.prepared = false; + this.enabled = true; + + // + this.logDebug('Reset'); + // + }, + + // + // Events + // + + /** + * Helper to register listeners relative to screen component. + * + * Special handling for '$screen' selector to reference the screen itself. + * + * @protected + * @param {Object} selectors + */ + control: function (selectors) { + var me = this, + xtype = me.screenXtype, + ctrl = me.controller; + + Ext.Object.each(selectors, function (selector, listeners) { + var q; + if (selector === '$screen') { + q = xtype; + } + else { + q = xtype + ' ' + selector; + } + ctrl.control(q, me.normalizeListeners(listeners)); + }); + }, + + /** + * Normalize listeners to ensure scope to step, unless otherwise configured. + * + * @private + * @param {Object} listeners + * @return {Object} + */ + normalizeListeners: function(listeners) { + var me = this, + entry, + listener; + + for (entry in listeners) { + if (listeners.hasOwnProperty(entry)) { + listener = listeners[entry]; + + // if listener is a function convert to object and apply scope + if (Ext.isFunction(listener)) { + listener = { + fn: listener, + scope: me + }; + listeners[entry] = listener; + } + else { + // else apply scope if not specified + Ext.applyIf(listener, { + scope: me + }); + } + } + } + + return listeners; + }, + + /** + * Helper to register listeners on controller. + * + * @protected + * @param {Object} to + */ + listen: function (to) { + var me = this, + domain, + selectors, + selector, + ctrl = me.controller; + + // normalize all listeners + for (domain in to) { + if (to.hasOwnProperty(domain)) { + selectors = to[domain]; + for (selector in selectors) { + if (selectors.hasOwnProperty(selector)) { + me.normalizeListeners(selectors[selector]); + } + } + } + } + + ctrl.listen(to); + }, + + // + // Context + // + + /** + * Get the shared context. + * + * @returns {Ext.util.MixedCollection} + */ + getContext: function() { + return this.controller.getContext(); + }, + + /** + * Get shared context value. + * + * @protected + * @param {String} name + * @return {Object|undefined} + */ + get: function (name) { + return this.getContext().get(name); + }, + + /** + * Set shared context value. + * + * @protected + * @param {String} name + * @param {Object} value + */ + set: function (name, value) { + this.getContext().add(name, value); + }, + + /** + * Unset shared context value. + * + * @protected + * @param {String} name + */ + unset: function (name) { + this.getContext().removeAtKey(name); + }, + + // + // Navigation + // + + /** + * Request move to next step. + * + * @protected + */ + moveNext: function () { + this.controller.moveNext(); + }, + + /** + * Request move to previous step. + * + * @protected + */ + moveBack: function () { + this.controller.moveBack(); + + // optionally reset when moving back + if (this.resetOnBack) { + this.reset(); + } + }, + + /** + * Request cancel. + * + * @protected + */ + cancel: function () { + this.controller.cancel(); + }, + + /** + * Inform finished. + * + * @protected + */ + finish: function () { + this.controller.finish(); + }, + + // + // Helpers + // + + /** + * Return a store from controller by name. + * + * @protected + * @param {String} name + * @returns {Ext.data.Store} + */ + getStore: function(name) { + return this.controller.getStore(name); + }, + + /** + * Display content mask. + * + * @protected + * @param {String} message + */ + mask: function (message) { + this.controller.mask(message); + }, + + /** + * Remove content mask. + * + * @protected + */ + unmask: function () { + this.controller.unmask(); + } +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/baseapp-debug.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/baseapp-debug.js new file mode 100644 index 0000000000000000000000000000000000000000..7eb621dd38762b227c077f5c15aa419389973654 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/baseapp-debug.js @@ -0,0 +1,140268 @@ +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ + + + + + +var Ext = Ext || {}; +Ext._startTime = new Date().getTime(); +(function() { + var global = this, + objectPrototype = Object.prototype, + toString = objectPrototype.toString, + enumerables = true, + enumerablesTest = {toString: 1}, + emptyFn = function () {}, + + + callOverrideParent = function () { + var method = callOverrideParent.caller.caller; + return method.$owner.prototype[method.$name].apply(this, arguments); + }, + i, + nonWhitespaceRe = /\S/, + ExtApp, + iterableRe = /\[object\s*(?:Array|Arguments|\w*Collection|\w*List|HTML\s+document\.all\s+class)\]/; + + Ext.global = global; + + for (i in enumerablesTest) { + enumerables = null; + } + + if (enumerables) { + enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'constructor']; + } + + + Ext.enumerables = enumerables; + + + Ext.apply = function(object, config, defaults) { + if (defaults) { + Ext.apply(object, defaults); + } + + if (object && config && typeof config === 'object') { + var i, j, k; + + for (i in config) { + object[i] = config[i]; + } + + if (enumerables) { + for (j = enumerables.length; j--;) { + k = enumerables[j]; + if (config.hasOwnProperty(k)) { + object[k] = config[k]; + } + } + } + } + + return object; + }; + + Ext.buildSettings = Ext.apply({ + baseCSSPrefix: 'x-' + }, Ext.buildSettings || {}); + + Ext.apply(Ext, { + + + name: Ext.sandboxName || 'Ext', + + + emptyFn: emptyFn, + + + identityFn: function(o) { + return o; + }, + + + emptyString: new String(), + + + baseCSSPrefix: Ext.buildSettings.baseCSSPrefix, + + + applyIf: function(object, config) { + var property; + + if (object) { + for (property in config) { + if (object[property] === undefined) { + object[property] = config[property]; + } + } + } + + return object; + }, + + + iterate: function(object, fn, scope) { + if (Ext.isEmpty(object)) { + return; + } + + if (scope === undefined) { + scope = object; + } + + if (Ext.isIterable(object)) { + Ext.Array.each.call(Ext.Array, object, fn, scope); + } + else { + Ext.Object.each.call(Ext.Object, object, fn, scope); + } + } + }); + + Ext.apply(Ext, { + + + extend: (function() { + + var objectConstructor = objectPrototype.constructor, + inlineOverrides = function(o) { + for (var m in o) { + if (!o.hasOwnProperty(m)) { + continue; + } + this[m] = o[m]; + } + }; + + return function(subclass, superclass, overrides) { + + if (Ext.isObject(superclass)) { + overrides = superclass; + superclass = subclass; + subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() { + superclass.apply(this, arguments); + }; + } + + if (!superclass) { + Ext.Error.raise({ + sourceClass: 'Ext', + sourceMethod: 'extend', + msg: 'Attempting to extend from a class which has not been loaded on the page.' + }); + } + + + var F = function() {}, + subclassProto, superclassProto = superclass.prototype; + + F.prototype = superclassProto; + subclassProto = subclass.prototype = new F(); + subclassProto.constructor = subclass; + subclass.superclass = superclassProto; + + if (superclassProto.constructor === objectConstructor) { + superclassProto.constructor = superclass; + } + + subclass.override = function(overrides) { + Ext.override(subclass, overrides); + }; + + subclassProto.override = inlineOverrides; + subclassProto.proto = subclassProto; + + subclass.override(overrides); + subclass.extend = function(o) { + return Ext.extend(subclass, o); + }; + + return subclass; + }; + }()), + + + override: function (target, overrides) { + if (target.$isClass) { + target.override(overrides); + } else if (typeof target == 'function') { + Ext.apply(target.prototype, overrides); + } else { + var owner = target.self, + name, value; + + if (owner && owner.$isClass) { + for (name in overrides) { + if (overrides.hasOwnProperty(name)) { + value = overrides[name]; + + if (typeof value == 'function') { + if (owner.$className) { + value.displayName = owner.$className + '#' + name; + } + + value.$name = name; + value.$owner = owner; + value.$previous = target.hasOwnProperty(name) + ? target[name] + : callOverrideParent; + } + + target[name] = value; + } + } + } else { + Ext.apply(target, overrides); + } + } + + return target; + } + }); + + + Ext.apply(Ext, { + + + valueFrom: function(value, defaultValue, allowBlank){ + return Ext.isEmpty(value, allowBlank) ? defaultValue : value; + }, + + + typeOf: function(value) { + var type, + typeToString; + + if (value === null) { + return 'null'; + } + + type = typeof value; + + if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') { + return type; + } + + typeToString = toString.call(value); + + switch(typeToString) { + case '[object Array]': + return 'array'; + case '[object Date]': + return 'date'; + case '[object Boolean]': + return 'boolean'; + case '[object Number]': + return 'number'; + case '[object RegExp]': + return 'regexp'; + } + + if (type === 'function') { + return 'function'; + } + + if (type === 'object') { + if (value.nodeType !== undefined) { + if (value.nodeType === 3) { + return (nonWhitespaceRe).test(value.nodeValue) ? 'textnode' : 'whitespace'; + } + else { + return 'element'; + } + } + + return 'object'; + } + + Ext.Error.raise({ + sourceClass: 'Ext', + sourceMethod: 'typeOf', + msg: 'Failed to determine the type of the specified value "' + value + '". This is most likely a bug.' + }); + }, + + + coerce: function(from, to) { + var fromType = Ext.typeOf(from), + toType = Ext.typeOf(to), + isString = typeof from === 'string'; + + if (fromType !== toType) { + switch (toType) { + case 'string': + return String(from); + case 'number': + return Number(from); + case 'boolean': + return isString && (!from || from === 'false') ? false : Boolean(from); + case 'null': + return isString && (!from || from === 'null') ? null : from; + case 'undefined': + return isString && (!from || from === 'undefined') ? undefined : from; + case 'date': + return isString && isNaN(from) ? Ext.Date.parse(from, Ext.Date.defaultFormat) : Date(Number(from)); + } + } + return from; + }, + + + isEmpty: function(value, allowEmptyString) { + return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0); + }, + + + isArray: ('isArray' in Array) ? Array.isArray : function(value) { + return toString.call(value) === '[object Array]'; + }, + + + isDate: function(value) { + return toString.call(value) === '[object Date]'; + }, + + + isObject: (toString.call(null) === '[object Object]') ? + function(value) { + + return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined; + } : + function(value) { + return toString.call(value) === '[object Object]'; + }, + + + isSimpleObject: function(value) { + return value instanceof Object && value.constructor === Object; + }, + + isPrimitive: function(value) { + var type = typeof value; + + return type === 'string' || type === 'number' || type === 'boolean'; + }, + + + isFunction: + + + (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) { + return !!value && toString.call(value) === '[object Function]'; + } : function(value) { + return !!value && typeof value === 'function'; + }, + + + isNumber: function(value) { + return typeof value === 'number' && isFinite(value); + }, + + + isNumeric: function(value) { + return !isNaN(parseFloat(value)) && isFinite(value); + }, + + + isString: function(value) { + return typeof value === 'string'; + }, + + + isBoolean: function(value) { + return typeof value === 'boolean'; + }, + + + isElement: function(value) { + return value ? value.nodeType === 1 : false; + }, + + + isTextNode: function(value) { + return value ? value.nodeName === "#text" : false; + }, + + + isDefined: function(value) { + return typeof value !== 'undefined'; + }, + + + isIterable: function(value) { + + if (!value || typeof value.length !== 'number' || typeof value === 'string' || Ext.isFunction(value)) { + return false; + } + + + + + if (!value.propertyIsEnumerable) { + return !!value.item; + } + + + + if (value.hasOwnProperty('length') && !value.propertyIsEnumerable('length')) { + return true; + } + + + return iterableRe.test(toString.call(value)); + } + }); + + Ext.apply(Ext, { + + + clone: function(item) { + var type, + i, + j, + k, + clone, + key; + + if (item === null || item === undefined) { + return item; + } + + + + + if (item.nodeType && item.cloneNode) { + return item.cloneNode(true); + } + + type = toString.call(item); + + + if (type === '[object Date]') { + return new Date(item.getTime()); + } + + + + if (type === '[object Array]') { + i = item.length; + + clone = []; + + while (i--) { + clone[i] = Ext.clone(item[i]); + } + } + + else if (type === '[object Object]' && item.constructor === Object) { + clone = {}; + + for (key in item) { + clone[key] = Ext.clone(item[key]); + } + + if (enumerables) { + for (j = enumerables.length; j--;) { + k = enumerables[j]; + if (item.hasOwnProperty(k)) { + clone[k] = item[k]; + } + } + } + } + + return clone || item; + }, + + + getUniqueGlobalNamespace: function() { + var uniqueGlobalNamespace = this.uniqueGlobalNamespace, + i; + + if (uniqueGlobalNamespace === undefined) { + i = 0; + + do { + uniqueGlobalNamespace = 'ExtBox' + (++i); + } while (Ext.global[uniqueGlobalNamespace] !== undefined); + + Ext.global[uniqueGlobalNamespace] = Ext; + this.uniqueGlobalNamespace = uniqueGlobalNamespace; + } + + return uniqueGlobalNamespace; + }, + + + functionFactoryCache: {}, + + cacheableFunctionFactory: function() { + var me = this, + args = Array.prototype.slice.call(arguments), + cache = me.functionFactoryCache, + idx, fn, ln; + + if (Ext.isSandboxed) { + ln = args.length; + if (ln > 0) { + ln--; + args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln]; + } + } + idx = args.join(''); + fn = cache[idx]; + if (!fn) { + fn = Function.prototype.constructor.apply(Function.prototype, args); + + cache[idx] = fn; + } + return fn; + }, + + functionFactory: function() { + var me = this, + args = Array.prototype.slice.call(arguments), + ln; + + if (Ext.isSandboxed) { + ln = args.length; + if (ln > 0) { + ln--; + args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln]; + } + } + + return Function.prototype.constructor.apply(Function.prototype, args); + }, + + + Logger: { + verbose: emptyFn, + log: emptyFn, + info: emptyFn, + warn: emptyFn, + error: function(message) { + throw new Error(message); + }, + deprecate: emptyFn + } + }); + + + Ext.type = Ext.typeOf; + + + + + ExtApp = Ext.app; + if (!ExtApp) { + ExtApp = Ext.app = {}; + } + Ext.apply(ExtApp, { + namespaces: {}, + + + collectNamespaces: function(paths) { + var namespaces = Ext.app.namespaces, + path; + + for (path in paths) { + if (paths.hasOwnProperty(path)) { + namespaces[path] = true; + } + } + }, + + + addNamespaces: function(ns) { + var namespaces = Ext.app.namespaces, + i, l; + + if (!Ext.isArray(ns)) { + ns = [ns]; + } + + for (i = 0, l = ns.length; i < l; i++) { + namespaces[ns[i]] = true; + } + }, + + + clearNamespaces: function() { + Ext.app.namespaces = {}; + }, + + + getNamespace: function(className) { + var namespaces = Ext.app.namespaces, + deepestPrefix = '', + prefix; + + for (prefix in namespaces) { + if (namespaces.hasOwnProperty(prefix) && + prefix.length > deepestPrefix.length && + (prefix + '.' === className.substring(0, prefix.length + 1))) { + deepestPrefix = prefix; + } + } + + return deepestPrefix === '' ? undefined : deepestPrefix; + } + }); +}()); + + +Ext.globalEval = Ext.global.execScript + ? function(code) { + execScript(code); + } + : function($$code) { + + + (function(){ + + + + var Ext = this.Ext; + eval($$code); + }()); + }; + + + + + + + +(function() { + + + +var version = '4.2.3.1477', + + checkVerTemp = [''], + endOfVersionRe = /([^\d\.])/, + notDigitsRe = /[^\d]/g, + plusMinusRe = /[\-+]/g, + stripRe = /\s/g, + underscoreRe = /_/g, + Version; + + Ext.Version = Version = Ext.extend(Object, { + isVersion: true, + + padModes: { + '~': NaN, + '^': Infinity + }, + + + release: '', + + + constructor: function (version, defaultMode) { + var me = this, + padModes = me.padModes, + ch, i, pad, parts, release, releaseStartIndex, ver; + + if (version.isVersion) { + return version; + } + + me.version = ver = String(version).toLowerCase(). + replace(underscoreRe, '.').replace(plusMinusRe, ''); + + ch = ver.charAt(0); + if (ch in padModes) { + ver = ver.substring(1); + pad = padModes[ch]; + } else { + pad = defaultMode ? padModes[defaultMode] : 0; + } + me.pad = pad; + + releaseStartIndex = ver.search(endOfVersionRe); + me.shortVersion = ver; + + if (releaseStartIndex !== -1) { + me.release = release = ver.substr(releaseStartIndex, version.length); + me.shortVersion = ver.substr(0, releaseStartIndex); + release = Version.releaseValueMap[release] || release; + } + + me.releaseValue = release || pad; + me.shortVersion = me.shortVersion.replace(notDigitsRe, ''); + + + me.parts = parts = ver.split('.'); + for (i = parts.length; i--; ) { + parts[i] = parseInt(parts[i], 10); + } + if (pad === Infinity) { + + parts.push(pad); + } + + + me.major = parts[0] || pad; + + + me.minor = parts[1] || pad; + + + me.patch = parts[2] || pad; + + + me.build = parts[3] || pad; + + return me; + }, + + + compareTo: function (other) { + + + var me = this, + lhsPad = me.pad, + lhsParts = me.parts, + lhsLength = lhsParts.length, + rhsVersion = other.isVersion ? other : new Version(other), + rhsPad = rhsVersion.pad, + rhsParts = rhsVersion.parts, + rhsLength = rhsParts.length, + length = Math.max(lhsLength, rhsLength), + i, lhs, rhs; + + for (i = 0; i < length; i++) { + lhs = (i < lhsLength) ? lhsParts[i] : lhsPad; + rhs = (i < rhsLength) ? rhsParts[i] : rhsPad; + + + + if (lhs < rhs) { + return -1; + } + if (lhs > rhs) { + return 1; + } + } + + + lhs = me.releaseValue; + rhs = rhsVersion.releaseValue; + if (lhs < rhs) { + return -1; + } + if (lhs > rhs) { + 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(target) { + return this.compareTo(target) > 0; + }, + + + isGreaterThanOrEqual: function(target) { + return this.compareTo(target) >= 0; + }, + + + isLessThan: function(target) { + return this.compareTo(target) < 0; + }, + + + isLessThanOrEqual: function(target) { + return this.compareTo(target) <= 0; + }, + + + equals: function(target) { + return this.compareTo(target) === 0; + }, + + + match: function(target) { + target = String(target); + return this.version.substr(0, target.length) === target; + }, + + + toArray: function() { + var me = this; + return [me.getMajor(), me.getMinor(), me.getPatch(), me.getBuild(), me.getRelease()]; + }, + + + getShortVersion: function() { + return this.shortVersion; + }, + + + gt: function (target) { + return this.compareTo(target) > 0; + }, + + + lt: function (target) { + return this.compareTo(target) < 0; + }, + + + gtEq: function (target) { + return this.compareTo(target) >= 0; + }, + + + ltEq: function (target) { + return this.compareTo(target) <= 0; + } + }); + + Ext.apply(Version, { + + releaseValueMap: { + dev: -6, + alpha: -5, + a: -5, + beta: -4, + b: -4, + rc: -3, + '#': -2, + p: -1, + pl: -1 + }, + + + getComponentValue: function(value) { + return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10)); + }, + + + compare: function (current, target) { + var ver = current.isVersion ? current : new Version(current); + return ver.compareTo(target); + } + }); + + + Ext.apply(Ext, { + + versions: {}, + + + lastRegisteredVersion: null, + + + setVersion: function(packageName, version) { + Ext.lastRegisteredVersion = Ext.versions[packageName] = new Version(version); + return this; + }, + + + getVersion: function(packageName) { + if (packageName === undefined) { + return Ext.lastRegisteredVersion; + } + + return Ext.versions[packageName]; + }, + + + checkVersion: function (specs, matchAll) { + var isArray = Ext.isArray(specs), + compat = isArray ? specs : checkVerTemp, + length = compat.length, + versions = Ext.versions, + frameworkVer = versions.ext || versions.touch, + i, index, matches, minVer, maxVer, spec, range, ver; + + if (!isArray) { + checkVerTemp[0] = specs; + } + + for (i = 0; i < length; ++i) { + if (!Ext.isString(spec = compat[i])) { + matches = Ext.checkVersion(spec.and || spec.or, !spec.or); + if (spec.not) { + matches = !matches; + } + } else { + if (spec.indexOf(' ') >= 0) { + spec = spec.replace(stripRe, ''); + } + + + + index = spec.indexOf('@'); + if (index < 0) { + range = spec; + ver = frameworkVer; + } else { + if (!(ver = versions[spec.substring(0, index)])) { + + + if (matchAll) { + return false; + } + + + continue; + } + range = spec.substring(index+1); + } + + + index = range.indexOf('-'); + if (index < 0) { + + if (range.charAt(index = range.length - 1) === '+') { + minVer = range.substring(0, index); + maxVer = null; + } else { + minVer = maxVer = range; + } + } else if (index > 0) { + + minVer = range.substring(0, index); + maxVer = range.substring(index+1); + } else { + + minVer = null; + maxVer = range.substring(index+1); + } + + matches = true; + if (minVer) { + minVer = new Version(minVer, '~'); + matches = minVer.ltEq(ver); + } + if (matches && maxVer) { + maxVer = new Version(maxVer, '~'); + matches = maxVer.gtEq(ver); + } + } + + if (matches) { + + if (!matchAll) { + return true; + } + } else if (matchAll) { + + return false; + } + } + + + + + + return !!matchAll; + }, + + + deprecate: function(packageName, since, closure, scope) { + if (Version.compare(Ext.getVersion(packageName), since) < 1) { + closure.call(scope); + } + } + }); + + Ext.setVersion('core', version); + +}()); + + + + + + + + +Ext.String = (function() { + var trimRegex = /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g, + escapeRe = /('|\\)/g, + formatRe = /\{\d+\}/, + escapeRegexRe = /([-.*+?\^${}()|\[\]\/\\])/g, + basicTrimRe = /^\s+|\s+$/g, + whitespaceRe = /\s+/, + varReplace = /(^[^a-z]*|[^\w])/gi, + charToEntity, + entityToChar, + charToEntityRegex, + entityToCharRegex, + htmlEncodeReplaceFn = function(match, capture) { + return charToEntity[capture]; + }, + htmlDecodeReplaceFn = function(match, capture) { + return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10)); + }, + boundsCheck = function(s, other){ + if (s === null || s === undefined || other === null || other === undefined) { + return false; + } + + return other.length <= s.length; + }, + + + + formatTplConfig = {useFormat: false, compiled: true, stringFormat: true}, + formatFns = {}, + generateFormatFn = function(format) { + + if (formatRe.test(format)) { + format = new Ext.Template(format, formatTplConfig); + return function() { + return format.apply(arguments); + }; + } + + else { + return function() { + return format; + }; + } + }; + + return { + + + insert: function(s, value, index) { + if (!s) { + return value; + } + + if (!value) { + return s; + } + + var len = s.length; + + if (!index && index !== 0) { + index = len; + } + + if (index < 0) { + index *= -1; + if (index >= len) { + + index = 0; + } else { + index = len - index; + } + } + + if (index === 0) { + s = value + s; + } else if (index >= s.length) { + s += value; + } else { + s = s.substr(0, index) + value + s.substr(index); + } + return s; + }, + + + startsWith: function(s, start, ignoreCase){ + var result = boundsCheck(s, start); + + if (result) { + if (ignoreCase) { + s = s.toLowerCase(); + start = start.toLowerCase(); + } + result = s.lastIndexOf(start, 0) === 0; + } + return result; + }, + + + endsWith: function(s, end, ignoreCase){ + var result = boundsCheck(s, end); + + if (result) { + if (ignoreCase) { + s = s.toLowerCase(); + end = end.toLowerCase(); + } + result = s.indexOf(end, s.length - end.length) !== -1; + } + return result; + }, + + + createVarName: function(s) { + return s.replace(varReplace, ''); + }, + + + htmlEncode: function(value) { + return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn); + }, + + + htmlDecode: function(value) { + return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn); + }, + + + hasHtmlCharacters: function(s) { + return charToEntityRegex.test(s); + }, + + + addCharacterEntities: function(newEntities) { + var charKeys = [], + entityKeys = [], + key, echar; + for (key in newEntities) { + echar = newEntities[key]; + entityToChar[key] = echar; + charToEntity[echar] = key; + charKeys.push(echar); + entityKeys.push(key); + } + charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g'); + entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g'); + }, + + + resetCharacterEntities: function() { + charToEntity = {}; + entityToChar = {}; + + this.addCharacterEntities({ + '&' : '&', + '>' : '>', + '<' : '<', + '"' : '"', + ''' : "'" + }); + }, + + + urlAppend : function(url, string) { + if (!Ext.isEmpty(string)) { + return url + (url.indexOf('?') === -1 ? '?' : '&') + string; + } + + return url; + }, + + + trim: function(string) { + return string.replace(trimRegex, ""); + }, + + + capitalize: function(string) { + return string.charAt(0).toUpperCase() + string.substr(1); + }, + + + uncapitalize: function(string) { + return string.charAt(0).toLowerCase() + string.substr(1); + }, + + + ellipsis: function(value, length, word) { + if (value && value.length > length) { + if (word) { + var vs = value.substr(0, length - 2), + index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?')); + if (index !== -1 && index >= (length - 15)) { + return vs.substr(0, index) + "..."; + } + } + return value.substr(0, length - 3) + "..."; + } + return value; + }, + + + escapeRegex: function(string) { + return string.replace(escapeRegexRe, "\\$1"); + }, + + + escape: function(string) { + return string.replace(escapeRe, "\\$1"); + }, + + + toggle: function(string, value, other) { + return string === value ? other : value; + }, + + + leftPad: function(string, size, character) { + var result = String(string); + character = character || " "; + while (result.length < size) { + result = character + result; + } + return result; + }, + + + format: function(format) { + var formatFn = formatFns[format] || (formatFns[format] = generateFormatFn(format)); + return formatFn.apply(this, arguments); + }, + + + repeat: function(pattern, count, sep) { + if (count < 1) { + count = 0; + } + for (var buf = [], i = count; i--; ) { + buf.push(pattern); + } + return buf.join(sep || ''); + }, + + + splitWords: function (words) { + if (words && typeof words == 'string') { + return words.replace(basicTrimRe, '').split(whitespaceRe); + } + return words || []; + } + }; +}()); + + +Ext.String.resetCharacterEntities(); + + +Ext.htmlEncode = Ext.String.htmlEncode; + + + +Ext.htmlDecode = Ext.String.htmlDecode; + + +Ext.urlAppend = Ext.String.urlAppend; + + + + + + + + +Ext.Number = new function() { + + var me = this, + isToFixedBroken = (0.9).toFixed() !== '1', + math = Math; + + Ext.apply(this, { + + constrain: function(number, min, max) { + var x = parseFloat(number); + + + + + + + + + + return (x < min) ? min : ((x > max) ? max : x); + }, + + + snap : function(value, increment, minValue, maxValue) { + var m; + + + + if (value === undefined || value < minValue) { + return minValue || 0; + } + + if (increment) { + m = value % increment; + if (m !== 0) { + value -= m; + if (m * 2 >= increment) { + value += increment; + } else if (m * 2 < -increment) { + value -= increment; + } + } + } + return me.constrain(value, minValue, maxValue); + }, + + + snapInRange : function(value, increment, minValue, maxValue) { + var tween; + + + minValue = (minValue || 0); + + + if (value === undefined || value < minValue) { + return minValue; + } + + + if (increment && (tween = ((value - minValue) % increment))) { + value -= tween; + tween *= 2; + if (tween >= increment) { + value += increment; + } + } + + + if (maxValue !== undefined) { + if (value > (maxValue = me.snapInRange(maxValue, increment, minValue))) { + value = maxValue; + } + } + + return value; + }, + + + toFixed: isToFixedBroken ? function(value, precision) { + precision = precision || 0; + var pow = math.pow(10, precision); + return (math.round(value * pow) / pow).toFixed(precision); + } : function(value, precision) { + return value.toFixed(precision); + }, + + + from: function(value, defaultValue) { + if (isFinite(value)) { + value = parseFloat(value); + } + + return !isNaN(value) ? value : defaultValue; + }, + + + randomInt: function (from, to) { + return math.floor(math.random() * (to - from + 1) + from); + }, + + + correctFloat: function(n) { + + + + return parseFloat(n.toPrecision(14)); + } + }); + + + Ext.num = function() { + return me.from.apply(this, arguments); + }; +}; + + + + + + + +(function() { + + var arrayPrototype = Array.prototype, + slice = arrayPrototype.slice, + supportsSplice = (function () { + var array = [], + lengthBefore, + j = 20; + + if (!array.splice) { + return false; + } + + + + + while (j--) { + array.push("A"); + } + + array.splice(15, 0, "F", "F", "F", "F", "F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F"); + + lengthBefore = array.length; + array.splice(13, 0, "XXX"); + + if (lengthBefore+1 != array.length) { + return false; + } + + + return true; + }()), + supportsForEach = 'forEach' in arrayPrototype, + supportsMap = 'map' in arrayPrototype, + supportsIndexOf = 'indexOf' in arrayPrototype, + supportsEvery = 'every' in arrayPrototype, + supportsSome = 'some' in arrayPrototype, + supportsFilter = 'filter' in arrayPrototype, + supportsSort = (function() { + var a = [1,2,3,4,5].sort(function(){ return 0; }); + return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5; + }()), + supportsSliceOnNodeList = true, + ExtArray, + erase, + replace, + splice; + + try { + + if (typeof document !== 'undefined') { + slice.call(document.getElementsByTagName('body')); + } + } catch (e) { + supportsSliceOnNodeList = false; + } + + function fixArrayIndex (array, index) { + return (index < 0) ? Math.max(0, array.length + index) + : Math.min(array.length, index); + } + + + function replaceSim (array, index, removeCount, insert) { + var add = insert ? insert.length : 0, + length = array.length, + pos = fixArrayIndex(array, index), + remove, + tailOldPos, + tailNewPos, + tailCount, + lengthAfterRemove, + i; + + + if (pos === length) { + if (add) { + array.push.apply(array, insert); + } + } else { + remove = Math.min(removeCount, length - pos); + tailOldPos = pos + remove; + tailNewPos = tailOldPos + add - remove; + tailCount = length - tailOldPos; + lengthAfterRemove = length - remove; + + if (tailNewPos < tailOldPos) { + for (i = 0; i < tailCount; ++i) { + array[tailNewPos+i] = array[tailOldPos+i]; + } + } else if (tailNewPos > tailOldPos) { + for (i = tailCount; i--; ) { + array[tailNewPos+i] = array[tailOldPos+i]; + } + } + + if (add && pos === lengthAfterRemove) { + array.length = lengthAfterRemove; + array.push.apply(array, insert); + } else { + array.length = lengthAfterRemove + add; + for (i = 0; i < add; ++i) { + array[pos+i] = insert[i]; + } + } + } + + return array; + } + + function replaceNative (array, index, removeCount, insert) { + if (insert && insert.length) { + + if (index === 0 && !removeCount) { + array.unshift.apply(array, insert); + } + + else if (index < array.length) { + array.splice.apply(array, [index, removeCount].concat(insert)); + } + + else { + array.push.apply(array, insert); + } + } else { + array.splice(index, removeCount); + } + return array; + } + + function eraseSim (array, index, removeCount) { + return replaceSim(array, index, removeCount); + } + + function eraseNative (array, index, removeCount) { + array.splice(index, removeCount); + return array; + } + + function spliceSim (array, index, removeCount) { + var pos = fixArrayIndex(array, index), + removed = array.slice(index, fixArrayIndex(array, pos+removeCount)); + + if (arguments.length < 4) { + replaceSim(array, pos, removeCount); + } else { + replaceSim(array, pos, removeCount, slice.call(arguments, 3)); + } + + return removed; + } + + function spliceNative (array) { + return array.splice.apply(array, slice.call(arguments, 1)); + } + + erase = supportsSplice ? eraseNative : eraseSim; + replace = supportsSplice ? replaceNative : replaceSim; + splice = supportsSplice ? spliceNative : spliceSim; + + + + ExtArray = Ext.Array = { + + each: function(array, fn, scope, reverse) { + array = ExtArray.from(array); + + var i, + ln = array.length; + + if (reverse !== true) { + for (i = 0; i < ln; i++) { + if (fn.call(scope || array[i], array[i], i, array) === false) { + return i; + } + } + } + else { + for (i = ln - 1; i > -1; i--) { + if (fn.call(scope || array[i], array[i], i, array) === false) { + return i; + } + } + } + + return true; + }, + + + forEach: supportsForEach ? function(array, fn, scope) { + array.forEach(fn, scope); + } : function(array, fn, scope) { + var i = 0, + ln = array.length; + + for (; i < ln; i++) { + fn.call(scope, array[i], i, array); + } + }, + + + indexOf: supportsIndexOf ? function(array, item, from) { + return arrayPrototype.indexOf.call(array, item, from); + } : function(array, item, from) { + var i, length = array.length; + + for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) { + if (array[i] === item) { + return i; + } + } + + return -1; + }, + + + contains: supportsIndexOf ? function(array, item) { + return arrayPrototype.indexOf.call(array, item) !== -1; + } : function(array, item) { + var i, ln; + + for (i = 0, ln = array.length; i < ln; i++) { + if (array[i] === item) { + return true; + } + } + + return false; + }, + + + toArray: function(iterable, start, end){ + if (!iterable || !iterable.length) { + return []; + } + + if (typeof iterable === 'string') { + iterable = iterable.split(''); + } + + if (supportsSliceOnNodeList) { + return slice.call(iterable, start || 0, end || iterable.length); + } + + var array = [], + i; + + start = start || 0; + end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length; + + for (i = start; i < end; i++) { + array.push(iterable[i]); + } + + return array; + }, + + + pluck: function(array, propertyName) { + var ret = [], + i, ln, item; + + for (i = 0, ln = array.length; i < ln; i++) { + item = array[i]; + + ret.push(item[propertyName]); + } + + return ret; + }, + + + map: supportsMap ? function(array, fn, scope) { + if (!fn) { + Ext.Error.raise('Ext.Array.map must have a callback function passed as second argument.'); + } + return array.map(fn, scope); + } : function(array, fn, scope) { + if (!fn) { + Ext.Error.raise('Ext.Array.map must have a callback function passed as second argument.'); + } + var results = [], + i = 0, + len = array.length; + + for (; i < len; i++) { + results[i] = fn.call(scope, array[i], i, array); + } + + return results; + }, + + + every: supportsEvery ? function(array, fn, scope) { + if (!fn) { + Ext.Error.raise('Ext.Array.every must have a callback function passed as second argument.'); + } + return array.every(fn, scope); + } : function(array, fn, scope) { + if (!fn) { + Ext.Error.raise('Ext.Array.every must have a callback function passed as second argument.'); + } + var i = 0, + ln = array.length; + + for (; i < ln; ++i) { + if (!fn.call(scope, array[i], i, array)) { + return false; + } + } + + return true; + }, + + + some: supportsSome ? function(array, fn, scope) { + if (!fn) { + Ext.Error.raise('Ext.Array.some must have a callback function passed as second argument.'); + } + return array.some(fn, scope); + } : function(array, fn, scope) { + if (!fn) { + Ext.Error.raise('Ext.Array.some must have a callback function passed as second argument.'); + } + var i = 0, + ln = array.length; + + for (; i < ln; ++i) { + if (fn.call(scope, array[i], i, array)) { + return true; + } + } + + return false; + }, + + + equals: function(array1, array2) { + var len1 = array1.length, + len2 = array2.length, + i; + + + if (array1 === array2) { + return true; + } + + if (len1 !== len2) { + return false; + } + + for (i = 0; i < len1; ++i) { + if (array1[i] !== array2[i]) { + return false; + } + } + + return true; + }, + + + clean: function(array) { + var results = [], + i = 0, + ln = array.length, + item; + + for (; i < ln; i++) { + item = array[i]; + + if (!Ext.isEmpty(item)) { + results.push(item); + } + } + + return results; + }, + + + unique: function(array) { + var clone = [], + i = 0, + ln = array.length, + item; + + for (; i < ln; i++) { + item = array[i]; + + if (ExtArray.indexOf(clone, item) === -1) { + clone.push(item); + } + } + + return clone; + }, + + + filter: supportsFilter ? function(array, fn, scope) { + if (!fn) { + Ext.Error.raise('Ext.Array.filter must have a filter function passed as second argument.'); + } + return array.filter(fn, scope); + } : function(array, fn, scope) { + if (!fn) { + Ext.Error.raise('Ext.Array.filter must have a filter function passed as second argument.'); + } + var results = [], + i = 0, + ln = array.length; + + for (; i < ln; i++) { + if (fn.call(scope, array[i], i, array)) { + results.push(array[i]); + } + } + + return results; + }, + + + findBy : function(array, fn, scope) { + var i = 0, + len = array.length; + + for (; i < len; i++) { + if (fn.call(scope || array, array[i], i)) { + return array[i]; + } + } + return null; + }, + + + from: function(value, newReference) { + if (value === undefined || value === null) { + return []; + } + + if (Ext.isArray(value)) { + return (newReference) ? slice.call(value) : value; + } + + var type = typeof value; + + + if (value && value.length !== undefined && type !== 'string' && (type !== 'function' || !value.apply)) { + return ExtArray.toArray(value); + } + + return [value]; + }, + + + remove: function(array, item) { + var index = ExtArray.indexOf(array, item); + + if (index !== -1) { + erase(array, index, 1); + } + + return array; + }, + + + include: function(array, item) { + if (!ExtArray.contains(array, item)) { + array.push(item); + } + }, + + + clone: function(array) { + return slice.call(array); + }, + + + merge: function() { + var args = slice.call(arguments), + array = [], + i, ln; + + for (i = 0, ln = args.length; i < ln; i++) { + array = array.concat(args[i]); + } + + return ExtArray.unique(array); + }, + + + intersect: function() { + var intersection = [], + arrays = slice.call(arguments), + arraysLength, + array, + arrayLength, + minArray, + minArrayIndex, + minArrayCandidate, + minArrayLength, + element, + elementCandidate, + elementCount, + i, j, k; + + if (!arrays.length) { + return intersection; + } + + + arraysLength = arrays.length; + for (i = minArrayIndex = 0; i < arraysLength; i++) { + minArrayCandidate = arrays[i]; + if (!minArray || minArrayCandidate.length < minArray.length) { + minArray = minArrayCandidate; + minArrayIndex = i; + } + } + + minArray = ExtArray.unique(minArray); + erase(arrays, minArrayIndex, 1); + + + + + minArrayLength = minArray.length; + arraysLength = arrays.length; + for (i = 0; i < minArrayLength; i++) { + element = minArray[i]; + elementCount = 0; + + for (j = 0; j < arraysLength; j++) { + array = arrays[j]; + arrayLength = array.length; + for (k = 0; k < arrayLength; k++) { + elementCandidate = array[k]; + if (element === elementCandidate) { + elementCount++; + break; + } + } + } + + if (elementCount === arraysLength) { + intersection.push(element); + } + } + + return intersection; + }, + + + difference: function(arrayA, arrayB) { + var clone = slice.call(arrayA), + ln = clone.length, + i, j, lnB; + + for (i = 0,lnB = arrayB.length; i < lnB; i++) { + for (j = 0; j < ln; j++) { + if (clone[j] === arrayB[i]) { + erase(clone, j, 1); + j--; + ln--; + } + } + } + + return clone; + }, + + + + slice: ([1,2].slice(1, undefined).length ? + function (array, begin, end) { + return slice.call(array, begin, end); + } : + + function (array, begin, end) { + + + if (typeof begin === 'undefined') { + return slice.call(array); + } + if (typeof end === 'undefined') { + return slice.call(array, begin); + } + return slice.call(array, begin, end); + } + ), + + + sort: supportsSort ? function(array, sortFn) { + if (sortFn) { + return array.sort(sortFn); + } else { + return array.sort(); + } + } : function(array, sortFn) { + var length = array.length, + i = 0, + comparison, + j, min, tmp; + + for (; i < length; i++) { + min = i; + for (j = i + 1; j < length; j++) { + if (sortFn) { + comparison = sortFn(array[j], array[min]); + if (comparison < 0) { + min = j; + } + } else if (array[j] < array[min]) { + min = j; + } + } + if (min !== i) { + tmp = array[i]; + array[i] = array[min]; + array[min] = tmp; + } + } + + return array; + }, + + + flatten: function(array) { + var worker = []; + + function rFlatten(a) { + var i, ln, v; + + for (i = 0, ln = a.length; i < ln; i++) { + v = a[i]; + + if (Ext.isArray(v)) { + rFlatten(v); + } else { + worker.push(v); + } + } + + return worker; + } + + return rFlatten(array); + }, + + + min: function(array, comparisonFn) { + var min = array[0], + i, ln, item; + + for (i = 0, ln = array.length; i < ln; i++) { + item = array[i]; + + if (comparisonFn) { + if (comparisonFn(min, item) === 1) { + min = item; + } + } + else { + if (item < min) { + min = item; + } + } + } + + return min; + }, + + + max: function(array, comparisonFn) { + var max = array[0], + i, ln, item; + + for (i = 0, ln = array.length; i < ln; i++) { + item = array[i]; + + if (comparisonFn) { + if (comparisonFn(max, item) === -1) { + max = item; + } + } + else { + if (item > max) { + max = item; + } + } + } + + return max; + }, + + + mean: function(array) { + return array.length > 0 ? ExtArray.sum(array) / array.length : undefined; + }, + + + sum: function(array) { + var sum = 0, + i, ln, item; + + for (i = 0,ln = array.length; i < ln; i++) { + item = array[i]; + + sum += item; + } + + return sum; + }, + + + toMap: function(array, getKey, scope) { + var map = {}, + i = array.length; + + if (!getKey) { + while (i--) { + map[array[i]] = i+1; + } + } else if (typeof getKey == 'string') { + while (i--) { + map[array[i][getKey]] = i+1; + } + } else { + while (i--) { + map[getKey.call(scope, array[i])] = i+1; + } + } + + return map; + }, + + + toValueMap: function(array, getKey, scope) { + var map = {}, + i = array.length; + + if (!getKey) { + while (i--) { + map[array[i]] = array[i]; + } + } else if (typeof getKey == 'string') { + while (i--) { + map[array[i][getKey]] = array[i]; + } + } else { + while (i--) { + map[getKey.call(scope, array[i])] = array[i]; + } + } + + return map; + }, + + _replaceSim: replaceSim, + _spliceSim: spliceSim, + + + erase: erase, + + + insert: function (array, index, items) { + return replace(array, index, 0, items); + }, + + + replace: replace, + + + splice: splice, + + + push: function(array) { + var len = arguments.length, + i = 1, + newItem; + + if (array === undefined) { + array = []; + } else if (!Ext.isArray(array)) { + array = [array]; + } + for (; i < len; i++) { + newItem = arguments[i]; + Array.prototype.push[Ext.isIterable(newItem) ? 'apply' : 'call'](array, newItem); + } + return array; + } + }; + + + Ext.each = ExtArray.each; + + + ExtArray.union = ExtArray.merge; + + + Ext.min = ExtArray.min; + + + Ext.max = ExtArray.max; + + + Ext.sum = ExtArray.sum; + + + Ext.mean = ExtArray.mean; + + + Ext.flatten = ExtArray.flatten; + + + Ext.clean = ExtArray.clean; + + + Ext.unique = ExtArray.unique; + + + Ext.pluck = ExtArray.pluck; + + + Ext.toArray = function() { + return ExtArray.toArray.apply(ExtArray, arguments); + }; +}()); + + + + + + + +Ext.Function = { + + + flexSetter: function(fn) { + return function(a, b) { + var k, i; + + if (a === null) { + return this; + } + + if (typeof a !== 'string') { + for (k in a) { + if (a.hasOwnProperty(k)) { + fn.call(this, k, a[k]); + } + } + + if (Ext.enumerables) { + for (i = Ext.enumerables.length; i--;) { + k = Ext.enumerables[i]; + if (a.hasOwnProperty(k)) { + fn.call(this, k, a[k]); + } + } + } + } else { + fn.call(this, a, b); + } + + return this; + }; + }, + + + bind: function(fn, scope, args, appendArgs) { + if (arguments.length === 2) { + return function() { + return fn.apply(scope, arguments); + }; + } + + var method = fn, + slice = Array.prototype.slice; + + return function() { + var callArgs = args || arguments; + + if (appendArgs === true) { + callArgs = slice.call(arguments, 0); + callArgs = callArgs.concat(args); + } + else if (typeof appendArgs == 'number') { + callArgs = slice.call(arguments, 0); + Ext.Array.insert(callArgs, appendArgs, args); + } + + return method.apply(scope || Ext.global, callArgs); + }; + }, + + + pass: function(fn, args, scope) { + if (!Ext.isArray(args)) { + if (Ext.isIterable(args)) { + args = Ext.Array.clone(args); + } else { + args = args !== undefined ? [args] : []; + } + } + + return function() { + var fnArgs = [].concat(args); + fnArgs.push.apply(fnArgs, arguments); + return fn.apply(scope || this, fnArgs); + }; + }, + + + alias: function(object, methodName) { + return function() { + return object[methodName].apply(object, arguments); + }; + }, + + + clone: function(method) { + return function() { + return method.apply(this, arguments); + }; + }, + + + createInterceptor: function(origFn, newFn, scope, returnValue) { + var method = origFn; + if (!Ext.isFunction(newFn)) { + return origFn; + } else { + returnValue = Ext.isDefined(returnValue) ? returnValue : null; + return function() { + var me = this, + args = arguments; + + newFn.target = me; + newFn.method = origFn; + return (newFn.apply(scope || me || Ext.global, args) !== false) ? origFn.apply(me || Ext.global, args) : returnValue; + }; + } + }, + + + createDelayed: function(fn, delay, scope, args, appendArgs) { + if (scope || args) { + fn = Ext.Function.bind(fn, scope, args, appendArgs); + } + + return function() { + var me = this, + args = Array.prototype.slice.call(arguments); + + setTimeout(function() { + fn.apply(me, args); + }, delay); + }; + }, + + + defer: function(fn, millis, scope, args, appendArgs) { + fn = Ext.Function.bind(fn, scope, args, appendArgs); + if (millis > 0) { + return setTimeout(Ext.supports.TimeoutActualLateness ? function () { + fn(); + } : fn, millis); + } + fn(); + return 0; + }, + + + createSequence: function(originalFn, newFn, scope) { + if (!newFn) { + return originalFn; + } + else { + return function() { + var result = originalFn.apply(this, arguments); + newFn.apply(scope || this, arguments); + return result; + }; + } + }, + + + createBuffered: function(fn, buffer, scope, args) { + var timerId; + + return function() { + var callArgs = args || Array.prototype.slice.call(arguments, 0), + me = scope || this; + + if (timerId) { + clearTimeout(timerId); + } + + timerId = setTimeout(function(){ + fn.apply(me, callArgs); + }, buffer); + }; + }, + + + createThrottled: function(fn, interval, scope) { + var lastCallTime, elapsed, lastArgs, timer, execute = function() { + fn.apply(scope || this, lastArgs); + lastCallTime = Ext.Date.now(); + }; + + return function() { + elapsed = Ext.Date.now() - lastCallTime; + lastArgs = arguments; + + clearTimeout(timer); + if (!lastCallTime || (elapsed >= interval)) { + execute(); + } else { + timer = setTimeout(execute, interval - elapsed); + } + }; + }, + + + + interceptBefore: function(object, methodName, fn, scope) { + var method = object[methodName] || Ext.emptyFn; + + return (object[methodName] = function() { + var ret = fn.apply(scope || this, arguments); + method.apply(this, arguments); + + return ret; + }); + }, + + + interceptAfter: function(object, methodName, fn, scope) { + var method = object[methodName] || Ext.emptyFn; + + return (object[methodName] = function() { + method.apply(this, arguments); + return fn.apply(scope || this, arguments); + }); + } +}; + + +Ext.defer = Ext.Function.alias(Ext.Function, 'defer'); + + +Ext.pass = Ext.Function.alias(Ext.Function, 'pass'); + + +Ext.bind = Ext.Function.alias(Ext.Function, 'bind'); + + + + + + + + +(function() { + + +var TemplateClass = function(){}, + ExtObject = Ext.Object = { + + + chain: Object.create || function (object) { + TemplateClass.prototype = object; + var result = new TemplateClass(); + TemplateClass.prototype = null; + return result; + }, + + + clear: function (object) { + var keys = ExtObject.getKeys(object), + n = keys.length; + + while (n--) { + delete object[keys[n]]; + } + + return object; + }, + + + toQueryObjects: function(name, value, recursive) { + var self = ExtObject.toQueryObjects, + objects = [], + i, ln; + + if (Ext.isArray(value)) { + for (i = 0, ln = value.length; i < ln; i++) { + if (recursive) { + objects = objects.concat(self(name + '[' + i + ']', value[i], true)); + } + else { + objects.push({ + name: name, + value: value[i] + }); + } + } + } + else if (Ext.isObject(value)) { + for (i in value) { + if (value.hasOwnProperty(i)) { + if (recursive) { + objects = objects.concat(self(name + '[' + i + ']', value[i], true)); + } + else { + objects.push({ + name: name, + value: value[i] + }); + } + } + } + } + else { + objects.push({ + name: name, + value: value + }); + } + + return objects; + }, + + + toQueryString: function(object, recursive) { + var paramObjects = [], + params = [], + i, j, ln, paramObject, value; + + for (i in object) { + if (object.hasOwnProperty(i)) { + paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive)); + } + } + + for (j = 0, ln = paramObjects.length; j < ln; j++) { + paramObject = paramObjects[j]; + value = paramObject.value; + + if (Ext.isEmpty(value)) { + value = ''; + } else if (Ext.isDate(value)) { + value = Ext.Date.toString(value); + } + + params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value))); + } + + return params.join('&'); + }, + + + fromQueryString: function(queryString, recursive) { + var parts = queryString.replace(/^\?/, '').split('&'), + object = {}, + temp, components, name, value, i, ln, + part, j, subLn, matchedKeys, matchedName, + keys, key, nextKey; + + for (i = 0, ln = parts.length; i < ln; i++) { + part = parts[i]; + + if (part.length > 0) { + components = part.split('='); + name = decodeURIComponent(components[0]); + value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : ''; + + if (!recursive) { + if (object.hasOwnProperty(name)) { + if (!Ext.isArray(object[name])) { + object[name] = [object[name]]; + } + + object[name].push(value); + } + else { + object[name] = value; + } + } + else { + matchedKeys = name.match(/(\[):?([^\]]*)\]/g); + matchedName = name.match(/^([^\[]+)/); + + if (!matchedName) { + throw new Error('[Ext.Object.fromQueryString] Malformed query string given, failed parsing name from "' + part + '"'); + } + + name = matchedName[0]; + keys = []; + + if (matchedKeys === null) { + object[name] = value; + continue; + } + + for (j = 0, subLn = matchedKeys.length; j < subLn; j++) { + key = matchedKeys[j]; + key = (key.length === 2) ? '' : key.substring(1, key.length - 1); + keys.push(key); + } + + keys.unshift(name); + + temp = object; + + for (j = 0, subLn = keys.length; j < subLn; j++) { + key = keys[j]; + + if (j === subLn - 1) { + if (Ext.isArray(temp) && key === '') { + temp.push(value); + } + else { + temp[key] = value; + } + } + else { + if (temp[key] === undefined || typeof temp[key] === 'string') { + nextKey = keys[j+1]; + + temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {}; + } + + temp = temp[key]; + } + } + } + } + } + + return object; + }, + + + each: function(object, fn, scope) { + for (var property in object) { + if (object.hasOwnProperty(property)) { + if (fn.call(scope || object, property, object[property], object) === false) { + return; + } + } + } + }, + + + merge: function(destination) { + var i = 1, + ln = arguments.length, + mergeFn = ExtObject.merge, + cloneFn = Ext.clone, + object, key, value, sourceKey; + + for (; i < ln; i++) { + object = arguments[i]; + + for (key in object) { + value = object[key]; + if (value && value.constructor === Object) { + sourceKey = destination[key]; + if (sourceKey && sourceKey.constructor === Object) { + mergeFn(sourceKey, value); + } + else { + destination[key] = cloneFn(value); + } + } + else { + destination[key] = value; + } + } + } + + return destination; + }, + + + mergeIf: function(destination) { + var i = 1, + ln = arguments.length, + cloneFn = Ext.clone, + object, key, value; + + for (; i < ln; i++) { + object = arguments[i]; + + for (key in object) { + if (!(key in destination)) { + value = object[key]; + + if (value && value.constructor === Object) { + destination[key] = cloneFn(value); + } + else { + destination[key] = value; + } + } + } + } + + return destination; + }, + + + getKey: function(object, value) { + for (var property in object) { + if (object.hasOwnProperty(property) && object[property] === value) { + return property; + } + } + + return null; + }, + + + getValues: function(object) { + var values = [], + property; + + for (property in object) { + if (object.hasOwnProperty(property)) { + values.push(object[property]); + } + } + + return values; + }, + + + getKeys: (typeof Object.keys == 'function') + ? function(object){ + if (!object) { + return []; + } + return Object.keys(object); + } + : function(object) { + var keys = [], + property; + + for (property in object) { + if (object.hasOwnProperty(property)) { + keys.push(property); + } + } + + return keys; + }, + + + getSize: function(object) { + var size = 0, + property; + + for (property in object) { + if (object.hasOwnProperty(property)) { + size++; + } + } + + return size; + }, + + + isEmpty: function(object){ + for (var key in object) { + if (object.hasOwnProperty(key)) { + return false; + } + } + return true; + }, + + + equals: (function() { + var check = function(o1, o2) { + var key; + + for (key in o1) { + if (o1.hasOwnProperty(key)) { + if (o1[key] !== o2[key]) { + return false; + } + } + } + return true; + }; + + return function(object1, object2) { + + + if (object1 === object2) { + return true; + } if (object1 && object2) { + + + return check(object1, object2) && check(object2, object1); + } else if (!object1 && !object2) { + return object1 === object2; + } else { + return false; + } + }; + })(), + + + classify: function(object) { + var prototype = object, + objectProperties = [], + propertyClassesMap = {}, + objectClass = function() { + var i = 0, + ln = objectProperties.length, + property; + + for (; i < ln; i++) { + property = objectProperties[i]; + this[property] = new propertyClassesMap[property](); + } + }, + key, value; + + for (key in object) { + if (object.hasOwnProperty(key)) { + value = object[key]; + + if (value && value.constructor === Object) { + objectProperties.push(key); + propertyClassesMap[key] = ExtObject.classify(value); + } + } + } + + objectClass.prototype = prototype; + + return objectClass; + } +}; + + +Ext.merge = Ext.Object.merge; + + +Ext.mergeIf = Ext.Object.mergeIf; + + +Ext.urlEncode = function() { + var args = Ext.Array.from(arguments), + prefix = ''; + + + if ((typeof args[1] === 'string')) { + prefix = args[1] + '&'; + args[1] = false; + } + + return prefix + ExtObject.toQueryString.apply(ExtObject, args); +}; + + +Ext.urlDecode = function() { + return ExtObject.fromQueryString.apply(ExtObject, arguments); +}; + +}()); + + + + + + + + + + +Ext.Date = new function() { + var utilDate = this, + stripEscapeRe = /(\\.)/g, + hourInfoRe = /([gGhHisucUOPZ]|MS)/, + dateInfoRe = /([djzmnYycU]|MS)/, + slashRe = /\\/gi, + numberTokenRe = /\{(\d+)\}/g, + MSFormatRe = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/'), + code = [ + + "var me = this, dt, y, m, d, h, i, s, ms, o, O, z, zz, u, v, W, year, jan4, week1monday, daysInMonth, dayMatched,", + "def = me.defaults,", + "from = Ext.Number.from,", + "results = String(input).match(me.parseRegexes[{0}]);", + + "if(results){", + "{1}", + + "if(u != null){", + "v = new Date(u * 1000);", + "}else{", + + + + "dt = me.clearTime(new Date);", + + "y = from(y, from(def.y, dt.getFullYear()));", + "m = from(m, from(def.m - 1, dt.getMonth()));", + "dayMatched = d !== undefined;", + "d = from(d, from(def.d, dt.getDate()));", + + + + + + + + + "if (!dayMatched) {", + "dt.setDate(1);", + "dt.setMonth(m);", + "dt.setFullYear(y);", + + "daysInMonth = me.getDaysInMonth(dt);", + "if (d > 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;" + ].join('\n'); + + + + + function xf(format) { + var args = Array.prototype.slice.call(arguments, 1); + return format.replace(numberTokenRe, function(m, i) { + return args[i]; + }); + } + + Ext.apply(utilDate, { + + now: Date.now || function() { + return +new Date(); + }, + + + toString: function(date) { + var pad = Ext.String.leftPad; + + return date.getFullYear() + "-" + + pad(date.getMonth() + 1, 2, '0') + "-" + + pad(date.getDate(), 2, '0') + "T" + + pad(date.getHours(), 2, '0') + ":" + + pad(date.getMinutes(), 2, '0') + ":" + + pad(date.getSeconds(), 2, '0'); + }, + + + getElapsed: function(dateA, dateB) { + return Math.abs(dateA - (dateB || utilDate.now())); + }, + + + useStrict: false, + + + formatCodeToRegex: function(character, currentGroup) { + + var p = utilDate.parseCodes[character]; + + if (p) { + p = typeof p == 'function'? p() : p; + utilDate.parseCodes[character] = p; + } + + return p ? Ext.applyIf({ + c: p.c ? xf(p.c, currentGroup || "{0}") : p.c + }, p) : { + g: 0, + c: null, + s: Ext.String.escapeRegex(character) + }; + }, + + + parseFunctions: { + "MS": function(input, strict) { + + + var r = (input || '').match(MSFormatRe); + return r ? new Date(((r[1] || '') + r[2]) * 1) : null; + }, + "time": function(input, strict) { + var num = parseInt(input, 10); + if (num || num === 0) { + return new Date(num); + } + return null; + }, + "timestamp": function(input, strict) { + var num = parseInt(input, 10); + if (num || num === 0) { + return new Date(num * 1000); + } + return null; + } + }, + parseRegexes: [], + + + formatFunctions: { + "MS": function() { + + return '\\/Date(' + this.getTime() + ')\\/'; + }, + "time": function(){ + return this.getTime().toString(); + }, + "timestamp": function(){ + return utilDate.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(month) { + return Ext.Date.monthNames[month].substring(0, 3); + }, + + + + + getShortDayName : function(day) { + return Ext.Date.dayNames[day].substring(0, 3); + }, + + + + + getMonthNumber : function(name) { + + return Ext.Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()]; + }, + + + + formatContainsHourInfo : function(format){ + return hourInfoRe.test(format.replace(stripEscapeRe, '')); + }, + + + formatContainsDateInfo : function(format){ + return dateInfoRe.test(format.replace(stripEscapeRe, '')); + }, + + + unescapeFormat: function(format) { + + + + return format.replace(slashRe, ''); + }, + + + formatCodes : { + d: "Ext.String.leftPad(this.getDate(), 2, '0')", + D: "Ext.Date.getShortDayName(this.getDay())", + j: "this.getDate()", + l: "Ext.Date.dayNames[this.getDay()]", + N: "(this.getDay() ? this.getDay() : 7)", + S: "Ext.Date.getSuffix(this)", + w: "this.getDay()", + z: "Ext.Date.getDayOfYear(this)", + W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')", + F: "Ext.Date.monthNames[this.getMonth()]", + m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')", + M: "Ext.Date.getShortMonthName(this.getMonth())", + n: "(this.getMonth() + 1)", + t: "Ext.Date.getDaysInMonth(this)", + L: "(Ext.Date.isLeapYear(this) ? 1 : 0)", + o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))", + Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')", + y: "('' + this.getFullYear()).substring(2, 4)", + a: "(this.getHours() < 12 ? 'am' : 'pm')", + A: "(this.getHours() < 12 ? 'AM' : 'PM')", + g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)", + G: "this.getHours()", + h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')", + H: "Ext.String.leftPad(this.getHours(), 2, '0')", + i: "Ext.String.leftPad(this.getMinutes(), 2, '0')", + s: "Ext.String.leftPad(this.getSeconds(), 2, '0')", + u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')", + O: "Ext.Date.getGMTOffset(this)", + P: "Ext.Date.getGMTOffset(this, true)", + T: "Ext.Date.getTimezone(this)", + Z: "(this.getTimezoneOffset() * -60)", + + c: function() { + var c, code, i, l, e; + for (c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) { + e = c.charAt(i); + code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); + } + return code.join(" + "); + }, + + + U: "Math.round(this.getTime() / 1000)" + }, + + + isValid : function(y, m, d, h, i, s, ms) { + + h = h || 0; + i = i || 0; + s = s || 0; + ms = ms || 0; + + + var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0); + + return y == dt.getFullYear() && + m == dt.getMonth() + 1 && + d == dt.getDate() && + h == dt.getHours() && + i == dt.getMinutes() && + s == dt.getSeconds() && + ms == dt.getMilliseconds(); + }, + + + parse : function(input, format, strict) { + var p = utilDate.parseFunctions; + if (p[format] == null) { + utilDate.createParser(format); + } + return p[format].call(utilDate, input, Ext.isDefined(strict) ? strict : utilDate.useStrict); + }, + + + parseDate: function(input, format, strict){ + return utilDate.parse(input, format, strict); + }, + + + + getFormatCode : function(character) { + var f = utilDate.formatCodes[character]; + + if (f) { + f = typeof f == 'function'? f() : f; + utilDate.formatCodes[character] = f; + } + + + return f || ("'" + Ext.String.escape(character) + "'"); + }, + + + createFormat : function(format) { + var code = [], + special = false, + ch = '', + i; + + for (i = 0; i < format.length; ++i) { + ch = format.charAt(i); + if (!special && ch == "\\") { + special = true; + } else if (special) { + special = false; + code.push("'" + Ext.String.escape(ch) + "'"); + } else { + code.push(utilDate.getFormatCode(ch)); + } + } + utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+')); + }, + + + createParser : function(format) { + var regexNum = utilDate.parseRegexes.length, + currentGroup = 1, + calc = [], + regex = [], + special = false, + ch = "", + i = 0, + len = format.length, + atEnd = [], + obj; + + for (; i < len; ++i) { + ch = format.charAt(i); + if (!special && ch == "\\") { + special = true; + } else if (special) { + special = false; + regex.push(Ext.String.escape(ch)); + } else { + obj = utilDate.formatCodeToRegex(ch, currentGroup); + currentGroup += obj.g; + regex.push(obj.s); + if (obj.g && obj.c) { + if (obj.calcAtEnd) { + atEnd.push(obj.c); + } else { + calc.push(obj.c); + } + } + } + } + + calc = calc.concat(atEnd); + + utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i'); + utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join(''))); + }, + + + parseCodes : { + + d: { + g:1, + c:"d = parseInt(results[{0}], 10);\n", + s:"(3[0-1]|[1-2][0-9]|0[1-9])" + }, + j: { + g:1, + c:"d = parseInt(results[{0}], 10);\n", + s:"(3[0-1]|[1-2][0-9]|[1-9])" + }, + D: function() { + for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); + return { + g:0, + c:null, + s:"(?:" + a.join("|") +")" + }; + }, + l: function() { + return { + g:0, + c:null, + s:"(?:" + utilDate.dayNames.join("|") + ")" + }; + }, + N: { + g:0, + c:null, + s:"[1-7]" + }, + + S: { + g:0, + c:null, + s:"(?:st|nd|rd|th)" + }, + + w: { + g:0, + c:null, + s:"[0-6]" + }, + z: { + g:1, + c:"z = parseInt(results[{0}], 10);\n", + s:"(\\d{1,3})" + }, + W: { + g:1, + c:"W = parseInt(results[{0}], 10);\n", + s:"(\\d{2})" + }, + F: function() { + return { + g:1, + c:"m = parseInt(me.getMonthNumber(results[{0}]), 10);\n", + s:"(" + utilDate.monthNames.join("|") + ")" + }; + }, + M: function() { + for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); + return Ext.applyIf({ + s:"(" + a.join("|") + ")" + }, utilDate.formatCodeToRegex("F")); + }, + m: { + g:1, + c:"m = parseInt(results[{0}], 10) - 1;\n", + s:"(1[0-2]|0[1-9])" + }, + n: { + g:1, + c:"m = parseInt(results[{0}], 10) - 1;\n", + s:"(1[0-2]|[1-9])" + }, + t: { + g:0, + c:null, + s:"(?:\\d{2})" + }, + L: { + g:0, + c:null, + s:"(?:1|0)" + }, + o: { + g: 1, + c: "y = parseInt(results[{0}], 10);\n", + s: "(\\d{4})" + + }, + Y: { + g:1, + c:"y = parseInt(results[{0}], 10);\n", + s:"(\\d{4})" + }, + y: { + g:1, + c:"var ty = parseInt(results[{0}], 10);\n" + + "y = ty > me.y2kYear ? 1900 + ty : 2000 + ty;\n", + s:"(\\d{1,2})" + }, + + + a: { + g:1, + c:"if (/(am)/i.test(results[{0}])) {\n" + + "if (!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}])) {\n" + + "if (!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;\n" + + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n", + s:"([+-]?\\d{1,5})" + }, + c: function() { + var calc = [], + arr = [ + utilDate.formatCodeToRegex("Y", 1), + utilDate.formatCodeToRegex("m", 2), + utilDate.formatCodeToRegex("d", 3), + utilDate.formatCodeToRegex("H", 4), + utilDate.formatCodeToRegex("i", 5), + utilDate.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){", + utilDate.formatCodeToRegex("P", 8).c, + "}else{", + utilDate.formatCodeToRegex("O", 8).c, + "}", + "}" + ].join('\n')} + ], + i, + l; + + for (i = 0, l = arr.length; i < l; ++i) { + calc.push(arr[i].c); + } + + return { + g:1, + c:calc.join(""), + s:[ + arr[0].s, + "(?:", "-", arr[1].s, + "(?:", "-", arr[2].s, + "(?:", + "(?:T| )?", + arr[3].s, ":", arr[4].s, + "(?::", arr[5].s, ")?", + "(?:(?:\\.|,)(\\d+))?", + "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", + ")?", + ")?", + ")?" + ].join("") + }; + }, + U: { + g:1, + c:"u = parseInt(results[{0}], 10);\n", + s:"(-?\\d+)" + } + }, + + + + dateFormat: function(date, format) { + return utilDate.format(date, format); + }, + + + isEqual: function(date1, date2) { + + if (date1 && date2) { + return (date1.getTime() === date2.getTime()); + } + + return !(date1 || date2); + }, + + + format: function(date, format) { + var formatFunctions = utilDate.formatFunctions; + + if (!Ext.isDate(date)) { + return ''; + } + + if (formatFunctions[format] == null) { + utilDate.createFormat(format); + } + + return formatFunctions[format].call(date) + ''; + }, + + + getTimezone : function(date) { + + + + + + + + + + + + + return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,5})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, ""); + }, + + + getGMTOffset : function(date, colon) { + var offset = date.getTimezoneOffset(); + return (offset > 0 ? "-" : "+") + + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0") + + (colon ? ":" : "") + + Ext.String.leftPad(Math.abs(offset % 60), 2, "0"); + }, + + + getDayOfYear: function(date) { + var num = 0, + d = Ext.Date.clone(date), + m = date.getMonth(), + i; + + for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) { + num += utilDate.getDaysInMonth(d); + } + return num + date.getDate() - 1; + }, + + + getWeekOfYear : (function() { + + var ms1d = 864e5, + ms7d = 7 * ms1d; + + return function(date) { + var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, + AWN = Math.floor(DC3 / 7), + Wyr = new Date(AWN * ms7d).getUTCFullYear(); + + return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1; + }; + }()), + + + isLeapYear : function(date) { + var year = date.getFullYear(); + return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year))); + }, + + + getFirstDayOfMonth : function(date) { + var day = (date.getDay() - (date.getDate() - 1)) % 7; + return (day < 0) ? (day + 7) : day; + }, + + + getLastDayOfMonth : function(date) { + return utilDate.getLastDateOfMonth(date).getDay(); + }, + + + + getFirstDateOfMonth : function(date) { + return new Date(date.getFullYear(), date.getMonth(), 1); + }, + + + getLastDateOfMonth : function(date) { + return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date)); + }, + + + getDaysInMonth: (function() { + var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + return function(date) { + var m = date.getMonth(); + + return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m]; + }; + }()), + + + + getSuffix : function(date) { + switch (date.getDate()) { + case 1: + case 21: + case 31: + return "st"; + case 2: + case 22: + return "nd"; + case 3: + case 23: + return "rd"; + default: + return "th"; + } + }, + + + + clone : function(date) { + return new Date(date.getTime()); + }, + + + isDST : function(date) { + + + return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset(); + }, + + + clearTime : function(date, clone) { + if (clone) { + return Ext.Date.clearTime(Ext.Date.clone(date)); + } + + + var d = date.getDate(), + hr, + c; + + + date.setHours(0); + date.setMinutes(0); + date.setSeconds(0); + date.setMilliseconds(0); + + if (date.getDate() != d) { + + + + + for (hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr)); + + date.setDate(d); + date.setHours(c.getHours()); + } + + return date; + }, + + + add : function(date, interval, value) { + var d = Ext.Date.clone(date), + Date = Ext.Date, + day, decimalValue, base = 0; + if (!interval || value === 0) { + return d; + } + + decimalValue = value - parseInt(value, 10); + value = parseInt(value, 10); + + if (value) { + switch(interval.toLowerCase()) { + + + + + + + + + + + + + + + + + + + case Ext.Date.MILLI: + d.setTime(d.getTime() + value); + break; + case Ext.Date.SECOND: + d.setTime(d.getTime() + value * 1000); + break; + case Ext.Date.MINUTE: + d.setTime(d.getTime() + value * 60 * 1000); + break; + case Ext.Date.HOUR: + d.setTime(d.getTime() + value * 60 * 60 * 1000); + break; + case Ext.Date.DAY: + d.setDate(d.getDate() + value); + break; + case Ext.Date.MONTH: + day = date.getDate(); + if (day > 28) { + day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), Ext.Date.MONTH, value)).getDate()); + } + d.setDate(day); + d.setMonth(date.getMonth() + value); + break; + case Ext.Date.YEAR: + day = date.getDate(); + if (day > 28) { + day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), Ext.Date.YEAR, value)).getDate()); + } + d.setDate(day); + d.setFullYear(date.getFullYear() + value); + break; + } + } + + if (decimalValue) { + switch (interval.toLowerCase()) { + case Ext.Date.MILLI: base = 1; break; + case Ext.Date.SECOND: base = 1000; break; + case Ext.Date.MINUTE: base = 1000*60; break; + case Ext.Date.HOUR: base = 1000*60*60; break; + case Ext.Date.DAY: base = 1000*60*60*24; break; + + case Ext.Date.MONTH: + day = utilDate.getDaysInMonth(d); + base = 1000*60*60*24*day; + break; + + case Ext.Date.YEAR: + day = (utilDate.isLeapYear(d) ? 366 : 365); + base = 1000*60*60*24*day; + break; + } + if (base) { + d.setTime(d.getTime() + base * decimalValue); + } + } + + return d; + }, + + + subtract: function(date, interval, value){ + return utilDate.add(date, interval, -value); + }, + + + between : function(date, start, end) { + var t = date.getTime(); + return start.getTime() <= t && t <= end.getTime(); + }, + + + compat: function() { + var nativeDate = window.Date, + p, + statics = ['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'], + proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'], + sLen = statics.length, + pLen = proto.length, + stat, prot, s; + + + for (s = 0; s < sLen; s++) { + stat = statics[s]; + nativeDate[stat] = utilDate[stat]; + } + + + for (p = 0; p < pLen; p++) { + prot = proto[p]; + nativeDate.prototype[prot] = function() { + var args = Array.prototype.slice.call(arguments); + args.unshift(this); + return utilDate[prot].apply(utilDate, args); + }; + } + } + }); +}; + + + + + + + +(function(flexSetter) { + +var noArgs = [], + Base = function(){}, + hookFunctionFactory = function(hookFunction, underriddenFunction, methodName, owningClass) { + var result = function() { + var result = this.callParent(arguments); + hookFunction.apply(this, arguments); + return result; + }; + result.$name = methodName; + result.$owner = owningClass; + if (underriddenFunction) { + result.$previous = underriddenFunction.$previous; + underriddenFunction.$previous = result; + } + return result; + }; + + + Ext.apply(Base, { + $className: 'Ext.Base', + + $isClass: true, + + + create: function() { + return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0))); + }, + + + extend: function(parent) { + var parentPrototype = parent.prototype, + basePrototype, prototype, i, ln, name, statics; + + prototype = this.prototype = Ext.Object.chain(parentPrototype); + prototype.self = this; + + this.superclass = prototype.superclass = parentPrototype; + + if (!parent.$isClass) { + basePrototype = Ext.Base.prototype; + + for (i in basePrototype) { + if (i in prototype) { + prototype[i] = basePrototype[i]; + } + } + } + + + statics = parentPrototype.$inheritableStatics; + + if (statics) { + for (i = 0,ln = statics.length; i < ln; i++) { + name = statics[i]; + + if (!this.hasOwnProperty(name)) { + this[name] = parent[name]; + } + } + } + + if (parent.$onExtended) { + this.$onExtended = parent.$onExtended.slice(); + } + + prototype.config = new prototype.configClass(); + prototype.initConfigList = prototype.initConfigList.slice(); + prototype.initConfigMap = Ext.clone(prototype.initConfigMap); + prototype.configMap = Ext.Object.chain(prototype.configMap); + }, + + + $onExtended: [], + + + triggerExtended: function() { + Ext.classSystemMonitor && Ext.classSystemMonitor(this, 'Ext.Base#triggerExtended', arguments); + + var callbacks = this.$onExtended, + ln = callbacks.length, + i, callback; + + if (ln > 0) { + for (i = 0; i < ln; i++) { + callback = callbacks[i]; + callback.fn.apply(callback.scope || this, arguments); + } + } + }, + + + onExtended: function(fn, scope) { + this.$onExtended.push({ + fn: fn, + scope: scope + }); + + return this; + }, + + + addConfig: function(config, fullMerge) { + var prototype = this.prototype, + configNameCache = Ext.Class.configNameCache, + hasConfig = prototype.configMap, + initConfigList = prototype.initConfigList, + initConfigMap = prototype.initConfigMap, + defaultConfig = prototype.config, + initializedName, name, value; + + for (name in config) { + if (config.hasOwnProperty(name)) { + if (!hasConfig[name]) { + hasConfig[name] = true; + } + + value = config[name]; + + initializedName = configNameCache[name].initialized; + + if (!initConfigMap[name] && value !== null && !prototype[initializedName]) { + initConfigMap[name] = true; + initConfigList.push(name); + } + } + } + + if (fullMerge) { + Ext.merge(defaultConfig, config); + } + else { + Ext.mergeIf(defaultConfig, config); + } + + prototype.configClass = Ext.Object.classify(defaultConfig); + }, + + + addStatics: function(members) { + var member, name; + + for (name in members) { + if (members.hasOwnProperty(name)) { + member = members[name]; + if (typeof member == 'function' && !member.$isClass && member !== Ext.emptyFn && member !== Ext.identityFn) { + member.$owner = this; + member.$name = name; + member.displayName = Ext.getClassName(this) + '.' + name; + } + this[name] = member; + } + } + + return this; + }, + + + addInheritableStatics: function(members) { + var inheritableStatics, + hasInheritableStatics, + prototype = this.prototype, + name, member; + + inheritableStatics = prototype.$inheritableStatics; + hasInheritableStatics = prototype.$hasInheritableStatics; + + if (!inheritableStatics) { + inheritableStatics = prototype.$inheritableStatics = []; + hasInheritableStatics = prototype.$hasInheritableStatics = {}; + } + + for (name in members) { + if (members.hasOwnProperty(name)) { + member = members[name]; + if (typeof member == 'function') { + member.displayName = Ext.getClassName(this) + '.' + name; + } + this[name] = member; + + if (!hasInheritableStatics[name]) { + hasInheritableStatics[name] = true; + inheritableStatics.push(name); + } + } + } + + return this; + }, + + + addMembers: function(members) { + var prototype = this.prototype, + enumerables = Ext.enumerables, + names = [], + i, ln, name, member; + + for (name in members) { + names.push(name); + } + + if (enumerables) { + names.push.apply(names, enumerables); + } + + for (i = 0,ln = names.length; i < ln; i++) { + name = names[i]; + + if (members.hasOwnProperty(name)) { + member = members[name]; + + if (typeof member == 'function' && !member.$isClass && member !== Ext.emptyFn && member !== Ext.identityFn) { + member.$owner = this; + member.$name = name; + member.displayName = (this.$className || '') + '#' + name; + } + + prototype[name] = member; + } + } + + return this; + }, + + + addMember: function(name, member) { + if (typeof member == 'function' && !member.$isClass && member !== Ext.emptyFn && member !== Ext.identityFn) { + member.$owner = this; + member.$name = name; + member.displayName = (this.$className || '') + '#' + name; + } + + this.prototype[name] = member; + return this; + }, + + + implement: function() { + this.addMembers.apply(this, arguments); + }, + + + borrow: function(fromClass, members) { + Ext.classSystemMonitor && Ext.classSystemMonitor(this, 'Ext.Base#borrow', arguments); + + var prototype = this.prototype, + fromPrototype = fromClass.prototype, + className = Ext.getClassName(this), + i, ln, name, fn, toBorrow; + + members = Ext.Array.from(members); + + for (i = 0,ln = members.length; i < ln; i++) { + name = members[i]; + + toBorrow = fromPrototype[name]; + + if (typeof toBorrow == 'function') { + fn = Ext.Function.clone(toBorrow); + + if (className) { + fn.displayName = className + '#' + name; + } + + fn.$owner = this; + fn.$name = name; + + prototype[name] = fn; + } + else { + prototype[name] = toBorrow; + } + } + + return this; + }, + + + override: function(members) { + var me = this, + enumerables = Ext.enumerables, + target = me.prototype, + cloneFunction = Ext.Function.clone, + name, index, member, statics, names, previous; + + if (arguments.length === 2) { + name = members; + members = {}; + members[name] = arguments[1]; + enumerables = null; + } + + do { + names = []; + statics = null; + + for (name in members) { + if (name == 'statics') { + statics = members[name]; + } else if (name == 'inheritableStatics'){ + me.addInheritableStatics(members[name]); + } else if (name == 'config') { + me.addConfig(members[name], true); + } else { + names.push(name); + } + } + + if (enumerables) { + names.push.apply(names, enumerables); + } + + for (index = names.length; index--; ) { + name = names[index]; + + if (members.hasOwnProperty(name)) { + member = members[name]; + + if (typeof member == 'function' && !member.$className && member !== Ext.emptyFn && member !== Ext.identityFn) { + if (typeof member.$owner != 'undefined') { + member = cloneFunction(member); + } + + if (me.$className) { + member.displayName = me.$className + '#' + name; + } + + member.$owner = me; + member.$name = name; + + previous = target.hasOwnProperty(name) && target[name]; + if (previous) { + member.$previous = previous; + } + } + + target[name] = member; + } + } + + target = me; + members = statics; + } while (members); + + return this; + }, + + + callParent: function(args) { + var method; + + + return (method = this.callParent.caller) && (method.$previous || + ((method = method.$owner ? method : method.caller) && + method.$owner.superclass.self[method.$name])).apply(this, args || noArgs); + }, + + + callSuper: function(args) { + var method; + + + return (method = this.callSuper.caller) && + ((method = method.$owner ? method : method.caller) && + method.$owner.superclass.self[method.$name]).apply(this, args || noArgs); + }, + + + mixin: function(name, mixinClass) { + var me = this, + mixin = mixinClass.prototype, + prototype = me.prototype, + key, statics, i, ln, staticName, + mixinValue, hookKey, hookFunction; + + if (typeof mixin.onClassMixedIn != 'undefined') { + mixin.onClassMixedIn.call(mixinClass, me); + } + + if (!prototype.hasOwnProperty('mixins')) { + if ('mixins' in prototype) { + prototype.mixins = Ext.Object.chain(prototype.mixins); + } + else { + prototype.mixins = {}; + } + } + + for (key in mixin) { + mixinValue = mixin[key]; + if (key === 'mixins') { + Ext.merge(prototype.mixins, mixinValue); + } + else if (key === 'xhooks') { + for (hookKey in mixinValue) { + hookFunction = mixinValue[hookKey]; + + + hookFunction.$previous = Ext.emptyFn; + + if (prototype.hasOwnProperty(hookKey)) { + + + + + hookFunctionFactory(hookFunction, prototype[hookKey], hookKey, me); + } else { + + + prototype[hookKey] = hookFunctionFactory(hookFunction, null, hookKey, me); + } + } + } + else if (!(key === 'mixinId' || key === 'config') && (prototype[key] === undefined)) { + prototype[key] = mixinValue; + } + } + + + statics = mixin.$inheritableStatics; + + if (statics) { + for (i = 0, ln = statics.length; i < ln; i++) { + staticName = statics[i]; + + if (!me.hasOwnProperty(staticName)) { + me[staticName] = mixinClass[staticName]; + } + } + } + + if ('config' in mixin) { + me.addConfig(mixin.config, false); + } + + prototype.mixins[name] = mixin; + return me; + }, + + + getName: function() { + return Ext.getClassName(this); + }, + + + createAlias: flexSetter(function(alias, origin) { + this.override(alias, function() { + return this[origin].apply(this, arguments); + }); + }), + + + addXtype: function(xtype) { + var prototype = this.prototype, + xtypesMap = prototype.xtypesMap, + xtypes = prototype.xtypes, + xtypesChain = prototype.xtypesChain; + + if (!prototype.hasOwnProperty('xtypesMap')) { + xtypesMap = prototype.xtypesMap = Ext.merge({}, prototype.xtypesMap || {}); + xtypes = prototype.xtypes = prototype.xtypes ? [].concat(prototype.xtypes) : []; + xtypesChain = prototype.xtypesChain = prototype.xtypesChain ? [].concat(prototype.xtypesChain) : []; + prototype.xtype = xtype; + } + + if (!xtypesMap[xtype]) { + xtypesMap[xtype] = true; + xtypes.push(xtype); + xtypesChain.push(xtype); + Ext.ClassManager.setAlias(this, 'widget.' + xtype); + } + + return this; + } + }); + + Base.implement({ + + isInstance: true, + + + $className: 'Ext.Base', + + + configClass: Ext.emptyFn, + + + initConfigList: [], + + + configMap: {}, + + + initConfigMap: {}, + + + statics: function() { + var method = this.statics.caller, + self = this.self; + + if (!method) { + return self; + } + + return method.$owner; + }, + + + callParent: function(args) { + + + + + var method, + superMethod = (method = this.callParent.caller) && (method.$previous || + ((method = method.$owner ? method : method.caller) && + method.$owner.superclass[method.$name])); + + if (!superMethod) { + method = this.callParent.caller; + var parentClass, methodName; + + if (!method.$owner) { + if (!method.caller) { + throw new Error("Attempting to call a protected method from the public scope, which is not allowed"); + } + + method = method.caller; + } + + parentClass = method.$owner.superclass; + methodName = method.$name; + + if (!(methodName in parentClass)) { + throw new Error("this.callParent() was called but there's no such method (" + methodName + + ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")"); + } + } + + return superMethod.apply(this, args || noArgs); + }, + + + callSuper: function(args) { + + + + + var method, + superMethod = (method = this.callSuper.caller) && + ((method = method.$owner ? method : method.caller) && + method.$owner.superclass[method.$name]); + + if (!superMethod) { + method = this.callSuper.caller; + var parentClass, methodName; + + if (!method.$owner) { + if (!method.caller) { + throw new Error("Attempting to call a protected method from the public scope, which is not allowed"); + } + + method = method.caller; + } + + parentClass = method.$owner.superclass; + methodName = method.$name; + + if (!(methodName in parentClass)) { + throw new Error("this.callSuper() was called but there's no such method (" + methodName + + ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")"); + } + } + + return superMethod.apply(this, args || noArgs); + }, + + + self: Base, + + + constructor: function() { + return this; + }, + + + initConfig: function(config) { + var instanceConfig = config, + configNameCache = Ext.Class.configNameCache, + defaultConfig = new this.configClass(), + defaultConfigList = this.initConfigList, + hasConfig = this.configMap, + nameMap, i, ln, name, initializedName; + + this.initConfig = Ext.emptyFn; + + this.initialConfig = instanceConfig || {}; + + this.config = config = (instanceConfig) ? Ext.merge(defaultConfig, config) : defaultConfig; + + if (instanceConfig) { + defaultConfigList = defaultConfigList.slice(); + + for (name in instanceConfig) { + if (hasConfig[name]) { + if (instanceConfig[name] !== null) { + defaultConfigList.push(name); + this[configNameCache[name].initialized] = false; + } + } + } + } + + for (i = 0,ln = defaultConfigList.length; i < ln; i++) { + name = defaultConfigList[i]; + nameMap = configNameCache[name]; + initializedName = nameMap.initialized; + + if (!this[initializedName]) { + this[initializedName] = true; + this[nameMap.set].call(this, config[name]); + } + } + + return this; + }, + + + hasConfig: function(name) { + return Boolean(this.configMap[name]); + }, + + + setConfig: function(config, applyIfNotSet) { + if (!config) { + return this; + } + + var configNameCache = Ext.Class.configNameCache, + currentConfig = this.config, + hasConfig = this.configMap, + initialConfig = this.initialConfig, + name, value; + + applyIfNotSet = Boolean(applyIfNotSet); + + for (name in config) { + if (applyIfNotSet && initialConfig.hasOwnProperty(name)) { + continue; + } + + value = config[name]; + currentConfig[name] = value; + + if (hasConfig[name]) { + this[configNameCache[name].set](value); + } + } + + return this; + }, + + + getConfig: function(name) { + var configNameCache = Ext.Class.configNameCache; + + return this[configNameCache[name].get](); + }, + + + getInitialConfig: function(name) { + var config = this.config; + + if (!name) { + return config; + } + else { + return config[name]; + } + }, + + + onConfigUpdate: function(names, callback, scope) { + var self = this.self, + className = self.$className, + i, ln, name, + updaterName, updater, newUpdater; + + names = Ext.Array.from(names); + + scope = scope || this; + + for (i = 0,ln = names.length; i < ln; i++) { + name = names[i]; + updaterName = 'update' + Ext.String.capitalize(name); + updater = this[updaterName] || Ext.emptyFn; + newUpdater = function() { + updater.apply(this, arguments); + scope[callback].apply(scope, arguments); + }; + newUpdater.$name = updaterName; + newUpdater.$owner = self; + newUpdater.displayName = className + '#' + updaterName; + + this[updaterName] = newUpdater; + } + }, + + + destroy: function() { + this.destroy = Ext.emptyFn; + } + }); + + + Base.prototype.callOverridden = Base.prototype.callParent; + + Ext.Base = Base; + +}(Ext.Function.flexSetter)); + + + + + + + +(function() { + var ExtClass, + Base = Ext.Base, + baseStaticMembers = [], + baseStaticMember, baseStaticMemberLength; + + for (baseStaticMember in Base) { + if (Base.hasOwnProperty(baseStaticMember)) { + baseStaticMembers.push(baseStaticMember); + } + } + + baseStaticMemberLength = baseStaticMembers.length; + + + function makeCtor (className) { + function constructor () { + + + return this.constructor.apply(this, arguments) || null; + } + if (className) { + constructor.displayName = className; + } + return constructor; + } + + + Ext.Class = ExtClass = function(Class, data, onCreated) { + if (typeof Class != 'function') { + onCreated = data; + data = Class; + Class = null; + } + + if (!data) { + data = {}; + } + + Class = ExtClass.create(Class, data); + + ExtClass.process(Class, data, onCreated); + + return Class; + }; + + Ext.apply(ExtClass, { + + onBeforeCreated: function(Class, data, hooks) { + Ext.classSystemMonitor && Ext.classSystemMonitor(Class, '>> Ext.Class#onBeforeCreated', arguments); + + Class.addMembers(data); + + hooks.onCreated.call(Class, Class); + + Ext.classSystemMonitor && Ext.classSystemMonitor(Class, '<< Ext.Class#onBeforeCreated', arguments); + }, + + + create: function(Class, data) { + var name, i; + + if (!Class) { + Class = makeCtor( + data.$className + ); + } + + for (i = 0; i < baseStaticMemberLength; i++) { + name = baseStaticMembers[i]; + Class[name] = Base[name]; + } + + return Class; + }, + + + process: function(Class, data, onCreated) { + var preprocessorStack = data.preprocessors || ExtClass.defaultPreprocessors, + registeredPreprocessors = this.preprocessors, + hooks = { + onBeforeCreated: this.onBeforeCreated + }, + preprocessors = [], + preprocessor, preprocessorsProperties, + i, ln, j, subLn, preprocessorProperty; + + delete data.preprocessors; + + for (i = 0,ln = preprocessorStack.length; i < ln; i++) { + preprocessor = preprocessorStack[i]; + + if (typeof preprocessor == 'string') { + preprocessor = registeredPreprocessors[preprocessor]; + preprocessorsProperties = preprocessor.properties; + + if (preprocessorsProperties === true) { + preprocessors.push(preprocessor.fn); + } + else if (preprocessorsProperties) { + for (j = 0,subLn = preprocessorsProperties.length; j < subLn; j++) { + preprocessorProperty = preprocessorsProperties[j]; + + if (data.hasOwnProperty(preprocessorProperty)) { + preprocessors.push(preprocessor.fn); + break; + } + } + } + } + else { + preprocessors.push(preprocessor); + } + } + + hooks.onCreated = onCreated ? onCreated : Ext.emptyFn; + hooks.preprocessors = preprocessors; + + this.doProcess(Class, data, hooks); + }, + + doProcess: function(Class, data, hooks) { + var me = this, + preprocessors = hooks.preprocessors, + preprocessor = preprocessors.shift(), + doProcess = me.doProcess; + + for ( ; preprocessor ; preprocessor = preprocessors.shift()) { + + if (preprocessor.call(me, Class, data, hooks, doProcess) === false) { + return; + } + } + hooks.onBeforeCreated.apply(me, arguments); + }, + + + preprocessors: {}, + + + registerPreprocessor: function(name, fn, properties, position, relativeTo) { + if (!position) { + position = 'last'; + } + + if (!properties) { + properties = [name]; + } + + this.preprocessors[name] = { + name: name, + properties: properties || false, + fn: fn + }; + + this.setDefaultPreprocessorPosition(name, position, relativeTo); + + return this; + }, + + + getPreprocessor: function(name) { + return this.preprocessors[name]; + }, + + + getPreprocessors: function() { + return this.preprocessors; + }, + + + defaultPreprocessors: [], + + + getDefaultPreprocessors: function() { + return this.defaultPreprocessors; + }, + + + setDefaultPreprocessors: function(preprocessors) { + this.defaultPreprocessors = Ext.Array.from(preprocessors); + + return this; + }, + + + setDefaultPreprocessorPosition: function(name, offset, relativeName) { + var defaultPreprocessors = this.defaultPreprocessors, + index; + + if (typeof offset == 'string') { + if (offset === 'first') { + defaultPreprocessors.unshift(name); + + return this; + } + else if (offset === 'last') { + defaultPreprocessors.push(name); + + return this; + } + + offset = (offset === 'after') ? 1 : -1; + } + + index = Ext.Array.indexOf(defaultPreprocessors, relativeName); + + if (index !== -1) { + Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name); + } + + return this; + }, + + configNameCache: {}, + + getConfigNameMap: function(name) { + var cache = this.configNameCache, + map = cache[name], + capitalizedName; + + if (!map) { + capitalizedName = name.charAt(0).toUpperCase() + name.substr(1); + + map = cache[name] = { + internal: name, + initialized: '_is' + capitalizedName + 'Initialized', + apply: 'apply' + capitalizedName, + update: 'update' + capitalizedName, + 'set': 'set' + capitalizedName, + 'get': 'get' + capitalizedName, + doSet : 'doSet' + capitalizedName, + changeEvent: name.toLowerCase() + 'change' + }; + } + + return map; + } + }); + + + ExtClass.registerPreprocessor('extend', function(Class, data, hooks) { + Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#extendPreProcessor', arguments); + + var Base = Ext.Base, + basePrototype = Base.prototype, + extend = data.extend, + Parent, parentPrototype, i; + + delete data.extend; + + if (extend && extend !== Object) { + Parent = extend; + } + else { + Parent = Base; + } + + parentPrototype = Parent.prototype; + + if (!Parent.$isClass) { + for (i in basePrototype) { + if (!parentPrototype[i]) { + parentPrototype[i] = basePrototype[i]; + } + } + } + + Class.extend(Parent); + + Class.triggerExtended.apply(Class, arguments); + + if (data.onClassExtended) { + Class.onExtended(data.onClassExtended, Class); + delete data.onClassExtended; + } + + }, true); + + + ExtClass.registerPreprocessor('statics', function(Class, data) { + Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#staticsPreprocessor', arguments); + + Class.addStatics(data.statics); + + delete data.statics; + }); + + + ExtClass.registerPreprocessor('inheritableStatics', function(Class, data) { + Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#inheritableStaticsPreprocessor', arguments); + + Class.addInheritableStatics(data.inheritableStatics); + + delete data.inheritableStatics; + }); + + + ExtClass.registerPreprocessor('config', function(Class, data) { + Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#configPreProcessor', arguments); + + var config = data.config, + prototype = Class.prototype; + + delete data.config; + + Ext.Object.each(config, function(name, value) { + var nameMap = ExtClass.getConfigNameMap(name), + internalName = nameMap.internal, + initializedName = nameMap.initialized, + applyName = nameMap.apply, + updateName = nameMap.update, + setName = nameMap.set, + getName = nameMap.get, + hasOwnSetter = (setName in prototype) || data.hasOwnProperty(setName), + hasOwnApplier = (applyName in prototype) || data.hasOwnProperty(applyName), + hasOwnUpdater = (updateName in prototype) || data.hasOwnProperty(updateName), + optimizedGetter, customGetter; + + if (value === null || (!hasOwnSetter && !hasOwnApplier && !hasOwnUpdater)) { + prototype[internalName] = value; + prototype[initializedName] = true; + } + else { + prototype[initializedName] = false; + } + + if (!hasOwnSetter) { + data[setName] = function(value) { + var oldValue = this[internalName], + applier = this[applyName], + updater = this[updateName]; + + if (!this[initializedName]) { + this[initializedName] = true; + } + + if (applier) { + value = applier.call(this, value, oldValue); + } + + if (typeof value != 'undefined') { + this[internalName] = value; + + if (updater && value !== oldValue) { + updater.call(this, value, oldValue); + } + } + + return this; + }; + } + + if (!(getName in prototype) || data.hasOwnProperty(getName)) { + customGetter = data[getName] || false; + + if (customGetter) { + optimizedGetter = function() { + return customGetter.apply(this, arguments); + }; + } + else { + optimizedGetter = function() { + return this[internalName]; + }; + } + + data[getName] = function() { + var currentGetter; + + if (!this[initializedName]) { + this[initializedName] = true; + this[setName](this.config[name]); + } + + currentGetter = this[getName]; + + if ('$previous' in currentGetter) { + currentGetter.$previous = optimizedGetter; + } + else { + this[getName] = optimizedGetter; + } + + return optimizedGetter.apply(this, arguments); + }; + } + }); + + Class.addConfig(config, true); + }); + + + ExtClass.registerPreprocessor('mixins', function(Class, data, hooks) { + Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#mixinsPreprocessor', arguments); + + var mixins = data.mixins, + name, mixin, i, ln; + + delete data.mixins; + + Ext.Function.interceptBefore(hooks, 'onCreated', function() { + Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#mixinsPreprocessor#beforeCreated', arguments); + + if (mixins instanceof Array) { + for (i = 0,ln = mixins.length; i < ln; i++) { + mixin = mixins[i]; + name = mixin.prototype.mixinId || mixin.$className; + + Class.mixin(name, mixin); + } + } + else { + for (var mixinName in mixins) { + if (mixins.hasOwnProperty(mixinName)) { + Class.mixin(mixinName, mixins[mixinName]); + } + } + } + }); + }); + + + Ext.extend = function(Class, Parent, members) { + Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#extend-backwards-compatible', arguments); + + if (arguments.length === 2 && Ext.isObject(Parent)) { + members = Parent; + Parent = Class; + Class = null; + } + + var cls; + + if (!Parent) { + throw new Error("[Ext.extend] Attempting to extend from a class which has not been loaded on the page."); + } + + members.extend = Parent; + members.preprocessors = [ + 'extend' + ,'statics' + ,'inheritableStatics' + ,'mixins' + ,'config' + ]; + + if (Class) { + cls = new ExtClass(Class, members); + + cls.prototype.constructor = Class; + } else { + cls = new ExtClass(members); + } + + cls.prototype.override = function(o) { + for (var m in o) { + if (o.hasOwnProperty(m)) { + this[m] = o[m]; + } + } + }; + + return cls; + }; +}()); + + + + + + + +(function(Class, alias, arraySlice, arrayFrom, global) { + + + function makeCtor () { + function constructor () { + + + return this.constructor.apply(this, arguments) || null; + } + return constructor; + } + + var Manager = Ext.ClassManager = { + + + classes: {}, + + + existCache: {}, + + + namespaceRewrites: [{ + from: 'Ext.', + to: Ext + }], + + + maps: { + alternateToName: {}, + aliasToName: {}, + nameToAliases: {}, + nameToAlternates: {} + }, + + + enableNamespaceParseCache: true, + + + namespaceParseCache: {}, + + + instantiators: [], + + + isCreated: function(className) { + var existCache = this.existCache, + i, ln, part, root, parts; + + if (typeof className != 'string' || className.length < 1) { + throw new Error("[Ext.ClassManager] Invalid classname, must be a string and must not be empty"); + } + + if (this.classes[className] || existCache[className]) { + return true; + } + + root = global; + parts = this.parseNamespace(className); + + for (i = 0, ln = parts.length; i < ln; i++) { + part = parts[i]; + + if (typeof part != 'string') { + root = part; + } else { + if (!root || !root[part]) { + return false; + } + + root = root[part]; + } + } + + existCache[className] = true; + + this.triggerCreated(className); + + return true; + }, + + + createdListeners: [], + + + nameCreatedListeners: {}, + + + triggerCreated: function(className) { + var listeners = this.createdListeners, + nameListeners = this.nameCreatedListeners, + alternateNames = this.maps.nameToAlternates[className], + names = [className], + i, ln, j, subLn, listener, name; + + for (i = 0,ln = listeners.length; i < ln; i++) { + listener = listeners[i]; + listener.fn.call(listener.scope, className); + } + + if (alternateNames) { + names.push.apply(names, alternateNames); + } + + for (i = 0,ln = names.length; i < ln; i++) { + name = names[i]; + listeners = nameListeners[name]; + + if (listeners) { + for (j = 0,subLn = listeners.length; j < subLn; j++) { + listener = listeners[j]; + listener.fn.call(listener.scope, name); + } + delete nameListeners[name]; + } + } + }, + + + onCreated: function(fn, scope, className) { + Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'Ext.ClassManager#onCreated', arguments); + + var listeners = this.createdListeners, + nameListeners = this.nameCreatedListeners, + listener = { + fn: fn, + scope: scope + }; + + if (className) { + if (this.isCreated(className)) { + fn.call(scope, className); + return; + } + + if (!nameListeners[className]) { + nameListeners[className] = []; + } + + nameListeners[className].push(listener); + } + else { + listeners.push(listener); + } + }, + + + parseNamespace: function(namespace) { + if (typeof namespace != 'string') { + throw new Error("[Ext.ClassManager] Invalid namespace, must be a string"); + } + + var cache = this.namespaceParseCache, + parts, + rewrites, + root, + name, + rewrite, from, to, i, ln; + + if (this.enableNamespaceParseCache) { + if (cache.hasOwnProperty(namespace)) { + return cache[namespace]; + } + } + + parts = []; + rewrites = this.namespaceRewrites; + root = global; + name = namespace; + + for (i = 0, ln = rewrites.length; i < ln; i++) { + rewrite = rewrites[i]; + from = rewrite.from; + to = rewrite.to; + + if (name === from || name.substring(0, from.length) === from) { + name = name.substring(from.length); + + if (typeof to != 'string') { + root = to; + } else { + parts = parts.concat(to.split('.')); + } + + break; + } + } + + parts.push(root); + + parts = parts.concat(name.split('.')); + + if (this.enableNamespaceParseCache) { + cache[namespace] = parts; + } + + return parts; + }, + + + setNamespace: function(name, value) { + var root = global, + parts = this.parseNamespace(name), + ln = parts.length - 1, + leaf = parts[ln], + i, part; + + for (i = 0; i < ln; i++) { + part = parts[i]; + + if (typeof part != 'string') { + root = part; + } else { + if (!root[part]) { + root[part] = {}; + } + + root = root[part]; + } + } + + root[leaf] = value; + + return root[leaf]; + }, + + + createNamespaces: function() { + var root = global, + parts, part, i, j, ln, subLn; + + for (i = 0, ln = arguments.length; i < ln; i++) { + parts = this.parseNamespace(arguments[i]); + + for (j = 0, subLn = parts.length; j < subLn; j++) { + part = parts[j]; + + if (typeof part != 'string') { + root = part; + } else { + if (!root[part]) { + root[part] = {}; + } + + root = root[part]; + } + } + } + + return root; + }, + + + set: function(name, value) { + var me = this, + maps = me.maps, + nameToAlternates = maps.nameToAlternates, + targetName = me.getName(value), + alternates; + + me.classes[name] = me.setNamespace(name, value); + + if (targetName && targetName !== name) { + maps.alternateToName[name] = targetName; + alternates = nameToAlternates[targetName] || (nameToAlternates[targetName] = []); + alternates.push(name); + } + + return this; + }, + + + get: function(name) { + var classes = this.classes, + root, + parts, + part, i, ln; + + if (classes[name]) { + return classes[name]; + } + + root = global; + parts = this.parseNamespace(name); + + for (i = 0, ln = parts.length; i < ln; i++) { + part = parts[i]; + + if (typeof part != 'string') { + root = part; + } else { + if (!root || !root[part]) { + return null; + } + + root = root[part]; + } + } + + return root; + }, + + + setAlias: function(cls, alias) { + var aliasToNameMap = this.maps.aliasToName, + nameToAliasesMap = this.maps.nameToAliases, + className; + + if (typeof cls == 'string') { + className = cls; + } else { + className = this.getName(cls); + } + + if (alias && aliasToNameMap[alias] !== className) { + if (aliasToNameMap[alias] && Ext.isDefined(global.console)) { + global.console.log("[Ext.ClassManager] Overriding existing alias: '" + alias + "' " + + "of: '" + aliasToNameMap[alias] + "' with: '" + className + "'. Be sure it's intentional."); + } + + aliasToNameMap[alias] = className; + } + + if (!nameToAliasesMap[className]) { + nameToAliasesMap[className] = []; + } + + if (alias) { + Ext.Array.include(nameToAliasesMap[className], alias); + } + + return this; + }, + + + addNameAliasMappings: function(aliases){ + var aliasToNameMap = this.maps.aliasToName, + nameToAliasesMap = this.maps.nameToAliases, + className, aliasList, alias, i; + + for (className in aliases) { + aliasList = nameToAliasesMap[className] || + (nameToAliasesMap[className] = []); + + for (i = 0; i < aliases[className].length; i++) { + alias = aliases[className][i]; + if (!aliasToNameMap[alias]) { + aliasToNameMap[alias] = className; + aliasList.push(alias); + } + } + + } + return this; + }, + + + addNameAlternateMappings: function(alternates) { + var alternateToName = this.maps.alternateToName, + nameToAlternates = this.maps.nameToAlternates, + className, aliasList, alternate, i; + + for (className in alternates) { + aliasList = nameToAlternates[className] || + (nameToAlternates[className] = []); + + for (i = 0; i < alternates[className].length; i++) { + alternate = alternates[className][i]; + if (!alternateToName[alternate]) { + alternateToName[alternate] = className; + aliasList.push(alternate); + } + } + + } + return this; + }, + + + getByAlias: function(alias) { + return this.get(this.getNameByAlias(alias)); + }, + + + getNameByAlias: function(alias) { + return this.maps.aliasToName[alias] || ''; + }, + + + getNameByAlternate: function(alternate) { + return this.maps.alternateToName[alternate] || ''; + }, + + + getAliasesByName: function(name) { + return this.maps.nameToAliases[name] || []; + }, + + + getName: function(object) { + return object && object.$className || ''; + }, + + + getClass: function(object) { + return object && object.self || null; + }, + + + create: function(className, data, createdFn) { + if (className != null && typeof className != 'string') { + throw new Error("[Ext.define] Invalid class name '" + className + "' specified, must be a non-empty string"); + } + + var ctor = makeCtor(); + if (typeof data == 'function') { + data = data(ctor); + } + + if (className) { + ctor.displayName = className; + } + + data.$className = className; + + return new Class(ctor, data, function() { + var postprocessorStack = data.postprocessors || Manager.defaultPostprocessors, + registeredPostprocessors = Manager.postprocessors, + postprocessors = [], + postprocessor, i, ln, j, subLn, postprocessorProperties, postprocessorProperty; + + delete data.postprocessors; + + for (i = 0,ln = postprocessorStack.length; i < ln; i++) { + postprocessor = postprocessorStack[i]; + + if (typeof postprocessor == 'string') { + postprocessor = registeredPostprocessors[postprocessor]; + postprocessorProperties = postprocessor.properties; + + if (postprocessorProperties === true) { + postprocessors.push(postprocessor.fn); + } + else if (postprocessorProperties) { + for (j = 0,subLn = postprocessorProperties.length; j < subLn; j++) { + postprocessorProperty = postprocessorProperties[j]; + + if (data.hasOwnProperty(postprocessorProperty)) { + postprocessors.push(postprocessor.fn); + break; + } + } + } + } + else { + postprocessors.push(postprocessor); + } + } + + data.postprocessors = postprocessors; + data.createdFn = createdFn; + Manager.processCreate(className, this, data); + }); + }, + + processCreate: function(className, cls, clsData){ + var me = this, + postprocessor = clsData.postprocessors.shift(), + createdFn = clsData.createdFn; + + if (!postprocessor) { + Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'Ext.ClassManager#classCreated', arguments); + + if (className) { + me.set(className, cls); + } + + if (createdFn) { + createdFn.call(cls, cls); + } + + if (className) { + me.triggerCreated(className); + } + return; + } + + if (postprocessor.call(me, className, cls, clsData, me.processCreate) !== false) { + me.processCreate(className, cls, clsData); + } + }, + + createOverride: function (className, data, createdFn) { + var me = this, + overriddenClassName = data.override, + requires = data.requires, + uses = data.uses, + compat = data.compatibility, + classReady = function () { + var cls, temp; + + if (requires) { + temp = requires; + requires = null; + + + + + Ext.Loader.require(temp, classReady); + } else { + + + cls = me.get(overriddenClassName); + + + delete data.override; + delete data.compatibility; + delete data.requires; + delete data.uses; + + Ext.override(cls, data); + + + + + me.triggerCreated(className); + + if (uses) { + Ext.Loader.addUsedClasses(uses); + } + + if (createdFn) { + createdFn.call(cls); + } + } + }; + + me.existCache[className] = true; + + if (!compat || Ext.checkVersion(compat)) { + + me.onCreated(classReady, me, overriddenClassName); + } + + return me; + }, + + + instantiateByAlias: function() { + var alias = arguments[0], + args = arraySlice.call(arguments), + className = this.getNameByAlias(alias); + + if (!className) { + className = this.maps.aliasToName[alias]; + + if (!className) { + throw new Error("[Ext.createByAlias] Cannot create an instance of unrecognized alias: " + alias); + } + + if (global.console) { + global.console.warn("[Ext.Loader] Synchronously loading '" + className + "'; consider adding " + + "Ext.require('" + alias + "') above Ext.onReady"); + } + + Ext.syncRequire(className); + } + + args[0] = className; + + return this.instantiate.apply(this, args); + }, + + + instantiate: function() { + var name = arguments[0], + nameType = typeof name, + args = arraySlice.call(arguments, 1), + alias = name, + possibleName, cls; + + if (nameType != 'function') { + if (nameType != 'string' && args.length === 0) { + args = [name]; + name = name.xclass; + } + + if (typeof name != 'string' || name.length < 1) { + throw new Error("[Ext.create] Invalid class name or alias '" + name + "' specified, must be a non-empty string"); + } + + cls = this.get(name); + } + else { + cls = name; + } + + + if (!cls) { + possibleName = this.getNameByAlias(name); + + if (possibleName) { + name = possibleName; + + cls = this.get(name); + } + } + + + if (!cls) { + possibleName = this.getNameByAlternate(name); + + if (possibleName) { + name = possibleName; + + cls = this.get(name); + } + } + + + if (!cls) { + if (global.console) { + global.console.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " + + "Ext.require('" + ((possibleName) ? alias : name) + "') above Ext.onReady"); + } + + Ext.syncRequire(name); + + cls = this.get(name); + } + + if (!cls) { + throw new Error("[Ext.create] Cannot create an instance of unrecognized class name / alias: " + alias); + } + + if (typeof cls != 'function') { + throw new Error("[Ext.create] '" + name + "' is a singleton and cannot be instantiated"); + } + + return this.getInstantiator(args.length)(cls, args); + }, + + + dynInstantiate: function(name, args) { + args = arrayFrom(args, true); + args.unshift(name); + + return this.instantiate.apply(this, args); + }, + + + getInstantiator: function(length) { + var instantiators = this.instantiators, + instantiator, + i, + args; + + instantiator = instantiators[length]; + + if (!instantiator) { + i = length; + args = []; + + for (i = 0; i < length; i++) { + args.push('a[' + i + ']'); + } + + instantiator = instantiators[length] = new Function('c', 'a', 'return new c(' + args.join(',') + ')'); + instantiator.displayName = "Ext.ClassManager.instantiate" + length; + } + + return instantiator; + }, + + + postprocessors: {}, + + + defaultPostprocessors: [], + + + registerPostprocessor: function(name, fn, properties, position, relativeTo) { + if (!position) { + position = 'last'; + } + + if (!properties) { + properties = [name]; + } + + this.postprocessors[name] = { + name: name, + properties: properties || false, + fn: fn + }; + + this.setDefaultPostprocessorPosition(name, position, relativeTo); + + return this; + }, + + + setDefaultPostprocessors: function(postprocessors) { + this.defaultPostprocessors = arrayFrom(postprocessors); + + return this; + }, + + + setDefaultPostprocessorPosition: function(name, offset, relativeName) { + var defaultPostprocessors = this.defaultPostprocessors, + index; + + if (typeof offset == 'string') { + if (offset === 'first') { + defaultPostprocessors.unshift(name); + + return this; + } + else if (offset === 'last') { + defaultPostprocessors.push(name); + + return this; + } + + offset = (offset === 'after') ? 1 : -1; + } + + index = Ext.Array.indexOf(defaultPostprocessors, relativeName); + + if (index !== -1) { + Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name); + } + + return this; + }, + + + getNamesByExpression: function(expression) { + var nameToAliasesMap = this.maps.nameToAliases, + names = [], + name, alias, aliases, possibleName, regex, i, ln; + + if (typeof expression != 'string' || expression.length < 1) { + throw new Error("[Ext.ClassManager.getNamesByExpression] Expression " + expression + " is invalid, must be a non-empty string"); + } + + if (expression.indexOf('*') !== -1) { + expression = expression.replace(/\*/g, '(.*?)'); + regex = new RegExp('^' + expression + '$'); + + for (name in nameToAliasesMap) { + if (nameToAliasesMap.hasOwnProperty(name)) { + aliases = nameToAliasesMap[name]; + + if (name.search(regex) !== -1) { + names.push(name); + } + else { + for (i = 0, ln = aliases.length; i < ln; i++) { + alias = aliases[i]; + + if (alias.search(regex) !== -1) { + names.push(name); + break; + } + } + } + } + } + + } else { + possibleName = this.getNameByAlias(expression); + + if (possibleName) { + names.push(possibleName); + } else { + possibleName = this.getNameByAlternate(expression); + + if (possibleName) { + names.push(possibleName); + } else { + names.push(expression); + } + } + } + + return names; + } + }; + + + Manager.registerPostprocessor('alias', function(name, cls, data) { + Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#aliasPostProcessor', arguments); + + var aliases = data.alias, + i, ln; + + for (i = 0,ln = aliases.length; i < ln; i++) { + alias = aliases[i]; + + this.setAlias(cls, alias); + } + + }, ['xtype', 'alias']); + + + Manager.registerPostprocessor('singleton', function(name, cls, data, fn) { + Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#singletonPostProcessor', arguments); + + if (data.singleton) { + fn.call(this, name, new cls(), data); + } + else { + return true; + } + return false; + }); + + + Manager.registerPostprocessor('alternateClassName', function(name, cls, data) { + Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#alternateClassNamePostprocessor', arguments); + + var alternates = data.alternateClassName, + i, ln, alternate; + + if (!(alternates instanceof Array)) { + alternates = [alternates]; + } + + for (i = 0, ln = alternates.length; i < ln; i++) { + alternate = alternates[i]; + + if (typeof alternate != 'string') { + throw new Error("[Ext.define] Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string"); + } + + this.set(alternate, cls); + } + }); + + Ext.apply(Ext, { + + create: alias(Manager, 'instantiate'), + + + widget: function(name, config) { + + + + + + + + var xtype = name, + alias, className, T, load; + + if (typeof xtype != 'string') { + + config = name; + xtype = config.xtype; + } else { + config = config || {}; + } + + if (config.isComponent) { + return config; + } + + alias = 'widget.' + xtype; + className = Manager.getNameByAlias(alias); + + + if (!className) { + load = true; + } + + T = Manager.get(className); + if (load || !T) { + return Manager.instantiateByAlias(alias, config); + } + return new T(config); + }, + + + createByAlias: alias(Manager, 'instantiateByAlias'), + + + define: function (className, data, createdFn) { + Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'ClassManager#define', arguments); + + if (data.override) { + return Manager.createOverride.apply(Manager, arguments); + } + + return Manager.create.apply(Manager, arguments); + }, + + + undefine: function(className) { + Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'Ext.ClassManager#undefine', arguments); + + var classes = Manager.classes, + maps = Manager.maps, + aliasToName = maps.aliasToName, + nameToAliases = maps.nameToAliases, + alternateToName = maps.alternateToName, + nameToAlternates = maps.nameToAlternates, + aliases = nameToAliases[className], + alternates = nameToAlternates[className], + parts, partCount, namespace, i; + + delete Manager.namespaceParseCache[className]; + delete nameToAliases[className]; + delete nameToAlternates[className]; + delete classes[className]; + + if (aliases) { + for (i = aliases.length; i--;) { + delete aliasToName[aliases[i]]; + } + } + + if (alternates) { + for (i = alternates.length; i--; ) { + delete alternateToName[alternates[i]]; + } + } + + parts = Manager.parseNamespace(className); + partCount = parts.length - 1; + namespace = parts[0]; + + for (i = 1; i < partCount; i++) { + namespace = namespace[parts[i]]; + if (!namespace) { + return; + } + } + + + try { + delete namespace[parts[partCount]]; + } + catch (e) { + namespace[parts[partCount]] = undefined; + } + }, + + + getClassName: alias(Manager, 'getName'), + + + getDisplayName: function(object) { + if (object) { + if (object.displayName) { + return object.displayName; + } + + if (object.$name && object.$class) { + return Ext.getClassName(object.$class) + '#' + object.$name; + } + + if (object.$className) { + return object.$className; + } + } + + return 'Anonymous'; + }, + + + getClass: alias(Manager, 'getClass'), + + + namespace: alias(Manager, 'createNamespaces') + }); + + + Ext.createWidget = Ext.widget; + + + Ext.ns = Ext.namespace; + + Class.registerPreprocessor('className', function(cls, data) { + if ('$className' in data) { + cls.$className = data.$className; + cls.displayName = cls.$className; + } + + Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#classNamePreprocessor', arguments); + }, true, 'first'); + + Class.registerPreprocessor('alias', function(cls, data) { + Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#aliasPreprocessor', arguments); + + var prototype = cls.prototype, + xtypes = arrayFrom(data.xtype), + aliases = arrayFrom(data.alias), + widgetPrefix = 'widget.', + widgetPrefixLength = widgetPrefix.length, + xtypesChain = Array.prototype.slice.call(prototype.xtypesChain || []), + xtypesMap = Ext.merge({}, prototype.xtypesMap || {}), + i, ln, alias, xtype; + + for (i = 0,ln = aliases.length; i < ln; i++) { + alias = aliases[i]; + + if (typeof alias != 'string' || alias.length < 1) { + throw new Error("[Ext.define] Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string"); + } + + if (alias.substring(0, widgetPrefixLength) === widgetPrefix) { + xtype = alias.substring(widgetPrefixLength); + Ext.Array.include(xtypes, xtype); + } + } + + cls.xtype = data.xtype = xtypes[0]; + data.xtypes = xtypes; + + for (i = 0,ln = xtypes.length; i < ln; i++) { + xtype = xtypes[i]; + + if (!xtypesMap[xtype]) { + xtypesMap[xtype] = true; + xtypesChain.push(xtype); + } + } + + data.xtypesChain = xtypesChain; + data.xtypesMap = xtypesMap; + + Ext.Function.interceptAfter(data, 'onClassCreated', function() { + Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#aliasPreprocessor#afterClassCreated', arguments); + + var mixins = prototype.mixins, + key, mixin; + + for (key in mixins) { + if (mixins.hasOwnProperty(key)) { + mixin = mixins[key]; + + xtypes = mixin.xtypes; + + if (xtypes) { + for (i = 0,ln = xtypes.length; i < ln; i++) { + xtype = xtypes[i]; + + if (!xtypesMap[xtype]) { + xtypesMap[xtype] = true; + xtypesChain.push(xtype); + } + } + } + } + } + }); + + for (i = 0,ln = xtypes.length; i < ln; i++) { + xtype = xtypes[i]; + + if (typeof xtype != 'string' || xtype.length < 1) { + throw new Error("[Ext.define] Invalid xtype of: '" + xtype + "' for class: '" + name + "'; must be a valid non-empty string"); + } + + Ext.Array.include(aliases, widgetPrefix + xtype); + } + + data.alias = aliases; + + }, ['xtype', 'alias']); + +}(Ext.Class, Ext.Function.alias, Array.prototype.slice, Ext.Array.from, Ext.global)); + + + +if (Ext._alternatesMetadata) { + Ext.ClassManager.addNameAlternateMappings(Ext._alternatesMetadata); + Ext._alternatesMetadata = null; +} + +if (Ext._aliasMetadata) { + Ext.ClassManager.addNameAliasMappings(Ext._aliasMetadata); + Ext._aliasMetadata = null; +} + + + + + + + + +Ext.Loader = new function() { + var Loader = this, + Manager = Ext.ClassManager, + Class = Ext.Class, + flexSetter = Ext.Function.flexSetter, + alias = Ext.Function.alias, + pass = Ext.Function.pass, + defer = Ext.Function.defer, + arrayErase = Ext.Array.erase, + dependencyProperties = ['extend', 'mixins', 'requires'], + isInHistory = {}, + history = [], + slashDotSlashRe = /\/\.\//g, + dotRe = /\./g, + setPathCount = 0; + + Ext.apply(Loader, { + + + isInHistory: isInHistory, + + + history: history, + + + config: { + + enabled: false, + + + scriptChainDelay : false, + + + disableCaching: true, + + + disableCachingParam: '_dc', + + + garbageCollect : false, + + + paths: { + 'Ext': '.' + }, + + + preserveScripts : true, + + + scriptCharset : undefined + }, + + + setConfig: function(name, value) { + if (Ext.isObject(name) && arguments.length === 1) { + Ext.merge(Loader.config, name); + + if ('paths' in name) { + Ext.app.collectNamespaces(name.paths); + } + } + else { + Loader.config[name] = (Ext.isObject(value)) ? Ext.merge(Loader.config[name], value) : value; + + if (name === 'paths') { + Ext.app.collectNamespaces(value); + } + } + + return Loader; + }, + + + getConfig: function(name) { + if (name) { + return Loader.config[name]; + } + + return Loader.config; + }, + + + setPath: flexSetter(function(name, path) { + Loader.config.paths[name] = path; + Ext.app.namespaces[name] = true; + setPathCount++; + + return Loader; + }), + + + addClassPathMappings: function(paths) { + var name; + + if(setPathCount == 0){ + Loader.config.paths = paths; + } else { + for(name in paths){ + Loader.config.paths[name] = paths[name]; + } + } + setPathCount++; + return Loader; + }, + + + getPath: function(className) { + var path = '', + paths = Loader.config.paths, + prefix = Loader.getPrefix(className); + + if (prefix.length > 0) { + if (prefix === className) { + return paths[prefix]; + } + + path = paths[prefix]; + className = className.substring(prefix.length + 1); + } + + if (path.length > 0) { + path += '/'; + } + + return path.replace(slashDotSlashRe, '/') + className.replace(dotRe, "/") + '.js'; + }, + + + getPrefix: function(className) { + var paths = Loader.config.paths, + prefix, deepestPrefix = ''; + + if (paths.hasOwnProperty(className)) { + return className; + } + + for (prefix in paths) { + if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) { + if (prefix.length > deepestPrefix.length) { + deepestPrefix = prefix; + } + } + } + + return deepestPrefix; + }, + + + isAClassNameWithAKnownPrefix: function(className) { + var prefix = Loader.getPrefix(className); + + + return prefix !== '' && prefix !== className; + }, + + + require: function(expressions, fn, scope, excludes) { + if (fn) { + fn.call(scope); + } + }, + + + syncRequire: function() {}, + + + exclude: function(excludes) { + return { + require: function(expressions, fn, scope) { + return Loader.require(expressions, fn, scope, excludes); + }, + + syncRequire: function(expressions, fn, scope) { + return Loader.syncRequire(expressions, fn, scope, excludes); + } + }; + }, + + + onReady: function(fn, scope, withDomReady, options) { + var oldFn; + + if (withDomReady !== false && Ext.onDocumentReady) { + oldFn = fn; + + fn = function() { + Ext.onDocumentReady(oldFn, scope, options); + }; + } + + fn.call(scope); + } + }); + + var queue = [], + isClassFileLoaded = {}, + isFileLoaded = {}, + inFlight = {}, + classNameToFilePathMap = {}, + scriptElements = {}, + readyListeners = [], + usedClasses = [], + requiresMap = {}, + comparePriority = function(listenerA, listenerB) { + return listenerB.priority - listenerA.priority; + }; + + Ext.apply(Loader, { + + documentHead: typeof document != 'undefined' && (document.head || document.getElementsByTagName('head')[0]), + + + isLoading: false, + + + queue: queue, + + + isClassFileLoaded: isClassFileLoaded, + + + isFileLoaded: isFileLoaded, + + + readyListeners: readyListeners, + + + optionalRequires: usedClasses, + + + requiresMap: requiresMap, + + + numPendingFiles: 0, + + + numLoadedFiles: 0, + + + hasFileLoadError: false, + + + classNameToFilePathMap: classNameToFilePathMap, + + + scriptsLoading: 0, + + + syncModeEnabled: false, + + scriptElements: scriptElements, + + + refreshQueue: function() { + var ln = queue.length, + i, item, j, requires; + + + + if (!ln && !Loader.scriptsLoading) { + return Loader.triggerReady(); + } + + for (i = 0; i < ln; i++) { + item = queue[i]; + + if (item) { + requires = item.requires; + + + for (j = 0; j < requires.length; ) { + if (Manager.isCreated(requires[j])) { + + arrayErase(requires, j, 1); + } + else { + j++; + } + } + + + if (item.requires.length === 0) { + arrayErase(queue, i, 1); + item.callback.call(item.scope); + Loader.refreshQueue(); + break; + } + } + } + + return Loader; + }, + + + injectScriptElement: function(url, onLoad, onError, scope, charset) { + var script = document.createElement('script'), + dispatched = false, + config = Loader.config, + onLoadFn = function() { + + if(!dispatched) { + dispatched = true; + script.onload = script.onreadystatechange = script.onerror = null; + if (typeof config.scriptChainDelay == 'number') { + + defer(onLoad, config.scriptChainDelay, scope); + } else { + onLoad.call(scope); + } + Loader.cleanupScriptElement(script, config.preserveScripts === false, config.garbageCollect); + } + + }, + onErrorFn = function(arg) { + defer(onError, 1, scope); + Loader.cleanupScriptElement(script, config.preserveScripts === false, config.garbageCollect); + }; + + script.type = 'text/javascript'; + script.onerror = onErrorFn; + charset = charset || config.scriptCharset; + if (charset) { + script.charset = charset; + } + + + if ('addEventListener' in script ) { + script.onload = onLoadFn; + } else if ('readyState' in script) { + script.onreadystatechange = function() { + if ( this.readyState == 'loaded' || this.readyState == 'complete' ) { + onLoadFn(); + } + }; + } else { + script.onload = onLoadFn; + } + + script.src = url; + (Loader.documentHead || document.getElementsByTagName('head')[0]).appendChild(script); + + return script; + }, + + + removeScriptElement: function(url) { + if (scriptElements[url]) { + Loader.cleanupScriptElement(scriptElements[url], true, !!Loader.getConfig('garbageCollect')); + delete scriptElements[url]; + } + + return Loader; + }, + + + cleanupScriptElement: function(script, remove, collect) { + var prop; + script.onload = script.onreadystatechange = script.onerror = null; + if (remove) { + Ext.removeNode(script); + if (collect) { + for (prop in script) { + try { + if (prop != 'src') { + + + script[prop] = null; + } + delete script[prop]; + } catch (cleanEx) { + + } + } + } + } + + return Loader; + }, + + + loadScript: function (options) { + var config = Loader.getConfig(), + isString = typeof options == 'string', + url = isString ? options : options.url, + onError = !isString && options.onError, + onLoad = !isString && options.onLoad, + scope = !isString && options.scope, + onScriptError = function() { + Loader.numPendingFiles--; + Loader.scriptsLoading--; + + if (onError) { + onError.call(scope, "Failed loading '" + url + "', please verify that the file exists"); + } + + if (Loader.numPendingFiles + Loader.scriptsLoading === 0) { + Loader.refreshQueue(); + } + }, + onScriptLoad = function () { + Loader.numPendingFiles--; + Loader.scriptsLoading--; + + if (onLoad) { + onLoad.call(scope); + } + + if (Loader.numPendingFiles + Loader.scriptsLoading === 0) { + Loader.refreshQueue(); + } + }, + src; + + Loader.isLoading = true; + Loader.numPendingFiles++; + Loader.scriptsLoading++; + + src = config.disableCaching ? + + (url + (url.indexOf('?') === -1 ? '?' : '&') + config.disableCachingParam + '=' + Ext.Date.now()) : url; + + scriptElements[url] = Loader.injectScriptElement(src, onScriptLoad, onScriptError); + }, + + + loadScriptFile: function(url, onLoad, onError, scope, synchronous) { + var config = Loader.getConfig(), + noCacheUrl = url + (config.disableCaching ? ('?' + config.disableCachingParam + '=' + Ext.Date.now()) : ''), + isCrossOriginRestricted = false, + xhr, status, onScriptError, + debugSourceURL = ""; + + scope = scope || Loader; + + Loader.isLoading = true; + + if (!synchronous) { + onScriptError = function() { + onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous); + }; + + scriptElements[url] = Loader.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope); + } else { + if (typeof XMLHttpRequest != 'undefined') { + xhr = new XMLHttpRequest(); + } else { + xhr = new ActiveXObject('Microsoft.XMLHTTP'); + } + + try { + xhr.open('GET', noCacheUrl, false); + xhr.send(null); + } catch (e) { + isCrossOriginRestricted = true; + } + + status = (xhr.status === 1223) ? 204 : + (xhr.status === 0 && ((self.location || {}).protocol == 'file:' || (self.location || {}).protocol == 'ionp:')) ? 200 : xhr.status; + + isCrossOriginRestricted = isCrossOriginRestricted || (status === 0); + + if (isCrossOriginRestricted + ) { + onError.call(Loader, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " + + "being loaded from a different domain or from the local file system whereby cross origin " + + "requests are not allowed due to security reasons. Use asynchronous loading with " + + "Ext.require instead.", synchronous); + } + else if ((status >= 200 && status < 300) || (status === 304) + ) { + + + if (!Ext.isIE) { + debugSourceURL = "\n//@ sourceURL=" + url; + } + + Ext.globalEval(xhr.responseText + debugSourceURL); + + onLoad.call(scope); + } + else { + onError.call(Loader, "Failed loading synchronously via XHR: '" + url + "'; please " + + "verify that the file exists. " + + "XHR status code: " + status, synchronous); + } + + + xhr = null; + } + }, + + + syncRequire: function() { + var syncModeEnabled = Loader.syncModeEnabled; + + if (!syncModeEnabled) { + Loader.syncModeEnabled = true; + } + + Loader.require.apply(Loader, arguments); + + if (!syncModeEnabled) { + Loader.syncModeEnabled = false; + } + + Loader.refreshQueue(); + }, + + + require: function(expressions, fn, scope, excludes) { + var excluded = {}, + included = {}, + excludedClassNames = [], + possibleClassNames = [], + classNames = [], + references = [], + callback, + syncModeEnabled, + filePath, expression, exclude, className, + possibleClassName, i, j, ln, subLn; + + if (excludes) { + + excludes = (typeof excludes === 'string') ? [ excludes ] : excludes; + + for (i = 0,ln = excludes.length; i < ln; i++) { + exclude = excludes[i]; + + if (typeof exclude == 'string' && exclude.length > 0) { + excludedClassNames = Manager.getNamesByExpression(exclude); + + for (j = 0,subLn = excludedClassNames.length; j < subLn; j++) { + excluded[excludedClassNames[j]] = true; + } + } + } + } + + + expressions = (typeof expressions === 'string') ? [ expressions ] : (expressions ? expressions : []); + + if (fn) { + if (fn.length > 0) { + callback = function() { + var classes = [], + i, ln; + + for (i = 0,ln = references.length; i < ln; i++) { + classes.push(Manager.get(references[i])); + } + + return fn.apply(this, classes); + }; + } + else { + callback = fn; + } + } + else { + callback = Ext.emptyFn; + } + + scope = scope || Ext.global; + + for (i = 0,ln = expressions.length; i < ln; i++) { + expression = expressions[i]; + + if (typeof expression == 'string' && expression.length > 0) { + possibleClassNames = Manager.getNamesByExpression(expression); + subLn = possibleClassNames.length; + + for (j = 0; j < subLn; j++) { + possibleClassName = possibleClassNames[j]; + + if (excluded[possibleClassName] !== true) { + references.push(possibleClassName); + + if (!Manager.isCreated(possibleClassName) && !included[possibleClassName]) { + included[possibleClassName] = true; + classNames.push(possibleClassName); + } + } + } + } + } + + + + if (classNames.length > 0) { + if (!Loader.config.enabled) { + throw new Error("Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " + + "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')); + } + } + else { + callback.call(scope); + return Loader; + } + + syncModeEnabled = Loader.syncModeEnabled; + + if (!syncModeEnabled) { + queue.push({ + requires: classNames.slice(), + + callback: callback, + scope: scope + }); + } + + ln = classNames.length; + + for (i = 0; i < ln; i++) { + className = classNames[i]; + + filePath = Loader.getPath(className); + + + + + if (syncModeEnabled && isClassFileLoaded.hasOwnProperty(className)) { + if (!isClassFileLoaded[className]) { + Loader.numPendingFiles--; + Loader.removeScriptElement(filePath); + delete isClassFileLoaded[className]; + } + } + + if (!isClassFileLoaded.hasOwnProperty(className)) { + + + + + + + + + + + + if (Loader.isFileLoaded[filePath] || inFlight[filePath]) { + isClassFileLoaded[className] = true; + } else { + inFlight[filePath] = true; + isClassFileLoaded[className] = false; + + Loader.numPendingFiles++; + Loader.loadScriptFile( + filePath, + pass(Loader.onFileLoaded, [className, filePath], Loader), + pass(Loader.onFileLoadError, [className, filePath], Loader), + Loader, + syncModeEnabled + ); + } + classNameToFilePathMap[className] = filePath; + } + } + + if (syncModeEnabled) { + callback.call(scope); + + if (ln === 1) { + return Manager.get(className); + } + } + + return Loader; + }, + + + onFileLoaded: function(className, filePath) { + var loaded = isClassFileLoaded[className]; + Loader.numLoadedFiles++; + + isClassFileLoaded[className] = true; + isFileLoaded[filePath] = true; + delete inFlight[filePath]; + + + + if (!loaded) { + Loader.numPendingFiles--; + } + + if (Loader.numPendingFiles === 0) { + Loader.refreshQueue(); + } + + if (!Loader.syncModeEnabled && Loader.numPendingFiles === 0 && Loader.isLoading && !Loader.hasFileLoadError) { + var missingClasses = [], + missingPaths = [], + requires, + i, ln, j, subLn; + + for (i = 0,ln = queue.length; i < ln; i++) { + requires = queue[i].requires; + + for (j = 0,subLn = requires.length; j < subLn; j++) { + if (isClassFileLoaded[requires[j]]) { + missingClasses.push(requires[j]); + } + } + } + + if (missingClasses.length < 1) { + return; + } + + missingClasses = Ext.Array.filter(Ext.Array.unique(missingClasses), function(item) { + return !requiresMap.hasOwnProperty(item); + }, Loader); + + if (missingClasses.length < 1) { + return; + } + + for (i = 0,ln = missingClasses.length; i < ln; i++) { + missingPaths.push(classNameToFilePathMap[missingClasses[i]]); + } + + throw new Error("The following classes are not declared even if their files have been " + + "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " + + "corresponding files for possible typos: '" + missingPaths.join("', '")); + } + }, + + + onFileLoadError: function(className, filePath, errorMessage, isSynchronous) { + Loader.numPendingFiles--; + Loader.hasFileLoadError = true; + + throw new Error("[Ext.Loader] " + errorMessage); + }, + + + addUsedClasses: function (classes) { + var cls, i, ln; + + if (classes) { + classes = (typeof classes == 'string') ? [classes] : classes; + for (i = 0, ln = classes.length; i < ln; i++) { + cls = classes[i]; + if (typeof cls == 'string' && !Ext.Array.contains(usedClasses, cls)) { + usedClasses.push(cls); + } + } + } + + return Loader; + }, + + + triggerReady: function() { + var listener, + refClasses = usedClasses; + + if (Loader.isLoading) { + Loader.isLoading = false; + + if (refClasses.length !== 0) { + + refClasses = refClasses.slice(); + usedClasses.length = 0; + + + Loader.require(refClasses, Loader.triggerReady, Loader); + return Loader; + } + } + + Ext.Array.sort(readyListeners, comparePriority); + + + + + while (readyListeners.length && !Loader.isLoading) { + + + listener = readyListeners.shift(); + listener.fn.call(listener.scope); + } + + return Loader; + }, + + + onReady: function(fn, scope, withDomReady, options) { + var oldFn; + + if (withDomReady !== false && Ext.onDocumentReady) { + oldFn = fn; + + fn = function() { + Ext.onDocumentReady(oldFn, scope, options); + }; + } + + if (!Loader.isLoading) { + fn.call(scope); + } + else { + readyListeners.push({ + fn: fn, + scope: scope, + priority: (options && options.priority) || 0 + }); + } + }, + + + historyPush: function(className) { + if (className && isClassFileLoaded.hasOwnProperty(className) && !isInHistory[className]) { + isInHistory[className] = true; + history.push(className); + } + return Loader; + } + }); + + + Ext.disableCacheBuster = function (disable, path) { + var date = new Date(); + date.setTime(date.getTime() + (disable ? 10*365 : -1) * 24*60*60*1000); + date = date.toGMTString(); + document.cookie = 'ext-cache=1; expires=' + date + '; path='+(path || '/'); + }; + + + + Ext.require = alias(Loader, 'require'); + + + Ext.syncRequire = alias(Loader, 'syncRequire'); + + + Ext.exclude = alias(Loader, 'exclude'); + + + Ext.onReady = function(fn, scope, options) { + Loader.onReady(fn, scope, true, options); + }; + + + Class.registerPreprocessor('loader', function(cls, data, hooks, continueFn) { + Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.Loader#loaderPreprocessor', arguments); + + var me = this, + dependencies = [], + dependency, + className = Manager.getName(cls), + i, j, ln, subLn, value, propertyName, propertyValue, + requiredMap, requiredDep; + + + + for (i = 0,ln = dependencyProperties.length; i < ln; i++) { + propertyName = dependencyProperties[i]; + + if (data.hasOwnProperty(propertyName)) { + propertyValue = data[propertyName]; + + if (typeof propertyValue == 'string') { + dependencies.push(propertyValue); + } + else if (propertyValue instanceof Array) { + for (j = 0, subLn = propertyValue.length; j < subLn; j++) { + value = propertyValue[j]; + + if (typeof value == 'string') { + dependencies.push(value); + } + } + } + else if (typeof propertyValue != 'function') { + for (j in propertyValue) { + if (propertyValue.hasOwnProperty(j)) { + value = propertyValue[j]; + + if (typeof value == 'string') { + dependencies.push(value); + } + } + } + } + } + } + + if (dependencies.length === 0) { + return; + } + + var deadlockPath = [], + detectDeadlock; + + + + if (className) { + requiresMap[className] = dependencies; + requiredMap = Loader.requiredByMap || (Loader.requiredByMap = {}); + + for (i = 0,ln = dependencies.length; i < ln; i++) { + dependency = dependencies[i]; + (requiredMap[dependency] || (requiredMap[dependency] = [])).push(className); + } + detectDeadlock = function(cls) { + deadlockPath.push(cls); + + if (requiresMap[cls]) { + if (Ext.Array.contains(requiresMap[cls], className)) { + throw new Error("Deadlock detected while loading dependencies! '" + className + "' and '" + + deadlockPath[1] + "' " + "mutually require each other. Path: " + + deadlockPath.join(' -> ') + " -> " + deadlockPath[0]); + } + + for (i = 0,ln = requiresMap[cls].length; i < ln; i++) { + detectDeadlock(requiresMap[cls][i]); + } + } + }; + + detectDeadlock(className); + } + + + Loader.require(dependencies, function() { + for (i = 0,ln = dependencyProperties.length; i < ln; i++) { + propertyName = dependencyProperties[i]; + + if (data.hasOwnProperty(propertyName)) { + propertyValue = data[propertyName]; + + if (typeof propertyValue == 'string') { + data[propertyName] = Manager.get(propertyValue); + } + else if (propertyValue instanceof Array) { + for (j = 0, subLn = propertyValue.length; j < subLn; j++) { + value = propertyValue[j]; + + if (typeof value == 'string') { + data[propertyName][j] = Manager.get(value); + } + } + } + else if (typeof propertyValue != 'function') { + for (var k in propertyValue) { + if (propertyValue.hasOwnProperty(k)) { + value = propertyValue[k]; + + if (typeof value == 'string') { + data[propertyName][k] = Manager.get(value); + } + } + } + } + } + } + + continueFn.call(me, cls, data, hooks); + }); + + return false; + }, true, 'after', 'className'); + + + Manager.registerPostprocessor('uses', function(name, cls, data) { + Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.Loader#usesPostprocessor', arguments); + + var uses = data.uses; + if (uses) { + Loader.addUsedClasses(uses); + } + }); + + Manager.onCreated(Loader.historyPush); +}; + + + +if (Ext._classPathMetadata) { + Ext.Loader.addClassPathMappings(Ext._classPathMetadata); + Ext._classPathMetadata = null; +} + + +(function() { + var scripts = document.getElementsByTagName('script'), + currentScript = scripts[scripts.length - 1], + src = currentScript.src, + path = src.substring(0, src.lastIndexOf('/') + 1), + Loader = Ext.Loader; + + if(src.indexOf("/platform/core/src/class/") != -1) { + path = path + "../../../../extjs/"; + } else if(src.indexOf("/core/src/class/") != -1) { + path = path + "../../../"; + } + + Loader.setConfig({ + enabled: true, + disableCaching: + (/[?&](?:cache|disableCacheBuster)\b/i.test(location.search) || + /(^|[ ;])ext-cache=1/.test(document.cookie)) ? false : + true, + paths: { + 'Ext': path + 'src' + } + }); +})(); + + + +Ext._endTime = new Date().getTime(); +if (Ext._beforereadyhandler){ + Ext._beforereadyhandler(); +} + + + + + + + +Ext.Error = Ext.extend(Error, { + statics: { + + ignore: false, + + + + + + raise: function(err){ + err = err || {}; + if (Ext.isString(err)) { + err = { msg: err }; + } + + var method = this.raise.caller, + msg; + + if (method) { + if (method.$name) { + err.sourceMethod = method.$name; + } + if (method.$owner) { + err.sourceClass = method.$owner.$className; + } + } + + if (Ext.Error.handle(err) !== true) { + msg = Ext.Error.prototype.toString.call(err); + + Ext.log({ + msg: msg, + level: 'error', + dump: err, + stack: true + }); + + throw new Ext.Error(err); + } + }, + + + handle: function(){ + return Ext.Error.ignore; + } + }, + + + name: 'Ext.Error', + + + constructor: function(config){ + if (Ext.isString(config)) { + config = { msg: config }; + } + + var me = this; + + Ext.apply(me, config); + + me.message = me.message || me.msg; + + }, + + + toString: function(){ + var me = this, + className = me.sourceClass ? me.sourceClass : '', + methodName = me.sourceMethod ? '.' + me.sourceMethod + '(): ' : '', + msg = me.msg || '(No description provided)'; + + return className + methodName + msg; + } +}); + + +Ext.deprecated = function (suggestion) { + if (!suggestion) { + suggestion = ''; + } + + function fail () { + Ext.Error.raise('The method "' + fail.$owner.$className + '.' + fail.$name + + '" has been removed. ' + suggestion); + } + + return fail; + return Ext.emptyFn; +}; + + +(function () { + var timer, errors = 0, + win = Ext.global, + msg; + + if (typeof window === 'undefined') { + return; + } + + + function notify () { + var counters = Ext.log.counters, + supports = Ext.supports, + hasOnError = supports && supports.WindowOnError; + + + if (counters && (counters.error + counters.warn + counters.info + counters.log)) { + msg = [ 'Logged Errors:',counters.error, 'Warnings:',counters.warn, + 'Info:',counters.info, 'Log:',counters.log].join(' '); + if (errors) { + msg = '*** Errors: ' + errors + ' - ' + msg; + } else if (counters.error) { + msg = '*** ' + msg; + } + win.status = msg; + } + + + if (!Ext.isDefined(Ext.Error.notify)) { + Ext.Error.notify = Ext.isIE6 || Ext.isIE7; + } + if (Ext.Error.notify && (hasOnError ? errors : (counters && counters.error))) { + Ext.Error.notify = false; + + if (timer) { + win.clearInterval(timer); + timer = null; + } + + alert('Unhandled error on page: See console or log'); + poll(); + } + } + + + + + function poll () { + timer = win.setInterval(notify, 1000); + } + + + + poll(); +}()); + + + + + + + +Ext.JSON = (new(function() { + var me = this, + encodingFunction, + decodingFunction, + useNative = null, + useHasOwn = !! {}.hasOwnProperty, + isNative = function() { + if (useNative === null) { + useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]'; + } + return useNative; + }, + 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 Ext.JSON.encodeDate(o); + } else if (Ext.isString(o)) { + return Ext.JSON.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', + '"': '\\"', + "\\": '\\\\', + '\x0b': '\\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); + }) + '"'; + }, + + encodeArrayPretty = function(o, newline) { + var len = o.length, + cnewline = newline + ' ', + sep = ',' + cnewline, + a = ["[", cnewline], + i; + + for (i = 0; i < len; i += 1) { + a.push(Ext.JSON.encodeValue(o[i], cnewline), sep); + } + + + a[a.length - 1] = newline + ']'; + + return a.join(''); + }, + + encodeObjectPretty = function(o, newline) { + var cnewline = newline + ' ', + sep = ',' + cnewline, + a = ["{", cnewline], + i, val; + + for (i in o) { + val = o[i]; + if (!useHasOwn || o.hasOwnProperty(i)) { + + if (typeof val === 'function' || val === undefined) { + continue; + } + a.push(Ext.JSON.encodeValue(i) + ': ' + Ext.JSON.encodeValue(val, cnewline), sep); + } + } + + + a[a.length - 1] = newline + '}'; + + return a.join(''); + }, + + encodeArray = function(o, newline) { + if (newline) { + return encodeArrayPretty(o, newline); + } + + var a = ["[", ""], + len = o.length, + i; + for (i = 0; i < len; i += 1) { + a.push(Ext.JSON.encodeValue(o[i]), ','); + } + + a[a.length - 1] = ']'; + return a.join(""); + }, + + encodeObject = function(o, newline) { + if (newline) { + return encodeObjectPretty(o, newline); + } + + var a = ["{", ""], + i, val; + for (i in o) { + val = o[i]; + if (!useHasOwn || o.hasOwnProperty(i)) { + + if (typeof val === 'function' || val === undefined) { + continue; + } + a.push(Ext.JSON.encodeValue(i), ":", Ext.JSON.encodeValue(val), ','); + + } + } + + a[a.length - 1] = '}'; + return a.join(""); + }; + + + me.encodeString = encodeString; + + + me.encodeValue = doEncode; + + + me.encodeDate = function(o) { + return '"' + o.getFullYear() + "-" + + pad(o.getMonth() + 1) + "-" + + pad(o.getDate()) + "T" + + pad(o.getHours()) + ":" + + pad(o.getMinutes()) + ":" + + pad(o.getSeconds()) + '"'; + }; + + + me.encode = function(o) { + if (!encodingFunction) { + + encodingFunction = isNative() ? JSON.stringify : me.encodeValue; + } + return encodingFunction(o); + }; + + + me.decode = function(json, safe) { + if (!decodingFunction) { + + decodingFunction = isNative() ? JSON.parse : doDecode; + } + try { + return decodingFunction(json); + } catch (e) { + if (safe === true) { + return null; + } + Ext.Error.raise({ + sourceClass: "Ext.JSON", + sourceMethod: "decode", + msg: "You're trying to decode an invalid JSON String: " + json + }); + } + }; +})()); + +Ext.encode = Ext.JSON.encode; + +Ext.decode = Ext.JSON.decode; + + + + + + + +Ext.apply(Ext, { + userAgent: navigator.userAgent.toLowerCase(), + cache: {}, + idSeed: 1000, + windowId: 'ext-window', + documentId: 'ext-document', + + + isReady: false, + + + enableGarbageCollector: true, + + + enableListenerCollection: true, + + + rootHierarchyState: {}, + + addCacheEntry: function(id, el, dom) { + dom = dom || el.dom; + + if (!dom) { + + Ext.Error.raise('Cannot add an entry to the element cache without the DOM node'); + } + + var cache = Ext.cache, + key = id || (el && el.id) || dom.id, + entry = cache[key] || (cache[key] = { + data: {}, + events: {}, + + dom: dom, + + + skipGarbageCollection: !!(dom.getElementById || dom.navigator) + }); + + if (el) { + el.$cache = entry; + + + entry.el = el; + } + + return entry; + }, + + updateCacheEntry: function(cacheItem, dom) { + var oldDom = cacheItem.dom; + + + if (dom !== oldDom) { + Ext.EventManager.removeAll(oldDom); + } + cacheItem.dom = dom; + if (cacheItem.el) { + cacheItem.el.dom = dom; + } + + return cacheItem; + }, + + + id: function(el, prefix) { + var me = this, + sandboxPrefix = ''; + el = Ext.getDom(el, true) || {}; + if (el === document) { + el.id = me.documentId; + } + else if (el === window) { + el.id = me.windowId; + } + if (!el.id) { + if (me.isSandboxed) { + sandboxPrefix = Ext.sandboxName.toLowerCase() + '-'; + } + el.id = sandboxPrefix + (prefix || "ext-gen") + (++Ext.idSeed); + } + return el.id; + }, + + escapeId: (function(){ + var validIdRe = /^[a-zA-Z_][a-zA-Z0-9_\-]*$/i, + escapeRx = /([\W]{1})/g, + leadingNumRx = /^(\d)/g, + escapeFn = function(match, capture){ + return "\\" + capture; + }, + numEscapeFn = function(match, capture){ + return '\\00' + capture.charCodeAt(0).toString(16) + ' '; + }; + + return function(id) { + return validIdRe.test(id) + ? id + + + : id.replace(escapeRx, escapeFn) + .replace(leadingNumRx, numEscapeFn); + }; + }()), + + + getBody: (function() { + var body; + return function() { + return body || (body = Ext.get(document.body)); + }; + }()), + + + getHead: (function() { + var head; + return function() { + return head || (head = Ext.get(document.getElementsByTagName("head")[0])); + }; + }()), + + + getDoc: (function() { + var doc; + return function() { + return doc || (doc = Ext.get(document)); + }; + }()), + + + getOrientation: function() { + return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape'; + }, + + + destroy: function() { + var ln = arguments.length, + i, arg; + + for (i = 0; i < ln; i++) { + arg = arguments[i]; + if (arg) { + if (Ext.isArray(arg)) { + this.destroy.apply(this, arg); + } else if (arg.isStore) { + arg.destroyStore(); + } else if (Ext.isFunction(arg.destroy)) { + arg.destroy(); + } else if (arg.dom) { + arg.remove(); + } + } + } + }, + + + callback: function (callback, scope, args, delay) { + var fn, ret; + + if (Ext.isFunction(callback)){ + fn = callback; + } else if (scope && Ext.isString(callback)) { + fn = scope[callback]; + if (!fn) { + Ext.Error.raise('No method named "' + callback + '"'); + } + } + + if (fn) { + args = args || []; + scope = scope || window; + if (delay) { + Ext.defer(fn, delay, scope, args); + } else { + ret = fn.apply(scope, args); + } + } + + return ret; + }, + + + resolveMethod: function(fn, scope) { + if (Ext.isFunction(fn)) { + return fn; + } + + if (!Ext.isObject(scope) || !Ext.isFunction(scope[fn])) { + Ext.Error.raise('No method named "' + fn + '"'); + } + + return scope[fn]; + }, + + + htmlEncode : function(value) { + return Ext.String.htmlEncode(value); + }, + + + htmlDecode : function(value) { + return Ext.String.htmlDecode(value); + }, + + + urlAppend : function(url, s) { + return Ext.String.urlAppend(url, s); + }, + + + splitAndUnescape: (function() { + var cache = {}; + + return function(origin, delimiter) { + if (!origin) { + return []; + } + else if (!delimiter) { + return [origin]; + } + + var replaceRe = cache[delimiter] || (cache[delimiter] = new RegExp('\\\\' + delimiter, 'g')), + result = [], + parts, part; + + parts = origin.split(delimiter); + + while ((part = parts.shift()) !== undefined) { + + + while (part.charAt(part.length - 1) === '\\' && parts.length > 0) { + part = part + delimiter + parts.shift(); + } + + + part = part.replace(replaceRe, delimiter); + + result.push(part); + } + + return result; + } + })() +}); + + +Ext.ns = Ext.namespace; + + +window.undefined = window.undefined; + + +(function(){ + + var check = function(regex){ + return regex.test(Ext.userAgent); + }, + isStrict = document.compatMode == "CSS1Compat", + version = function (is, regex) { + var m; + return (is && (m = regex.exec(Ext.userAgent))) ? parseFloat(m[1]) : 0; + }, + docMode = document.documentMode, + isOpera = check(/opera/), + isOpera10_5 = isOpera && check(/version\/10\.5/), + isChrome = check(/\bchrome\b/), + isWebKit = check(/webkit/), + isSafari = !isChrome && check(/safari/), + isSafari2 = isSafari && check(/applewebkit\/4/), + isSafari3 = isSafari && check(/version\/3/), + isSafari4 = isSafari && check(/version\/4/), + isSafari5_0 = isSafari && check(/version\/5\.0/), + isSafari5 = isSafari && check(/version\/5/), + isIE = !isOpera && (check(/msie/) || check(/trident/)), + isIE7 = isIE && ((check(/msie 7/) && docMode != 8 && docMode != 9 && docMode != 10) || docMode == 7), + isIE8 = isIE && ((check(/msie 8/) && docMode != 7 && docMode != 9 && docMode != 10) || docMode == 8), + isIE9 = isIE && ((check(/msie 9/) && docMode != 7 && docMode != 8 && docMode != 10) || docMode == 9), + isIE10 = isIE && ((check(/msie 10/) && docMode != 7 && docMode != 8 && docMode != 9) || docMode == 10), + isIE11 = isIE && ((check(/trident\/7\.0/) && docMode != 7 && docMode != 8 && docMode != 9 && docMode != 10) || docMode == 11), + isIE6 = isIE && check(/msie 6/), + isGecko = !isWebKit && !isIE && check(/gecko/), + isGecko3 = isGecko && check(/rv:1\.9/), + isGecko4 = isGecko && check(/rv:2\.0/), + isGecko5 = isGecko && check(/rv:5\./), + isGecko10 = isGecko && check(/rv:10\./), + isFF3_0 = isGecko3 && check(/rv:1\.9\.0/), + isFF3_5 = isGecko3 && check(/rv:1\.9\.1/), + isFF3_6 = isGecko3 && check(/rv:1\.9\.2/), + isWindows = check(/windows|win32/), + isMac = check(/macintosh|mac os x/), + isLinux = check(/linux/), + scrollbarSize = null, + chromeVersion = version(true, /\bchrome\/(\d+\.\d+)/), + firefoxVersion = version(true, /\bfirefox\/(\d+\.\d+)/), + ieVersion = version(isIE, /msie (\d+\.\d+)/), + operaVersion = version(isOpera, /version\/(\d+\.\d+)/), + safariVersion = version(isSafari, /version\/(\d+\.\d+)/), + webKitVersion = version(isWebKit, /webkit\/(\d+\.\d+)/), + isSecure = /^https/i.test(window.location.protocol), + nullLog; + + + try { + document.execCommand("BackgroundImageCache", false, true); + } catch(e) {} + + + var primitiveRe = /string|number|boolean/; + function dumpObject (object) { + var member, type, value, name, + members = []; + + + + for (name in object) { + if (object.hasOwnProperty(name)) { + value = object[name]; + + type = typeof value; + if (type == "function") { + continue; + } + + if (type == 'undefined') { + member = type; + } else if (value === null || primitiveRe.test(type) || Ext.isDate(value)) { + member = Ext.encode(value); + } else if (Ext.isArray(value)) { + member = '[ ]'; + } else if (Ext.isObject(value)) { + member = '{ }'; + } else { + member = type; + } + members.push(Ext.encode(name) + ': ' + member); + } + } + + if (members.length) { + return ' \nData: {\n ' + members.join(',\n ') + '\n}'; + } + return ''; + } + + function log (message) { + var options, dump, + con = Ext.global.console, + level = 'log', + indent = log.indent || 0, + stack, + out, + max; + + log.indent = indent; + + if (typeof message != 'string') { + options = message; + message = options.msg || ''; + level = options.level || level; + dump = options.dump; + stack = options.stack; + + if (options.indent) { + ++log.indent; + } else if (options.outdent) { + log.indent = indent = Math.max(indent - 1, 0); + } + + if (dump && !(con && con.dir)) { + message += dumpObject(dump); + dump = null; + } + } + + if (arguments.length > 1) { + message += Array.prototype.slice.call(arguments, 1).join(''); + } + + message = indent ? Ext.String.repeat(' ', log.indentSize * indent) + message : message; + + if (level != 'log') { + message = '[' + level.charAt(0).toUpperCase() + '] ' + message; + } + + + + + if (con) { + if (con[level]) { + con[level](message); + } else { + con.log(message); + } + + if (dump) { + con.dir(dump); + } + + if (stack && con.trace) { + + if (!con.firebug || level != 'error') { + con.trace(); + } + } + } else { + if (Ext.isOpera) { + opera.postError(message); + } else { + out = log.out; + max = log.max; + + if (out.length >= max) { + + + Ext.Array.erase(out, 0, out.length - 3 * Math.floor(max / 4)); + } + + out.push(message); + } + } + + + ++log.count; + ++log.counters[level]; + } + + function logx (level, args) { + if (typeof args[0] == 'string') { + args.unshift({}); + } + args[0].level = level; + log.apply(this, args); + } + + log.error = function () { + logx('error', Array.prototype.slice.call(arguments)); + }; + log.info = function () { + logx('info', Array.prototype.slice.call(arguments)); + }; + log.warn = function () { + logx('warn', Array.prototype.slice.call(arguments)); + }; + + log.count = 0; + log.counters = { error: 0, warn: 0, info: 0, log: 0 }; + log.indentSize = 2; + log.out = []; + log.max = 750; + log.show = function () { + window.open('','extlog').document.write([ + ''].join('')); + }; + + nullLog = function () {}; + nullLog.info = nullLog.warn = nullLog.error = Ext.emptyFn; + + + Ext.setVersion('ext', '4.2.3.1477'); + Ext.setVersion('extjs', '4.2.3.1477'); + Ext.apply(Ext, { + + SSL_SECURE_URL : isSecure && isIE ? 'javascript:\'\'' : 'about:blank', + + + + plainTableCls: Ext.buildSettings.baseCSSPrefix + 'table-plain', + + plainListCls: Ext.buildSettings.baseCSSPrefix + 'list-plain', + + + enableNestedListenerRemoval : false, + + + USE_NATIVE_JSON : false, + + + getDom : function(el, strict) { + if (!el || !document) { + return null; + } + if (el.dom) { + return el.dom; + } else { + if (typeof el == 'string') { + var e = Ext.getElementById(el); + + + if (e && isIE && strict) { + if (el == e.getAttribute('id')) { + return e; + } else { + return null; + } + } + return e; + } else { + return el; + } + } + }, + + + removeNode : isIE6 || isIE7 || isIE8 + ? (function() { + var d; + return function(n){ + if(n && n.tagName.toUpperCase() != 'BODY'){ + (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n); + + var cache = Ext.cache, + id = n.id; + + if (cache[id]) { + delete cache[id].dom; + delete cache[id]; + } + + if (isIE8 && n.parentNode) { + n.parentNode.removeChild(n); + } + d = d || document.createElement('div'); + d.appendChild(n); + d.innerHTML = ''; + } + }; + }()) + : function(n) { + if (n && n.parentNode && n.tagName.toUpperCase() != 'BODY') { + (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n); + + var cache = Ext.cache, + id = n.id; + + if (cache[id]) { + delete cache[id].dom; + delete cache[id]; + } + + n.parentNode.removeChild(n); + } + }, + + isStrict: isStrict, + + + isIEQuirks: isIE && (!isStrict && (isIE6 || isIE7 || isIE8 || isIE9)), + + + isOpera : isOpera, + + + isOpera10_5 : isOpera10_5, + + + isWebKit : isWebKit, + + + isChrome : isChrome, + + + isSafari : isSafari, + + + isSafari3 : isSafari3, + + + isSafari4 : isSafari4, + + + isSafari5 : isSafari5, + + + isSafari5_0 : isSafari5_0, + + + + isSafari2 : isSafari2, + + + isIE : isIE, + + + isIE6 : isIE6, + + + isIE7 : isIE7, + + + isIE7m : isIE6 || isIE7, + + + isIE7p : isIE && !isIE6, + + + isIE8 : isIE8, + + + isIE8m : isIE6 || isIE7 || isIE8, + + + isIE8p : isIE && !(isIE6 || isIE7), + + + isIE9 : isIE9, + + + isIE9m : isIE6 || isIE7 || isIE8 || isIE9, + + + isIE9p : isIE && !(isIE6 || isIE7 || isIE8), + + + isIE10 : isIE10, + + + isIE10m : isIE6 || isIE7 || isIE8 || isIE9 || isIE10, + + + isIE10p : isIE && !(isIE6 || isIE7 || isIE8 || isIE9), + + + isIE11: isIE11, + + + isIE11m : isIE6 || isIE7 || isIE8 || isIE9 || isIE10 || isIE11, + + + isIE11p : isIE && !(isIE6 || isIE7 || isIE8 || isIE9 || isIE10), + + + isGecko : isGecko, + + + isGecko3 : isGecko3, + + + isGecko4 : isGecko4, + + + isGecko5 : isGecko5, + + + isGecko10 : isGecko10, + + + isFF3_0 : isFF3_0, + + + isFF3_5 : isFF3_5, + + + isFF3_6 : isFF3_6, + + + isFF4 : 4 <= firefoxVersion && firefoxVersion < 5, + + + isFF5 : 5 <= firefoxVersion && firefoxVersion < 6, + + + isFF10 : 10 <= firefoxVersion && firefoxVersion < 11, + + + isLinux : isLinux, + + + isWindows : isWindows, + + + isMac : isMac, + + + chromeVersion: chromeVersion, + + + firefoxVersion: firefoxVersion, + + + ieVersion: ieVersion, + + + operaVersion: operaVersion, + + + safariVersion: safariVersion, + + + webKitVersion: webKitVersion, + + + isSecure: isSecure, + + + BLANK_IMAGE_URL : (isIE6 || isIE7) ? '/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==', + + + value : function(v, defaultValue, allowBlank){ + return Ext.isEmpty(v, allowBlank) ? defaultValue : v; + }, + + + escapeRe : function(s) { + return s.replace(/([-.*+?\^${}()|\[\]\/\\])/g, "\\$1"); + }, + + + addBehaviors : function(o){ + if(!Ext.isReady){ + Ext.onReady(function(){ + Ext.addBehaviors(o); + }); + } else { + var cache = {}, + parts, + b, + s; + for (b in o) { + if ((parts = b.split('@'))[1]) { + s = parts[0]; + if(!cache[s]){ + cache[s] = Ext.select(s); + } + cache[s].on(parts[1], o[b]); + } + } + cache = null; + } + }, + + + getScrollbarSize: function (force) { + if (!Ext.isReady) { + return {}; + } + + if (force || !scrollbarSize) { + var db = document.body, + div = document.createElement('div'); + + div.style.width = div.style.height = '100px'; + div.style.overflow = 'scroll'; + div.style.position = 'absolute'; + + db.appendChild(div); + + + scrollbarSize = { + width: div.offsetWidth - div.clientWidth, + height: div.offsetHeight - div.clientHeight + }; + + db.removeChild(div); + } + + return scrollbarSize; + }, + + + getScrollBarWidth: function(force){ + var size = Ext.getScrollbarSize(force); + return size.width + 2; + }, + + + copyTo : function(dest, source, names, usePrototypeKeys){ + if(typeof names == 'string'){ + names = names.split(/[,;\s]/); + } + + var n, + nLen = names? names.length : 0, + name; + + for(n = 0; n < nLen; n++) { + name = names[n]; + + if(usePrototypeKeys || source.hasOwnProperty(name)){ + dest[name] = source[name]; + } + } + + return dest; + }, + + + destroyMembers : function(o){ + for (var i = 1, a = arguments, len = a.length; i < len; i++) { + Ext.destroy(o[a[i]]); + delete o[a[i]]; + } + }, + + + log : + log || + nullLog, + + + partition : function(arr, truth){ + var ret = [[],[]], + a, v, + aLen = arr.length; + + for (a = 0; a < aLen; a++) { + v = arr[a]; + ret[ (truth && truth(v, a, arr)) || (!truth && v) ? 0 : 1].push(v); + } + + return ret; + }, + + + invoke : function(arr, methodName){ + var ret = [], + args = Array.prototype.slice.call(arguments, 2), + a, v, + aLen = arr.length; + + for (a = 0; a < aLen; a++) { + v = arr[a]; + + if (v && typeof v[methodName] == 'function') { + ret.push(v[methodName].apply(v, args)); + } else { + ret.push(undefined); + } + } + + return ret; + }, + + + zip : function(){ + var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }), + arrs = parts[0], + fn = parts[1][0], + len = Ext.max(Ext.pluck(arrs, "length")), + ret = [], + i, + j, + aLen; + + for (i = 0; i < len; i++) { + ret[i] = []; + if(fn){ + ret[i] = fn.apply(fn, Ext.pluck(arrs, i)); + }else{ + for (j = 0, aLen = arrs.length; j < aLen; j++){ + ret[i].push( arrs[j][i] ); + } + } + } + return ret; + }, + + + toSentence: function(items, connector) { + var length = items.length, + head, + tail; + + if (length <= 1) { + return items[0]; + } else { + head = items.slice(0, length - 1); + tail = items[length - 1]; + + return Ext.util.Format.format("{0} {1} {2}", head.join(", "), connector || 'and', tail); + } + }, + + + setGlyphFontFamily: function(fontFamily) { + Ext._glyphFontFamily = fontFamily; + }, + + + useShims: isIE6 + }); +}()); + + +Ext.application = function(config) { + var App, paths, ns, + + createApp = function() { + Ext.onReady(function() { + Ext.app.Application.instance = new App(); + }); + }; + + if (typeof config === "string") { + Ext.require(config, function() { + App = Ext.ClassManager.get(config); + createApp(); + }); + } + else { + + + Ext.Loader.setPath(config.name, config.appFolder || 'app'); + + if (paths = config.paths) { + for (ns in paths) { + if (paths.hasOwnProperty(ns)) { + Ext.Loader.setPath(ns, paths[ns]); + } + } + } + + config['paths processed'] = true; + + + Ext.define(config.name + ".$application", Ext.apply({ + extend: 'Ext.app.Application' + }, config), + + function () { + App = this; + createApp(); + }); + } +}; + + + + + + + +(function() { + Ext.ns('Ext.util'); + + var UtilFormat = Ext.util.Format = {}, + stripTagsRE = /<\/?[^>]+>/gi, + stripScriptsRe = /(?:)((\n|\r|.)*?)(?:<\/script>)/ig, + nl2brRe = /\r?\n/g, + hashRe = /#+$/, + + + formatPattern = /[\d,\.#]+/, + + + formatCleanRe = /[^\d\.#]/g, + + + + I18NFormatCleanRe, + lastDecimalSeparator, + + + formatFns = {}; + + Ext.apply(UtilFormat, { + + + thousandSeparator: ',', + + + + + decimalSeparator: '.', + + + + + currencyPrecision: 2, + + + + + currencySign: '$', + + + + + currencyAtEnd: false, + + + + undef : function(value) { + return value !== undefined ? value : ""; + }, + + + defaultValue : function(value, defaultValue) { + return value !== undefined && value !== '' ? value : defaultValue; + }, + + + substr : 'ab'.substr(-1) != 'b' + ? function (value, start, length) { + var str = String(value); + return (start < 0) + ? str.substr(Math.max(str.length + start, 0), length) + : str.substr(start, length); + } + : function(value, start, length) { + return String(value).substr(start, length); + }, + + + lowercase : function(value) { + return String(value).toLowerCase(); + }, + + + uppercase : function(value) { + return String(value).toUpperCase(); + }, + + + usMoney : function(v) { + return UtilFormat.currency(v, '$', 2); + }, + + + currency: function(v, currencySign, decimals, end) { + var negativeSign = '', + format = ",0", + i = 0; + v = v - 0; + if (v < 0) { + v = -v; + negativeSign = '-'; + } + decimals = Ext.isDefined(decimals) ? decimals : UtilFormat.currencyPrecision; + format += (decimals > 0 ? '.' : ''); + for (; i < decimals; i++) { + format += '0'; + } + v = UtilFormat.number(v, format); + if ((end || UtilFormat.currencyAtEnd) === true) { + return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || UtilFormat.currencySign); + } else { + return Ext.String.format("{0}{1}{2}", negativeSign, currencySign || UtilFormat.currencySign, v); + } + }, + + + date: function(v, format) { + if (!v) { + return ""; + } + if (!Ext.isDate(v)) { + v = new Date(Date.parse(v)); + } + return Ext.Date.dateFormat(v, format || Ext.Date.defaultFormat); + }, + + + dateRenderer : function(format) { + return function(v) { + return UtilFormat.date(v, format); + }; + }, + + + stripTags : function(v) { + return !v ? v : String(v).replace(stripTagsRE, ""); + }, + + + stripScripts : function(v) { + return !v ? v : String(v).replace(stripScriptsRe, ""); + }, + + + fileSize : (function(){ + var byteLimit = 1024, + kbLimit = 1048576, + mbLimit = 1073741824; + + return function(size) { + var out; + if (size < byteLimit) { + if (size === 1) { + out = '1 byte'; + } else { + out = size + ' bytes'; + } + } else if (size < kbLimit) { + out = (Math.round(((size*10) / byteLimit))/10) + ' KB'; + } else if (size < mbLimit) { + out = (Math.round(((size*10) / kbLimit))/10) + ' MB'; + } else { + out = (Math.round(((size*10) / mbLimit))/10) + ' GB'; + } + return out; + }; + })(), + + + math : (function(){ + var fns = {}; + + return function(v, a){ + if (!fns[a]) { + fns[a] = Ext.functionFactory('v', 'return v ' + a + ';'); + } + return fns[a](v); + }; + }()), + + + round : function(value, precision) { + var result = Number(value); + if (typeof precision == 'number') { + precision = Math.pow(10, precision); + result = Math.round(value * precision) / precision; + } + return result; + }, + + + number : function(v, formatString) { + if (!formatString) { + return v; + } + var formatFn = formatFns[formatString]; + + + + if (!formatFn) { + + var originalFormatString = formatString, + comma = UtilFormat.thousandSeparator, + decimalSeparator = UtilFormat.decimalSeparator, + precision = 0, + trimPart = '', + hasComma, + splitFormat, + extraChars, + multiplier, + trimTrailingZeroes, + code, len; + + + + + + if (formatString.substr(formatString.length - 2) == '/i') { + + + if (!I18NFormatCleanRe || lastDecimalSeparator !== decimalSeparator) { + I18NFormatCleanRe = new RegExp('[^\\d\\' + decimalSeparator + ']','g'); + lastDecimalSeparator = decimalSeparator; + } + formatString = formatString.substr(0, formatString.length - 2); + hasComma = formatString.indexOf(comma) !== -1; + splitFormat = formatString.replace(I18NFormatCleanRe, '').split(decimalSeparator); + } else { + hasComma = formatString.indexOf(',') != -1; + splitFormat = formatString.replace(formatCleanRe, '').split('.'); + } + extraChars = formatString.replace(formatPattern, ''); + + if (splitFormat.length > 2) { + Ext.Error.raise({ + sourceClass: "Ext.util.Format", + sourceMethod: "number", + value: v, + formatString: formatString, + msg: "Invalid number format, should have no more than 1 decimal" + }); + } else if (splitFormat.length === 2) { + precision = splitFormat[1].length; + + + trimTrailingZeroes = splitFormat[1].match(hashRe); + if (trimTrailingZeroes) { + len = trimTrailingZeroes[0].length; + + trimPart = 'trailingZeroes=new RegExp(Ext.String.escapeRegex(utilFormat.decimalSeparator) + "*0{0,' + len + '}$")' + } + } + + + code = [ + 'var utilFormat=Ext.util.Format,extNumber=Ext.Number,neg,absVal,fnum,parts' + + (hasComma ? ',thousandSeparator,thousands=[],j,n,i' : '') + + (extraChars ? ',formatString="' + formatString + '",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, ' + precision + ');', + trimPart, ';' + ]; + + if (hasComma) { + + + + if (precision) { + code[code.length] = 'parts=fnum.split(".");'; + code[code.length] = 'fnum=parts[0];'; + } + code[code.length] = + 'if(absVal>=1000) {'; + code[code.length] = 'thousandSeparator=utilFormat.thousandSeparator;' + + 'thousands.length=0;' + + 'j=fnum.length;' + + 'n=fnum.length%3||3;' + + 'for(i=0;i'); + }, + + + capitalize: Ext.String.capitalize, + + + ellipsis: Ext.String.ellipsis, + + + format: Ext.String.format, + + + htmlDecode: Ext.String.htmlDecode, + + + htmlEncode: Ext.String.htmlEncode, + + + leftPad: Ext.String.leftPad, + + + trim : Ext.String.trim, + + + parseBox : function(box) { + box = box || 0; + + if (typeof box === 'number') { + return { + top : box, + right : box, + bottom: box, + left : box + }; + } + + var parts = box.split(' '), + ln = parts.length; + + if (ln == 1) { + parts[1] = parts[2] = parts[3] = parts[0]; + } + else if (ln == 2) { + parts[2] = parts[0]; + parts[3] = parts[1]; + } + else if (ln == 3) { + parts[3] = parts[1]; + } + + return { + top :parseInt(parts[0], 10) || 0, + right :parseInt(parts[1], 10) || 0, + bottom:parseInt(parts[2], 10) || 0, + left :parseInt(parts[3], 10) || 0 + }; + }, + + + escapeRegex : function(s) { + return s.replace(/([\-.*+?\^${}()|\[\]\/\\])/g, "\\$1"); + } + }); +}()); + + + + + + +Ext.define('Ext.util.TaskRunner', { + + + + interval: 10, + + + timerId: null, + + constructor: function (interval) { + var me = this; + + if (typeof interval == 'number') { + me.interval = interval; + } else if (interval) { + Ext.apply(me, interval); + } + + me.tasks = []; + me.timerFn = Ext.Function.bind(me.onTick, me); + }, + + + newTask: function (config) { + var task = new Ext.util.TaskRunner.Task(config); + task.manager = this; + return task; + }, + + + start: function(task) { + var me = this, + now = Ext.Date.now(); + + if (!task.pending) { + me.tasks.push(task); + task.pending = true; + } + + task.stopped = false; + task.taskStartTime = now; + task.taskRunTime = task.fireOnStart !== false ? 0 : task.taskStartTime; + task.taskRunCount = 0; + + if (!me.firing) { + if (task.fireOnStart !== false) { + me.startTimer(0, now); + } else { + me.startTimer(task.interval, now); + } + } + + return task; + }, + + + stop: function(task) { + + + + if (!task.stopped) { + task.stopped = true; + + if (task.onStop) { + task.onStop.call(task.scope || task, task); + } + } + + return task; + }, + + + stopAll: function() { + + Ext.each(this.tasks, this.stop, this); + }, + + + + firing: false, + + nextExpires: 1e99, + + + onTick: function () { + var me = this, + tasks = me.tasks, + now = Ext.Date.now(), + nextExpires = 1e99, + len = tasks.length, + expires, newTasks, i, task, rt, remove; + + me.timerId = null; + me.firing = true; + + + + + + for (i = 0; i < len || i < (len = tasks.length); ++i) { + task = tasks[i]; + + if (!(remove = task.stopped)) { + expires = task.taskRunTime + task.interval; + + if (expires <= now) { + rt = 1; + try { + rt = task.run.apply(task.scope || task, task.args || [++task.taskRunCount]); + } catch (taskError) { + try { + Ext.log({ + msg: taskError, + level: 'error' + }); + if (task.onError) { + rt = task.onError.call(task.scope || task, task, taskError); + } + } catch (ignore) { } + } + task.taskRunTime = now; + if (rt === false || task.taskRunCount === task.repeat) { + me.stop(task); + remove = true; + } else { + remove = task.stopped; + expires = now + task.interval; + } + } + + if (!remove && task.duration && task.duration <= (now - task.taskStartTime)) { + me.stop(task); + remove = true; + } + } + + if (remove) { + task.pending = false; + + + + + + + if (!newTasks) { + newTasks = tasks.slice(0, i); + + + + } + } else { + if (newTasks) { + newTasks.push(task); + } + + if (nextExpires > expires) { + nextExpires = expires; + } + } + } + + if (newTasks) { + + + me.tasks = newTasks; + } + + me.firing = false; + + if (me.tasks.length) { + + + + me.startTimer(nextExpires - now, Ext.Date.now()); + } + + + if (me.fireIdleEvent !== false) { + Ext.EventManager.idleEvent.fire(); + } + }, + + + startTimer: function (timeout, now) { + var me = this, + expires = now + timeout, + timerId = me.timerId; + + + + if (timerId && me.nextExpires - expires > me.interval) { + clearTimeout(timerId); + timerId = null; + } + + if (!timerId) { + if (timeout < me.interval) { + timeout = me.interval; + } + + me.timerId = setTimeout(me.timerFn, timeout); + me.nextExpires = expires; + } + } +}, +function () { + var me = this, + proto = me.prototype; + + + proto.destroy = proto.stopAll; + + + Ext.util.TaskManager = Ext.TaskManager = new me(); + + + me.Task = new Ext.Class({ + isTask: true, + + + stopped: true, + + + fireOnStart: false, + + constructor: function (config) { + Ext.apply(this, config); + }, + + + restart: function (interval) { + if (interval !== undefined) { + this.interval = interval; + } + + this.manager.start(this); + }, + + + start: function (interval) { + if (this.stopped) { + this.restart(interval); + } + }, + + + stop: function () { + this.manager.stop(this); + } + }); + + proto = me.Task.prototype; + + + proto.destroy = proto.stop; +}); + + + + + + + + + + + + + +Ext.define('Ext.util.TaskManager', { + extend: Ext.util.TaskRunner , + + alternateClassName: [ + 'Ext.TaskManager' + ], + + singleton: true +}); + + + + + + +Ext.define('Ext.perf.Accumulator', (function () { + var currentFrame = null, + khrome = Ext.global['chrome'], + formatTpl, + + + getTimestamp = function () { + + getTimestamp = function () { + return new Date().getTime(); + }; + + var interval, toolbox; + + + if (Ext.isChrome && khrome && khrome.Interval) { + interval = new khrome.Interval(); + interval.start(); + getTimestamp = function () { + return interval.microseconds() / 1000; + }; + } else if (window.ActiveXObject) { + try { + + toolbox = new ActiveXObject('SenchaToolbox.Toolbox'); + Ext.senchaToolbox = toolbox; + getTimestamp = function () { + return toolbox.milliseconds; + }; + } catch (e) { + + } + } else if (Date.now) { + getTimestamp = Date.now; + } + + Ext.perf.getTimestamp = Ext.perf.Accumulator.getTimestamp = getTimestamp; + return getTimestamp(); + }; + + function adjustSet (set, time) { + set.sum += time; + set.min = Math.min(set.min, time); + set.max = Math.max(set.max, time); + } + + function leaveFrame (time) { + var totalTime = time ? time : (getTimestamp() - this.time), + me = this, + accum = me.accum; + + ++accum.count; + if (! --accum.depth) { + adjustSet(accum.total, totalTime); + } + adjustSet(accum.pure, totalTime - me.childTime); + + currentFrame = me.parent; + if (currentFrame) { + ++currentFrame.accum.childCount; + currentFrame.childTime += totalTime; + } + } + + function makeSet () { + return { + min: Number.MAX_VALUE, + max: 0, + sum: 0 + }; + } + + function makeTap (me, fn) { + return function () { + var frame = me.enter(), + ret = fn.apply(this, arguments); + + frame.leave(); + return ret; + }; + } + + function round (x) { + return Math.round(x * 100) / 100; + } + + function setToJSON (count, childCount, calibration, set) { + var data = { + avg: 0, + min: set.min, + max: set.max, + sum: 0 + }; + + if (count) { + calibration = calibration || 0; + data.sum = set.sum - childCount * calibration; + data.avg = data.sum / count; + + + } + + return data; + } + + return { + constructor: function (name) { + var me = this; + + me.count = me.childCount = me.depth = me.maxDepth = 0; + me.pure = makeSet(); + me.total = makeSet(); + me.name = name; + }, + + statics: { + getTimestamp: getTimestamp + }, + + format: function (calibration) { + if (!formatTpl) { + formatTpl = new Ext.XTemplate([ + '{name} - {count} call(s)', + '', + '', + ' ({childCount} children)', + '', + '', + ' ({depth} deep)', + '', + '', + ', {type}: {[this.time(values.sum)]} msec (', + + 'avg={[this.time(values.sum / parent.count)]}', + + ')', + '', + '' + ].join(''), { + time: function (t) { + return Math.round(t * 100) / 100; + } + }); + } + + var data = this.getData(calibration); + data.name = this.name; + data.pure.type = 'Pure'; + data.total.type = 'Total'; + data.times = [data.pure, data.total]; + return formatTpl.apply(data); + }, + + getData: function (calibration) { + var me = this; + + return { + count: me.count, + childCount: me.childCount, + depth: me.maxDepth, + pure: setToJSON(me.count, me.childCount, calibration, me.pure), + total: setToJSON(me.count, me.childCount, calibration, me.total) + }; + }, + + enter: function () { + var me = this, + frame = { + accum: me, + leave: leaveFrame, + childTime: 0, + parent: currentFrame + }; + + ++me.depth; + if (me.maxDepth < me.depth) { + me.maxDepth = me.depth; + } + + currentFrame = frame; + frame.time = getTimestamp(); + return frame; + }, + + monitor: function (fn, scope, args) { + var frame = this.enter(); + if (args) { + fn.apply(scope, args); + } else { + fn.call(scope); + } + frame.leave(); + }, + + report: function () { + Ext.log(this.format()); + }, + + tap: function (className, methodName) { + var me = this, + methods = typeof methodName == 'string' ? [methodName] : methodName, + klass, statik, i, parts, length, name, src, + tapFunc; + + tapFunc = function(){ + if (typeof className == 'string') { + klass = Ext.global; + parts = className.split('.'); + for (i = 0, length = parts.length; i < length; ++i) { + klass = klass[parts[i]]; + } + } else { + klass = className; + } + + for (i = 0, length = methods.length; i < length; ++i) { + name = methods[i]; + statik = name.charAt(0) == '!'; + + if (statik) { + name = name.substring(1); + } else { + statik = !(name in klass.prototype); + } + + src = statik ? klass : klass.prototype; + src[name] = makeTap(me, src[name]); + } + }; + + Ext.ClassManager.onCreated(tapFunc, me, className); + + return me; + } + }; +}()), + +function () { + Ext.perf.getTimestamp = this.getTimestamp; +}); + + + + + + +Ext.define('Ext.perf.Monitor', { + singleton: true, + alternateClassName: 'Ext.Perf', + + + + + + constructor: function () { + this.accumulators = []; + this.accumulatorsByName = {}; + }, + + calibrate: function () { + var accum = new Ext.perf.Accumulator('$'), + total = accum.total, + getTimestamp = Ext.perf.Accumulator.getTimestamp, + count = 0, + frame, + endTime, + startTime; + + startTime = getTimestamp(); + + do { + frame = accum.enter(); + frame.leave(); + ++count; + } while (total.sum < 100); + + endTime = getTimestamp(); + + return (endTime - startTime) / count; + }, + + get: function (name) { + var me = this, + accum = me.accumulatorsByName[name]; + + if (!accum) { + me.accumulatorsByName[name] = accum = new Ext.perf.Accumulator(name); + me.accumulators.push(accum); + } + + return accum; + }, + + enter: function (name) { + return this.get(name).enter(); + }, + + monitor: function (name, fn, scope) { + this.get(name).monitor(fn, scope); + }, + + report: function () { + var me = this, + accumulators = me.accumulators, + calibration = me.calibrate(); + + accumulators.sort(function (a, b) { + return (a.name < b.name) ? -1 : ((b.name < a.name) ? 1 : 0); + }); + + me.updateGC(); + + Ext.log('Calibration: ' + Math.round(calibration * 100) / 100 + ' msec/sample'); + Ext.each(accumulators, function (accum) { + Ext.log(accum.format(calibration)); + }); + }, + + getData: function (all) { + var ret = {}, + accumulators = this.accumulators; + + Ext.each(accumulators, function (accum) { + if (all || accum.count) { + ret[accum.name] = accum.getData(); + } + }); + + return ret; + }, + + reset: function(){ + Ext.each(this.accumulators, function(accum){ + var me = accum; + me.count = me.childCount = me.depth = me.maxDepth = 0; + me.pure = { + min: Number.MAX_VALUE, + max: 0, + sum: 0 + }; + me.total = { + min: Number.MAX_VALUE, + max: 0, + sum: 0 + }; + }); + }, + + updateGC: function () { + var accumGC = this.accumulatorsByName.GC, + toolbox = Ext.senchaToolbox, + bucket; + + if (accumGC) { + accumGC.count = toolbox.garbageCollectionCounter || 0; + + if (accumGC.count) { + bucket = accumGC.pure; + accumGC.total.sum = bucket.sum = toolbox.garbageCollectionMilliseconds; + bucket.min = bucket.max = bucket.sum / accumGC.count; + bucket = accumGC.total; + bucket.min = bucket.max = bucket.sum / accumGC.count; + } + } + }, + + watchGC: function () { + Ext.perf.getTimestamp(); + + var toolbox = Ext.senchaToolbox; + + if (toolbox) { + this.get("GC"); + toolbox.watchGarbageCollector(false); + } + }, + + setup: function (config) { + if (!config) { + config = { + + + + + + + + + render: { + 'Ext.AbstractComponent': 'render' + }, + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + layout: { + 'Ext.layout.Context': 'run' + } + }; + } + + this.currentConfig = config; + + var key, prop, + accum, className, methods; + for (key in config) { + if (config.hasOwnProperty(key)) { + prop = config[key]; + accum = Ext.Perf.get(key); + + for (className in prop) { + if (prop.hasOwnProperty(className)) { + methods = prop[className]; + accum.tap(className, methods); + } + } + } + } + + this.watchGC(); + } +}); + + + + + + + +Ext.is = { + init : function(navigator) { + var platforms = this.platforms, + ln = platforms.length, + i, platform; + + navigator = navigator || window.navigator; + + for (i = 0; i < ln; i++) { + platform = platforms[i]; + this[platform.identity] = platform.regex.test(navigator[platform.property]); + } + + + this.Desktop = this.Mac || this.Windows || (this.Linux && !this.Android); + + this.Tablet = this.iPad; + + this.Phone = !this.Desktop && !this.Tablet; + + this.iOS = this.iPhone || this.iPad || this.iPod; + + + this.Standalone = !!window.navigator.standalone; + }, + + + platforms: [{ + property: 'platform', + regex: /iPhone/i, + identity: 'iPhone' + }, + + + { + property: 'platform', + regex: /iPod/i, + identity: 'iPod' + }, + + + { + property: 'userAgent', + regex: /iPad/i, + identity: 'iPad' + }, + + + { + property: 'userAgent', + regex: /Blackberry/i, + identity: 'Blackberry' + }, + + + { + property: 'userAgent', + regex: /Android/i, + identity: 'Android' + }, + + + { + property: 'platform', + regex: /Mac/i, + identity: 'Mac' + }, + + + { + property: 'platform', + regex: /Win/i, + identity: 'Windows' + }, + + + { + property: 'platform', + regex: /Linux/i, + identity: 'Linux' + }] +}; + +Ext.is.init(); + + +(function(){ + + + + + var getStyle = function(element, styleName){ + var view = element.ownerDocument.defaultView, + style = (view ? view.getComputedStyle(element, null) : element.currentStyle) || element.style; + return style[styleName]; + }, + supportsVectors = { + 'IE6-quirks': [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0,0,0], + 'IE6-strict': [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,1,1,0,0,1,0,1,0,0,0,0,0], + 'IE7-quirks': [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0,0,0], + 'IE7-strict': [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,1,0,0,1,0,1,0,0,0,0,0], + 'IE8-quirks': [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0,0,0], + 'IE8-strict': [0,1,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,1,1,1,1,1,0,0,1,0,1,0,0,1,0,0], + 'IE9-quirks': [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0,0,0], + 'IE9-strict': [0,1,0,0,1,1,1,1,1,1,1,0,1,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,0,1,0,0], + 'IE10-quirks': [1,1,0,0,1,1,1,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,0,0,1,0,0], + 'IE10-strict': [1,1,0,0,1,1,1,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,0,0,1,0,0] + }, + doc = document, + div = doc.createElement('div'); + +function getBrowserKey() { + var browser = Ext.isIE6 ? 'IE6' : Ext.isIE7 ? 'IE7' : Ext.isIE8 ? 'IE8' : + Ext.isIE9 ? 'IE9': Ext.isIE10 ? 'IE10' : ''; + + return browser ? browser + (Ext.isStrict ? '-strict' : '-quirks') : ''; +} + +Ext.supports = { + + init : function() { + var me = this, + toRun = me.toRun || me.tests, + n = toRun.length, + notRun = [], + browserKey = getBrowserKey(), + test, vector, value, + docReady = Ext.isReady; + + if (docReady) { + div.innerHTML = [ + '
', + '
', + '
', + '
', + '
', + '
', + '
', + '
' + ].join(''); + + doc.body.appendChild(div); + } + + vector = supportsVectors[browserKey]; + while (n--) { + test = toRun[n]; + value = vector && vector[n]; + if (value !== undefined) { + me[test.identity] = value; + } else if (docReady || test.early) { + me[test.identity] = test.fn.call(me, doc, div); + } else { + notRun.push(test); + } + } + + if (docReady) { + doc.body.removeChild(div); + } + + me.toRun = notRun; + }, + + + generateVector: function() { + var tests = this.tests, + vector = [], + i = 0, + ln = tests.length, + test; + + for (; i < ln; i++) { + test = tests[i]; + vector.push(this[test.identity] ? 1 : 0); + } + return vector; + }, + + + PointerEvents: 'pointerEvents' in document.documentElement.style, + + + + + LocalStorage: (function() { + try { + return 'localStorage' in window && window['localStorage'] !== null; + } catch (e) { + return false; + } + })(), + + + CSS3BoxShadow: 'boxShadow' in document.documentElement.style || 'WebkitBoxShadow' in document.documentElement.style || 'MozBoxShadow' in document.documentElement.style, + + + ClassList: !!document.documentElement.classList, + + + OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)), + + + DeviceMotion: ('ondevicemotion' in window), + + + + + Touch: ('ontouchstart' in window) && (!Ext.is.Desktop), + + + TimeoutActualLateness: (function(){ + setTimeout(function(){ + Ext.supports.TimeoutActualLateness = arguments.length !== 0; + }, 0); + }()), + + tests: [ + + { + + identity: 'Transitions', + fn: function(doc, div) { + var prefix = [ + 'webkit', + 'Moz', + 'o', + 'ms', + 'khtml' + ], + TE = 'TransitionEnd', + transitionEndName = [ + prefix[0] + TE, + 'transitionend', + prefix[2] + TE, + prefix[3] + TE, + prefix[4] + TE + ], + ln = prefix.length, + i = 0, + out = false; + + for (; i < ln; i++) { + if (getStyle(div, prefix[i] + "TransitionProperty")) { + Ext.supports.CSS3Prefix = prefix[i]; + Ext.supports.CSS3TransitionEnd = transitionEndName[i]; + out = true; + break; + } + } + return out; + } + }, + + + { + + identity: 'RightMargin', + fn: function(doc, div) { + var view = doc.defaultView; + return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px'); + } + }, + + + { + + identity: 'DisplayChangeInputSelectionBug', + early: true, + fn: function() { + var webKitVersion = Ext.webKitVersion; + + return 0 < webKitVersion && webKitVersion < 533; + } + }, + + + { + + identity: 'DisplayChangeTextAreaSelectionBug', + early: true, + fn: function() { + var webKitVersion = Ext.webKitVersion; + + + return 0 < webKitVersion && webKitVersion < 534.24; + } + }, + + + { + + identity: 'TransparentColor', + fn: function(doc, div, view) { + view = doc.defaultView; + return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent'); + } + }, + + + { + + identity: 'ComputedStyle', + fn: function(doc, div, view) { + view = doc.defaultView; + return view && view.getComputedStyle; + } + }, + + + { + + identity: 'Svg', + fn: function(doc) { + return !!doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect; + } + }, + + + { + + identity: 'Canvas', + fn: function(doc) { + return !!doc.createElement('canvas').getContext; + }, + early: true + }, + + + { + + identity: 'Vml', + fn: function(doc) { + var d = doc.createElement("div"); + d.innerHTML = ""; + return (d.childNodes.length == 2); + }, + early: true + }, + + + { + + identity: 'Float', + fn: function(doc) { + return 'cssFloat' in doc.documentElement.style; + }, + early: true + }, + + + { + + identity: 'AudioTag', + fn: function(doc) { + return !!doc.createElement('audio').canPlayType; + }, + early: true + }, + + + { + + identity: 'History', + fn: function() { + var history = window.history; + return !!(history && history.pushState); + }, + early: true + }, + + + { + + identity: 'Hashchange', + fn: function() { + + var docMode = document.documentMode; + return 'onhashchange' in window && (docMode === undefined || docMode > 7); + }, + early: true + }, + + + { + + identity: 'CSS3DTransform', + fn: function() { + return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41')); + }, + early: true + }, + + + { + + identity: 'CSS3LinearGradient', + fn: function(doc, div) { + var property = 'background-image:', + webkit = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))', + w3c = 'linear-gradient(left top, black, white)', + moz = '-moz-' + w3c, + ms = '-ms-' + w3c, + opera = '-o-' + w3c, + options = [property + webkit, property + w3c, property + moz, property + ms, property + opera]; + + div.style.cssText = options.join(';'); + + return (("" + div.style.backgroundImage).indexOf('gradient') !== -1) && !Ext.isIE9; + }, + early: true + }, + + + { + + identity: 'CSS3BorderRadius', + fn: function(doc) { + var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'], + i; + for (i = 0; i < domPrefixes.length; i++) { + if (domPrefixes[i] in doc.documentElement.style) { + return true; + } + } + return false; + }, + early: true + }, + + + { + + identity: 'GeoLocation', + fn: function() { + + return (typeof navigator != 'undefined' && 'geolocation' in navigator) || (typeof google != 'undefined' && typeof google.gears != 'undefined'); + }, + early: true + }, + + { + + identity: 'MouseEnterLeave', + fn: function(doc){ + return ('onmouseenter' in doc.documentElement && 'onmouseleave' in doc.documentElement); + }, + early: true + }, + + { + + identity: 'MouseWheel', + fn: function(doc) { + return ('onmousewheel' in doc.documentElement); + }, + early: true + }, + + { + + identity: 'Opacity', + fn: function(doc, div){ + + if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) { + return false; + } + div.style.cssText = 'opacity:0.73'; + return div.style.opacity == '0.73'; + }, + early: true + }, + + { + + identity: 'Placeholder', + fn: function(doc) { + return 'placeholder' in doc.createElement('input'); + }, + early: true + }, + + + { + + identity: 'Direct2DBug', + fn: function(doc) { + return Ext.isString(doc.documentElement.style.msTransformOrigin) && Ext.isIE10m; + }, + early: true + }, + + { + + + identity: 'BoundingClientRect', + fn: function(doc) { + return !Ext.isIEQuirks && 'getBoundingClientRect' in doc.documentElement; + }, + early: true + }, + + { + + identity: 'RotatedBoundingClientRect', + fn: function(doc) { + var body = doc.body, + supports = false, + el = doc.createElement('div'), + style = el.style; + + if (el.getBoundingClientRect) { + style.WebkitTransform = style.MozTransform = + style.OTransform = style.transform = 'rotate(90deg)'; + style.width = '100px'; + style.height = '30px'; + body.appendChild(el); + + supports = el.getBoundingClientRect().height !== 100; + body.removeChild(el); + } + + return supports; + } + }, + { + + identity: 'IncludePaddingInWidthCalculation', + fn: function(doc, div){ + return div.childNodes[1].firstChild.offsetWidth == 210; + } + }, + { + + identity: 'IncludePaddingInHeightCalculation', + fn: function(doc, div){ + return div.childNodes[1].firstChild.offsetHeight == 210; + } + }, + + + { + + identity: 'ArraySort', + fn: function() { + var a = [1,2,3,4,5].sort(function(){ return 0; }); + return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5; + }, + early: true + }, + + { + + identity: 'Range', + fn: function(doc) { + return !!doc.createRange; + }, + early: true + }, + + { + + identity: 'CreateContextualFragment', + fn: function(doc) { + var range = doc.createRange ? doc.createRange() : false; + + return range && !!range.createContextualFragment; + }, + early: true + }, + + + { + + identity: 'WindowOnError', + fn: function () { + + return Ext.isIE || Ext.isGecko || Ext.webKitVersion >= 534.16; + }, + early: true + }, + + + { + + identity: 'TextAreaMaxLength', + fn: function(doc) { + var el = doc.createElement('textarea'); + return ('maxlength' in el); + }, + early: true + }, + + + { + + identity: 'GetPositionPercentage', + fn: function(doc, div){ + return getStyle(div.childNodes[2], 'left') == '10%'; + } + }, + + { + + identity: 'PercentageHeightOverflowBug', + fn: function(doc) { + var hasBug = false, + style, el; + + if (Ext.getScrollbarSize().height) { + + el = doc.createElement('div'); + style = el.style; + style.height = '50px'; + style.width = '50px'; + style.overflow = 'auto'; + style.position = 'absolute'; + + el.innerHTML = [ + '
', + + + + '
', + '
' + ].join(''); + doc.body.appendChild(el); + if (el.firstChild.offsetHeight === 50) { + hasBug = true; + } + doc.body.removeChild(el); + } + + return hasBug; + } + }, + + { + + identity: 'xOriginBug', + fn: function(doc, div) { + div.innerHTML = '
' + + '
' + + '
' + + '
'; + + var outerBox = document.getElementById('b1').getBoundingClientRect(), + b2 = document.getElementById('b2').getBoundingClientRect(), + b3 = document.getElementById('b3').getBoundingClientRect(); + + return (b2.left !== outerBox.left && b3.right !== outerBox.right); + } + }, + + + { + + identity: 'ScrollWidthInlinePaddingBug', + fn: function(doc) { + var hasBug = false, + style, el; + + el = doc.createElement('div'); + style = el.style; + style.height = '50px'; + style.width = '50px'; + style.padding = '10px'; + style.overflow = 'hidden'; + style.position = 'absolute'; + + el.innerHTML = + ''; + doc.body.appendChild(el); + if (el.scrollWidth === 70) { + hasBug = true; + } + doc.body.removeChild(el); + + return hasBug; + } + }, + + + { + identity: 'rtlVertScrollbarOnRight', + fn: function(doc, div) { + div.innerHTML = '
' + + '
' + + '
'; + + var outerBox = div.firstChild, + innerBox = outerBox.firstChild; + + return (innerBox.offsetLeft + innerBox.offsetWidth !== outerBox.offsetLeft + outerBox.offsetWidth); + } + }, + + + { + identity: 'rtlVertScrollbarOverflowBug', + fn: function(doc, div) { + div.innerHTML = '
' + + '
' + + '
'; + + + + + var outerBox = div.firstChild; + return outerBox.clientHeight === outerBox.offsetHeight; + } + } + ] +}; +}()); + +Ext.supports.init(); + + + + + + + +Ext.util.DelayedTask = function(fn, scope, args, cancelOnDelay) { + var me = this, + delay, + call = function() { + clearInterval(me.id); + me.id = null; + fn.apply(scope, args || []); + Ext.EventManager.idleEvent.fire(); + }; + + cancelOnDelay = typeof cancelOnDelay === 'boolean' ? cancelOnDelay : true; + + + me.id = null; + + + me.delay = function(newDelay, newFn, newScope, newArgs) { + if (cancelOnDelay) { + me.cancel(); + } + if (typeof newDelay === 'number') { + delay = newDelay; + } + fn = newFn || fn; + scope = newScope || scope; + args = newArgs || args; + if (!me.id) { + me.id = setInterval(call, delay); + } + }; + + + me.cancel = function() { + if (me.id) { + clearInterval(me.id); + me.id = null; + } + }; +}; + + + + +Ext.define('Ext.util.Event', function() { + var arraySlice = Array.prototype.slice, + arrayInsert = Ext.Array.insert, + toArray = Ext.Array.toArray, + DelayedTask = Ext.util.DelayedTask; + + return { + + + + isEvent: true, + + + suspended: 0, + + noOptions: {}, + + constructor: function(observable, name) { + this.name = name; + this.observable = observable; + this.listeners = []; + }, + + addListener: function(fn, scope, options) { + var me = this, + listeners, listener, priority, isNegativePriority, highestNegativePriorityIndex, + hasNegativePriorityIndex, length, index, i, listenerPriority; + + scope = scope || me.observable; + + if (!fn) { + Ext.Error.raise({ + sourceClass: Ext.getClassName(this.observable), + sourceMethod: "addListener", + msg: "The specified callback function is undefined" + }); + } + + if (!me.isListening(fn, scope)) { + listener = me.createListener(fn, scope, options); + if (me.firing) { + + me.listeners = me.listeners.slice(0); + } + listeners = me.listeners; + index = length = listeners.length; + priority = options && options.priority; + highestNegativePriorityIndex = me._highestNegativePriorityIndex; + hasNegativePriorityIndex = (highestNegativePriorityIndex !== undefined); + if (priority) { + + + isNegativePriority = (priority < 0); + if (!isNegativePriority || hasNegativePriorityIndex) { + + + + + + + for(i = (isNegativePriority ? highestNegativePriorityIndex : 0); i < length; i++) { + + listenerPriority = listeners[i].o ? listeners[i].o.priority||0 : 0; + if (listenerPriority < priority) { + index = i; + break; + } + } + } else { + + + + me._highestNegativePriorityIndex = index; + } + } else if (hasNegativePriorityIndex) { + + + + + index = highestNegativePriorityIndex; + } + + if (!isNegativePriority && index <= highestNegativePriorityIndex) { + me._highestNegativePriorityIndex ++; + } + if (index === length) { + me.listeners[length] = listener; + } else { + arrayInsert(me.listeners, index, [listener]); + } + } + }, + + createListener: function(fn, scope, o) { + scope = scope || this.observable; + + var me = this, + listener = { + fn: fn, + scope: scope, + ev: me + }, + handler = fn; + + + + if (o) { + listener.o = o; + if (o.single) { + handler = me.createSingle(handler, listener, o, scope); + } + if (o.target) { + handler = me.createTargeted(handler, listener, o, scope); + } + if (o.delay) { + handler = me.createDelayed(handler, listener, o, scope); + } + if (o.buffer) { + handler = me.createBuffered(handler, listener, o, scope); + } + } + + listener.fireFn = handler; + return listener; + }, + + findListener: function(fn, scope) { + var listeners = this.listeners, + i = listeners.length, + listener, + s; + + while (i--) { + listener = listeners[i]; + if (listener) { + s = listener.scope; + + + + + if (listener.fn == fn && (s == (scope || this.observable))) { + return i; + } + } + } + + return - 1; + }, + + isListening: function(fn, scope) { + return this.findListener(fn, scope) !== -1; + }, + + removeListener: function(fn, scope) { + var me = this, + index, + listener, + highestNegativePriorityIndex, + k; + index = me.findListener(fn, scope); + if (index != -1) { + listener = me.listeners[index]; + highestNegativePriorityIndex = me._highestNegativePriorityIndex; + + if (me.firing) { + me.listeners = me.listeners.slice(0); + } + + + if (listener.task) { + listener.task.cancel(); + delete listener.task; + } + + + k = listener.tasks && listener.tasks.length; + if (k) { + while (k--) { + listener.tasks[k].cancel(); + } + delete listener.tasks; + } + + + + + me.listeners.splice(index, 1); + + + + if (highestNegativePriorityIndex) { + if (index < highestNegativePriorityIndex) { + me._highestNegativePriorityIndex --; + } else if (index === highestNegativePriorityIndex && index === me.listeners.length) { + delete me._highestNegativePriorityIndex; + } + } + return true; + } + + return false; + }, + + + clearListeners: function() { + var listeners = this.listeners, + i = listeners.length; + + while (i--) { + this.removeListener(listeners[i].fn, listeners[i].scope); + } + }, + + suspend: function() { + ++this.suspended; + }, + + resume: function() { + if (this.suspended) { + --this.suspended; + } + }, + + isSuspended: function() { + return this.suspended > 0; + }, + + fire: function() { + var me = this, + listeners = me.listeners, + count = listeners.length, + i, + args, + listener, + len; + + if (!me.suspended && count > 0) { + me.firing = true; + args = arguments.length ? arraySlice.call(arguments, 0) : [] + len = args.length; + for (i = 0; i < count; i++) { + listener = listeners[i]; + if (listener.o) { + args[len] = listener.o; + } + if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) { + return (me.firing = false); + } + } + } + me.firing = false; + return true; + }, + + createTargeted: function (handler, listener, o, scope) { + return function(){ + if (o.target === arguments[0]){ + handler.apply(scope, arguments); + } + }; + }, + + createBuffered: function (handler, listener, o, scope) { + listener.task = new DelayedTask(); + return function() { + listener.task.delay(o.buffer, handler, scope, toArray(arguments)); + }; + }, + + createDelayed: function (handler, listener, o, scope) { + return function() { + var task = new DelayedTask(); + if (!listener.tasks) { + listener.tasks = []; + } + listener.tasks.push(task); + task.delay(o.delay || 10, handler, scope, toArray(arguments)); + }; + }, + + createSingle: function (handler, listener, o, scope) { + return function() { + var event = listener.ev; + + if (event.removeListener(listener.fn, scope) && event.observable) { + + + event.observable.hasListeners[event.name]--; + } + + return handler.apply(scope, arguments); + }; + } + }; +}); + + + + + + + +Ext.EventManager = new function() { + var EventManager = this, + doc = document, + win = window, + supports = Ext.supports, + escapeRx = /\\/g, + prefix = Ext.baseCSSPrefix, + + supportsAddEventListener = !Ext.isIE9 && 'addEventListener' in doc, + readyEvent, + initExtCss = function() { + + var bd = doc.body || doc.getElementsByTagName('body')[0], + cls = [], + htmlCls = [], + supportsLG = supports.CSS3LinearGradient, + supportsBR = supports.CSS3BorderRadius, + html; + + + if (!Ext.scopeCss) { + cls.push(prefix + 'body'); + } + + if (!bd) { + return false; + } + + html = bd.parentNode; + + function add (c) { + cls.push(prefix + c); + } + + + if (Ext.isIE && Ext.isIE9m) { + add('ie'); + + + + + + + + + + + + + if (Ext.isIE6) { + add('ie6'); + } else { + add('ie7p'); + + if (Ext.isIE7) { + add('ie7'); + } else { + add('ie8p'); + + if (Ext.isIE8) { + add('ie8'); + } else { + add('ie9p'); + + if (Ext.isIE9) { + add('ie9'); + } + } + } + } + + if (Ext.isIE7m) { + add('ie7m'); + } + if (Ext.isIE8m) { + add('ie8m'); + } + if (Ext.isIE9m) { + add('ie9m'); + } + if (Ext.isIE7 || Ext.isIE8) { + add('ie78'); + } + } + + if (Ext.isIE10) { + add('ie10'); + } + + if (Ext.isGecko) { + add('gecko'); + if (Ext.isGecko3) { + add('gecko3'); + } + if (Ext.isGecko4) { + add('gecko4'); + } + if (Ext.isGecko5) { + add('gecko5'); + } + } + if (Ext.isOpera) { + add('opera'); + } + if (Ext.isWebKit) { + add('webkit'); + } + if (Ext.isSafari) { + add('safari'); + if (Ext.isSafari2) { + add('safari2'); + } + if (Ext.isSafari3) { + add('safari3'); + } + if (Ext.isSafari4) { + add('safari4'); + } + if (Ext.isSafari5) { + add('safari5'); + } + if (Ext.isSafari5_0) { + add('safari5_0'); + } + } + if (Ext.isChrome) { + add('chrome'); + } + if (Ext.isMac) { + add('mac'); + } + if (Ext.isLinux) { + add('linux'); + } + if (!supportsBR) { + add('nbr'); + } + if (!supportsLG) { + add('nlg'); + } + + + if (html) { + if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) { + Ext.isBorderBox = false; + } + else { + Ext.isBorderBox = true; + } + + if(!Ext.isBorderBox) { + htmlCls.push(prefix + 'content-box'); + } + if (Ext.isStrict) { + htmlCls.push(prefix + 'strict'); + } else { + htmlCls.push(prefix + 'quirks'); + } + Ext.fly(html, '_internal').addCls(htmlCls); + } + + Ext.fly(bd, '_internal').addCls(cls); + return true; + }; + + Ext.apply(EventManager, { + + hasBoundOnReady: false, + + + hasFiredReady: false, + + + deferReadyEvent : 1, + + + onReadyChain : [], + + + readyEvent: + (function () { + readyEvent = new Ext.util.Event(); + readyEvent.fire = function () { + Ext._beforeReadyTime = Ext._beforeReadyTime || new Date().getTime(); + readyEvent.self.prototype.fire.apply(readyEvent, arguments); + Ext._afterReadytime = new Date().getTime(); + }; + return readyEvent; + }()), + + + idleEvent: new Ext.util.Event(), + + + isReadyPaused: function(){ + return (/[?&]ext-pauseReadyFire\b/i.test(location.search) && !Ext._continueFireReady); + }, + + + bindReadyEvent: function() { + if (EventManager.hasBoundOnReady) { + return; + } + + + if ( doc.readyState == 'complete' ) { + EventManager.onReadyEvent({ + type: doc.readyState || 'body' + }); + } else { + doc.addEventListener('DOMContentLoaded', EventManager.onReadyEvent, false); + win.addEventListener('load', EventManager.onReadyEvent, false); + EventManager.hasBoundOnReady = true; + } + }, + + onReadyEvent : function(e) { + if (e && e.type) { + EventManager.onReadyChain.push(e.type); + } + + if (EventManager.hasBoundOnReady) { + doc.removeEventListener('DOMContentLoaded', EventManager.onReadyEvent, false); + win.removeEventListener('load', EventManager.onReadyEvent, false); + } + + if (!Ext.isReady) { + EventManager.fireDocReady(); + } + }, + + + fireDocReady: function() { + if (!Ext.isReady) { + Ext._readyTime = new Date().getTime(); + Ext.isReady = true; + + supports.init(); + EventManager.onWindowUnload(); + readyEvent.onReadyChain = EventManager.onReadyChain; + + if (Ext.isNumber(EventManager.deferReadyEvent)) { + Ext.Function.defer(EventManager.fireReadyEvent, EventManager.deferReadyEvent); + EventManager.hasDocReadyTimer = true; + } else { + EventManager.fireReadyEvent(); + } + } + }, + + + fireReadyEvent: function() { + + + + EventManager.hasDocReadyTimer = false; + EventManager.isFiring = true; + + + + + while (readyEvent.listeners.length && !EventManager.isReadyPaused()) { + readyEvent.fire(); + } + EventManager.isFiring = false; + EventManager.hasFiredReady = true; + EventManager.idleEvent.fire(); + }, + + + onDocumentReady: function(fn, scope, options) { + options = options || {}; + + options.single = true; + readyEvent.addListener(fn, scope, options); + + + + if (!(EventManager.isFiring || EventManager.hasDocReadyTimer)) { + if (Ext.isReady) { + EventManager.fireReadyEvent(); + } else { + EventManager.bindReadyEvent(); + } + } + }, + + + + + stoppedMouseDownEvent: new Ext.util.Event(), + + + propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/, + + + getId : function(element) { + var id; + + element = Ext.getDom(element); + + if (element === doc) { + id = Ext.documentId; + + + } else if (element == win) { + id = Ext.windowId; + } + else { + id = Ext.id(element); + } + + if (!Ext.cache[id]) { + Ext.addCacheEntry(id, null, element); + } + + return id; + }, + + + prepareListenerConfig: function(element, config, isRemove) { + var propRe = EventManager.propRe, + key, value, args; + + + for (key in config) { + if (config.hasOwnProperty(key)) { + + if (!propRe.test(key)) { + value = config[key]; + + + if (typeof value == 'function') { + + args = [element, key, value, config.scope, config]; + } else { + + args = [element, key, value.fn, value.scope, value]; + } + + if (isRemove) { + EventManager.removeListener.apply(EventManager, args); + } else { + EventManager.addListener.apply(EventManager, args); + } + } + } + } + }, + + mouseEnterLeaveRe: /mouseenter|mouseleave/, + + + normalizeEvent: function(eventName, fn) { + if (EventManager.mouseEnterLeaveRe.test(eventName) && !supports.MouseEnterLeave) { + if (fn) { + fn = Ext.Function.createInterceptor(fn, EventManager.contains); + } + eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout'; + } else if (eventName == 'mousewheel' && !supports.MouseWheel && !Ext.isOpera) { + eventName = 'DOMMouseScroll'; + } + return { + eventName: eventName, + fn: fn + }; + }, + + + contains: function(event) { + event = event.browserEvent || event; + var parent = event.currentTarget, + child = EventManager.getRelatedTarget(event); + + if (parent && parent.firstChild) { + while (child) { + if (child === parent) { + return false; + } + child = child.parentNode; + if (child && (child.nodeType != 1)) { + child = null; + } + } + } + return true; + }, + + + addListener: function(element, eventName, fn, scope, options) { + + if (typeof eventName !== 'string') { + EventManager.prepareListenerConfig(element, eventName); + return; + } + + var dom = element.dom || Ext.getDom(element), + hasAddEventListener, bind, wrap, cache, id, cacheItem, capture; + + if (typeof fn === 'string') { + fn = Ext.resolveMethod(fn, scope || element); + } + + if (!fn) { + Ext.Error.raise({ + sourceClass: 'Ext.EventManager', + sourceMethod: 'addListener', + targetElement: element, + eventName: eventName, + msg: 'Error adding "' + eventName + '\" listener. The handler function is undefined.' + }); + } + + + options = options || {}; + + bind = EventManager.normalizeEvent(eventName, fn); + wrap = EventManager.createListenerWrap(dom, eventName, bind.fn, scope, options); + + + cache = EventManager.getEventListenerCache(element.dom ? element : dom, eventName); + eventName = bind.eventName; + + + hasAddEventListener = supportsAddEventListener || (Ext.isIE9 && !dom.attachEvent); + + if (!hasAddEventListener) { + id = EventManager.normalizeId(dom); + + + if (id) { + cacheItem = Ext.cache[id][eventName]; + if (cacheItem && cacheItem.firing) { + + + + + + + cache = EventManager.cloneEventListenerCache(dom, eventName); + } + } + } + + capture = !!options.capture; + cache.push({ + fn: fn, + wrap: wrap, + scope: scope, + capture: capture + }); + + if (!hasAddEventListener) { + + + if (cache.length === 1) { + id = EventManager.normalizeId(dom, true); + fn = Ext.Function.bind(EventManager.handleSingleEvent, EventManager, [id, eventName], true); + Ext.cache[id][eventName] = { + firing: false, + fn: fn + }; + dom.attachEvent('on' + eventName, fn); + } + } else { + dom.addEventListener(eventName, wrap, capture); + } + + if (dom == doc && eventName == 'mousedown') { + EventManager.stoppedMouseDownEvent.addListener(wrap); + } + }, + + + + normalizeId: function(dom, force) { + var id; + if (dom === doc) { + id = Ext.documentId; + + } else if (dom == win) { + id = Ext.windowId; + } else { + id = dom.id; + } + if (!id && force) { + id = EventManager.getId(dom); + } + return id; + }, + + handleSingleEvent: function(e, id, eventName) { + + + + var listenerCache = EventManager.getEventListenerCache(id, eventName), + attachItem = Ext.cache[id][eventName], + len, i; + + + + + if (attachItem.firing) { + return; + } + + attachItem.firing = true; + for (i = 0, len = listenerCache.length; i < len; ++i) { + listenerCache[i].wrap(e); + } + attachItem.firing = false; + + }, + + + removeListener : function(element, eventName, fn, scope) { + + if (typeof eventName !== 'string') { + EventManager.prepareListenerConfig(element, eventName, true); + return; + } + + var dom = Ext.getDom(element), + id, el = element.dom ? element : Ext.get(dom), + cache = EventManager.getEventListenerCache(el, eventName), + bindName = EventManager.normalizeEvent(eventName).eventName, + i = cache.length, j, cacheItem, hasRemoveEventListener, + listener, wrap; + + if (!dom) { + return; + } + + + hasRemoveEventListener = supportsAddEventListener || (Ext.isIE9 && !dom.detachEvent); + + if (typeof fn === 'string') { + fn = Ext.resolveMethod(fn, scope || element); + } + + while (i--) { + listener = cache[i]; + + if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) { + wrap = listener.wrap; + + + if (wrap.task) { + clearTimeout(wrap.task); + delete wrap.task; + } + + + j = wrap.tasks && wrap.tasks.length; + if (j) { + while (j--) { + clearTimeout(wrap.tasks[j]); + } + delete wrap.tasks; + } + + if (!hasRemoveEventListener) { + + + id = EventManager.normalizeId(dom, true); + cacheItem = Ext.cache[id][bindName]; + if (cacheItem && cacheItem.firing) { + + cache = EventManager.cloneEventListenerCache(dom, bindName); + } + + if (cache.length === 1) { + fn = cacheItem.fn; + delete Ext.cache[id][bindName]; + dom.detachEvent('on' + bindName, fn); + } + } else { + dom.removeEventListener(bindName, wrap, listener.capture); + } + + if (wrap && dom == doc && eventName == 'mousedown') { + EventManager.stoppedMouseDownEvent.removeListener(wrap); + } + + + Ext.Array.erase(cache, i, 1); + } + } + }, + + + removeAll : function(element) { + var id = (typeof element === 'string') ? element : element.id, + cache, events, eventName; + + + if (id && (cache = Ext.cache[id])) { + events = cache.events; + + for (eventName in events) { + if (events.hasOwnProperty(eventName)) { + EventManager.removeListener(element, eventName); + } + } + cache.events = {}; + } + }, + + + purgeElement : function(element, eventName) { + var dom = Ext.getDom(element), + i = 0, len, childNodes; + + if (eventName) { + EventManager.removeListener(element, eventName); + } else { + EventManager.removeAll(element); + } + + if (dom && dom.childNodes) { + childNodes = dom.childNodes; + for (len = childNodes.length; i < len; i++) { + EventManager.purgeElement(childNodes[i], eventName); + } + } + }, + + + createListenerWrap : function(dom, ename, fn, scope, options) { + options = options || {}; + + var gen, wrap = function(e, args) { + var f; + + if (!gen) { + f = ['if(!' + Ext.name + ') {return;}']; + + if (options.buffer || options.delay || options.freezeEvent) { + if (options.freezeEvent) { + + + f.push('e = X.EventObject.setEvent(e);'); + } + f.push('e = new X.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');'); + } else { + f.push('e = X.EventObject.setEvent(e);'); + } + + if (options.delegate) { + + + f.push('var result, t = e.getTarget("' + (options.delegate + '').replace(escapeRx, '\\\\') + '", this);'); + f.push('if(!t) {return;}'); + } else { + f.push('var t = e.target, result;'); + } + + if (options.target) { + f.push('if(e.target !== options.target) {return;}'); + } + + if (options.stopEvent) { + f.push('e.stopEvent();'); + } else { + if(options.preventDefault) { + f.push('e.preventDefault();'); + } + if(options.stopPropagation) { + f.push('e.stopPropagation();'); + } + } + + if (options.normalized === false) { + f.push('e = e.browserEvent;'); + } + + if (options.buffer) { + f.push('(wrap.task && clearTimeout(wrap.task));'); + f.push('wrap.task = setTimeout(function() {'); + } + + if (options.delay) { + f.push('wrap.tasks = wrap.tasks || [];'); + f.push('wrap.tasks.push(setTimeout(function() {'); + } + + + f.push('result = fn.call(scope || dom, e, t, options);'); + + if (options.single) { + f.push('evtMgr.removeListener(dom, ename, fn, scope);'); + } + + + + if (ename !== 'mousemove' && ename !== 'unload') { + f.push('if (evtMgr.idleEvent.listeners.length) {'); + f.push('evtMgr.idleEvent.fire();'); + f.push('}'); + } + + if (options.delay) { + f.push('}, ' + options.delay + '));'); + } + + if (options.buffer) { + f.push('}, ' + options.buffer + ');'); + } + f.push('return result;'); + + gen = Ext.cacheableFunctionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', 'X', 'evtMgr', f.join('\n')); + } + + return gen.call(dom, e, options, fn, scope, ename, dom, wrap, args, Ext, EventManager); + }; + return wrap; + }, + + + getEventCache: function(element) { + var elementCache, eventCache, id; + + if (!element) { + return []; + } + + if (element.$cache) { + elementCache = element.$cache; + } else { + + if (typeof element === 'string') { + id = element; + } else { + id = EventManager.getId(element); + } + elementCache = Ext.cache[id]; + } + eventCache = elementCache.events || (elementCache.events = {}); + return eventCache; + }, + + + getEventListenerCache : function(element, eventName) { + var eventCache = EventManager.getEventCache(element); + return eventCache[eventName] || (eventCache[eventName] = []); + }, + + + cloneEventListenerCache: function(element, eventName){ + var eventCache = EventManager.getEventCache(element), + out; + + if (eventCache[eventName]) { + out = eventCache[eventName].slice(0); + } else { + out = []; + } + eventCache[eventName] = out; + return out; + }, + + + mouseLeaveRe: /(mouseout|mouseleave)/, + mouseEnterRe: /(mouseover|mouseenter)/, + + + stopEvent: function(event) { + EventManager.stopPropagation(event); + EventManager.preventDefault(event); + }, + + + stopPropagation: function(event) { + event = event.browserEvent || event; + if (event.stopPropagation) { + event.stopPropagation(); + } else { + event.cancelBubble = true; + } + }, + + + preventDefault: function(event) { + event = event.browserEvent || event; + if (event.preventDefault) { + event.preventDefault(); + } else { + event.returnValue = false; + + try { + + if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) { + event.keyCode = -1; + } + } catch (e) { + + } + } + }, + + + getRelatedTarget: function(event) { + event = event.browserEvent || event; + var target = event.relatedTarget; + if (!target) { + if (EventManager.mouseLeaveRe.test(event.type)) { + target = event.toElement; + } else if (EventManager.mouseEnterRe.test(event.type)) { + target = event.fromElement; + } + } + return EventManager.resolveTextNode(target); + }, + + + getPageX: function(event) { + return EventManager.getPageXY(event)[0]; + }, + + + getPageY: function(event) { + return EventManager.getPageXY(event)[1]; + }, + + + getPageXY: function(event) { + event = event.browserEvent || event; + var x = event.pageX, + y = event.pageY, + docEl = doc.documentElement, + body = doc.body; + + + if (!x && x !== 0) { + x = event.clientX + (docEl && docEl.scrollLeft || body && body.scrollLeft || 0) - (docEl && docEl.clientLeft || body && body.clientLeft || 0); + y = event.clientY + (docEl && docEl.scrollTop || body && body.scrollTop || 0) - (docEl && docEl.clientTop || body && body.clientTop || 0); + } + return [x, y]; + }, + + + getTarget: function(event) { + event = event.browserEvent || event; + return EventManager.resolveTextNode(event.target || event.srcElement); + }, + + + + + + resolveTextNode: Ext.isGecko ? + function(node) { + if (node) { + + var s = HTMLElement.prototype.toString.call(node); + if (s !== '[xpconnect wrapped native prototype]' && s !== '[object XULElement]') { + return node.nodeType == 3 ? node.parentNode: node; + } + } + } + : + function(node) { + return node && node.nodeType == 3 ? node.parentNode: node; + }, + + + + + curWidth: 0, + curHeight: 0, + + + onWindowResize: function(fn, scope, options) { + var resize = EventManager.resizeEvent; + + if (!resize) { + EventManager.resizeEvent = resize = new Ext.util.Event(); + EventManager.on(win, 'resize', EventManager.fireResize, null, {buffer: 100}); + } + resize.addListener(fn, scope, options); + }, + + + fireResize: function() { + var w = Ext.Element.getViewWidth(), + h = Ext.Element.getViewHeight(); + + + if (EventManager.curHeight != h || EventManager.curWidth != w) { + EventManager.curHeight = h; + EventManager.curWidth = w; + EventManager.resizeEvent.fire(w, h); + } + }, + + + removeResizeListener: function(fn, scope) { + var resize = EventManager.resizeEvent; + if (resize) { + resize.removeListener(fn, scope); + } + }, + + + onWindowUnload: function(fn, scope, options) { + var unload = EventManager.unloadEvent; + + if (!unload) { + EventManager.unloadEvent = unload = new Ext.util.Event(); + EventManager.addListener(win, 'unload', EventManager.fireUnload); + } + if (fn) { + unload.addListener(fn, scope, options); + } + }, + + + fireUnload: function() { + + try { + + doc = win = undefined; + + var gridviews, i, ln, + el, cache; + + EventManager.unloadEvent.fire(); + + if (Ext.isGecko3) { + gridviews = Ext.ComponentQuery.query('gridview'); + i = 0; + ln = gridviews.length; + for (; i < ln; i++) { + gridviews[i].scrollToTop(); + } + } + + cache = Ext.cache; + + for (el in cache) { + if (cache.hasOwnProperty(el)) { + EventManager.removeAll(el); + } + } + } catch(e) { + } + }, + + + removeUnloadListener: function(fn, scope) { + var unload = EventManager.unloadEvent; + if (unload) { + unload.removeListener(fn, scope); + } + }, + + + useKeyDown: Ext.isWebKit ? + parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 : + !((Ext.isGecko && !Ext.isWindows) || (Ext.isOpera && Ext.operaVersion < 12)), + + + getKeyEvent: function() { + return EventManager.useKeyDown ? 'keydown' : 'keypress'; + } + }); + + + if(!supportsAddEventListener && document.attachEvent) { + Ext.apply( EventManager, { + + + + pollScroll : function() { + var scrollable = true; + + try { + document.documentElement.doScroll('left'); + } catch(e) { + scrollable = false; + } + + + if (scrollable && document.body) { + EventManager.onReadyEvent({ + type:'doScroll' + }); + } else { + + EventManager.scrollTimeout = setTimeout(EventManager.pollScroll, 20); + } + + return scrollable; + }, + + + scrollTimeout: null, + + + readyStatesRe : /complete/i, + + + checkReadyState: function() { + var state = document.readyState; + + if (EventManager.readyStatesRe.test(state)) { + EventManager.onReadyEvent({ + type: state + }); + } + }, + + bindReadyEvent: function() { + var topContext = true; + + if (EventManager.hasBoundOnReady) { + return; + } + + + try { + topContext = window.frameElement === undefined; + } catch(e) { + + + topContext = false; + } + + if (!topContext || !doc.documentElement.doScroll) { + EventManager.pollScroll = Ext.emptyFn; + } + + + if (EventManager.pollScroll() === true) { + return; + } + + + if (doc.readyState == 'complete' ) { + EventManager.onReadyEvent({type: 'already ' + (doc.readyState || 'body') }); + } else { + doc.attachEvent('onreadystatechange', EventManager.checkReadyState); + window.attachEvent('onload', EventManager.onReadyEvent); + EventManager.hasBoundOnReady = true; + } + }, + + onReadyEvent : function(e) { + if (e && e.type) { + EventManager.onReadyChain.push(e.type); + } + + if (EventManager.hasBoundOnReady) { + document.detachEvent('onreadystatechange', EventManager.checkReadyState); + window.detachEvent('onload', EventManager.onReadyEvent); + } + + if (Ext.isNumber(EventManager.scrollTimeout)) { + clearTimeout(EventManager.scrollTimeout); + delete EventManager.scrollTimeout; + } + + if (!Ext.isReady) { + EventManager.fireDocReady(); + } + }, + + + onReadyChain : [] + }); + } + + + + Ext.onReady = function(fn, scope, options) { + Ext.Loader.onReady(fn, scope, true, options); + }; + + + Ext.onDocumentReady = EventManager.onDocumentReady; + + + EventManager.on = EventManager.addListener; + + + EventManager.un = EventManager.removeListener; + + Ext.onReady(initExtCss); +}; + +Ext.setVersion("ext-theme-base", "4.2.3.1477"); +Ext.setVersion("ext-theme-neptune", "4.2.3.1477"); +Ext.setVersion("ext-theme-neutral", "4.2.3.1477"); +Ext.setVersion("font-awesome", "4.5.0"); +Ext.setVersion("rapture-theme", "1.0.0"); + + + + +Ext.define('Ext.util.Observable', function(Observable) { + + var emptyFn = Ext.emptyFn, + emptyArray = [], + arrayProto = Array.prototype, + arraySlice = arrayProto.slice, + ExtEvent = Ext.util.Event, + + ListenerRemover = function(observable) { + + + if (observable instanceof ListenerRemover) { + return observable; + } + + this.observable = observable; + + + + if (arguments[1].isObservable) { + this.managedListeners = true; + } + this.args = arraySlice.call(arguments, 1); + }; + + ListenerRemover.prototype.destroy = function() { + this.observable[this.managedListeners ? 'mun' : 'un'].apply(this.observable, this.args); + }; + + return { + + + + + + statics: { + + releaseCapture: function(o) { + o.fireEventArgs = this.prototype.fireEventArgs; + }, + + + capture: function(o, fn, scope) { + + + + + var newFn = function(eventName, args) { + return fn.apply(scope, [eventName].concat(args)); + } + + this.captureArgs(o, newFn, scope); + }, + + + captureArgs: function(o, fn, scope) { + o.fireEventArgs = Ext.Function.createInterceptor(o.fireEventArgs, fn, scope); + }, + + + observe: function(cls, listeners) { + if (cls) { + if (!cls.isObservable) { + Ext.applyIf(cls, new this()); + this.captureArgs(cls.prototype, cls.fireEventArgs, cls); + } + if (Ext.isObject(listeners)) { + cls.on(listeners); + } + } + return cls; + }, + + + prepareClass: function (T, mixin) { + + + + + + + if (!T.HasListeners) { + + + + var HasListeners = function () {}, + SuperHL = T.superclass.HasListeners || (mixin && mixin.HasListeners) || + Observable.HasListeners; + + + T.prototype.HasListeners = T.HasListeners = HasListeners; + + + + HasListeners.prototype = T.hasListeners = new SuperHL(); + } + } + }, + + + + + + + isObservable: true, + + + eventsSuspended: 0, + + + + constructor: function(config) { + var me = this; + + Ext.apply(me, config); + + + if (!me.hasListeners) { + me.hasListeners = new me.HasListeners(); + } + + me.events = me.events || {}; + if (me.listeners) { + me.on(me.listeners); + me.listeners = null; + } + + if (me.bubbleEvents) { + me.enableBubble(me.bubbleEvents); + } + }, + + onClassExtended: function (T) { + if (!T.HasListeners) { + + + Observable.prepareClass(T); + } + }, + + + + eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|destroyable|vertical|horizontal|freezeEvent|priority)$/, + + + addManagedListener: function(item, ename, fn, scope, options, noDestroy) { + var me = this, + managedListeners = me.managedListeners = me.managedListeners || [], + config, passedOptions; + + if (typeof ename !== 'string') { + + + + + passedOptions = arguments.length > 4 ? options : ename; + + options = ename; + for (ename in options) { + if (options.hasOwnProperty(ename)) { + config = options[ename]; + if (!me.eventOptionsRe.test(ename)) { + + + me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope || scope, config.fn ? config : passedOptions, true); + } + } + } + if (options && options.destroyable) { + return new ListenerRemover(me, item, options); + } + } + else { + if (typeof fn === 'string') { + scope = scope || me; + fn = Ext.resolveMethod(fn, scope); + } + + if (fn !== emptyFn) { + managedListeners.push({ + item: item, + ename: ename, + fn: fn, + scope: scope, + options: options + }); + + item.on(ename, fn, scope, options); + + + if (!noDestroy && options && options.destroyable) { + return new ListenerRemover(me, item, ename, fn, scope); + } + } + } + }, + + + removeManagedListener: function(item, ename, fn, scope) { + var me = this, + options, + config, + managedListeners, + length, + i; + + if (typeof ename !== 'string') { + options = ename; + for (ename in options) { + if (options.hasOwnProperty(ename)) { + config = options[ename]; + if (!me.eventOptionsRe.test(ename)) { + me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope || scope); + } + } + } + } else { + managedListeners = me.managedListeners ? me.managedListeners.slice() : []; + + if (typeof fn === 'string') { + scope = scope || me; + fn = Ext.resolveMethod(fn, scope); + } + + for (i = 0, length = managedListeners.length; i < length; i++) { + me.removeManagedListenerItem(false, managedListeners[i], item, ename, fn, scope); + } + } + }, + + + fireEvent: function(eventName) { + return this.fireEventArgs(eventName, arraySlice.call(arguments, 1)); + }, + + + fireEventArgs: function(eventName, args) { + eventName = eventName.toLowerCase(); + var me = this, + events = me.events, + event = events && events[eventName], + ret = true; + + + + if (event && me.hasListeners[eventName]) { + ret = me.continueFireEvent(eventName, args || emptyArray, event.bubble); + } + return ret; + }, + + + continueFireEvent: function(eventName, args, bubbles) { + var target = this, + queue, event, + ret = true; + + do { + if (target.eventsSuspended) { + if ((queue = target.eventQueue)) { + queue.push([eventName, args, bubbles]); + } + return ret; + } else { + event = target.events[eventName]; + + + if (event && event !== true) { + if ((ret = event.fire.apply(event, args)) === false) { + break; + } + } + } + } while (bubbles && (target = target.getBubbleParent())); + return ret; + }, + + + getBubbleParent: function() { + var me = this, parent = me.getBubbleTarget && me.getBubbleTarget(); + if (parent && parent.isObservable) { + return parent; + } + return null; + }, + + + addListener: function(ename, fn, scope, options) { + var me = this, + config, event, + prevListenerCount = 0; + + + if (typeof ename !== 'string') { + options = ename; + for (ename in options) { + if (options.hasOwnProperty(ename)) { + config = options[ename]; + if (!me.eventOptionsRe.test(ename)) { + + me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options); + } + } + } + if (options && options.destroyable) { + return new ListenerRemover(me, options); + } + } + + else { + ename = ename.toLowerCase(); + event = me.events[ename]; + if (event && event.isEvent) { + prevListenerCount = event.listeners.length; + } else { + me.events[ename] = event = new ExtEvent(me, ename); + } + if (!fn) { + Ext.Error.raise('No function passed for event ' + me.$className + '.' + ename); + } + + + if (typeof fn === 'string') { + scope = scope || me; + fn = Ext.resolveMethod(fn, scope); + } + + if (fn !== emptyFn) { + event.addListener(fn, scope, options); + + + + if (event.listeners.length !== prevListenerCount) { + me.hasListeners._incr_(ename); + } + if (options && options.destroyable) { + return new ListenerRemover(me, ename, fn, scope, options); + } + } + } + }, + + + removeListener: function(ename, fn, scope) { + var me = this, + config, + event, + options; + + if (typeof ename !== 'string') { + options = ename; + for (ename in options) { + if (options.hasOwnProperty(ename)) { + config = options[ename]; + if (!me.eventOptionsRe.test(ename)) { + me.removeListener(ename, config.fn || config, config.scope || options.scope); + } + } + } + } else { + ename = ename.toLowerCase(); + event = me.events[ename]; + if (event && event.isEvent) { + if (typeof fn === 'string') { + scope = scope || me; + fn = Ext.resolveMethod(fn, scope); + } + + if (event.removeListener(fn, scope)) { + me.hasListeners._decr_(ename); + } + } + } + }, + + + clearListeners: function() { + var events = this.events, + hasListeners = this.hasListeners, + event, + key; + + for (key in events) { + if (events.hasOwnProperty(key)) { + event = events[key]; + if (event.isEvent) { + delete hasListeners[key]; + event.clearListeners(); + } + } + } + + this.clearManagedListeners(); + }, + + purgeListeners : function() { + if (Ext.global.console) { + Ext.global.console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.'); + } + return this.clearListeners.apply(this, arguments); + }, + + + clearManagedListeners : function() { + var managedListeners = this.managedListeners || [], + i = 0, + len = managedListeners.length; + + for (; i < len; i++) { + this.removeManagedListenerItem(true, managedListeners[i]); + } + + this.managedListeners = []; + }, + + + removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){ + if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) { + managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope); + if (!isClear) { + Ext.Array.remove(this.managedListeners, managedListener); + } + } + }, + + purgeManagedListeners : function() { + if (Ext.global.console) { + Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.'); + } + return this.clearManagedListeners.apply(this, arguments); + }, + + + addEvents: function(o) { + var me = this, + events = me.events || (me.events = {}), + arg, args, i; + + if (typeof o == 'string') { + for (args = arguments, i = args.length; i--; ) { + arg = args[i]; + if (!events[arg]) { + events[arg] = true; + } + } + } else { + Ext.applyIf(me.events, o); + } + }, + + + hasListener: function(ename) { + return !!this.hasListeners[ename.toLowerCase()]; + }, + + + isSuspended: function(event) { + var suspended = this.eventsSuspended > 0; + if (!suspended && event) { + event = this.events[event]; + if (event && event.isEvent) { + return event.isSuspended(); + } + } + return suspended; + }, + + + suspendEvents: function(queueSuspended) { + this.eventsSuspended += 1; + if (queueSuspended && !this.eventQueue) { + this.eventQueue = []; + } + }, + + + suspendEvent: function(eventName) { + var len = arguments.length, + events = this.events, + i, event, ename; + + for (i = 0; i < len; i++) { + ename = arguments[i]; + event = events[ename]; + if (!event || typeof event == 'boolean') { + events[ename] = event = new ExtEvent(this, ename); + } + + + event.suspend(); + } + }, + + + resumeEvent: function() { + var len = arguments.length, + i, event; + + for (i = 0; i < len; i++) { + + + event = this.events[arguments[i]]; + if (event && event.resume) { + event.resume(); + } + } + }, + + + resumeEvents: function() { + var me = this, + queued = me.eventQueue, + qLen, q; + + if (me.eventsSuspended && ! --me.eventsSuspended) { + delete me.eventQueue; + + if (queued) { + qLen = queued.length; + for (q = 0; q < qLen; q++) { + me.continueFireEvent.apply(me, queued[q]); + } + } + } + }, + + + relayEvents : function(origin, events, prefix) { + var me = this, + len = events.length, + i = 0, + oldName, + relayers = {}; + + for (; i < len; i++) { + oldName = events[i]; + + + relayers[oldName] = me.createRelayer(prefix ? prefix + oldName : oldName); + } + + + + me.mon(origin, relayers, null, null, undefined); + + + return new ListenerRemover(me, origin, relayers); + }, + + + createRelayer: function(newName, beginEnd) { + var me = this; + return function() { + return me.fireEventArgs.call(me, newName, beginEnd ? arraySlice.apply(arguments, beginEnd) : arguments); + }; + }, + + + enableBubble: function(eventNames) { + if (eventNames) { + var me = this, + names = (typeof eventNames == 'string') ? arguments : eventNames, + length = names.length, + events = me.events, + ename, event, i; + + for (i = 0; i < length; ++i) { + ename = names[i].toLowerCase(); + event = events[ename]; + + if (!event || typeof event == 'boolean') { + events[ename] = event = new ExtEvent(me, ename); + } + + + + me.hasListeners._incr_(ename); + + event.bubble = true; + } + } + } + }; +}, function() { + var Observable = this, + proto = Observable.prototype, + HasListeners = function () {}, + prepareMixin = function (T) { + if (!T.HasListeners) { + var proto = T.prototype; + + + Observable.prepareClass(T, this); + + + + T.onExtended(function (U) { + Ext.classSystemMonitor && Ext.classSystemMonitor('extend mixin', arguments); + + Observable.prepareClass(U); + }); + + + + if (proto.onClassMixedIn) { + + Ext.override(T, { + onClassMixedIn: function (U) { + prepareMixin.call(this, U); + this.callParent(arguments); + } + }); + } else { + + proto.onClassMixedIn = function (U) { + prepareMixin.call(this, U); + }; + } + } + }, + globalEvents; + + HasListeners.prototype = { + + _decr_: function (ev) { + if (! --this[ev]) { + + + + delete this[ev]; + } + }, + _incr_: function (ev) { + if (this.hasOwnProperty(ev)) { + + ++this[ev]; + } else { + + + this[ev] = 1; + } + } + }; + + proto.HasListeners = Observable.HasListeners = HasListeners; + + Observable.createAlias({ + + on: 'addListener', + + un: 'removeListener', + + mon: 'addManagedListener', + + mun: 'removeManagedListener' + }); + + + Observable.observeClass = Observable.observe; + + + Ext.globalEvents = globalEvents = new Observable({ + events: { + idle: Ext.EventManager.idleEvent, + ready: Ext.EventManager.readyEvent + } + }); + + + Ext.on = function() { + return globalEvents.addListener.apply(globalEvents, arguments); + }; + + + Ext.un = function() { + return globalEvents.removeListener.apply(globalEvents, arguments); + }; + + + + + function getMethodEvent(method){ + var e = (this.methodEvents = this.methodEvents || {})[method], + returnValue, + v, + cancel, + obj = this, + makeCall; + + if (!e) { + this.methodEvents[method] = e = {}; + e.originalFn = this[method]; + e.methodName = method; + e.before = []; + e.after = []; + + makeCall = function(fn, scope, args){ + if((v = fn.apply(scope || obj, args)) !== undefined){ + if (typeof v == 'object') { + if(v.returnValue !== undefined){ + returnValue = v.returnValue; + }else{ + returnValue = v; + } + cancel = !!v.cancel; + } + else + if (v === false) { + cancel = true; + } + else { + returnValue = v; + } + } + }; + + this[method] = function(){ + var args = Array.prototype.slice.call(arguments, 0), + b, i, len; + returnValue = v = undefined; + cancel = false; + + for(i = 0, len = e.before.length; i < len; i++){ + b = e.before[i]; + makeCall(b.fn, b.scope, args); + if (cancel) { + return returnValue; + } + } + + if((v = e.originalFn.apply(obj, args)) !== undefined){ + returnValue = v; + } + + for(i = 0, len = e.after.length; i < len; i++){ + b = e.after[i]; + makeCall(b.fn, b.scope, args); + if (cancel) { + return returnValue; + } + } + return returnValue; + }; + } + return e; + } + + Ext.apply(proto, { + onClassMixedIn: prepareMixin, + + + + + beforeMethod : function(method, fn, scope){ + getMethodEvent.call(this, method).before.push({ + fn: fn, + scope: scope + }); + }, + + + afterMethod : function(method, fn, scope){ + getMethodEvent.call(this, method).after.push({ + fn: fn, + scope: scope + }); + }, + + removeMethodListener: function(method, fn, scope){ + var e = this.getMethodEvent(method), + i, len; + for(i = 0, len = e.before.length; i < len; i++){ + if(e.before[i].fn == fn && e.before[i].scope == scope){ + Ext.Array.erase(e.before, i, 1); + return; + } + } + for(i = 0, len = e.after.length; i < len; i++){ + if(e.after[i].fn == fn && e.after[i].scope == scope){ + Ext.Array.erase(e.after, i, 1); + return; + } + } + }, + + toggleEventLogging: function(toggle) { + Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.log(en, arguments); + } + }); + } + }); +}); + + + + + + + +Ext.define('Ext.EventObjectImpl', { + + + + 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 scale; + + if (Ext.isGecko) { + + scale = 3; + } else if (Ext.isMac) { + + + + + if (Ext.isSafari && Ext.webKitVersion >= 532.0) { + + + + + + + scale = 120; + } else { + + + scale = 12; + } + + + + + + scale *= 3; + } else { + + scale = 120; + } + + return scale; + }()), + + + clickRe: /(dbl)?click/, + + safariKeys: { + 3: 13, + 63234: 37, + 63235: 39, + 63232: 38, + 63233: 40, + 63276: 33, + 63277: 34, + 63272: 46, + 63273: 36, + 63275: 35 + }, + + btnMap: Ext.isIE9m ? { + 1: 0, + 4: 1, + 2: 2 + } : { + 0: 0, + 1: 1, + 2: 2 + }, + + + + + + + + isEvent: true, + + constructor: function(event, freezeEvent){ + if (event) { + this.setEvent(event.browserEvent || event, freezeEvent); + } + }, + + setEvent: function(event, freezeEvent){ + var me = this, button; + + if (event === me || (event && event.browserEvent)) { + return event; + } + me.browserEvent = event; + if (event) { + + button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1); + if (me.clickRe.test(event.type) && button == -1) { + button = 0; + } + me.type = event.type; + me.button = button; + me.shiftKey = event.shiftKey; + + me.ctrlKey = event.ctrlKey || event.metaKey || false; + me.altKey = event.altKey; + + me.keyCode = event.keyCode; + me.charCode = event.charCode; + + me.target = Ext.EventManager.getTarget(event); + me.relatedTarget = Ext.EventManager.getRelatedTarget(event); + me.currentTarget = event.currentTarget; + me.xy = (freezeEvent ? me.getXY() : null); + } else { + me.button = -1; + me.shiftKey = false; + me.ctrlKey = false; + me.altKey = false; + me.keyCode = 0; + me.charCode = 0; + me.target = null; + me.xy = [0, 0]; + } + return me; + }, + + + clone: function() { + return new this.self(this.browserEvent, this); + }, + + + stopEvent: function(){ + this.stopPropagation(); + this.preventDefault(); + }, + + + preventDefault: function(){ + if (this.browserEvent) { + Ext.EventManager.preventDefault(this.browserEvent); + } + }, + + + stopPropagation: function(){ + var browserEvent = this.browserEvent; + + if (browserEvent) { + if (browserEvent.type == 'mousedown') { + Ext.EventManager.stoppedMouseDownEvent.fire(this); + } + Ext.EventManager.stopPropagation(browserEvent); + } + }, + + + getCharCode: function(){ + return this.charCode || this.keyCode; + }, + + + getKey: function(){ + return this.normalizeKey(this.keyCode || this.charCode); + }, + + + normalizeKey: function(key){ + + return Ext.isWebKit ? (this.safariKeys[key] || key) : key; + }, + + + getPageX: function(){ + return this.getX(); + }, + + + getPageY: function(){ + return this.getY(); + }, + + + getX: function() { + return this.getXY()[0]; + }, + + + getY: function() { + return this.getXY()[1]; + }, + + + getXY: function() { + if (!this.xy) { + + this.xy = Ext.EventManager.getPageXY(this.browserEvent); + } + return this.xy; + }, + + + getTarget : function(selector, maxDepth, returnEl){ + if (selector) { + return Ext.fly(this.target).findParent(selector, maxDepth, returnEl); + } + return returnEl ? Ext.get(this.target) : this.target; + }, + + + getRelatedTarget : function(selector, maxDepth, returnEl){ + if (selector && this.relatedTarget) { + return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl); + } + return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget; + }, + + + correctWheelDelta : function (delta) { + var scale = this.WHEEL_SCALE, + ret = Math.round(delta / scale); + + if (!ret && delta) { + ret = (delta < 0) ? -1 : 1; + } + + return ret; + }, + + + getWheelDeltas : function () { + var me = this, + event = me.browserEvent, + dx = 0, dy = 0; + + if (Ext.isDefined(event.wheelDeltaX)) { + dx = event.wheelDeltaX; + dy = event.wheelDeltaY; + } else if (event.wheelDelta) { + dy = event.wheelDelta; + } else if (event.detail) { + dy = -event.detail; + + + + if (dy > 100) { + dy = 3; + } else if (dy < -100) { + dy = -3; + } + + + + if (Ext.isDefined(event.axis) && event.axis === event.HORIZONTAL_AXIS) { + dx = dy; + dy = 0; + } + } + + return { + x: me.correctWheelDelta(dx), + y: me.correctWheelDelta(dy) + }; + }, + + + getWheelDelta : function(){ + var deltas = this.getWheelDeltas(); + + return deltas.y; + }, + + + within : function(el, related, allowEl){ + if(el){ + var t = related ? this.getRelatedTarget() : this.getTarget(), + result; + + if (t) { + result = Ext.fly(el, '_internal').contains(t); + if (!result && allowEl) { + result = t == Ext.getDom(el); + } + return result; + } + } + return false; + }, + + + isNavKeyPress : function(){ + var me = this, + k = this.normalizeKey(me.keyCode); + + return (k >= 33 && k <= 40) || + k == me.RETURN || + k == me.TAB || + k == me.ESC; + }, + + + isSpecialKey : function(){ + var k = this.normalizeKey(this.keyCode); + return (this.type == 'keypress' && this.ctrlKey) || + this.isNavKeyPress() || + (k == this.BACKSPACE) || + (k >= 16 && k <= 20) || + (k >= 44 && k <= 46); + }, + + + getPoint : function(){ + var xy = this.getXY(); + return new Ext.util.Point(xy[0], xy[1]); + }, + + + hasModifier : function(){ + var me = this; + return !!(me.ctrlKey || me.altKey || me.shiftKey || me.metaKey); + }, + + + injectEvent: (function () { + var API, + dispatchers = {}, + crazyIEButtons; + + + + + + + if (!Ext.isIE9m && document.createEvent) { + API = { + createHtmlEvent: function (doc, type, bubbles, cancelable) { + var event = doc.createEvent('HTMLEvents'); + + event.initEvent(type, bubbles, cancelable); + return event; + }, + + createMouseEvent: function (doc, type, bubbles, cancelable, detail, + clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, + button, relatedTarget) { + var event = doc.createEvent('MouseEvents'), + view = doc.defaultView || window; + + if (event.initMouseEvent) { + event.initMouseEvent(type, bubbles, cancelable, view, detail, + clientX, clientY, clientX, clientY, ctrlKey, altKey, + shiftKey, metaKey, button, relatedTarget); + } else { + event = doc.createEvent('UIEvents'); + event.initEvent(type, bubbles, cancelable); + event.view = view; + event.detail = detail; + event.screenX = clientX; + event.screenY = clientY; + event.clientX = clientX; + event.clientY = clientY; + event.ctrlKey = ctrlKey; + event.altKey = altKey; + event.metaKey = metaKey; + event.shiftKey = shiftKey; + event.button = button; + event.relatedTarget = relatedTarget; + } + + return event; + }, + + createUIEvent: function (doc, type, bubbles, cancelable, detail) { + var event = doc.createEvent('UIEvents'), + view = doc.defaultView || window; + + event.initUIEvent(type, bubbles, cancelable, view, detail); + return event; + }, + + fireEvent: function (target, type, event) { + target.dispatchEvent(event); + }, + + fixTarget: function (target) { + + if (target == window && !target.dispatchEvent) { + return document; + } + + return target; + } + }; + } else if (document.createEventObject) { + crazyIEButtons = { 0: 1, 1: 4, 2: 2 }; + + API = { + createHtmlEvent: function (doc, type, bubbles, cancelable) { + var event = doc.createEventObject(); + event.bubbles = bubbles; + event.cancelable = cancelable; + return event; + }, + + createMouseEvent: function (doc, type, bubbles, cancelable, detail, + clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, + button, relatedTarget) { + var event = doc.createEventObject(); + event.bubbles = bubbles; + event.cancelable = cancelable; + event.detail = detail; + event.screenX = clientX; + event.screenY = clientY; + event.clientX = clientX; + event.clientY = clientY; + event.ctrlKey = ctrlKey; + event.altKey = altKey; + event.shiftKey = shiftKey; + event.metaKey = metaKey; + event.button = crazyIEButtons[button] || button; + event.relatedTarget = relatedTarget; + return event; + }, + + createUIEvent: function (doc, type, bubbles, cancelable, detail) { + var event = doc.createEventObject(); + event.bubbles = bubbles; + event.cancelable = cancelable; + return event; + }, + + fireEvent: function (target, type, event) { + target.fireEvent('on' + type, event); + }, + + fixTarget: function (target) { + if (target == document) { + + + return document.documentElement; + } + + return target; + } + }; + } + + + + + 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 (name, value) { + var bubbles = value[0], cancelable = value[1]; + dispatchers[name] = function (targetEl, srcEvent) { + var e = API.createHtmlEvent(name, bubbles, cancelable); + API.fireEvent(targetEl, name, e); + }; + }); + + + + + function createMouseEventDispatcher (type, detail) { + var cancelable = (type != 'mousemove'); + return function (targetEl, srcEvent) { + var xy = srcEvent.getXY(), + e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable, + detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey, + srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button, + srcEvent.relatedTarget); + API.fireEvent(targetEl, type, e); + }; + } + + Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'], + function (eventName) { + dispatchers[eventName] = createMouseEventDispatcher(eventName, 1); + }); + + + + + Ext.Object.each({ + focusin: [true, false], + focusout: [true, false], + activate: [true, true], + focus: [false, false], + blur: [false, false] + }, + function (name, value) { + var bubbles = value[0], cancelable = value[1]; + dispatchers[name] = function (targetEl, srcEvent) { + var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1); + API.fireEvent(targetEl, name, e); + }; + }); + + + if (!API) { + + + dispatchers = {}; + + API = { + fixTarget: Ext.identityFn + }; + } + + function cannotInject (target, srcEvent) { + + } + + return function (target) { + var me = this, + dispatcher = dispatchers[me.type] || cannotInject, + t = target ? (target.dom || target) : me.getTarget(); + + t = API.fixTarget(t); + dispatcher(t, me); + }; + }()) + +}, function() { + +Ext.EventObject = new Ext.EventObjectImpl(); + +}); + + + + + + + +Ext.define('Ext.dom.AbstractQuery', { + + select: function(q, root) { + var results = [], + nodes, + i, + j, + qlen, + nlen; + + root = root || document; + + if (typeof root == 'string') { + root = document.getElementById(root); + } + + q = Ext.splitAndUnescape(q, ","); + + for (i = 0,qlen = q.length; i < qlen; i++) { + if (typeof q[i] == 'string') { + + + if (typeof q[i][0] == '@') { + nodes = root.getAttributeNode(q[i].substring(1)); + results.push(nodes); + } else { + nodes = root.querySelectorAll(q[i]); + + for (j = 0,nlen = nodes.length; j < nlen; j++) { + results.push(nodes[j]); + } + } + } + } + + return results; + }, + + + selectNode: function(q, root) { + return this.select(q, root)[0]; + }, + + + is: function(el, q) { + if (typeof el == "string") { + el = document.getElementById(el); + } + return this.select(q).indexOf(el) !== -1; + } + +}); + + + + + + +Ext.define('Ext.dom.AbstractHelper', { + emptyTags : /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i, + confRe : /^(?:tag|children|cn|html|tpl|tplData)$/i, + endRe : /end/i, + styleSepRe: /\s*(?::|;)\s*/, + + + attributeTransform: { cls : 'class', htmlFor : 'for' }, + + closeTags: {}, + + decamelizeName : (function () { + var camelCaseRe = /([a-z])([A-Z])/g, + cache = {}; + + function decamel (match, p1, p2) { + return p1 + '-' + p2.toLowerCase(); + } + + return function (s) { + return cache[s] || (cache[s] = s.replace(camelCaseRe, decamel)); + }; + }()), + + generateMarkup: function(spec, buffer) { + var me = this, + specType = typeof spec, + attr, val, tag, i, closeTags; + + if (specType == "string" || specType == "number") { + buffer.push(spec); + } else if (Ext.isArray(spec)) { + for (i = 0; i < spec.length; i++) { + if (spec[i]) { + me.generateMarkup(spec[i], buffer); + } + } + } else { + tag = spec.tag || 'div'; + buffer.push('<', tag); + + for (attr in spec) { + if (spec.hasOwnProperty(attr)) { + val = spec[attr]; + if (!me.confRe.test(attr)) { + if (typeof val == "object") { + buffer.push(' ', attr, '="'); + me.generateStyles(val, buffer, true).push('"'); + } else { + buffer.push(' ', me.attributeTransform[attr] || attr, '="', val, '"'); + } + } + } + } + + + if (me.emptyTags.test(tag)) { + buffer.push('/>'); + } else { + buffer.push('>'); + + + if ((val = spec.tpl)) { + val.applyOut(spec.tplData, buffer); + } + if ((val = spec.html)) { + buffer.push(val); + } + if ((val = spec.cn || spec.children)) { + me.generateMarkup(val, buffer); + } + + + closeTags = me.closeTags; + buffer.push(closeTags[tag] || (closeTags[tag] = '')); + } + } + + return buffer; + }, + + + generateStyles: function (styles, buffer, encode) { + var a = buffer || [], + name, val; + + for (name in styles) { + if (styles.hasOwnProperty(name)) { + val = styles[name]; + + + + name = this.decamelizeName(name); + if (encode && Ext.String.hasHtmlCharacters(val)) { + val = Ext.String.htmlEncode(val); + } + a.push(name, ':', val, ';'); + } + } + + return buffer || a.join(''); + }, + + + markup: function(spec) { + if (typeof spec == "string") { + return spec; + } + + var buf = this.generateMarkup(spec, []); + return buf.join(''); + }, + + + applyStyles: function(el, styles) { + if (styles) { + var i = 0, + len; + + el = Ext.fly(el, '_applyStyles'); + if (typeof styles == 'function') { + styles = styles.call(); + } + if (typeof styles == 'string') { + styles = Ext.util.Format.trim(styles).split(this.styleSepRe); + for (len = styles.length; i < len;) { + el.setStyle(styles[i++], styles[i++]); + } + } else if (Ext.isObject(styles)) { + el.setStyle(styles); + } + } + }, + + + insertHtml: function(where, el, html) { + var hash = {}, + setStart, + range, + frag, + rangeEl; + + where = where.toLowerCase(); + + + hash.beforebegin = ['BeforeBegin', 'previousSibling']; + hash.afterend = ['AfterEnd', 'nextSibling']; + + range = el.ownerDocument.createRange(); + setStart = 'setStart' + (this.endRe.test(where) ? 'After' : 'Before'); + if (hash[where]) { + range[setStart](el); + frag = range.createContextualFragment(html); + el.parentNode.insertBefore(frag, where == 'beforebegin' ? el : el.nextSibling); + return el[(where == 'beforebegin' ? 'previous' : 'next') + 'Sibling']; + } + else { + rangeEl = (where == 'afterbegin' ? 'first' : 'last') + 'Child'; + if (el.firstChild) { + range[setStart](el[rangeEl]); + frag = range.createContextualFragment(html); + if (where == 'afterbegin') { + el.insertBefore(frag, el.firstChild); + } + else { + el.appendChild(frag); + } + } + else { + el.innerHTML = html; + } + return el[rangeEl]; + } + + throw 'Illegal insertion point -> "' + where + '"'; + }, + + + insertBefore: function(el, o, returnElement) { + return this.doInsert(el, o, returnElement, 'beforebegin'); + }, + + + insertAfter: function(el, o, returnElement) { + return this.doInsert(el, o, returnElement, 'afterend', 'nextSibling'); + }, + + + insertFirst: function(el, o, returnElement) { + return this.doInsert(el, o, returnElement, 'afterbegin', 'firstChild'); + }, + + + append: function(el, o, returnElement) { + return this.doInsert(el, o, returnElement, 'beforeend', '', true); + }, + + + overwrite: function(el, o, returnElement) { + el = Ext.getDom(el); + el.innerHTML = this.markup(o); + return returnElement ? Ext.get(el.firstChild) : el.firstChild; + }, + + doInsert: function(el, o, returnElement, pos, sibling, append) { + var newNode = this.insertHtml(pos, Ext.getDom(el), this.markup(o)); + return returnElement ? Ext.get(newNode, true) : newNode; + } + +}); + + + + +Ext.define('Ext.dom.AbstractElement_static', { + override: 'Ext.dom.AbstractElement', + + inheritableStatics: { + unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i, + camelRe: /(-[a-z])/gi, + msRe: /^-ms-/, + cssRe: /([a-z0-9\-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*)?;?/gi, + opacityRe: /alpha\(opacity=(.*)\)/i, + propertyCache: {}, + defaultUnit : "px", + borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'}, + paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'}, + margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'}, + + addUnits: function(size, units) { + + if (typeof size == 'number') { + return size + (units || this.defaultUnit || 'px'); + } + + + if (size === "" || size == "auto" || size === undefined || size === null) { + return size || ''; + } + + + if (!this.unitRe.test(size)) { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn("Warning, size detected as NaN on Element.addUnits."); + } + return size || ''; + } + + return size; + }, + + + isAncestor: function(p, c) { + var ret = false; + + p = Ext.getDom(p); + c = Ext.getDom(c); + if (p && c) { + if (p.contains) { + return p.contains(c); + } else if (p.compareDocumentPosition) { + return !!(p.compareDocumentPosition(c) & 16); + } else { + while ((c = c.parentNode)) { + ret = c == p || ret; + } + } + } + return ret; + }, + + + parseBox: function(box) { + box = box || 0; + + var type = typeof box, + parts, + ln; + + if (type === 'number') { + return { + top : box, + right : box, + bottom: box, + left : box + }; + } else if (type !== 'string') { + + return box; + } + + parts = box.split(' '); + ln = parts.length; + + if (ln == 1) { + parts[1] = parts[2] = parts[3] = parts[0]; + } else if (ln == 2) { + parts[2] = parts[0]; + parts[3] = parts[1]; + } else if (ln == 3) { + parts[3] = parts[1]; + } + + return { + top :parseFloat(parts[0]) || 0, + right :parseFloat(parts[1]) || 0, + bottom:parseFloat(parts[2]) || 0, + left :parseFloat(parts[3]) || 0 + }; + }, + + + unitizeBox: function(box, units) { + var a = this.addUnits, + b = this.parseBox(box); + + return a(b.top, units) + ' ' + + a(b.right, units) + ' ' + + a(b.bottom, units) + ' ' + + a(b.left, units); + + }, + + + camelReplaceFn: function(m, a) { + return a.charAt(1).toUpperCase(); + }, + + + normalize: function(prop) { + + if (prop == 'float') { + prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat'; + } + + return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.msRe, 'ms-').replace(this.camelRe, this.camelReplaceFn)); + }, + + + getDocumentHeight: function() { + return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight()); + }, + + + getDocumentWidth: function() { + return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth()); + }, + + + getViewportHeight: function(){ + return window.innerHeight; + }, + + + getViewportWidth: function() { + return window.innerWidth; + }, + + + getViewSize: function() { + return { + width: window.innerWidth, + height: window.innerHeight + }; + }, + + + getOrientation: function() { + if (Ext.supports.OrientationChange) { + return (window.orientation == 0) ? 'portrait' : 'landscape'; + } + + return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape'; + }, + + + fromPoint: function(x, y) { + return Ext.get(document.elementFromPoint(x, y)); + }, + + + parseStyles: function(styles){ + var out = {}, + cssRe = this.cssRe, + matches; + + if (styles) { + + + + + cssRe.lastIndex = 0; + while ((matches = cssRe.exec(styles))) { + out[matches[1]] = matches[2]||''; + } + } + return out; + } + } +}, +function () { + var doc = document, + activeElement = null, + isCSS1 = doc.compatMode == "CSS1Compat"; + + + + + if (!('activeElement' in doc) && doc.addEventListener) { + doc.addEventListener('focus', + function (ev) { + if (ev && ev.target) { + activeElement = (ev.target == doc) ? null : ev.target; + } + }, true); + } + + + function makeSelectionRestoreFn (activeEl, start, end) { + return function () { + activeEl.selectionStart = start; + activeEl.selectionEnd = end; + }; + } + + this.addInheritableStatics({ + + getActiveElement: function () { + var active; + + + + try { + active = doc.activeElement; + } catch(e) {} + + + + active = active || activeElement; + if (!active) { + active = activeElement = document.body; + } + return active; + }, + + + getRightMarginFixCleaner: function (target) { + var supports = Ext.supports, + hasInputBug = supports.DisplayChangeInputSelectionBug, + hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug, + activeEl, + tag, + start, + end; + + if (hasInputBug || hasTextAreaBug) { + activeEl = doc.activeElement || activeElement; + tag = activeEl && activeEl.tagName; + + if ((hasTextAreaBug && tag == 'TEXTAREA') || + (hasInputBug && tag == 'INPUT' && activeEl.type == 'text')) { + if (Ext.dom.Element.isAncestor(target, activeEl)) { + start = activeEl.selectionStart; + end = activeEl.selectionEnd; + + if (Ext.isNumber(start) && Ext.isNumber(end)) { + + + + + return makeSelectionRestoreFn(activeEl, start, end); + } + } + } + } + + return Ext.emptyFn; + }, + + getViewWidth: function(full) { + return full ? Ext.dom.Element.getDocumentWidth() : Ext.dom.Element.getViewportWidth(); + }, + + getViewHeight: function(full) { + return full ? Ext.dom.Element.getDocumentHeight() : Ext.dom.Element.getViewportHeight(); + }, + + getDocumentHeight: function() { + return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, Ext.dom.Element.getViewportHeight()); + }, + + getDocumentWidth: function() { + return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, Ext.dom.Element.getViewportWidth()); + }, + + getViewportHeight: function(){ + return Ext.isIE9m ? + (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) : + self.innerHeight; + }, + + getViewportWidth: function() { + return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth : + Ext.isIE9m ? doc.documentElement.clientWidth : self.innerWidth; + }, + + + serializeForm: function(form) { + var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements, + hasSubmit = false, + encoder = encodeURIComponent, + data = '', + eLen = fElements.length, + element, name, type, options, hasValue, e, + o, oLen, opt; + + for (e = 0; e < eLen; e++) { + element = fElements[e]; + name = element.name; + type = element.type; + options = element.options; + + if (!element.disabled && name) { + if (/select-(one|multiple)/i.test(type)) { + oLen = options.length; + for (o = 0; o < oLen; o++) { + opt = options[o]; + if (opt.selected) { + hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified; + data += Ext.String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text)); + } + } + } else if (!(/file|undefined|reset|button/i.test(type))) { + if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) { + data += encoder(name) + '=' + encoder(element.value) + '&'; + hasSubmit = /submit/i.test(type); + } + } + } + } + return data.substr(0, data.length - 1); + } + }); +}); + + + + +Ext.define('Ext.dom.AbstractElement_insertion', { + override: 'Ext.dom.AbstractElement', + + + appendChild: function(el, returnDom) { + var me = this, + insertEl, + eLen, e, oldUseDom; + + if (el.nodeType || el.dom || typeof el == 'string') { + el = Ext.getDom(el); + me.dom.appendChild(el); + return !returnDom ? Ext.get(el) : el; + } else if (el.length) { + + insertEl = Ext.fly(document.createDocumentFragment(), '_internal'); + eLen = el.length; + + + Ext.DomHelper.useDom = true; + for (e = 0; e < eLen; e++) { + insertEl.appendChild(el[e], returnDom); + } + Ext.DomHelper.useDom = oldUseDom; + me.dom.appendChild(insertEl.dom); + return returnDom ? insertEl.dom : insertEl; + } + else { + return me.createChild(el, null, returnDom); + } + }, + + + appendTo: function(el) { + Ext.getDom(el).appendChild(this.dom); + return this; + }, + + + insertBefore: function(el) { + el = Ext.getDom(el); + el.parentNode.insertBefore(this.dom, el); + return this; + }, + + + insertAfter: function(el) { + el = Ext.getDom(el); + el.parentNode.insertBefore(this.dom, el.nextSibling); + return this; + }, + + + insertFirst: function(el, returnDom) { + el = el || {}; + if (el.nodeType || el.dom || typeof el == 'string') { + el = Ext.getDom(el); + this.dom.insertBefore(el, this.dom.firstChild); + return !returnDom ? Ext.get(el) : el; + } + else { + return this.createChild(el, this.dom.firstChild, returnDom); + } + }, + + + insertSibling: function(el, where, returnDom) { + var me = this, + DomHelper = Ext.core.DomHelper, + oldUseDom = DomHelper.useDom, + isAfter = (where || 'before').toLowerCase() == 'after', + rt, insertEl, eLen, e; + + if (Ext.isArray(el)) { + + insertEl = Ext.fly(document.createDocumentFragment(), '_internal'); + eLen = el.length; + + + DomHelper.useDom = true; + for (e = 0; e < eLen; e++) { + rt = insertEl.appendChild(el[e], returnDom); + } + DomHelper.useDom = oldUseDom; + + + me.dom.parentNode.insertBefore(insertEl.dom, isAfter ? me.dom.nextSibling : me.dom); + return rt; + } + + el = el || {}; + + if (el.nodeType || el.dom) { + rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom); + if (!returnDom) { + rt = Ext.get(rt); + } + } else { + if (isAfter && !me.dom.nextSibling) { + rt = DomHelper.append(me.dom.parentNode, el, !returnDom); + } else { + rt = DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom); + } + } + return rt; + }, + + + replace: function(el) { + el = Ext.get(el); + this.insertBefore(el); + el.remove(); + return this; + }, + + + replaceWith: function(el){ + var me = this; + + if (el.nodeType || el.dom || typeof el == 'string') { + el = Ext.get(el); + me.dom.parentNode.insertBefore(el.dom, me.dom); + } else { + el = Ext.core.DomHelper.insertBefore(me.dom, el); + } + + delete Ext.cache[me.id]; + Ext.removeNode(me.dom); + me.id = Ext.id(me.dom = el); + Ext.dom.AbstractElement.addToCache(me.isFlyweight ? new Ext.dom.AbstractElement(me.dom) : me); + return me; + }, + + + createChild: function(config, insertBefore, returnDom) { + config = config || {tag:'div'}; + if (insertBefore) { + return Ext.core.DomHelper.insertBefore(insertBefore, config, returnDom !== true); + } + else { + return Ext.core.DomHelper.append(this.dom, config, returnDom !== true); + } + }, + + + wrap: function(config, returnDom, selector) { + var newEl = Ext.core.DomHelper.insertBefore(this.dom, config || {tag: "div"}, true), + target = newEl; + + if (selector) { + target = Ext.DomQuery.selectNode(selector, newEl.dom); + } + + target.appendChild(this.dom); + return returnDom ? newEl.dom : newEl; + }, + + + insertHtml: function(where, html, returnEl) { + var el = Ext.core.DomHelper.insertHtml(where, this.dom, html); + return returnEl ? Ext.get(el) : el; + } +}); + + + + +Ext.define('Ext.dom.AbstractElement_style', { + + override: 'Ext.dom.AbstractElement' + +}, function() { + + var Element = this, + wordsRe = /\w/g, + spacesRe = /\s+/, + transparentRe = /^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i, + + + + + hasClassList = Ext.supports.ClassList, + PADDING = 'padding', + MARGIN = 'margin', + BORDER = 'border', + LEFT_SUFFIX = '-left', + RIGHT_SUFFIX = '-right', + TOP_SUFFIX = '-top', + BOTTOM_SUFFIX = '-bottom', + WIDTH = '-width', + + borders = {l: BORDER + LEFT_SUFFIX + WIDTH, r: BORDER + RIGHT_SUFFIX + WIDTH, t: BORDER + TOP_SUFFIX + WIDTH, b: BORDER + BOTTOM_SUFFIX + WIDTH}, + paddings = {l: PADDING + LEFT_SUFFIX, r: PADDING + RIGHT_SUFFIX, t: PADDING + TOP_SUFFIX, b: PADDING + BOTTOM_SUFFIX}, + margins = {l: MARGIN + LEFT_SUFFIX, r: MARGIN + RIGHT_SUFFIX, t: MARGIN + TOP_SUFFIX, b: MARGIN + BOTTOM_SUFFIX}, + internalFly = new Element.Fly(); + + + Ext.override(Element, { + + + styleHooks: {}, + + + addStyles : function(sides, styles){ + var totalSize = 0, + sidesArr = (sides || '').match(wordsRe), + i, + len = sidesArr.length, + side, + styleSides = []; + + if (len == 1) { + totalSize = Math.abs(parseFloat(this.getStyle(styles[sidesArr[0]])) || 0); + } else if (len) { + for (i = 0; i < len; i++) { + side = sidesArr[i]; + styleSides.push(styles[side]); + } + + styleSides = this.getStyle(styleSides); + + for (i=0; i < len; i++) { + side = sidesArr[i]; + totalSize += Math.abs(parseFloat(styleSides[styles[side]]) || 0); + } + } + + return totalSize; + }, + + + addCls: (function(){ + var addWithClassList = function(className) { + if (String(className).indexOf('undefined') > -1) { + Ext.Logger.warn("called with an undefined className: " + className); + } + var me = this, + dom = me.dom, + trimRe = me.trimRe, + origClassName = className, + classList, + newCls, + i, + len, + cls; + + if (typeof(className) == 'string') { + + className = className.replace(trimRe, '').split(spacesRe); + } + + + + if (dom && className && !!(len = className.length)) { + if (!dom.className) { + dom.className = className.join(' '); + } else { + classList = dom.classList; + if (classList) { + for (i = 0; i < len; ++i) { + cls = className[i]; + if (cls) { + if (!classList.contains(cls)) { + if (newCls) { + newCls.push(cls); + } else { + newCls = dom.className.replace(trimRe, ''); + newCls = newCls ? [newCls, cls] : [cls]; + } + } + } + } + + if (newCls) { + dom.className = newCls.join(' '); + } + } else { + addWithoutClassList(origClassName); + } + } + } + return me; + }, addWithoutClassList = function(className) { + if (String(className).indexOf('undefined') > -1) { + Ext.Logger.warn("called with an undefined className: '" + className + "'"); + } + var me = this, + dom = me.dom, + elClasses; + + if (dom && className && className.length) { + elClasses = Ext.Element.mergeClsList(dom.className, className); + if (elClasses.changed) { + dom.className = elClasses.join(' '); + } + } + return me; + }; + + return hasClassList ? addWithClassList : addWithoutClassList; + })(), + + + + removeCls: function(className) { + var me = this, + dom = me.dom, + classList, + len, + elClasses; + + if (typeof(className) == 'string') { + + className = className.replace(me.trimRe, '').split(spacesRe); + } + + if (dom && dom.className && className && !!(len = className.length)) { + classList = dom.classList; + if (len === 1 && classList) { + if (className[0]) { + classList.remove(className[0]); + } + } else { + elClasses = Ext.Element.removeCls(dom.className, className); + if (elClasses.changed) { + dom.className = elClasses.join(' '); + } + } + } + return me; + }, + + + radioCls: function(className) { + var cn = this.dom.parentNode.childNodes, + v, + i, len; + className = Ext.isArray(className) ? className: [className]; + for (i = 0, len = cn.length; i < len; i++) { + v = cn[i]; + if (v && v.nodeType == 1) { + internalFly.attach(v).removeCls(className); + } + } + return this.addCls(className); + }, + + + toggleCls: (function(){ + var toggleWithClassList = function(className){ + var me = this, + dom = me.dom, + classList; + + if (dom) { + className = className.replace(me.trimRe, ''); + if (className) { + classList = dom.classList; + if (classList) { + classList.toggle(className); + } else { + toggleWithoutClassList(className); + } + } + } + + return me; + }, toggleWithoutClassList = function(className){ + return this.hasCls(className) ? this.removeCls(className) : this.addCls(className); + }; + + return hasClassList ? toggleWithClassList : toggleWithoutClassList; + })(), + + + hasCls: (function(){ + var hasClsWithClassList = function(className) { + var dom = this.dom, + out = false, + classList; + + if (dom && className) { + classList = dom.classList; + if (classList) { + out = classList.contains(className); + } else { + out = hasClsWithoutClassList(className); + } + } + return out; + }, hasClsWithoutClassList = function(className){ + var dom = this.dom; + return dom ? className && (' '+dom.className+' ').indexOf(' '+className+' ') !== -1 : false; + }; + + return hasClassList ? hasClsWithClassList : hasClsWithoutClassList; + })(), + + + replaceCls: function(oldClassName, newClassName){ + return this.removeCls(oldClassName).addCls(newClassName); + }, + + + isStyle: function(style, val) { + return this.getStyle(style) == val; + }, + + + getStyle: function (property, inline) { + var me = this, + dom = me.dom, + multiple = typeof property != 'string', + hooks = me.styleHooks, + prop = property, + props = prop, + len = 1, + domStyle, camel, values, hook, out, style, i; + + if (multiple) { + values = {}; + prop = props[0]; + i = 0; + if (!(len = props.length)) { + return values; + } + } + + if (!dom || dom.documentElement) { + return values || ''; + } + + domStyle = dom.style; + + if (inline) { + style = domStyle; + } else { + + + + + style = dom.ownerDocument.defaultView.getComputedStyle(dom, null); + + + if (!style) { + inline = true; + style = domStyle; + } + } + + do { + hook = hooks[prop]; + + if (!hook) { + hooks[prop] = hook = { name: Element.normalize(prop) }; + } + + if (hook.get) { + out = hook.get(dom, me, inline, style); + } else { + camel = hook.name; + out = style[camel]; + } + + if (!multiple) { + return out; + } + + values[prop] = out; + prop = props[++i]; + } while (i < len); + + return values; + }, + + getStyles: function () { + var props = Ext.Array.slice(arguments), + len = props.length, + inline; + + if (len && typeof props[len-1] == 'boolean') { + inline = props.pop(); + } + + return this.getStyle(props, inline); + }, + + + isTransparent: function (prop) { + var value = this.getStyle(prop); + return value ? transparentRe.test(value) : false; + }, + + + setStyle: function(prop, value) { + var me = this, + dom = me.dom, + hooks = me.styleHooks, + style = dom.style, + name = prop, + hook; + + + if (typeof name == 'string') { + hook = hooks[name]; + if (!hook) { + hooks[name] = hook = { name: Element.normalize(name) }; + } + value = (value == null) ? '' : value; + if (hook.set) { + hook.set(dom, value, me); + } else { + style[hook.name] = value; + } + if (hook.afterSet) { + hook.afterSet(dom, value, me); + } + } else { + for (name in prop) { + if (prop.hasOwnProperty(name)) { + hook = hooks[name]; + if (!hook) { + hooks[name] = hook = { name: Element.normalize(name) }; + } + value = prop[name]; + value = (value == null) ? '' : value; + if (hook.set) { + hook.set(dom, value, me); + } else { + style[hook.name] = value; + } + if (hook.afterSet) { + hook.afterSet(dom, value, me); + } + } + } + } + + return me; + }, + + + getHeight: function(contentHeight) { + var dom = this.dom, + height = contentHeight ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight; + return height > 0 ? height: 0; + }, + + + getWidth: function(contentWidth) { + var dom = this.dom, + width = contentWidth ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth; + return width > 0 ? width: 0; + }, + + + setWidth: function(width) { + var me = this; + me.dom.style.width = Element.addUnits(width); + return me; + }, + + + setHeight: function(height) { + var me = this; + me.dom.style.height = Element.addUnits(height); + return me; + }, + + + getBorderWidth: function(side){ + return this.addStyles(side, borders); + }, + + + getPadding: function(side){ + return this.addStyles(side, paddings); + }, + + margins : margins, + + + applyStyles: function(styles) { + if (styles) { + var i, + len, + dom = this.dom; + + if (typeof styles == 'function') { + styles = styles.call(); + } + if (typeof styles == 'string') { + styles = Ext.util.Format.trim(styles).split(/\s*(?::|;)\s*/); + for (i = 0, len = styles.length; i < len;) { + dom.style[Element.normalize(styles[i++])] = styles[i++]; + } + } + else if (typeof styles == 'object') { + this.setStyle(styles); + } + } + }, + + + setSize: function(width, height) { + var me = this, + style = me.dom.style; + + if (Ext.isObject(width)) { + + height = width.height; + width = width.width; + } + + style.width = Element.addUnits(width); + style.height = Element.addUnits(height); + return me; + }, + + + getViewSize: function() { + var doc = document, + dom = this.dom; + + if (dom == doc || dom == doc.body) { + return { + width: Element.getViewportWidth(), + height: Element.getViewportHeight() + }; + } + else { + return { + width: dom.clientWidth, + height: dom.clientHeight + }; + } + }, + + + getSize: function(contentSize) { + var dom = this.dom; + return { + width: Math.max(0, contentSize ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth), + height: Math.max(0, contentSize ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight) + }; + }, + + + repaint: function() { + var dom = this.dom; + this.addCls(Ext.baseCSSPrefix + 'repaint'); + setTimeout(function(){ + internalFly.attach(dom).removeCls(Ext.baseCSSPrefix + 'repaint'); + }, 1); + return this; + }, + + + getMargin: function(side){ + var me = this, + hash = {t:"top", l:"left", r:"right", b: "bottom"}, + key, + o, + margins; + + if (!side) { + margins = []; + for (key in me.margins) { + if(me.margins.hasOwnProperty(key)) { + margins.push(me.margins[key]); + } + } + o = me.getStyle(margins); + if(o && typeof o == 'object') { + + for (key in me.margins) { + if(me.margins.hasOwnProperty(key)) { + o[hash[key]] = parseFloat(o[me.margins[key]]) || 0; + } + } + } + + return o; + } else { + return me.addStyles(side, me.margins); + } + }, + + + mask: function(msg, msgCls, transparent) { + var me = this, + dom = me.dom, + data = (me.$cache || me.getCache()).data, + el = data.mask, + mask, + size, + cls = '', + prefix = Ext.baseCSSPrefix; + + me.addCls(prefix + 'masked'); + if (me.getStyle("position") == "static") { + me.addCls(prefix + 'masked-relative'); + } + if (el) { + el.remove(); + } + if (msgCls && typeof msgCls == 'string' ) { + cls = ' ' + msgCls; + } + else { + cls = ' ' + prefix + 'mask-gray'; + } + + mask = me.createChild({ + role: 'presentation', + cls: prefix + 'mask' + ((transparent !== false) ? '' : (' ' + prefix + 'mask-gray')), + html: msg ? ('') : '' + }); + + size = me.getSize(); + + data.mask = mask; + + if (dom === document.body) { + size.height = window.innerHeight; + if (me.orientationHandler) { + Ext.EventManager.unOrientationChange(me.orientationHandler, me); + } + + me.orientationHandler = function() { + size = me.getSize(); + size.height = window.innerHeight; + mask.setSize(size); + }; + + Ext.EventManager.onOrientationChange(me.orientationHandler, me); + } + mask.setSize(size); + if (Ext.is.iPad) { + Ext.repaint(); + } + }, + + + unmask: function() { + var me = this, + data = (me.$cache || me.getCache()).data, + mask = data.mask, + prefix = Ext.baseCSSPrefix; + + if (mask) { + mask.remove(); + delete data.mask; + } + me.removeCls([prefix + 'masked', prefix + 'masked-relative']); + + if (me.dom === document.body) { + Ext.EventManager.unOrientationChange(me.orientationHandler, me); + delete me.orientationHandler; + } + } + }); + + + Ext.onReady(function () { + var supports = Ext.supports, + styleHooks, + colorStyles, i, name, camel; + + function fixTransparent (dom, el, inline, style) { + var value = style[this.name] || ''; + return transparentRe.test(value) ? 'transparent' : value; + } + + function fixRightMargin (dom, el, inline, style) { + var result = style.marginRight, + domStyle, display; + + + + if (result != '0px') { + domStyle = dom.style; + display = domStyle.display; + domStyle.display = 'inline-block'; + result = (inline ? style : dom.ownerDocument.defaultView.getComputedStyle(dom, null)).marginRight; + domStyle.display = display; + } + + return result; + } + + function fixRightMarginAndInputFocus (dom, el, inline, style) { + var result = style.marginRight, + domStyle, cleaner, display; + + if (result != '0px') { + domStyle = dom.style; + cleaner = Element.getRightMarginFixCleaner(dom); + display = domStyle.display; + domStyle.display = 'inline-block'; + result = (inline ? style : dom.ownerDocument.defaultView.getComputedStyle(dom, '')).marginRight; + domStyle.display = display; + cleaner(); + } + + return result; + } + + styleHooks = Element.prototype.styleHooks; + + + + if (supports.init) { + supports.init(); + } + + + if (!supports.RightMargin) { + styleHooks.marginRight = styleHooks['margin-right'] = { + name: 'marginRight', + + + get: (supports.DisplayChangeInputSelectionBug || supports.DisplayChangeTextAreaSelectionBug) ? + fixRightMarginAndInputFocus : fixRightMargin + }; + } + + if (!supports.TransparentColor) { + colorStyles = ['background-color', 'border-color', 'color', 'outline-color']; + for (i = colorStyles.length; i--; ) { + name = colorStyles[i]; + camel = Element.normalize(name); + + styleHooks[name] = styleHooks[camel] = { + name: camel, + get: fixTransparent + }; + } + } + }); + +}); + + + + +Ext.define('Ext.dom.AbstractElement_traversal', { + override: 'Ext.dom.AbstractElement', + + + findParent: function(simpleSelector, limit, returnEl) { + var target = this.dom, + topmost = document.documentElement, + depth = 0, + stopEl; + + limit = limit || 50; + if (isNaN(limit)) { + stopEl = Ext.getDom(limit); + limit = Number.MAX_VALUE; + } + while (target && target.nodeType == 1 && depth < limit && target != topmost && target != stopEl) { + if (Ext.DomQuery.is(target, simpleSelector)) { + return returnEl ? Ext.get(target) : target; + } + depth++; + target = target.parentNode; + } + return null; + }, + + + findParentNode: function(simpleSelector, limit, returnEl) { + var p = Ext.fly(this.dom.parentNode, '_internal'); + return p ? p.findParent(simpleSelector, limit, returnEl) : null; + }, + + + up: function(simpleSelector, limit, returnDom) { + return this.findParentNode(simpleSelector, limit, !returnDom); + }, + + + select: function(selector, composite) { + return Ext.dom.Element.select(selector, this.dom, composite); + }, + + + query: function(selector) { + return Ext.DomQuery.select(selector, this.dom); + }, + + + down: function(selector, returnDom) { + var n = Ext.DomQuery.selectNode(selector, this.dom); + return returnDom ? n : Ext.get(n); + }, + + + child: function(selector, returnDom) { + var node, + me = this, + id; + + + + id = Ext.id(me.dom); + + id = Ext.escapeId(id); + node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom); + return returnDom ? node : Ext.get(node); + }, + + + parent: function(selector, returnDom) { + return this.matchNode('parentNode', 'parentNode', selector, returnDom); + }, + + + next: function(selector, returnDom) { + return this.matchNode('nextSibling', 'nextSibling', selector, returnDom); + }, + + + prev: function(selector, returnDom) { + return this.matchNode('previousSibling', 'previousSibling', selector, returnDom); + }, + + + + first: function(selector, returnDom) { + return this.matchNode('nextSibling', 'firstChild', selector, returnDom); + }, + + + last: function(selector, returnDom) { + return this.matchNode('previousSibling', 'lastChild', selector, returnDom); + }, + + matchNode: function(dir, start, selector, returnDom) { + if (!this.dom) { + return null; + } + + var n = this.dom[start]; + while (n) { + if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) { + return !returnDom ? Ext.get(n) : n; + } + n = n[dir]; + } + return null; + }, + + isAncestor: function(element) { + return this.self.isAncestor.call(this.self, this.dom, element); + } +}); + + + + + + +Ext.define('Ext.dom.AbstractElement', { + + + + + + + + + trimRe: /^\s+|\s+$/g, + whitespaceRe: /\s/, + + inheritableStatics: { + trimRe: /^\s+|\s+$/g, + whitespaceRe: /\s/, + + + get: function(el) { + var me = this, + document = window.document, + El = Ext.dom.Element, + cacheItem, + docEl, + extEl, + dom, + id; + + if (!el) { + return null; + } + + + if (el.isFly) { + el = el.dom; + } + + if (typeof el == "string") { + if (el == Ext.windowId) { + return El.get(window); + } else if (el == Ext.documentId) { + return El.get(document); + } + + cacheItem = Ext.cache[el]; + + + + + if (cacheItem && cacheItem.skipGarbageCollection) { + extEl = cacheItem.el; + return extEl; + } + + if (!(dom = document.getElementById(el))) { + return null; + } + + if (cacheItem && cacheItem.el) { + extEl = Ext.updateCacheEntry(cacheItem, dom).el; + } else { + + extEl = new El(dom, !!cacheItem); + } + return extEl; + } else if (el.tagName) { + if (!(id = el.id)) { + id = Ext.id(el); + } + cacheItem = Ext.cache[id]; + if (cacheItem && cacheItem.el) { + extEl = Ext.updateCacheEntry(cacheItem, el).el; + } else { + + extEl = new El(el, !!cacheItem); + } + return extEl; + } else if (el instanceof me) { + if (el != me.docEl && el != me.winEl) { + id = el.id; + + + cacheItem = Ext.cache[id]; + if (cacheItem) { + Ext.updateCacheEntry(cacheItem, document.getElementById(id) || el.dom); + } + } + return el; + } else if (el.isComposite) { + return el; + } else if (Ext.isArray(el)) { + return me.select(el); + } else if (el === document) { + + if (!me.docEl) { + docEl = me.docEl = Ext.Object.chain(El.prototype); + docEl.dom = document; + + + + docEl.el = docEl; + docEl.id = Ext.id(document); + me.addToCache(docEl); + } + return me.docEl; + } else if (el === window) { + if (!me.winEl) { + me.winEl = Ext.Object.chain(El.prototype); + me.winEl.dom = window; + me.winEl.id = Ext.id(window); + me.addToCache(me.winEl); + } + return me.winEl; + } + return null; + }, + + addToCache: function(el, id) { + if (el) { + Ext.addCacheEntry(id, el); + } + return el; + }, + + addMethods: function() { + this.override.apply(this, arguments); + }, + + + mergeClsList: function() { + var clsList, clsHash = {}, + i, length, j, listLength, clsName, result = [], + changed = false, + trimRe = this.trimRe, + whitespaceRe = this.whitespaceRe; + + for (i = 0, length = arguments.length; i < length; i++) { + clsList = arguments[i]; + if (Ext.isString(clsList)) { + clsList = clsList.replace(trimRe, '').split(whitespaceRe); + } + if (clsList) { + for (j = 0, listLength = clsList.length; j < listLength; j++) { + clsName = clsList[j]; + if (!clsHash[clsName]) { + if (i) { + changed = true; + } + clsHash[clsName] = true; + } + } + } + } + + for (clsName in clsHash) { + result.push(clsName); + } + result.changed = changed; + return result; + }, + + + removeCls: function(existingClsList, removeClsList) { + var clsHash = {}, + i, length, clsName, result = [], + changed = false, + whitespaceRe = this.whitespaceRe; + + if (existingClsList) { + if (Ext.isString(existingClsList)) { + existingClsList = existingClsList.replace(this.trimRe, '').split(whitespaceRe); + } + for (i = 0, length = existingClsList.length; i < length; i++) { + clsHash[existingClsList[i]] = true; + } + } + if (removeClsList) { + if (Ext.isString(removeClsList)) { + removeClsList = removeClsList.split(whitespaceRe); + } + for (i = 0, length = removeClsList.length; i < length; i++) { + clsName = removeClsList[i]; + if (clsHash[clsName]) { + changed = true; + delete clsHash[clsName]; + } + } + } + for (clsName in clsHash) { + result.push(clsName); + } + result.changed = changed; + return result; + }, + + + VISIBILITY: 1, + + + DISPLAY: 2, + + + OFFSETS: 3, + + + ASCLASS: 4 + }, + + constructor: function(element, forceNew) { + var me = this, + dom = typeof element == 'string' + ? document.getElementById(element) + : element, + id; + + + + + me.el = me; + + if (!dom) { + return null; + } + + id = dom.id; + if (!forceNew && id && Ext.cache[id]) { + + return Ext.cache[id].el; + } + + + me.dom = dom; + + + me.id = id || Ext.id(dom); + + me.self.addToCache(me); + }, + + + set: function(o, useSet) { + var el = this.dom, + attr, + value; + + for (attr in o) { + if (o.hasOwnProperty(attr)) { + value = o[attr]; + if (attr == 'style') { + this.applyStyles(value); + } + else if (attr == 'cls') { + el.className = value; + } + else if (useSet !== false) { + if (value === undefined) { + el.removeAttribute(attr); + } else { + el.setAttribute(attr, value); + } + } + else { + el[attr] = value; + } + } + } + return this; + }, + + + defaultUnit: "px", + + + is: function(simpleSelector) { + return Ext.DomQuery.is(this.dom, simpleSelector); + }, + + + getValue: function(asNumber) { + var val = this.dom.value; + return asNumber ? parseInt(val, 10) : val; + }, + + + remove: function() { + var me = this, + dom = me.dom; + + if (me.isAnimate) { + me.stopAnimation(); + } + + if (dom) { + Ext.removeNode(dom); + delete me.dom; + } + }, + + + contains: (function () { + + + + + var isXpc = function (el) { + var s; + + try { + el = el.dom || el; + } catch (e) { + return true; + } + + s = HTMLElement.prototype.toString.call(el); + return s === '[xpconnect wrapped native prototype]' || s === '[object XULElement]'; + }; + + return function (el) { + if (!el || (Ext.isGecko3 && isXpc(el))) { + return false; + } + + var me = this, + dom = el.dom || el; + + + + return (dom === me.dom) || Ext.dom.AbstractElement.isAncestor(me.dom, dom); + }; + }()), + + + getAttribute: function(name, ns) { + var dom = this.dom; + return dom.getAttributeNS(ns, name) || dom.getAttribute(ns + ":" + name) || dom.getAttribute(name) || dom[name]; + }, + + + update: function(html) { + if (this.dom) { + this.dom.innerHTML = html; + } + return this; + }, + + + + setHTML: function(html) { + if(this.dom) { + this.dom.innerHTML = html; + } + return this; + }, + + + getHTML: function() { + return this.dom ? this.dom.innerHTML : ''; + }, + + + hide: function() { + this.setVisible(false); + return this; + }, + + + show: function() { + this.setVisible(true); + return this; + }, + + + setVisible: function(visible, animate) { + var me = this, + statics = me.self, + mode = me.getVisibilityMode(), + prefix = Ext.baseCSSPrefix; + + switch (mode) { + case statics.VISIBILITY: + me.removeCls([prefix + 'hidden-display', prefix + 'hidden-offsets']); + me[visible ? 'removeCls' : 'addCls'](prefix + 'hidden-visibility'); + break; + + case statics.DISPLAY: + me.removeCls([prefix + 'hidden-visibility', prefix + 'hidden-offsets']); + me[visible ? 'removeCls' : 'addCls'](prefix + 'hidden-display'); + break; + + case statics.OFFSETS: + me.removeCls([prefix + 'hidden-visibility', prefix + 'hidden-display']); + me[visible ? 'removeCls' : 'addCls'](prefix + 'hidden-offsets'); + break; + } + + return me; + }, + + getVisibilityMode: function() { + + + + var data = (this.$cache || this.getCache()).data, + visMode = data.visibilityMode; + + if (visMode === undefined) { + data.visibilityMode = visMode = this.self.DISPLAY; + } + + return visMode; + }, + + + setVisibilityMode: function(mode) { + (this.$cache || this.getCache()).data.visibilityMode = mode; + return this; + }, + + getCache: function() { + var me = this, + id = me.dom.id || Ext.id(me.dom); + + + + + me.$cache = Ext.cache[id] || Ext.addCacheEntry(id, null, me.dom); + + return me.$cache; + } +}, +function() { + var AbstractElement = this; + + + Ext.getDetachedBody = function () { + var detachedEl = AbstractElement.detachedBodyEl; + + if (!detachedEl) { + detachedEl = document.createElement('div'); + AbstractElement.detachedBodyEl = detachedEl = new AbstractElement.Fly(detachedEl); + detachedEl.isDetachedBody = true; + } + + return detachedEl; + }; + + + Ext.getElementById = function (id) { + var el = document.getElementById(id), + detachedBodyEl; + + if (!el && (detachedBodyEl = AbstractElement.detachedBodyEl)) { + el = detachedBodyEl.dom.querySelector('#' + Ext.escapeId(id)); + } + + return el; + }; + + + Ext.get = function(el) { + return Ext.dom.Element.get(el); + }; + + this.addStatics({ + + Fly: new Ext.Class({ + + + + + extend: AbstractElement, + + + isFly: true, + + constructor: function(dom) { + this.dom = dom; + + + + this.el = this; + }, + + + attach: function (dom) { + + + this.dom = dom; + + + this.$cache = dom && dom.id ? Ext.cache[dom.id] : null; + return this; + } + }), + + _flyweights: {}, + + + fly: function(dom, named) { + var fly = null, + _flyweights = AbstractElement._flyweights; + + named = named || '_global'; + + dom = Ext.getDom(dom); + + if (dom) { + fly = _flyweights[named] || (_flyweights[named] = new AbstractElement.Fly()); + + + + fly.dom = dom; + + + fly.$cache = dom.id ? Ext.cache[dom.id] : null; + } + return fly; + } + }); + + + Ext.fly = function() { + return AbstractElement.fly.apply(AbstractElement, arguments); + }; + + (function (proto) { + + proto.destroy = proto.remove; + + + if (document.querySelector) { + proto.getById = function (id, asDom) { + + + var dom = document.getElementById(id) || + this.dom.querySelector('#'+Ext.escapeId(id)); + return asDom ? dom : (dom ? Ext.get(dom) : null); + }; + } else { + proto.getById = function (id, asDom) { + var dom = document.getElementById(id); + return asDom ? dom : (dom ? Ext.get(dom) : null); + }; + } + }(this.prototype)); +}); + + + + + + + + +Ext.define('Ext.dom.Helper', (function() { + + +var afterbegin = 'afterbegin', + afterend = 'afterend', + beforebegin = 'beforebegin', + beforeend = 'beforeend', + ts = '', + te = '
', + tbs = ts+'', + tbe = ''+te, + trs = tbs + '', + tre = ''+tbe, + detachedDiv = document.createElement('div'), + bbValues = ['BeforeBegin', 'previousSibling'], + aeValues = ['AfterEnd', 'nextSibling'], + bb_ae_PositionHash = { + beforebegin: bbValues, + afterend: aeValues + }, + fullPositionHash = { + beforebegin: bbValues, + afterend: aeValues, + afterbegin: ['AfterBegin', 'firstChild'], + beforeend: ['BeforeEnd', 'lastChild'] + }; + + +return { + extend: Ext.dom.AbstractHelper , + + + tableRe: /^(?:table|thead|tbody|tr|td)$/i, + + tableElRe: /td|tr|tbody|thead/i, + + + useDom : false, + + + createDom: function(o, parentNode){ + var el, + doc = document, + useSet, + attr, + val, + cn, + i, l; + + if (Ext.isArray(o)) { + el = doc.createDocumentFragment(); + for (i = 0, l = o.length; i < l; i++) { + this.createDom(o[i], el); + } + } else if (typeof o == 'string') { + el = doc.createTextNode(o); + } else { + el = doc.createElement(o.tag || 'div'); + useSet = !!el.setAttribute; + for (attr in o) { + if (!this.confRe.test(attr)) { + val = o[attr]; + if (attr == 'cls') { + el.className = val; + } else { + if (useSet) { + el.setAttribute(attr, val); + } else { + el[attr] = val; + } + } + } + } + Ext.DomHelper.applyStyles(el, o.style); + + if ((cn = o.children || o.cn)) { + this.createDom(cn, el); + } else if (o.html) { + el.innerHTML = o.html; + } + } + if (parentNode) { + parentNode.appendChild(el); + } + return el; + }, + + ieTable: function(depth, openingTags, htmlContent, closingTags){ + detachedDiv.innerHTML = [openingTags, htmlContent, closingTags].join(''); + + var i = -1, + el = detachedDiv, + ns; + + while (++i < depth) { + el = el.firstChild; + } + + ns = el.nextSibling; + + if (ns) { + ns = el; + el = document.createDocumentFragment(); + + while (ns) { + nx = ns.nextSibling; + el.appendChild(ns); + ns = nx; + } + } + return el; + }, + + + insertIntoTable: function(tag, where, destinationEl, html) { + var node, + before, + bb = where == beforebegin, + ab = where == afterbegin, + be = where == beforeend, + ae = where == afterend; + + if (tag == 'td' && (ab || be) || !this.tableElRe.test(tag) && (bb || ae)) { + return null; + } + before = bb ? destinationEl : + ae ? destinationEl.nextSibling : + ab ? destinationEl.firstChild : null; + + if (bb || ae) { + destinationEl = destinationEl.parentNode; + } + + if (tag == 'td' || (tag == 'tr' && (be || ab))) { + node = this.ieTable(4, trs, html, tre); + } else if (((tag == 'tbody' || tag == 'thead') && (be || ab)) || + (tag == 'tr' && (bb || ae))) { + node = this.ieTable(3, tbs, html, tbe); + } else { + node = this.ieTable(2, ts, html, te); + } + destinationEl.insertBefore(node, before); + return node; + }, + + + createContextualFragment: function(html) { + var fragment = document.createDocumentFragment(), + length, childNodes; + + detachedDiv.innerHTML = html; + childNodes = detachedDiv.childNodes; + length = childNodes.length; + + + while (length--) { + fragment.appendChild(childNodes[0]); + } + return fragment; + }, + + applyStyles: function(el, styles) { + if (styles) { + if (typeof styles == "function") { + styles = styles.call(); + } + if (typeof styles == "string") { + styles = Ext.dom.Element.parseStyles(styles); + } + if (typeof styles == "object") { + Ext.fly(el, '_applyStyles').setStyle(styles); + } + } + }, + + + createHtml: function(spec) { + return this.markup(spec); + }, + + doInsert: function(el, o, returnElement, pos, sibling, append) { + + el = el.dom || Ext.getDom(el); + + var newNode; + + if (this.useDom) { + newNode = this.createDom(o, null); + + if (append) { + el.appendChild(newNode); + } + else { + (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el); + } + + } else { + newNode = this.insertHtml(pos, el, this.markup(o)); + } + return returnElement ? Ext.get(newNode, true) : newNode; + }, + + + overwrite: function(el, html, returnElement) { + var newNode; + + el = Ext.getDom(el); + html = this.markup(html); + + + if (Ext.isIE && this.tableRe.test(el.tagName)) { + + while (el.firstChild) { + el.removeChild(el.firstChild); + } + if (html) { + newNode = this.insertHtml('afterbegin', el, html); + return returnElement ? Ext.get(newNode) : newNode; + } + return null; + } + el.innerHTML = html; + return returnElement ? Ext.get(el.firstChild) : el.firstChild; + }, + + insertHtml: function(where, el, html) { + var hashVal, + range, + rangeEl, + setStart, + frag; + + where = where.toLowerCase(); + + + if (el.insertAdjacentHTML) { + + + if (Ext.isIE && this.tableRe.test(el.tagName) && (frag = this.insertIntoTable(el.tagName.toLowerCase(), where, el, html))) { + return frag; + } + + if ((hashVal = fullPositionHash[where])) { + + if (Ext.global.MSApp && Ext.global.MSApp.execUnsafeLocalFunction) { + + MSApp.execUnsafeLocalFunction(function () { + el.insertAdjacentHTML(hashVal[0], html); + }); + } else { + el.insertAdjacentHTML(hashVal[0], html); + } + + return el[hashVal[1]]; + } + + } else { + + if (el.nodeType === 3) { + where = where === 'afterbegin' ? 'beforebegin' : where; + where = where === 'beforeend' ? 'afterend' : where; + } + range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined; + setStart = 'setStart' + (this.endRe.test(where) ? 'After' : 'Before'); + if (bb_ae_PositionHash[where]) { + if (range) { + range[setStart](el); + frag = range.createContextualFragment(html); + } else { + frag = this.createContextualFragment(html); + } + el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling); + return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling']; + } else { + rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child'; + if (el.firstChild) { + if (range) { + range[setStart](el[rangeEl]); + frag = range.createContextualFragment(html); + } else { + frag = this.createContextualFragment(html); + } + + if (where == afterbegin) { + el.insertBefore(frag, el.firstChild); + } else { + el.appendChild(frag); + } + } else { + el.innerHTML = html; + } + return el[rangeEl]; + } + } + Ext.Error.raise({ + sourceClass: 'Ext.DomHelper', + sourceMethod: 'insertHtml', + htmlToInsert: html, + targetElement: el, + msg: 'Illegal insertion point reached: "' + where + '"' + }); + }, + + + createTemplate: function(o) { + var html = this.markup(o); + return new Ext.Template(html); + } + +}; +})(), function() { + Ext.ns('Ext.core'); + Ext.DomHelper = Ext.core.DomHelper = new this; +}); + + + + +Ext.define('Ext.Template', { + + + + + + inheritableStatics: { + + from: function(el, config) { + el = Ext.getDom(el); + return new this(el.value || el.innerHTML, config || ''); + } + }, + + + + + useEval: Ext.isGecko, + + + + + constructor: function(html) { + var me = this, + args = arguments, + buffer = [], + i = 0, + length = args.length, + value; + + me.initialConfig = {}; + + + + + if (length === 1 && Ext.isArray(html)) { + args = html; + length = args.length; + } + + if (length > 1) { + for (; i < length; i++) { + value = args[i]; + if (typeof value == 'object') { + Ext.apply(me.initialConfig, value); + Ext.apply(me, value); + } else { + buffer.push(value); + } + } + } else { + buffer.push(html); + } + + + me.html = buffer.join(''); + + if (me.compiled) { + me.compile(); + } + }, + + + isTemplate: true, + + + + + disableFormats: false, + + + re: /\{(?:(?:(\d*)|([\w\-]+))(?:\:([A-Za-z_\.]*)(?:\((.*?)?\))?)?)\}/g, + + + apply: function(values) { + if (this.compiled) { + return this.compiled(values).join(''); + } + return this.evaluate(values); + }, + + + + evaluate: function(values) { + var me = this, + useFormat = me.disableFormats !== true, + fm = Ext.util.Format, + tpl = me; + + function fn(match, index, name, formatFn, args) { + + + if (name == null || name == '') { + name = index; + } + if (formatFn && useFormat) { + if (args) { + args = [values[name]].concat(Ext.functionFactory('return ['+ args +'];')()); + } else { + args = [values[name]]; + } + + + if (formatFn.substr(0, 5) === "this.") { + return tpl[formatFn.substr(5)].apply(tpl, args); + } + + else if (fm[formatFn]) { + return fm[formatFn].apply(fm, args); + } + + else { + return match; + } + } + else { + return values[name] !== undefined ? values[name] : ""; + } + } + + return me.html.replace(me.re, fn); + }, + + + applyOut: function(values, out) { + var me = this; + + if (me.compiled) { + out.push.apply(out, me.compiled(values)); + } else { + out.push(me.apply(values)); + } + + return out; + }, + + + applyTemplate: function () { + return this.apply.apply(this, arguments); + }, + + + set: function(html, compile) { + var me = this; + me.html = html; + me.compiled = null; + return compile ? me.compile() : me; + }, + + compileARe: /\\/g, + compileBRe: /(\r\n|\n)/g, + compileCRe: /'/g, + + /** + * Compiles the template into an internal function, eliminating the RegEx overhead. + * @return {Ext.Template} this + */ + compile: function() { + var me = this, + code; + + code = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, Ext.Function.bind(me.regexReplaceFn, me)); + code = (this.disableFormats !== true ? 'var fm=Ext.util.Format;' : '') + + (me.useEval ? '$=' : 'return') + + " function(v){return ['" + code + "'];};"; + me.compiled = me.useEval ? me.evalCompiled(code) : (new Function('Ext', code))(Ext); + return me; + }, + + // @private + evalCompiled: function($) { + + // We have to use eval to realize the code block and capture the inner func we also + // don't want a deep scope chain. We only do this in Firefox and it is also unhappy + // with eval containing a return statement, so instead we assign to "$" and return + // that. Because we use "eval", we are automatically sandboxed properly. + eval($); + return $; + }, + + regexReplaceFn: function fn(match, index, name, formatFn, args) { + // Calculate the correct expression to use to index into the values object/array + // index may be a numeric string, or a quoted alphanumeric string. + // Certain browser pass unmatched parameters as undefined, some as an empty string. + if (index == null || index == '') { + index = '"' + name + '"'; + } + // If we are being used as a formatter for Ext.String.format, we must skip the string itself in the argument list. + // Doing this enables String.format to omit the Array slice call. + else if (this.stringFormat) { + index = parseInt(index) + 1; + } + if (formatFn && this.disableFormats !== true) { + args = args ? ',' + args: ""; + + // Caller used '{0:this.bold}'. Create a call to member function + if (formatFn.substr(0, 5) === "this.") { + formatFn = formatFn + '('; + } + // Caller used '{0:number("0.00")}'. Create a call to Ext.util.Format function + else if (Ext.util.Format[formatFn]) { + formatFn = "fm." + formatFn + '('; + } + // Caller used '{0:someRandomText}'. We must pass it through unchanged + else { + return match; + } + return "'," + formatFn + "v[" + index + "]" + args + "),'"; + } + else { + return "',v[" + index + "] == undefined ? '' : v[" + index + "],'"; + } + }, + + /** + * Applies the supplied values to the template and inserts the new node(s) as the first child of el. + * + * @param {String/HTMLElement/Ext.Element} el The context element + * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. + * @param {Boolean} returnElement (optional) true to return a Ext.Element. + * @return {HTMLElement/Ext.Element} The new node or Element + */ + insertFirst: function(el, values, returnElement) { + return this.doInsert('afterBegin', el, values, returnElement); + }, + + /** + * Applies the supplied values to the template and inserts the new node(s) before el. + * + * @param {String/HTMLElement/Ext.Element} el The context element + * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. + * @param {Boolean} returnElement (optional) true to return a Ext.Element. + * @return {HTMLElement/Ext.Element} The new node or Element + */ + insertBefore: function(el, values, returnElement) { + return this.doInsert('beforeBegin', el, values, returnElement); + }, + + /** + * Applies the supplied values to the template and inserts the new node(s) after el. + * + * @param {String/HTMLElement/Ext.Element} el The context element + * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. + * @param {Boolean} returnElement (optional) true to return a Ext.Element. + * @return {HTMLElement/Ext.Element} The new node or Element + */ + insertAfter: function(el, values, returnElement) { + return this.doInsert('afterEnd', el, values, returnElement); + }, + + /** + * Applies the supplied `values` to the template and appends the new node(s) to the specified `el`. + * + * For example usage see {@link Ext.Template Ext.Template class docs}. + * + * @param {String/HTMLElement/Ext.Element} el The context element + * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. + * @param {Boolean} returnElement (optional) true to return an Ext.Element. + * @return {HTMLElement/Ext.Element} The new node or Element + */ + append: function(el, values, returnElement) { + return this.doInsert('beforeEnd', el, values, returnElement); + }, + + doInsert: function(where, el, values, returnElement) { + var newNode = Ext.DomHelper.insertHtml(where, Ext.getDom(el), this.apply(values)); + return returnElement ? Ext.get(newNode) : newNode; + }, + + /** + * Applies the supplied values to the template and overwrites the content of el with the new node(s). + * + * @param {String/HTMLElement/Ext.Element} el The context element + * @param {Object/Array} values The template values. See {@link #applyTemplate} for details. + * @param {Boolean} returnElement (optional) true to return a Ext.Element. + * @return {HTMLElement/Ext.Element} The new node or Element + */ + overwrite: function(el, values, returnElement) { + var newNode = Ext.DomHelper.overwrite(Ext.getDom(el), this.apply(values)); + return returnElement ? Ext.get(newNode) : newNode; + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +// @tag core +/** + * This class parses the XTemplate syntax and calls abstract methods to process the parts. + * @private + */ +Ext.define('Ext.XTemplateParser', { + constructor: function (config) { + Ext.apply(this, config); + }, + + /** + * @property {Number} level The 'for' or 'foreach' loop context level. This is adjusted + * up by one prior to calling {@link #doFor} or {@link #doForEach} and down by one after + * calling the corresponding {@link #doEnd} that closes the loop. This will be 1 on the + * first {@link #doFor} or {@link #doForEach} call. + */ + + /** + * This method is called to process a piece of raw text from the tpl. + * @param {String} text + * @method doText + */ + // doText: function (text) + + /** + * This method is called to process expressions (like `{[expr]}`). + * @param {String} expr The body of the expression (inside "{[" and "]}"). + * @method doExpr + */ + // doExpr: function (expr) + + /** + * This method is called to process simple tags (like `{tag}`). + * @method doTag + */ + // doTag: function (tag) + + /** + * This method is called to process ``. + * @method doElse + */ + // doElse: function () + + /** + * This method is called to process `{% text %}`. + * @param {String} text + * @method doEval + */ + // doEval: function (text) + + /** + * This method is called to process ``. If there are other attributes, + * these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). + * @method doIf + */ + // doIf: function (action, actions) + + /** + * This method is called to process ``. If there are other attributes, + * these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). + * @method doElseIf + */ + // doElseIf: function (action, actions) + + /** + * This method is called to process ``. If there are other attributes, + * these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). + * @method doSwitch + */ + // doSwitch: function (action, actions) + + /** + * This method is called to process ``. If there are other attributes, + * these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). + * @method doCase + */ + // doCase: function (action, actions) + + /** + * This method is called to process ``. + * @method doDefault + */ + // doDefault: function () + + /** + * This method is called to process ``. It is given the action type that started + * the tpl and the set of additional actions. + * @param {String} type The type of action that is being ended. + * @param {Object} actions The other actions keyed by the attribute name (such as 'exec'). + * @method doEnd + */ + // doEnd: function (type, actions) + + /** + * This method is called to process ``. If there are other attributes, + * these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). + * @method doFor + */ + // doFor: function (action, actions) + + /** + * This method is called to process ``. If there are other + * attributes, these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name (such as 'exec'). + * @method doForEach + */ + // doForEach: function (action, actions) + + /** + * This method is called to process ``. If there are other attributes, + * these are passed in the actions object. + * @param {String} action + * @param {Object} actions Other actions keyed by the attribute name. + * @method doExec + */ + // doExec: function (action, actions) + + /** + * This method is called to process an empty ``. This is unlikely to need to be + * implemented, so a default (do nothing) version is provided. + * @method + */ + doTpl: Ext.emptyFn, + + parse: function (str) { + var me = this, + len = str.length, + aliases = { elseif: 'elif' }, + topRe = me.topRe, + actionsRe = me.actionsRe, + index, stack, s, m, t, prev, frame, subMatch, begin, end, actions, + prop, expectTplNext; + + me.level = 0; + me.stack = stack = []; + + for (index = 0; index < len; index = end) { + topRe.lastIndex = index; + m = topRe.exec(str); + + if (!m) { + me.doText(str.substring(index, len)); + break; + } + + begin = m.index; + end = topRe.lastIndex; + + if (index < begin) { + // In the case of a switch statement, we expect a tpl for each case. + // However, if we have spaces they will get matched as plaintext, so + // we want to skip over them here. + s = str.substring(index, begin); + if (!(expectTplNext && Ext.String.trim(s) === '')) { + me.doText(s); + } + } + + expectTplNext = false; + + if (m[1]) { + end = str.indexOf('%}', begin+2); + me.doEval(str.substring(begin+2, end)); + end += 2; + } else if (m[2]) { + end = str.indexOf(']}', begin+2); + me.doExpr(str.substring(begin+2, end)); + end += 2; + } else if (m[3]) { // if ('{' token) + me.doTag(m[3]); + } else if (m[4]) { // content of a tag + actions = null; + while ((subMatch = actionsRe.exec(m[4])) !== null) { + s = subMatch[2] || subMatch[3]; + if (s) { + s = Ext.String.htmlDecode(s); // decode attr value + t = subMatch[1]; + t = aliases[t] || t; + actions = actions || {}; + prev = actions[t]; + + if (typeof prev == 'string') { + actions[t] = [prev, s]; + } else if (prev) { + actions[t].push(s); + } else { + actions[t] = s; + } + } + } + + if (!actions) { + if (me.elseRe.test(m[4])) { + me.doElse(); + } else if (me.defaultRe.test(m[4])) { + me.doDefault(); + } else { + me.doTpl(); + stack.push({ type: 'tpl' }); + } + } + else if (actions['if']) { + me.doIf(actions['if'], actions); + stack.push({ type: 'if' }); + } + else if (actions['switch']) { + me.doSwitch(actions['switch'], actions); + stack.push({ type: 'switch' }); + expectTplNext = true; + } + else if (actions['case']) { + me.doCase(actions['case'], actions); + } + else if (actions['elif']) { + me.doElseIf(actions['elif'], actions); + } + else if (actions['for']) { + ++me.level; + + // Extract property name to use from indexed item + if (prop = me.propRe.exec(m[4])) { + actions.propName = prop[1] || prop[2]; + } + me.doFor(actions['for'], actions); + stack.push({ type: 'for', actions: actions }); + } + else if (actions['foreach']) { + ++me.level; + + // Extract property name to use from indexed item + if (prop = me.propRe.exec(m[4])) { + actions.propName = prop[1] || prop[2]; + } + me.doForEach(actions['foreach'], actions); + stack.push({ type: 'foreach', actions: actions }); + } + else if (actions.exec) { + me.doExec(actions.exec, actions); + stack.push({ type: 'exec', actions: actions }); + } + /* + else { + // todo - error + } + */ + } else if (m[0].length === 5) { + // if the length of m[0] is 5, assume that we're dealing with an opening tpl tag with no attributes (e.g. ...) + // in this case no action is needed other than pushing it on to the stack + stack.push({ type: 'tpl' }); + } else { + frame = stack.pop(); + me.doEnd(frame.type, frame.actions); + if (frame.type == 'for' || frame.type == 'foreach') { + --me.level; + } + } + } + }, + + // Internal regexes + + topRe: /(?:(\{\%)|(\{\[)|\{([^{}]+)\})|(?:]*)\>)|(?:<\/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*$/ +}); + + + + +Ext.define('Ext.XTemplateCompiler', { + extend: Ext.XTemplateParser , + + + + + useEval: Ext.isGecko, + + + + + useIndex: Ext.isIE8m, + + useFormat: true, + + propNameRe: /^[\w\d\$]*$/, + + compile: function (tpl) { + var me = this, + code = me.generate(tpl); + + + + + + return me.useEval ? me.evalTpl(code) : (new Function('Ext', code))(Ext); + }, + + generate: function (tpl) { + var me = this, + + definitions = 'var fm=Ext.util.Format,ts=Object.prototype.toString;', + code; + + + me.maxLevel = 0; + + me.body = [ + 'var c0=values, a0=' + me.createArrayTest(0) + ', p0=parent, n0=xcount, i0=xindex, k0, v;\n' + ]; + if (me.definitions) { + if (typeof me.definitions === 'string') { + me.definitions = [me.definitions, definitions ]; + } else { + me.definitions.push(definitions); + } + } else { + me.definitions = [ definitions ]; + } + me.switches = []; + + me.parse(tpl); + + me.definitions.push( + (me.useEval ? '$=' : 'return') + ' function (' + me.fnArgs + ') {', + me.body.join(''), + '}' + ); + + code = me.definitions.join('\n'); + + + me.definitions.length = me.body.length = me.switches.length = 0; + delete me.definitions; + delete me.body; + delete me.switches; + + return code; + }, + + + + + doText: function (text) { + var me = this, + out = me.body; + + text = text.replace(me.aposRe, "\\'").replace(me.newLineRe, '\\n'); + if (me.useIndex) { + out.push('out[out.length]=\'', text, '\'\n'); + } else { + out.push('out.push(\'', text, '\')\n'); + } + }, + + doExpr: function (expr) { + var out = this.body; + out.push('if ((v=' + expr + ') != null) out'); + + + + if (this.useIndex) { + out.push('[out.length]=v+\'\'\n'); + } else { + out.push('.push(v+\'\')\n'); + } + }, + + doTag: function (tag) { + var expr = this.parseTag(tag); + if (expr) { + this.doExpr(expr); + } else { + + this.doText('{' + tag + '}'); + } + }, + + doElse: function () { + this.body.push('} else {\n'); + }, + + doEval: function (text) { + this.body.push(text, '\n'); + }, + + doIf: function (action, actions) { + var me = this; + + + if (action === '.') { + me.body.push('if (values) {\n'); + } else if (me.propNameRe.test(action)) { + me.body.push('if (', me.parseTag(action), ') {\n'); + } + + else { + me.body.push('if (', me.addFn(action), me.callFn, ') {\n'); + } + if (actions.exec) { + me.doExec(actions.exec); + } + }, + + doElseIf: function (action, actions) { + var me = this; + + + if (action === '.') { + me.body.push('else if (values) {\n'); + } else if (me.propNameRe.test(action)) { + me.body.push('} else if (', me.parseTag(action), ') {\n'); + } + + else { + me.body.push('} else if (', me.addFn(action), me.callFn, ') {\n'); + } + if (actions.exec) { + me.doExec(actions.exec); + } + }, + + doSwitch: function (action) { + var me = this, + key; + + + if (action === '.' || action === '#') { + key = action === '.' ? 'values' : 'xindex'; + me.body.push('switch (', key, ') {\n'); + } else if (me.propNameRe.test(action)) { + me.body.push('switch (', me.parseTag(action), ') {\n'); + } + + else { + me.body.push('switch (', me.addFn(action), me.callFn, ') {\n'); + } + me.switches.push(0); + }, + + doCase: function (action) { + var me = this, + cases = Ext.isArray(action) ? action : [action], + n = me.switches.length - 1, + match, i; + + if (me.switches[n]) { + me.body.push('break;\n'); + } else { + me.switches[n]++; + } + + for (i = 0, n = cases.length; i < n; ++i) { + match = me.intRe.exec(cases[i]); + cases[i] = match ? match[1] : ("'" + cases[i].replace(me.aposRe,"\\'") + "'"); + } + + me.body.push('case ', cases.join(': case '), ':\n'); + }, + + doDefault: function () { + var me = this, + n = me.switches.length - 1; + + if (me.switches[n]) { + me.body.push('break;\n'); + } else { + me.switches[n]++; + } + + me.body.push('default:\n'); + }, + + doEnd: function (type, actions) { + var me = this, + L = me.level-1; + + if (type == 'for' || type == 'foreach') { + + if (actions.exec) { + me.doExec(actions.exec); + } + + me.body.push('}\n'); + me.body.push('parent=p',L,';values=r',L+1,';xcount=n'+L+';xindex=i',L,'+1;xkey=k',L,';\n'); + } else if (type == 'if' || type == 'switch') { + me.body.push('}\n'); + } + }, + + doFor: function (action, actions) { + var me = this, + s, + L = me.level, + up = L-1, + parentAssignment; + + + if (action === '.') { + s = 'values'; + } else if (me.propNameRe.test(action)) { + s = me.parseTag(action); + } + + else { + s = me.addFn(action) + me.callFn; + } + + + + + if (me.maxLevel < L) { + me.maxLevel = L; + me.body.push('var '); + } + + if (action == '.') { + parentAssignment = 'c' + L; + } else { + parentAssignment = 'a' + up + '?c' + up + '[i' + up + ']:c' + up; + } + + me.body.push('i',L,'=0,n', L, '=0,c',L,'=',s,',a',L,'=', me.createArrayTest(L),',r',L,'=values,p',L,',k',L,';\n', + 'p',L,'=parent=',parentAssignment,'\n', + 'if (c',L,'){if(a',L,'){n', L,'=c', L, '.length;}else if (c', L, '.isMixedCollection){c',L,'=c',L,'.items;n',L,'=c',L,'.length;}else if(c',L,'.isStore){c',L,'=c',L,'.data.items;n',L,'=c',L,'.length;}else{c',L,'=[c',L,'];n',L,'=1;}}\n', + 'for (xcount=n',L,';i',L,'1){ out.push("',actions.between,'"); } \n'); + } + }, + + doForEach: function (action, actions) { + var me = this, + s, + L = me.level, + up = L-1, + parentAssignment; + + + if (action === '.') { + s = 'values'; + } else if (me.propNameRe.test(action)) { + s = me.parseTag(action); + } + + else { + s = me.addFn(action) + me.callFn; + } + + + + + if (me.maxLevel < L) { + me.maxLevel = L; + me.body.push('var '); + } + + if (action == '.') { + parentAssignment = 'c' + L; + } else { + parentAssignment = 'a' + up + '?c' + up + '[i' + up + ']:c' + up; + } + + me.body.push('i',L,'=-1,n',L,'=0,c',L,'=',s,',a',L,'=',me.createArrayTest(L),',r',L,'=values,p',L,',k',L,';\n', + 'p',L,'=parent=',parentAssignment,'\n', + 'for(k',L,' in c',L,'){\n', + 'xindex=++i',L,'+1;\n', + 'xkey=k',L,';\n', + 'values=c',L,'[k',L,'];'); + if (actions.propName) { + me.body.push('.', actions.propName); + } + + if (actions.between) { + me.body.push('if(xindex>1){ out.push("',actions.between,'"); } \n'); + } + }, + + createArrayTest: ('isArray' in Array) ? function(L) { + return 'Array.isArray(c' + L + ')'; + } : function(L) { + return 'ts.call(c' + L + ')==="[object Array]"'; + }, + + doExec: function (action, actions) { + var me = this, + name = 'f' + me.definitions.length; + + me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', + ' try { with(values) {', + ' ' + action, + ' }} catch(e) {', + 'Ext.log("XTemplate Error: " + e.message);', + '}', + '}'); + + me.body.push(name + me.callFn + '\n'); + }, + + + + + addFn: function (body) { + var me = this, + name = 'f' + me.definitions.length; + + if (body === '.') { + me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', + ' return values', + '}'); + } else if (body === '..') { + me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', + ' return parent', + '}'); + } else { + me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', + ' try { with(values) {', + ' return(' + body + ')', + ' }} catch(e) {', + 'Ext.log("XTemplate Error: " + e.message);', + '}', + '}'); + } + + return name; + }, + + parseTag: function (tag) { + var me = this, + m = me.tagRe.exec(tag), + name, format, args, math, v; + + if (!m) { + return null; + } + + name = m[1]; + format = m[2]; + args = m[3]; + math = m[4]; + + + if (name == '.') { + + if (!me.validTypes) { + me.definitions.push('var validTypes={string:1,number:1,boolean:1};'); + me.validTypes = true; + } + v = 'validTypes[typeof values] || ts.call(values) === "[object Date]" ? values : ""'; + } + + else if (name == '#') { + v = 'xindex'; + } + + else if (name == '$') { + v = 'xkey'; + } + else if (name.substr(0, 7) == "parent.") { + v = name; + } + + else if (isNaN(name) && name.indexOf('-') == -1 && name.indexOf('.') != -1) { + v = "values." + name; + } + + + else { + v = "values['" + name + "']"; + } + + if (math) { + v = '(' + v + math + ')'; + } + + if (format && me.useFormat) { + args = args ? ',' + args : ""; + if (format.substr(0, 5) != "this.") { + format = "fm." + format + '('; + } else { + format += '('; + } + } else { + return v; + } + + return format + v + args + ')'; + }, + + + evalTpl: function ($) { + + + + + + eval($); + return $; + }, + + newLineRe: /\r\n|\r|\n/g, + aposRe: /[']/g, + intRe: /^\s*(\d+)\s*$/, + tagRe: /^([\w-\.\#\$]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?$/ + +}, function () { + var proto = this.prototype; + + proto.fnArgs = 'out,values,parent,xindex,xcount,xkey'; + proto.callFn = '.call(this,' + proto.fnArgs + ')'; +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +// @tag core +/** + * A template class that supports advanced functionality like: + * + * - Autofilling arrays using templates and sub-templates + * - Conditional processing with basic comparison operators + * - Basic math function support + * - Execute arbitrary inline code with special built-in template variables + * - Custom member functions + * - Many special tags and built-in operators that aren't defined as part of the API, but are supported in the templates that can be created + * + * XTemplate provides the templating mechanism built into {@link Ext.view.View}. + * + * The {@link Ext.Template} describes the acceptable parameters to pass to the constructor. The following examples + * demonstrate all of the supported features. + * + * # Sample Data + * + * This is the data object used for reference in each code example: + * + * var data = { + * name: 'Don Griffin', + * title: 'Senior Technomage', + * company: 'Sencha Inc.', + * drinks: ['Coffee', 'Water', 'More Coffee'], + * kids: [ + * { name: 'Aubrey', age: 17 }, + * { name: 'Joshua', age: 13 }, + * { name: 'Cale', age: 10 }, + * { name: 'Nikol', age: 5 }, + * { name: 'Solomon', age: 0 } + * ] + * }; + * + * # Auto filling of arrays + * + * The **tpl** tag and the **for** operator are used to process the provided data object: + * + * - If the value specified in for is an array, it will auto-fill, repeating the template block inside the tpl + * tag for each item in the array. + * - If for="." is specified, the data object provided is examined. + * - If between="..." is specified, the provided value will be inserted between the items. + * This is also supported in the "foreach" looping template. + * - While processing an array, the special variable {#} will provide the current array index + 1 (starts at 1, not 0). + * + * Examples: + * + * ... + * ... + * ... + * ... + * + * Using the sample data above: + * + * var tpl = new Ext.XTemplate( + * '

Kids: ', + * '', + * '

{#}. {name}

', + * '

' + * ); + * tpl.overwrite(panel.body, data.kids); + * + * An example illustrating how the **for** property can be leveraged to access specified members of the provided data + * object to populate the template: + * + * var tpl = new Ext.XTemplate( + * '

Name: {name}

', + * '

Title: {title}

', + * '

Company: {company}

', + * '

Kids: ', + * '', + * '

{name}

', + * '

' + * ); + * tpl.overwrite(panel.body, data); + * + * Flat arrays that contain values (and not objects) can be auto-rendered using the special **`{.}`** variable inside a + * loop. This variable will represent the value of the array at the current index: + * + * var tpl = new Ext.XTemplate( + * '

{name}\'s favorite beverages:

', + * '', + * '
- {.}
', + * '
' + * ); + * tpl.overwrite(panel.body, data); + * + * When processing a sub-template, for example while looping through a child array, you can access the parent object's + * members via the **parent** object: + * + * var tpl = new Ext.XTemplate( + * '

Name: {name}

', + * '

Kids: ', + * '', + * '', + * '

{name}

', + * '

Dad: {parent.name}

', + * '
', + * '

' + * ); + * tpl.overwrite(panel.body, data); + * + * The **foreach** operator is used to loop over an object's properties. The following + * example demonstrates looping over the main data object's properties: + * + * var tpl = new Ext.XTemplate( + * '
', + * '', + * '
{$}
', // the special **`{$}`** variable contains the property name + * '
{.}
', // within the loop, the **`{.}`** variable is set to the property value + * '
', + * '
' + * ); + * tpl.overwrite(panel.body, data); + * + * # Conditional processing with basic comparison operators + * + * The **tpl** tag and the **if** operator are used to provide conditional checks for deciding whether or not to render + * specific parts of the template. + * + * Using the sample data above: + * + * var tpl = new Ext.XTemplate( + * '

Name: {name}

', + * '

Kids: ', + * '', + * '', + * '

{name}

', + * '
', + * '

' + * ); + * tpl.overwrite(panel.body, data); + * + * More advanced conditionals are also supported: + * + * var tpl = new Ext.XTemplate( + * '

Name: {name}

', + * '

Kids: ', + * '', + * '

{name} is a ', + * '', + * '

teenager

', + * '', + * '

kid

', + * '', + * '

baby

', + * '
', + * '

' + * ); + * + * var tpl = new Ext.XTemplate( + * '

Name: {name}

', + * '

Kids: ', + * '', + * '

{name} is a ', + * '', + * '', + * '

girl

', + * '', + * '

boy

', + * '
', + * '

' + * ); + * + * A `break` is implied between each case and default, however, multiple cases can be listed + * in a single <tpl> tag. + * + * # Using double quotes + * + * Examples: + * + * var tpl = new Ext.XTemplate( + * "Child", + * "Teenager", + * "...", + * '...', + * "", + * "Hello" + * ); + * + * # Basic math support + * + * The following basic math operators may be applied directly on numeric data values: + * + * + - * / + * + * For example: + * + * var tpl = new Ext.XTemplate( + * '

Name: {name}

', + * '

Kids: ', + * '', + * '', + * '

{#}: {name}

', + * '

In 5 Years: {age+5}

', + * '

Dad: {parent.name}

', + * '
', + * '

' + * ); + * tpl.overwrite(panel.body, data); + * + * # Execute arbitrary inline code with special built-in template variables + * + * Anything between `{[ ... ]}` is considered code to be executed in the scope of the template. + * The expression is evaluated and the result is included in the generated result. There are + * some special variables available in that code: + * + * - **out**: The output array into which the template is being appended (using `push` to later + * `join`). + * - **values**: The values in the current scope. If you are using scope changing sub-templates, + * you can change what values is. + * - **parent**: The scope (values) of the ancestor template. + * - **xindex**: If you are in a "for" or "foreach" looping template, the index of the loop you are in (1-based). + * - **xcount**: If you are in a "for" looping template, the total length of the array you are looping. + * - **xkey**: If you are in a "foreach" looping template, the key of the current property + * being examined. + * + * This example demonstrates basic row striping using an inline code block and the xindex variable: + * + * var tpl = new Ext.XTemplate( + * '

Name: {name}

', + * '

Company: {[values.company.toUpperCase() + ", " + values.title]}

', + * '

Kids: ', + * '', + * '

', + * '{name}', + * '
', + * '

' + * ); + * + * Any code contained in "verbatim" blocks (using "{% ... %}") will be inserted directly in + * the generated code for the template. These blocks are not included in the output. This + * can be used for simple things like break/continue in a loop, or control structures or + * method calls (when they don't produce output). The `this` references the template instance. + * + * var tpl = new Ext.XTemplate( + * '

Name: {name}

', + * '

Company: {[values.company.toUpperCase() + ", " + values.title]}

', + * '

Kids: ', + * '', + * '{% if (xindex % 2 === 0) continue; %}', + * '{name}', + * '{% if (xindex > 100) break; %}', + * '', + * '

' + * ); + * + * # Template member functions + * + * One or more member functions can be specified in a configuration object passed into the XTemplate constructor for + * more complex processing: + * + * var tpl = new Ext.XTemplate( + * '

Name: {name}

', + * '

Kids: ', + * '', + * '', + * '

Girl: {name} - {age}

', + * '', + * '

Boy: {name} - {age}

', + * '
', + * '', + * '

{name} is a baby!

', + * '
', + * '

', + * { + * // XTemplate configuration: + * disableFormats: true, + * // member functions: + * isGirl: function(name){ + * return name == 'Aubrey' || name == 'Nikol'; + * }, + * isBaby: function(age){ + * return age < 1; + * } + * } + * ); + * tpl.overwrite(panel.body, data); + */ +Ext.define('Ext.XTemplate', { + extend: Ext.Template , + + + + /** + * @private + */ + emptyObj: {}, + + /** + * @cfg {Boolean} compiled + * Only applies to {@link Ext.Template}, XTemplates are compiled automatically on the + * first call to {@link #apply} or {@link #applyOut}. + * @hide + */ + + /** + * @cfg {String/Array} definitions + * Optional. A statement, or array of statements which set up `var`s which may then + * be accessed within the scope of the generated function. + */ + + apply: function(values, parent) { + return this.applyOut(values, [], parent).join(''); + }, + + applyOut: function(values, out, parent) { + var me = this, + compiler; + + if (!me.fn) { + compiler = new Ext.XTemplateCompiler({ + useFormat: me.disableFormats !== true, + definitions: me.definitions + }); + + me.fn = compiler.compile(me.html); + } + + try { + me.fn(out, values, parent || me.emptyObj, 1, 1); + } catch (e) { + Ext.log('Error: ' + e.message); + } + + return out; + }, + + /** + * Does nothing. XTemplates are compiled automatically, so this function simply returns this. + * @return {Ext.XTemplate} this + */ + compile: function() { + return this; + }, + + statics: { + /** + * Gets an `XTemplate` from an object (an instance of an {@link Ext#define}'d class). + * Many times, templates are configured high in the class hierarchy and are to be + * shared by all classes that derive from that base. To further complicate matters, + * these templates are seldom actual instances but are rather configurations. For + * example: + * + * Ext.define('MyApp.Class', { + * extraCls: 'extra-class', + * + * someTpl: [ + * '
', + * { + * + * emitClass: function(out) { + * out.push(this.owner.extraCls); + * } + * }] + * }); + * + * The goal being to share that template definition with all instances and even + * instances of derived classes, until `someTpl` is overridden. This method will + * "upgrade" these configurations to be real `XTemplate` instances *in place* (to + * avoid creating one instance per object). + * + * The resulting XTemplate will have an `owner` reference injected which refers back + * to the owning object whether that is an object which has an *own instance*, or a + * class prototype. Through this link, XTemplate member functions will be able to access + * prototype properties of its owning class. + * + * @param {Object} instance The object from which to get the `XTemplate` (must be + * an instance of an {@link Ext#define}'d class). + * @param {String} name The name of the property by which to get the `XTemplate`. + * @return {Ext.XTemplate} The `XTemplate` instance or null if not found. + * @protected + * @static + */ + getTpl: function (instance, name) { + var tpl = instance[name], // go for it! 99% of the time we will get it! + owner; + + if (tpl && !tpl.isTemplate) { // tpl is just a configuration (not an instance) + // create the template instance from the configuration: + tpl = Ext.ClassManager.dynInstantiate('Ext.XTemplate', tpl); + + // and replace the reference with the new instance: + if (instance.hasOwnProperty(name)) { // the tpl is on the instance + owner = instance; + } else { // must be somewhere in the prototype chain + for (owner = instance.self.prototype; owner && !owner.hasOwnProperty(name); owner = owner.superclass) { + } + } + owner[name] = tpl; + tpl.owner = owner; + } + // else !tpl (no such tpl) or the tpl is an instance already... either way, tpl + // is ready to return + + return tpl || null; + } + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +// @tag dom,core +// @require Helper.js + +// @define Ext.dom.Query +// @define Ext.core.DomQuery +// @define Ext.DomQuery + +/* + * This is code is also distributed under MIT license for use + * with jQuery and prototype JavaScript libraries. + */ +/** + * @class Ext.dom.Query + * @alternateClassName Ext.DomQuery + * @alternateClassName Ext.core.DomQuery + * @singleton + * + * Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes + * and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in). + * + * DomQuery supports most of the [CSS3 selectors spec][1], along with some custom selectors and basic XPath. + * + * All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example + * `div.foo:nth-child(odd)[@foo=bar].bar:first` would be a perfectly valid selector. Node filters are processed + * in the order in which they appear, which allows you to optimize your queries for your document structure. + * + * ## Simple Selectors + * + * For performance reasons, some query methods accept selectors that are termed as **simple selectors**. A simple + * selector is a selector that does not include contextual information about any parent/sibling elements. + * + * Some examples of valid simple selectors: + * + * var simple = '.foo'; // Only asking for the class name on the element + * var simple = 'div.bar'; // Only asking for the tag/class name on the element + * var simple = '[href];' // Asking for an attribute on the element. + * var simple = ':not(.foo)'; // Only asking for the non-matches against the class name + * var simple = 'span:first-child'; // Doesn't require any contextual information about the parent node + * + * Simple examples of invalid simple selectors: + * + * var notSimple = 'div.foo div.bar'; + * var notSimple = 'span + div'; + * + * ## Element Selectors: + * + * - **`*`** any element + * - **`E`** an element with the tag E + * - **`E F`** All descendent elements of E that have the tag F + * - **`E > F`** or **E/F** all direct children elements of E that have the tag F + * - **`E + F`** all elements with the tag F that are immediately preceded by an element with the tag E + * - **`E ~ F`** all elements with the tag F that are preceded by a sibling element with the tag E + * + * ## Attribute Selectors: + * + * The use of `@` and quotes are optional. For example, `div[@foo='bar']` is also a valid attribute selector. + * + * - **`E[foo]`** has an attribute "foo" + * - **`E[foo=bar]`** has an attribute "foo" that equals "bar" + * - **`E[foo^=bar]`** has an attribute "foo" that starts with "bar" + * - **`E[foo$=bar]`** has an attribute "foo" that ends with "bar" + * - **`E[foo*=bar]`** has an attribute "foo" that contains the substring "bar" + * - **`E[foo%=2]`** has an attribute "foo" that is evenly divisible by 2 + * - **`E[foo!=bar]`** attribute "foo" does not equal "bar" + * + * ## Pseudo Classes: + * + * - **`E:first-child`** E is the first child of its parent + * - **`E:last-child`** E is the last child of its parent + * - **`E:nth-child(_n_)`** E is the _n_th child of its parent (1 based as per the spec) + * - **`E:nth-child(odd)`** E is an odd child of its parent + * - **`E:nth-child(even)`** E is an even child of its parent + * - **`E:only-child`** E is the only child of its parent + * - **`E:checked`** E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) + * - **`E:first`** the first E in the resultset + * - **`E:last`** the last E in the resultset + * - **`E:nth(_n_)`** the _n_th E in the resultset (1 based) + * - **`E:odd`** shortcut for :nth-child(odd) + * - **`E:even`** shortcut for :nth-child(even) + * - **`E:contains(foo)`** E's innerHTML contains the substring "foo" + * - **`E:nodeValue(foo)`** E contains a textNode with a nodeValue that equals "foo" + * - **`E:not(S)`** an E element that does not match simple selector S + * - **`E:has(S)`** an E element that has a descendent that matches simple selector S + * - **`E:next(S)`** an E element whose next sibling matches simple selector S + * - **`E:prev(S)`** an E element whose previous sibling matches simple selector S + * - **`E:any(S1|S2|S2)`** an E element which matches any of the simple selectors S1, S2 or S3 + * - **`E:visible(true)`** an E element which is deeply visible according to {@link Ext.dom.Element#isVisible} + * + * ## CSS Value Selectors: + * + * - **`E{display=none}`** css value "display" that equals "none" + * - **`E{display^=none}`** css value "display" that starts with "none" + * - **`E{display$=none}`** css value "display" that ends with "none" + * - **`E{display*=none}`** css value "display" that contains the substring "none" + * - **`E{display%=2}`** css value "display" that is evenly divisible by 2 + * - **`E{display!=none}`** css value "display" that does not equal "none" + * + * ## XML Namespaces: + * - **`ns|E`** an element with tag E and namespace prefix ns + * + * [1]: http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#selectors + */ +Ext.ns('Ext.core'); + +Ext.dom.Query = Ext.core.DomQuery = Ext.DomQuery = (function() { + var DQ, + doc = document, + cache, simpleCache, valueCache, + useClassList = !!doc.documentElement.classList, + useElementPointer = !!doc.documentElement.firstElementChild, + useChildrenCollection = (function() { + var d = doc.createElement('div'); + d.innerHTML = 'text'; + 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*#/, + // This is for IE MSXML which does not support expandos. + // IE runs the same speed using setAttribute, however FF slows way down + // and Safari completely fails so they need to continue to use expandos. + 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, + // True if the browser supports the following syntax: + // document.getElementsByTagName('namespacePrefix:tagName') + 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; + })(), + + // replaces a long hex regex match group with the appropriate ascii value + // $args indicate regex match pos + longHexToChar = function($0, $1) { + return String.fromCharCode(parseInt($1, 16)); + }, + + // converts a shortHex regex match to the long form + shortToLongHex = function($0, $1) { + while ($1.length < 6) { + $1 = '0' + $1; + } + return '\\' + $1; + }, + + // converts a single char escape to long escape form + charToLongHex = function($0, $1) { + num = $1.charCodeAt(0).toString(16); + if (num.length === 1) { + num = '0' + num; + } + return '\\0000' + num; + }, + + // Un-escapes an input selector string. Assumes all escape sequences have been + // normalized to the css '\\0000##' 6-hex-digit style escape sequence : + // will not handle any other escape formats + unescapeCssSelector = function(selector) { + return (hasEscapes) ? selector.replace(longHex, longHexToChar) : selector; + }, + + // checks if the path has escaping & does any appropriate replacements + setupEscapes = function(path) { + hasEscapes = (path.indexOf('\\') > -1); + if (hasEscapes) { + path = path + .replace(shortHex, shortToLongHex) + .replace(nonHex, charToLongHex) + .replace(escapes, '\\\\'); // double the '\' for js compilation + } + return path; + }; + + // this eval is stop the compressor from + // renaming the variable to something shorter + eval("var batch = 30803, child, next, prev, byClassName;"); + + // Retrieve the child node from a particular + // parent at the specified index. + 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; + }; + + // retrieve the next element node + next = useElementPointer ? + function(n) { + return n.nextElementSibling; + } : + function(n) { + while ((n = n.nextSibling) && n.nodeType != 1); + return n; + }; + + // retrieve the previous element node + prev = useElementPointer ? + function(n) { + return n.previousElementSibling; + } : + function(n) { + while ((n = n.previousSibling) && n.nodeType != 1); + return n; + }; + + // Mark each child node with a nodeIndex skipping and + // removing empty text nodes. + function children(parent) { + var n = parent.firstChild, + nodeIndex = -1, + nextNode; + + while (n) { + nextNode = n.nextSibling; + // clean worthless empty nodes. + if (n.nodeType == 3 && !nonSpace.test(n.nodeValue)) { + parent.removeChild(n); + } else { + // add an expando nodeIndex + n.nodeIndex = ++nodeIndex; + } + n = nextNode; + } + return this; + } + + // nodeSet - array of nodes + // cls - CSS Class + byClassName = useClassList ? // Use classList API where available: http://jsperf.com/classlist-vs-old-school-check/ + 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) { + // Some elements types (SVG) may not always have a classList + // in some browsers, so fallback to the old style here + 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 its an array, use the first node. + 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]; + + } + + // ns - nodes + // mode - false, /, >, +, ~ + // tagName - defaults to "*" + 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('|', ':') || "*"; + // convert to array + if (typeof ns.getElementsByTagName != "undefined") { + ns = [ns]; + } + + // no mode specified, grab all elements by tagName + // at any depth + if (!mode) { + tagName = unescapeCssSelector(tagName); + if (!supportsColonNsSeparator && DQ.isXml(ns[0]) && + tagName.indexOf(':') !== -1) { + // Some browsers (e.g. WebKit and Opera do not support the following syntax + // in xml documents: getElementsByTagName('ns:tagName'). To work around + // this, we remove the namespace prefix from the tagName, get the elements + // by tag name only, and then compare each element's tagName property to + + + 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; i < len; i++) { + c = cs[i]; + if (!c.getAttribute("_nodup") != d) { + c.setAttribute("_nodup", d); + r[r.length] = c; + } + } + for (i = 0, len = cs.length; i < len; i++) { + cs[i].removeAttribute("_nodup"); + } + return r; + } + + function nodup(cs) { + if (!cs) { + return []; + } + var len = cs.length, c, i, r = cs, cj, ri = -1, d, j; + if (!len || typeof cs.nodeType != "undefined" || len == 1) { + return cs; + } + if (isIE && typeof cs[0].selectSingleNode != "undefined") { + return nodupIEXml(cs); + } + d = ++key; + cs[0]._nodup = d; + for (i = 1; c = cs[i]; i++) { + if (c._nodup != d) { + c._nodup = d; + } else { + r = []; + for (j = 0; j < i; j++) { + r[++ri] = cs[j]; + } + for (j = i + 1; cj = cs[j]; j++) { + if (cj._nodup != d) { + cj._nodup = d; + r[++ri] = cj; + } + } + return r; + } + } + return r; + } + + function quickDiffIEXml(c1, c2) { + var d = ++key, + r = [], + i, len; + for (i = 0, len = c1.length; i < len; i++) { + c1[i].setAttribute("_qdiff", d); + } + for (i = 0, len = c2.length; i < len; i++) { + if (c2[i].getAttribute("_qdiff") != d) { + r[r.length] = c2[i]; + } + } + for (i = 0, len = c1.length; i < len; i++) { + c1[i].removeAttribute("_qdiff"); + } + return r; + } + + function quickDiff(c1, c2) { + var len1 = c1.length, + d = ++key, + r = [], + i, len; + if (!len1) { + return c2; + } + if (isIE && typeof c1[0].selectSingleNode != "undefined") { + return quickDiffIEXml(c1, c2); + } + for (i = 0; i < len1; i++) { + c1[i]._qdiff = d; + } + for (i = 0, len = c2.length; i < len; i++) { + if (c2[i]._qdiff != d) { + r[r.length] = c2[i]; + } + } + return r; + } + + function quickId(ns, mode, root, id) { + if (ns == root) { + id = unescapeCssSelector(id); + var d = root.ownerDocument || root; + return d.getElementById(id); + } + ns = getNodes(ns, mode, "*"); + return byId(ns, id); + } + + return DQ = { + clearCache: function () { + cache && cache.clear(); + valueCache && valueCache.clear(); + simpleCache && simpleCache.clear(); + }, + + getStyle: function(el, name) { + return Ext.fly(el, '_DomQuery').getStyle(name); + }, + + compile: function(path, type) { + type = type || "select"; + + + var fn = ["var f = function(root) {\n var mode; ++batch; var n = root || document;\n"], + lastPath, + matchers = DQ.matchers, + matchersLn = matchers.length, + modeMatch, + + lmode = path.match(modeRe), + tokenMatch, matched, j, t, m; + + path = setupEscapes(path); + + if (lmode && lmode[1]) { + fn[fn.length] = 'mode="' + lmode[1].replace(trimRe, "") + '";'; + path = path.replace(lmode[1], ""); + } + + + while (path.substr(0, 1) == "/") { + path = path.substr(1); + } + + while (path && lastPath != path) { + lastPath = path; + tokenMatch = path.match(tagTokenRe); + if (type == "select") { + if (tokenMatch) { + + if (tokenMatch[1] == "#") { + fn[fn.length] = 'n = quickId(n, mode, root, "' + tokenMatch[2] + '");'; + } else { + fn[fn.length] = 'n = getNodes(n, mode, "' + tokenMatch[2] + '");'; + } + path = path.replace(tokenMatch[0], ""); + } else if (path.substr(0, 1) != '@') { + fn[fn.length] = 'n = getNodes(n, mode, "*");'; + } + + } else { + if (tokenMatch) { + if (tokenMatch[1] == "#") { + fn[fn.length] = 'n = byId(n, "' + tokenMatch[2] + '");'; + } else { + fn[fn.length] = 'n = byTag(n, "' + tokenMatch[2] + '");'; + } + path = path.replace(tokenMatch[0], ""); + } + } + while (!(modeMatch = path.match(modeRe))) { + matched = false; + for (j = 0; j < matchersLn; j++) { + t = matchers[j]; + m = path.match(t.re); + if (m) { + fn[fn.length] = t.select.replace(tplRe, function(x, i) { + return m[i]; + }); + path = path.replace(m[0], ""); + matched = true; + break; + } + } + + if (!matched) { + Ext.Error.raise({ + sourceClass:'Ext.DomQuery', + sourceMethod:'compile', + msg:'Error parsing selector. Parsing failed at "' + path + '"' + }); + } + } + if (modeMatch[1]) { + fn[fn.length] = 'mode="' + modeMatch[1].replace(trimRe, "") + '";'; + path = path.replace(modeMatch[1], ""); + } + } + + fn[fn.length] = "return nodup(n);\n}"; + + + eval(fn.join("")); + return f; + }, + + + jsSelect: function(path, root, type) { + if (!cache) { + DQ._cache = cache = new Ext.util.LruCache({ + maxSize: 200 + }); + } + + root = root || doc; + + if (typeof root == "string") { + root = doc.getElementById(root); + } + var paths = Ext.splitAndUnescape(path, ","), + results = [], + query, + i, len, subPath, result; + + + for (i = 0, len = paths.length; i < len; i++) { + subPath = paths[i].replace(trimRe, ""); + + query = cache.get(subPath); + if (!query) { + + query = DQ.compile(subPath, type); + if (!query) { + Ext.Error.raise({ + sourceClass:'Ext.DomQuery', + sourceMethod:'jsSelect', + msg:subPath + ' is not a valid selector' + }); + } + cache.add(subPath, query); + } else { + + + setupEscapes(subPath); + } + result = query(root); + if (result && result !== doc) { + results = results.concat(result); + } + } + + + + if (paths.length > 1) { + 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.escapeId(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) { + if (!valueCache) { + DQ._valueCache = valueCache = new Ext.util.LruCache({ + maxSize: 200 + }); + } + 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); + + n = n[0] ? n[0] : n; + + + + + + if (typeof n.normalize == 'function') { + n.normalize(); + } + + v = (n && n.firstChild ? n.firstChild.nodeValue : null); + return ((v === null || v === undefined || v === '') ? defaultValue : 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, ""); + if (!simpleCache) { + DQ._simpleCache = simpleCache = new Ext.util.LruCache({ + maxSize: 200 + }); + } + 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}")}};' + }], + + + operators: { + "=": function(a, v) { + return a == v; + }, + "!=": function(a, v) { + return a != v; + }, + "^=": function(a, v) { + return a && a.substr(0, v.length) == v; + }, + "$=": function(a, v) { + return a && a.substr(a.length - v.length) == v; + }, + "*=": function(a, v) { + return a && a.indexOf(v) !== -1; + }, + "%=": function(a, v) { + return (a % v) === 0; + }, + "|=": function(a, v) { + return a && (a == v || a.substr(0, v.length + 1) == v + '-'); + }, + "~=": function(a, v) { + return a && (' ' + a + ' ').indexOf(' ' + v + ' ') != -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 (; i < len; i++) { + c = candidates[i]; + if (Ext.fly(c, '_DomQuery').isFocusable()) { + results.push(c); + } + } + + return results; + }, + + visible: function(candidates, deep) { + var len = candidates.length, + results = [], + i = 0, + c; + + for (; i < len; i++) { + c = candidates[i]; + if (Ext.fly(c, '_DomQuery').isVisible(deep)) { + results.push(c); + } + } + + return results; + } + } + }; +}()); + + +Ext.query = Ext.DomQuery.select; + + + + + + +Ext.define('Ext.dom.Element_anim', { + override: 'Ext.dom.Element', + + + animate: function(config) { + var me = this, + animId = me.dom.id || Ext.id(me.dom), + listeners, + anim, + end; + + + if (!Ext.fx.Manager.hasFxBlock(animId)) { + + if (config.listeners) { + listeners = config.listeners; + delete config.listeners; + } + if (config.internalListeners) { + config.listeners = config.internalListeners; + delete config.internalListeners; + } + end = config.autoEnd; + delete config.autoEnd; + anim = new Ext.fx.Anim(me.anim(config)); + if (listeners) { + anim.on(listeners); + } + Ext.fx.Manager.queueFx(anim); + if (end) { + anim.jumpToEnd(); + } + } + return me; + }, + + + anim: function(config) { + if (!Ext.isObject(config)) { + return (config) ? {} : false; + } + + var me = this, + duration = config.duration || Ext.fx.Anim.prototype.duration, + easing = config.easing || 'ease', + animConfig; + + if (config.stopAnimation) { + me.stopAnimation(); + } + + Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id)); + + + Ext.fx.Manager.setFxDefaults(me.id, { + delay: 0 + }); + + animConfig = { + + target: me.dom, + remove: config.remove, + alternate: config.alternate || false, + duration: duration, + easing: easing, + callback: config.callback, + listeners: config.listeners, + iterations: config.iterations || 1, + scope: config.scope, + block: config.block, + concurrent: config.concurrent, + delay: config.delay || 0, + paused: true, + keyframes: config.keyframes, + from: config.from || {}, + to: Ext.apply({}, config) + }; + Ext.apply(animConfig.to, config.to); + + + delete animConfig.to.to; + delete animConfig.to.from; + delete animConfig.to.remove; + delete animConfig.to.alternate; + delete animConfig.to.keyframes; + delete animConfig.to.iterations; + delete animConfig.to.listeners; + delete animConfig.to.target; + delete animConfig.to.paused; + delete animConfig.to.callback; + delete animConfig.to.scope; + delete animConfig.to.duration; + delete animConfig.to.easing; + delete animConfig.to.concurrent; + delete animConfig.to.block; + delete animConfig.to.stopAnimation; + delete animConfig.to.delay; + return animConfig; + }, + + + slideIn: function(anchor, obj, slideOut) { + var me = this, + dom = me.dom, + elStyle = dom.style, + beforeAnim, + wrapAnim, + restoreScroll, + wrapDomParentNode; + + anchor = anchor || "t"; + obj = obj || {}; + + beforeAnim = function() { + var animScope = this, + listeners = obj.listeners, + el = Ext.fly(dom, '_anim'), + box, originalStyles, anim, wrap; + + if (!slideOut) { + el.fixDisplay(); + } + + box = el.getBox(); + if ((anchor == 't' || anchor == 'b') && box.height === 0) { + box.height = dom.scrollHeight; + } + else if ((anchor == 'l' || anchor == 'r') && box.width === 0) { + box.width = dom.scrollWidth; + } + + originalStyles = el.getStyles('width', 'height', 'left', 'right', 'top', 'bottom', 'position', 'z-index', true); + el.setSize(box.width, box.height); + + + if (obj.preserveScroll) { + restoreScroll = el.cacheScrollValues(); + } + + wrap = el.wrap({ + role: 'presentation', + id: Ext.id() + '-anim-wrap-for-' + el.dom.id, + style: { + visibility: slideOut ? 'visible' : 'hidden' + } + }); + wrapDomParentNode = wrap.dom.parentNode; + wrap.setPositioning(el.getPositioning(true)); + if (wrap.isStyle('position', 'static')) { + wrap.position('relative'); + } + el.clearPositioning('auto'); + wrap.clip(); + + + if (restoreScroll) { + restoreScroll(); + } + + + + + el.setStyle({ + visibility: '', + position: 'absolute' + }); + if (slideOut) { + wrap.setSize(box.width, box.height); + } + + switch (anchor) { + case 't': + anim = { + from: { + width: box.width + 'px', + height: '0px' + }, + to: { + width: box.width + 'px', + height: box.height + 'px' + } + }; + elStyle.bottom = '0px'; + break; + case 'l': + anim = { + from: { + width: '0px', + height: box.height + 'px' + }, + to: { + width: box.width + 'px', + height: box.height + 'px' + } + }; + me.anchorAnimX(anchor); + break; + case 'r': + anim = { + from: { + x: box.x + box.width, + width: '0px', + height: box.height + 'px' + }, + to: { + x: box.x, + width: box.width + 'px', + height: box.height + 'px' + } + }; + me.anchorAnimX(anchor); + break; + case 'b': + anim = { + from: { + y: box.y + box.height, + width: box.width + 'px', + height: '0px' + }, + to: { + y: box.y, + width: box.width + 'px', + height: box.height + 'px' + } + }; + break; + case 'tl': + anim = { + from: { + x: box.x, + y: box.y, + width: '0px', + height: '0px' + }, + to: { + width: box.width + 'px', + height: box.height + 'px' + } + }; + elStyle.bottom = '0px'; + me.anchorAnimX('l'); + break; + case 'bl': + anim = { + from: { + y: box.y + box.height, + width: '0px', + height: '0px' + }, + to: { + y: box.y, + width: box.width + 'px', + height: box.height + 'px' + } + }; + me.anchorAnimX('l'); + break; + case 'br': + anim = { + from: { + x: box.x + box.width, + y: box.y + box.height, + width: '0px', + height: '0px' + }, + to: { + x: box.x, + y: box.y, + width: box.width + 'px', + height: box.height + 'px' + } + }; + me.anchorAnimX('r'); + break; + case 'tr': + anim = { + from: { + x: box.x + box.width, + width: '0px', + height: '0px' + }, + to: { + x: box.x, + width: box.width + 'px', + height: box.height + 'px' + } + }; + elStyle.bottom = '0px'; + me.anchorAnimX('r'); + break; + } + + wrap.show(); + wrapAnim = Ext.apply({}, obj); + delete wrapAnim.listeners; + wrapAnim = new Ext.fx.Anim(Ext.applyIf(wrapAnim, { + target: wrap, + duration: 500, + easing: 'ease-out', + from: slideOut ? anim.to : anim.from, + to: slideOut ? anim.from : anim.to + })); + + + wrapAnim.on('afteranimate', function() { + var el = Ext.fly(dom, '_anim'); + + el.setStyle(originalStyles); + if (slideOut) { + if (obj.useDisplay) { + el.setDisplayed(false); + } else { + el.hide(); + } + } + if (wrap.dom) { + if (wrap.dom.parentNode) { + wrap.dom.parentNode.insertBefore(el.dom, wrap.dom); + } else { + wrapDomParentNode.appendChild(el.dom); + } + wrap.remove(); + } + + if (restoreScroll) { + restoreScroll(); + } + + animScope.end(); + }); + + if (listeners) { + wrapAnim.on(listeners); + } + }; + + me.animate({ + + duration: obj.duration ? Math.max(obj.duration, 500) * 2 : 1000, + listeners: { + beforeanimate: beforeAnim + } + }); + return me; + }, + + + + slideOut: function(anchor, o) { + return this.slideIn(anchor, o, true); + }, + + + puff: function(obj) { + var me = this, + dom = me.dom, + beforeAnim, + box = me.getBox(), + originalStyles = me.getStyles('width', 'height', 'left', 'right', 'top', 'bottom', 'position', 'z-index', 'font-size', 'opacity', true); + + obj = Ext.applyIf(obj || {}, { + easing: 'ease-out', + duration: 500, + useDisplay: false + }); + + beforeAnim = function() { + var el = Ext.fly(dom, '_anim'); + + el.clearOpacity(); + el.show(); + this.to = { + width: box.width * 2, + height: box.height * 2, + x: box.x - (box.width / 2), + y: box.y - (box.height /2), + opacity: 0, + fontSize: '200%' + }; + this.on('afteranimate',function() { + var el = Ext.fly(dom, '_anim'); + if (el) { + if (obj.useDisplay) { + el.setDisplayed(false); + } else { + el.hide(); + } + el.setStyle(originalStyles); + Ext.callback(obj.callback, obj.scope); + } + }); + }; + + me.animate({ + duration: obj.duration, + easing: obj.easing, + listeners: { + beforeanimate: { + fn: beforeAnim + } + } + }); + return me; + }, + + + switchOff: function(obj) { + var me = this, + dom = me.dom, + beforeAnim; + + obj = Ext.applyIf(obj || {}, { + easing: 'ease-in', + duration: 500, + remove: false, + useDisplay: false + }); + + beforeAnim = function() { + var el = Ext.fly(dom, '_anim'), + animScope = this, + size = el.getSize(), + xy = el.getXY(), + keyframe, position; + + el.clearOpacity(); + el.clip(); + position = el.getPositioning(); + + keyframe = new Ext.fx.Animator({ + target: dom, + duration: obj.duration, + easing: obj.easing, + keyframes: { + 33: { + opacity: 0.3 + }, + 66: { + height: 1, + y: xy[1] + size.height / 2 + }, + 100: { + width: 1, + x: xy[0] + size.width / 2 + } + } + }); + keyframe.on('afteranimate', function() { + var el = Ext.fly(dom, '_anim'); + if (obj.useDisplay) { + el.setDisplayed(false); + } else { + el.hide(); + } + el.clearOpacity(); + el.setPositioning(position); + el.setSize(size); + + animScope.end(); + }); + }; + + me.animate({ + + duration: (Math.max(obj.duration, 500) * 2), + listeners: { + beforeanimate: { + fn: beforeAnim + } + }, + callback: obj.callback, + scope: obj.scope + }); + return me; + }, + + + frame : function(color, count, obj){ + var me = this, + dom = me.dom, + beforeAnim; + + color = color || '#C3DAF9'; + count = count || 1; + obj = obj || {}; + + beforeAnim = function() { + var el = Ext.fly(dom, '_anim'), + animScope = this, + box, + proxy, proxyAnim; + + el.show(); + box = el.getBox(); + proxy = Ext.getBody().createChild({ + role: 'presentation', + id: el.dom.id + '-anim-proxy', + style: { + position : 'absolute', + 'pointer-events': 'none', + 'z-index': 35000, + border : '0px solid ' + color + } + }); + + proxyAnim = new Ext.fx.Anim({ + target: proxy, + duration: obj.duration || 1000, + iterations: count, + from: { + top: box.y, + left: box.x, + borderWidth: 0, + opacity: 1, + height: box.height, + width: box.width + }, + to: { + top: box.y - 20, + left: box.x - 20, + borderWidth: 10, + opacity: 0, + height: box.height + 40, + width: box.width + 40 + } + }); + proxyAnim.on('afteranimate', function() { + proxy.remove(); + + animScope.end(); + }); + }; + + me.animate({ + + duration: (Math.max(obj.duration, 500) * 2) || 2000, + listeners: { + beforeanimate: { + fn: beforeAnim + } + }, + callback: obj.callback, + scope: obj.scope + }); + return me; + }, + + + ghost: function(anchor, obj) { + var me = this, + dom = me.dom, + beforeAnim; + + anchor = anchor || "b"; + beforeAnim = function() { + var el = Ext.fly(dom, '_anim'), + width = el.getWidth(), + height = el.getHeight(), + xy = el.getXY(), + position = el.getPositioning(), + to = { + opacity: 0 + }; + switch (anchor) { + case 't': + to.y = xy[1] - height; + break; + case 'l': + to.x = xy[0] - width; + break; + case 'r': + to.x = xy[0] + width; + break; + case 'b': + to.y = xy[1] + height; + break; + case 'tl': + to.x = xy[0] - width; + to.y = xy[1] - height; + break; + case 'bl': + to.x = xy[0] - width; + to.y = xy[1] + height; + break; + case 'br': + to.x = xy[0] + width; + to.y = xy[1] + height; + break; + case 'tr': + to.x = xy[0] + width; + to.y = xy[1] - height; + break; + } + this.to = to; + this.on('afteranimate', function () { + var el = Ext.fly(dom, '_anim'); + if (el) { + el.hide(); + el.clearOpacity(); + el.setPositioning(position); + } + }); + }; + + me.animate(Ext.applyIf(obj || {}, { + duration: 500, + easing: 'ease-out', + listeners: { + beforeanimate: beforeAnim + } + })); + return me; + }, + + + highlight: function(color, o) { + var me = this, + dom = me.dom, + from = {}, + restore, to, attr, lns, event, fn; + + + if (dom.tagName.match(me.tableTagRe)) { + return me.select('div').highlight(color, o); + } + + o = o || {}; + lns = o.listeners || {}; + attr = o.attr || 'backgroundColor'; + from[attr] = color || 'ffff9c'; + + if (!o.to) { + to = {}; + to[attr] = o.endColor || me.getColor(attr, 'ffffff', ''); + } + else { + to = o.to; + } + + + o.listeners = Ext.apply(Ext.apply({}, lns), { + beforeanimate: function() { + restore = dom.style[attr]; + var el = Ext.fly(dom, '_anim'); + el.clearOpacity(); + el.show(); + + event = lns.beforeanimate; + if (event) { + fn = event.fn || event; + return fn.apply(event.scope || lns.scope || window, arguments); + } + }, + afteranimate: function() { + if (dom) { + dom.style[attr] = restore; + } + + event = lns.afteranimate; + if (event) { + fn = event.fn || event; + fn.apply(event.scope || lns.scope || window, arguments); + } + } + }); + + me.animate(Ext.apply({}, o, { + duration: 1000, + easing: 'ease-in', + from: from, + to: to + })); + return me; + }, + + + pause: function(ms) { + var me = this; + Ext.fx.Manager.setFxDefaults(me.id, { + delay: ms + }); + return me; + }, + + + fadeIn: function(o) { + var me = this, + dom = me.dom; + + me.animate(Ext.apply({}, o, { + opacity: 1, + internalListeners: { + beforeanimate: function(anim){ + + + var el = Ext.fly(dom, '_anim'); + if (el.isStyle('display', 'none')) { + el.setDisplayed(''); + } else { + el.show(); + } + } + } + })); + return this; + }, + + + fadeOut: function(o) { + var me = this, + dom = me.dom; + + o = Ext.apply({ + opacity: 0, + internalListeners: { + afteranimate: function(anim){ + if (dom && anim.to.opacity === 0) { + var el = Ext.fly(dom, '_anim'); + if (o.useDisplay) { + el.setDisplayed(false); + } else { + el.hide(); + } + } + } + } + }, o); + me.animate(o); + return me; + }, + + + scale: function(w, h, o) { + this.animate(Ext.apply({}, o, { + width: w, + height: h + })); + return this; + }, + + + shift: function(config) { + this.animate(config); + return this; + }, + + + anchorAnimX: function(anchor) { + var xName = (anchor === 'l') ? 'right' : 'left'; + this.dom.style[xName] = '0px'; + } +}); + + + + +Ext.define('Ext.dom.Element_dd', { + override: 'Ext.dom.Element', + + + initDD : function(group, config, overrides){ + var dd = new Ext.dd.DD(Ext.id(this.dom), group, config); + return Ext.apply(dd, overrides); + }, + + + initDDProxy : function(group, config, overrides){ + var dd = new Ext.dd.DDProxy(Ext.id(this.dom), group, config); + return Ext.apply(dd, overrides); + }, + + + initDDTarget : function(group, config, overrides){ + var dd = new Ext.dd.DDTarget(Ext.id(this.dom), group, config); + return Ext.apply(dd, overrides); + } +}); + + + + +Ext.define('Ext.dom.Element_fx', { + override: 'Ext.dom.Element' +}, +function() { + +var Element = Ext.dom.Element, + VISIBILITY = "visibility", + DISPLAY = "display", + NONE = "none", + HIDDEN = 'hidden', + VISIBLE = 'visible', + OFFSETS = "offsets", + ASCLASS = "asclass", + NOSIZE = 'nosize', + ORIGINALDISPLAY = 'originalDisplay', + VISMODE = 'visibilityMode', + ISVISIBLE = 'isVisible', + OFFSETCLASS = Ext.baseCSSPrefix + 'hide-offsets', + getDisplay = function(el) { + var data = (el.$cache || el.getCache()).data, + display = data[ORIGINALDISPLAY]; + + if (display === undefined) { + data[ORIGINALDISPLAY] = display = ''; + } + return display; + }, + getVisMode = function(el){ + var data = (el.$cache || el.getCache()).data, + visMode = data[VISMODE]; + + if (visMode === undefined) { + data[VISMODE] = visMode = Element.VISIBILITY; + } + return visMode; + }; + +Element.override({ + + originalDisplay : "", + visibilityMode : 1, + + + setVisible : function(visible, animate) { + var me = this, + dom = me.dom, + visMode = getVisMode(me); + + + if (typeof animate == 'string') { + switch (animate) { + case DISPLAY: + visMode = Element.DISPLAY; + break; + case VISIBILITY: + visMode = Element.VISIBILITY; + break; + case OFFSETS: + visMode = Element.OFFSETS; + break; + case NOSIZE: + case ASCLASS: + visMode = Element.ASCLASS; + break; + } + me.setVisibilityMode(visMode); + animate = false; + } + + if (!animate || !me.anim) { + if (visMode == Element.DISPLAY) { + return me.setDisplayed(visible); + } else if (visMode == Element.OFFSETS) { + me[visible?'removeCls':'addCls'](OFFSETCLASS); + } else if (visMode == Element.VISIBILITY) { + me.fixDisplay(); + + dom.style.visibility = visible ? '' : HIDDEN; + } else if (visMode == Element.ASCLASS) { + me[visible?'removeCls':'addCls'](me.visibilityCls || Element.visibilityCls); + } + } else { + + if (visible) { + me.setOpacity(0.01); + me.setVisible(true); + } + if (!Ext.isObject(animate)) { + animate = { + duration: 350, + easing: 'ease-in' + }; + } + me.animate(Ext.applyIf({ + callback: function() { + if (!visible) { + + + Ext.fly(dom, '_internal').setVisible(false).setOpacity(1); + } + }, + to: { + opacity: (visible) ? 1 : 0 + } + }, animate)); + } + (me.$cache || me.getCache()).data[ISVISIBLE] = visible; + return me; + }, + + + hasMetrics : function(){ + var visMode = getVisMode(this); + return this.isVisible() || (visMode == Element.OFFSETS) || (visMode == Element.VISIBILITY); + }, + + + toggle : function(animate){ + var me = this; + me.setVisible(!me.isVisible(), me.anim(animate)); + return me; + }, + + + setDisplayed : function(value) { + if(typeof value == "boolean"){ + value = value ? getDisplay(this) : NONE; + } + this.setStyle(DISPLAY, value); + return this; + }, + + + fixDisplay : function(){ + var me = this; + if (me.isStyle(DISPLAY, NONE)) { + me.setStyle(VISIBILITY, HIDDEN); + me.setStyle(DISPLAY, getDisplay(me)); + if (me.isStyle(DISPLAY, NONE)) { + me.setStyle(DISPLAY, "block"); + } + } + }, + + + hide : function(animate){ + + if (typeof animate == 'string'){ + this.setVisible(false, animate); + return this; + } + this.setVisible(false, this.anim(animate)); + return this; + }, + + + show : function(animate){ + + if (typeof animate == 'string'){ + this.setVisible(true, animate); + return this; + } + this.setVisible(true, this.anim(animate)); + return this; + } +}); + +}); + + + + +Ext.define('Ext.dom.Element_position', { + override: 'Ext.dom.Element' +}, +function() { + +var flyInstance, + Element = this, + LEFT = "left", + RIGHT = "right", + TOP = "top", + BOTTOM = "bottom", + POSITION = "position", + STATIC = "static", + RELATIVE = "relative", + ZINDEX = "z-index", + BODY = 'BODY', + + PADDING = 'padding', + BORDER = 'border', + SLEFT = '-left', + SRIGHT = '-right', + STOP = '-top', + SBOTTOM = '-bottom', + SWIDTH = '-width', + + borders = {l: BORDER + SLEFT + SWIDTH, r: BORDER + SRIGHT + SWIDTH, t: BORDER + STOP + SWIDTH, b: BORDER + SBOTTOM + SWIDTH}, + paddings = {l: PADDING + SLEFT, r: PADDING + SRIGHT, t: PADDING + STOP, b: PADDING + SBOTTOM}, + paddingsTLRB = [paddings.l, paddings.r, paddings.t, paddings.b], + bordersTLRB = [borders.l, borders.r, borders.t, borders.b], + round = Math.round, + doc = document, + fly = function (el) { + if (!flyInstance) { + flyInstance = new Ext.Element.Fly(); + } + flyInstance.attach(el); + return flyInstance; + }; + + Element.override({ + + pxRe: /^\d+(?:\.\d*)?px$/i, + + inheritableStatics: { + getX: function(el) { + return Element.getXY(el)[0]; + }, + + getXY: function(el) { + var bd = doc.body, + docEl = doc.documentElement, + leftBorder = 0, + topBorder = 0, + ret = [0,0], + box, + scroll; + + el = Ext.getDom(el); + + if(el != doc && el != bd){ + + + if (Ext.isIE) { + try { + box = el.getBoundingClientRect(); + + + topBorder = docEl.clientTop || bd.clientTop; + leftBorder = docEl.clientLeft || bd.clientLeft; + } catch (ex) { + box = { left: 0, top: 0 }; + } + } else { + box = el.getBoundingClientRect(); + } + + scroll = fly(doc).getScroll(); + ret = [ + round(box.left + scroll.left - leftBorder), + round(box.top + scroll.top - topBorder) + ]; + } + return ret; + }, + + getY: function(el) { + return Element.getXY(el)[1]; + }, + + setX: function(el, x) { + Element.setXY(el, [x, false]); + }, + + setXY: function(el, xy) { + (el = Ext.fly(el, '_setXY')).position(); + + var pts = el.translatePoints(xy), + style = el.dom.style, + pos; + + + + style.right = 'auto'; + for (pos in pts) { + if (!isNaN(pts[pos])) { + style[pos] = pts[pos] + "px"; + } + } + }, + + setY: function(el, y) { + Element.setXY(el, [false, y]); + } + }, + + + center: function(centerIn){ + return this.alignTo(centerIn || doc, 'c-c'); + }, + + + clearPositioning: function(value) { + value = value || ''; + return this.setStyle({ + left : value, + right : value, + top : value, + bottom : value, + 'z-index' : '', + position : STATIC + }); + }, + + getAnchorToXY: function(el, anchor, local, mySize) { + return el.getAnchorXY(anchor, local, mySize); + }, + + + getBottom: function(local) { + return (local ? this.getLocalY() : this.getY()) + this.getHeight(); + }, + + getBorderPadding: function() { + var paddingWidth = this.getStyle(paddingsTLRB), + bordersWidth = this.getStyle(bordersTLRB); + + return { + beforeX: (parseFloat(bordersWidth[borders.l]) || 0) + (parseFloat(paddingWidth[paddings.l]) || 0), + afterX: (parseFloat(bordersWidth[borders.r]) || 0) + (parseFloat(paddingWidth[paddings.r]) || 0), + beforeY: (parseFloat(bordersWidth[borders.t]) || 0) + (parseFloat(paddingWidth[paddings.t]) || 0), + afterY: (parseFloat(bordersWidth[borders.b]) || 0) + (parseFloat(paddingWidth[paddings.b]) || 0) + }; + }, + + + getCenterXY: function(){ + return this.getAlignToXY(doc, 'c-c'); + }, + + + getLeft: function(local) { + return local ? this.getLocalX() : this.getX(); + }, + + + getLocalX: function() { + var me = this, + offsetParent = me.dom.offsetParent, + x = me.getStyle('left'); + + if (!x || x === 'auto') { + x = 0; + } else if (me.pxRe.test(x)) { + x = parseFloat(x); + } else { + x = me.getX(); + if (offsetParent) { + x -= Element.getX(offsetParent); + } + } + + return x; + }, + + + getLocalXY: function() { + var me = this, + offsetParent = me.dom.offsetParent, + style = me.getStyle(['left', 'top']), + x = style.left, + y = style.top; + + if (!x || x === 'auto') { + x = 0; + } else if (me.pxRe.test(x)) { + x = parseFloat(x); + } else { + x = me.getX(); + if (offsetParent) { + x -= Element.getX(offsetParent); + } + } + + if (!y || y === 'auto') { + y = 0; + } else if (me.pxRe.test(y)) { + y = parseFloat(y); + } else { + y = me.getY(); + if (offsetParent) { + y -= Element.getY(offsetParent); + } + } + + return [x, y]; + }, + + + getLocalY: function() { + var me = this, + offsetParent = me.dom.offsetParent, + y = me.getStyle('top'); + + if (!y || y === 'auto') { + y = 0; + } else if (me.pxRe.test(y)) { + y = parseFloat(y); + } else { + y = me.getY(); + if (offsetParent) { + y -= Element.getY(offsetParent); + } + } + + return y; + }, + + + getPageBox: function(getRegion) { + var me = this, + dom = me.dom, + isDoc = dom.nodeName == BODY, + w = isDoc ? Ext.Element.getViewWidth() : dom.offsetWidth, + h = isDoc ? Ext.Element.getViewHeight() : dom.offsetHeight, + xy = me.getXY(), + t = xy[1], + r = xy[0] + w, + b = xy[1] + h, + l = xy[0]; + + if (getRegion) { + return new Ext.util.Region(t, r, b, l); + } + else { + return { + left: l, + top: t, + width: w, + height: h, + right: r, + bottom: b + }; + } + }, + + + getPositioning: function(autoPx){ + var styles = this.getStyle(['left', 'top', 'position', 'z-index']), + dom = this.dom; + + if(autoPx) { + if(styles.left === 'auto') { + styles.left = dom.offsetLeft + 'px'; + } + if(styles.top === 'auto') { + styles.top = dom.offsetTop + 'px'; + } + } + + return styles; + }, + + + getRight: function(local) { + return (local ? this.getLocalX() : this.getX()) + this.getWidth(); + }, + + + getTop: function(local) { + return local ? this.getLocalY() : this.getY(); + }, + + + getX: function() { + return Element.getX(this.dom); + }, + + + getXY: function() { + return Element.getXY(this.dom); + }, + + + getY: function() { + return Element.getY(this.dom); + }, + + + moveTo: function(x, y, animate) { + return this.setXY([x, y], animate); + }, + + + position: function(pos, zIndex, x, y) { + var me = this; + + if (!pos && me.isStyle(POSITION, STATIC)) { + me.setStyle(POSITION, RELATIVE); + } else if (pos) { + me.setStyle(POSITION, pos); + } + if (zIndex) { + me.setStyle(ZINDEX, zIndex); + } + if (x || y) { + me.setXY([x || false, y || false]); + } + }, + + + setBottom: function(bottom) { + this.dom.style[BOTTOM] = this.addUnits(bottom); + return this; + }, + + + setBounds: function(x, y, width, height, animate) { + return this.setBox({ + x: x, + y: y, + width: width, + height: height + }, animate); + }, + + + setLeft: function(left) { + this.dom.style[LEFT] = this.addUnits(left); + return this; + }, + + + setLeftTop: function(left, top) { + var me = this, + style = me.dom.style; + + style.left = me.addUnits(left); + style.top = me.addUnits(top); + + return me; + }, + + setLocalX: function(x) { + var style = this.dom.style; + + + style.right = 'auto'; + style.left = (x === null) ? 'auto' : x + 'px'; + }, + + setLocalXY: function(x, y) { + var style = this.dom.style; + + + style.right = 'auto'; + + if (x && x.length) { + y = x[1]; + x = x[0]; + } + + if (x === null) { + style.left = 'auto'; + } else if (x !== undefined) { + style.left = x + 'px'; + } + + if (y === null) { + style.top = 'auto'; + } else if (y !== undefined) { + style.top = y + 'px'; + } + }, + + setLocalY: function(y) { + this.dom.style.top = (y === null) ? 'auto' : y + 'px'; + }, + + + setLocation: function(x, y, animate) { + return this.setXY([x, y], animate); + }, + + + setPositioning: function(pc) { + return this.setStyle(pc); + }, + + + setRight: function(right) { + this.dom.style[RIGHT] = this.addUnits(right); + return this; + }, + + + setTop: function(top) { + this.dom.style[TOP] = this.addUnits(top); + return this; + }, + + setX: function(x, animate) { + return this.setXY([x, this.getY()], animate); + }, + + setXY: function(xy, animate) { + var me = this; + + if (!animate || !me.anim) { + Element.setXY(me.dom, xy); + } else { + if (!Ext.isObject(animate)) { + animate = {}; + } + me.animate(Ext.applyIf({ to: { x: xy[0], y: xy[1] } }, animate)); + } + return this; + }, + + setY: function(y, animate) { + return this.setXY([this.getX(), y], animate); + } + }); + + + Element.getTrueXY = Element.getXY; + +}); + + + + +Ext.define('Ext.dom.Element_scroll', { + override: 'Ext.dom.Element', + + + isScrollable: function() { + var dom = this.dom; + return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth; + }, + + + getScroll: function() { + var me = this, + dom = me.dom, + doc = document, + body = doc.body, + docElement = doc.documentElement, + left, top; + + if (dom === doc || dom === body) { + + + + + + + + + left = docElement.scrollLeft || (body ? body.scrollLeft : 0); + top = docElement.scrollTop || (body ? body.scrollTop : 0); + } else { + left = dom.scrollLeft; + top = dom.scrollTop; + } + + return { + left: left, + top: top + }; + }, + + + getScrollLeft: function() { + var dom = this.dom, + doc = document; + + if (dom === doc || dom === doc.body) { + return this.getScroll().left; + } else { + return dom.scrollLeft; + } + }, + + + getScrollTop: function(){ + var dom = this.dom, + doc = document; + + if (dom === doc || dom === doc.body) { + return this.getScroll().top; + } else { + return dom.scrollTop; + } + }, + + + setScrollLeft: function(left){ + this.dom.scrollLeft = left; + return this; + }, + + + setScrollTop: function(top) { + this.dom.scrollTop = top; + return this; + }, + + + scrollBy: function(deltaX, deltaY, animate) { + var me = this, + dom = me.dom; + + + if (deltaX.length) { + animate = deltaY; + deltaY = deltaX[1]; + deltaX = deltaX[0]; + } else if (typeof deltaX != 'number') { + animate = deltaY; + deltaY = deltaX.y; + deltaX = deltaX.x; + } + + if (deltaX) { + me.scrollTo('left', me.constrainScrollLeft(dom.scrollLeft + deltaX), animate); + } + if (deltaY) { + me.scrollTo('top', me.constrainScrollTop(dom.scrollTop + deltaY), animate); + } + + return me; + }, + + + scrollTo: function(side, value, animate) { + + var top = /top/i.test(side), + me = this, + prop = top ? 'scrollTop' : 'scrollLeft', + dom = me.dom, + animCfg; + + if (!animate || !me.anim) { + + dom[prop] = value; + + dom[prop] = value; + } + else { + animCfg = { + to: {} + }; + animCfg.to[prop] = value; + if (Ext.isObject(animate)) { + Ext.applyIf(animCfg, animate); + } + me.animate(animCfg); + } + return me; + }, + + + scrollIntoView: function(container, hscroll, animate, highlight) { + var me = this, + dom = me.dom, + offsets = me.getOffsetsTo(container = Ext.getDom(container) || Ext.getBody().dom), + + left = offsets[0] + container.scrollLeft, + top = offsets[1] + container.scrollTop, + bottom = top + dom.offsetHeight, + right = left + dom.offsetWidth, + + ctClientHeight = container.clientHeight, + ctScrollTop = parseInt(container.scrollTop, 10), + ctScrollLeft = parseInt(container.scrollLeft, 10), + ctBottom = ctScrollTop + ctClientHeight, + ctRight = ctScrollLeft + container.clientWidth, + newPos; + + + if (highlight) { + if (animate) { + animate = Ext.apply({ + listeners: { + afteranimate: function() { + me.scrollChildFly.attach(dom).highlight(); + } + } + }, animate); + } else { + me.scrollChildFly.attach(dom).highlight(); + } + } + + if (dom.offsetHeight > ctClientHeight || top < ctScrollTop) { + newPos = top; + } else if (bottom > ctBottom) { + newPos = bottom - ctClientHeight; + } + if (newPos != null) { + me.scrollChildFly.attach(container).scrollTo('top', newPos, animate); + } + + if (hscroll !== false) { + newPos = null; + if (dom.offsetWidth > container.clientWidth || left < ctScrollLeft) { + newPos = left; + } else if (right > ctRight) { + newPos = right - container.clientWidth; + } + if (newPos != null) { + me.scrollChildFly.attach(container).scrollTo('left', newPos, animate); + } + } + return me; + }, + + + scrollChildIntoView: function(child, hscroll) { + this.scrollChildFly.attach(Ext.getDom(child)).scrollIntoView(this, hscroll); + }, + + + scroll: function(direction, distance, animate) { + if (!this.isScrollable()) { + return false; + } + + + + direction = direction.charAt(0); + var me = this, + dom = me.dom, + side = direction === 'r' || direction === 'l' ? 'left' : 'top', + scrolled = false, + currentScroll, constrainedScroll; + + if (direction === 'l' || direction === 't' || direction === 'u') { + distance = -distance; + } + + if (side === 'left') { + currentScroll = dom.scrollLeft; + constrainedScroll = me.constrainScrollLeft(currentScroll + distance); + } else { + currentScroll = dom.scrollTop; + constrainedScroll = me.constrainScrollTop(currentScroll + distance); + } + + if (constrainedScroll !== currentScroll) { + this.scrollTo(side, constrainedScroll, animate); + scrolled = true; + } + + return scrolled; + }, + + constrainScrollLeft: function(left) { + var dom = this.dom; + return Math.max(Math.min(left, dom.scrollWidth - dom.clientWidth), 0); + }, + + constrainScrollTop: function(top) { + var dom = this.dom; + return Math.max(Math.min(top, dom.scrollHeight - dom.clientHeight), 0); + } +}, function() { + this.prototype.scrollChildFly = new this.Fly(); + this.prototype.scrolltoFly = new this.Fly(); +}); + + + + +Ext.define('Ext.dom.Element_style', { + override: 'Ext.dom.Element' +}, +function() { + +var Element = this, + view = document.defaultView, + adjustDirect2DTableRe = /table-row|table-.*-group/, + INTERNAL = '_internal', + HIDDEN = 'hidden', + HEIGHT = 'height', + WIDTH = 'width', + ISCLIPPED = 'isClipped', + OVERFLOW = 'overflow', + OVERFLOWX = 'overflow-x', + OVERFLOWY = 'overflow-y', + ORIGINALCLIP = 'originalClip', + DOCORBODYRE = /#document|body/i, + + + styleHooks, verticalStyleHooks90, verticalStyleHooks270, + edges, k, edge, borderWidth; + +if (!view || !view.getComputedStyle) { + Element.prototype.getStyle = function (property, inline) { + var me = this, + dom = me.dom, + multiple = typeof property != 'string', + hooks = me.styleHooks, + prop = property, + props = prop, + len = 1, + isInline = inline, + camel, domStyle, values, hook, out, style, i; + + if (multiple) { + values = {}; + prop = props[0]; + i = 0; + if (!(len = props.length)) { + return values; + } + } + + if (!dom || dom.documentElement) { + return values || ''; + } + + domStyle = dom.style; + + if (inline) { + style = domStyle; + } else { + style = dom.currentStyle; + + + if (!style) { + isInline = true; + style = domStyle; + } + } + + do { + hook = hooks[prop]; + + if (!hook) { + hooks[prop] = hook = { name: Element.normalize(prop) }; + } + + if (hook.get) { + out = hook.get(dom, me, isInline, style); + } else { + camel = hook.name; + + + + + + if (hook.canThrow) { + try { + out = style[camel]; + } catch (e) { + out = ''; + } + } else { + + + out = style ? style[camel] : ''; + } + } + + if (!multiple) { + return out; + } + + values[prop] = out; + prop = props[++i]; + } while (i < len); + + return values; + }; +} + +Element.override({ + getHeight: function(contentHeight, preciseHeight) { + var me = this, + hidden = me.isStyle('display', 'none'), + height, + floating; + + if (hidden) { + return 0; + } + + height = me.dom.offsetHeight; + + + if (Ext.supports.Direct2DBug) { + floating = me.adjustDirect2DDimension(HEIGHT); + if (preciseHeight) { + height += floating; + } + else if (floating > 0 && floating < 0.5) { + height++; + } + } + + if (contentHeight) { + height -= me.getBorderWidth("tb") + me.getPadding("tb"); + } + + return (height < 0) ? 0 : height; + }, + + getWidth: function(contentWidth, preciseWidth) { + var me = this, + dom = me.dom, + hidden = me.isStyle('display', 'none'), + rect, width, floating; + + if (hidden) { + return 0; + } + + + + + + + + if (Ext.supports.BoundingClientRect) { + rect = dom.getBoundingClientRect(); + + + + + width = (me.vertical && !Ext.isIE9 && !Ext.supports.RotatedBoundingClientRect) ? + (rect.bottom - rect.top) : (rect.right - rect.left); + width = preciseWidth ? width : Math.ceil(width); + } else { + width = dom.offsetWidth; + } + + + + + if (Ext.supports.Direct2DBug && !me.vertical) { + + floating = me.adjustDirect2DDimension(WIDTH); + if (preciseWidth) { + width += floating; + } + + + + else if (floating > 0 && floating < 0.5) { + width++; + } + } + + if (contentWidth) { + width -= me.getBorderWidth("lr") + me.getPadding("lr"); + } + + return (width < 0) ? 0 : width; + }, + + setWidth: function(width, animate) { + var me = this; + width = me.adjustWidth(width); + if (!animate || !me.anim) { + me.dom.style.width = me.addUnits(width); + } + else { + if (!Ext.isObject(animate)) { + animate = {}; + } + me.animate(Ext.applyIf({ + to: { + width: width + } + }, animate)); + } + return me; + }, + + setHeight : function(height, animate) { + var me = this; + + height = me.adjustHeight(height); + if (!animate || !me.anim) { + me.dom.style.height = me.addUnits(height); + } + else { + if (!Ext.isObject(animate)) { + animate = {}; + } + me.animate(Ext.applyIf({ + to: { + height: height + } + }, animate)); + } + + return me; + }, + + applyStyles: function(style) { + Ext.DomHelper.applyStyles(this.dom, style); + return this; + }, + + setSize: function(width, height, animate) { + var me = this; + + if (Ext.isObject(width)) { + animate = height; + height = width.height; + width = width.width; + } + + width = me.adjustWidth(width); + height = me.adjustHeight(height); + + if (!animate || !me.anim) { + me.dom.style.width = me.addUnits(width); + me.dom.style.height = me.addUnits(height); + } + else { + if (animate === true) { + animate = {}; + } + me.animate(Ext.applyIf({ + to: { + width: width, + height: height + } + }, animate)); + } + + return me; + }, + + getViewSize : function() { + var me = this, + dom = me.dom, + isDoc = DOCORBODYRE.test(dom.nodeName), + ret; + + + if (isDoc) { + ret = { + width : Element.getViewWidth(), + height : Element.getViewHeight() + }; + } else { + ret = { + width : dom.clientWidth, + height : dom.clientHeight + }; + } + + return ret; + }, + + getSize: function(contentSize) { + return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)}; + }, + + + + + adjustWidth : function(width) { + var me = this, + isNum = (typeof width == 'number'); + + if (isNum && me.autoBoxAdjust && !me.isBorderBox()) { + width -= (me.getBorderWidth("lr") + me.getPadding("lr")); + } + return (isNum && width < 0) ? 0 : width; + }, + + + adjustHeight : function(height) { + var me = this, + isNum = (typeof height == "number"); + + if (isNum && me.autoBoxAdjust && !me.isBorderBox()) { + height -= (me.getBorderWidth("tb") + me.getPadding("tb")); + } + return (isNum && height < 0) ? 0 : height; + }, + + + getColor : function(attr, defaultValue, prefix) { + var v = this.getStyle(attr), + color = prefix || prefix === '' ? prefix : '#', + h, len, i=0; + + if (!v || (/transparent|inherit/.test(v))) { + return defaultValue; + } + if (/^r/.test(v)) { + v = v.slice(4, v.length - 1).split(','); + len = v.length; + for (; i 5 ? color.toLowerCase() : defaultValue); + }, + + + setOpacity: function(opacity, animate) { + var me = this; + + if (!me.dom) { + return me; + } + + if (!animate || !me.anim) { + me.setStyle('opacity', opacity); + } + else { + if (typeof animate != 'object') { + animate = { + duration: 350, + easing: 'ease-in' + }; + } + + me.animate(Ext.applyIf({ + to: { + opacity: opacity + } + }, animate)); + } + return me; + }, + + + clearOpacity : function() { + return this.setOpacity(''); + }, + + + adjustDirect2DDimension: function(dimension) { + var me = this, + dom = me.dom, + display = me.getStyle('display'), + inlineDisplay = dom.style.display, + inlinePosition = dom.style.position, + originIndex = dimension === WIDTH ? 0 : 1, + currentStyle = dom.currentStyle, + floating; + + if (display === 'inline') { + dom.style.display = 'inline-block'; + } + + dom.style.position = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static'; + + + + + + + floating = (parseFloat(currentStyle[dimension]) || parseFloat(currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1; + + dom.style.position = inlinePosition; + + if (display === 'inline') { + dom.style.display = inlineDisplay; + } + + return floating; + }, + + + clip : function() { + var me = this, + data = (me.$cache || me.getCache()).data, + style; + + if (!data[ISCLIPPED]) { + data[ISCLIPPED] = true; + style = me.getStyle([OVERFLOW, OVERFLOWX, OVERFLOWY]); + data[ORIGINALCLIP] = { + o: style[OVERFLOW], + x: style[OVERFLOWX], + y: style[OVERFLOWY] + }; + me.setStyle(OVERFLOW, HIDDEN); + me.setStyle(OVERFLOWX, HIDDEN); + me.setStyle(OVERFLOWY, HIDDEN); + } + return me; + }, + + + unclip : function() { + var me = this, + data = (me.$cache || me.getCache()).data, + clip; + + if (data[ISCLIPPED]) { + data[ISCLIPPED] = false; + clip = data[ORIGINALCLIP]; + if (clip.o) { + me.setStyle(OVERFLOW, clip.o); + } + if (clip.x) { + me.setStyle(OVERFLOWX, clip.x); + } + if (clip.y) { + me.setStyle(OVERFLOWY, clip.y); + } + } + return me; + }, + + + boxWrap : function(cls) { + cls = cls || Ext.baseCSSPrefix + 'box'; + var el = Ext.get(this.insertHtml("beforeBegin", "")); + Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom); + return el; + }, + + + getComputedHeight : function() { + var me = this, + h = Math.max(me.dom.offsetHeight, me.dom.clientHeight); + if (!h) { + h = parseFloat(me.getStyle(HEIGHT)) || 0; + if (!me.isBorderBox()) { + h += me.getFrameWidth('tb'); + } + } + return h; + }, + + + getComputedWidth : function() { + var me = this, + w = Math.max(me.dom.offsetWidth, me.dom.clientWidth); + + if (!w) { + w = parseFloat(me.getStyle(WIDTH)) || 0; + if (!me.isBorderBox()) { + w += me.getFrameWidth('lr'); + } + } + return w; + }, + + + getFrameWidth : function(sides, onlyContentBox) { + return (onlyContentBox && this.isBorderBox()) ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides)); + }, + + + addClsOnOver : function(className, testFn, scope) { + var me = this, + dom = me.dom, + hasTest = Ext.isFunction(testFn); + + me.hover( + function() { + if (hasTest && testFn.call(scope || me, me) === false) { + return; + } + Ext.fly(dom, INTERNAL).addCls(className); + }, + function() { + Ext.fly(dom, INTERNAL).removeCls(className); + } + ); + return me; + }, + + + addClsOnFocus : function(className, testFn, scope) { + var me = this, + dom = me.dom, + hasTest = Ext.isFunction(testFn); + + me.on("focus", function() { + if (hasTest && testFn.call(scope || me, me) === false) { + return false; + } + Ext.fly(dom, INTERNAL).addCls(className); + }); + me.on("blur", function() { + Ext.fly(dom, INTERNAL).removeCls(className); + }); + return me; + }, + + + addClsOnClick : function(className, testFn, scope) { + var me = this, + dom = me.dom, + hasTest = Ext.isFunction(testFn); + + me.on("mousedown", function() { + if (hasTest && testFn.call(scope || me, me) === false) { + return false; + } + Ext.fly(dom, INTERNAL).addCls(className); + var d = Ext.getDoc(), + fn = function() { + Ext.fly(dom, INTERNAL).removeCls(className); + d.removeListener("mouseup", fn); + }; + d.on("mouseup", fn); + }); + return me; + }, + + + getStyleSize : function() { + var me = this, + d = this.dom, + isDoc = DOCORBODYRE.test(d.nodeName), + s , + w, h; + + + if (isDoc) { + return { + width : Element.getViewWidth(), + height : Element.getViewHeight() + }; + } + + s = me.getStyle([HEIGHT, WIDTH], true); + + if (s.width && s.width != 'auto') { + w = parseFloat(s.width); + if (me.isBorderBox()) { + w -= me.getFrameWidth('lr'); + } + } + + if (s.height && s.height != 'auto') { + h = parseFloat(s.height); + if (me.isBorderBox()) { + h -= me.getFrameWidth('tb'); + } + } + + return {width: w || me.getWidth(true), height: h || me.getHeight(true)}; + }, + + statics: { + selectableCls: Ext.baseCSSPrefix + 'selectable', + unselectableCls: Ext.baseCSSPrefix + 'unselectable' + }, + + + selectable : function() { + var me = this; + + + + me.dom.unselectable = ''; + + me.removeCls(Element.unselectableCls); + me.addCls(Element.selectableCls); + + return me; + }, + + + unselectable : function() { + + + + + + + + var me = this; + + + + + + + if (Ext.isOpera) { + me.dom.unselectable = 'on'; + } + + + + + + + + + + + me.removeCls(Element.selectableCls); + me.addCls(Element.unselectableCls); + + return me; + }, + + + setVertical: function(angle, cls) { + var me = this, + proto = Element.prototype, + hooks; + + me.vertical = true; + if (cls) { + me.addCls(me.verticalCls = cls); + } + + me.setWidth = proto.setHeight; + me.setHeight = proto.setWidth; + if (!Ext.isIE9m) { + + + + + me.getWidth = proto.getHeight; + me.getHeight = proto.getWidth; + } + + + me.styleHooks = (angle === 270) ? + Element.prototype.verticalStyleHooks270 : Element.prototype.verticalStyleHooks90; + }, + + + setHorizontal: function() { + var me = this, + cls = me.verticalCls; + + delete me.vertical; + if (cls) { + delete me.verticalCls; + me.removeCls(cls); + } + + + delete me.setWidth; + delete me.setHeight; + if (!Ext.isIE9m) { + delete me.getWidth; + delete me.getHeight; + } + + + delete me.styleHooks; + } +}); + +Element.prototype.styleHooks = styleHooks = Ext.dom.AbstractElement.prototype.styleHooks; + + + +Element.prototype.verticalStyleHooks90 = verticalStyleHooks90 = Ext.Object.chain(Element.prototype.styleHooks); +Element.prototype.verticalStyleHooks270 = verticalStyleHooks270 = Ext.Object.chain(Element.prototype.styleHooks); + +verticalStyleHooks90.width = { name: 'height' }; +verticalStyleHooks90.height = { name: 'width' }; +verticalStyleHooks90['margin-top'] = { name: 'marginLeft' }; +verticalStyleHooks90['margin-right'] = { name: 'marginTop' }; +verticalStyleHooks90['margin-bottom'] = { name: 'marginRight' }; +verticalStyleHooks90['margin-left'] = { name: 'marginBottom' }; +verticalStyleHooks90['padding-top'] = { name: 'paddingLeft' }; +verticalStyleHooks90['padding-right'] = { name: 'paddingTop' }; +verticalStyleHooks90['padding-bottom'] = { name: 'paddingRight' }; +verticalStyleHooks90['padding-left'] = { name: 'paddingBottom' }; +verticalStyleHooks90['border-top'] = { name: 'borderLeft' }; +verticalStyleHooks90['border-right'] = { name: 'borderTop' }; +verticalStyleHooks90['border-bottom'] = { name: 'borderRight' }; +verticalStyleHooks90['border-left'] = { name: 'borderBottom' }; + +verticalStyleHooks270.width = { name: 'height' }; +verticalStyleHooks270.height = { name: 'width' }; +verticalStyleHooks270['margin-top'] = { name: 'marginRight' }; +verticalStyleHooks270['margin-right'] = { name: 'marginBottom' }; +verticalStyleHooks270['margin-bottom'] = { name: 'marginLeft' }; +verticalStyleHooks270['margin-left'] = { name: 'marginTop' }; +verticalStyleHooks270['padding-top'] = { name: 'paddingRight' }; +verticalStyleHooks270['padding-right'] = { name: 'paddingBottom' }; +verticalStyleHooks270['padding-bottom'] = { name: 'paddingLeft' }; +verticalStyleHooks270['padding-left'] = { name: 'paddingTop' }; +verticalStyleHooks270['border-top'] = { name: 'borderRight' }; +verticalStyleHooks270['border-right'] = { name: 'borderBottom' }; +verticalStyleHooks270['border-bottom'] = { name: 'borderLeft' }; +verticalStyleHooks270['border-left'] = { name: 'borderTop' }; + +if (Ext.isIE7m) { + styleHooks.fontSize = styleHooks['font-size'] = { + name: 'fontSize', + canThrow: true + }; + + styleHooks.fontStyle = styleHooks['font-style'] = { + name: 'fontStyle', + canThrow: true + }; + + styleHooks.fontFamily = styleHooks['font-family'] = { + name: 'fontFamily', + canThrow: true + }; +} + + +if (Ext.isIEQuirks || Ext.isIE && Ext.ieVersion <= 8) { + function getBorderWidth (dom, el, inline, style) { + if (style[this.styleName] == 'none') { + return '0px'; + } + return style[this.name]; + } + + edges = ['Top','Right','Bottom','Left']; + k = edges.length; + + while (k--) { + edge = edges[k]; + borderWidth = 'border' + edge + 'Width'; + + styleHooks['border-'+edge.toLowerCase()+'-width'] = styleHooks[borderWidth] = { + name: borderWidth, + styleName: 'border' + edge + 'Style', + get: getBorderWidth + }; + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Ext.getDoc().on('selectstart', function(ev, dom) { + var doc = document.documentElement, + selectableCls = Element.selectableCls, + unselectableCls = Element.unselectableCls, + tagName = dom && dom.tagName; + + tagName = tagName && tagName.toLowerCase(); + + + + + if (tagName === 'input' || tagName === 'textarea') { + return; + } + + + while (dom && dom.nodeType === 1 && dom !== doc) { + var el = Ext.fly(dom); + + + if (el.hasCls(selectableCls)) { + return; + } + + + if (el.hasCls(unselectableCls)) { + ev.stopEvent(); + return; + } + + dom = dom.parentNode; + } +}); + +}); + +Ext.onReady(function () { + var opacityRe = /alpha\(opacity=(.*)\)/i, + trimRe = /^\s+|\s+$/g, + hooks = Ext.dom.Element.prototype.styleHooks; + + + hooks.opacity = { + name: 'opacity', + afterSet: function(dom, value, el) { + if (el.isLayer) { + el.onOpacitySet(value); + } + } + }; + if (!Ext.supports.Opacity && Ext.isIE) { + Ext.apply(hooks.opacity, { + get: function (dom) { + var filter = dom.style.filter, + match, opacity; + if (filter.match) { + match = filter.match(opacityRe); + if (match) { + opacity = parseFloat(match[1]); + if (!isNaN(opacity)) { + return opacity ? opacity / 100 : 0; + } + } + } + return 1; + }, + set: function (dom, value) { + var style = dom.style, + val = style.filter.replace(opacityRe, '').replace(trimRe, ''); + + style.zoom = 1; + + + if (typeof(value) == 'number' && value >= 0 && value < 1) { + value *= 100; + style.filter = val + (val.length ? ' ' : '') + 'alpha(opacity='+value+')'; + } else { + style.filter = val; + } + } + }); + } + + +}); + + + + +Ext.define('Ext.util.Positionable', { + + _positionTopLeft: ['position', 'top', 'left'], + + _alignRe: /^([a-z]+)-([a-z]+)(\?)?$/, + + + + afterSetPosition: Ext.emptyFn, + + + + + + + getAnchorToXY: function() { + Ext.Error.raise("getAnchorToXY is not implemented in " + this.$className); + }, + + + getBorderPadding: function() { + Ext.Error.raise("getBorderPadding is not implemented in " + this.$className); + }, + + + getLocalX: function() { + Ext.Error.raise("getLocalX is not implemented in " + this.$className); + }, + + + getLocalXY: function() { + Ext.Error.raise("getLocalXY is not implemented in " + this.$className); + }, + + + getLocalY: function() { + Ext.Error.raise("getLocalY is not implemented in " + this.$className); + }, + + + getX: function() { + Ext.Error.raise("getX is not implemented in " + this.$className); + }, + + + getXY: function() { + Ext.Error.raise("getXY is not implemented in " + this.$className); + }, + + + getY: function() { + Ext.Error.raise("getY is not implemented in " + this.$className); + }, + + + setLocalX: function() { + Ext.Error.raise("setLocalX is not implemented in " + this.$className); + }, + + + setLocalXY: function() { + Ext.Error.raise("setLocalXY is not implemented in " + this.$className); + }, + + + setLocalY: function() { + Ext.Error.raise("setLocalY is not implemented in " + this.$className); + }, + + + setX: function() { + Ext.Error.raise("setX is not implemented in " + this.$className); + }, + + + setXY: function() { + Ext.Error.raise("setXY is not implemented in " + this.$className); + }, + + + setY: function() { + Ext.Error.raise("setY is not implemented in " + this.$className); + }, + + + + + + + + adjustForConstraints: function(xy, parent) { + var vector = this.getConstrainVector(parent, xy); + if (vector) { + xy[0] += vector[0]; + xy[1] += vector[1]; + } + return xy; + }, + + + alignTo: function(element, position, offsets, animate) { + var me = this, + el = me.el; + + return me.setXY(me.getAlignToXY(element, position, offsets), + el.anim && !!animate ? el.anim(animate) : false); + }, + + + anchorTo: function(anchorToEl, alignment, offsets, animate, monitorScroll, callback) { + var me = this, + scroll = !Ext.isEmpty(monitorScroll), + action = function() { + me.alignTo(anchorToEl, alignment, offsets, animate); + Ext.callback(callback, me); + }, + anchor = me.getAnchor(); + + + me.removeAnchor(); + Ext.apply(anchor, { + fn: action, + scroll: scroll + }); + + Ext.EventManager.onWindowResize(action, null); + + if (scroll) { + Ext.EventManager.on(window, 'scroll', action, null, + {buffer: !isNaN(monitorScroll) ? monitorScroll : 50}); + } + action(); + return me; + }, + + + calculateAnchorXY: function(anchor, extraX, extraY, mySize) { + + + var me = this, + el = me.el, + doc = document, + isViewport = el.dom == doc.body || el.dom == doc, + round = Math.round, + xy, myWidth, myHeight; + + anchor = (anchor || "tl").toLowerCase(); + mySize = mySize || {}; + + myWidth = mySize.width || (isViewport ? Ext.Element.getViewWidth() : me.getWidth()); + myHeight = mySize.height || (isViewport ? Ext.Element.getViewHeight() : me.getHeight()); + + + + switch (anchor) { + case 'tl' : xy = [0, 0]; + break; + case 'bl' : xy = [0, myHeight]; + break; + case 'tr' : xy = [myWidth, 0]; + break; + case 'c' : xy = [round(myWidth * 0.5), round(myHeight * 0.5)]; + break; + case 't' : xy = [round(myWidth * 0.5), 0]; + break; + case 'l' : xy = [0, round(myHeight * 0.5)]; + break; + case 'r' : xy = [myWidth, round(myHeight * 0.5)]; + break; + case 'b' : xy = [round(myWidth * 0.5), myHeight]; + break; + case 'tc' : xy = [round(myWidth * 0.5), 0]; + break; + case 'bc' : xy = [round(myWidth * 0.5), myHeight]; + break; + case 'br' : xy = [myWidth, myHeight]; + } + return [xy[0] + extraX, xy[1] + extraY]; + }, + + + convertPositionSpec: Ext.identityFn, + + + getAlignToXY: function(alignToEl, posSpec, offset) { + var me = this, + constrainToEl, + constrainTo, + alignMatch, myPosition, alignToElPosition, myWidth, myHeight, + alignToElRegion, swapY, swapX, constrain, align1, align2, + p1y, p1x, p2y, p2x, x, y; + + alignToEl = Ext.get(alignToEl.el || alignToEl); + + if (!alignToEl || !alignToEl.dom) { + Ext.Error.raise({ + sourceClass: 'Ext.util.Positionable', + sourceMethod: 'getAlignToXY', + msg: 'Attempted to align an element that doesn\'t exist' + }); + } + + offset = offset || [0,0]; + posSpec = (!posSpec || posSpec == "?" ? "tl-bl?" : + (!(/-/).test(posSpec) && posSpec !== "" ? "tl-" + posSpec : posSpec || "tl-bl")).toLowerCase(); + + posSpec = me.convertPositionSpec(posSpec); + + alignMatch = posSpec.match(me._alignRe); + + if (!alignMatch) { + Ext.Error.raise({ + sourceClass: 'Ext.util.Positionable', + sourceMethod: 'getAlignToXY', + el: alignToEl, + position: posSpec, + offset: offset, + msg: 'Attemmpted to align an element with an invalid position: "' + posSpec + '"' + }); + } + + align1 = alignMatch[1]; + align2 = alignMatch[2]; + constrain = !!alignMatch[3]; + + + + myPosition = me.getAnchorXY(align1, true); + alignToElPosition = me.getAnchorToXY(alignToEl, align2, false); + + x = alignToElPosition[0] - myPosition[0] + offset[0]; + y = alignToElPosition[1] - myPosition[1] + offset[1]; + + + if (constrain) { + + + + + constrainToEl = me.constrainTo || me.container || me.el.parent(); + constrainToEl = Ext.get(constrainToEl.el || constrainToEl) + constrainTo = constrainToEl.getViewRegion(); + constrainTo.right = constrainTo.left + constrainToEl.el.dom.clientWidth; + + myWidth = me.getWidth(); + myHeight = me.getHeight(); + alignToElRegion = alignToEl.getRegion(); + + + + + p1y = align1.charAt(0); + p1x = align1.charAt(align1.length - 1); + p2y = align2.charAt(0); + p2x = align2.charAt(align2.length - 1); + + + + swapY = (x < alignToElRegion.right && x + myWidth >= alignToElRegion.left) && ((p1y == "t" && p2y == "b") || (p1y == "b" && p2y == "t")); + + + + swapX = (y < alignToElRegion.bottom && y + myHeight >= alignToElRegion.top) && ((p1x == "r" && p2x == "l") || (p1x == "l" && p2x == "r")); + + if (x + myWidth > constrainTo.right) { + if (swapX) { + x = alignToElRegion.left - myWidth; + + + swapX = false; + } else { + x = constrainTo.right - myWidth; + } + } + if (x < constrainTo.left) { + x = swapX ? alignToElRegion.right : constrainTo.left; + } + if (y + myHeight > constrainTo.bottom) { + if (swapY) { + y = alignToElRegion.top - myHeight; + + + swapY = false; + } else { + y = constrainTo.bottom - myHeight; + } + } + if (y < constrainTo.top) { + y = swapY ? alignToElRegion.bottom : constrainTo.top; + } + } + return [x,y]; + }, + + + getAnchor: function(){ + var el = this.el, + data = (el.$cache || el.getCache()).data, + anchor; + + if (!el.dom) { + return; + } + anchor = data._anchor; + + if(!anchor){ + anchor = data._anchor = {}; + } + return anchor; + }, + + + getAnchorXY: function(anchor, local, mySize) { + var me = this, + myPos = me.getXY(), + el = me.el, + doc = document, + isViewport = el.dom == doc.body || el.dom == doc, + scroll = el.getScroll(), + extraX = isViewport ? scroll.left : local ? 0 : myPos[0], + extraY = isViewport ? scroll.top : local ? 0 : myPos[1]; + + return me.calculateAnchorXY(anchor, extraX, extraY, mySize); + }, + + + getBox: function(contentBox, local) { + var me = this, + xy = local ? me.getLocalXY() : me.getXY(), + x = xy[0], + y = xy[1], + w = me.getWidth(), + h = me.getHeight(), + borderPadding, beforeX, beforeY; + + if (contentBox) { + borderPadding = me.getBorderPadding(); + beforeX = borderPadding.beforeX; + beforeY = borderPadding.beforeY; + + x += beforeX; + y += beforeY; + w -= (beforeX + borderPadding.afterX); + h -= (beforeY + borderPadding.afterY); + } + + return { + x: x, + left: x, + 0: x, + y: y, + top: y, + 1: y, + width: w, + height: h, + right: x + w, + bottom: y + h + }; + }, + + + calculateConstrainedPosition: function(constrainTo, proposedPosition, local, proposedSize) { + var me = this, + vector, + fp = me.floatParent, + parentNode = fp ? fp.getTargetEl() : null, + parentOffset, + borderPadding, + proposedConstrainPosition, + xy = false; + + if (local && fp) { + parentOffset = parentNode.getXY(); + borderPadding = parentNode.getBorderPadding(); + parentOffset[0] += borderPadding.beforeX; + parentOffset[1] += borderPadding.beforeY; + if (proposedPosition) { + proposedConstrainPosition = [proposedPosition[0] + parentOffset[0], proposedPosition[1] + parentOffset[1]]; + } + } else { + proposedConstrainPosition = proposedPosition; + } + + + + constrainTo = constrainTo || me.constrainTo || parentNode || me.container || me.el.parent(); + vector = (me.constrainHeader ? me.header : me).getConstrainVector(constrainTo, proposedConstrainPosition, proposedSize); + + + if (vector) { + xy = proposedPosition || me.getPosition(local); + xy[0] += vector[0]; + xy[1] += vector[1]; + } + return xy; + }, + + + getConstrainVector: function(constrainTo, proposedPosition, proposedSize) { + var thisRegion = this.getRegion(), + vector = [0, 0], + shadowSize = (this.shadow && this.constrainShadow && !this.shadowDisabled) ? this.shadow.getShadowSize() : undefined, + overflowed = false, + constrainSize, + constraintInsets = this.constraintInsets; + + if (!(constrainTo instanceof Ext.util.Region)) { + constrainTo = Ext.get(constrainTo.el || constrainTo); + + + + constrainSize = constrainTo.getViewSize(); + constrainTo = constrainTo.getViewRegion(); + constrainTo.right = constrainTo.left + constrainSize.width; + constrainTo.bottom = constrainTo.top + constrainSize.height; + } + + + if (constraintInsets) { + constraintInsets = Ext.isObject(constraintInsets) ? constraintInsets : Ext.Element.parseBox(constraintInsets); + constrainTo.adjust(constraintInsets.top, constraintInsets.right, constraintInsets.bottom, constraintInsets.length); + } + + + if (proposedPosition) { + thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y); + } + + if (proposedSize) { + thisRegion.right = thisRegion.left + proposedSize[0]; + thisRegion.bottom = thisRegion.top + proposedSize[1]; + } + + + if (shadowSize) { + constrainTo.adjust(shadowSize[0], -shadowSize[1], -shadowSize[2], shadowSize[3]); + } + + + if (thisRegion.right > constrainTo.right) { + overflowed = true; + vector[0] = (constrainTo.right - thisRegion.right); + } + if (thisRegion.left + vector[0] < constrainTo.left) { + overflowed = true; + vector[0] = (constrainTo.left - thisRegion.left); + } + + + if (thisRegion.bottom > constrainTo.bottom) { + overflowed = true; + vector[1] = (constrainTo.bottom - thisRegion.bottom); + } + if (thisRegion.top + vector[1] < constrainTo.top) { + overflowed = true; + vector[1] = (constrainTo.top - thisRegion.top); + } + return overflowed ? vector : false; + }, + + + getOffsetsTo: function(offsetsTo) { + var o = this.getXY(), + e = Ext.fly(offsetsTo.el || offsetsTo, '_internal').getXY(); + return [o[0] - e[0],o[1] - e[1]]; + }, + + + getRegion: function() { + var box = this.getBox(); + return new Ext.util.Region(box.top, box.right, box.bottom, box.left); + }, + + + getViewRegion: function() { + var me = this, + el = me.el, + isBody = el.dom.nodeName === 'BODY', + borderPadding, scroll, pos, top, left, width, height; + + + if (isBody) { + scroll = el.getScroll(); + left = scroll.left; + top = scroll.top; + width = Ext.dom.AbstractElement.getViewportWidth(); + height = Ext.dom.AbstractElement.getViewportHeight(); + } + else { + borderPadding = me.getBorderPadding(); + pos = me.getXY(); + left = pos[0] + borderPadding.beforeX; + top = pos[1] + borderPadding.beforeY; + width = me.getWidth(true); + height = me.getHeight(true); + } + + return new Ext.util.Region(top, left + width, top + height, left); + }, + + + move: function(direction, distance, animate) { + var me = this, + xy = me.getXY(), + x = xy[0], + y = xy[1], + left = [x - distance, y], + right = [x + distance, y], + top = [x, y - distance], + bottom = [x, y + distance], + hash = { + l: left, + left: left, + r: right, + right: right, + t: top, + top: top, + up: top, + b: bottom, + bottom: bottom, + down: bottom + }; + + direction = direction.toLowerCase(); + me.setXY([hash[direction][0], hash[direction][1]], animate); + }, + + + removeAnchor: function() { + var anchor = this.getAnchor(); + + if (anchor && anchor.fn) { + Ext.EventManager.removeResizeListener(anchor.fn); + if (anchor.scroll) { + Ext.EventManager.un(window, 'scroll', anchor.fn); + } + delete anchor.fn; + } + return this; + }, + + + setBox: function(box, animate) { + var me = this, + el = me.el, + x = box.x, + y = box.y, + xy = [x, y], + w = box.width, + h = box.height, + doConstrain = (me.constrain || me.constrainHeader), + constrainedPos = doConstrain && me.calculateConstrainedPosition(null, [x, y], false, [w, h]); + + + if (constrainedPos) { + x = constrainedPos[0]; + y = constrainedPos[1]; + } + if (!animate || !el.anim) { + me.setSize(w, h); + me.setXY([x, y]); + me.afterSetPosition(x, y); + } else { + me.animate(Ext.applyIf({ + to: { + x: x, + y: y, + width: el.adjustWidth(w), + height: el.adjustHeight(h) + }, + listeners: { + afteranimate: Ext.Function.bind(me.afterSetPosition, me, [x, y]) + } + }, animate)); + } + return me; + }, + + + setRegion: function(region, animate) { + return this.setBox({ + x: region.left, + y: region.top, + width: region.right - region.left, + height: region.bottom - region.top + }, animate); + }, + + + translatePoints: function(x, y) { + var pos = this.translateXY(x, y); + + return { + left: pos.x, + top: pos.y + }; + }, + + + translateXY: function(x, y) { + var me = this, + el = me.el, + styles = el.getStyle(me._positionTopLeft), + relative = styles.position == 'relative', + left = parseFloat(styles.left), + top = parseFloat(styles.top), + xy = me.getXY(); + + if (Ext.isArray(x)) { + y = x[1]; + x = x[0]; + } + if (isNaN(left)) { + left = relative ? 0 : el.dom.offsetLeft; + } + if (isNaN(top)) { + top = relative ? 0 : el.dom.offsetTop; + } + left = (typeof x == 'number') ? x - xy[0] + left : undefined; + top = (typeof y == 'number') ? y - xy[1] + top : undefined; + return { + x: left, + y: top + }; + } +}); + + + + +Ext.define('Ext.dom.Element', function(Element) { + var HIDDEN = 'hidden', + DOC = document, + VISIBILITY = "visibility", + DISPLAY = "display", + NONE = "none", + XMASKED = Ext.baseCSSPrefix + "masked", + XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative", + EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg", + bodyRe = /^body/i, + visFly, + + + noBoxAdjust = Ext.isStrict ? { + select: 1 + }: { + input: 1, + select: 1, + textarea: 1 + }, + + + isScrolled = function(c) { + var r = [], ri = -1, + i, ci; + for (i = 0; ci = c[i]; i++) { + if (ci.scrollTop > 0 || ci.scrollLeft > 0) { + r[++ri] = ci; + } + } + return r; + }; + + return { + + extend: Ext.dom.AbstractElement , + + alternateClassName: ['Ext.Element', 'Ext.core.Element'], + + + + + + + + + + + + isElement: true, + + tableTagRe: /^(?:tr|td|table|tbody)$/i, + + mixins: [ + Ext.util.Positionable + ], + + addUnits: function() { + return Element.addUnits.apply(Element, arguments); + }, + + + focus: function(defer, dom) { + var me = this; + + dom = dom || me.dom; + try { + if (Number(defer)) { + Ext.defer(me.focus, defer, me, [null, dom]); + } else { + Ext.globalEvents.fireEvent('beforefocus', dom); + dom.focus(); + } + } catch(e) { + } + return me; + }, + + + blur: function() { + var me = this, + dom = me.dom; + + + if (dom !== document.body) { + try { + dom.blur(); + } catch(e) { + } + return me; + } else { + return me.focus(undefined, dom); + } + }, + + + isBorderBox: function() { + var box = Ext.isBorderBox; + + + + if (Ext.isIE7m && !box) { + box = ((this.dom.tagName || "").toLowerCase() in noBoxAdjust); + } + return box; + }, + + + hover: function(overFn, outFn, scope, options) { + var me = this; + me.on('mouseenter', overFn, scope || me.dom, options); + me.on('mouseleave', outFn, scope || me.dom, options); + return me; + }, + + + getAttributeNS: function(ns, name) { + return this.getAttribute(name, ns); + }, + + getAttribute: (Ext.isIE && !(Ext.isIE9p && DOC.documentMode >= 9)) ? + + + + + + + + + + + + function(name, ns) { + var d = this.dom, + type; + if (ns) { + type = typeof d[ns + ":" + name]; + if (type != 'undefined' && type != 'unknown') { + return d[ns + ":" + name] || null; + } + return null; + } + if (name === "for") { + name = "htmlFor"; + } + return d[name] || null; + } : function(name, ns) { + var d = this.dom; + if (ns) { + return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name); + } + return d.getAttribute(name) || d[name] || null; + }, + + + cacheScrollValues: function() { + var me = this, + scrolledDescendants, + el, i, + scrollValues = [], + result = function() { + for (i = 0; i < scrolledDescendants.length; i++) { + el = scrolledDescendants[i]; + el.scrollLeft = scrollValues[i][0]; + el.scrollTop = scrollValues[i][1]; + } + }; + + if (!Ext.DomQuery.pseudos.isScrolled) { + Ext.DomQuery.pseudos.isScrolled = isScrolled; + } + scrolledDescendants = me.query(':isScrolled'); + for (i = 0; i < scrolledDescendants.length; i++) { + el = scrolledDescendants[i]; + scrollValues[i] = [el.scrollLeft, el.scrollTop]; + } + return result; + }, + + + autoBoxAdjust: true, + + + isVisible : function(deep) { + var me = this, + dom = me.dom, + stopNode = dom.ownerDocument.documentElement; + + if (!visFly) { + visFly = new Element.Fly(); + } + + while (dom !== stopNode) { + + + if (!dom || dom.nodeType === 11 || (visFly.attach(dom)).isStyle(VISIBILITY, HIDDEN) || visFly.isStyle(DISPLAY, NONE)) { + return false; + } + + if (!deep) { + break; + } + dom = dom.parentNode; + } + return true; + }, + + + isDisplayed : function() { + return !this.isStyle(DISPLAY, NONE); + }, + + + enableDisplayMode : function(display) { + var me = this; + + me.setVisibilityMode(Element.DISPLAY); + + if (!Ext.isEmpty(display)) { + (me.$cache || me.getCache()).data.originalDisplay = display; + } + + return me; + }, + + + mask: function (msg, msgCls , elHeight) { + var me = this, + dom = me.dom, + + + setExpression = dom.style.setExpression, + data = (me.$cache || me.getCache()).data, + maskShimEl = data.maskShimEl, + maskEl = data.maskEl, + maskMsg = data.maskMsg, + widthExpression, heightExpression, docElem, ie6DocElHeight; + + if (!(bodyRe.test(dom.tagName) && me.getStyle('position') == 'static')) { + me.addCls(XMASKEDRELATIVE); + } + + + if (maskEl) { + maskEl.remove(); + } + + if (maskMsg) { + maskMsg.remove(); + } + + if (maskShimEl) { + maskShimEl.remove(); + } + + if (Ext.isIE6) { + maskShimEl = Ext.DomHelper.append(dom, { + tag: 'iframe', + role: 'presentation', + cls : Ext.baseCSSPrefix + 'shim ' + Ext.baseCSSPrefix + 'mask-shim' + }, true); + data.maskShimEl = maskShimEl; + maskShimEl.setDisplayed(true); + } + + Ext.DomHelper.append(dom, [{ + role: 'presentation', + cls : Ext.baseCSSPrefix + "mask", + style: 'top:0;left:0;' + }, { + role: 'presentation', + cls : msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG, + cn : { + tag: 'div', + role: 'presentation', + cls: Ext.baseCSSPrefix + 'mask-msg-inner', + cn: { + tag: 'div', + role: 'presentation', + cls: Ext.baseCSSPrefix + 'mask-msg-text', + html: msg || '' + } + } + }]); + + maskMsg = Ext.get(dom.lastChild); + maskEl = Ext.get(maskMsg.dom.previousSibling); + + data.maskMsg = maskMsg; + data.maskEl = maskEl; + + me.addCls(XMASKED); + maskEl.setDisplayed(true); + + if (typeof msg == 'string') { + maskMsg.setDisplayed(true); + maskMsg.center(me); + } else { + maskMsg.setDisplayed(false); + } + + + + + + if (Ext.isStrict && !Ext.isIE6 && (dom === DOC.body)) { + maskEl.addCls(Ext.baseCSSPrefix + 'mask-fixed'); + } + + + + + + + + + + if (dom !== DOC.body || Ext.isIE6 || Ext.isIEQuirks) { + if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) { + + try { + maskEl.dom.style.setExpression('width', 'this.parentNode.clientWidth + "px"'); + widthExpression = 'this.parentNode.clientWidth + "px"'; + if (maskShimEl) { + maskShimEl.dom.style.setExpression('width', widthExpression); + } + maskEl.dom.style.setExpression('width', widthExpression); + } catch (e) {} + } + + + + if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) { + + try { + heightExpression = 'this.parentNode.' + (dom == DOC.body ? 'scrollHeight' : 'offsetHeight') + ' + "px"'; + if (maskShimEl) { + maskShimEl.dom.style.setExpression('height', heightExpression); + } + maskEl.dom.style.setExpression('height', heightExpression); + } catch (e) {} + } + + else if (Ext.isIE9m && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') { + if (Ext.isIE6 && Ext.isStrict) { + docElem = dom.parentNode; + + + + + + + ie6DocElHeight = Math.max(docElem.clientHeight, docElem.scrollHeight); + } + if (maskShimEl) { + maskShimEl.setSize(undefined, elHeight || ie6DocElHeight || me.getHeight()); + } + maskEl.setSize(undefined, elHeight || ie6DocElHeight || me.getHeight()); + } + } + return maskEl; + }, + + + unmask : function() { + var me = this, + data = (me.$cache || me.getCache()).data, + maskEl = data.maskEl, + maskShimEl = data.maskShimEl, + maskMsg = data.maskMsg, + style; + + if (maskEl) { + style = maskEl.dom.style; + + if (style.clearExpression) { + style.clearExpression('width'); + style.clearExpression('height'); + } + + if (maskEl) { + maskEl.remove(); + delete data.maskEl; + } + + if (maskMsg) { + maskMsg.remove(); + delete data.maskMsg; + } + + me.removeCls([XMASKED, XMASKEDRELATIVE]); + + if (maskShimEl) { + style = maskShimEl.dom.style; + + if (style.clearExpression) { + style.clearExpression('width'); + style.clearExpression('height'); + } + + maskShimEl.remove(); + delete data.maskShimEl; + } + } + }, + + + isMasked : function() { + var me = this, + data = (me.$cache || me.getCache()).data, + maskEl = data.maskEl, + maskMsg = data.maskMsg, + hasMask = false; + + if (maskEl && maskEl.isVisible()) { + if (maskMsg) { + maskMsg.center(me); + } + hasMask = true; + } + return hasMask; + }, + + + createShim : function() { + var el = DOC.createElement('iframe'), + shim; + + el.frameBorder = '0'; + el.className = Ext.baseCSSPrefix + 'shim'; + el.src = Ext.SSL_SECURE_URL; + el.setAttribute('role', 'presentation'); + shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom)); + shim.autoBoxAdjust = false; + return shim; + }, + + + addKeyListener : function(key, fn, scope){ + var config; + if(typeof key != 'object' || Ext.isArray(key)){ + config = { + target: this, + key: key, + fn: fn, + scope: scope + }; + }else{ + config = { + target: this, + key : key.key, + shift : key.shift, + ctrl : key.ctrl, + alt : key.alt, + fn: fn, + scope: scope + }; + } + return new Ext.util.KeyMap(config); + }, + + + addKeyMap : function(config) { + return new Ext.util.KeyMap(Ext.apply({ + target: this + }, config)); + }, + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on: function(eventName, fn, scope, options) { + Ext.EventManager.on(this, eventName, fn, scope || this, options); + return this; + }, + + + un: function(eventName, fn, scope) { + Ext.EventManager.un(this, eventName, fn, scope || this); + return this; + }, + + + removeAllListeners: function() { + Ext.EventManager.removeAll(this); + return this; + }, + + + purgeAllListeners: function() { + Ext.EventManager.purgeElement(this); + return this; + }, + + select: function(selector) { + return Element.select(selector, false, this.dom); + } + }; +}, function() { + + var DOC = document, + EC = Ext.cache, + Element = this, + AbstractElement = Ext.dom.AbstractElement, + focusRe = /^a|button|embed|iframe|input|object|select|textarea$/i, + nonSpaceRe = /\S/, + scriptTagRe = /(?:]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig, + replaceScriptTagRe = /(?:)((\n|\r|.)*?)(?:<\/script>)/ig, + srcRe = /\ssrc=([\'\"])(.*?)\1/i, + typeRe = /\stype=([\'\"])(.*?)\1/i, + useDocForId = !Ext.isIE8m, + internalFly; + + Element.boxMarkup = [ + '', + '', + '' + ].join(''); + + + + + + function garbageCollect() { + if (!Ext.enableGarbageCollector) { + clearInterval(Element.collectorThreadId); + } else { + var eid, + d, + o, + t; + + for (eid in EC) { + if (!EC.hasOwnProperty(eid)) { + continue; + } + + o = EC[eid]; + + + if (o.skipGarbageCollection) { + continue; + } + + d = o.dom; + + + if (!d) { + Ext.Error.raise('Missing DOM node in element garbage collection: ' + eid); + } + + + if (d && (d.getElementById || d.navigator)) { + Ext.Error.raise('Unexpected document or window element in element garbage collection'); + } + + + + + + + + + + + + + + + + if (d && (!d.parentNode || (!d.offsetParent && !Ext.getElementById(eid)))) { + if (Ext.enableListenerCollection) { + Ext.EventManager.removeAll(d); + } + delete EC[eid]; + } + } + + if (Ext.isIE) { + t = {}; + for (eid in EC) { + if (!EC.hasOwnProperty(eid)) { + continue; + } + t[eid] = EC[eid]; + } + EC = Ext.cache = t; + } + } + } + + Element.collectorThreadId = setInterval(garbageCollect, 30000); + + + Element.addMethods({ + + + monitorMouseLeave: function(delay, handler, scope) { + var me = this, + timer, + listeners = { + mouseleave: function(e) { + timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay); + }, + mouseenter: function() { + clearTimeout(timer); + }, + freezeEvent: true + }; + + me.on(listeners); + return listeners; + }, + + + swallowEvent : function(eventName, preventDefault) { + var me = this, + e, eLen, + fn = function(e) { + e.stopPropagation(); + if (preventDefault) { + e.preventDefault(); + } + }; + + if (Ext.isArray(eventName)) { + eLen = eventName.length; + + for (e = 0; e < eLen; e++) { + me.on(eventName[e], fn); + } + + return me; + } + me.on(eventName, fn); + return me; + }, + + + relayEvent : function(eventName, observable) { + this.on(eventName, function(e) { + observable.fireEvent(eventName, e); + }); + }, + + + clean : function(forceReclean) { + var me = this, + dom = me.dom, + data = (me.$cache || me.getCache()).data, + n = dom.firstChild, + ni = -1, + nx; + + if (data.isCleaned && forceReclean !== true) { + return me; + } + + while (n) { + nx = n.nextSibling; + if (n.nodeType == 3) { + + if (!(nonSpaceRe.test(n.nodeValue))) { + dom.removeChild(n); + + } else if (nx && nx.nodeType == 3) { + n.appendData(Ext.String.trim(nx.data)); + dom.removeChild(nx); + nx = n.nextSibling; + n.nodeIndex = ++ni; + } + } else { + + internalFly.attach(n).clean(); + n.nodeIndex = ++ni; + } + n = nx; + } + + data.isCleaned = true; + return me; + }, + + + load : function(options) { + this.getLoader().load(options); + return this; + }, + + + getLoader : function() { + var me = this, + data = (me.$cache || me.getCache()).data, + loader = data.loader; + + if (!loader) { + data.loader = loader = new Ext.ElementLoader({ + target: me + }); + } + return loader; + }, + + + syncContent: function(source) { + source = Ext.getDom(source); + var sourceNodes = source.childNodes, + sourceLen = sourceNodes.length, + dest = this.dom, + destNodes = dest.childNodes, + destLen = destNodes.length, + i, destNode, sourceNode, + nodeType, newAttrs, attLen, attName; + + + + + + if (Ext.isIE9m && dest.mergeAttributes) { + dest.mergeAttributes(source, true); + + + + dest.src = source.src; + } else { + newAttrs = source.attributes; + attLen = newAttrs.length; + for (i = 0; i < attLen; i++) { + attName = newAttrs[i].name; + if (attName !== 'id') { + dest.setAttribute(attName, newAttrs[i].value); + } + } + } + + + if (sourceLen !== destLen) { + dest.innerHTML = source.innerHTML; + return; + } + + + + for (i = 0; i < sourceLen; i++) { + sourceNode = sourceNodes[i]; + destNode = destNodes[i]; + nodeType = sourceNode.nodeType; + + + if (nodeType !== destNode.nodeType || (nodeType === 1 && sourceNode.tagName !== destNode.tagName)) { + dest.innerHTML = source.innerHTML; + return; + } + + + if (nodeType === 3) { + destNode.data = sourceNode.data; + } + + else { + if (sourceNode.id && destNode.id !== sourceNode.id) { + destNode.id = sourceNode.id; + } + destNode.style.cssText = sourceNode.style.cssText; + destNode.className = sourceNode.className; + internalFly.attach(destNode).syncContent(sourceNode); + } + } + }, + + + update : function(html, loadScripts, callback) { + var me = this, + id, + dom, + interval; + + if (!me.dom) { + return me; + } + html = html || ''; + dom = me.dom; + + if (loadScripts !== true) { + dom.innerHTML = html; + Ext.callback(callback, me); + return me; + } + + id = Ext.id(); + html += ''; + + interval = setInterval(function() { + var hd, + match, + attrs, + srcMatch, + typeMatch, + el, + s; + if (!(el = DOC.getElementById(id))) { + return false; + } + clearInterval(interval); + Ext.removeNode(el); + hd = Ext.getHead().dom; + + while ((match = scriptTagRe.exec(html))) { + attrs = match[1]; + srcMatch = attrs ? attrs.match(srcRe) : false; + if (srcMatch && srcMatch[2]) { + s = DOC.createElement("script"); + s.src = srcMatch[2]; + typeMatch = attrs.match(typeRe); + if (typeMatch && typeMatch[2]) { + s.type = typeMatch[2]; + } + hd.appendChild(s); + } else if (match[2] && match[2].length > 0) { + if (window.execScript) { + window.execScript(match[2]); + } else { + window.eval(match[2]); + } + } + } + Ext.callback(callback, me); + }, 20); + dom.innerHTML = html.replace(replaceScriptTagRe, ''); + return me; + }, + + + removeAllListeners : function() { + this.removeAnchor(); + Ext.EventManager.removeAll(this.dom); + return this; + }, + + + createProxy : function(config, renderTo, matchBox) { + config = (typeof config == 'object') ? config : { tag: "div", role: 'presentation', cls: config }; + + var me = this, + proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) : + Ext.DomHelper.insertBefore(me.dom, config, true); + + proxy.setVisibilityMode(Element.DISPLAY); + proxy.hide(); + if (matchBox && me.setBox && me.getBox) { + proxy.setBox(me.getBox()); + } + return proxy; + }, + + + needsTabIndex: function() { + if (this.dom) { + if ((this.dom.nodeName === 'a') && (!this.dom.href)) { + return true; + } + return !focusRe.test(this.dom.nodeName); + } + }, + + + isFocusable: function ( asFocusEl) { + var dom = this.dom, + tabIndexAttr = dom.getAttributeNode('tabIndex'), + tabIndex, + nodeName = dom.nodeName, + canFocus = false; + + + + + + + + + + + + if (tabIndexAttr && tabIndexAttr.specified) { + tabIndex = tabIndexAttr.value; + } + if (dom && !dom.disabled) { + + + if (tabIndex == -1) { + canFocus = Ext.enableFocusManager && asFocusEl; + } + else { + + if (focusRe.test(nodeName)) { + if ((nodeName !== 'a') || dom.href) { + canFocus = true; + } + } + + else { + canFocus = tabIndex != null && tabIndex >= 0; + } + } + canFocus = canFocus && this.isVisible(true); + } + return canFocus; + } + }); + + if (Ext.isIE9m) { + Element.prototype.getById = function (id, asDom) { + var dom = this.dom, + cacheItem, el, ret; + + if (dom) { + + + el = (useDocForId && DOC.getElementById(id)) || dom.all[id]; + if (el) { + if (asDom) { + ret = el; + } else { + + + cacheItem = EC[id]; + if (cacheItem && cacheItem.el) { + ret = Ext.updateCacheEntry(cacheItem, el).el; + } else { + ret = new Element(el); + } + } + return ret; + } + } + + return asDom ? Ext.getDom(id) : Element.get(id); + }; + } + + Element.createAlias({ + + addListener: 'on', + + removeListener: 'un', + + clearListeners: 'removeAllListeners', + + focusable: 'isFocusable' + }); + + Element.Fly = AbstractElement.Fly = new Ext.Class({ + extend: Element, + + isFly: true, + + constructor: function(dom) { + this.dom = dom; + + + + this.el = this; + }, + + attach: AbstractElement.Fly.prototype.attach + }); + + internalFly = new Element.Fly(); + + if (Ext.isIE9m) { + Ext.getElementById = function (id) { + var el = DOC.getElementById(id), + detachedBodyEl; + + if (!el && (detachedBodyEl = AbstractElement.detachedBodyEl)) { + el = detachedBodyEl.dom.all[id]; + } + + return el; + }; + } else if (!DOC.querySelector) { + Ext.getDetachedBody = Ext.getBody; + + Ext.getElementById = function (id) { + return DOC.getElementById(id); + }; + } +}); + + + + +Ext.define('Ext.dom.CompositeElementLite', { + alternateClassName: 'Ext.CompositeElementLite', + + + + statics: { + + importElementMethods: function() { + var name, + elementPrototype = Ext.dom.Element.prototype, + prototype = this.prototype; + + for (name in elementPrototype) { + if (typeof elementPrototype[name] == 'function'){ + (function(key) { + prototype[key] = prototype[key] || function() { + return this.invoke(key, arguments); + }; + }).call(prototype, name); + + } + } + } + }, + + constructor: function(elements, root) { + + this.elements = []; + this.add(elements, root); + this.el = new Ext.dom.AbstractElement.Fly(); + }, + + + isComposite: true, + + + getElement: function(el) { + + return this.el.attach(el); + }, + + + transformElement: function(el) { + return Ext.getDom(el); + }, + + + getCount: function() { + return this.elements.length; + }, + + + add: function(els, root) { + var elements = this.elements, + i, ln; + + if (!els) { + return this; + } + + if (typeof els == "string") { + els = Ext.dom.Element.selectorFunction(els, root); + } + else if (els.isComposite) { + els = els.elements; + } + else if (!Ext.isIterable(els)) { + els = [els]; + } + + for (i = 0, ln = els.length; i < ln; ++i) { + elements.push(this.transformElement(els[i])); + } + + return this; + }, + + invoke: function(fn, args) { + var elements = this.elements, + ln = elements.length, + element, + i; + + fn = Ext.dom.Element.prototype[fn]; + for (i = 0; i < ln; i++) { + element = elements[i]; + + if (element) { + fn.apply(this.getElement(element), args); + } + } + return this; + }, + + + item: function(index) { + var el = this.elements[index], + out = null; + + if (el) { + out = this.getElement(el); + } + + return out; + }, + + + slice: function() { + return this.elements.slice.apply(this.elements, arguments); + }, + + + addListener: function(eventName, handler, scope, opt) { + var els = this.elements, + len = els.length, + i, e; + + for (i = 0; i < len; i++) { + e = els[i]; + if (e) { + Ext.EventManager.on(e, eventName, handler, scope || e, opt); + } + } + return this; + }, + + each: function(fn, scope) { + var me = this, + els = me.elements, + len = els.length, + i, e; + + for (i = 0; i < len; i++) { + e = els[i]; + if (e) { + e = this.getElement(e); + if (fn.call(scope || e, e, me, i) === false) { + break; + } + } + } + return me; + }, + + + fill: function(els) { + var me = this; + me.elements = []; + me.add(els); + return me; + }, + + insert: function(index, nodes) { + Ext.Array.insert(this.elements, index, nodes); + }, + + + filter: function(selector) { + var me = this, + els = me.elements, + len = els.length, + out = [], + i = 0, + isFunc = typeof selector == 'function', + add, + el; + + for (; i < len; i++) { + el = els[i]; + add = false; + if (el) { + el = me.getElement(el); + + if (isFunc) { + add = selector.call(el, el, me, i) !== false; + } else { + add = el.is(selector); + } + + if (add) { + out.push(me.transformElement(el)); + } + } + } + + me.elements = out; + return me; + }, + + + indexOf: function(el) { + return Ext.Array.indexOf(this.elements, this.transformElement(el)); + }, + + + replaceElement: function(el, replacement, domReplace) { + var index = !isNaN(el) ? el : this.indexOf(el), + d; + if (index > -1) { + replacement = Ext.getDom(replacement); + if (domReplace) { + d = this.elements[index]; + d.parentNode.insertBefore(replacement, d); + Ext.removeNode(d); + } + Ext.Array.splice(this.elements, index, 1, replacement); + } + return this; + }, + + + clear: function(removeDom) { + var me = this, + els = me.elements, + i = els.length - 1; + + if (removeDom) { + for (; i >= 0; i--) { + Ext.removeNode(els[i]); + } + } + this.elements = []; + }, + + addElements: function(els, root) { + if (!els) { + return this; + } + + if (typeof els == "string") { + els = Ext.dom.Element.selectorFunction(els, root); + } + + var yels = this.elements, + eLen = els.length, + e; + + for (e = 0; e < eLen; e++) { + yels.push(Ext.get(els[e])); + } + + return this; + }, + + + first: function() { + return this.item(0); + }, + + + last: function() { + return this.item(this.getCount() - 1); + }, + + + contains: function(el) { + return this.indexOf(el) != -1; + }, + + + removeElement: function(keys, removeDom) { + keys = [].concat(keys); + + var me = this, + elements = me.elements, + kLen = keys.length, + val, el, k; + + for (k = 0; k < kLen; k++) { + val = keys[k]; + + if ((el = (elements[val] || elements[val = me.indexOf(val)]))) { + if (removeDom) { + if (el.dom) { + el.remove(); + } else { + Ext.removeNode(el); + } + } + Ext.Array.erase(elements, val, 1); + } + } + + return me; + } + +}, function() { + this.importElementMethods(); + + this.prototype.on = this.prototype.addListener; + + if (Ext.DomQuery){ + Ext.dom.Element.selectorFunction = Ext.DomQuery.select; + } + + + Ext.dom.Element.select = function(selector, root) { + var elements; + + if (typeof selector == "string") { + elements = Ext.dom.Element.selectorFunction(selector, root); + } + else if (selector.length !== undefined) { + elements = selector; + } + else { + throw new Error("[Ext.select] Invalid selector specified: " + selector); + } + + return new Ext.CompositeElementLite(elements); + }; + + + Ext.select = function() { + return Ext.dom.Element.select.apply(Ext.dom.Element, arguments); + }; +}); + + + + +Ext.define('Ext.dom.CompositeElement', { + alternateClassName: 'Ext.CompositeElement', + + extend: Ext.dom.CompositeElementLite , + + + getElement: function(el) { + + return el; + }, + + + transformElement: function(el) { + return Ext.get(el); + } + +}, function() { + + + Ext.dom.Element.select = function(selector, unique, root) { + var elements; + + if (typeof selector == "string") { + elements = Ext.dom.Element.selectorFunction(selector, root); + } + else if (selector.length !== undefined) { + elements = selector; + } + else { + throw new Error("[Ext.select] Invalid selector specified: " + selector); + } + return (unique === true) ? new Ext.CompositeElement(elements) : new Ext.CompositeElementLite(elements); + }; +}); + + +Ext.select = Ext.Element.select; + + + +Ext.define('Ext.util.HashMap', { + mixins: { + observable: Ext.util.Observable + }, + + + generation: 0, + + + + + constructor: function(config) { + config = config || {}; + + var me = this, + keyFn = config.keyFn; + + me.initialConfig = config; + me.addEvents( + + 'add', + + 'clear', + + 'remove', + + 'replace' + ); + + me.mixins.observable.constructor.call(me, config); + me.clear(true); + + if (keyFn) { + me.getKey = keyFn; + } + }, + + + getCount: function() { + return this.length; + }, + + + getData: function(key, value) { + + if (value === undefined) { + value = key; + key = this.getKey(value); + } + + return [key, value]; + }, + + + getKey: function(o) { + return o.id; + }, + + + add: function(key, value) { + var me = this; + + + + if (arguments.length === 1) { + value = key; + key = me.getKey(value); + } + + if (me.containsKey(key)) { + return me.replace(key, value); + } + + me.map[key] = value; + ++me.length; + me.generation++; + if (me.hasListeners.add) { + me.fireEvent('add', me, key, value); + } + return value; + }, + + + replace: function(key, value) { + var me = this, + map = me.map, + old; + + + + if (arguments.length === 1) { + value = key; + key = me.getKey(value); + } + + if (!me.containsKey(key)) { + me.add(key, value); + } + old = map[key]; + map[key] = value; + me.generation++; + if (me.hasListeners.replace) { + me.fireEvent('replace', me, key, value, old); + } + return value; + }, + + + remove: function(o) { + var key = this.findKey(o); + if (key !== undefined) { + return this.removeAtKey(key); + } + return false; + }, + + + removeAtKey: function(key) { + var me = this, + value; + + if (me.containsKey(key)) { + value = me.map[key]; + delete me.map[key]; + --me.length; + me.generation++; + if (me.hasListeners.remove) { + me.fireEvent('remove', me, key, value); + } + return true; + } + return false; + }, + + + get: function(key) { + var map = this.map; + return map.hasOwnProperty(key) ? map[key] : undefined; + }, + + + + + + clear: function( initial) { + var me = this; + + + if (initial || me.generation) { + me.map = {}; + me.length = 0; + me.generation = initial ? 0 : me.generation + 1; + } + if (initial !== true && me.hasListeners.clear) { + me.fireEvent('clear', me); + } + return me; + }, + + + containsKey: function(key) { + var map = this.map; + return map.hasOwnProperty(key) && map[key] !== undefined; + }, + + + contains: function(value) { + return this.containsKey(this.findKey(value)); + }, + + + getKeys: function() { + return this.getArray(true); + }, + + + getValues: function() { + return this.getArray(false); + }, + + + getArray: function(isKey) { + var arr = [], + key, + map = this.map; + for (key in map) { + if (map.hasOwnProperty(key)) { + arr.push(isKey ? key: map[key]); + } + } + return arr; + }, + + + each: function(fn, scope) { + + var items = Ext.apply({}, this.map), + key, + length = this.length; + + scope = scope || this; + for (key in items) { + if (items.hasOwnProperty(key)) { + if (fn.call(scope, key, items[key], length) === false) { + break; + } + } + } + return this; + }, + + + clone: function() { + var hash = new this.self(this.initialConfig), + map = this.map, + key; + + hash.suspendEvents(); + for (key in map) { + if (map.hasOwnProperty(key)) { + hash.add(key, map[key]); + } + } + hash.resumeEvents(); + return hash; + }, + + + findKey: function(value) { + var key, + map = this.map; + + for (key in map) { + if (map.hasOwnProperty(key) && map[key] === value) { + return key; + } + } + return undefined; + } +}); + + + +Ext.define('Ext.AbstractManager', { + + + + + + + + typeName: 'type', + + constructor: function(config) { + Ext.apply(this, config || {}); + + + this.all = new Ext.util.HashMap(); + + this.types = {}; + }, + + + get : function(id) { + return this.all.get(id); + }, + + + register: function(item) { + var all = this.all, + key = all.getKey(item); + + if (all.containsKey(key)) { + Ext.Error.raise('Registering duplicate id "' + key + '" with this manager'); + } + this.all.add(item); + }, + + + unregister: function(item) { + var all = this.all; + all.removeAtKey(all.getKey(item)); + }, + + + registerType : function(type, cls) { + this.types[type] = cls; + cls[this.typeName] = type; + }, + + + isRegistered : function(type){ + return this.types[type] !== undefined; + }, + + + create: function(config, defaultType) { + var type = config[this.typeName] || config.type || defaultType, + Constructor = this.types[type]; + + if (Constructor === undefined) { + Ext.Error.raise("The '" + type + "' type has not been registered with this manager"); + } + + return new Constructor(config); + }, + + + onAvailable : function(id, fn, scope){ + var all = this.all, + item, + callback; + + if (all.containsKey(id)) { + item = all.get(id); + fn.call(scope || item, item); + } else { + callback = function(map, key, item){ + if (key == id) { + fn.call(scope || item, item); + all.un('add', callback); + } + }; + all.on('add', callback); + } + }, + + + each: function(fn, scope){ + this.all.each(fn, scope || this); + }, + + + getCount: function(){ + return this.all.getCount(); + } +}); + + + +Ext.define('Ext.ComponentManager', { + extend: Ext.AbstractManager , + alternateClassName: 'Ext.ComponentMgr', + + singleton: true, + + typeName: 'xtype', + + + create: function(component, defaultType){ + if (typeof component == 'string') { + return Ext.widget(component); + } + if (component.isComponent) { + return component; + } + return Ext.widget(component.xtype || defaultType, component); + }, + + registerType: function(type, cls) { + this.types[type] = cls; + cls[this.typeName] = type; + cls.prototype[this.typeName] = type; + } +}, +function () { + + Ext.getCmp = function(id) { + return Ext.ComponentManager.get(id); + }; +}); + + + +Ext.define('Ext.util.LruCache', { + extend: Ext.util.HashMap , + + + + constructor: function(config) { + Ext.apply(this, config); + this.callParent([config]); + }, + + + add: function(key, newValue) { + var me = this, + entry, last; + + me.removeAtKey(key); + last = me.last; + entry = { + prev: last, + next: null, + key: key, + value: newValue + }; + + + if (last) { + + last.next = entry; + } else { + + me.first = entry; + } + me.last = entry; + me.callParent([key, entry]); + me.prune(); + return newValue; + }, + + + insertBefore: function(key, newValue, sibling) { + var me = this, + existingKey, + entry; + + + + if (sibling = this.map[this.findKey(sibling)]) { + existingKey = me.findKey(newValue); + + + if (existingKey) { + me.unlinkEntry(entry = me.map[existingKey]); + } + + else { + entry = { + prev: sibling.prev, + next: sibling, + key: key, + value: newValue + }; + } + + if (sibling.prev) { + entry.prev.next = entry; + } else { + me.first = entry; + } + entry.next = sibling; + sibling.prev = entry; + me.prune(); + return newValue; + } + + else { + return me.add(key, newValue); + } + }, + + + get: function(key) { + var entry = this.map[key]; + if (entry) { + + + if (entry.next) { + this.moveToEnd(entry); + } + return entry.value; + } + }, + + + removeAtKey: function(key) { + this.unlinkEntry(this.map[key]); + return this.callParent(arguments); + }, + + + clear: function( initial) { + this.first = this.last = null; + return this.callParent(arguments); + }, + + + unlinkEntry: function(entry) { + + if (entry) { + if (entry.next) { + entry.next.prev = entry.prev; + } else { + this.last = entry.prev; + } + if (entry.prev) { + entry.prev.next = entry.next; + } else { + this.first = entry.next; + } + entry.prev = entry.next = null; + } + }, + + + moveToEnd: function(entry) { + this.unlinkEntry(entry); + + + + if (entry.prev = this.last) { + this.last.next = entry; + } + + else { + this.first = entry; + } + this.last = entry; + }, + + + getArray: function(isKey) { + var arr = [], + entry = this.first; + + while (entry) { + arr.push(isKey ? entry.key: entry.value); + entry = entry.next; + } + return arr; + }, + + + each: function(fn, scope, reverse) { + var me = this, + entry = reverse ? me.last : me.first, + length = me.length; + + scope = scope || me; + while (entry) { + if (fn.call(scope, entry.key, entry.value, length) === false) { + break; + } + entry = reverse ? entry.prev : entry.next; + } + return me; + }, + + + findKey: function(value) { + var key, + map = this.map; + + for (key in map) { + + + if (map.hasOwnProperty(key) && map[key].value === value) { + return key; + } + } + return undefined; + }, + + + clone: function() { + var newCache = new this.self(this.initialConfig), + map = this.map, + key; + + newCache.suspendEvents(); + for (key in map) { + if (map.hasOwnProperty(key)) { + newCache.add(key, map[key].value); + } + } + newCache.resumeEvents(); + return newCache; + }, + + + prune: function() { + var me = this, + purgeCount = me.maxSize ? (me.length - me.maxSize) : 0; + + if (purgeCount > 0) { + for (; me.first && purgeCount; purgeCount--) { + me.removeAtKey(me.first.key); + } + } + } + + + + + +}); + + + +Ext.define('Ext.ComponentQuery', { + singleton: true + + + + + +}, function() { + + var cq = this, + domQueryOperators = Ext.dom.Query.operators, + nthRe = /(\d*)n\+?(\d*)/, + nthRe2 = /\D/, + stripLeadingSpaceRe = /^(\s)+/, + unescapeRe = /\\(.)/g, + regexCache = new Ext.util.LruCache({ + maxSize: 100 + }), + + + + filterFnPattern = [ + '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(''), + + filterItems = function(items, operation) { + + + + return operation.method.apply(this, [ items ].concat(operation.args)); + }, + + getItems = function(items, mode) { + var result = [], + i = 0, + length = items.length, + candidate, + deep = mode !== '>'; + + for (; i < length; i++) { + candidate = items[i]; + if (candidate.getRefItems) { + result = result.concat(candidate.getRefItems(deep)); + } + } + return result; + }, + + getAncestors = function(items) { + var result = [], + i = 0, + length = items.length, + candidate; + for (; i < length; i++) { + candidate = items[i]; + while (!!(candidate = candidate.getRefOwner())) { + result.push(candidate); + } + } + return result; + }, + + + filterByXType = function(items, xtype, shallow) { + if (xtype === '*') { + return items.slice(); + } + else { + var result = [], + i = 0, + length = items.length, + candidate; + for (; i < length; i++) { + candidate = items[i]; + if (candidate.isXType(xtype, shallow)) { + result.push(candidate); + } + } + return result; + } + }, + + + filterByAttribute = function(items, property, operator, compareTo) { + var result = [], + i = 0, + length = items.length, + mustBeOwnProperty, + presenceOnly, + candidate, propValue, + j, propLen; + + + if (property.charAt(0) === '@') { + mustBeOwnProperty = true; + property = property.substr(1); + } + if (property.charAt(0) === '?') { + mustBeOwnProperty = true; + presenceOnly = true; + property = property.substr(1); + } + + for (; i < length; i++) { + candidate = items[i]; + + + if (!mustBeOwnProperty || candidate.hasOwnProperty(property)) { + + + propValue = candidate[property]; + + if (presenceOnly) { + result.push(candidate); + } + + else if (operator === '~=') { + if (propValue) { + + if (!Ext.isArray(propValue)) { + propValue = propValue.split(' '); + } + + for (j = 0, propLen = propValue.length; j < propLen; j++) { + if (domQueryOperators[operator](Ext.coerce(propValue[j], compareTo), compareTo)) { + result.push(candidate); + break; + } + } + } + } + else if (operator === '/=') { + if (candidate[property] !== undefined && compareTo.test(candidate[property])) { + result.push(candidate); + } + } else if (!compareTo ? !!candidate[property] : domQueryOperators[operator](Ext.coerce(propValue, compareTo), compareTo)) { + result.push(candidate); + } + } + } + return result; + }, + + + filterById = function(items, id) { + var result = [], + i = 0, + length = items.length, + candidate; + for (; i < length; i++) { + candidate = items[i]; + if (candidate.getItemId() === id) { + result.push(candidate); + } + } + return result; + }, + + + filterByPseudo = function(items, name, value) { + return cq.pseudos[name](items, value); + }, + + + + modeRe = /^(\s?([>\^])\s?|\s|$)/, + + + tokenRe = /^(#)?((?:\\\.|[\w\-])+|\*)(?:\((true|false)\))?/, + + matchers = [{ + + re: /^\.((?:\\\.|[\w\-])+)(?:\((true|false)\))?/, + method: filterByXType, + argTransform: function(args) { + var selector = args[0]; + Ext.log.warn('"'+ selector +'" ComponentQuery selector style is deprecated,' + + ' use "' + selector.replace(/^\./, '') + '" without the leading dot instead'); + + if (args[1] !== undefined) { + args[1] = args[1].replace(unescapeRe, '$1'); + } + + return args.slice(1); + } + }, { + + + + + + + + + re: /^(?:\[((?:[@?$])?[\w\-]*)\s*(?:([\^$*~%!\/]?=)\s*(['"])?((?:\\\]|.)*?)\3)?(?!\\)\])/, + method: filterByAttribute, + argTransform: function(args) { + var selector = args[0], + property = args[1], + operator = args[2], + quote = args[3], + compareTo = args[4], + compareRe; + + // Unescape the attribute value matcher first + if (compareTo !== undefined) { + compareTo = compareTo.replace(unescapeRe, '$1'); + + var format = Ext.String.format, + msg = "ComponentQuery selector '{0}' has an unescaped ({1}) character at the {2} " + + "of the attribute value pattern. Usually that indicates an error " + + "where the opening quote is not followed by the closing quote. " + + "If you need to match a ({1}) character at the {2} of the attribute " + + "value, escape the quote character in your pattern: (\\{1})", + match; + + if (match = /^(['"]).*?[^'"]$/.exec(compareTo)) { + Ext.log.warn(format(msg, selector, match[1], 'beginning')); + } + else if (match = /^[^'"].*?(['"])$/.exec(compareTo)) { + Ext.log.warn(format(msg, selector, match[1], 'end')); + } + } + + if (operator === '/=') { + compareRe = regexCache.get(compareTo); + if (compareRe) { + compareTo = compareRe; + } else { + compareTo = regexCache.add(compareTo, new RegExp(compareTo)); + } + } + + return [property, operator, compareTo]; + } + }, { + + re: /^#((?:\\\.|[\w\-])+)/, + method: filterById + }, { + + re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/, + method: filterByPseudo, + argTransform: function(args) { + if (args[2] !== undefined) { + args[2] = args[2].replace(unescapeRe, '$1'); + } + + return args.slice(1); + } + }, { + + re: /^(?:\{([^\}]+)\})/, + method: filterFnPattern + }]; + + + cq.Query = Ext.extend(Object, { + constructor: function(cfg) { + cfg = cfg || {}; + Ext.apply(this, cfg); + }, + + + + + + + + + + execute: function(root) { + var operations = this.operations, + result = [], + op, i, len; + + for (i = 0, len = operations.length; i < len; i++) { + op = operations[i]; + + result = result.concat(this._execute(root, op)); + } + + return result; + }, + + _execute: function(root, operations) { + var i = 0, + length = operations.length, + operation, + workingItems; + + + if (!root) { + workingItems = Ext.ComponentManager.all.getArray(); + } + + else if (Ext.isIterable(root)) { + workingItems = root; + } + + else if (root.isMixedCollection) { + workingItems = root.items; + } + + + + for (; i < length; i++) { + operation = operations[i]; + + + + + + + + if (operation.mode === '^') { + workingItems = getAncestors(workingItems || [root]); + } + else if (operation.mode) { + workingItems = getItems(workingItems || [root], operation.mode); + } + else { + workingItems = filterItems(workingItems || getItems([root]), operation); + } + + + + if (i === length -1) { + return workingItems; + } + } + return []; + }, + + is: function(component) { + var operations = this.operations, + result = false, + len = operations.length, + op, i; + + if (len === 0) { + return true; + } + + for (i = 0; i < len; i++) { + op = operations[i]; + + result = this._is(component, op); + + if (result) { + return result; + } + } + + return false; + }, + + _is: function(component, operations) { + var len = operations.length, + active = [component], + operation, i, j, mode, matches, items, item; + + + for (i = len - 1; i >= 0; --i) { + operation = operations[i]; + mode = operation.mode; + + if (mode) { + if (mode === '^') { + active = getItems(active, ' '); + } else if (mode === '>') { + items = []; + for (j = 0, len = active.length; j < len; ++j) { + item = active[j].getRefOwner(); + if (item) { + items.push(item); + } + } + active = items; + } else { + active = getAncestors(active); + } + + + if (active.length === 0) { + return false; + } + + } else { + active = filterItems(active, operation); + if (active.length === 0) { + return false; + } + } + } + return true; + }, + + getMatches: function(components, operations) { + var len = operations.length, + i; + + for (i = 0; i < len; ++i) { + components = filterItems(components, operations[i]); + + + if (components.length === 0) { + break; + } + } + return components; + }, + + isMultiMatch: function() { + return this.operations.length > 1; + } + }); + + Ext.apply(this, { + + + cache: new Ext.util.LruCache({ + maxSize: 100 + }), + + + pseudos: { + not: function(components, selector){ + var CQ = Ext.ComponentQuery, + i = 0, + length = components.length, + results = [], + index = -1, + component; + + for(; i < length; ++i) { + component = components[i]; + if (!CQ.is(component, selector)) { + results[++index] = component; + } + } + return results; + }, + first: function(components) { + var ret = []; + + if (components.length > 0) { + ret.push(components[0]); + } + return ret; + }, + last: function(components) { + var len = components.length, + ret = []; + + if (len > 0) { + ret.push(components[len - 1]); + } + return ret; + }, + focusable: function(cmps) { + var len = cmps.length, + results = [], + i = 0, + c; + + for (; i < len; i++) { + c = cmps[i]; + + + if (c.isFocusable()) { + results.push(c); + } + } + + return results; + }, + "nth-child" : function(c, a) { + var result = [], + 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, nodeIndex; + for (i = 0; n = c[i]; i++) { + nodeIndex = i + 1; + if (f == 1) { + if (l == 0 || nodeIndex == l) { + result.push(n); + } + } else if ((nodeIndex + l) % f == 0){ + result.push(n); + } + } + + return result; + } + }, + + + query: function(selector, root) { + + if (!selector) { + return Ext.ComponentManager.all.getArray(); + } + + var results = [], + noDupResults = [], + dupMatcher = {}, + query = this.cache.get(selector), + resultsLn, cmp, i; + + if (!query) { + query = this.cache.add(selector, this.parse(selector)); + } + + results = query.execute(root); + + + + if (query.isMultiMatch()) { + resultsLn = results.length; + for (i = 0; i < resultsLn; i++) { + cmp = results[i]; + if (!dupMatcher[cmp.id]) { + noDupResults.push(cmp); + dupMatcher[cmp.id] = true; + } + } + results = noDupResults; + } + return results; + }, + + + visitPreOrder: function(selector, root, fn, scope, extraArgs) { + this._visit(true, selector, root, fn, scope, extraArgs); + }, + + + visitPostOrder: function(selector, root, fn, scope, extraArgs) { + this._visit(false, selector, root, fn, scope, extraArgs); + }, + + + + _visit: function(preOrder, selector, root, fn, scope, extraArgs) { + var me = this, + query = me.cache.get(selector), + callArgs = [root], + children, + len = 0, + i, rootMatch; + + if (!query) { + query = me.cache.add(selector, me.parse(selector)); + } + + rootMatch = query.is(root); + + if (root.getRefItems) { + children = root.getRefItems(); + len = children.length; + } + + + if (extraArgs) { + Ext.Array.push(callArgs, extraArgs); + } + if (preOrder) { + if (rootMatch) { + if (fn.apply(scope || root, callArgs) === false) { + return false; + } + } + } + for (i = 0; i < len; i++) { + if (me._visit.call(me, preOrder, selector, children[i], fn, scope, extraArgs) === false) { + return false; + } + } + if (!preOrder) { + if (rootMatch) { + if (fn.apply(scope || root, callArgs) === false) { + return false; + } + } + } + }, + + + is: function(component, selector) { + if (!selector) { + return true; + } + + var query = this.cache.get(selector); + if (!query) { + query = this.cache.add(selector, this.parse(selector)); + } + + return query.is(component); + }, + + parse: function(selector) { + var operations = [], + selectors, sel, i, len; + + selectors = Ext.splitAndUnescape(selector, ','); + + for (i = 0, len = selectors.length; i < len; i++) { + + sel = Ext.String.trim(selectors[i]); + + + + + if (sel === '') { + Ext.Error.raise('Invalid ComponentQuery selector: ""'); + } + + operations.push(this._parse(sel)); + } + + + + return new cq.Query({ + operations: operations + }); + }, + + _parse: function(selector) { + var operations = [], + trim = Ext.String.trim, + length = matchers.length, + lastSelector, + tokenMatch, + token, + matchedChar, + modeMatch, + selectorMatch, + transform, + i, matcher, method, args; + + + + + while (selector && lastSelector !== selector) { + lastSelector = selector; + + + tokenMatch = selector.match(tokenRe); + + if (tokenMatch) { + matchedChar = tokenMatch[1]; + token = trim(tokenMatch[2]).replace(unescapeRe, '$1'); + + + if (matchedChar === '#') { + operations.push({ + method: filterById, + args: [token] + }); + } + + + else { + operations.push({ + method: filterByXType, + args: [token, Boolean(tokenMatch[3])] + }); + } + + + selector = selector.replace(tokenMatch[0], '').replace(stripLeadingSpaceRe, '$1'); + } + + + + + while (!(modeMatch = selector.match(modeRe))) { + + + for (i = 0; selector && i < length; i++) { + matcher = matchers[i]; + selectorMatch = selector.match(matcher.re); + method = matcher.method; + transform = matcher.argTransform; + + + + + if (selectorMatch) { + + if (transform) { + args = transform(selectorMatch); + } + else { + args = selectorMatch.slice(1); + } + + operations.push({ + method: Ext.isString(matcher.method) + + + + ? Ext.functionFactory('items', Ext.String.format.apply(Ext.String, [method].concat(selectorMatch.slice(1)))) + : matcher.method, + args: args + }); + + selector = selector.replace(selectorMatch[0], '').replace(stripLeadingSpaceRe, '$1'); + break; + } + + if (i === (length - 1)) { + Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"'); + } + } + } + + + + + + if (modeMatch[1]) { + operations.push({ + mode: modeMatch[2]||modeMatch[1] + }); + + + selector = selector.replace(modeMatch[0], '').replace(stripLeadingSpaceRe, ''); + } + } + + return operations; + } + }); +}); + + + + + +Ext.define('Ext.util.ProtoElement', (function () { + var splitWords = Ext.String.splitWords, + toMap = Ext.Array.toMap; + + return { + + isProtoEl: true, + + + clsProp: 'cls', + + + styleProp: 'style', + + + removedProp: 'removed', + + + styleIsText: false, + + constructor: function (config) { + var me = this; + + Ext.apply(me, config); + + me.classList = splitWords(me.cls); + me.classMap = toMap(me.classList); + delete me.cls; + + if (Ext.isFunction(me.style)) { + me.styleFn = me.style; + delete me.style; + } else if (typeof me.style == 'string') { + me.style = Ext.Element.parseStyles(me.style); + } else if (me.style) { + me.style = Ext.apply({}, me.style); + } + }, + + + flush: function(){ + this.flushClassList = []; + this.removedClasses = {}; + + delete this.style; + delete this.unselectableAttr; + }, + + + addCls: function (cls) { + var me = this, + add = (typeof cls === 'string') ? splitWords(cls) : cls, + length = add.length, + list = me.classList, + map = me.classMap, + flushList = me.flushClassList, + i = 0, + c; + + for (; i < length; ++i) { + c = add[i]; + if (!map[c]) { + map[c] = true; + list.push(c); + if (flushList) { + flushList.push(c); + delete me.removedClasses[c]; + } + } + } + + return me; + }, + + + hasCls: function (cls) { + return cls in this.classMap; + }, + + + removeCls: function (cls) { + var me = this, + list = me.classList, + newList = (me.classList = []), + remove = toMap(splitWords(cls)), + length = list.length, + map = me.classMap, + removedClasses = me.removedClasses, + i, c; + + for (i = 0; i < length; ++i) { + c = list[i]; + if (remove[c]) { + if (removedClasses) { + if (map[c]) { + removedClasses[c] = true; + Ext.Array.remove(me.flushClassList, c); + } + } + delete map[c]; + } else { + newList.push(c); + } + } + + return me; + }, + + + setStyle: function (prop, value) { + var me = this, + style = me.style || (me.style = {}); + + if (typeof prop == 'string') { + if (arguments.length === 1) { + me.setStyle(Ext.Element.parseStyles(prop)); + } else { + style[prop] = value; + } + } else { + Ext.apply(style, prop); + } + + return me; + }, + + unselectable: function() { + + this.addCls(Ext.dom.Element.unselectableCls); + + if (Ext.isOpera) { + this.unselectableAttr = true; + } + }, + + + writeTo: function (to) { + var me = this, + classList = me.flushClassList || me.classList, + removedClasses = me.removedClasses, + style; + + if (me.styleFn) { + style = Ext.apply({}, me.styleFn()); + Ext.apply(style, me.style); + } else { + style = me.style; + } + + to[me.clsProp] = classList.join(' '); + + if (style) { + to[me.styleProp] = me.styleIsText ? Ext.DomHelper.generateStyles(style, null, true) : style; + } + + if (removedClasses) { + removedClasses = Ext.Object.getKeys(removedClasses); + if (removedClasses.length) { + to[me.removedProp] = removedClasses.join(' '); + } + } + + if (me.unselectableAttr) { + to.unselectable = 'on'; + } + + return to; + } + }; +}())); + + + +Ext.define('Ext.PluginManager', { + extend: Ext.AbstractManager , + alternateClassName: 'Ext.PluginMgr', + singleton: true, + typeName: 'ptype', + + + create : function(config, defaultType, host) { + var result; + + if (config.init) { + result = config; + } else { + + if (host) { + config = Ext.apply({}, config); + config.cmp = host; + } + + else { + host = config.cmp; + } + + if (config.xclass) { + result = Ext.create(config); + } else { + + result = Ext.ClassManager.getByAlias(('plugin.' + (config.ptype || defaultType))); + + if (typeof result === 'function') { + result = new result(config); + } + } + } + + + if (result && host && result.setCmp && !result.setCmpCalled) { + result.setCmp(host); + result.setCmpCalled = true; + } + return result; + }, + + + findByType: function(type, defaultsOnly) { + var matches = [], + types = this.types, + name, + item; + + for (name in types) { + if (!types.hasOwnProperty(name)) { + continue; + } + item = types[name]; + + if (item.type == type && (!defaultsOnly || (defaultsOnly === true && item.isDefault))) { + matches.push(item); + } + } + + return matches; + } +}, function() { + + Ext.preg = function() { + return Ext.PluginManager.registerType.apply(Ext.PluginManager, arguments); + }; +}); + + + +Ext.define('Ext.util.Filter', { + + + + + + + + id: null, + + + anyMatch: false, + + + exactMatch: false, + + + caseSensitive: false, + + + disabled: false, + + + operator: null, + + + + statics: { + + createFilterFn: function(filters) { + return filters && filters.length ? function(candidate) { + var isMatch = true, + length = filters.length, + i, filter; + + for (i = 0; isMatch && i < length; i++) { + filter = filters[i]; + + + if (!filter.disabled) { + isMatch = isMatch && filter.filterFn.call(filter.scope || filter, candidate); + } + } + return isMatch; + } : function() { + return true; + }; + } + }, + + operatorFns: { + "<": function(candidate) { + return Ext.coerce(this.getRoot(candidate)[this.property], this.value) < this.value; + }, + "<=": function(candidate) { + return Ext.coerce(this.getRoot(candidate)[this.property], this.value) <= this.value; + }, + "=": function(candidate) { + return Ext.coerce(this.getRoot(candidate)[this.property], this.value) == this.value; + }, + ">=": function(candidate) { + return Ext.coerce(this.getRoot(candidate)[this.property], this.value) >= this.value; + }, + ">": function(candidate) { + return Ext.coerce(this.getRoot(candidate)[this.property], this.value) > this.value; + }, + "!=": function(candidate) { + return Ext.coerce(this.getRoot(candidate)[this.property], this.value) != this.value; + } + }, + + + constructor: function(config) { + var me = this; + me.initialConfig = config; + Ext.apply(me, config); + + + + me.filter = me.filter || me.filterFn; + + if (me.filter === undefined) { + me.setValue(config.value); + } + }, + + + setValue: function(value) { + var me = this; + me.value = value; + if (me.property === undefined || me.value === undefined) { + + + + + } else { + me.filter = me.createFilterFn(); + } + + me.filterFn = me.filter; + }, + + + setFilterFn: function(filterFn) { + this.filterFn = this.filter = filterFn; + }, + + + createFilterFn: function() { + var me = this, + matcher = me.createValueMatcher(), + property = me.property; + + if (me.operator) { + return me.operatorFns[me.operator]; + } else { + return function(item) { + var value = me.getRoot(item)[property]; + return matcher === null ? value === null : matcher.test(value); + }; + } + }, + + + getRoot: function(item) { + var root = this.root; + return root === undefined ? item : item[root]; + }, + + + createValueMatcher : function() { + var me = this, + value = me.value, + anyMatch = me.anyMatch, + exactMatch = me.exactMatch, + caseSensitive = me.caseSensitive, + escapeRe = Ext.String.escapeRegex; + + if (value === null) { + return value; + } + + if (!value.exec) { + value = String(value); + + if (anyMatch === true) { + value = escapeRe(value); + } else { + value = '^' + escapeRe(value); + if (exactMatch === true) { + value += '$'; + } + } + value = new RegExp(value, caseSensitive ? '' : 'i'); + } + + return value; + }, + + serialize: function() { + var me = this, + result = Ext.apply({}, me.initialConfig); + + result.value = me.value; + return result; + } +}, function() { + + this.prototype.operatorFns['=='] = this.prototype.operatorFns['=']; +}); + + + +Ext.define('Ext.util.AbstractMixedCollection', { + + + mixins: { + observable: Ext.util.Observable + }, + + + isMixedCollection: true, + + + generation: 0, + + + indexGeneration: 0, + + constructor: function(allowFunctions, keyFn) { + var me = this; + + + if (arguments.length === 1 && Ext.isObject(allowFunctions)) { + me.initialConfig = allowFunctions; + Ext.apply(me, allowFunctions); + } + + else { + me.allowFunctions = allowFunctions === true; + if (keyFn) { + me.getKey = keyFn; + } + me.initialConfig = { + allowFunctions: me.allowFunctions, + getKey: me.getKey + }; + } + + me.items = []; + me.map = {}; + me.keys = []; + me.indexMap = {}; + me.length = 0; + + + + + + + + + + me.mixins.observable.constructor.call(me); + }, + + + allowFunctions : false, + + + add : function(key, obj) { + var len = this.length, + out; + + if (arguments.length === 1) { + out = this.insert(len, key); + } else { + out = this.insert(len, key, obj); + } + return out; + }, + + + getKey : function(o) { + return o.id; + }, + + + replace : function(key, o) { + var me = this, + old, + index; + + if (arguments.length == 1) { + o = arguments[0]; + key = me.getKey(o); + } + old = me.map[key]; + if (typeof key == 'undefined' || key === null || typeof old == 'undefined') { + return me.add(key, o); + } + me.generation++; + index = me.indexOfKey(key); + me.items[index] = o; + me.map[key] = o; + if (me.hasListeners.replace) { + me.fireEvent('replace', key, old, o); + } + return o; + }, + + + updateKey: function(oldKey, newKey) { + var me = this, + map = me.map, + index = me.indexOfKey(oldKey), + + indexMap = me.indexMap, + item; + + if (index > -1) { + item = map[oldKey]; + delete map[oldKey]; + delete indexMap[oldKey]; + map[newKey] = item; + indexMap[newKey] = index; + me.keys[index] = newKey; + + + + me.indexGeneration = ++me.generation; + } + }, + + + addAll : function(objs) { + var me = this, + key; + + if (arguments.length > 1 || Ext.isArray(objs)) { + me.insert(me.length, arguments.length > 1 ? arguments : objs); + } else { + for (key in objs) { + if (objs.hasOwnProperty(key)) { + if (me.allowFunctions || typeof objs[key] != 'function') { + me.add(key, objs[key]); + } + } + } + } + }, + + + each : function(fn, scope){ + var items = Ext.Array.push([], this.items), + i = 0, + len = items.length, + item; + + for (; i < len; i++) { + item = items[i]; + if (fn.call(scope || item, item, i, len) === false) { + break; + } + } + }, + + + eachKey : function(fn, scope){ + var keys = this.keys, + items = this.items, + i = 0, + len = keys.length; + + for (; i < len; i++) { + fn.call(scope || window, keys[i], items[i], i, len); + } + }, + + + findBy : function(fn, scope) { + var keys = this.keys, + items = this.items, + i = 0, + len = items.length; + + for (; i < len; i++) { + if (fn.call(scope || window, items[i], keys[i])) { + return items[i]; + } + } + return null; + }, + + + + insert : function(index, key, obj) { + var out; + if (Ext.isIterable(key)) { + out = this.doInsert(index, key, obj); + } else { + if (arguments.length > 2) { + out = this.doInsert(index, [key], [obj]); + } else { + out = this.doInsert(index, [key]); + } + out = out[0]; + } + return out; + }, + + + doInsert : function(index, keys, objects) { + var me = this, + itemKey, + removeIndex, + i, len = keys.length, + deDupedLen = len, + fireAdd = me.hasListeners.add, + syncIndices, + newKeys = {}, + passedDuplicates, + oldKeys, oldObjects; + + + + if (objects != null) { + me.useLinearSearch = true; + } + + else { + objects = keys; + keys = new Array(len); + for (i = 0; i < len; i++) { + keys[i] = this.getKey(objects[i]); + } + } + + + me.suspendEvents(); + for (i = 0; i < len; i++) { + itemKey = keys[i]; + + + removeIndex = me.indexOfKey(itemKey); + if (removeIndex !== -1) { + if (removeIndex < index) { + index--; + } + me.removeAt(removeIndex); + } + + if (itemKey != null) { + + if (newKeys[itemKey] != null) { + passedDuplicates = true; + deDupedLen--; + } + newKeys[itemKey] = i; + } + } + me.resumeEvents(); + + + if (passedDuplicates) { + oldKeys = keys; + oldObjects = objects; + keys = new Array(deDupedLen); + objects = new Array(deDupedLen); + i = 0; + + + + for (itemKey in newKeys) { + keys[i] = oldKeys[newKeys[itemKey]]; + objects[i] = oldObjects[newKeys[itemKey]]; + i++; + } + len = deDupedLen; + } + + + syncIndices = index === me.length && me.indexGeneration === me.generation; + + + Ext.Array.insert(me.items, index, objects); + Ext.Array.insert(me.keys, index, keys); + me.length += len; + me.generation++; + if (syncIndices) { + me.indexGeneration = me.generation; + } + for (i = 0; i < len; i++, index++) { + itemKey = keys[i]; + if (itemKey != null) { + me.map[itemKey] = objects[i]; + + + if (syncIndices) { + me.indexMap[itemKey] = index; + } + } + if (fireAdd) { + me.fireEvent('add', index, objects[i], itemKey); + } + } + return objects; + }, + + + remove : function(o) { + var me = this, + removeKey, + index; + + + + + + + if (!me.useLinearSearch && (removeKey = me.getKey(o))) { + index = me.indexOfKey(removeKey); + } + + + else { + index = Ext.Array.indexOf(me.items, o); + } + + return (index === -1) ? false : me.removeAt(index); + }, + + + removeAll : function(items) { + var me = this, + i; + + if (items || me.hasListeners.remove) { + + if (items) { + for (i = items.length - 1; i >= 0; --i) { + me.remove(items[i]); + } + } else { + while (me.length) { + me.removeAt(0); + } + } + } else { + me.length = me.items.length = me.keys.length = 0; + me.map = {}; + me.indexMap = {}; + me.generation++; + me.indexGeneration = me.generation; + } + }, + + + removeAt : function(index) { + var me = this, + o, + key; + + if (index < me.length && index >= 0) { + me.length--; + o = me.items[index]; + Ext.Array.erase(me.items, index, 1); + key = me.keys[index]; + if (typeof key != 'undefined') { + delete me.map[key]; + } + Ext.Array.erase(me.keys, index, 1); + if (me.hasListeners.remove) { + me.fireEvent('remove', o, key); + } + me.generation++; + return o; + } + return false; + }, + + + removeRange : function(index, removeCount) { + var me = this, + o, + key, + i, + limit, + syncIndices, + trimming; + + if (index < me.length && index >= 0) { + if (!removeCount) { + removeCount = 1; + } + limit = Math.min(index + removeCount, me.length); + removeCount = limit - index; + + + trimming = limit === me.length; + syncIndices = trimming && me.indexGeneration === me.generation; + + + for (i = index; i < limit; i++) { + key = me.keys[i]; + if (key != null) { + delete me.map[key]; + if (syncIndices) { + delete me.indexMap[key]; + } + } + } + + o = me.items[i - 1]; + + me.length -= removeCount; + me.generation++; + if (syncIndices) { + me.indexGeneration = me.generation; + } + + + + + + if (trimming) { + me.items.length = me.keys.length = me.length; + } else { + me.items.splice(index, removeCount); + me.keys.splice(index, removeCount); + } + + + return o; + } + return false; + }, + + + removeAtKey : function(key) { + var me = this, + keys = me.keys, + i; + + + if (key == null) { + for (i = keys.length - 1; i >=0; i--) { + if (keys[i] == null) { + me.removeAt(i); + } + } + } + + else { + return me.removeAt(me.indexOfKey(key)); + } + }, + + + getCount : function() { + return this.length; + }, + + + indexOf : function(o) { + var me = this, + key; + + if (o != null) { + + + + + + if (!me.useLinearSearch && (key = me.getKey(o))) { + return this.indexOfKey(key); + } + + + return Ext.Array.indexOf(me.items, o); + } + + + return -1; + }, + + + indexOfKey : function(key) { + if (!this.map.hasOwnProperty(key)) { + return -1; + } + if (this.indexGeneration !== this.generation) { + this.rebuildIndexMap(); + } + return this.indexMap[key]; + }, + + rebuildIndexMap: function() { + var me = this, + indexMap = me.indexMap = {}, + keys = me.keys, + len = keys.length, + i; + + for (i = 0; i < len; i++) { + indexMap[keys[i]] = i; + } + me.indexGeneration = me.generation; + }, + + + get : function(key) { + var me = this, + mk = me.map[key], + item = mk !== undefined ? mk : (typeof key == 'number') ? me.items[key] : undefined; + return typeof item != 'function' || me.allowFunctions ? item : null; + }, + + + getAt : function(index) { + return this.items[index]; + }, + + + getByKey : function(key) { + return this.map[key]; + }, + + + contains : function(o) { + var me = this, + key; + + if (o != null) { + + + + + + if (!me.useLinearSearch && (key = me.getKey(o))) { + return this.map[key] != null; + } + + + return Ext.Array.indexOf(this.items, o) !== -1; + } + + return false; + }, + + + containsKey : function(key) { + return this.map.hasOwnProperty(key); + }, + + + clear : function() { + var me = this; + + + if (me.generation) { + me.length = 0; + me.items = []; + me.keys = []; + me.map = {}; + me.indexMap = {}; + + me.generation++; + me.indexGeneration = me.generation; + } + if (me.hasListeners.clear) { + me.fireEvent('clear'); + } + }, + + + first : function() { + return this.items[0]; + }, + + + last : function() { + return this.items[this.length - 1]; + }, + + + sum: function(property, root, start, end) { + var values = this.extractValues(property, root), + length = values.length, + sum = 0, + i; + + start = start || 0; + end = (end || end === 0) ? end : length - 1; + + for (i = start; i <= end; i++) { + sum += values[i]; + } + + return sum; + }, + + + collect: function(property, root, allowNull) { + var values = this.extractValues(property, root), + length = values.length, + hits = {}, + unique = [], + value, strValue, i; + + for (i = 0; i < length; i++) { + value = values[i]; + strValue = String(value); + + if ((allowNull || !Ext.isEmpty(value)) && !hits[strValue]) { + hits[strValue] = true; + unique.push(value); + } + } + + return unique; + }, + + + extractValues: function(property, root) { + var values = this.items; + + if (root) { + values = Ext.Array.pluck(values, root); + } + + return Ext.Array.pluck(values, property); + }, + + + hasRange: function(start, end) { + return (end < this.length); + }, + + + getRange : function(start, end){ + var me = this, + items = me.items, + range = [], + len = items.length, + tmp, reverse; + + if (len < 1) { + return range; + } + + if (start > end) { + reverse = true; + tmp = start; + start = end; + end = tmp; + } + + if (start < 0) { + start = 0; + } + + if (end == null || end >= len) { + end = len - 1; + } + + range = items.slice(start, end + 1); + if (reverse && range.length) { + range.reverse(); + } + return range; + }, + + + filter : function(property, value, anyMatch, caseSensitive) { + var filters = []; + + + if (Ext.isString(property)) { + filters.push(new Ext.util.Filter({ + property : property, + value : value, + anyMatch : anyMatch, + caseSensitive: caseSensitive + })); + } else if (Ext.isArray(property) || property instanceof Ext.util.Filter) { + filters = filters.concat(property); + } + + + + + return this.filterBy(Ext.util.Filter.createFilterFn(filters)); + }, + + + filterBy : function(fn, scope) { + var me = this, + newMC = new me.self(me.initialConfig), + keys = me.keys, + items = me.items, + length = items.length, + i; + + newMC.getKey = me.getKey; + + for (i = 0; i < length; i++) { + if (fn.call(scope || me, items[i], keys[i])) { + newMC.add(keys[i], items[i]); + } + } + + + + + newMC.useLinearSearch = me.useLinearSearch; + return newMC; + }, + + + findIndex : function(property, value, start, anyMatch, caseSensitive){ + if(Ext.isEmpty(value, false)){ + return -1; + } + value = this.createValueMatcher(value, anyMatch, caseSensitive); + return this.findIndexBy(function(o){ + return o && value.test(o[property]); + }, null, start); + }, + + + findIndexBy : function(fn, scope, start){ + var me = this, + keys = me.keys, + items = me.items, + i = start || 0, + len = items.length; + + for (; i < len; i++) { + if (fn.call(scope || me, items[i], keys[i])) { + return i; + } + } + return -1; + }, + + + createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) { + if (!value.exec) { + var er = Ext.String.escapeRegex; + value = String(value); + + if (anyMatch === true) { + value = er(value); + } else { + value = '^' + er(value); + if (exactMatch === true) { + value += '$'; + } + } + value = new RegExp(value, caseSensitive ? '' : 'i'); + } + return value; + }, + + + clone : function() { + var me = this, + copy = new me.self(me.initialConfig); + + copy.add(me.keys, me.items); + + + + + copy.useLinearSearch = me.useLinearSearch; + return copy; + } +}); + + + +Ext.define('Ext.util.Sorter', { + + + + + + + + + + + direction: "ASC", + + constructor: function(config) { + var me = this; + + Ext.apply(me, config); + if (me.direction) { + me.direction = me.direction.toUpperCase(); + } + + if (me.property === undefined && me.sorterFn === undefined) { + Ext.Error.raise("A Sorter requires either a property or a sorter function"); + } + + me.updateSortFunction(); + }, + + + createSortFunction: function(sorterFn) { + var me = this, + direction = me.direction || "ASC", + modifier = direction == "DESC" ? -1 : 1; + + + + return function(o1, o2) { + return modifier * sorterFn.call(me, o1, o2); + }; + }, + + + defaultSorterFn: function(o1, o2) { + var me = this, + transform = me.transform, + v1 = me.getRoot(o1)[me.property], + v2 = me.getRoot(o2)[me.property]; + + if (transform) { + v1 = transform(v1); + v2 = transform(v2); + } + + return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0); + }, + + + getRoot: function(item) { + return this.root === undefined ? item : item[this.root]; + }, + + + setDirection: function(direction) { + var me = this; + me.direction = direction ? direction.toUpperCase() : direction; + me.updateSortFunction(); + }, + + + toggle: function() { + var me = this; + me.direction = Ext.String.toggle(me.direction, "ASC", "DESC"); + me.updateSortFunction(); + }, + + + updateSortFunction: function(fn) { + var me = this; + fn = fn || me.sorterFn || me.defaultSorterFn; + me.sort = me.createSortFunction(fn); + }, + + serialize: function() { + return { + root: this.root, + property: this.property, + direction: this.direction + }; + } +}); + + + +Ext.define("Ext.util.Sortable", { + + isSortable: true, + + + defaultSortDirection: "ASC", + + + + + + + + + multiSortLimit: 3, + + statics: { + + createComparator: function(sorters) { + return sorters && sorters.length ? function(r1, r2) { + var result = sorters[0].sort(r1, r2), + length = sorters.length, + i = 1; + + + + for (; !result && i < length; i++) { + result = sorters[i].sort.call(this, r1, r2); + } + return result; + }: function() { + return 0; + }; + } + }, + + + + + + + initSortable: function() { + var me = this, + sorters = me.sorters; + + + me.sorters = new Ext.util.AbstractMixedCollection(false, function(item) { + return item.id || item.property; + }); + + if (sorters) { + me.sorters.addAll(me.decodeSorters(sorters)); + } + }, + + + sort: function(sorters, direction, insertionPosition, doSort) { + var me = this, + sorter, + overFlow; + + if (Ext.isArray(sorters)) { + doSort = insertionPosition; + insertionPosition = direction; + } + else if (Ext.isObject(sorters)) { + sorters = [sorters]; + doSort = insertionPosition; + insertionPosition = direction; + } + else if (Ext.isString(sorters)) { + sorter = me.sorters.get(sorters); + + if (!sorter) { + sorter = { + property : sorters, + direction: direction + }; + } + else if (direction == null) { + sorter.toggle(); + } + else { + sorter.setDirection(direction); + } + sorters = [sorter]; + } + + if (sorters && sorters.length) { + sorters = me.decodeSorters(sorters); + + switch (insertionPosition) { + + + + + case "multi": + + me.sorters.insert(0, sorters[0]); + + + overFlow = me.sorters.getCount() - me.multiSortLimit; + if (overFlow > 0) { + me.sorters.removeRange(me.multiSortLimit, overFlow); + } + break; + case "prepend" : + me.sorters.insert(0, sorters); + break; + case "append" : + me.sorters.addAll(sorters); + break; + case undefined: + case null: + case "replace": + me.sorters.clear(); + me.sorters.addAll(sorters); + break; + default: + Ext.Error.raise('Sorter insertion point must be "multi", "prepend", "append" or "replace"'); + } + } + + if (doSort !== false) { + me.fireEvent('beforesort', me, sorters); + me.onBeforeSort(sorters); + if (me.getSorterCount()) { + + me.doSort(me.generateComparator()); + } + } + + return sorters; + }, + + + getSorterCount: function( ){ + return this.sorters.items.length; + }, + + + generateComparator: function() { + var sorters = this.sorters.getRange(); + return sorters.length ? this.createComparator(sorters) : this.emptyComparator; + }, + + emptyComparator: function(){ + return 0; + }, + + onBeforeSort: Ext.emptyFn, + + + decodeSorters: function(sorters) { + if (!Ext.isArray(sorters)) { + if (sorters === undefined) { + sorters = []; + } else { + sorters = [sorters]; + } + } + + var length = sorters.length, + Sorter = Ext.util.Sorter, + fields = this.model ? this.model.prototype.fields : null, + field, + config, i; + + for (i = 0; i < length; i++) { + config = sorters[i]; + + if (!(config instanceof Sorter)) { + if (Ext.isString(config)) { + config = { + property: config + }; + } + + Ext.applyIf(config, { + root : this.sortRoot, + direction: "ASC" + }); + + + if (config.fn) { + config.sorterFn = config.fn; + } + + + if (typeof config == 'function') { + config = { + sorterFn: config + }; + } + + + if (fields && !config.transform) { + field = fields.get(config.property); + config.transform = field && field.sortType !== Ext.identityFn ? field.sortType : undefined; + } + sorters[i] = new Ext.util.Sorter(config); + } + } + + return sorters; + }, + + getSorters: function() { + return this.sorters.items; + }, + + + getFirstSorter: function(){ + var sorters = this.sorters.items, + len = sorters.length, + i = 0, + sorter; + + for (; i < len; ++i) { + sorter = sorters[i]; + if (!sorter.isGrouper) { + return sorter; + } + } + return null; + } +}, function() { + + this.prototype.createComparator = this.createComparator; +}); + + + +Ext.define('Ext.util.MixedCollection', { + extend: Ext.util.AbstractMixedCollection , + mixins: { + sortable: Ext.util.Sortable + }, + + + + + constructor: function() { + var me = this; + me.callParent(arguments); + me.addEvents('sort'); + me.mixins.sortable.initSortable.call(me); + }, + + doSort: function(sorterFn) { + this.sortBy(sorterFn); + }, + + + _sort : function(property, dir, fn) { + var me = this, + i, len, + dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1, + + + c = [], + keys = me.keys, + items = me.items, + o; + + + fn = fn || function(a, b) { + return a - b; + }; + + + for (i = 0, len = items.length; i < len; i++) { + c[c.length] = { + key : keys[i], + value: items[i], + index: i + }; + } + + + Ext.Array.sort(c, function(a, b) { + return fn(a[property], b[property]) * dsc || + + (a.index < b.index ? -1 : 1); + }); + + + + for (i = 0, len = c.length; i < len; i++) { + o = c[i]; + items[i] = o.value; + keys[i] = o.key; + me.indexMap[o.key] = i; + } + me.generation++; + me.indexGeneration = me.generation; + me.fireEvent('sort', me); + }, + + + sortBy: function(sorterFn) { + var me = this, + items = me.items, + item, + keys = me.keys, + key, + length = items.length, + i; + + + for (i = 0; i < length; i++) { + items[i].$extCollectionIndex = i; + } + + Ext.Array.sort(items, function(a, b) { + return sorterFn(a, b) || + + (a.$extCollectionIndex < b.$extCollectionIndex ? -1 : 1); + }); + + + for (i = 0; i < length; i++) { + item = items[i]; + key = me.getKey(item); + keys[i] = key; + me.indexMap[key] = i; + delete items.$extCollectionIndex; + } + me.generation++; + me.indexGeneration = me.generation; + me.fireEvent('sort', me, items, keys); + }, + + + findInsertionIndex: function(newItem, sorterFn) { + var me = this, + items = me.items, + start = 0, + end = items.length - 1, + middle, + comparison; + + if (!sorterFn) { + sorterFn = me.generateComparator(); + } + while (start <= end) { + middle = (start + end) >> 1; + comparison = sorterFn(newItem, items[middle]); + if (comparison >= 0) { + start = middle + 1; + } else if (comparison < 0) { + end = middle - 1; + } + } + return start; + }, + + + reorder: function(mapping) { + var me = this, + items = me.items, + index = 0, + length = items.length, + order = [], + remaining = [], + oldIndex; + + me.suspendEvents(); + + + for (oldIndex in mapping) { + order[mapping[oldIndex]] = items[oldIndex]; + } + + for (index = 0; index < length; index++) { + if (mapping[index] == undefined) { + remaining.push(items[index]); + } + } + + for (index = 0; index < length; index++) { + if (order[index] == undefined) { + order[index] = remaining.shift(); + } + } + + me.clear(); + me.addAll(order); + + me.resumeEvents(); + me.fireEvent('sort', me); + }, + + + sortByKey : function(dir, fn){ + this._sort('key', dir, fn || function(a, b){ + var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase(); + return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0); + }); + } +}); + + + +Ext.define('Ext.fx.target.Target', { + + isAnimTarget: true, + + + constructor: function(target) { + this.target = target; + this.id = this.getId(); + }, + + getId: function() { + return this.target.id; + }, + + remove: function() { + Ext.destroy(this.target); + } +}); + + + +Ext.define('Ext.fx.target.Element', { + + + + extend: Ext.fx.target.Target , + + + + type: 'element', + + getElVal: function(el, attr, val) { + if (val == undefined) { + if (attr === 'x') { + val = el.getX(); + } else if (attr === 'y') { + val = el.getY(); + } else if (attr === 'scrollTop') { + val = el.getScroll().top; + } else if (attr === 'scrollLeft') { + val = el.getScroll().left; + } else if (attr === 'height') { + val = el.getHeight(); + } else if (attr === 'width') { + val = el.getWidth(); + } else { + val = el.getStyle(attr); + } + } + return val; + }, + + getAttr: function(attr, val) { + var el = this.target; + return [[ el, this.getElVal(el, attr, val)]]; + }, + + setAttr: function(targetData) { + var target = this.target, + ln = targetData.length, + attrs, attr, o, i, j, ln2; + + for (i = 0; i < ln; i++) { + attrs = targetData[i].attrs; + for (attr in attrs) { + if (attrs.hasOwnProperty(attr)) { + ln2 = attrs[attr].length; + for (j = 0; j < ln2; j++) { + o = attrs[attr][j]; + this.setElVal(o[0], attr, o[1]); + } + } + } + } + }, + + setElVal: function(element, attr, value){ + if (attr === 'x') { + element.setX(value); + } else if (attr === 'y') { + element.setY(value); + } else if (attr === 'scrollTop') { + element.scrollTo('top', value); + } else if (attr === 'scrollLeft') { + element.scrollTo('left',value); + } else if (attr === 'width') { + element.setWidth(value); + } else if (attr === 'height') { + element.setHeight(value); + } else { + element.setStyle(attr, value); + } + } +}); + + + +Ext.define('Ext.fx.target.ElementCSS', { + + + + extend: Ext.fx.target.Element , + + + + setAttr: function(targetData, isFirstFrame) { + var cssArr = { + attrs: [], + duration: [], + easing: [] + }, + ln = targetData.length, + attributes, + attrs, + attr, + easing, + duration, + o, + i, + j, + ln2; + for (i = 0; i < ln; i++) { + attrs = targetData[i]; + duration = attrs.duration; + easing = attrs.easing; + attrs = attrs.attrs; + for (attr in attrs) { + if (Ext.Array.indexOf(cssArr.attrs, attr) == -1) { + cssArr.attrs.push(attr.replace(/[A-Z]/g, function(v) { + return '-' + v.toLowerCase(); + })); + cssArr.duration.push(duration + 'ms'); + cssArr.easing.push(easing); + } + } + } + attributes = cssArr.attrs.join(','); + duration = cssArr.duration.join(','); + easing = cssArr.easing.join(', '); + for (i = 0; i < ln; i++) { + attrs = targetData[i].attrs; + for (attr in attrs) { + ln2 = attrs[attr].length; + for (j = 0; j < ln2; j++) { + o = attrs[attr][j]; + o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionProperty', isFirstFrame ? '' : attributes); + o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionDuration', isFirstFrame ? '' : duration); + o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionTimingFunction', isFirstFrame ? '' : easing); + o[0].setStyle(attr, o[1]); + + + if (isFirstFrame) { + o = o[0].dom.offsetWidth; + } + else { + + o[0].on(Ext.supports.CSS3TransitionEnd, function() { + this.setStyle(Ext.supports.CSS3Prefix + 'TransitionProperty', null); + this.setStyle(Ext.supports.CSS3Prefix + 'TransitionDuration', null); + this.setStyle(Ext.supports.CSS3Prefix + 'TransitionTimingFunction', null); + }, o[0], { single: true }); + } + } + } + } + } +}); + + + +Ext.define('Ext.fx.target.CompositeElement', { + + + + extend: Ext.fx.target.Element , + + + + + isComposite: true, + + constructor: function(target) { + target.id = target.id || Ext.id(null, 'ext-composite-'); + this.callParent([target]); + }, + + getAttr: function(attr, val) { + var out = [], + target = this.target, + elements = target.elements, + length = elements.length, + i, + el; + + for (i = 0; i < length; i++) { + el = elements[i]; + + if (el) { + el = target.getElement(el); + out.push([el, this.getElVal(el, attr, val)]); + } + } + + return out; + }, + + setAttr: function(targetData){ + var target = this.target, + ln = targetData.length, + elements = target.elements, + ln3 = elements.length, + value, k, + attrs, attr, o, i, j, ln2; + + for (i = 0; i < ln; i++) { + attrs = targetData[i].attrs; + for (attr in attrs) { + if (attrs.hasOwnProperty(attr)) { + ln2 = attrs[attr].length; + for (j = 0; j < ln2; j++) { + value = attrs[attr][j][1]; + for (k = 0; k < ln3; ++k) { + el = elements[k]; + if (el) { + el = target.getElement(el); + this.setElVal(el, attr, value); + } + } + } + } + } + } + }, + + remove: function() { + this.target.remove(); + } +}); + + + +Ext.define('Ext.fx.target.CompositeElementCSS', { + + + + extend: Ext.fx.target.CompositeElement , + + + + + setAttr: function() { + return Ext.fx.target.ElementCSS.prototype.setAttr.apply(this, arguments); + } +}); + + + + +Ext.define('Ext.fx.target.Sprite', { + + + + extend: Ext.fx.target.Target , + + + + type: 'draw', + + getFromPrim: function (sprite, attr) { + var obj; + switch (attr) { + case 'rotate': + case 'rotation': + obj = sprite.attr.rotation; + return { + x: obj.x || 0, + y: obj.y || 0, + degrees: obj.degrees || 0 + }; + case 'scale': + case 'scaling': + obj = sprite.attr.scaling; + return { + x: obj.x || 1, + y: obj.y || 1, + cx: obj.cx || 0, + cy: obj.cy || 0 + }; + case 'translate': + case 'translation': + obj = sprite.attr.translation; + return { + x: obj.x || 0, + y: obj.y || 0 + }; + default: + return sprite.attr[attr]; + } + }, + + getAttr: function (attr, val) { + return [ + [this.target, val != undefined ? val : this.getFromPrim(this.target, attr)] + ]; + }, + + setAttr: function (targetData) { + var ln = targetData.length, + spriteArr = [], + attrsConf, attr, attrArr, attrs, sprite, idx, value, i, j, x, y, ln2; + for (i = 0; i < ln; i++) { + attrsConf = targetData[i].attrs; + for (attr in attrsConf) { + attrArr = attrsConf[attr]; + ln2 = attrArr.length; + for (j = 0; j < ln2; j++) { + sprite = attrArr[j][0]; + attrs = attrArr[j][1]; + if (attr === 'translate' || attr === 'translation') { + value = { + x: attrs.x, + y: attrs.y + }; + } + else if (attr === 'rotate' || attr === 'rotation') { + x = attrs.x; + if (isNaN(x)) { + x = null; + } + y = attrs.y; + if (isNaN(y)) { + y = null; + } + value = { + degrees: attrs.degrees, + x: x, + y: y + }; + } else if (attr === 'scale' || attr === 'scaling') { + x = attrs.x; + if (isNaN(x)) { + x = null; + } + y = attrs.y; + if (isNaN(y)) { + y = null; + } + value = { + x: x, + y: y, + cx: attrs.cx, + cy: attrs.cy + }; + } + else if (attr === 'width' || attr === 'height' || attr === 'x' || attr === 'y') { + value = parseFloat(attrs); + } + else { + value = attrs; + } + idx = Ext.Array.indexOf(spriteArr, sprite); + if (idx == -1) { + spriteArr.push([sprite, {}]); + idx = spriteArr.length - 1; + } + spriteArr[idx][1][attr] = value; + } + } + } + ln = spriteArr.length; + for (i = 0; i < ln; i++) { + spriteArr[i][0].setAttributes(spriteArr[i][1]); + } + this.target.redraw(); + } +}); + + + + +Ext.define('Ext.fx.target.CompositeSprite', { + + + + extend: Ext.fx.target.Sprite , + + + + getAttr: function(attr, val) { + var out = [], + sprites = [].concat(this.target.items), + length = sprites.length, + i, + sprite; + + for (i = 0; i < length; i++) { + sprite = sprites[i]; + out.push([sprite, val != undefined ? val : this.getFromPrim(sprite, attr)]); + } + + return out; + } +}); + + + +Ext.define('Ext.fx.target.Component', { + + + + extend: Ext.fx.target.Target , + + + + type: 'component', + + + getPropMethod: { + top: function() { + return this.getPosition(true)[1]; + }, + left: function() { + return this.getPosition(true)[0]; + }, + x: function() { + return this.getPosition()[0]; + }, + y: function() { + return this.getPosition()[1]; + }, + height: function() { + return this.getHeight(); + }, + width: function() { + return this.getWidth(); + }, + opacity: function() { + return this.el.getStyle('opacity'); + } + }, + + setMethods: { + top: 'setPosition', + left: 'setPosition', + x: 'setPagePosition', + y: 'setPagePosition', + height: 'setSize', + width: 'setSize', + opacity: 'setOpacity' + }, + + + getAttr: function(attr, val) { + return [[this.target, val !== undefined ? val : this.getPropMethod[attr].call(this.target)]]; + }, + + setAttr: function(targetData, isFirstFrame, isLastFrame) { + var me = this, + ln = targetData.length, + attrs, attr, o, i, j, targets, left, top, w, h, + methodsToCall = {}, + methodProps; + + for (i = 0; i < ln; i++) { + attrs = targetData[i].attrs; + for (attr in attrs) { + targets = attrs[attr].length; + for (j = 0; j < targets; j++) { + o = attrs[attr][j]; + methodProps = methodsToCall[me.setMethods[attr]] || (methodsToCall[me.setMethods[attr]] = {}); + methodProps.target = o[0]; + methodProps[attr] = o[1]; + + } + } + if (methodsToCall.setPosition) { + o = methodsToCall.setPosition; + left = (o.left === undefined) ? undefined : parseFloat(o.left); + top = (o.top === undefined) ? undefined : parseFloat(o.top); + o.target.setPosition(left, top); + } + if (methodsToCall.setPagePosition) { + o = methodsToCall.setPagePosition; + o.target.setPagePosition(o.x, o.y); + } + if (methodsToCall.setSize) { + o = methodsToCall.setSize; + + w = (o.width === undefined) ? o.target.getWidth() : parseFloat(o.width); + h = (o.height === undefined) ? o.target.getHeight() : parseFloat(o.height); + + + + + + + + + o.target.el.setSize(w, h); + if (isLastFrame || me.dynamic) { + + + + Ext.globalEvents.on({ + idle: Ext.Function.bind(o.target.setSize, o.target, [w, h]), + single: true + }); + } + } + if (methodsToCall.setOpacity) { + o = methodsToCall.setOpacity; + o.target.el.setStyle('opacity', o.opacity); + } + } + } +}); + + + + +Ext.define('Ext.fx.Queue', { + + + + constructor: function() { + this.targets = new Ext.util.HashMap(); + this.fxQueue = {}; + }, + + + getFxDefaults: function(targetId) { + var target = this.targets.get(targetId); + if (target) { + return target.fxDefaults; + } + return {}; + }, + + + setFxDefaults: function(targetId, obj) { + var target = this.targets.get(targetId); + if (target) { + target.fxDefaults = Ext.apply(target.fxDefaults || {}, obj); + } + }, + + + stopAnimation: function(targetId) { + var me = this, + queue = me.getFxQueue(targetId), + ln = queue.length; + while (ln) { + queue[ln - 1].end(); + ln--; + } + }, + + + getActiveAnimation: function(targetId) { + var queue = this.getFxQueue(targetId); + return (queue && !!queue.length) ? queue[0] : false; + }, + + + hasFxBlock: function(targetId) { + var queue = this.getFxQueue(targetId); + return queue && queue[0] && queue[0].block; + }, + + + getFxQueue: function(targetId) { + if (!targetId) { + return false; + } + var me = this, + queue = me.fxQueue[targetId], + target = me.targets.get(targetId); + + if (!target) { + return false; + } + + if (!queue) { + me.fxQueue[targetId] = []; + + if (target.type != 'element') { + target.target.on('destroy', function() { + me.fxQueue[targetId] = []; + }); + } + } + return me.fxQueue[targetId]; + }, + + + queueFx: function(anim) { + var me = this, + target = anim.target, + queue, ln; + + if (!target) { + return; + } + + queue = me.getFxQueue(target.getId()); + ln = queue.length; + + if (ln) { + if (anim.concurrent) { + anim.paused = false; + } + else { + queue[ln - 1].on('afteranimate', function() { + anim.paused = false; + }); + } + } + else { + anim.paused = false; + } + anim.on('afteranimate', function() { + Ext.Array.remove(queue, anim); + if (queue.length === 0) { + me.targets.remove(anim.target); + } + if (anim.remove) { + if (target.type == 'element') { + var el = Ext.get(target.id); + if (el) { + el.remove(); + } + } + } + }, me, { + single: true + }); + queue.push(anim); + } +}); + + + + +Ext.define('Ext.fx.Manager', { + + + + singleton: true, + + + + + + + + + + + mixins: { + queue: Ext.fx.Queue + }, + + + + constructor: function() { + var me = this; + me.items = new Ext.util.MixedCollection(); + me.targetArr = {}; + me.mixins.queue.constructor.call(me); + + + + me.taskRunner = new Ext.util.TaskRunner(); + + + + + + + + + + + + + + + + + + + }, + + + interval: 16, + + + forceJS: true, + + + createTarget: function(target) { + var me = this, + useCSS3 = !me.forceJS && Ext.supports.Transitions, + targetObj; + + me.useCSS3 = useCSS3; + + if (target) { + + if (target.tagName || Ext.isString(target) || target.isFly) { + target = Ext.get(target); + targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target); + } + + else if (target.dom) { + targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target); + } + + else if (target.isComposite) { + targetObj = new Ext.fx.target['CompositeElement' + (useCSS3 ? 'CSS' : '')](target); + } + + else if (target.isSprite) { + targetObj = new Ext.fx.target.Sprite(target); + } + + else if (target.isCompositeSprite) { + targetObj = new Ext.fx.target.CompositeSprite(target); + } + + else if (target.isComponent) { + targetObj = new Ext.fx.target.Component(target); + } + else if (target.isAnimTarget) { + return target; + } + else { + return null; + } + me.targets.add(targetObj); + return targetObj; + } + else { + return null; + } + }, + + + addAnim: function(anim) { + var me = this, + items = me.items, + task = me.task; + + + + + items.add(anim.id, anim); + + + + if (!task && items.length) { + task = me.task = { + run: me.runner, + interval: me.interval, + scope: me + }; + + me.taskRunner.start(task); + } + }, + + + removeAnim: function(anim) { + var me = this, + items = me.items, + task = me.task; + + items.removeAtKey(anim.id); + + + + if (task && !items.length) { + + me.taskRunner.stop(task); + delete me.task; + } + }, + + + runner: function() { + var me = this, + items = me.items.getRange(), + i = 0, + len = items.length, + anim; + + + me.targetArr = {}; + + + me.timestamp = new Date(); + + + + + + + + + + + + for (; i < len; i++) { + anim = items[i]; + + if (anim.isReady()) { + + me.startAnim(anim); + } + } + + for (i = 0; i < len; i++) { + anim = items[i]; + + if (anim.isRunning()) { + + me.runAnim(anim); + } else if (!me.useCSS3) { + + + + + + } + } + + + me.applyPendingAttrs(); + }, + + + startAnim: function(anim) { + anim.start(this.timestamp); + }, + + + runAnim: function(anim, forceEnd) { + if (!anim) { + return; + } + var me = this, + useCSS3 = me.useCSS3 && anim.target.type == 'element', + elapsedTime = me.timestamp - anim.startTime, + lastFrame = (elapsedTime >= anim.duration), + target, o; + + if (forceEnd) { + elapsedTime = anim.duration; + lastFrame = true; + } + + target = this.collectTargetData(anim, elapsedTime, useCSS3, lastFrame); + + + + if (useCSS3) { + + + + anim.target.setAttr(target.anims[anim.id].attributes, true); + + + me.collectTargetData(anim, anim.duration, useCSS3, lastFrame); + + + anim.paused = true; + + target = anim.target.target; + + if (anim.target.isComposite) { + target = anim.target.target.last(); + } + + + o = {}; + o[Ext.supports.CSS3TransitionEnd] = anim.lastFrame; + o.scope = anim; + o.single = true; + target.on(o); + } + return target; + }, + + jumpToEnd: function(anim) { + var target = this.runAnim(anim, true); + this.applyAnimAttrs(target, target.anims[anim.id]); + }, + + + collectTargetData: function(anim, elapsedTime, useCSS3, isLastFrame) { + var targetId = anim.target.getId(), + target = this.targetArr[targetId]; + + if (!target) { + + + + + target = this.targetArr[targetId] = { + id: targetId, + el: anim.target, + anims: {} + }; + } + + + + + + target.anims[anim.id] = { + id: anim.id, + anim: anim, + elapsed: elapsedTime, + isLastFrame: isLastFrame, + + attributes: [{ + duration: anim.duration, + easing: (useCSS3 && anim.reverse) ? anim.easingFn.reverse().toCSS3() : anim.easing, + + + attrs: anim.runAnim(elapsedTime) + }] + }; + + return target; + }, + + + + + applyAnimAttrs: function(target, animWrap) { + var anim = animWrap.anim; + if (animWrap.attributes && anim.isRunning()) { + target.el.setAttr(animWrap.attributes, false, animWrap.isLastFrame); + + + if (animWrap.isLastFrame) { + anim.lastFrame(); + } + } + }, + + + applyPendingAttrs: function() { + var targetArr = this.targetArr, + target, targetId, animWrap, anim, animId; + + + for (targetId in targetArr) { + if (targetArr.hasOwnProperty(targetId)) { + target = targetArr[targetId]; + + + for (animId in target.anims) { + if (target.anims.hasOwnProperty(animId)) { + animWrap = target.anims[animId]; + anim = animWrap.anim; + + + if (animWrap.attributes && anim.isRunning()) { + + target.el.setAttr(animWrap.attributes, false, animWrap.isLastFrame); + + + if (animWrap.isLastFrame) { + + anim.lastFrame(); + } + } + } + } + } + } + } +}); + + + +Ext.define('Ext.fx.Animator', { + + + + mixins: { + observable: Ext.util.Observable + }, + + + + + + + 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(config) { + var me = this; + config = Ext.apply(me, config || {}); + me.config = config; + me.id = Ext.id(null, 'ext-animator-'); + me.addEvents( + + 'beforeanimate', + + 'keyframe', + + 'afteranimate' + ); + me.mixins.observable.constructor.call(me, config); + me.timeline = []; + me.createTimeline(me.keyframes); + if (me.target) { + me.applyAnimator(me.target); + Ext.fx.Manager.addAnim(me); + } + }, + + + sorter: function (a, b) { + return a.pct - b.pct; + }, + + + createTimeline: function(keyframes) { + var me = this, + attrs = [], + to = me.to || {}, + duration = me.duration, + prevMs, ms, i, ln, pct, attr; + + for (pct in keyframes) { + if (keyframes.hasOwnProperty(pct) && me.animKeyFramesRE.test(pct)) { + attr = {attrs: Ext.apply(keyframes[pct], to)}; + + if (pct == "from") { + pct = 0; + } + else if (pct == "to") { + pct = 100; + } + + attr.pct = parseInt(pct, 10); + attrs.push(attr); + } + } + + Ext.Array.sort(attrs, me.sorter); + + + + + + ln = attrs.length; + for (i = 0; i < ln; i++) { + prevMs = (attrs[i - 1]) ? duration * (attrs[i - 1].pct / 100) : 0; + ms = duration * (attrs[i].pct / 100); + me.timeline.push({ + duration: ms - prevMs, + attrs: attrs[i].attrs + }); + } + }, + + + applyAnimator: function(target) { + var me = this, + anims = [], + timeline = me.timeline, + ln = timeline.length, + anim, easing, damper, attrs, i; + + if (me.fireEvent('beforeanimate', me) !== false) { + for (i = 0; i < ln; i++) { + anim = timeline[i]; + attrs = anim.attrs; + easing = attrs.easing || me.easing; + damper = attrs.damper || me.damper; + delete attrs.easing; + delete attrs.damper; + anim = new Ext.fx.Anim({ + target: target, + easing: easing, + damper: damper, + duration: anim.duration, + paused: true, + to: attrs + }); + anims.push(anim); + } + me.animations = anims; + me.target = anim.target; + for (i = 0; i < ln - 1; i++) { + anim = anims[i]; + anim.nextAnim = anims[i + 1]; + anim.on('afteranimate', function() { + this.nextAnim.paused = false; + }); + anim.on('afteranimate', function() { + this.fireEvent('keyframe', this, ++this.keyframeStep); + }, me); + } + anims[ln - 1].on('afteranimate', function() { + this.lastFrame(); + }, me); + } + }, + + + start: function(startTime) { + var me = this, + delay = me.delay, + delayStart = me.delayStart, + delayDelta; + if (delay) { + if (!delayStart) { + me.delayStart = startTime; + return; + } + else { + delayDelta = startTime - delayStart; + if (delayDelta < delay) { + return; + } + else { + + startTime = new Date(delayStart.getTime() + delay); + } + } + } + if (me.fireEvent('beforeanimate', me) !== false) { + me.startTime = startTime; + me.running = true; + me.animations[me.keyframeStep].paused = false; + } + }, + + + lastFrame: function() { + var me = this, + iter = me.iterations, + iterCount = me.currentIteration; + + iterCount++; + if (iterCount < iter) { + me.startTime = new Date(); + me.currentIteration = iterCount; + me.keyframeStep = 0; + me.applyAnimator(me.target); + me.animations[me.keyframeStep].paused = false; + } + else { + me.currentIteration = 0; + me.end(); + } + }, + + + end: function() { + var me = this; + me.fireEvent('afteranimate', me, me.startTime, new Date() - me.startTime); + }, + + isReady: function() { + return this.paused === false && this.running === false && this.iterations > 0; + }, + + isRunning: function() { + + return false; + } +}); + + + +Ext.define('Ext.fx.CubicBezier', { + + + + singleton: true, + + + + cubicBezierAtTime: function(t, p1x, p1y, p2x, p2y, duration) { + var cx = 3 * p1x, + bx = 3 * (p2x - p1x) - cx, + ax = 1 - cx - bx, + cy = 3 * p1y, + by = 3 * (p2y - p1y) - cy, + ay = 1 - cy - by; + function sampleCurveX(t) { + return ((ax * t + bx) * t + cx) * t; + } + function solve(x, epsilon) { + var t = solveCurveX(x, epsilon); + return ((ay * t + by) * t + cy) * t; + } + function solveCurveX(x, epsilon) { + var t0, t1, t2, x2, d2, i; + for (t2 = x, i = 0; i < 8; i++) { + x2 = sampleCurveX(t2) - x; + if (Math.abs(x2) < epsilon) { + return t2; + } + d2 = (3 * ax * t2 + 2 * bx) * t2 + cx; + if (Math.abs(d2) < 1e-6) { + break; + } + t2 = t2 - x2 / d2; + } + t0 = 0; + t1 = 1; + t2 = x; + if (t2 < t0) { + return t0; + } + if (t2 > t1) { + return t1; + } + while (t0 < t1) { + x2 = sampleCurveX(t2); + if (Math.abs(x2 - x) < epsilon) { + return t2; + } + if (x > x2) { + t0 = t2; + } else { + t1 = t2; + } + t2 = (t1 - t0) / 2 + t0; + } + return t2; + } + return solve(t, 1 / (200 * duration)); + }, + + cubicBezier: function(x1, y1, x2, y2) { + var fn = function(pos) { + return Ext.fx.CubicBezier.cubicBezierAtTime(pos, x1, y1, x2, y2, 1); + }; + fn.toCSS3 = function() { + return 'cubic-bezier(' + [x1, y1, x2, y2].join(',') + ')'; + }; + fn.reverse = function() { + return Ext.fx.CubicBezier.cubicBezier(1 - x2, 1 - y2, 1 - x1, 1 - y1); + }; + return fn; + } +}); + + + + + +Ext.require('Ext.fx.CubicBezier', function() { + var math = Math, + pi = math.PI, + pow = math.pow, + sin = math.sin, + sqrt = math.sqrt, + abs = math.abs, + backInSeed = 1.70158; + + Ext.define('Ext.fx.Easing', { + singleton: true, + + linear: Ext.identityFn, + ease: function(n) { + var q = 0.07813 - n / 2, + alpha = -0.25, + Q = sqrt(0.0066 + q * q), + x = Q - q, + X = pow(abs(x), 1/3) * (x < 0 ? -1 : 1), + y = -Q - q, + Y = pow(abs(y), 1/3) * (y < 0 ? -1 : 1), + t = X + Y + 0.25; + return pow(1 - t, 2) * 3 * t * 0.1 + (1 - t) * 3 * t * t + t * t * t; + }, + easeIn: function (n) { + return pow(n, 1.7); + }, + easeOut: function (n) { + return pow(n, 0.48); + }, + easeInOut: function(n) { + var q = 0.48 - n / 1.04, + Q = sqrt(0.1734 + q * q), + x = Q - q, + X = pow(abs(x), 1/3) * (x < 0 ? -1 : 1), + y = -Q - q, + Y = pow(abs(y), 1/3) * (y < 0 ? -1 : 1), + t = X + Y + 0.5; + return (1 - t) * 3 * t * t + t * t * t; + }, + backIn: function (n) { + return n * n * ((backInSeed + 1) * n - backInSeed); + }, + backOut: function (n) { + n = n - 1; + return n * n * ((backInSeed + 1) * n + backInSeed) + 1; + }, + elasticIn: function (n) { + if (n === 0 || n === 1) { + return n; + } + var p = 0.3, + s = p / 4; + return pow(2, -10 * n) * sin((n - s) * (2 * pi) / p) + 1; + }, + elasticOut: function (n) { + return 1 - Ext.fx.Easing.elasticIn(1 - n); + }, + bounceIn: function (n) { + return 1 - Ext.fx.Easing.bounceOut(1 - n); + }, + bounceOut: function (n) { + var s = 7.5625, + p = 2.75, + l; + if (n < (1 / p)) { + l = s * n * n; + } else { + if (n < (2 / p)) { + n -= (1.5 / p); + l = s * n * n + 0.75; + } else { + if (n < (2.5 / p)) { + n -= (2.25 / p); + l = s * n * n + 0.9375; + } else { + n -= (2.625 / p); + l = s * n * n + 0.984375; + } + } + } + return l; + } + }, function(){ + var easing = Ext.fx.Easing.self, + proto = easing.prototype; + + easing.implement({ + 'back-in': proto.backIn, + 'back-out': proto.backOut, + 'ease-in': proto.easeIn, + 'ease-out': proto.easeOut, + 'elastic-in': proto.elasticIn, + 'elastic-out': proto.elasticOut, + 'bounce-in': proto.bounceIn, + 'bounce-out': proto.bounceOut, + 'ease-in-out': proto.easeInOut + }); + }); +}); + + + +Ext.define('Ext.draw.Color', { + + + + + + colorToHexRe: /(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/, + rgbRe: /\s*rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)\s*/, + hexRe: /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/, + + + lightnessFactor: 0.2, + + + constructor : function(red, green, blue) { + var me = this, + clamp = Ext.Number.constrain; + me.r = clamp(red, 0, 255); + me.g = clamp(green, 0, 255); + me.b = clamp(blue, 0, 255); + }, + + + getRed: function() { + return this.r; + }, + + + getGreen: function() { + return this.g; + }, + + + getBlue: function() { + return this.b; + }, + + + getRGB: function() { + var me = this; + return [me.r, me.g, me.b]; + }, + + + getHSL: function() { + var me = this, + r = me.r / 255, + g = me.g / 255, + b = me.b / 255, + max = Math.max(r, g, b), + min = Math.min(r, g, b), + delta = max - min, + h, + s = 0, + l = 0.5 * (max + min); + + + if (min != max) { + s = (l < 0.5) ? delta / (max + min) : delta / (2 - max - min); + if (r == max) { + h = 60 * (g - b) / delta; + } else if (g == max) { + h = 120 + 60 * (b - r) / delta; + } else { + h = 240 + 60 * (r - g) / delta; + } + if (h < 0) { + h += 360; + } + if (h >= 360) { + h -= 360; + } + } + return [h, s, l]; + }, + + + getLighter: function(factor) { + var hsl = this.getHSL(); + factor = factor || this.lightnessFactor; + hsl[2] = Ext.Number.constrain(hsl[2] + factor, 0, 1); + return this.fromHSL(hsl[0], hsl[1], hsl[2]); + }, + + + getDarker: function(factor) { + factor = factor || this.lightnessFactor; + return this.getLighter(-factor); + }, + + + toString: function() { + var me = this, + round = Math.round, + r = round(me.r).toString(16), + g = round(me.g).toString(16), + b = round(me.b).toString(16); + r = (r.length == 1) ? '0' + r : r; + g = (g.length == 1) ? '0' + g : g; + b = (b.length == 1) ? '0' + b : b; + return ['#', r, g, b].join(''); + }, + + + toHex: function(color) { + if (Ext.isArray(color)) { + color = color[0]; + } + if (!Ext.isString(color)) { + return ''; + } + if (color.substr(0, 1) === '#') { + return color; + } + var digits = this.colorToHexRe.exec(color), + red, + green, + blue, + rgb; + + if (Ext.isArray(digits)) { + red = parseInt(digits[2], 10); + green = parseInt(digits[3], 10); + blue = parseInt(digits[4], 10); + rgb = blue | (green << 8) | (red << 16); + return digits[1] + '#' + ("000000" + rgb.toString(16)).slice(-6); + } + else { + return color; + } + }, + + + fromString: function(str) { + var values, r, g, b, + parse = parseInt, + firstChar = str.substr(0, 1), + colorValue; + + if (firstChar != '#') { + colorValue = Ext.draw.Color.cssColors[str]; + if (colorValue) { + str = colorValue; + firstChar = str.substr(0, 1); + } + } + + if ((str.length == 4 || str.length == 7) && firstChar === '#') { + values = str.match(this.hexRe); + if (values) { + r = parse(values[1], 16) >> 0; + g = parse(values[2], 16) >> 0; + b = parse(values[3], 16) >> 0; + if (str.length == 4) { + r += (r * 16); + g += (g * 16); + b += (b * 16); + } + } + } + else { + values = str.match(this.rgbRe); + if (values) { + r = values[1]; + g = values[2]; + b = values[3]; + } + } + + return (typeof r == 'undefined') ? undefined : new Ext.draw.Color(r, g, b); + }, + + + getGrayscale: function() { + + return this.r * 0.3 + this.g * 0.59 + this.b * 0.11; + }, + + + fromHSL: function(h, s, l) { + var C, X, m, i, rgb = [], + abs = Math.abs, + floor = Math.floor; + + if (s == 0 || h == null) { + + rgb = [l, l, l]; + } + else { + + + + + h /= 60; + C = s * (1 - abs(2 * l - 1)); + X = C * (1 - abs(h - 2 * floor(h / 2) - 1)); + m = l - C / 2; + switch (floor(h)) { + case 0: + rgb = [C, X, 0]; + break; + case 1: + rgb = [X, C, 0]; + break; + case 2: + rgb = [0, C, X]; + break; + case 3: + rgb = [0, X, C]; + break; + case 4: + rgb = [X, 0, C]; + break; + case 5: + rgb = [C, 0, X]; + break; + } + rgb = [rgb[0] + m, rgb[1] + m, rgb[2] + m]; + } + return new Ext.draw.Color(rgb[0] * 255, rgb[1] * 255, rgb[2] * 255); + } +}, function() { + var prototype = this.prototype; + + this.addStatics({ + + fromHSL: function() { + return prototype.fromHSL.apply(prototype, arguments); + }, + fromString: function() { + return prototype.fromString.apply(prototype, arguments); + }, + toHex: function() { + return prototype.toHex.apply(prototype, arguments); + }, + + cssColors: { + aliceblue: '#F0F8FF', + antiquewhite: '#FAEBD7', + aqua: '#00FFFF', + aquamarine: '#7FFFD4', + azure: '#F0FFFF', + beige: '#F5F5DC', + bisque: '#FFE4C4', + black: '#000000', + blanchedalmond: '#FFEBCD', + blue: '#0000FF', + blueviolet: '#8A2BE2', + brown: '#A52A2A', + burlywood: '#DEB887', + cadetblue: '#5F9EA0', + chartreuse: '#7FFF00', + chocolate: '#D2691E', + coral: '#FF7F50', + cornflowerblue: '#6495ED', + cornsilk: '#FFF8DC', + crimson: '#DC143C', + cyan: '#00FFFF', + darkblue: '#00008B', + darkcyan: '#008B8B', + darkgoldenrod: '#B8860B', + darkgray: '#A9A9A9', + darkgreen: '#006400', + darkgrey: '#A9A9A9', + darkkhaki: '#BDB76B', + darkmagenta: '#8B008B', + darkolivegreen: '#556B2F', + darkorange: '#FF8C00', + darkorchid: '#9932CC', + darkred: '#8B0000', + darksalmon: '#E9967A', + darkseagreen: '#8FBC8F', + darkslateblue: '#483D8B', + darkslategray: '#2F4F4F', + darkslategrey: '#2F4F4F', + darkturquoise: '#00CED1', + darkviolet: '#9400D3', + deeppink: '#FF1493', + deepskyblue: '#00BFFF', + dimgray: '#696969', + dimgrey: '#696969', + dodgerblue: '#1E90FF', + firebrick: '#B22222', + floralwhite: '#FFFAF0', + forestgreen: '#228B22', + fuchsia: '#FF00FF', + gainsboro: '#DCDCDC', + ghostwhite: '#F8F8FF', + gold: '#FFD700', + goldenrod: '#DAA520', + gray: '#808080', + grey: '#808080', + green: '#008000', + greenyellow: '#ADFF2F', + honeydew: '#F0FFF0', + hotpink: '#FF69B4', + indianred: '#CD5C5C', + indigo: '#4B0082', + ivory: '#FFFFF0', + khaki: '#F0E68C', + lavender: '#E6E6FA', + lavenderblush: '#FFF0F5', + lawngreen: '#7CFC00', + lemonchiffon: '#FFFACD', + lightblue: '#ADD8E6', + lightcoral: '#F08080', + lightcyan: '#E0FFFF', + lightgoldenrodyellow: '#FAFAD2', + lightgray: '#D3D3D3', + lightgreen: '#90EE90', + lightgrey: '#D3D3D3', + lightpink: '#FFB6C1', + lightsalmon: '#FFA07A', + lightseagreen: '#20B2AA', + lightskyblue: '#87CEFA', + lightslategray: '#778899', + lightslategrey: '#778899', + lightsteelblue: '#B0C4DE', + lightyellow: '#FFFFE0', + lime: '#00FF00', + limegreen: '#32CD32', + linen: '#FAF0E6', + magenta: '#FF00FF', + maroon: '#800000', + mediumaquamarine: '#66CDAA', + mediumblue: '#0000CD', + mediumorchid: '#BA55D3', + mediumpurple: '#9370DB', + mediumseagreen: '#3CB371', + mediumslateblue: '#7B68EE', + mediumspringgreen: '#00FA9A', + mediumturquoise: '#48D1CC', + mediumvioletred: '#C71585', + midnightblue: '#191970', + mintcream: '#F5FFFA', + mistyrose: '#FFE4E1', + moccasin: '#FFE4B5', + navajowhite: '#FFDEAD', + navy: '#000080', + oldlace: '#FDF5E6', + olive: '#808000', + olivedrab: '#6B8E23', + orange: '#FFA500', + orangered: '#FF4500', + orchid: '#DA70D6', + palegoldenrod: '#EEE8AA', + palegreen: '#98FB98', + paleturquoise: '#AFEEEE', + palevioletred: '#DB7093', + papayawhip: '#FFEFD5', + peachpuff: '#FFDAB9', + peru: '#CD853F', + pink: '#FFC0CB', + plum: '#DDA0DD', + powderblue: '#B0E0E6', + purple: '#800080', + red: '#FF0000', + rosybrown: '#BC8F8F', + royalblue: '#4169E1', + saddlebrown: '#8B4513', + salmon: '#FA8072', + sandybrown: '#F4A460', + seagreen: '#2E8B57', + seashell: '#FFF5EE', + sienna: '#A0522D', + silver: '#C0C0C0', + skyblue: '#87CEEB', + slateblue: '#6A5ACD', + slategray: '#708090', + slategrey: '#708090', + snow: '#FFFAFA', + springgreen: '#00FF7F', + steelblue: '#4682B4', + tan: '#D2B48C', + teal: '#008080', + thistle: '#D8BFD8', + tomato: '#FF6347', + turquoise: '#40E0D0', + violet: '#EE82EE', + wheat: '#F5DEB3', + white: '#FFFFFF', + whitesmoke: '#F5F5F5', + yellow: '#FFFF00', + yellowgreen: '#9ACD32' + } + }); + +}); + + + +Ext.define('Ext.draw.Draw', { + + + 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, + + availableAnimAttrs: { + along: "along", + blur: null, + "clip-rect": "csv", + cx: null, + cy: null, + fill: "color", + "fill-opacity": null, + "font-size": null, + height: null, + opacity: null, + path: "path", + r: null, + rotation: "csv", + rx: null, + ry: null, + scale: "csv", + stroke: "color", + "stroke-opacity": null, + "stroke-width": null, + translation: "csv", + width: null, + x: null, + y: null + }, + + is: function(o, type) { + type = String(type).toLowerCase(); + return (type == "object" && o === Object(o)) || + (type == "undefined" && typeof o == type) || + (type == "null" && o === null) || + (type == "array" && Array.isArray && Array.isArray(o)) || + (Object.prototype.toString.call(o).toLowerCase().slice(8, -1)) == type; + }, + + ellipsePath: function(sprite) { + var attr = sprite.attr; + return Ext.String.format("M{0},{1}A{2},{3},0,1,1,{0},{4}A{2},{3},0,1,1,{0},{1}z", attr.x, attr.y - attr.ry, attr.rx, attr.ry, attr.y + attr.ry); + }, + + rectPath: function(sprite) { + var attr = sprite.attr; + if (attr.radius) { + return Ext.String.format("M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z", attr.x + attr.radius, attr.y, attr.width - attr.radius * 2, attr.radius, -attr.radius, attr.height - attr.radius * 2, attr.radius * 2 - attr.width, attr.radius * 2 - attr.height); + } + else { + return Ext.String.format("M{0},{1}L{2},{1},{2},{3},{0},{3}z", attr.x, attr.y, attr.width + attr.x, attr.height + attr.y); + } + }, + + + path2string: function () { + return this.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1"); + }, + + + pathToString: function(arrayPath) { + return arrayPath.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1"); + }, + + parsePathString: function (pathString) { + if (!pathString) { + return null; + } + var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0}, + data = [], + me = this; + if (me.is(pathString, "array") && me.is(pathString[0], "array")) { + data = me.pathClone(pathString); + } + if (!data.length) { + String(pathString).replace(me.pathCommandRE, function (a, b, c) { + var params = [], + name = b.toLowerCase(); + c.replace(me.pathValuesRE, function (a, b) { + b && params.push(+b); + }); + if (name == "m" && params.length > 2) { + data.push([b].concat(Ext.Array.splice(params, 0, 2))); + name = "l"; + b = (b == "m") ? "l" : "L"; + } + while (params.length >= paramCounts[name]) { + data.push([b].concat(Ext.Array.splice(params, 0, paramCounts[name]))); + if (!paramCounts[name]) { + break; + } + } + }); + } + data.toString = me.path2string; + return data; + }, + + mapPath: function (path, matrix) { + if (!matrix) { + return path; + } + var x, y, i, ii, j, jj, pathi; + path = this.path2curve(path); + for (i = 0, ii = path.length; i < ii; i++) { + pathi = path[i]; + for (j = 1, jj = pathi.length; j < jj-1; j += 2) { + x = matrix.x(pathi[j], pathi[j + 1]); + y = matrix.y(pathi[j], pathi[j + 1]); + pathi[j] = x; + pathi[j + 1] = y; + } + } + return path; + }, + + pathClone: function(pathArray) { + var res = [], + j, jj, i, ii; + if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { + pathArray = this.parsePathString(pathArray); + } + for (i = 0, ii = pathArray.length; i < ii; i++) { + res[i] = []; + for (j = 0, jj = pathArray[i].length; j < jj; j++) { + res[i][j] = pathArray[i][j]; + } + } + res.toString = this.path2string; + return res; + }, + + pathToAbsolute: function (pathArray) { + if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { + pathArray = this.parsePathString(pathArray); + } + var res = [], + x = 0, + y = 0, + mx = 0, + my = 0, + i = 0, + ln = pathArray.length, + r, pathSegment, j, ln2; + + if (ln && pathArray[0][0] == "M") { + x = +pathArray[0][1]; + y = +pathArray[0][2]; + mx = x; + my = y; + i++; + res[0] = ["M", x, y]; + } + for (; i < ln; i++) { + r = res[i] = []; + pathSegment = pathArray[i]; + if (pathSegment[0] != pathSegment[0].toUpperCase()) { + r[0] = pathSegment[0].toUpperCase(); + switch (r[0]) { + + case "A": + r[1] = pathSegment[1]; + r[2] = pathSegment[2]; + r[3] = pathSegment[3]; + r[4] = pathSegment[4]; + r[5] = pathSegment[5]; + r[6] = +(pathSegment[6] + x); + r[7] = +(pathSegment[7] + y); + break; + + case "V": + r[1] = +pathSegment[1] + y; + break; + + case "H": + r[1] = +pathSegment[1] + x; + break; + case "M": + + mx = +pathSegment[1] + x; + my = +pathSegment[2] + y; + default: + j = 1; + ln2 = pathSegment.length; + for (; j < ln2; j++) { + r[j] = +pathSegment[j] + ((j % 2) ? x : y); + } + } + } + else { + j = 0; + ln2 = pathSegment.length; + for (; j < ln2; j++) { + res[i][j] = pathSegment[j]; + } + } + switch (r[0]) { + + case "Z": + x = mx; + y = my; + break; + + case "H": + x = r[1]; + break; + + case "V": + y = r[1]; + break; + + case "M": + pathSegment = res[i]; + ln2 = pathSegment.length; + mx = pathSegment[ln2 - 2]; + my = pathSegment[ln2 - 1]; + default: + pathSegment = res[i]; + ln2 = pathSegment.length; + x = pathSegment[ln2 - 2]; + y = pathSegment[ln2 - 1]; + } + } + res.toString = this.path2string; + return res; + }, + + + pathToRelative: function (pathArray) { + if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { + pathArray = this.parsePathString(pathArray); + } + var res = [], + x = 0, + y = 0, + mx = 0, + my = 0, + start = 0, + r, + pa, + i, + j, + k, + len, + ii, + jj, + kk; + + if (pathArray[0][0] == "M") { + x = pathArray[0][1]; + y = pathArray[0][2]; + mx = x; + my = y; + start++; + res.push(["M", x, y]); + } + for (i = start, ii = pathArray.length; i < ii; i++) { + r = res[i] = []; + pa = pathArray[i]; + if (pa[0] != pa[0].toLowerCase()) { + r[0] = pa[0].toLowerCase(); + switch (r[0]) { + case "a": + r[1] = pa[1]; + r[2] = pa[2]; + r[3] = pa[3]; + r[4] = pa[4]; + r[5] = pa[5]; + r[6] = +(pa[6] - x).toFixed(3); + r[7] = +(pa[7] - y).toFixed(3); + break; + case "v": + r[1] = +(pa[1] - y).toFixed(3); + break; + case "m": + mx = pa[1]; + my = pa[2]; + default: + for (j = 1, jj = pa.length; j < jj; j++) { + r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3); + } + } + } else { + r = res[i] = []; + if (pa[0] == "m") { + mx = pa[1] + x; + my = pa[2] + y; + } + for (k = 0, kk = pa.length; k < kk; k++) { + res[i][k] = pa[k]; + } + } + len = res[i].length; + switch (res[i][0]) { + case "z": + x = mx; + y = my; + break; + case "h": + x += +res[i][len - 1]; + break; + case "v": + y += +res[i][len - 1]; + break; + default: + x += +res[i][len - 2]; + y += +res[i][len - 1]; + } + } + res.toString = this.path2string; + return res; + }, + + + path2curve: function (path) { + var me = this, + points = me.pathToAbsolute(path), + ln = points.length, + attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, + i, seg, segLn, point; + + for (i = 0; i < ln; i++) { + points[i] = me.command2curve(points[i], attrs); + if (points[i].length > 7) { + points[i].shift(); + point = points[i]; + while (point.length) { + Ext.Array.splice(points, i++, 0, ["C"].concat(Ext.Array.splice(point, 0, 6))); + } + Ext.Array.erase(points, i, 1); + ln = points.length; + i--; + } + seg = points[i]; + segLn = seg.length; + attrs.x = seg[segLn - 2]; + attrs.y = seg[segLn - 1]; + attrs.bx = parseFloat(seg[segLn - 4]) || attrs.x; + attrs.by = parseFloat(seg[segLn - 3]) || attrs.y; + } + return points; + }, + + interpolatePaths: function (path, path2) { + var me = this, + p = me.pathToAbsolute(path), + p2 = me.pathToAbsolute(path2), + attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, + attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, + fixArc = function (pp, i) { + if (pp[i].length > 7) { + pp[i].shift(); + var pi = pp[i]; + while (pi.length) { + Ext.Array.splice(pp, i++, 0, ["C"].concat(Ext.Array.splice(pi, 0, 6))); + } + Ext.Array.erase(pp, i, 1); + ii = Math.max(p.length, p2.length || 0); + } + }, + fixM = function (path1, path2, a1, a2, i) { + if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") { + Ext.Array.splice(path2, i, 0, ["M", a2.x, a2.y]); + a1.bx = 0; + a1.by = 0; + a1.x = path1[i][1]; + a1.y = path1[i][2]; + ii = Math.max(p.length, p2.length || 0); + } + }, + i, ii, + seg, seg2, seglen, seg2len; + for (i = 0, ii = Math.max(p.length, p2.length || 0); i < ii; i++) { + p[i] = me.command2curve(p[i], attrs); + fixArc(p, i); + (p2[i] = me.command2curve(p2[i], attrs2)); + fixArc(p2, i); + fixM(p, p2, attrs, attrs2, i); + fixM(p2, p, attrs2, attrs, i); + seg = p[i]; + seg2 = p2[i]; + seglen = seg.length; + seg2len = seg2.length; + attrs.x = seg[seglen - 2]; + attrs.y = seg[seglen - 1]; + attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x; + attrs.by = parseFloat(seg[seglen - 3]) || attrs.y; + attrs2.bx = (parseFloat(seg2[seg2len - 4]) || attrs2.x); + attrs2.by = (parseFloat(seg2[seg2len - 3]) || attrs2.y); + attrs2.x = seg2[seg2len - 2]; + attrs2.y = seg2[seg2len - 1]; + } + return [p, p2]; + }, + + + command2curve: function (pathCommand, d) { + var me = this; + if (!pathCommand) { + return ["C", d.x, d.y, d.x, d.y, d.x, d.y]; + } + if (pathCommand[0] != "T" && pathCommand[0] != "Q") { + d.qx = d.qy = null; + } + switch (pathCommand[0]) { + case "M": + d.X = pathCommand[1]; + d.Y = pathCommand[2]; + break; + case "A": + pathCommand = ["C"].concat(me.arc2curve.apply(me, [d.x, d.y].concat(pathCommand.slice(1)))); + break; + case "S": + pathCommand = ["C", d.x + (d.x - (d.bx || d.x)), d.y + (d.y - (d.by || d.y))].concat(pathCommand.slice(1)); + break; + case "T": + d.qx = d.x + (d.x - (d.qx || d.x)); + d.qy = d.y + (d.y - (d.qy || d.y)); + pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, d.qx, d.qy, pathCommand[1], pathCommand[2])); + break; + case "Q": + d.qx = pathCommand[1]; + d.qy = pathCommand[2]; + pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[3], pathCommand[4])); + break; + case "L": + pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[1], pathCommand[2]); + break; + case "H": + pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], d.y, pathCommand[1], d.y); + break; + case "V": + pathCommand = ["C"].concat(d.x, d.y, d.x, pathCommand[1], d.x, pathCommand[1]); + break; + case "Z": + pathCommand = ["C"].concat(d.x, d.y, d.X, d.Y, d.X, d.Y); + break; + } + return pathCommand; + }, + + quadratic2curve: function (x1, y1, ax, ay, x2, y2) { + var _13 = 1 / 3, + _23 = 2 / 3; + return [ + _13 * x1 + _23 * ax, + _13 * y1 + _23 * ay, + _13 * x2 + _23 * ax, + _13 * y2 + _23 * ay, + x2, + y2 + ]; + }, + + rotate: function (x, y, rad) { + var cos = Math.cos(rad), + sin = Math.sin(rad), + X = x * cos - y * sin, + Y = x * sin + y * cos; + return {x: X, y: Y}; + }, + + arc2curve: function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) { + + + var me = this, + PI = Math.PI, + radian = me.radian, + _120 = PI * 120 / 180, + rad = radian * (+angle || 0), + res = [], + math = Math, + mcos = math.cos, + msin = math.sin, + msqrt = math.sqrt, + mabs = math.abs, + masin = math.asin, + xy, x, y, h, rx2, ry2, k, cx, cy, f1, f2, df, c1, s1, c2, s2, + t, hx, hy, m1, m2, m3, m4, newres, i, ln, f2old, x2old, y2old; + if (!recursive) { + xy = me.rotate(x1, y1, -rad); + x1 = xy.x; + y1 = xy.y; + xy = me.rotate(x2, y2, -rad); + x2 = xy.x; + y2 = xy.y; + x = (x1 - x2) / 2; + y = (y1 - y2) / 2; + h = (x * x) / (rx * rx) + (y * y) / (ry * ry); + if (h > 1) { + h = msqrt(h); + rx = h * rx; + ry = h * ry; + } + rx2 = rx * rx; + ry2 = ry * ry; + k = (large_arc_flag == sweep_flag ? -1 : 1) * + msqrt(mabs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))); + cx = k * rx * y / ry + (x1 + x2) / 2; + cy = k * -ry * x / rx + (y1 + y2) / 2; + f1 = masin(((y1 - cy) / ry).toFixed(7)); + f2 = masin(((y2 - cy) / ry).toFixed(7)); + + f1 = x1 < cx ? PI - f1 : f1; + f2 = x2 < cx ? PI - f2 : f2; + if (f1 < 0) { + f1 = PI * 2 + f1; + } + if (f2 < 0) { + f2 = PI * 2 + f2; + } + if (sweep_flag && f1 > f2) { + f1 = f1 - PI * 2; + } + if (!sweep_flag && f2 > f1) { + f2 = f2 - PI * 2; + } + } + else { + f1 = recursive[0]; + f2 = recursive[1]; + cx = recursive[2]; + cy = recursive[3]; + } + df = f2 - f1; + if (mabs(df) > _120) { + f2old = f2; + x2old = x2; + y2old = y2; + f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); + x2 = cx + rx * mcos(f2); + y2 = cy + ry * msin(f2); + res = me.arc2curve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]); + } + df = f2 - f1; + c1 = mcos(f1); + s1 = msin(f1); + c2 = mcos(f2); + s2 = msin(f2); + t = math.tan(df / 4); + hx = 4 / 3 * rx * t; + hy = 4 / 3 * ry * t; + m1 = [x1, y1]; + m2 = [x1 + hx * s1, y1 - hy * c1]; + m3 = [x2 + hx * s2, y2 - hy * c2]; + m4 = [x2, y2]; + m2[0] = 2 * m1[0] - m2[0]; + m2[1] = 2 * m1[1] - m2[1]; + if (recursive) { + return [m2, m3, m4].concat(res); + } + else { + res = [m2, m3, m4].concat(res).join().split(","); + newres = []; + ln = res.length; + for (i = 0; i < ln; i++) { + newres[i] = i % 2 ? me.rotate(res[i - 1], res[i], rad).y : me.rotate(res[i], res[i + 1], rad).x; + } + return newres; + } + }, + + + rotateAndTranslatePath: function (sprite) { + var alpha = sprite.rotation.degrees, + cx = sprite.rotation.x, + cy = sprite.rotation.y, + dx = sprite.translation.x, + dy = sprite.translation.y, + path, + i, + p, + xy, + j, + res = []; + if (!alpha && !dx && !dy) { + return this.pathToAbsolute(sprite.attr.path); + } + dx = dx || 0; + dy = dy || 0; + path = this.pathToAbsolute(sprite.attr.path); + for (i = path.length; i--;) { + p = res[i] = path[i].slice(); + if (p[0] == "A") { + xy = this.rotatePoint(p[6], p[7], alpha, cx, cy); + p[6] = xy.x + dx; + p[7] = xy.y + dy; + } else { + j = 1; + while (p[j + 1] != null) { + xy = this.rotatePoint(p[j], p[j + 1], alpha, cx, cy); + p[j] = xy.x + dx; + p[j + 1] = xy.y + dy; + j += 2; + } + } + } + return res; + }, + + + rotatePoint: function (x, y, alpha, cx, cy) { + if (!alpha) { + return { + x: x, + y: y + }; + } + cx = cx || 0; + cy = cy || 0; + x = x - cx; + y = y - cy; + alpha = alpha * this.radian; + var cos = Math.cos(alpha), + sin = Math.sin(alpha); + return { + x: x * cos - y * sin + cx, + y: x * sin + y * cos + cy + }; + }, + + pathDimensions: function (path) { + if (!path || !(path + "")) { + return {x: 0, y: 0, width: 0, height: 0}; + } + path = this.path2curve(path); + var x = 0, + y = 0, + X = [], + Y = [], + i = 0, + ln = path.length, + p, xmin, ymin, xmax, ymax, dim; + for (; i < ln; i++) { + p = path[i]; + if (p[0] == "M") { + x = p[1]; + y = p[2]; + X.push(x); + Y.push(y); + } + else { + dim = this.curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); + X = X.concat(dim.min.x, dim.max.x); + Y = Y.concat(dim.min.y, dim.max.y); + x = p[5]; + y = p[6]; + } + } + xmin = Math.min.apply(0, X); + ymin = Math.min.apply(0, Y); + xmax = Math.max.apply(0, X); + ymax = Math.max.apply(0, Y); + return { + x: Math.round(xmin), + y: Math.round(ymin), + path: path, + width: Math.round(xmax - xmin), + height: Math.round(ymax - ymin) + }; + }, + + intersectInside: function(path, cp1, cp2) { + return (cp2[0] - cp1[0]) * (path[1] - cp1[1]) > (cp2[1] - cp1[1]) * (path[0] - cp1[0]); + }, + + intersectIntersection: function(s, e, cp1, cp2) { + var p = [], + dcx = cp1[0] - cp2[0], + dcy = cp1[1] - cp2[1], + dpx = s[0] - e[0], + dpy = s[1] - e[1], + n1 = cp1[0] * cp2[1] - cp1[1] * cp2[0], + n2 = s[0] * e[1] - s[1] * e[0], + n3 = 1 / (dcx * dpy - dcy * dpx); + + p[0] = (n1 * dpx - n2 * dcx) * n3; + p[1] = (n1 * dpy - n2 * dcy) * n3; + return p; + }, + + intersect: function(subjectPolygon, clipPolygon) { + var me = this, + i = 0, + ln = clipPolygon.length, + cp1 = clipPolygon[ln - 1], + outputList = subjectPolygon, + cp2, s, e, ln2, inputList, j; + for (; i < ln; ++i) { + cp2 = clipPolygon[i]; + inputList = outputList; + outputList = []; + s = inputList[inputList.length - 1]; + j = 0; + ln2 = inputList.length; + for (; j < ln2; j++) { + e = inputList[j]; + if (me.intersectInside(e, cp1, cp2)) { + if (!me.intersectInside(s, cp1, cp2)) { + outputList.push(me.intersectIntersection(s, e, cp1, cp2)); + } + outputList.push(e); + } + else if (me.intersectInside(s, cp1, cp2)) { + outputList.push(me.intersectIntersection(s, e, cp1, cp2)); + } + s = e; + } + cp1 = cp2; + } + return outputList; + }, + + bezier : function (a, b, c, d, x) { + if (x === 0) { + return a; + } + else if (x === 1) { + return d; + } + var du = 1 - x, + d3 = du * du * du, + r = x / du; + return d3 * (a + r * (3 * b + r * (3 * c + d * r))); + }, + + bezierDim : function (a, b, c, d) { + var points = [], r, + A, top, C, delta, bottom, s, + min, max, i; + + if (a + 3 * c == d + 3 * b) { + r = a - b; + r /= 2 * (a - b - b + c); + if ( r < 1 && r > 0) { + points.push(r); + } + } else { + + + A = a - 3 * b + 3 * c - d; + top = 2 * (a - b - b + c); + C = a - b; + delta = top * top - 4 * A * C; + bottom = A + A; + if (delta === 0) { + r = top / bottom; + if (r < 1 && r > 0) { + points.push(r); + } + } else if (delta > 0) { + s = Math.sqrt(delta); + r = (s + top) / bottom; + + if (r < 1 && r > 0) { + points.push(r); + } + + r = (top - s) / bottom; + + if (r < 1 && r > 0) { + points.push(r); + } + } + } + min = Math.min(a, d); + max = Math.max(a, d); + for (i = 0; i < points.length; i++) { + min = Math.min(min, this.bezier(a, b, c, d, points[i])); + max = Math.max(max, this.bezier(a, b, c, d, points[i])); + } + return [min, max]; + }, + + curveDim: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { + var x = this.bezierDim(p1x, c1x, c2x, p2x), + y = this.bezierDim(p1y, c1y, c2y, p2y); + return { + min: { + x: x[0], + y: y[0] + }, + max: { + x: x[1], + y: y[1] + } + }; + }, + + + getAnchors: function (prevX, prevY, curX, curY, nextX, nextY, value) { + value = value || 4; + var M = Math, + PI = M.PI, + halfPI = PI / 2, + abs = M.abs, + sin = M.sin, + cos = M.cos, + atan = M.atan, + control1Length, control2Length, control1Angle, control2Angle, + control1X, control1Y, control2X, control2Y, alpha; + + + + control1Length = (curX - prevX) / value; + control2Length = (nextX - curX) / value; + + + + + + if ((curY >= prevY && curY >= nextY) || (curY <= prevY && curY <= nextY)) { + control1Angle = control2Angle = halfPI; + } else { + control1Angle = atan((curX - prevX) / abs(curY - prevY)); + if (prevY < curY) { + control1Angle = PI - control1Angle; + } + control2Angle = atan((nextX - curX) / abs(curY - nextY)); + if (nextY < curY) { + control2Angle = PI - control2Angle; + } + } + + + alpha = halfPI - ((control1Angle + control2Angle) % (PI * 2)) / 2; + if (alpha > halfPI) { + alpha -= PI; + } + control1Angle += alpha; + control2Angle += alpha; + + + control1X = curX - control1Length * sin(control1Angle); + control1Y = curY + control1Length * cos(control1Angle); + control2X = curX + control2Length * sin(control2Angle); + control2Y = curY + control2Length * cos(control2Angle); + + + + + + if ((curY > prevY && control1Y < prevY) || (curY < prevY && control1Y > prevY)) { + control1X += abs(prevY - control1Y) * (control1X - curX) / (control1Y - curY); + control1Y = prevY; + } + if ((curY > nextY && control2Y < nextY) || (curY < nextY && control2Y > nextY)) { + control2X -= abs(nextY - control2Y) * (control2X - curX) / (control2Y - curY); + control2Y = nextY; + } + + return { + x1: control1X, + y1: control1Y, + x2: control2X, + y2: control2Y + }; + }, + + + smooth: function (originalPath, value) { + var path = this.path2curve(originalPath), + newp = [path[0]], + x = path[0][1], + y = path[0][2], + j, + points, + i = 1, + ii = path.length, + beg = 1, + mx = x, + my = y, + pathi, + pathil, + pathim, + pathiml, + pathip, + pathipl, + begl; + + for (; i < ii; i++) { + pathi = path[i]; + pathil = pathi.length; + pathim = path[i - 1]; + pathiml = pathim.length; + pathip = path[i + 1]; + pathipl = pathip && pathip.length; + if (pathi[0] == "M") { + mx = pathi[1]; + my = pathi[2]; + j = i + 1; + while (path[j][0] != "C") { + j++; + } + newp.push(["M", mx, my]); + beg = newp.length; + x = mx; + y = my; + continue; + } + if (pathi[pathil - 2] == mx && pathi[pathil - 1] == my && (!pathip || pathip[0] == "M")) { + begl = newp[beg].length; + points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], mx, my, newp[beg][begl - 2], newp[beg][begl - 1], value); + newp[beg][1] = points.x2; + newp[beg][2] = points.y2; + } + else if (!pathip || pathip[0] == "M") { + points = { + x1: pathi[pathil - 2], + y1: pathi[pathil - 1] + }; + } else { + points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], pathi[pathil - 2], pathi[pathil - 1], pathip[pathipl - 2], pathip[pathipl - 1], value); + } + newp.push(["C", x, y, points.x1, points.y1, pathi[pathil - 2], pathi[pathil - 1]]); + x = points.x2; + y = points.y2; + } + return newp; + }, + + findDotAtSegment: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { + var t1 = 1 - t; + return { + x: Math.pow(t1, 3) * p1x + Math.pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + Math.pow(t, 3) * p2x, + y: Math.pow(t1, 3) * p1y + Math.pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + Math.pow(t, 3) * p2y + }; + }, + + + snapEnds: function (from, to, stepsMax, prettyNumbers) { + if (Ext.isDate(from)) { + return this.snapEndsByDate(from, to, stepsMax); + } + var step = (to - from) / stepsMax, + level = Math.floor(Math.log(step) / Math.LN10) + 1, + m = Math.pow(10, level), + cur, + floor, + modulo = Math.round((step % m) * Math.pow(10, 2 - level)), + interval = [[0, 15], [10, 1], [20, 4], [25, 2], [50, 9], [100, 15]], + stepCount = 0, + value, + weight, + i, + topValue, + topWeight = 1e9, + ln = interval.length; + + floor = Math.floor(from / m) * m; + if (from == floor && floor > 0) { + floor = Math.floor((from - (m/10)) / m) * m; + } + + if (prettyNumbers) { + for (i = 0; i < ln; i++) { + value = interval[i][0]; + weight = (value - modulo) < 0 ? 1e6 : (value - modulo) / interval[i][1]; + if (weight < topWeight) { + topValue = value; + topWeight = weight; + } + } + step = Math.floor(step * Math.pow(10, -level)) * Math.pow(10, level) + topValue * Math.pow(10, level - 2); + + if (from < 0 && to >= 0) { + cur = 0; + while (cur > from) { + cur -= step; + stepCount++; + } + from = +cur.toFixed(10); + + cur = 0; + while (cur < to) { + cur += step; + stepCount++; + } + to = +cur.toFixed(10); + } else { + cur = from = floor; + while (cur < to) { + cur += step; + stepCount++; + } + } + to = +cur.toFixed(10); + } else { + from = floor; + stepCount = stepsMax; + } + + return { + from: from, + to: to, + power: level, + step: step, + steps: stepCount + }; + }, + + + snapEndsByDate: function (from, to, stepsMax, lockEnds) { + var selectedStep = false, + scales = [ + [Ext.Date.MILLI, [1, 2, 5, 10, 20, 50, 100, 200, 250, 500]], + [Ext.Date.SECOND, [1, 2, 5, 10, 15, 30]], + [Ext.Date.MINUTE, [1, 2, 5, 10, 15, 30]], + [Ext.Date.HOUR, [1, 2, 3, 4, 6, 12]], + [Ext.Date.DAY, [1, 2, 7, 14]], + [Ext.Date.MONTH, [1, 2, 3, 6]] + ], + sLen = scales.length, + stop = false, + scale, j, yearDiff, s; + + + for (s = 0; s < sLen; s++) { + scale = scales[s]; + if (!stop) { + for (j = 0; j < scale[1].length; j++) { + if (to < Ext.Date.add(from, scale[0], scale[1][j] * stepsMax)) { + selectedStep = [scale[0], scale[1][j]]; + stop = true; + break; + } + } + } + } + + if (!selectedStep) { + yearDiff = this.snapEnds(from.getFullYear(), to.getFullYear() + 1, stepsMax, lockEnds); + selectedStep = [Date.YEAR, Math.round(yearDiff.step)]; + } + return this.snapEndsByDateAndStep(from, to, selectedStep, lockEnds); + }, + + + + + snapEndsByDateAndStep: function(from, to, step, lockEnds) { + var fromStat = [from.getFullYear(), from.getMonth(), from.getDate(), + from.getHours(), from.getMinutes(), from.getSeconds(), from.getMilliseconds()], + testFrom, testTo, date, year, month, day, fractionalMonth, stepsArray, + stepUnit = step[0], stepValue = step[1], + steps = 0; + + if (lockEnds) { + testFrom = from; + } + else { + switch (stepUnit) { + case Ext.Date.MILLI: + testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3], + fromStat[4], fromStat[5], Math.floor(fromStat[6] / stepValue) * stepValue); + break; + case Ext.Date.SECOND: + testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3], + fromStat[4], Math.floor(fromStat[5] / stepValue) * stepValue, 0); + break; + case Ext.Date.MINUTE: + testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3], + Math.floor(fromStat[4] / stepValue) * stepValue, 0, 0); + break; + case Ext.Date.HOUR: + testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], + Math.floor(fromStat[3] / stepValue) * stepValue, 0, 0, 0); + break; + case Ext.Date.DAY: + testFrom = new Date(fromStat[0], fromStat[1], + Math.floor((fromStat[2] - 1) / stepValue) * stepValue + 1, 0, 0, 0, 0); + break; + case Ext.Date.MONTH: + testFrom = new Date(fromStat[0], Math.floor(fromStat[1] / stepValue) * stepValue, 1, 0, 0, 0, 0); + steps = []; + stepsArray = true; + break; + default: + testFrom = new Date(Math.floor(fromStat[0] / stepValue) * stepValue, 0, 1, 0, 0, 0, 0); + steps = []; + stepsArray = true; + break; + } + } + + fractionalMonth = ((stepUnit === Ext.Date.MONTH) && (stepValue == 1/2 || stepValue == 1/3 || stepValue == 1/4)); + + + testTo = new Date(testFrom); + while (testTo < to) { + if (fractionalMonth) { + date = new Date(testTo); + year = date.getFullYear(); + month = date.getMonth(); + day = date.getDate(); + switch(stepValue) { + case 1/2: + if (day >= 15) { + day = 1; + if (++month > 11) { + year++; + } + } + else { + day = 15; + } + break; + + case 1/3: + if (day >= 20) { + day = 1; + if (++month > 11) { + year++; + } + } + else { + if (day >= 10) { + day = 20 + } + else { + day = 10; + } + } + break; + + case 1/4: + if (day >= 22) { + day = 1; + if (++month > 11) { + year++; + } + } + else { + if (day >= 15) { + day = 22 + } + else { + if (day >= 8) { + day = 15 + } + else { + day = 8; + } + } + } + break; + } + testTo.setYear(year); + testTo.setMonth(month); + testTo.setDate(day); + steps.push(new Date(testTo)); + } + else if (stepsArray) { + testTo = Ext.Date.add(testTo, stepUnit, stepValue); + steps.push(new Date(testTo)); + } + else { + testTo = Ext.Date.add(testTo, stepUnit, stepValue); + steps++; + } + } + + if (lockEnds) { + testTo = to; + } + + if (stepsArray) { + return { + from : +testFrom, + to : +testTo, + steps : steps + }; + } + else { + return { + from : +testFrom, + to : +testTo, + step : (testTo - testFrom) / steps, + steps : steps + }; + } + }, + + sorter: function (a, b) { + return a.offset - b.offset; + }, + + rad: function(degrees) { + return degrees % 360 * Math.PI / 180; + }, + + normalizeRadians: function(radian) { + var twoPi = 2 * Math.PI; + if (radian >= 0) { + return radian % twoPi; + } + return ((radian % twoPi) + twoPi) % twoPi; + }, + + degrees: function(radian) { + return radian * 180 / Math.PI % 360; + }, + + normalizeDegrees: function(degrees) { + if (degrees >= 0) { + return degrees % 360; + } + return ((degrees % 360) + 360) % 360; + }, + + withinBox: function(x, y, bbox) { + bbox = bbox || {}; + return (x >= bbox.x && x <= (bbox.x + bbox.width) && y >= bbox.y && y <= (bbox.y + bbox.height)); + }, + + parseGradient: function(gradient) { + var me = this, + type = gradient.type || 'linear', + angle = gradient.angle || 0, + radian = me.radian, + stops = gradient.stops, + stopsArr = [], + stop, + vector, + max, + stopObj; + + if (type == 'linear') { + vector = [0, 0, Math.cos(angle * radian), Math.sin(angle * radian)]; + max = 1 / (Math.max(Math.abs(vector[2]), Math.abs(vector[3])) || 1); + vector[2] *= max; + vector[3] *= max; + if (vector[2] < 0) { + vector[0] = -vector[2]; + vector[2] = 0; + } + if (vector[3] < 0) { + vector[1] = -vector[3]; + vector[3] = 0; + } + } + + for (stop in stops) { + if (stops.hasOwnProperty(stop) && me.stopsRE.test(stop)) { + stopObj = { + offset: parseInt(stop, 10), + color: Ext.draw.Color.toHex(stops[stop].color) || '#ffffff', + opacity: stops[stop].opacity || 1 + }; + stopsArr.push(stopObj); + } + } + + Ext.Array.sort(stopsArr, me.sorter); + if (type == 'linear') { + return { + id: gradient.id, + type: type, + vector: vector, + stops: stopsArr + }; + } + else { + return { + id: gradient.id, + type: type, + centerX: gradient.centerX, + centerY: gradient.centerY, + focalX: gradient.focalX, + focalY: gradient.focalY, + radius: gradient.radius, + vector: vector, + stops: stopsArr + }; + } + } +}); + + + +Ext.define('Ext.fx.PropertyHandler', { + + + + + + statics: { + defaultHandler: { + pixelDefaultsRE: /width|height|top$|bottom$|left$|right$/i, + unitRE: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/, + scrollRE: /^scroll/i, + + computeDelta: function(from, end, damper, initial, attr) { + damper = (typeof damper == 'number') ? damper : 1; + var unitRE = this.unitRE, + match = unitRE.exec(from), + start, units; + if (match) { + from = match[1]; + units = match[2]; + if (!this.scrollRE.test(attr) && !units && this.pixelDefaultsRE.test(attr)) { + units = 'px'; + } + } + from = +from || 0; + + match = unitRE.exec(end); + if (match) { + end = match[1]; + units = match[2] || units; + } + end = +end || 0; + start = (initial != null) ? initial : from; + return { + from: from, + delta: (end - start) * damper, + units: units + }; + }, + + get: function(from, end, damper, initialFrom, attr) { + var ln = from.length, + out = [], + i, initial, res, j, len; + for (i = 0; i < ln; i++) { + if (initialFrom) { + initial = initialFrom[i][1].from; + } + if (Ext.isArray(from[i][1]) && Ext.isArray(end)) { + res = []; + j = 0; + len = from[i][1].length; + for (; j < len; j++) { + res.push(this.computeDelta(from[i][1][j], end[j], damper, initial, attr)); + } + out.push([from[i][0], res]); + } + else { + out.push([from[i][0], this.computeDelta(from[i][1], end, damper, initial, attr)]); + } + } + return out; + }, + + set: function(values, easing) { + var ln = values.length, + out = [], + i, val, res, len, j; + for (i = 0; i < ln; i++) { + val = values[i][1]; + if (Ext.isArray(val)) { + res = []; + j = 0; + len = val.length; + for (; j < len; j++) { + res.push(val[j].from + val[j].delta * easing + (val[j].units || 0)); + } + out.push([values[i][0], res]); + } else { + out.push([values[i][0], val.from + val.delta * easing + (val.units || 0)]); + } + } + return out; + } + }, + stringHandler: { + computeDelta: function(from, end, damper, initial, attr) { + return { + from: from, + delta: end + }; + }, + + get: function(from, end, damper, initialFrom, attr) { + var ln = from.length, + out = [], + i, initial, res, j, len; + for (i = 0; i < ln; i++) { + out.push([from[i][0], this.computeDelta(from[i][1], end, damper, initial, attr)]); + } + return out; + }, + + set: function(values, easing) { + var ln = values.length, + out = [], + i, val, res, len, j; + for (i = 0; i < ln; i++) { + val = values[i][1]; + out.push([values[i][0], val.delta]); + } + return out; + } + }, + color: { + rgbRE: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i, + hexRE: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i, + hex3RE: /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i, + + parseColor : function(color, damper) { + damper = (typeof damper == 'number') ? damper : 1; + var out = false, + reList = [this.hexRE, this.rgbRE, this.hex3RE], + length = reList.length, + match, base, re, i; + + for (i = 0; i < length; i++) { + re = reList[i]; + + base = (i % 2 === 0) ? 16 : 10; + match = re.exec(color); + if (match && match.length === 4) { + if (i === 2) { + match[1] += match[1]; + match[2] += match[2]; + match[3] += match[3]; + } + out = { + red: parseInt(match[1], base), + green: parseInt(match[2], base), + blue: parseInt(match[3], base) + }; + break; + } + } + + return out || color; + }, + + computeDelta: function(from, end, damper, initial) { + from = this.parseColor(from); + end = this.parseColor(end, damper); + var start = initial ? initial : from, + tfrom = typeof start, + tend = typeof end; + + if (tfrom == 'string' || tfrom == 'undefined' + || tend == 'string' || tend == 'undefined') { + return end || start; + } + return { + from: from, + delta: { + red: Math.round((end.red - start.red) * damper), + green: Math.round((end.green - start.green) * damper), + blue: Math.round((end.blue - start.blue) * damper) + } + }; + }, + + get: function(start, end, damper, initialFrom) { + var ln = start.length, + out = [], + i, initial; + for (i = 0; i < ln; i++) { + if (initialFrom) { + initial = initialFrom[i][1].from; + } + out.push([start[i][0], this.computeDelta(start[i][1], end, damper, initial)]); + } + return out; + }, + + set: function(values, easing) { + var ln = values.length, + out = [], + i, val, parsedString, from, delta; + for (i = 0; i < ln; i++) { + val = values[i][1]; + if (val) { + from = val.from; + delta = val.delta; + + val = (typeof val == 'object' && 'red' in val)? + 'rgb(' + val.red + ', ' + val.green + ', ' + val.blue + ')' : val; + val = (typeof val == 'object' && val.length)? val[0] : val; + if (typeof val == 'undefined') { + return []; + } + parsedString = typeof val == 'string'? val : + 'rgb(' + [ + (from.red + Math.round(delta.red * easing)) % 256, + (from.green + Math.round(delta.green * easing)) % 256, + (from.blue + Math.round(delta.blue * easing)) % 256 + ].join(',') + ')'; + out.push([ + values[i][0], + parsedString + ]); + } + } + return out; + } + }, + object: { + interpolate: function(prop, damper) { + damper = (typeof damper == 'number') ? damper : 1; + var out = {}, + p; + for(p in prop) { + out[p] = parseFloat(prop[p]) * damper; + } + return out; + }, + + computeDelta: function(from, end, damper, initial) { + from = this.interpolate(from); + end = this.interpolate(end, damper); + var start = initial ? initial : from, + delta = {}, + p; + + for(p in end) { + delta[p] = end[p] - start[p]; + } + return { + from: from, + delta: delta + }; + }, + + get: function(start, end, damper, initialFrom) { + var ln = start.length, + out = [], + i, initial; + for (i = 0; i < ln; i++) { + if (initialFrom) { + initial = initialFrom[i][1].from; + } + out.push([start[i][0], this.computeDelta(start[i][1], end, damper, initial)]); + } + return out; + }, + + set: function(values, easing) { + var ln = values.length, + out = [], + outObject = {}, + i, from, delta, val, p; + for (i = 0; i < ln; i++) { + val = values[i][1]; + from = val.from; + delta = val.delta; + for (p in from) { + outObject[p] = from[p] + delta[p] * easing; + } + out.push([ + values[i][0], + outObject + ]); + } + return out; + } + }, + + path: { + computeDelta: function(from, end, damper, initial) { + damper = (typeof damper == 'number') ? damper : 1; + var start; + from = +from || 0; + end = +end || 0; + start = (initial != null) ? initial : from; + return { + from: from, + delta: (end - start) * damper + }; + }, + + forcePath: function(path) { + if (!Ext.isArray(path) && !Ext.isArray(path[0])) { + path = Ext.draw.Draw.parsePathString(path); + } + return path; + }, + + get: function(start, end, damper, initialFrom) { + var endPath = this.forcePath(end), + out = [], + startLn = start.length, + startPathLn, pointsLn, i, deltaPath, initial, j, k, path, startPath; + for (i = 0; i < startLn; i++) { + startPath = this.forcePath(start[i][1]); + + deltaPath = Ext.draw.Draw.interpolatePaths(startPath, endPath); + startPath = deltaPath[0]; + endPath = deltaPath[1]; + + startPathLn = startPath.length; + path = []; + for (j = 0; j < startPathLn; j++) { + deltaPath = [startPath[j][0]]; + pointsLn = startPath[j].length; + for (k = 1; k < pointsLn; k++) { + initial = initialFrom && initialFrom[0][1][j][k].from; + deltaPath.push(this.computeDelta(startPath[j][k], endPath[j][k], damper, initial)); + } + path.push(deltaPath); + } + out.push([start[i][0], path]); + } + return out; + }, + + set: function(values, easing) { + var ln = values.length, + out = [], + i, j, k, newPath, calcPath, deltaPath, deltaPathLn, pointsLn; + for (i = 0; i < ln; i++) { + deltaPath = values[i][1]; + newPath = []; + deltaPathLn = deltaPath.length; + for (j = 0; j < deltaPathLn; j++) { + calcPath = [deltaPath[j][0]]; + pointsLn = deltaPath[j].length; + for (k = 1; k < pointsLn; k++) { + calcPath.push(deltaPath[j][k].from + deltaPath[j][k].delta * easing); + } + newPath.push(calcPath.join(',')); + } + out.push([values[i][0], newPath.join(',')]); + } + return out; + } + } + + } +}, function() { + + var props = [ + 'outlineColor', + 'backgroundColor', + 'borderColor', + 'borderTopColor', + 'borderRightColor', + 'borderBottomColor', + 'borderLeftColor', + 'fill', + 'stroke' + ], + length = props.length, + i = 0, + prop; + + for (; i= duration) { + elapsedTime = duration; + lastFrame = true; + } + if (me.reverse) { + elapsedTime = duration - elapsedTime; + } + + for (attr in attrs) { + if (attrs.hasOwnProperty(attr)) { + values = attrs[attr]; + easing = lastFrame ? 1 : easingFn(elapsedTime / duration); + ret[attr] = propHandlers[attr].set(values, easing); + } + } + me.frameCount++; + + return ret; + }, + + + lastFrame: function() { + var me = this, + iter = me.iterations, + iterCount = me.currentIteration; + + iterCount++; + if (iterCount < iter) { + if (me.alternate) { + me.reverse = !me.reverse; + } + me.startTime = new Date(); + me.currentIteration = iterCount; + + me.paused = false; + } + else { + me.currentIteration = 0; + me.end(); + me.fireEvent('lastframe', me, me.startTime); + } + }, + + endWasCalled: 0, + + + end: function() { + var me = this; + if (me.endWasCalled++) { + return; + } + + me.startTime = 0; + me.paused = false; + me.running = false; + Ext.fx.Manager.removeAnim(me); + me.fireEvent('afteranimate', me, me.startTime); + Ext.callback(me.callback, me.scope, [me, me.startTime]); + if (me.remove) { + me.target.remove(); + } + }, + + isReady: function() { + return this.paused === false && this.running === false && this.iterations > 0; + }, + + isRunning: function() { + return this.paused === false && this.running === true && this.isAnimator !== true; + } +}); + +Ext.enableFx = true; + + + +Ext.define('Ext.util.Animate', { + + + + + + + + isAnimate: true, + + + animate: function(animObj) { + var me = this; + if (Ext.fx.Manager.hasFxBlock(me.id)) { + return me; + } + Ext.fx.Manager.queueFx(new Ext.fx.Anim(me.anim(animObj))); + return this; + }, + + + anim: function(config) { + if (!Ext.isObject(config)) { + return (config) ? {} : false; + } + + var me = this; + + if (config.stopAnimation) { + me.stopAnimation(); + } + + Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id)); + + return Ext.apply({ + target: me, + paused: true + }, config); + }, + + + getAnimationProps: function() { + var me = this, + layout = me.layout; + + return layout && layout.animate ? layout.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); + } +}, function(){ + + Ext.applyIf(Ext.Element.prototype, this.prototype); + + Ext.CompositeElementLite.importElementMethods(); +}); + + + +Ext.define('Ext.util.ElementContainer', { + + childEls: [ + + + + + ], + + constructor: function () { + var me = this, + childEls; + + + + if (me.hasOwnProperty('childEls')) { + childEls = me.childEls; + delete me.childEls; + + me.addChildEls.apply(me, childEls); + } + }, + + destroy: function () { + var me = this, + childEls = me.getChildEls(), + child, childName, i, k; + + for (i = childEls.length; i--; ) { + childName = childEls[i]; + if (typeof childName != 'string') { + childName = childName.name; + } + + child = me[childName]; + if (child) { + me[childName] = null; + child.remove(); + } + } + }, + + + addChildEls: function () { + var me = this, + args = arguments; + + if (me.hasOwnProperty('childEls')) { + me.childEls.push.apply(me.childEls, args); + } else { + me.childEls = me.getChildEls().concat(Array.prototype.slice.call(args)); + } + + me.prune(me.childEls, false); + }, + + + applyChildEls: function(el, id) { + var me = this, + childEls = me.getChildEls(), + baseId, childName, i, selector, value; + + baseId = (id || me.id) + '-'; + for (i = childEls.length; i--; ) { + childName = childEls[i]; + + if (typeof childName == 'string') { + + + value = el.getById(baseId + childName); + } else { + if ((selector = childName.select)) { + value = Ext.select(selector, true, el.dom); + } else if ((selector = childName.selectNode)) { + value = Ext.get(Ext.DomQuery.selectNode(selector, el.dom)); + } else { + + value = el.getById(childName.id || (baseId + childName.itemId)); + } + + childName = childName.name; + } + + me[childName] = value; + } + }, + + getChildEls: function () { + var me = this, + self; + + + if (me.hasOwnProperty('childEls')) { + return me.childEls; + } + + + + self = me.self; + return self.$childEls || me.getClassChildEls(self); + }, + + getClassChildEls: function (cls) { + var me = this, + result = cls.$childEls, + childEls, i, length, forked, mixin, mixins, name, parts, proto, supr, superMixins; + + if (!result) { + + + + + supr = cls.superclass; + if (supr) { + supr = supr.self; + parts = [supr.$childEls || me.getClassChildEls(supr)]; + superMixins = supr.prototype.mixins || {}; + } else { + parts = []; + superMixins = {}; + } + + proto = cls.prototype; + mixins = proto.mixins; + for (name in mixins) { + if (mixins.hasOwnProperty(name) && !superMixins.hasOwnProperty(name)) { + mixin = mixins[name].self; + parts.push(mixin.$childEls || me.getClassChildEls(mixin)); + } + } + + parts.push(proto.hasOwnProperty('childEls') && proto.childEls); + + for (i = 0, length = parts.length; i < length; ++i) { + childEls = parts[i]; + if (childEls && childEls.length) { + if (!result) { + result = childEls; + } else { + if (!forked) { + forked = true; + result = result.slice(0); + } + result.push.apply(result, childEls); + } + } + } + + cls.$childEls = result = (result ? me.prune(result, !forked) : []); + } + + return result; + }, + + prune: function (childEls, shared) { + var index = childEls.length, + map = {}, + name; + + while (index--) { + name = childEls[index]; + if (typeof name != 'string') { + name = name.name; + } + + if (!map[name]) { + map[name] = 1; + } else { + if (shared) { + shared = false; + childEls = childEls.slice(0); + } + Ext.Array.erase(childEls, index, 1); + } + } + + return childEls; + }, + + + removeChildEls: function (testFn) { + var me = this, + old = me.getChildEls(), + keepers = (me.childEls = []), + n, i, cel; + + for (i = 0, n = old.length; i < n; ++i) { + cel = old[i]; + if (!testFn(cel)) { + keepers.push(cel); + } + } + } +}); + + + +Ext.define('Ext.util.Renderable', { + + + + + frameCls: Ext.baseCSSPrefix + '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);%}' + ], + + + afterRender : function() { + var me = this, + data = {}, + protoEl = me.protoEl, + target = me.el, + item, pre, hide, contentEl; + + me.finishRenderChildren(); + + + + if (me.contentEl) { + pre = Ext.baseCSSPrefix; + hide = pre + 'hide-'; + contentEl = Ext.get(me.contentEl); + contentEl.removeCls([pre+'hidden', hide+'display', hide+'offsets', hide+'nosize']); + me.getContentTarget().appendChild(contentEl.dom); + } + + protoEl.writeTo(data); + + + + + item = data.removed; + if (item) { + target.removeCls(item); + } + + item = data.cls; + if (item.length) { + target.addCls(item); + } + + item = data.style; + if (data.style) { + target.setStyle(item); + } + + me.protoEl = null; + + + if (!me.ownerCt) { + me.updateLayout(); + } + }, + + afterFirstLayout : function(width, height) { + var me = this, + x = me.x, + y = me.y, + hasX, + hasY, + pos, xy, + alignSpec = me.defaultAlign, + alignOffset = me.alignOffset; + + + + if (!me.ownerLayout) { + hasX = Ext.isDefined(x); + hasY = Ext.isDefined(y); + } + + + + if (me.floating && (!hasX || !hasY)) { + if (me.floatParent) { + pos = me.floatParent.getTargetEl().getViewRegion(); + xy = me.el.getAlignToXY(me.alignTarget || me.floatParent.getTargetEl(), alignSpec, alignOffset); + pos.x = xy[0] - pos.x; + pos.y = xy[1] - pos.y; + } else { + xy = me.el.getAlignToXY(me.alignTarget || me.container, alignSpec, alignOffset); + pos = me.container.translateXY(xy[0], xy[1]); + } + x = hasX ? x : pos.x; + y = hasY ? y : pos.y; + hasX = hasY = true; + } + + if (hasX || hasY) { + me.setPosition(x, y); + } + me.onBoxReady(width, height); + }, + + + applyRenderSelectors: function() { + var me = this, + selectors = me.renderSelectors, + el = me.el, + dom = el.dom, + selector; + + me.applyChildEls(el); + + + + + if (selectors) { + for (selector in selectors) { + if (selectors.hasOwnProperty(selector) && selectors[selector]) { + me[selector] = Ext.get(Ext.DomQuery.selectNode(selectors[selector], dom)); + } + } + } + }, + + beforeRender: function () { + var me = this, + target = me.getTargetEl(), + overflowEl = me.getOverflowEl(), + layout = me.getComponentLayout(), + + overflowStyle = me.getOverflowStyle(); + + + me.frame = me.frame || me.alwaysFramed; + + if (!layout.initialized) { + layout.initLayout(); + } + + + + if (overflowEl) { + overflowEl.setStyle(overflowStyle); + me.overflowStyleSet = true; + } + + me.setUI(me.ui); + + if (me.disabled) { + + me.disable(true); + } + }, + + + doApplyRenderTpl: function(out, values) { + + + + var me = values.$comp, + tpl; + + + if (!me.rendered) { + tpl = me.initRenderTpl(); + tpl.applyOut(values.renderData, out); + } + }, + + + doAutoRender: function() { + var me = this; + if (!me.rendered) { + if (me.floating) { + me.render(me.renderTo || document.body); + } else { + me.render(Ext.isBoolean(me.autoRender) ? Ext.getBody() : me.autoRender); + } + } + }, + + doRenderContent: function (out, renderData) { + + + + var me = renderData.$comp; + + if (me.html) { + Ext.DomHelper.generateMarkup(me.html, out); + delete me.html; + } + + if (me.tpl) { + + if (!me.tpl.isTemplate) { + me.tpl = new Ext.XTemplate(me.tpl); + } + + if (me.data) { + + me.tpl.applyOut(me.data, out); + delete me.data; + } + } + }, + + doRenderFramingDockedItems: function (out, renderData, after) { + + + + var me = renderData.$comp; + + + + if (!me.rendered && me.doRenderDockedItems) { + + + renderData.renderData.$skipDockedItems = true; + + + + me.doRenderDockedItems.call(this, out, renderData, after); + } + }, + + + finishRender: function(containerIdx) { + var me = this, + tpl, data, el; + + + + + + + + + + if (!me.el || me.$pid) { + if (me.container) { + el = me.container.getById(me.id, true); + } else { + el = Ext.getDom(me.id); + } + + if (!me.el) { + + me.wrapPrimaryEl(el); + } else { + + + delete me.$pid; + + if (!me.el.dom) { + + me.wrapPrimaryEl(me.el); + } + el.parentNode.insertBefore(me.el.dom, el); + Ext.removeNode(el); + + } + } else if (!me.rendering) { + + + + tpl = me.initRenderTpl(); + if (tpl) { + data = me.initRenderData(); + tpl.insertFirst(me.getTargetEl(), data); + } + } + + + if (!me.container) { + + me.container = Ext.get(me.el.dom.parentNode); + } + + if (me.ctCls) { + me.container.addCls(me.ctCls); + } + + + me.onRender(me.container, containerIdx); + + + if (!me.overflowStyleSet) { + me.getOverflowEl().setStyle(me.getOverflowStyle()); + } + + + + me.el.setVisibilityMode(Ext.Element[me.hideMode.toUpperCase()]); + + if (me.overCls) { + me.el.hover(me.addOverCls, me.removeOverCls, me); + } + + if (me.hasListeners.render) { + me.fireEvent('render', me); + } + + me.afterRender(); + if (me.hasListeners.afterrender) { + me.fireEvent('afterrender', me); + } + me.initEvents(); + + if (me.hidden) { + + + + me.el.hide(); + } + }, + + finishRenderChildren: function () { + var layout = this.getComponentLayout(); + + layout.finishRender(); + }, + + getElConfig : function() { + var me = this, + autoEl = me.autoEl, + frameInfo = me.getFrameInfo(), + config = { + tag: 'div', + tpl: frameInfo ? me.initFramingTpl(frameInfo.table) : me.initRenderTpl() + }, + protoEl = me.protoEl, + i, frameElNames, len, suffix, frameGenId, frameData; + + me.initStyles(protoEl); + protoEl.writeTo(config); + protoEl.flush(); + + if (Ext.isString(autoEl)) { + config.tag = autoEl; + } else { + Ext.apply(config, autoEl); + } + + + config.id = me.id; + + if (config.tpl) { + + if (frameInfo) { + frameElNames = me.frameElNames; + len = frameElNames.length; + + config.tplData = frameData = me.getFrameRenderData(); + frameData.renderData = me.initRenderData(); + frameGenId = frameData.fgid; + + + for (i = 0; i < len; i++) { + suffix = frameElNames[i]; + me.addChildEls({ name: 'frame' + suffix, id: frameGenId + suffix }); + } + + + me.addChildEls({ + name: 'frameBody', + id: frameGenId + 'MC' + }); + } else { + config.tplData = me.initRenderData(); + } + } + + return config; + }, + + + + initFramingTpl: function(table) { + var tpl = this.getFrameTpl(table); + + if (tpl && !tpl.applyRenderTpl) { + this.setupFramingTpl(tpl); + } + + return tpl; + }, + + + setupFramingTpl: function(frameTpl) { + frameTpl.applyRenderTpl = this.doApplyRenderTpl; + frameTpl.renderDockedItems = this.doRenderFramingDockedItems; + }, + + + getInsertPosition: function(position) { + + if (position !== undefined) { + if (Ext.isNumber(position)) { + position = this.container.dom.childNodes[position]; + } + else { + position = Ext.getDom(position); + } + } + + return position; + }, + + getRenderTree: function() { + var me = this; + + if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) { + me.beforeRender(); + + + + me.rendering = true; + + if (me.el) { + + + + return { + tag: 'div', + role: 'presentation', + id: (me.$pid = Ext.id()) + }; + } + + return me.getElConfig(); + } + + return null; + }, + + initContainer: function(container) { + var me = this; + + + + + if (!container && me.el) { + container = me.el.dom.parentNode; + me.allowDomMove = false; + } + me.container = container.dom ? container : Ext.get(container); + + return me.container; + }, + + + initRenderData: function() { + var me = this; + + return Ext.apply({ + $comp: me, + id: me.id, + ui: me.ui, + uiCls: me.uiCls, + baseCls: me.baseCls, + componentCls: me.componentCls, + frame: me.frame, + role: me.ariaRole, + childElCls: '' + }, me.renderData); + }, + + + initRenderTpl: function() { + var tpl = this.getTpl('renderTpl'); + + if (tpl && !tpl.renderContent) { + this.setupRenderTpl(tpl); + } + + return tpl; + }, + + + onRender: function(parentNode, containerIdx) { + var me = this, + x = me.x, + y = me.y, + lastBox = null, + width, height, + el = me.el; + + me.applyRenderSelectors(); + + + + me.rendering = null; + + me.rendered = true; + + + if (x != null) { + lastBox = {x:x}; + } + if (y != null) { + (lastBox = lastBox || {}).y = y; + } + + + + if (!me.getFrameInfo() && Ext.isBorderBox) { + width = me.width; + height = me.height; + + if (typeof width === 'number') { + lastBox = lastBox || {}; + lastBox.width = width; + } + if (typeof height === 'number') { + lastBox = lastBox || {}; + lastBox.height = height; + } + } + + me.lastBox = el.lastBox = lastBox; + }, + + + render: function(container, position) { + var me = this, + el = me.el && (me.el = Ext.get(me.el)), + vetoed, + tree, + nextSibling; + + Ext.suspendLayouts(); + + container = me.initContainer(container); + + nextSibling = me.getInsertPosition(position); + + if (!el) { + tree = me.getRenderTree(); + if (me.ownerLayout && me.ownerLayout.transformItemRenderTree) { + tree = me.ownerLayout.transformItemRenderTree(tree); + } + + + if (tree) { + if (nextSibling) { + el = Ext.DomHelper.insertBefore(nextSibling, tree); + } else { + el = Ext.DomHelper.append(container, tree); + } + + me.wrapPrimaryEl(el); + } + } else { + if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) { + me.beforeRender(); + + me.initStyles(el); + if (me.allowDomMove !== false) { + if (nextSibling) { + container.dom.insertBefore(el.dom, nextSibling); + } else { + container.dom.appendChild(el.dom); + } + } + } else { + vetoed = true; + } + } + + if (el && !vetoed) { + me.finishRender(position); + } + + Ext.resumeLayouts(!me.hidden && !container.isDetachedBody); + }, + + + ensureAttachedToBody: function (runLayout) { + var comp = this, + body; + + while (comp.ownerCt) { + comp = comp.ownerCt; + } + + if (comp.container.isDetachedBody) { + comp.container = body = Ext.getBody(); + body.appendChild(comp.el.dom); + if (runLayout) { + comp.updateLayout(); + } + if (typeof comp.x == 'number' || typeof comp.y == 'number') { + comp.setPosition(comp.x, comp.y); + } + } + }, + + setupRenderTpl: function (renderTpl) { + renderTpl.renderBody = renderTpl.renderContent = this.doRenderContent; + }, + + wrapPrimaryEl: function (dom) { + this.el = Ext.get(dom, true); + }, + + + initFrame : function() { + if (Ext.supports.CSS3BorderRadius || !this.frame) { + return; + } + + var me = this, + frameInfo = me.getFrameInfo(), + frameTpl, frameGenId, + frameElNames = me.frameElNames, + len = frameElNames.length, + i, frameData, suffix; + + if (frameInfo) { + frameTpl = me.getFrameTpl(frameInfo.table); + frameData = me.getFrameRenderData(); + frameGenId = frameData.fgid; + + + + frameTpl.insertFirst(me.el, frameData); + + + + me.frameBody = me.el.down('.' + me.frameCls + '-mc'); + + + me.removeChildEls(function (c) { + return c.id && me.frameIdRegex.test(c.id); + }); + + + for (i = 0; i < len; i++) { + suffix = frameElNames[i]; + me['frame' + suffix] = me.el.getById(frameGenId + suffix); + } + } + }, + + getFrameRenderData: function () { + var me = this, + + frameInfo = me.frameSize, + frameGenId = (me.frameGenId || 0) + 1; + + + + me.frameGenId = frameGenId; + + return { + $comp: me, + fgid: me.id + '-frame' + frameGenId, + ui: me.ui, + uiCls: me.uiCls, + frameCls: me.frameCls, + baseCls: me.baseCls, + top: !!frameInfo.top, + left: !!frameInfo.left, + right: !!frameInfo.right, + bottom: !!frameInfo.bottom, + + + frameElCls: '' + }; + }, + + updateFrame: function() { + if (Ext.supports.CSS3BorderRadius || !this.frame) { + return; + } + + var me = this, + wasTable = me.frameSize && me.frameSize.table, + oldFrameTL = me.frameTL, + oldFrameBL = me.frameBL, + oldFrameML = me.frameML, + oldFrameMC = me.frameMC, + newMCClassName; + + me.initFrame(); + + if (oldFrameMC) { + if (me.frame) { + + + newMCClassName = me.frameMC.dom.className; + + + + oldFrameMC.insertAfter(me.frameMC); + me.frameMC.remove(); + + + me.frameBody = me.frameMC = oldFrameMC; + + + oldFrameMC.dom.className = newMCClassName; + + + if (wasTable) { + me.el.query('> table')[1].remove(); + } + else { + if (oldFrameTL) { + oldFrameTL.remove(); + } + if (oldFrameBL) { + oldFrameBL.remove(); + } + if (oldFrameML) { + oldFrameML.remove(); + } + } + } + } + else if (me.frame) { + me.applyRenderSelectors(); + } + }, + + + getFrameInfo: function() { + + if (Ext.supports.CSS3BorderRadius || !this.frame) { + return false; + } + + var me = this, + frameInfoCache = me.frameInfoCache, + cls = me.getFramingInfoCls() + '-frameInfo', + frameInfo = frameInfoCache[cls], + max = Math.max, + styleEl, match, info, frameTop, frameRight, frameBottom, frameLeft, + borderWidthT, borderWidthR, borderWidthB, borderWidthL, + paddingT, paddingR, paddingB, paddingL, + borderRadiusTL, borderRadiusTR, borderRadiusBR, borderRadiusBL; + + if (frameInfo == null) { + + styleEl = Ext.fly(me.getStyleProxy(cls), 'frame-style-el'); + info = styleEl.getStyle('font-family'); + + if (info) { + + + + + + + + + + + + + + + + + + info = info.split('-'); + + borderRadiusTL = parseInt(info[1], 10); + borderRadiusTR = parseInt(info[2], 10); + borderRadiusBR = parseInt(info[3], 10); + borderRadiusBL = parseInt(info[4], 10); + borderWidthT = parseInt(info[5], 10); + borderWidthR = parseInt(info[6], 10); + borderWidthB = parseInt(info[7], 10); + borderWidthL = parseInt(info[8], 10); + paddingT = parseInt(info[9], 10); + paddingR = parseInt(info[10], 10); + paddingB = parseInt(info[11], 10); + paddingL = parseInt(info[12], 10); + + + + frameTop = max(borderWidthT, max(borderRadiusTL, borderRadiusTR)); + frameRight = max(borderWidthR, max(borderRadiusTR, borderRadiusBR)); + frameBottom = max(borderWidthB, max(borderRadiusBL, borderRadiusBR)); + frameLeft = max(borderWidthL, max(borderRadiusTL, borderRadiusBL)); + + frameInfo = { + table: info[0].charAt(0) === 't', + vertical: info[0].charAt(1) === 'v', + + top: frameTop, + right: frameRight, + bottom: frameBottom, + left: frameLeft, + + width: frameLeft + frameRight, + height: frameTop + frameBottom, + + maxWidth: max(frameTop, frameRight, frameBottom, frameLeft), + + border: { + top: borderWidthT, + right: borderWidthR, + bottom: borderWidthB, + left: borderWidthL, + width: borderWidthL + borderWidthR, + height: borderWidthT + borderWidthB + }, + padding: { + top: paddingT, + right: paddingR, + bottom: paddingB, + left: paddingL, + width: paddingL + paddingR, + height: paddingT + paddingB + }, + radius: { + tl: borderRadiusTL, + tr: borderRadiusTR, + br: borderRadiusBR, + bl: borderRadiusBL + } + }; + } else { + frameInfo = false; + } + + + + if (me.frame === true && !frameInfo) { + Ext.log.error('You have set frame: true explicity on this component (' + me.getXType() + ') and it ' + + 'does not have any framing defined in the CSS template. In this case IE cannot figure out ' + + 'what sizes to use and thus framing on this component will be disabled.'); + } + + frameInfoCache[cls] = frameInfo; + } + + me.frame = !!frameInfo; + me.frameSize = frameInfo; + + return frameInfo; + }, + + getFramingInfoCls: function(){ + return this.baseCls + '-' + this.ui; + }, + + + getStyleProxy: function(cls) { + var result = this.styleProxyEl || (Ext.AbstractComponent.prototype.styleProxyEl = Ext.getBody().createChild({ + + 'data-sticky': true, + role: 'presentation', + style: { + position: 'absolute', + top: '-10000px' + } + }, null, true)); + + result.className = cls; + return result; + }, + + + getFrameTpl : function(table) { + return this.getTpl(table ? 'frameTableTpl' : 'frameTpl'); + }, + + + frameInfoCache: {} +}); + + + +Ext.define('Ext.state.Provider', { + mixins: { + observable: Ext.util.Observable + }, + + + prefix: 'ext-', + + constructor : function(config){ + config = config || {}; + var me = this; + Ext.apply(me, config); + + me.addEvents("statechange"); + me.state = {}; + me.mixins.observable.constructor.call(me); + }, + + + get : function(name, defaultValue){ + return typeof this.state[name] == "undefined" ? + defaultValue : this.state[name]; + }, + + + clear : function(name){ + var me = this; + delete me.state[name]; + me.fireEvent("statechange", me, name, null); + }, + + + set : function(name, value){ + var me = this; + me.state[name] = value; + me.fireEvent("statechange", me, name, value); + }, + + + decodeValue : function(value){ + + + + + + + + + + var me = this, + re = /^(a|n|d|b|s|o|e)\:(.*)$/, + matches = re.exec(unescape(value)), + all, + type, + keyValue, + values, + vLen, + v; + + if(!matches || !matches[1]){ + return; + } + + type = matches[1]; + value = matches[2]; + switch (type) { + case 'e': + return null; + case 'n': + return parseFloat(value); + case 'd': + return new Date(Date.parse(value)); + case 'b': + return (value == '1'); + case 'a': + all = []; + if(value != ''){ + values = value.split('^'); + vLen = values.length; + + for (v = 0; v < vLen; v++) { + value = values[v]; + all.push(me.decodeValue(value)); + } + } + return all; + case 'o': + all = {}; + if(value != ''){ + values = value.split('^'); + vLen = values.length; + + for (v = 0; v < vLen; v++) { + value = values[v]; + keyValue = value.split('='); + all[keyValue[0]] = me.decodeValue(keyValue[1]); + } + } + return all; + default: + return value; + } + }, + + + encodeValue : function(value){ + var flat = '', + i = 0, + enc, + len, + key; + + if (value == null) { + return 'e:1'; + } else if(typeof value == 'number') { + enc = 'n:' + value; + } else if(typeof value == 'boolean') { + enc = 'b:' + (value ? '1' : '0'); + } else if(Ext.isDate(value)) { + enc = 'd:' + value.toUTCString(); + } else if(Ext.isArray(value)) { + for (len = value.length; i < len; i++) { + flat += this.encodeValue(value[i]); + if (i != len - 1) { + flat += '^'; + } + } + enc = 'a:' + flat; + } else if (typeof value == 'object') { + for (key in value) { + if (typeof value[key] != 'function' && value[key] !== undefined) { + flat += key + '=' + this.encodeValue(value[key]) + '^'; + } + } + enc = 'o:' + flat.substring(0, flat.length-1); + } else { + enc = 's:' + value; + } + return escape(enc); + } +}); + + + +Ext.define('Ext.state.Manager', { + singleton: true, + + constructor: function() { + this.provider = new Ext.state.Provider(); + }, + + + + setProvider : function(stateProvider){ + this.provider = stateProvider; + }, + + + get : function(key, defaultValue){ + return this.provider.get(key, defaultValue); + }, + + + set : function(key, value){ + this.provider.set(key, value); + }, + + + clear : function(key){ + this.provider.clear(key); + }, + + + getProvider : function(){ + return this.provider; + } +}); + + + +Ext.define('Ext.state.Stateful', { + + + + mixins: { + observable: Ext.util.Observable + }, + + + + + + + stateful: false, + + + + + + + saveDelay: 100, + + constructor: function(config) { + var me = this; + + config = config || {}; + if (config.stateful !== undefined) { + me.stateful = config.stateful; + } + if (config.saveDelay !== undefined) { + me.saveDelay = config.saveDelay; + } + me.stateId = me.stateId || config.stateId; + + if (!me.stateEvents) { + me.stateEvents = []; + } + if (config.stateEvents) { + me.stateEvents.concat(config.stateEvents); + } + this.addEvents( + + 'beforestaterestore', + + + 'staterestore', + + + 'beforestatesave', + + + 'statesave' + ); + me.mixins.observable.constructor.call(me); + + if (me.stateful !== false) { + me.addStateEvents(me.stateEvents); + me.initState(); + } + }, + + + addStateEvents: function (events) { + var me = this, + i, event, stateEventsByName, + eventArray; + + if (me.stateful && me.getStateId()) { + eventArray = (typeof events == 'string') ? arguments : events; + + stateEventsByName = me.stateEventsByName || (me.stateEventsByName = {}); + + for (i = eventArray.length; i--; ) { + event = eventArray[i]; + + if (!stateEventsByName[event]) { + stateEventsByName[event] = 1; + me.on(event, me.onStateChange, me); + } + } + } + }, + + + onStateChange: function(){ + var me = this, + delay = me.saveDelay, + statics, runner; + + if (!me.stateful) { + return; + } + + if (delay) { + if (!me.stateTask) { + statics = Ext.state.Stateful; + runner = statics.runner || (statics.runner = new Ext.util.TaskRunner()); + + me.stateTask = runner.newTask({ + run: me.saveState, + scope: me, + interval: delay, + repeat: 1 + }); + } + + me.stateTask.start(); + } else { + me.saveState(); + } + }, + + + saveState: function() { + var me = this, + id = me.stateful && me.getStateId(), + hasListeners = me.hasListeners, + state; + + if (id) { + state = me.getState() || {}; + if (!hasListeners.beforestatesave || me.fireEvent('beforestatesave', me, state) !== false) { + Ext.state.Manager.set(id, state); + if (hasListeners.statesave) { + me.fireEvent('statesave', me, state); + } + } + } + }, + + + getState: function(){ + return null; + }, + + + applyState: function(state) { + if (state) { + Ext.apply(this, state); + } + }, + + + getStateId: function() { + var me = this; + return me.stateId || (me.autoGenId ? null : me.id); + }, + + + initState: function(){ + var me = this, + id = me.stateful && me.getStateId(), + hasListeners = me.hasListeners, + state; + + if (id) { + state = Ext.state.Manager.get(id); + if (state) { + state = Ext.apply({}, state); + if (!hasListeners.beforestaterestore || me.fireEvent('beforestaterestore', me, state) !== false) { + me.applyState(state); + if (hasListeners.staterestore) { + me.fireEvent('staterestore', me, state); + } + } + } + } + }, + + + savePropToState: function (propName, state, stateName) { + var me = this, + value = me[propName], + config = me.initialConfig; + + if (me.hasOwnProperty(propName)) { + if (!config || config[propName] !== value) { + if (state) { + state[stateName || propName] = value; + } + return true; + } + } + return false; + }, + + + savePropsToState: function (propNames, state) { + var me = this, + i, n; + + if (typeof propNames == 'string') { + me.savePropToState(propNames, state); + } else { + for (i = 0, n = propNames.length; i < n; ++i) { + me.savePropToState(propNames[i], state); + } + } + + return state; + }, + + + destroy: function(){ + var me = this, + task = me.stateTask; + + if (task) { + task.destroy(); + me.stateTask = null; + } + + me.clearListeners(); + } +}); + + + + +Ext.define('Ext.AbstractComponent', { + + + + + + + + + + + mixins: { + positionable: Ext.util.Positionable , + observable: Ext.util.Observable , + animate: Ext.util.Animate , + elementCt: Ext.util.ElementContainer , + renderable: Ext.util.Renderable , + state: Ext.state.Stateful + }, + + + + + + + + + + + + + + + + + + statics: { + AUTO_ID: 1000, + + pendingLayouts: null, + + layoutSuspendCount: 0, + + + cancelLayout: function(comp, isDestroying) { + var context = this.runningLayoutContext || this.pendingLayouts; + + if (context) { + context.cancelComponent(comp, false, isDestroying); + } + }, + + + findComponentByElement: function(node) { + var topmost = document.body, + target = node, + cmp; + + while (target && target.nodeType === 1 && target !== topmost) { + cmp = Ext.getCmp(target.id); + + if (cmp) { + return cmp; + } + + target = target.parentNode; + } + + return null; + }, + + + getComponentByElement: function(el) { + var cmpIdAttr = Ext.Component.componentIdAttribute, + cmpId; + + el = Ext.fly(el); + + if (!el) { + return null; + } + + cmpId = el.getAttribute(cmpIdAttr); + + if (cmpId) { + return Ext.getCmp(cmpId); + } + else { + return Ext.Component.findComponentByElement(el.dom); + } + }, + + + flushLayouts: function () { + var me = this, + context = me.pendingLayouts; + + if (context && context.invalidQueue.length) { + me.pendingLayouts = null; + me.runningLayoutContext = context; + + Ext.override(context, { + runComplete: function () { + + + + me.runningLayoutContext = null; + + var result = this.callParent(); + if (Ext.globalEvents.hasListeners.afterlayout) { + Ext.globalEvents.fireEvent('afterlayout'); + } + return result; + } + }); + + context.run(); + } + }, + + + resumeLayouts: function (flush) { + if (this.layoutSuspendCount && ! --this.layoutSuspendCount) { + if (flush) { + this.flushLayouts(); + } + if (Ext.globalEvents.hasListeners.resumelayouts) { + Ext.globalEvents.fireEvent('resumelayouts'); + } + } + }, + + + suspendLayouts: function () { + ++this.layoutSuspendCount; + }, + + + updateLayout: function (comp, defer) { + var me = this, + running = me.runningLayoutContext, + pending; + + if (running) { + running.queueInvalidate(comp); + } else { + pending = me.pendingLayouts || (me.pendingLayouts = new Ext.layout.Context()); + pending.queueInvalidate(comp); + + if (!defer && !me.layoutSuspendCount && !comp.isLayoutSuspended()) { + me.flushLayouts(); + } + } + } + }, + + + + + isComponent: true, + + + getAutoId: function() { + this.autoGenId = true; + return ++Ext.AbstractComponent.AUTO_ID; + }, + + deferLayouts: false, + + + + + autoGenId: false, + + + + + + + + + renderTpl: '{%this.renderContent(out,values)%}', + + + + + + + + + maskElement: null, + + + + + + + frameSize: null, + + + + + + + + + + + tplWriteMode: 'overwrite', + + + baseCls: Ext.baseCSSPrefix + 'component', + + + + + + + + + disabledCls: Ext.baseCSSPrefix + 'item-disabled', + + + ui: 'default', + + + uiCls: [], + + + + + + + + + + + + + + + hidden: false, + + + disabled: false, + + + disabledRe: /^(?:button|input|select|textarea|optgroup|option|fieldset)$/i, + + nonMaskableRe: (function () { + var re = ['input', 'select', 'textarea', 'optgroup', 'option', 'table']; + + + if (Ext.isIE9m && !(Ext.isIE9 && !Ext.isIEQuirks)) { + + + re.push('p'); + } + + return new RegExp('^(?:' + re.join('|') + ')$', 'i'); + }()), + + + + + draggable: false, + + + floating: false, + + + hideMode: 'display', + + + + + + + + + + + + + + + + autoShow: false, + + + autoRender: false, + + + allowDomMove: true, + + + + + rendered: false, + + + componentLayoutCounter: 0, + + + shrinkWrap: 2, + + + weight: 0, + + + maskOnDisable: true, + + + _isLayoutRoot: false, + + + contentPaddingProperty: 'padding', + + horizontalPosProp: 'left', + + + borderBoxCls: Ext.baseCSSPrefix + 'border-box', + rootCls: Ext.baseCSSPrefix + 'body', + + + constructor : function(config) { + var me = this, + i, len, xhooks; + + if (config) { + Ext.apply(me, config); + + xhooks = me.xhooks; + if (xhooks) { + delete me.xhooks; + Ext.override(me, xhooks); + } + } else { + config = {}; + } + + me.initialConfig = config; + + me.mixins.elementCt.constructor.call(me); + + me.addEvents( + + 'beforeactivate', + + 'activate', + + 'beforedeactivate', + + 'deactivate', + + 'added', + + 'disable', + + 'enable', + + 'beforeshow', + + 'show', + + 'beforehide', + + 'hide', + + 'removed', + + 'beforerender', + + 'render', + + 'afterrender', + + 'boxready', + + 'beforedestroy', + + 'destroy', + + 'resize', + + 'move', + + 'focus', + + 'blur' + ); + + me.getId(); + + me.setupProtoEl(); + + + + if (me.cls) { + me.initialCls = me.cls; + me.protoEl.addCls(me.cls); + } + if (me.style) { + me.initialStyle = me.style; + me.protoEl.setStyle(me.style); + } + + me.renderData = me.renderData || {}; + me.renderSelectors = me.renderSelectors || {}; + + + if (!me.hasListeners) { + me.hasListeners = new me.HasListeners(); + } + + me.initComponent(); + + + Ext.ComponentManager.register(me); + + + me.mixins.observable.constructor.call(me); + me.mixins.state.constructor.call(me, config); + + + this.addStateEvents('resize'); + + + if (me.plugins) { + for (i = 0, len = me.plugins.length; i < len; i++) { + me.plugins[i] = me.initPlugin(me.plugins[i]); + } + } + + me.loader = me.getLoader(); + + if (me.renderTo) { + me.render(me.renderTo); + + + + } + + + + if (me.autoShow && !me.isContained) { + me.show(); + } + + if (Ext.isDefined(me.disabledClass)) { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn('Ext.Component: disabledClass has been deprecated. Please use disabledCls.'); + } + me.disabledCls = me.disabledClass; + delete me.disabledClass; + } + }, + + initComponent: function () { + var me = this; + + + + if (me.plugins && !me.plugins.processed) { + me.plugins = me.constructPlugins(); + } + + + + me.setSize(me.width, me.height); + }, + + + getState: function() { + var me = this, + state = null, + sizeModel = me.getSizeModel(); + + if (sizeModel.width.configured) { + state = me.addPropertyToState(state, 'width'); + } + if (sizeModel.height.configured) { + state = me.addPropertyToState(state, 'height'); + } + + return state; + }, + + + addPropertyToState: function (state, propName, value) { + var me = this, + len = arguments.length; + + + + if (len == 3 || me.hasOwnProperty(propName)) { + if (len < 3) { + value = me[propName]; + } + + + + if (value !== me.initialConfig[propName]) { + (state || (state = {}))[propName] = value; + } + } + + return state; + }, + + show: Ext.emptyFn, + + animate: function(animObj) { + var me = this, + hasToWidth, + hasToHeight, + toHeight, + toWidth, + to, + clearWidth, + clearHeight, + curWidth, w, curHeight, h, isExpanding, + wasConstrained, + wasConstrainedHeader, + passedCallback, + oldOverflow; + + animObj = animObj || {}; + to = animObj.to || {}; + + if (Ext.fx.Manager.hasFxBlock(me.id)) { + return me; + } + + hasToWidth = Ext.isDefined(to.width); + if (hasToWidth) { + toWidth = Ext.Number.constrain(to.width, me.minWidth, me.maxWidth); + } + + hasToHeight = Ext.isDefined(to.height); + if (hasToHeight) { + toHeight = Ext.Number.constrain(to.height, me.minHeight, me.maxHeight); + } + + + if (!animObj.dynamic && (hasToWidth || hasToHeight)) { + curWidth = (animObj.from ? animObj.from.width : undefined) || me.getWidth(); + w = curWidth; + curHeight = (animObj.from ? animObj.from.height : undefined) || me.getHeight(); + h = curHeight; + isExpanding = false; + + if (hasToHeight && toHeight > curHeight) { + h = toHeight; + isExpanding = true; + } + if (hasToWidth && toWidth > curWidth) { + w = toWidth; + isExpanding = true; + } + + + if (hasToHeight || hasToWidth) { + oldOverflow = me.el.getStyle('overflow'); + if (oldOverflow !== 'hidden') { + me.el.setStyle('overflow', 'hidden'); + } + } + + + + + if (isExpanding) { + clearWidth = !Ext.isNumber(me.width); + clearHeight = !Ext.isNumber(me.height); + + + + + me.setSize(w, h); + me.el.setSize(curWidth, curHeight); + + if (clearWidth) { + delete me.width; + } + if (clearHeight) { + delete me.height; + } + } + if (hasToWidth) { + to.width = toWidth; + } + + if (hasToHeight) { + to.height = toHeight; + } + } + + + + wasConstrained = me.constrain; + wasConstrainedHeader = me.constrainHeader; + if (wasConstrained || wasConstrainedHeader) { + me.constrain = me.constrainHeader = false; + passedCallback = animObj.callback; + animObj.callback = function() { + me.constrain = wasConstrained; + me.constrainHeader = wasConstrainedHeader; + + if (passedCallback) { + passedCallback.call(animObj.scope||me, arguments); + } + if (oldOverflow !== 'hidden') { + me.el.setStyle('overflow', oldOverflow); + } + }; + } + return me.mixins.animate.animate.apply(me, arguments); + }, + + setHiddenState: function(hidden){ + var hierarchyState = this.getHierarchyState(); + + this.hidden = hidden; + if (hidden) { + hierarchyState.hidden = true; + } else { + delete hierarchyState.hidden; + } + }, + + onHide: function() { + + if (this.ownerLayout) { + this.updateLayout({ isRoot: false }); + } + }, + + onShow : function() { + this.updateLayout({ isRoot: false }); + }, + + + constructPlugin: function(plugin) { + var me = this; + + + if (typeof plugin == 'string') { + plugin = Ext.PluginManager.create({}, plugin, me); + } + + else { + plugin = Ext.PluginManager.create(plugin, null, me); + } + return plugin; + }, + + + constructPlugins: function() { + var me = this, + plugins = me.plugins, + result, i, len; + + if (plugins) { + result = []; + + + + + result.processed = true; + if (!Ext.isArray(plugins)) { + plugins = [ plugins ]; + } + for (i = 0, len = plugins.length; i < len; i++) { + + result[i] = me.constructPlugin(plugins[i]); + } + } + + me.pluginsInitialized = true; + return result; + }, + + + initPlugin : function(plugin) { + plugin.init(this); + + return plugin; + }, + + + + addPlugin: function(plugin) { + var me = this; + + plugin = me.constructPlugin(plugin); + if (me.plugins) { + me.plugins.push(plugin); + } else { + me.plugins = [ plugin ]; + } + if (me.pluginsInitialized) { + me.initPlugin(plugin); + } + return plugin; + }, + + removePlugin: function(plugin) { + Ext.Array.remove(this.plugins, plugin); + plugin.destroy(); + }, + + + findPlugin: function(ptype) { + var i, + plugins = this.plugins, + ln = plugins && plugins.length; + for (i = 0; i < ln; i++) { + if (plugins[i].ptype === ptype) { + return plugins[i]; + } + } + }, + + + getPlugin: function(pluginId) { + var i, + plugins = this.plugins, + ln = plugins && plugins.length; + for (i = 0; i < ln; i++) { + if (plugins[i].pluginId === pluginId) { + return plugins[i]; + } + } + }, + + + beforeLayout: Ext.emptyFn, + + + registerFloatingItem: function(cmp) { + var me = this; + if (!me.floatingDescendants) { + me.floatingDescendants = new Ext.ZIndexManager(me); + } + me.floatingDescendants.register(cmp); + }, + + unregisterFloatingItem: function(cmp) { + var me = this; + if (me.floatingDescendants) { + me.floatingDescendants.unregister(cmp); + } + }, + + layoutSuspendCount: 0, + + suspendLayouts: function () { + var me = this; + if (!me.rendered) { + return; + } + if (++me.layoutSuspendCount === 1) { + me.suspendLayout = true; + } + }, + + resumeLayouts: function (flushOptions) { + var me = this; + if (!me.rendered) { + return; + } + if (!me.layoutSuspendCount) { + Ext.log.warn('Mismatched call to resumeLayouts - layouts are currently not suspended.'); + } + if (me.layoutSuspendCount && !--me.layoutSuspendCount) { + me.suspendLayout = false; + if (flushOptions && !me.isLayoutSuspended()) { + me.updateLayout(flushOptions); + } + } + }, + + setupProtoEl: function() { + var cls = this.initCls(); + + this.protoEl = new Ext.util.ProtoElement({ + cls: cls.join(' ') + }); + }, + + initCls: function() { + var me = this, + cls = [ me.baseCls, me.getComponentLayout().targetCls ]; + + + if (me.componentCls) { + cls.push(me.componentCls); + } else { + me.componentCls = me.baseCls; + } + + return cls; + }, + + + setUI: function(ui) { + var me = this, + uiCls = me.uiCls, + activeUI = me.activeUI, + classes; + + if (ui === activeUI) { + + return; + } + + + if (activeUI) { + classes = me.removeClsWithUI(uiCls, true); + + if (classes.length) { + me.removeCls(classes); + } + + + me.removeUIFromElement(); + } + else { + + me.uiCls = []; + } + + + me.ui = ui; + + + + me.activeUI = ui; + + + me.addUIToElement(); + + classes = me.addClsWithUI(uiCls, true); + + if (classes.length) { + me.addCls(classes); + } + + + + + if (me.rendered) { + me.updateLayout(); + } + }, + + + addClsWithUI: function(classes, skip) { + var me = this, + clsArray = [], + i = 0, + uiCls = me.uiCls = Ext.Array.clone(me.uiCls), + activeUI = me.activeUI, + length, + cls; + + if (typeof classes === "string") { + classes = (classes.indexOf(' ') < 0) ? [classes] : Ext.String.splitWords(classes); + } + + length = classes.length; + + for (; i < length; i++) { + cls = classes[i]; + + if (cls && !me.hasUICls(cls)) { + uiCls.push(cls); + + + if (activeUI) { + clsArray = clsArray.concat(me.addUIClsToElement(cls)); + } + } + } + + if (skip !== true && activeUI) { + me.addCls(clsArray); + } + + return clsArray; + }, + + + removeClsWithUI: function(classes, skip) { + var me = this, + clsArray = [], + i = 0, + extArray = Ext.Array, + remove = extArray.remove, + uiCls = me.uiCls = extArray.clone(me.uiCls), + activeUI = me.activeUI, + length, cls; + + if (typeof classes === "string") { + classes = (classes.indexOf(' ') < 0) ? [classes] : Ext.String.splitWords(classes); + } + + length = classes.length; + + for (i = 0; i < length; i++) { + cls = classes[i]; + + if (cls && me.hasUICls(cls)) { + remove(uiCls, cls); + + + if (activeUI) { + clsArray = clsArray.concat(me.removeUIClsFromElement(cls)); + } + } + } + + if (skip !== true && activeUI) { + me.removeCls(clsArray); + } + + return clsArray; + }, + + + hasUICls: function(cls) { + var me = this, + uiCls = me.uiCls || []; + + return Ext.Array.contains(uiCls, cls); + }, + + frameElementsArray: ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'], + + + addUIClsToElement: function(cls) { + var me = this, + baseClsUi = me.baseCls + '-' + me.ui + '-' + cls, + result = [Ext.baseCSSPrefix + cls, me.baseCls + '-' + cls, baseClsUi], + frameElementsArray, frameElementsLength, i, el, frameElement; + + if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) { + + frameElementsArray = me.frameElementsArray; + frameElementsLength = frameElementsArray.length; + + + for (i = 0; i < frameElementsLength; i++) { + frameElement = frameElementsArray[i]; + el = me['frame' + frameElement.toUpperCase()]; + + if (el) { + el.addCls(baseClsUi + '-' + frameElement); + } + } + } + + return result; + }, + + + removeUIClsFromElement: function(cls) { + var me = this, + baseClsUi = me.baseCls + '-' + me.ui + '-' + cls, + result = [Ext.baseCSSPrefix + cls, me.baseCls + '-' + cls, baseClsUi], + frameElementsArray, frameElementsLength, i, el, frameElement; + + if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) { + + frameElementsArray = me.frameElementsArray; + frameElementsLength = frameElementsArray.length; + + + for (i = 0; i < frameElementsLength; i++) { + frameElement = frameElementsArray[i]; + el = me['frame' + frameElement.toUpperCase()]; + + if (el) { + el.removeCls(baseClsUi + '-' + frameElement); + } + } + } + + return result; + }, + + + addUIToElement: function() { + var me = this, + baseClsUI = me.baseCls + '-' + me.ui, + frameElementsArray, frameElementsLength, i, el, frameElement; + + me.addCls(baseClsUI); + + if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) { + + frameElementsArray = me.frameElementsArray; + frameElementsLength = frameElementsArray.length; + + + for (i = 0; i < frameElementsLength; i++) { + frameElement = frameElementsArray[i]; + el = me['frame' + frameElement.toUpperCase()]; + + if (el) { + el.addCls(baseClsUI + '-' + frameElement); + } + } + } + }, + + + removeUIFromElement: function() { + var me = this, + baseClsUI = me.baseCls + '-' + me.ui, + frameElementsArray, frameElementsLength, i, el, frameElement; + + me.removeCls(baseClsUI); + + if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) { + + frameElementsArray = me.frameElementsArray; + frameElementsLength = frameElementsArray.length; + + for (i = 0; i < frameElementsLength; i++) { + frameElement = frameElementsArray[i]; + el = me['frame' + frameElement.toUpperCase()]; + + if (el) { + el.removeCls(baseClsUI + '-' + frameElement); + } + } + } + }, + + + getTpl: function(name) { + return Ext.XTemplate.getTpl(this, name); + }, + + + initStyles: function(targetEl) { + var me = this, + Element = Ext.Element, + margin = me.margin, + border = me.border, + cls = me.cls, + style = me.style, + x = me.x, + y = me.y, + width, height; + + me.initPadding(targetEl); + + if (margin != null) { + targetEl.setStyle('margin', this.unitizeBox((margin === true) ? 5 : margin)); + } + + if (border != null) { + me.setBorder(border, targetEl); + } + + + + if (cls && cls != me.initialCls) { + targetEl.addCls(cls); + me.cls = me.initialCls = null; + } + if (style && style != me.initialStyle) { + targetEl.setStyle(style); + me.style = me.initialStyle = null; + } + + if (x != null) { + targetEl.setStyle(me.horizontalPosProp, (typeof x == 'number') ? (x + 'px') : x); + } + if (y != null) { + targetEl.setStyle('top', (typeof y == 'number') ? (y + 'px') : y); + } + + if (!me.ownerCt || me.floating) { + if (Ext.scopeCss) { + targetEl.addCls(me.rootCls); + } + if (Ext.isBorderBox) { + targetEl.addCls(me.borderBoxCls); + } + } + + + + if (!me.getFrameInfo()) { + width = me.width; + height = me.height; + + + if (width != null) { + if (typeof width === 'number') { + if (Ext.isBorderBox) { + targetEl.setStyle('width', width + 'px'); + } + } else { + targetEl.setStyle('width', width); + } + } + if (height != null) { + if (typeof height === 'number') { + if (Ext.isBorderBox) { + targetEl.setStyle('height', height + 'px'); + } + } else { + targetEl.setStyle('height', height); + } + } + } + }, + + + initPadding: function(targetEl) { + var me = this, + padding = me.padding; + + if (padding != null) { + if (me.layout && me.layout.managePadding && me.contentPaddingProperty === 'padding') { + + + + + + targetEl.setStyle('padding', 0); + } else { + + + targetEl.setStyle('padding', this.unitizeBox((padding === true) ? 5 : padding)); + } + } + }, + + parseBox: function(box) { + return Ext.dom.Element.parseBox(box); + }, + + unitizeBox: function(box) { + return Ext.dom.Element.unitizeBox(box); + }, + + + setMargin: function(margin, preventLayout) { + var me = this; + + if (me.rendered) { + if (!margin && margin !== 0) { + margin = ''; + } else { + if (margin === true) { + margin = 5; + } + margin = this.unitizeBox(margin); + } + me.getTargetEl().setStyle('margin', margin); + if (!preventLayout) { + me.updateLayout(); + } + } else { + me.margin = margin; + } + }, + + + initEvents : function() { + var me = this, + afterRenderEvents = me.afterRenderEvents, + afterRenderEvent, el, property, index, len; + + if (afterRenderEvents) { + for (property in afterRenderEvents) { + el = me[property]; + + if (el && el.on) { + afterRenderEvent = afterRenderEvents[property]; + + for (index = 0, len = afterRenderEvent.length ; index < len ; ++index) { + me.mon(el, afterRenderEvent[index]); + } + } + } + } + + + + + me.addFocusListener(); + }, + + + addFocusListener: function() { + var me = this, + focusEl = me.getFocusEl(), + needsTabIndex; + + + + + + + + + + if (focusEl) { + + + if (focusEl.isComponent) { + return focusEl.addFocusListener(); + } + + + + + needsTabIndex = focusEl.needsTabIndex(); + if (!me.focusListenerAdded && (!needsTabIndex || Ext.enableFocusManager)) { + if (needsTabIndex) { + focusEl.dom.tabIndex = -1; + } + focusEl.on({ + focus: me.onFocus, + blur: me.onBlur, + scope: me + }); + me.focusListenerAdded = true; + } + } + }, + + + getFocusEl: Ext.emptyFn, + + isFocusable: function() { + var me = this, + focusEl; + if ((me.focusable !== false) && (focusEl = me.getFocusEl()) && me.rendered && !me.destroying && !me.isDestroyed && !me.disabled && me.isVisible(true)) { + + + + + return focusEl.isFocusable(true); + } + }, + + + beforeFocus: Ext.emptyFn, + + + onFocus: function(e) { + var me = this, + focusCls = me.focusCls, + focusEl = me.getFocusEl(); + + if (!me.disabled) { + me.beforeFocus(e); + if (focusCls && focusEl) { + focusEl.addCls(me.addClsWithUI(focusCls, true)); + } + if (!me.hasFocus) { + me.hasFocus = true; + me.fireEvent('focus', me, e); + } + } + }, + + + beforeBlur : Ext.emptyFn, + + + onBlur : function(e) { + var me = this, + focusCls = me.focusCls, + focusEl = me.getFocusEl(); + + if (me.destroying) { + return; + } + + me.beforeBlur(e); + if (focusCls && focusEl) { + focusEl.removeCls(me.removeClsWithUI(focusCls, true)); + } + if (me.validateOnBlur) { + me.validate(); + } + me.hasFocus = false; + me.fireEvent('blur', me, e); + me.postBlur(e); + }, + + + postBlur : Ext.emptyFn, + + + owns: function (element) { + var result = false, + cmp; + + if (element.isEvent) { + element = element.target; + } else if (element.isElement) { + element = element.dom; + } + + cmp = Ext.AbstractComponent.findComponentByElement(element); + + if (cmp) { + result = (cmp === this) || (!!cmp.up(this)); + } + + return result; + }, + + + is: function(selector) { + return Ext.ComponentQuery.is(this, selector); + }, + + + up: function (selector, limit) { + var result = this.getRefOwner(), + limitSelector = typeof limit === 'string', + limitCount = typeof limit === 'number', + limitComponent = limit && limit.isComponent, + steps = 0; + + if (selector) { + for (; result; result = result.getRefOwner()) { + steps++; + if (selector.isComponent) { + if (result === selector) { + return result; + } + } else { + if (Ext.ComponentQuery.is(result, selector)) { + return result; + } + } + + + if (limitSelector && result.is(limit)) { + return; + } + if (limitCount && steps === limit) { + return; + } + if (limitComponent && result === limit) { + return; + } + } + } + return result; + }, + + + nextSibling: function(selector) { + var o = this.ownerCt, it, last, idx, c; + if (o) { + it = o.items; + idx = it.indexOf(this) + 1; + if (idx) { + if (selector) { + for (last = it.getCount(); idx < last; idx++) { + if ((c = it.getAt(idx)).is(selector)) { + return c; + } + } + } else { + if (idx < it.getCount()) { + return it.getAt(idx); + } + } + } + } + return null; + }, + + + previousSibling: function(selector) { + var o = this.ownerCt, it, idx, c; + if (o) { + it = o.items; + idx = it.indexOf(this); + if (idx != -1) { + if (selector) { + for (--idx; idx >= 0; idx--) { + if ((c = it.getAt(idx)).is(selector)) { + return c; + } + } + } else { + if (idx) { + return it.getAt(--idx); + } + } + } + } + return null; + }, + + + previousNode: function(selector, includeSelf) { + var node = this, + ownerCt = node.ownerCt, + result, + it, i, sib; + + + if (includeSelf && node.is(selector)) { + return node; + } + + if (ownerCt) { + for (it = ownerCt.items.items, i = Ext.Array.indexOf(it, node) - 1; i > -1; i--) { + sib = it[i]; + if (sib.query) { + result = sib.query(selector); + result = result[result.length - 1]; + if (result) { + return result; + } + } + if (sib.is(selector)) { + return sib; + } + } + return ownerCt.previousNode(selector, true); + } + return null; + }, + + + nextNode: function(selector, includeSelf) { + var node = this, + ownerCt = node.ownerCt, + result, + it, len, i, sib; + + + if (includeSelf && node.is(selector)) { + return node; + } + + if (ownerCt) { + for (it = ownerCt.items.items, i = Ext.Array.indexOf(it, node) + 1, len = it.length; i < len; i++) { + sib = it[i]; + if (sib.is(selector)) { + return sib; + } + if (sib.down) { + result = sib.down(selector); + if (result) { + return result; + } + } + } + return ownerCt.nextNode(selector); + } + return null; + }, + + + getId : function() { + return this.id || (this.id = 'ext-comp-' + (this.getAutoId())); + }, + + + getItemId : function() { + return this.itemId || this.id; + }, + + + getEl : function() { + return this.el; + }, + + + getTargetEl: function() { + return this.frameBody || this.el; + }, + + + getOverflowEl: function(){ + return this.getTargetEl(); + }, + + + getOverflowStyle: function() { + var me = this, + result = null, + auto = me.autoScroll, + ox, oy, + overflowStyle; + + + + + if (typeof auto === 'boolean') { + result = { + overflow: overflowStyle = (auto ? 'auto' : '') + }; + me.scrollFlags = { + overflowX: overflowStyle, + overflowY: overflowStyle, + x: auto, + y: auto, + both: auto + }; + } else { + ox = me.overflowX; + oy = me.overflowY; + if (ox !== undefined || oy !== undefined) { + if (ox && ox === true) { + ox = 'auto'; + } + + if (oy && oy === true) { + oy = 'auto'; + } + result = { + 'overflowX': ox = ox || '', + 'overflowY': oy = oy || '' + }; + + + me.scrollFlags = { + overflowX: ox, + overflowY: oy, + x: ox = (ox === 'auto' || ox === 'scroll'), + y: oy = (oy === 'auto' || oy === 'scroll'), + both: ox && oy + }; + } else { + me.scrollFlags = { + overflowX: '', + overflowY: '', + x: false, + y: false, + both: false + }; + } + } + + + + if (result && Ext.isIE7m) { + result.position = 'relative'; + } + + return result; + }, + + + isXType: function(xtype, shallow) { + if (shallow) { + return this.xtype === xtype; + } + else { + return this.xtypesMap[xtype]; + } + }, + + + getXTypes: function() { + var self = this.self, + xtypes, parentPrototype, parentXtypes; + + if (!self.xtypes) { + xtypes = []; + parentPrototype = this; + + while (parentPrototype) { + parentXtypes = parentPrototype.xtypes; + + if (parentXtypes !== undefined) { + xtypes.unshift.apply(xtypes, parentXtypes); + } + + parentPrototype = parentPrototype.superclass; + } + + self.xtypeChain = xtypes; + self.xtypes = xtypes.join('/'); + } + + return self.xtypes; + }, + + + update : function(htmlOrData, loadScripts, cb) { + var me = this, + isData = (me.tpl && !Ext.isString(htmlOrData)), + el, + sizeModel = me.getSizeModel(), + + doLayout = sizeModel.width.shrinkWrap || sizeModel.height.shrinkWrap; + + if (isData) { + me.data = htmlOrData; + } else { + me.html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData; + } + + if (me.rendered) { + if (me.isContainer) { + el = me.layout.getRenderTarget(); + + + doLayout = doLayout || me.items.items.length > 0; + } else { + el = me.getTargetEl(); + } + if (isData) { + me.tpl[me.tplWriteMode](el, htmlOrData || {}); + } else { + el.update(me.html, loadScripts, cb); + } + + if (doLayout) { + me.updateLayout(); + } + } + + }, + + + setVisible : function(visible) { + return this[visible ? 'show': 'hide'](); + }, + + + isVisible: function(deep) { + var me = this, + hidden; + + if (me.hidden || !me.rendered || me.isDestroyed) { + hidden = true; + } else if (deep) { + hidden = me.isHierarchicallyHidden(); + } + + return !hidden; + }, + + isHierarchicallyHidden: function() { + var child = this, + hidden = false, + parent, parentHierarchyState; + + + + + + for (; (parent = child.ownerCt || child.floatParent); child = parent) { + parentHierarchyState = parent.getHierarchyState(); + if (parentHierarchyState.hidden) { + hidden = true; + break; + } + if (child.getHierarchyState().collapseImmune) { + + if (parent.collapsed && !child.collapseImmune) { + + + + hidden = true; + break; + } + } else { + + + + hidden = !!parentHierarchyState.collapsed; + break; + } + } + + return hidden; + }, + + + onBoxReady: function(width, height) { + var me = this; + + if (me.disableOnBoxReady) { + me.onDisable(); + } else if (me.enableOnBoxReady) { + me.onEnable(); + } + if (me.resizable) { + me.initResizable(me.resizable); + } + + + + if (me.draggable) { + me.initDraggable(); + } + + if (me.hasListeners.boxready) { + me.fireEvent('boxready', me, width, height); + } + }, + + + enable: function(silent) { + var me = this; + + delete me.disableOnBoxReady; + me.removeCls(me.disabledCls); + if (me.rendered) { + me.onEnable(); + } else { + me.enableOnBoxReady = true; + } + + me.disabled = false; + delete me.resetDisable; + + if (silent !== true) { + me.fireEvent('enable', me); + } + + return me; + }, + + + disable: function(silent) { + var me = this; + + delete me.enableOnBoxReady; + me.addCls(me.disabledCls); + if (me.rendered) { + me.onDisable(); + } else { + me.disableOnBoxReady = true; + } + + me.disabled = true; + + if (silent !== true) { + delete me.resetDisable; + me.fireEvent('disable', me); + } + + return me; + }, + + + onEnable: function() { + var me = this, + dom, nodeName; + + if (me.maskOnDisable) { + dom = me.el.dom; + nodeName = dom.nodeName; + + if (me.disabledRe.test(nodeName)) { + dom.disabled = false; + } + + if (!me.nonMaskableRe.test(nodeName)) { + me.unmask(); + } + } + }, + + + onDisable: function() { + var me = this, + focusCls = me.focusCls, + focusEl = me.getFocusEl(), + dom, nodeName; + + if (focusCls && focusEl) { + focusEl.removeCls(me.removeClsWithUI(focusCls, true)); + } + + if (me.maskOnDisable) { + dom = me.el.dom; + nodeName = dom.nodeName; + + if (me.disabledRe.test(nodeName)) { + dom.disabled = true; + } + + if (!me.nonMaskableRe.test(nodeName)) { + me.mask(); + } + } + }, + + + mask: function (msg, msgCls, elHeight) { + var box = this.lastBox, + + + + target = this.getMaskTarget() || this.el; + + + if (box) { + elHeight = box.height; + } + target.mask(msg, msgCls, elHeight); + }, + + + unmask: function() { + (this.getMaskTarget() || this.el).unmask(); + }, + + + getMaskTarget: function() { + return this.maskElement ? this[this.maskElement] : null; + }, + + + isDisabled : function() { + return this.disabled; + }, + + + setDisabled : function(disabled) { + return this[disabled ? 'disable': 'enable'](); + }, + + + isHidden : function() { + return this.hidden; + }, + + + addCls : function(cls) { + var me = this, + el = me.rendered ? me.el : me.protoEl; + + el.addCls.apply(el, arguments); + return me; + }, + + + addClass : function() { + return this.addCls.apply(this, arguments); + }, + + + hasCls: function (cls) { + var me = this, + el = me.rendered ? me.el : me.protoEl; + + return el.hasCls.apply(el, arguments); + }, + + + removeCls : function(cls) { + var me = this, + el = me.rendered ? me.el : me.protoEl; + + el.removeCls.apply(el, arguments); + return me; + }, + + + removeClass : function() { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn('Ext.Component: removeClass has been deprecated. Please use removeCls.'); + } + return this.removeCls.apply(this, arguments); + }, + + addOverCls: function() { + var me = this; + if (!me.disabled) { + me.el.addCls(me.overCls); + } + }, + + removeOverCls: function() { + this.el.removeCls(this.overCls); + }, + + addListener : function(element, listeners, scope, options) { + var me = this, + fn, + option; + + if (Ext.isString(element) && (Ext.isObject(listeners) || options && options.element)) { + if (options.element) { + fn = listeners; + + listeners = {}; + listeners[element] = fn; + element = options.element; + if (scope) { + listeners.scope = scope; + } + + for (option in options) { + if (options.hasOwnProperty(option)) { + if (me.eventOptionsRe.test(option)) { + listeners[option] = options[option]; + } + } + } + } + + + + if (me[element] && me[element].on) { + me.mon(me[element], listeners); + } else { + me.afterRenderEvents = me.afterRenderEvents || {}; + if (!me.afterRenderEvents[element]) { + me.afterRenderEvents[element] = []; + } + me.afterRenderEvents[element].push(listeners); + } + return; + } + + return me.mixins.observable.addListener.apply(me, arguments); + }, + + + removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){ + var me = this, + element = managedListener.options ? managedListener.options.element : null; + + if (element) { + element = me[element]; + if (element && element.un) { + if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) { + element.un(managedListener.ename, managedListener.fn, managedListener.scope); + if (!isClear) { + Ext.Array.remove(me.managedListeners, managedListener); + } + } + } + } else { + return me.mixins.observable.removeManagedListenerItem.apply(me, arguments); + } + }, + + + getBubbleTarget : function() { + return this.ownerCt; + }, + + + isFloating : function() { + return this.floating; + }, + + + isDraggable : function() { + return !!this.draggable; + }, + + + isDroppable : function() { + return !!this.droppable; + }, + + + onAdded : function(container, pos) { + var me = this; + + me.ownerCt = container; + + if (me.hierarchyState) { + + + + me.hierarchyState.invalid = true; + + + + + + delete me.hierarchyState; + } + + if (me.hasListeners.added) { + me.fireEvent('added', me, container, pos); + } + }, + + + onRemoved : function(destroying) { + var me = this; + if (me.hasListeners.removed) { + me.fireEvent('removed', me, me.ownerCt); + } + delete me.ownerCt; + delete me.ownerLayout; + }, + + + beforeDestroy : Ext.emptyFn, + + + onResize: function(width, height, oldWidth, oldHeight) { + var me = this; + + + if (me.floating && me.constrain) { + me.doConstrain(); + } + if (me.hasListeners.resize) { + me.fireEvent('resize', me, width, height, oldWidth, oldHeight); + } + }, + + + setSize : function(width, height) { + var me = this; + + + if (width && typeof width == 'object') { + height = width.height; + width = width.width; + } + + + if (typeof width == 'number') { + me.width = Ext.Number.constrain(width, me.minWidth, me.maxWidth); + } else if (width === null) { + delete me.width; + } + + if (typeof height == 'number') { + me.height = Ext.Number.constrain(height, me.minHeight, me.maxHeight); + } else if (height === null) { + delete me.height; + } + + + + if (me.rendered && me.isVisible()) { + + + me.updateLayout({ + isRoot: false + }); + } + + return me; + }, + + + isLayoutRoot: function() { + var me = this, + ownerLayout = me.ownerLayout; + + + + + + if (!ownerLayout || me._isLayoutRoot || me.floating) { + return true; + } + + return ownerLayout.isItemLayoutRoot(me); + }, + + + isLayoutSuspended: function () { + var comp = this, + ownerLayout; + + while (comp) { + if (comp.layoutSuspendCount || comp.suspendLayout) { + return true; + } + + ownerLayout = comp.ownerLayout; + if (!ownerLayout) { + break; + } + + + + + + + comp = ownerLayout.owner; + } + + return false; + }, + + + updateLayout: function (options) { + var me = this, + defer, + lastBox = me.lastBox, + isRoot = options && options.isRoot; + + if (lastBox) { + + + lastBox.invalid = true; + } + + if (!me.rendered || me.layoutSuspendCount || me.suspendLayout) { + return; + } + + if (me.hidden) { + Ext.AbstractComponent.cancelLayout(me); + } else if (typeof isRoot != 'boolean') { + isRoot = me.isLayoutRoot(); + } + + + if (isRoot || !me.ownerLayout || !me.ownerLayout.onContentChange(me)) { + + if (!me.isLayoutSuspended()) { + + defer = (options && options.hasOwnProperty('defer')) ? options.defer : me.deferLayouts; + Ext.AbstractComponent.updateLayout(me, defer); + } + } + }, + + + getSizeModel: function (ownerCtSizeModel) { + var me = this, + models = Ext.layout.SizeModel, + ownerContext = me.componentLayout.ownerContext, + width = me.width, + height = me.height, + typeofWidth, typeofHeight, + hasPixelWidth, hasPixelHeight, + heightModel, ownerLayout, policy, shrinkWrap, topLevel, widthModel; + + if (ownerContext) { + + + + + widthModel = ownerContext.widthModel; + heightModel = ownerContext.heightModel; + } + + if (!widthModel || !heightModel) { + hasPixelWidth = ((typeofWidth = typeof width) == 'number'); + hasPixelHeight = ((typeofHeight = typeof height) == 'number'); + topLevel = me.floating || !(ownerLayout = me.ownerLayout); + + + if (topLevel) { + policy = Ext.layout.Layout.prototype.autoSizePolicy; + shrinkWrap = me.floating ? 3 : me.shrinkWrap; + + if (hasPixelWidth) { + widthModel = models.configured; + } + + if (hasPixelHeight) { + heightModel = models.configured; + } + } else { + policy = ownerLayout.getItemSizePolicy(me, ownerCtSizeModel); + shrinkWrap = ownerLayout.isItemShrinkWrap(me); + } + + if (ownerContext) { + ownerContext.ownerSizePolicy = policy; + } + + shrinkWrap = (shrinkWrap === true) ? 3 : (shrinkWrap || 0); + + + + + + if (topLevel && shrinkWrap) { + if (width && typeofWidth == 'string') { + shrinkWrap &= 2; + } + if (height && typeofHeight == 'string') { + shrinkWrap &= 1; + } + } + + if (shrinkWrap !== 3) { + if (!ownerCtSizeModel) { + ownerCtSizeModel = me.ownerCt && me.ownerCt.getSizeModel(); + } + + if (ownerCtSizeModel) { + shrinkWrap |= (ownerCtSizeModel.width.shrinkWrap ? 1 : 0) | (ownerCtSizeModel.height.shrinkWrap ? 2 : 0); + } + } + + if (!widthModel) { + if (!policy.setsWidth) { + if (hasPixelWidth) { + widthModel = models.configured; + } else { + widthModel = (shrinkWrap & 1) ? models.shrinkWrap : models.natural; + } + } else if (policy.readsWidth) { + if (hasPixelWidth) { + widthModel = models.calculatedFromConfigured; + } else { + widthModel = (shrinkWrap & 1) ? models.calculatedFromShrinkWrap : + models.calculatedFromNatural; + } + } else { + widthModel = models.calculated; + } + } + + if (!heightModel) { + if (!policy.setsHeight) { + if (hasPixelHeight) { + heightModel = models.configured; + } else { + heightModel = (shrinkWrap & 2) ? models.shrinkWrap : models.natural; + } + } else if (policy.readsHeight) { + if (hasPixelHeight) { + heightModel = models.calculatedFromConfigured; + } else { + heightModel = (shrinkWrap & 2) ? models.calculatedFromShrinkWrap : + models.calculatedFromNatural; + } + } else { + heightModel = models.calculated; + } + } + } + + + + return widthModel.pairsByHeightOrdinal[heightModel.ordinal]; + }, + + isDescendant: function(ancestor) { + if (ancestor.isContainer) { + for (var c = this.ownerCt; c; c = c.ownerCt) { + if (c === ancestor) { + return true; + } + } + } + return false; + }, + + + doComponentLayout : function() { + this.updateLayout(); + return this; + }, + + + forceComponentLayout: function () { + this.updateLayout(); + }, + + + setComponentLayout : function(layout) { + var currentLayout = this.componentLayout; + if (currentLayout && currentLayout.isLayout && currentLayout != layout) { + currentLayout.setOwner(null); + } + this.componentLayout = layout; + layout.setOwner(this); + }, + + getComponentLayout : function() { + var me = this; + + if (!me.componentLayout || !me.componentLayout.isLayout) { + me.setComponentLayout(Ext.layout.Layout.create(me.componentLayout, 'autocomponent')); + } + return me.componentLayout; + }, + + + afterComponentLayout: function(width, height, oldWidth, oldHeight) { + var me = this; + + if (++me.componentLayoutCounter === 1) { + me.afterFirstLayout(width, height); + } + + if (width !== oldWidth || height !== oldHeight) { + me.onResize(width, height, oldWidth, oldHeight); + } + }, + + + beforeComponentLayout: function(width, height) { + return true; + }, + + + setPosition: function(x, y, animate) { + var me = this, + pos = me.beforeSetPosition.apply(me, arguments); + + if (pos && me.rendered) { + x = pos.x; + y = pos.y; + + if (animate) { + + + + if (x !== me.getLocalX() || y !== me.getLocalY()) { + me.stopAnimation(); + me.animate(Ext.apply({ + duration: 1000, + listeners: { + afteranimate: Ext.Function.bind(me.afterSetPosition, me, [x, y]) + }, + to: { + + + + + left: x, + top: y + } + }, animate)); + } + } else { + me.setLocalXY(x, y); + me.afterSetPosition(x, y); + } + } + return me; + }, + + + beforeSetPosition: function (x, y, animate) { + var pos, x0; + + + + if (x) { + + if (Ext.isNumber(x0 = x[0])) { + animate = y; + y = x[1]; + x = x0; + } + + else if ((x0 = x.x) !== undefined) { + animate = y; + y = x.y; + x = x0; + } + } + + if (this.constrain || this.constrainHeader) { + pos = this.calculateConstrainedPosition(null, [x, y], true); + if (pos) { + x = pos[0]; + y = pos[1]; + } + } + + + pos = { + x : this.x = x, + y : this.y = y, + anim: animate, + hasX: x !== undefined, + hasY: y !== undefined + }; + + return (pos.hasX || pos.hasY) ? pos : null; + }, + + + afterSetPosition: function(x, y) { + var me = this; + me.onPosition(x, y); + if (me.hasListeners.move) { + me.fireEvent('move', me, x, y); + } + }, + + + onPosition: Ext.emptyFn, + + + setWidth : function(width) { + return this.setSize(width); + }, + + + setHeight : function(height) { + return this.setSize(undefined, height); + }, + + + getSize : function(contentSize) { + return this.el.getSize(contentSize); + }, + + + getWidth : function() { + return this.el.getWidth(); + }, + + + getHeight : function() { + return this.el.getHeight(); + }, + + + getLoader: function(){ + var me = this, + autoLoad = me.autoLoad ? (Ext.isObject(me.autoLoad) ? me.autoLoad : {url: me.autoLoad}) : null, + loader = me.loader || autoLoad; + + if (loader) { + if (!loader.isLoader) { + me.loader = new Ext.ComponentLoader(Ext.apply({ + target: me, + autoLoad: autoLoad + }, loader)); + } else { + loader.setTarget(me); + } + return me.loader; + + } + return null; + }, + + + setDocked : function(dock, layoutParent) { + var me = this; + + me.dock = dock; + if (layoutParent && me.ownerCt && me.rendered) { + me.ownerCt.updateLayout(); + } + return me; + }, + + + setBorder: function(border, targetEl) { + var me = this, + initial = !!targetEl; + + if (me.rendered || initial) { + if (!initial) { + targetEl = me.el; + } + + if (!border) { + border = 0; + } else if (border === true) { + border = '1px'; + } else { + border = this.unitizeBox(border); + } + targetEl.setStyle('border-width', border); + if (!initial) { + me.updateLayout(); + } + } + me.border = border; + }, + + onDestroy : function() { + var me = this; + + + Ext.destroy( + me.componentLayout, + me.loadMask, + me.floatingDescendants + ); + }, + + + destroy : function() { + var me = this, + selectors = me.renderSelectors, + selector, + el; + + if (!me.isDestroyed) { + if (!me.hasListeners.beforedestroy || me.fireEvent('beforedestroy', me) !== false) { + me.destroying = true; + me.beforeDestroy(); + + if (me.floating) { + delete me.floatParent; + + + if (me.zIndexManager) { + me.zIndexManager.unregister(me); + } + } else if (me.ownerCt && me.ownerCt.remove) { + me.ownerCt.remove(me, false); + } + + me.stopAnimation(); + me.onDestroy(); + + + Ext.destroy(me.plugins); + + if (me.hasListeners.destroy) { + me.fireEvent('destroy', me); + } + Ext.ComponentManager.unregister(me); + + me.mixins.state.destroy.call(me); + + me.clearListeners(); + + if (me.rendered) { + if (!me.preserveElOnDestroy) { + me.el.remove(); + } + me.mixins.elementCt.destroy.call(me); + if (selectors) { + for (selector in selectors) { + if (selectors.hasOwnProperty(selector)) { + el = me[selector]; + if (el) { + delete me[selector]; + el.remove(); + } + } + } + } + + delete me.el; + delete me.frameBody; + delete me.rendered; + } + delete me.initialConfig; + + me.destroying = false; + me.isDestroyed = true; + } + } + }, + + + isDescendantOf: function(container) { + return !!this.findParentBy(function(p){ + return p === container; + }); + }, + + + getHierarchyState: function (inner) { + var me = this, + hierarchyState = (inner && me.hierarchyStateInner) || me.hierarchyState, + ownerCt = me.ownerCt, + parent, layout, hierarchyStateInner, getInner; + + if (!hierarchyState || hierarchyState.invalid) { + + + + + parent = me.getRefOwner(); + + if (ownerCt) { + + + getInner = me.ownerLayout === ownerCt.layout; + } + + me.hierarchyState = hierarchyState = + + + + + + Ext.Object.chain(parent ? parent.getHierarchyState(getInner) + : Ext.rootHierarchyState); + + me.initHierarchyState(hierarchyState); + if ((layout = me.componentLayout).initHierarchyState) { + layout.initHierarchyState(hierarchyState); + } + + if (me.isContainer) { + me.hierarchyStateInner = hierarchyStateInner = Ext.Object.chain(hierarchyState); + + layout = me.layout; + if (layout && layout.initHierarchyState) { + layout.initHierarchyState(hierarchyStateInner, hierarchyState); + } + if (inner) { + hierarchyState = hierarchyStateInner; + } + } + } + + return hierarchyState; + }, + + + initHierarchyState: function(hierarchyState) { + var me = this; + + if (me.collapsed) { + hierarchyState.collapsed = true; + } + if (me.hidden) { + hierarchyState.hidden = true; + } + if (me.collapseImmune) { + hierarchyState.collapseImmune = true; + } + }, + + + + + + getAnchorToXY: function(el, anchor, local, mySize) { + return el.getAnchorXY(anchor, local, mySize); + }, + + 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(x) { + this.el.setLocalX(x); + }, + + setLocalXY: function(x, y) { + this.el.setLocalXY(x, y); + }, + + setLocalY: function(y) { + this.el.setLocalY(y); + }, + + setX: function(x, animate) { + this.el.setX(x, animate); + }, + + setXY: function(xy, animate) { + this.el.setXY(xy, animate); + }, + + setY: function(y, animate) { + this.el.setY(y, animate); + } + + + + +}, function() { + var AbstractComponent = this; + + AbstractComponent.createAlias({ + on: 'addListener', + prev: 'previousSibling', + next: 'nextSibling' + }); + + + Ext.resumeLayouts = function (flush) { + AbstractComponent.resumeLayouts(flush); + }; + + + Ext.suspendLayouts = function () { + AbstractComponent.suspendLayouts(); + }; + + + Ext.batchLayouts = function(fn, scope) { + AbstractComponent.suspendLayouts(); + + fn.call(scope); + AbstractComponent.resumeLayouts(true); + }; +}); + + + +Ext.define('Ext.AbstractPlugin', { + disabled: false, + + + isPlugin: true, + + + constructor: function(config) { + if (config) { + this.pluginConfig = config; + Ext.apply(this, config); + } + }, + + + clonePlugin: function(overrideCfg) { + return new this.self(Ext.apply({}, overrideCfg, this.pluginConfig)); + }, + + + setCmp: function(cmp) { + this.cmp = cmp; + }, + + + getCmp: function() { + return this.cmp; + }, + + + + + init: Ext.emptyFn, + + + destroy: Ext.emptyFn, + + + enable: function() { + this.disabled = false; + }, + + + disable: function() { + this.disabled = true; + }, + + + + onClassExtended: function(cls, data, hooks) { + var alias = data.alias; + + + if (alias && !data.ptype) { + if (Ext.isArray(alias)) { + alias = alias[0]; + } + cls.prototype.ptype = alias.split('plugin.')[1]; + } + } +}); + + + +Ext.define('Ext.Action', { + + + + + + + + + + + + + + + constructor : function(config){ + this.initialConfig = config; + this.itemId = config.itemId = (config.itemId || config.id || Ext.id()); + this.items = []; + }, + + + isAction : true, + + + setText : function(text){ + this.initialConfig.text = text; + this.callEach('setText', [text]); + }, + + + getText : function(){ + return this.initialConfig.text; + }, + + + setIconCls : function(cls){ + this.initialConfig.iconCls = cls; + this.callEach('setIconCls', [cls]); + }, + + + getIconCls : function(){ + return this.initialConfig.iconCls; + }, + + + setDisabled : function(v){ + this.initialConfig.disabled = v; + this.callEach('setDisabled', [v]); + }, + + + enable : function(){ + this.setDisabled(false); + }, + + + disable : function(){ + this.setDisabled(true); + }, + + + isDisabled : function(){ + return this.initialConfig.disabled; + }, + + + setHidden : function(v){ + this.initialConfig.hidden = v; + this.callEach('setVisible', [!v]); + }, + + + show : function(){ + this.setHidden(false); + }, + + + hide : function(){ + this.setHidden(true); + }, + + + isHidden : function(){ + return this.initialConfig.hidden; + }, + + + setHandler : function(fn, scope){ + this.initialConfig.handler = fn; + this.initialConfig.scope = scope; + this.callEach('setHandler', [fn, scope]); + }, + + + each : function(fn, scope){ + Ext.each(this.items, fn, scope); + }, + + + callEach : function(fnName, args){ + var items = this.items, + i = 0, + len = items.length, + item; + + Ext.suspendLayouts(); + for(; i < len; i++){ + item = items[i]; + item[fnName].apply(item, args); + } + Ext.resumeLayouts(true); + }, + + + addComponent : function(comp){ + this.items.push(comp); + comp.on('destroy', this.removeComponent, this); + }, + + + removeComponent : function(comp){ + Ext.Array.remove(this.items, comp); + }, + + + execute : function(){ + this.initialConfig.handler.apply(this.initialConfig.scope || Ext.global, arguments); + } +}); + + + +Ext.define('Ext.data.flash.BinaryXhr', { + + 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(javascriptId, state, data) { + var connection; + + connection = this.liveConnections[Number(javascriptId)]; + if (connection) { + connection.onFlashStateChange(state, data); + } + else { + Ext.warn.log("onFlashStateChange for unknown connection ID: " + javascriptId); + } + }, + + + registerConnection: function(conn) { + var i = this.connectionIndex; + this.conectionIndex = this.connectionIndex + 1; + this.liveConnections[i] = conn; + return i; + }, + + + injectFlashPlugin: function() { + var me = this, + flashLoaderPath, flashObjectPath; + + + + + + + me.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' + } + ] + } + ] + }); + + + + flashLoaderPath = [Ext.Loader.getPath('Ext.data.Connection'), '../../../plugins/flash/swfobject.js'].join('/'); + flashObjectPath = "/plugins/flash/FlashPlugin.swf"; + flashObjectPath = [Ext.Loader.getPath('Ext.data.Connection'), '../../plugins/flash/FlashPlugin.swf'].join('/'); + if (Ext.flashPluginPath) { + flashObjectPath = Ext.flashPluginPath; + } + + Ext.Loader.loadScript({ + url:flashLoaderPath, + onLoad: function() { + + var swfVersionStr = "11.4.0"; + + var xiSwfUrlStr = "playerProductInstall.swf"; + var flashvars = {}; + var params = {}; + params.quality = "high"; + params.bgcolor = "#ffffff"; + params.allowscriptaccess = "sameDomain"; + params.allowfullscreen = "true"; + var attributes = {}; + attributes.id = "ext-flash-polyfill"; + attributes.name = "polyfill"; + attributes.align = "middle"; + swfobject.embedSWF( + flashObjectPath, "ext-flash-polyfill", + "0", "0", + swfVersionStr, xiSwfUrlStr, + flashvars, params, attributes); + }, + onError: function() { + Ext.Error.raise("Could not load flash-loader file swfobject.js from " + flashLoader); + }, + scope: me + }); + Ext.globalEvents.addEvents("flashready"); + Ext.data.flash.BinaryXhr.flashPluginInjected = true; + } + + + }, + + + readyState: 0, + + + status: 0, + + + + statusText: "", + + + responseBytes: null, + + + javascriptId: null, + + + + constructor: function (config) { + + if (!Ext.data.flash.BinaryXhr.flashPluginInjected) { + Ext.data.flash.BinaryXhr.injectFlashPlugin(); + } + var me = this; + + Ext.apply(me, config); + me.requestHeaders = {}; + }, + + + abort: function () { + var me = this; + + if (me.readyState == 4) { + Ext.warn.log("Aborting a connection that's completed its transfer: " + this.url); + return; + } + + me.aborted = true; + + if (!Ext.data.flash.BinaryXhr.flashPluginActive) { + Ext.globalEvents.removeListener("flashready", me.onFlashReady, me); + return; + } + + Ext.data.flash.BinaryXhr.flashPlugin.abortRequest(me.javascriptId); + + delete Ext.data.flash.BinaryXhr.liveConnections[me.javascriptId]; + }, + + + getAllResponseHeaders: function () { + var headers = []; + Ext.Object.each(this.responseHeaders, function (name, value) { + headers.push(name + ': ' + value); + }); + return headers.join('\x0d\x0a'); + }, + + + getResponseHeader: function (header) { + var headers = this.responseHeaders; + return (headers && headers[header]) || null; + }, + + + open: function (method, url, async, user, password) { + var me = this; + me.method = method; + me.url = url; + me.async = async !== false; + me.user = user; + me.password = password; + + if (!me.async) { + Ext.Error.raise("Binary posts are only supported in async mode: " + url); + } + if (me.method != "POST") { + Ext.log.warn("Binary data can only be sent as a POST request: " + url); + } + }, + + + overrideMimeType: function (mimeType) { + this.mimeType = mimeType; + }, + + + send: function (body) { + var me = this; + me.body = body; + if (!Ext.data.flash.BinaryXhr.flashPluginActive) { + Ext.globalEvents.addListener("flashready", me.onFlashReady, me); + } else { + this.onFlashReady(); + } + }, + + + onFlashReady: function() { + var me = this, req, status; + me.javascriptId = Ext.data.flash.BinaryXhr.registerConnection(me); + + + req = { + method: me.method, + url: me.url, + user: me.user, + password: me.password, + mimeType: me.mimeType, + requestHeaders: me.requestHeaders, + body: me.body, + javascriptId: me.javascriptId + }; + status = Ext.data.flash.BinaryXhr.flashPlugin.postBinary(req); + }, + + + setReadyState: function (state) { + var me = this; + if (me.readyState != state) { + me.readyState = state; + me.onreadystatechange(); + } + }, + + + setRequestHeader: function (header, value) { + this.requestHeaders[header] = value; + }, + + + onreadystatechange: Ext.emptyFn, + + + parseData: function (data) { + var me = this; + + this.status = data.status || 0; + + me.responseHeaders = {}; + if (me.mimeType) { + me.responseHeaders["content-type"] = me.mimeType; + } + if (data.reason == "complete") { + + this.responseBytes = data.data; + me.responseHeaders["content-length"] = data.data.length; + } else if (data.reason == "error" || data.reason == "securityError") { + this.statusText = data.text; + me.responseHeaders["content-length"] = 0; + } + else { + Ext.Error.raise("Unkown reason code in data: " + data.reason); + } + }, + + + onFlashStateChange: function(state, data) { + var me = this; + if (state == 4) { + + me.parseData(data); + + delete Ext.data.flash.BinaryXhr.liveConnections[me.javascriptId]; + } + me.setReadyState(state); + } + +}); + + + +Ext.define('Ext.data.Connection', { + mixins: { + observable: Ext.util.Observable + }, + + + + + + statics: { + requestId: 0 + }, + + url: null, + async: true, + method: null, + username: '', + password: '', + + + disableCaching: true, + + + withCredentials: false, + + + binary: false, + + + cors: false, + + isXdr: false, + + defaultXdrContentType: 'text/plain', + + + disableCachingParam: '_dc', + + + timeout : 30000, + + + + + + + + + + useDefaultHeader : true, + defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8', + useDefaultXhrHeader : true, + defaultXhrHeader : 'XMLHttpRequest', + + constructor : function(config) { + config = config || {}; + Ext.apply(this, config); + + + + + this.requests = {}; + this.mixins.observable.constructor.call(this); + }, + + + request : function(options) { + options = options || {}; + var me = this, + scope = options.scope || window, + username = options.username || me.username, + password = options.password || me.password || '', + async, + requestOptions, + request, + headers, + xhr; + if (me.fireEvent('beforerequest', me, options) !== false) { + + requestOptions = me.setOptions(options, scope); + + if (me.isFormUpload(options)) { + me.upload(options.form, requestOptions.url, requestOptions.data, options); + return null; + } + + + if (options.autoAbort || me.autoAbort) { + me.abort(); + } + + + async = options.async !== false ? (options.async || me.async) : false; + xhr = me.openRequest(options, requestOptions, async, username, password); + + + if (!me.isXdr) { + headers = me.setupHeaders(xhr, options, requestOptions.data, requestOptions.params); + } + + + request = { + id: ++Ext.data.Connection.requestId, + xhr: xhr, + headers: headers, + options: options, + async: async, + binary: options.binary || me.binary, + timeout: setTimeout(function() { + request.timedout = true; + me.abort(request); + }, options.timeout || me.timeout) + }; + + me.requests[request.id] = request; + me.latestId = request.id; + + if (async) { + if (!me.isXdr) { + xhr.onreadystatechange = Ext.Function.bind(me.onStateChange, me, [request]); + } + } + + if (me.isXdr) { + me.processXdrRequest(request, xhr); + } + + + xhr.send(requestOptions.data); + if (!async) { + return me.onComplete(request); + } + return request; + } else { + Ext.callback(options.callback, options.scope, [options, undefined, undefined]); + return null; + } + }, + + processXdrRequest: function(request, xhr) { + var me = this; + + + delete request.headers; + + request.contentType = request.options.contentType || me.defaultXdrContentType; + + xhr.onload = Ext.Function.bind(me.onStateChange, me, [request, true]); + xhr.onerror = xhr.ontimeout = Ext.Function.bind(me.onStateChange, me, [request, false]); + }, + + processXdrResponse: function(response, xhr) { + + response.getAllResponseHeaders = function () { + return []; + }; + response.getResponseHeader = function () { + return ''; + }; + response.contentType = xhr.contentType || this.defaultXdrContentType; + }, + + + upload: function(form, url, params, options) { + form = Ext.getDom(form); + options = options || {}; + + var id = Ext.id(), + frame = document.createElement('iframe'), + hiddens = [], + encoding = 'multipart/form-data', + buf = { + target: form.target, + method: form.method, + encoding: form.encoding, + enctype: form.enctype, + action: form.action + }, + addField = function(name, value) { + hiddenItem = document.createElement('input'); + Ext.fly(hiddenItem).set({ + type: 'hidden', + value: value, + name: name + }); + form.appendChild(hiddenItem); + hiddens.push(hiddenItem); + }, + hiddenItem, obj, value, name, vLen, v, hLen, h; + + + Ext.fly(frame).set({ + id: id, + name: id, + cls: Ext.baseCSSPrefix + 'hide-display', + src: Ext.SSL_SECURE_URL + }); + + document.body.appendChild(frame); + + + if (document.frames) { + document.frames[id].name = id; + } + + Ext.fly(form).set({ + target: id, + method: 'POST', + enctype: encoding, + encoding: encoding, + action: url || buf.action + }); + + + if (params) { + obj = Ext.Object.fromQueryString(params) || {}; + + for (name in obj) { + if (obj.hasOwnProperty(name)) { + value = obj[name]; + if (Ext.isArray(value)) { + vLen = value.length; + for (v = 0; v < vLen; v++) { + addField(name, value[v]); + } + } else { + addField(name, value); + } + } + } + } + + Ext.fly(frame).on('load', Ext.Function.bind(this.onUploadComplete, this, [frame, options]), null, {single: !Ext.isOpera}); + form.submit(); + + Ext.fly(form).set(buf); + + hLen = hiddens.length; + + for (h = 0; h < hLen; h++) { + Ext.removeNode(hiddens[h]); + } + }, + + + onUploadComplete: function(frame, options) { + var me = this, + + response = { + responseText: '', + responseXML: null + }, callback, success, doc, contentNode; + + try { + doc = frame.contentWindow.document || frame.contentDocument || window.frames[frame.id].document; + + + if (doc) { + if (Ext.isOpera && doc.location == 'about:blank') { + return; + } + if (doc.body) { + + + + if ((contentNode = doc.body.firstChild) && /pre/i.test(contentNode.tagName)) { + response.responseText = contentNode.textContent || contentNode.innerText; + } + + + + else if ((contentNode = doc.getElementsByTagName('textarea')[0])) { + response.responseText = contentNode.value; + } + + else { + response.responseText = doc.body.textContent || doc.body.innerText; + } + } + + response.responseXML = doc.XMLDocument || doc; + callback = options.success; + success = true; + } + } catch (e) { + + response.responseText = '{success:false,message:"' + Ext.String.trim(e.message || e.description) + '"}'; + callback = options.failure; + success = false; + } + + me.fireEvent('requestcomplete', me, response, options); + + Ext.callback(callback, options.scope, [response, options]); + Ext.callback(options.callback, options.scope, [options, success, response]); + + setTimeout(function() { + Ext.removeNode(frame); + }, 100); + }, + + + isFormUpload: function(options) { + var form = this.getForm(options); + if (form) { + return (options.isUpload || (/multipart\/form-data/i).test(form.getAttribute('enctype'))); + } + return false; + }, + + + getForm: function(options) { + return Ext.getDom(options.form) || null; + }, + + + setOptions: function(options, scope) { + var me = this, + params = options.params || {}, + extraParams = me.extraParams, + urlParams = options.urlParams, + url = options.url || me.url, + jsonData = options.jsonData, + method, + disableCache, + data; + + + + if (Ext.isFunction(params)) { + params = params.call(scope, options); + } + + + if (Ext.isFunction(url)) { + url = url.call(scope, options); + } + + url = this.setupUrl(options, url); + + if (!url) { + Ext.Error.raise({ + options: options, + msg: 'No URL specified' + }); + } + + + data = options.rawData || options.binaryData || options.xmlData || jsonData || null; + if (jsonData && !Ext.isPrimitive(jsonData)) { + data = Ext.encode(data); + } + + if (options.binaryData) { + if (!Ext.isArray(options.binaryData)) { + Ext.log.warn("Binary submission data must be an array of byte values! Instead got " + typeof(options.binaryData)); + } + if (me.nativeBinaryPostSupport()) { + data = (new Uint8Array(options.binaryData)); + if ((Ext.isChrome && Ext.chromeVersion < 22) || Ext.isSafari || Ext.isGecko) { + data = data.buffer; + } + } + } + + + if (Ext.isObject(params)) { + params = Ext.Object.toQueryString(params); + } + + if (Ext.isObject(extraParams)) { + extraParams = Ext.Object.toQueryString(extraParams); + } + + params = params + ((extraParams) ? ((params) ? '&' : '') + extraParams : ''); + + urlParams = Ext.isObject(urlParams) ? Ext.Object.toQueryString(urlParams) : urlParams; + + params = this.setupParams(options, params); + + + method = (options.method || me.method || ((params || data) ? 'POST' : 'GET')).toUpperCase(); + this.setupMethod(options, method); + + + disableCache = options.disableCaching !== false ? (options.disableCaching || me.disableCaching) : false; + + if (method === 'GET' && disableCache) { + url = Ext.urlAppend(url, (options.disableCachingParam || me.disableCachingParam) + '=' + (new Date().getTime())); + } + + + if ((method == 'GET' || data) && params) { + url = Ext.urlAppend(url, params); + params = null; + } + + + if (urlParams) { + url = Ext.urlAppend(url, urlParams); + } + + return { + url: url, + method: method, + data: data || params || null + }; + }, + + + setupUrl: function(options, url) { + var form = this.getForm(options); + if (form) { + url = url || form.action; + } + return url; + }, + + + + setupParams: function(options, params) { + var form = this.getForm(options), + serializedForm; + if (form && !this.isFormUpload(options)) { + serializedForm = Ext.Element.serializeForm(form); + params = params ? (params + '&' + serializedForm) : serializedForm; + } + return params; + }, + + + setupMethod: function(options, method) { + if (this.isFormUpload(options)) { + return 'POST'; + } + return method; + }, + + + setupHeaders: function(xhr, options, data, params) { + var me = this, + headers = Ext.apply({}, options.headers || {}, me.defaultHeaders || {}), + contentType = me.defaultPostHeader, + jsonData = options.jsonData, + xmlData = options.xmlData, + type = 'Content-Type', + key, + header; + + if (!headers.hasOwnProperty(type) && (data || params)) { + if (data) { + if (options.rawData) { + contentType = 'text/plain'; + } else { + if (xmlData && Ext.isDefined(xmlData)) { + contentType = 'text/xml'; + } else if (jsonData && Ext.isDefined(jsonData)) { + contentType = 'application/json'; + } + } + } + headers[type] = contentType; + } + + if (me.useDefaultXhrHeader && !headers['X-Requested-With']) { + headers['X-Requested-With'] = me.defaultXhrHeader; + } + + + + if (headers[type] === undefined || headers[type] === null) { + delete headers[type]; + } + + + try { + for (key in headers) { + if (headers.hasOwnProperty(key)) { + header = headers[key]; + xhr.setRequestHeader(key, header); + } + } + } catch(e) { + me.fireEvent('exception', key, header); + } + return headers; + }, + + + newRequest: function (options) { + var me = this, + xhr; + + if (options.binaryData) { + + if (me.nativeBinaryPostSupport()) { + xhr = this.getXhrInstance(); + } else { + + xhr = new Ext.data.flash.BinaryXhr(); + } + } else if ((options.cors || me.cors) && Ext.isIE && Ext.ieVersion <= 9) { + xhr = me.getXdrInstance(); + me.isXdr = true; + } else { + xhr = me.getXhrInstance(); + } + + return xhr; + }, + + + openRequest: function (options, requestOptions, async, username, password) { + var me = this, + xhr = me.newRequest(options); + + if (username) { + xhr.open(requestOptions.method, requestOptions.url, async, username, password); + } else { + if (me.isXdr) { + xhr.open(requestOptions.method, requestOptions.url); + } else { + xhr.open(requestOptions.method, requestOptions.url, async); + } + } + + if (options.binary || me.binary) { + if (window.Uint8Array) { + xhr.responseType = 'arraybuffer'; + } else if (xhr.overrideMimeType) { + + + + + xhr.overrideMimeType('text\/plain; charset=x-user-defined'); + } else if (!Ext.isIE) { + Ext.log.warn("Your does not support loading binary data using Ajax."); + } + } + + if (options.withCredentials || me.withCredentials) { + xhr.withCredentials = true; + } + + return xhr; + }, + + + getXdrInstance: function() { + var xdr; + + if (Ext.ieVersion >= 8) { + xdr = new XDomainRequest(); + } else { + Ext.Error.raise({ + msg: 'Your browser does not support CORS' + }); + } + + return xdr; + }, + + + getXhrInstance: (function() { + var options = [function() { + return new XMLHttpRequest(); + }, function() { + return new ActiveXObject('MSXML2.XMLHTTP.3.0'); + }, function() { + return new ActiveXObject('MSXML2.XMLHTTP'); + }, function() { + return new ActiveXObject('Microsoft.XMLHTTP'); + }], i = 0, + len = options.length, + xhr; + + for (; i < len; ++i) { + try { + xhr = options[i]; + xhr(); + break; + } catch(e) { + } + } + return xhr; + }()), + + + isLoading : function(request) { + if (!request) { + request = this.getLatest(); + } + if (!(request && request.xhr)) { + return false; + } + + var state = request.xhr.readyState; + return ((request.xhr instanceof Ext.data.flash.BinaryXhr) && state != 4) || !(state === 0 || state == 4); + }, + + + abort : function(request) { + var me = this, + xhr; + + if (!request) { + request = me.getLatest(); + } + + if (request && me.isLoading(request)) { + + xhr = request.xhr; + try { + xhr.onreadystatechange = null; + } catch (e) { + + + xhr.onreadystatechange = Ext.emptyFn; + } + xhr.abort(); + me.clearTimeout(request); + if (!request.timedout) { + request.aborted = true; + } + me.onComplete(request); + me.cleanup(request); + } + }, + + + abortAll: function(){ + var requests = this.requests, + id; + + for (id in requests) { + if (requests.hasOwnProperty(id)) { + this.abort(requests[id]); + } + } + }, + + + getLatest: function(){ + var id = this.latestId, + request; + + if (id) { + request = this.requests[id]; + } + return request || null; + }, + + + onStateChange : function(request, xdrResult) { + var me = this; + + + if ((request.xhr && request.xhr.readyState == 4) || me.isXdr) { + me.clearTimeout(request); + me.onComplete(request, xdrResult); + me.cleanup(request); + Ext.EventManager.idleEvent.fire(); + } + }, + + + clearTimeout: function(request) { + clearTimeout(request.timeout); + delete request.timeout; + }, + + + cleanup: function(request) { + request.xhr = null; + delete request.xhr; + }, + + + onComplete : function(request, xdrResult) { + var me = this, + options = request.options, + xhr, + result, + success, + response; + + try { + xhr = request.xhr; + result = me.parseStatus(xhr.status); + if (result.success) { + + + + result.success = xhr.readyState === 4; + } + } catch (e) { + + result = { + success : false, + isException : false + }; + + } + success = me.isXdr ? xdrResult : result.success; + + if (success) { + response = me.createResponse(request); + me.fireEvent('requestcomplete', me, response, options); + Ext.callback(options.success, options.scope, [response, options]); + } else { + if (result.isException || request.aborted || request.timedout) { + response = me.createException(request); + } else { + response = me.createResponse(request); + } + me.fireEvent('requestexception', me, response, options); + Ext.callback(options.failure, options.scope, [response, options]); + } + Ext.callback(options.callback, options.scope, [options, success, response]); + delete me.requests[request.id]; + return response; + }, + + + parseStatus: function(status) { + + status = status == 1223 ? 204 : status; + + var success = (status >= 200 && status < 300) || status == 304, + isException = false; + + if (!success) { + switch (status) { + case 12002: + case 12029: + case 12030: + case 12031: + case 12152: + case 13030: + isException = true; + break; + } + } + return { + success: success, + isException: isException + }; + }, + + + createResponse : function(request) { + var me = this, + xhr = request.xhr, + isXdr = me.isXdr, + headers = {}, + lines = isXdr ? [] : xhr.getAllResponseHeaders().replace(/\r\n/g, '\n').split('\n'), + count = lines.length, + line, index, key, response, byteArray; + + while (count--) { + line = lines[count]; + index = line.indexOf(':'); + if (index >= 0) { + key = line.substr(0, index).toLowerCase(); + if (line.charAt(index + 1) == ' ') { + ++index; + } + headers[key] = line.substr(index + 1); + } + } + + request.xhr = null; + delete request.xhr; + + response = { + request: request, + requestId: request.id, + status: xhr.status, + statusText: xhr.statusText, + getResponseHeader: function(header) { + return headers[header.toLowerCase()]; + }, + getAllResponseHeaders: function() { + return headers; + } + }; + + if (isXdr) { + me.processXdrResponse(response, xhr); + } + + if (request.binary) { + response.responseBytes = me.getByteArray(xhr); + } else { + + + + + response.responseText = xhr.responseText; + response.responseXML = xhr.responseXML; + } + + + + xhr = null; + return response; + }, + + + createException : function(request) { + return { + request : request, + requestId : request.id, + status : request.aborted ? -1 : 0, + statusText : request.aborted ? 'transaction aborted' : 'communication failure', + aborted: request.aborted, + timedout: request.timedout + }; + }, + + + getByteArray: function(xhr) { + var response = xhr.response, + responseBody = xhr.responseBody, + byteArray, responseText, len, i; + + if (xhr instanceof Ext.data.flash.BinaryXhr) { + + byteArray = xhr.responseBytes; + } else if (window.Uint8Array) { + + + + byteArray = response ? new Uint8Array(response) : []; + } else if (Ext.isIE9p) { + + + + + try { + byteArray = new VBArray(responseBody).toArray(); + } catch(e) { + + + + + byteArray = []; + } + } else if (Ext.isIE) { + + + + + + if (!this.self.vbScriptInjected) { + this.injectVBScript(); + } + getIEByteArray(xhr.responseBody, byteArray = []); + } else { + + + byteArray = []; + responseText = xhr.responseText; + len = responseText.length; + for (i = 0; i < len; i++) { + + + + byteArray.push(responseText.charCodeAt(i) & 0xFF); + } + } + + return byteArray; + }, + + + injectVBScript: function() { + var scriptTag = document.createElement('script'); + scriptTag.type = 'text/vbscript'; + scriptTag.text = [ + 'Function getIEByteArray(byteArray, out)', + 'Dim len, i', + 'len = LenB(byteArray)', + 'For i = 1 to len', + 'out.push(AscB(MidB(byteArray, i, 1)))', + 'Next', + 'End Function' + ].join('\n'); + Ext.getHead().dom.appendChild(scriptTag); + this.self.vbScriptInjected = true; + }, + + + nativeBinaryPostSupport: function() { + return Ext.isChrome || + (Ext.isSafari && Ext.isDefined(window.Uint8Array)) || + (Ext.isGecko && Ext.isDefined(window.Uint8Array)); + } + + +}); + + + +Ext.define('Ext.Ajax', { + extend: Ext.data.Connection , + singleton: true, + + + + + + + + + + + + + + + + + autoAbort : false +}); + + + + +Ext.define('Ext.util.Offset', { + + + + statics: { + fromObject: function(obj) { + return new this(obj.x, obj.y); + } + }, + + + + constructor: function(x, y) { + this.x = (x != null && !isNaN(x)) ? x : 0; + this.y = (y != null && !isNaN(y)) ? y : 0; + + return this; + }, + + copy: function() { + return new Ext.util.Offset(this.x, this.y); + }, + + copyFrom: function(p) { + this.x = p.x; + this.y = p.y; + }, + + toString: function() { + return "Offset[" + this.x + "," + this.y + "]"; + }, + + equals: function(offset) { + if(!(offset instanceof this.statics())) { + Ext.Error.raise('Offset must be an instance of Ext.util.Offset'); + } + + return (this.x == offset.x && this.y == offset.y); + }, + + round: function(to) { + if (!isNaN(to)) { + var factor = Math.pow(10, to); + this.x = Math.round(this.x * factor) / factor; + this.y = Math.round(this.y * factor) / factor; + } else { + this.x = Math.round(this.x); + this.y = Math.round(this.y); + } + }, + + isZero: function() { + return this.x == 0 && this.y == 0; + } +}); + + + +Ext.define('Ext.util.Region', { + + + + + + statics: { + + getRegion: function(el) { + return Ext.fly(el).getRegion(); + }, + + + from: function(o) { + return new this(o.top, o.right, o.bottom, o.left); + } + }, + + + + + constructor : function(t, r, b, l) { + var me = this; + me.y = me.top = me[1] = t; + me.right = r; + me.bottom = b; + me.x = me.left = me[0] = l; + }, + + + contains : function(region) { + var me = this; + return (region.x >= me.x && + region.right <= me.right && + region.y >= me.y && + region.bottom <= me.bottom); + + }, + + + intersect : function(region) { + var me = this, + t = Math.max(me.y, region.y), + r = Math.min(me.right, region.right), + b = Math.min(me.bottom, region.bottom), + l = Math.max(me.x, region.x); + + if (b > t && r > l) { + return new this.self(t, r, b, l); + } + else { + return false; + } + }, + + + union : function(region) { + var me = this, + t = Math.min(me.y, region.y), + r = Math.max(me.right, region.right), + b = Math.max(me.bottom, region.bottom), + l = Math.min(me.x, region.x); + + return new this.self(t, r, b, l); + }, + + + constrainTo : function(r) { + var me = this, + constrain = Ext.Number.constrain; + me.top = me.y = constrain(me.top, r.y, r.bottom); + me.bottom = constrain(me.bottom, r.y, r.bottom); + me.left = me.x = constrain(me.left, r.x, r.right); + me.right = constrain(me.right, r.x, r.right); + return me; + }, + + + adjust : function(t, r, b, l) { + var me = this; + me.top = me.y += t; + me.left = me.x += l; + me.right += r; + me.bottom += b; + return me; + }, + + + getOutOfBoundOffset: function(axis, p) { + if (!Ext.isObject(axis)) { + if (axis == 'x') { + return this.getOutOfBoundOffsetX(p); + } else { + return this.getOutOfBoundOffsetY(p); + } + } else { + p = axis; + var d = new Ext.util.Offset(); + d.x = this.getOutOfBoundOffsetX(p.x); + d.y = this.getOutOfBoundOffsetY(p.y); + return d; + } + + }, + + + getOutOfBoundOffsetX: function(p) { + if (p <= this.x) { + return this.x - p; + } else if (p >= this.right) { + return this.right - p; + } + + return 0; + }, + + + getOutOfBoundOffsetY: function(p) { + if (p <= this.y) { + return this.y - p; + } else if (p >= this.bottom) { + return this.bottom - p; + } + + return 0; + }, + + + isOutOfBound: function(axis, p) { + if (!Ext.isObject(axis)) { + if (axis == 'x') { + return this.isOutOfBoundX(p); + } else { + return this.isOutOfBoundY(p); + } + } else { + p = axis; + return (this.isOutOfBoundX(p.x) || this.isOutOfBoundY(p.y)); + } + }, + + + isOutOfBoundX: function(p) { + return (p < this.x || p > this.right); + }, + + + isOutOfBoundY: function(p) { + return (p < this.y || p > this.bottom); + }, + + + restrict: function(axis, p, factor) { + if (Ext.isObject(axis)) { + var newP; + + factor = p; + p = axis; + + if (p.copy) { + newP = p.copy(); + } + else { + newP = { + x: p.x, + y: p.y + }; + } + + newP.x = this.restrictX(p.x, factor); + newP.y = this.restrictY(p.y, factor); + return newP; + } else { + if (axis == 'x') { + return this.restrictX(p, factor); + } else { + return this.restrictY(p, factor); + } + } + }, + + + restrictX : function(p, factor) { + if (!factor) { + factor = 1; + } + + if (p <= this.x) { + p -= (p - this.x) * factor; + } + else if (p >= this.right) { + p -= (p - this.right) * factor; + } + return p; + }, + + + restrictY : function(p, factor) { + if (!factor) { + factor = 1; + } + + if (p <= this.y) { + p -= (p - this.y) * factor; + } + else if (p >= this.bottom) { + p -= (p - this.bottom) * factor; + } + return p; + }, + + + 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(p) { + var me = this; + me.top = me.y = me[1] = p.y; + me.right = p.right; + me.bottom = p.bottom; + me.left = me.x = me[0] = p.x; + + return this; + }, + + + toString: function() { + return "Region[" + this.top + "," + this.right + "," + this.bottom + "," + this.left + "]"; + }, + + + translateBy: function(x, y) { + if (arguments.length == 1) { + y = x.y; + x = x.x; + } + var me = this; + me.top = me.y += y; + me.right += x; + me.bottom += y; + me.left = me.x += x; + + return me; + }, + + + round: function() { + var me = this; + me.top = me.y = Math.round(me.y); + me.right = Math.round(me.right); + me.bottom = Math.round(me.bottom); + me.left = me.x = Math.round(me.x); + + return me; + }, + + + equals: function(region) { + return (this.top == region.top && this.right == region.right && this.bottom == region.bottom && this.left == region.left); + } +}); + + + + + + +Ext.define('Ext.dd.DragDropManager', { + 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: Ext.baseCSSPrefix + 'dd-drag-current', + + + _execOnAll: function(sMethod, args) { + var ids = this.ids, + i, j, oDD, item; + + for (i in ids) { + if (ids.hasOwnProperty(i)) { + item = ids[i]; + for (j in item) { + if (item.hasOwnProperty(j)) { + oDD = item[j]; + if (! this.isTypeOfDD(oDD)) { + continue; + } + oDD[sMethod].apply(oDD, args); + } + } + } + } + }, + + + _onLoad: function() { + + this.init(); + + var Event = Ext.EventManager; + Event.on(document, "mouseup", this.handleMouseUp, this, true); + Event.on(document, "mousemove", this.handleMouseMove, this, true); + Event.on(window, "unload", this._onUnload, this, true); + Event.on(window, "resize", this._onResize, this, true); + + + }, + + + _onResize: function(e) { + this._execOnAll("resetConstraints", []); + }, + + + lock: function() { this.locked = true; }, + + + unlock: function() { this.locked = false; }, + + + isLocked: function() { return this.locked; }, + + + locationCache: {}, + + + useCache: true, + + + clickPixelThresh: 3, + + + clickTimeThresh: 350, + + + dragThreshMet: false, + + + clickTimeout: null, + + + startX: 0, + + + startY: 0, + + + regDragDrop: function(oDD, sGroup) { + if (!this.initialized) { this.init(); } + + if (!this.ids[sGroup]) { + this.ids[sGroup] = {}; + } + this.ids[sGroup][oDD.id] = oDD; + }, + + + removeDDFromGroup: function(oDD, sGroup) { + if (!this.ids[sGroup]) { + this.ids[sGroup] = {}; + } + + var obj = this.ids[sGroup]; + if (obj && obj[oDD.id]) { + delete obj[oDD.id]; + } + }, + + + _remove: function(oDD, clearGroup) { + var me = this, + ids = me.ids, + groups = oDD.groups, + g; + + + + if (me.clearingAll) { + return; + } + + if (me.dragCurrent === oDD) { + me.dragCurrent = null; + } + + for (g in groups) { + if (groups.hasOwnProperty(g)) { + if (clearGroup) { + delete ids[g]; + } else if (ids[g]) { + delete ids[g][oDD.id]; + } + } + } + delete me.handleIds[oDD.id]; + }, + + + regHandle: function(sDDId, sHandleId) { + if (!this.handleIds[sDDId]) { + this.handleIds[sDDId] = {}; + } + this.handleIds[sDDId][sHandleId] = sHandleId; + }, + + + isDragDrop: function(id) { + return ( this.getDDById(id) ) ? true : false; + }, + + + getRelated: function(p_oDD, bTargetsOnly) { + var oDDs = [], + i, j, dd; + for (i in p_oDD.groups) { + for (j in this.ids[i]) { + dd = this.ids[i][j]; + if (! this.isTypeOfDD(dd)) { + continue; + } + if (!bTargetsOnly || dd.isTarget) { + oDDs[oDDs.length] = dd; + } + } + } + + return oDDs; + }, + + + isLegalTarget: function (oDD, oTargetDD) { + var targets = this.getRelated(oDD, true), + i, len; + for (i=0, len=targets.length;i me.clickPixelThresh || diffY > me.clickPixelThresh) { + me.startDrag(me.startX, me.startY); + } + } + + if (me.dragThreshMet) { + current.b4Drag(e); + current.onDrag(e); + if (!current.moveOnly) { + me.fireEvents(e, false); + } + } + + me.stopEvent(e); + + return true; + }, + + + fireEvents: function(e, isDrop) { + var me = this, + dragCurrent = me.dragCurrent, + dragEl, + oldDragElTop, + mousePoint = e.getPoint(), + overTarget, + overTargetEl, + allTargets = [], + oldOvers = [], + outEvts = [], + overEvts = [], + dropEvts = [], + enterEvts = [], + xy, + needsSort, + i, + len, + sGroup; + + + + if (!dragCurrent || dragCurrent.isLocked()) { + return; + } + + + + + + + + if (!me.notifyOccluded && (!Ext.supports.PointerEvents || Ext.isIE10m || Ext.isOpera) && !(dragCurrent.deltaX < 0 || dragCurrent.deltaY < 0)) { + dragEl = dragCurrent.getDragEl(); + oldDragElTop = dragEl.style.top; + + + dragEl.style.visibility = 'hidden'; + xy = e.getXY(); + e.target = document.elementFromPoint(xy[0], xy[1]); + dragEl.style.visibility = 'visible'; + dragEl.style.top = oldDragElTop; + } + + + + for (i in me.dragOvers) { + + overTarget = me.dragOvers[i]; + delete me.dragOvers[i]; + + + if (!me.isTypeOfDD(overTarget) || overTarget.isDestroyed) { + continue; + } + + + if (me.notifyOccluded) { + if (!this.isOverTarget(mousePoint, overTarget, me.mode)) { + outEvts.push(overTarget); + } + } + + else { + if (!e.within(overTarget.getEl())) { + outEvts.push(overTarget); + } + } + + oldOvers[i] = true; + } + + + + + for (sGroup in dragCurrent.groups) { + + if ("string" != typeof sGroup) { + continue; + } + + + for (i in me.ids[sGroup]) { + overTarget = me.ids[sGroup][i]; + + + + + + + + if (me.isTypeOfDD(overTarget) && + (overTargetEl = overTarget.getEl()) && + (overTarget.isTarget) && + (!overTarget.isLocked()) && + (Ext.fly(overTargetEl).isVisible(true)) && + ((overTarget != dragCurrent) || (dragCurrent.ignoreSelf === false))) { + + + if (me.notifyOccluded) { + + + if ((overTarget.zIndex = me.getZIndex(overTargetEl)) !== -1) { + needsSort = true; + } + allTargets.push(overTarget); + } + + else { + if (e.within(overTarget.getEl())) { + allTargets.push(overTarget); + break; + } + } + } + } + } + + + if (needsSort) { + Ext.Array.sort(allTargets, me.byZIndex); + } + + + + for (i = 0, len = allTargets.length; i < len; i++) { + overTarget = allTargets[i]; + + + if (me.isOverTarget(mousePoint, overTarget, me.mode)) { + + if (isDrop) { + dropEvts.push( overTarget ); + + } else { + + + if (!oldOvers[overTarget.id]) { + enterEvts.push( overTarget ); + + } else { + overEvts.push( overTarget ); + } + me.dragOvers[overTarget.id] = overTarget; + } + + + if (!me.notifyOccluded) { + break; + } + } + } + + if (me.mode) { + if (outEvts.length) { + dragCurrent.b4DragOut(e, outEvts); + dragCurrent.onDragOut(e, outEvts); + } + + if (enterEvts.length) { + dragCurrent.onDragEnter(e, enterEvts); + } + + if (overEvts.length) { + dragCurrent.b4DragOver(e, overEvts); + dragCurrent.onDragOver(e, overEvts); + } + + if (dropEvts.length) { + dragCurrent.b4DragDrop(e, dropEvts); + dragCurrent.onDragDrop(e, dropEvts); + } + + } else { + + for (i=0, len=outEvts.length; i= this.minX; i = i - iTickSize) { + if (!tickMap[i]) { + this.xTicks[this.xTicks.length] = i; + tickMap[i] = true; + } + } + + for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) { + if (!tickMap[i]) { + this.xTicks[this.xTicks.length] = i; + tickMap[i] = true; + } + } + + Ext.Array.sort(this.xTicks, this.DDMInstance.numericSort); + }, + + + setYTicks: function(iStartY, iTickSize) { + this.yTicks = []; + this.yTickSize = iTickSize; + + var tickMap = {}, + i; + + for (i = this.initPageY; i >= this.minY; i = i - iTickSize) { + if (!tickMap[i]) { + this.yTicks[this.yTicks.length] = i; + tickMap[i] = true; + } + } + + for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) { + if (!tickMap[i]) { + this.yTicks[this.yTicks.length] = i; + tickMap[i] = true; + } + } + + Ext.Array.sort(this.yTicks, this.DDMInstance.numericSort); + }, + + + setXConstraint: function(iLeft, iRight, iTickSize) { + this.leftConstraint = iLeft; + this.rightConstraint = iRight; + + this.minX = this.initPageX - iLeft; + this.maxX = this.initPageX + iRight; + if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); } + + 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(iUp, iDown, iTickSize) { + this.topConstraint = iUp; + this.bottomConstraint = iDown; + + this.minY = this.initPageY - iUp; + this.maxY = this.initPageY + iDown; + if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); } + + this.constrainY = true; + + }, + + + resetConstraints: function() { + + if (this.initPageX || this.initPageX === 0) { + + var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0, + dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0; + + this.setInitPosition(dx, dy); + + + } 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(val, tickArray) { + if (!tickArray) { + + + return val; + } else if (tickArray[0] >= val) { + + + return tickArray[0]; + } else { + var i, len, next, diff1, diff2; + for (i=0, len=tickArray.length; i= val) { + diff1 = val - tickArray[i]; + diff2 = tickArray[next] - val; + return (diff2 > diff1) ? tickArray[i] : tickArray[next]; + } + } + + + + return tickArray[tickArray.length - 1]; + } + }, + + + toString: function() { + return ("DragDrop " + this.id); + } + +}); + + + + + + +Ext.define('Ext.dd.DD', { + extend: Ext.dd.DragDrop , + + + + constructor: function(id, sGroup, config) { + if (id) { + this.init(id, sGroup, config); + } + }, + + + scroll: true, + + + autoOffset: function(iPageX, iPageY) { + var x = iPageX - this.startPageX, + y = iPageY - this.startPageY; + this.setDelta(x, y); + }, + + + setDelta: function(iDeltaX, iDeltaY) { + this.deltaX = iDeltaX; + this.deltaY = iDeltaY; + }, + + + setDragElPos: function(iPageX, iPageY) { + + + + var el = this.getDragEl(); + this.alignElWithMouse(el, iPageX, iPageY); + }, + + + alignElWithMouse: function(el, iPageX, iPageY) { + var oCoord = this.getTargetCoord(iPageX, iPageY), + fly = el.dom ? el : Ext.fly(el, '_dd'), + elSize = fly.getSize(), + EL = Ext.Element, + vpSize, + aCoord, + newLeft, + newTop; + + if (!this.deltaSetXY) { + vpSize = this.cachedViewportSize = { width: EL.getDocumentWidth(), height: EL.getDocumentHeight() }; + aCoord = [ + Math.max(0, Math.min(oCoord.x, vpSize.width - elSize.width)), + Math.max(0, Math.min(oCoord.y, vpSize.height - elSize.height)) + ]; + fly.setXY(aCoord); + newLeft = this.getLocalX(fly); + newTop = fly.getLocalY(); + this.deltaSetXY = [newLeft - oCoord.x, newTop - oCoord.y]; + } else { + vpSize = this.cachedViewportSize; + this.setLocalXY( + fly, + Math.max(0, Math.min(oCoord.x + this.deltaSetXY[0], vpSize.width - elSize.width)), + Math.max(0, Math.min(oCoord.y + this.deltaSetXY[1], vpSize.height - elSize.height)) + ); + } + + this.cachePosition(oCoord.x, oCoord.y); + this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth); + return oCoord; + }, + + + cachePosition: function(iPageX, iPageY) { + if (iPageX) { + this.lastPageX = iPageX; + this.lastPageY = iPageY; + } else { + var aCoord = Ext.Element.getXY(this.getEl()); + this.lastPageX = aCoord[0]; + this.lastPageY = aCoord[1]; + } + }, + + + autoScroll: function(x, y, h, w) { + + if (this.scroll) { + + var clientH = Ext.Element.getViewHeight(), + + clientW = Ext.Element.getViewWidth(), + + st = this.DDMInstance.getScrollTop(), + + sl = this.DDMInstance.getScrollLeft(), + + bot = h + y, + + right = w + x, + + + + toBot = (clientH + st - y - this.deltaY), + + toRight = (clientW + sl - x - this.deltaX), + + + thresh = 40, + + + + scrAmt = (document.all) ? 80 : 30; + + + + if ( bot > clientH && toBot < thresh ) { + window.scrollTo(sl, st + scrAmt); + } + + + + if ( y < st && st > 0 && y - st < thresh ) { + window.scrollTo(sl, st - scrAmt); + } + + + + if ( right > clientW && toRight < thresh ) { + window.scrollTo(sl + scrAmt, st); + } + + + + if ( x < sl && sl > 0 && x - sl < thresh ) { + window.scrollTo(sl - scrAmt, st); + } + } + }, + + + getTargetCoord: function(iPageX, iPageY) { + var x = iPageX - this.deltaX, + y = iPageY - this.deltaY; + + if (this.constrainX) { + if (x < this.minX) { + x = this.minX; + } + if (x > this.maxX) { + x = this.maxX; + } + } + + if (this.constrainY) { + if (y < this.minY) { + y = this.minY; + } + if (y > this.maxY) { + y = this.maxY; + } + } + + x = this.getTick(x, this.xTicks); + y = this.getTick(y, this.yTicks); + + + return {x: x, y: y}; + }, + + + applyConfig: function() { + this.callParent(); + this.scroll = (this.config.scroll !== false); + }, + + + b4MouseDown: function(e) { + + this.autoOffset(e.getPageX(), e.getPageY()); + }, + + + b4Drag: function(e) { + this.setDragElPos(e.getPageX(), e.getPageY()); + }, + + toString: function() { + return ("DD " + this.id); + }, + + getLocalX: function(el) { + return el.getLocalX(); + }, + + setLocalXY: function(el, x, y) { + el.setLocalXY(x, y); + } + + + + + + +}); + + + +Ext.define('Ext.util.Floating', { + + + + + focusOnToFront: true, + + + shadow: 'sides', + + + constrain: false, + + + + + + constructor: function (dom) { + var me = this; + + + me.fixed = me.fixed && !(Ext.isIE6 || Ext.isIEQuirks); + + me.el = new Ext.dom.Layer(Ext.apply({ + preventSync : true, + hideMode : me.hideMode, + shadow : (typeof me.shadow != 'undefined') ? me.shadow : 'sides', + shadowOffset : me.shadowOffset, + constrain : false, + fixed : me.fixed, + shim : (me.shim === false) ? false : undefined + }, me.floating), dom); + + + + if (me.modal && !(Ext.enableFocusManager)) { + me.mon(me.el, { + keydown: me.onKeyDown, + scope: me + }); + } + + + me.mon(me.el, { + mousedown: me.onMouseDown, + scope: me + }); + + + me.floating = true; + + + + + me.registerWithOwnerCt(); + + me.initHierarchyEvents(); + }, + + initFloatConstrain: function () { + var me = this, + floatParent = me.floatParent; + + + + if ((me.constrain || me.constrainHeader) && !me.constrainTo) { + me.constrainTo = floatParent ? floatParent.getTargetEl() : me.container; + } + }, + + initHierarchyEvents: function() { + var me = this, + syncHidden = this.syncHidden; + + if (!me.hasHierarchyEventListeners) { + me.mon(me.hierarchyEventSource, { + hide: syncHidden, + collapse: syncHidden, + show: syncHidden, + expand: syncHidden, + added: syncHidden, + scope: me + }); + me.hasHierarchyEventListeners = true; + } + }, + + registerWithOwnerCt: function() { + var me = this, + ownerCt = me.ownerCt, + zip = me.zIndexParent; + + if (zip) { + zip.unregisterFloatingItem(me); + } + + + + zip = me.zIndexParent = me.up('[floating]'); + + + + + me.floatParent = ownerCt || zip; + me.initFloatConstrain(); + delete me.ownerCt; + + if (zip) { + zip.registerFloatingItem(me); + } else { + Ext.WindowManager.register(me); + } + }, + + + onKeyDown: function(e) { + var me = this, + shift, + focusables, + first, + last; + + + + + + + + + + + + + if (e.getKey() == Ext.EventObject.TAB) { + shift = e.shiftKey; + focusables = me.el.query(':focusable'); + first = focusables[0]; + last = focusables[focusables.length - 1]; + if (first && last && e.target === (shift ? first : last)) { + e.stopEvent(); + (shift ? last : first).focus(false, true); + } + } + }, + + + + onMouseDown: function (e) { + var focusTask = this.focusTask; + + if (this.floating && + + + + (!focusTask || !focusTask.id)) { + + this.toFront(!!e.getTarget(':focusable')); + } + }, + + + syncShadow : function() { + if (this.floating) { + this.el.sync(true); + } + }, + + onBeforeFloatLayout: function(){ + this.el.preventSync = true; + }, + + onAfterFloatLayout: function(){ + delete this.el.preventSync; + this.syncShadow(); + }, + + + syncHidden: function() { + var me = this, + hidden = me.hidden || !me.rendered, + hierarchicallyHidden = me.hierarchicallyHidden = me.isHierarchicallyHidden(), + pendingShow = me.pendingShow; + + if (hidden !== hierarchicallyHidden) { + if (hierarchicallyHidden) { + me.hide(); + me.pendingShow = true; + } else if (pendingShow) { + delete me.pendingShow; + if (pendingShow.length) { + me.show.apply(me, pendingShow); + } else { + me.show(); + } + } + } + }, + + + + + + + setZIndex: function(index) { + var me = this; + + me.el.setZIndex(index); + + + index += 10; + + + + if (me.floatingDescendants) { + index = Math.floor(me.floatingDescendants.setBase(index) / 100) * 100 + 10000; + } + return index; + }, + + + doConstrain: function(constrainTo) { + var me = this, + + + + xy = me.calculateConstrainedPosition(constrainTo, null, true); + + + if (xy) { + me.setPosition(xy); + } + }, + + + toFront: function(preventFocus) { + var me = this, + zip = me.zIndexParent, + preventFocusSetting = me.preventFocusOnActivate; + + + + if (zip && me.bringParentToFront !== false) { + zip.toFront(true); + } + + if (!Ext.isDefined(preventFocus)) { + preventFocus = !me.focusOnToFront; + } + + if (preventFocus) { + me.preventFocusOnActivate = true; + } + if (me.zIndexManager.bringToFront(me, preventFocus)) { + if (!preventFocus) { + + + + me.focus(false, true); + } + if (me.hasListeners.tofront) { + me.fireEvent('tofront', me, me.el.getZIndex()); + } + } + + + me.preventFocusOnActivate = preventFocusSetting; + return me; + }, + + + setActive: function(active, newActive) { + var me = this; + + if (active) { + if (me.el.shadow && !me.maximized) { + me.el.enableShadow(true); + } + if (!me.preventFocusOnActivate) { + me.focus(false, true); + } + me.fireEvent('activate', me); + } else { + + + if (me.isWindow && (newActive && newActive.isWindow) && me.hideShadowOnDeactivate) { + me.el.disableShadow(); + } + me.fireEvent('deactivate', me); + } + }, + + + toBack: function() { + this.zIndexManager.sendToBack(this); + return this; + }, + + + center: function() { + var me = this, + xy; + + if (me.isVisible()) { + xy = me.getAlignToXY(me.container, 'c-c'); + me.setPagePosition(xy); + } else { + me.needsCenter = true; + } + return me; + }, + + onFloatShow: function() { + if (this.needsCenter) { + this.center(); + } + delete this.needsCenter; + + if (this.toFrontOnShow) { + this.toFront(); + } + }, + + + fitContainer: function(animate) { + var me = this, + parent = me.floatParent, + container = parent ? parent.getTargetEl() : me.container, + newBox = container.getViewSize(false), + newPosition = parent || (container.dom !== document.body) ? + + [0, 0] : + + container.getXY(); + + newBox.x = newPosition[0]; + newBox.y = newPosition[1]; + me.setBox(newBox, animate); + } +}); + + + +Ext.define('Ext.Component', { + + + + alias: ['widget.component', 'widget.box'], + + extend: Ext.AbstractComponent , + + + + + + + + + mixins: { + floating: Ext.util.Floating + }, + + statics: { + + DIRECTION_TOP: 'top', + DIRECTION_RIGHT: 'right', + DIRECTION_BOTTOM: 'bottom', + DIRECTION_LEFT: 'left', + + VERTICAL_DIRECTION_Re: /^(?:top|bottom)$/, + + + + INVALID_ID_CHARS_Re: /[\.,\s]/g + }, + + + + + + + resizeHandles: 'all', + + + + + + + + + floating: false, + + + defaultAlign: 'c-c', + + + alignTarget: null, + + + toFrontOnShow: true, + + + + + + + + + + + + + + + + + + + + hideMode: 'display', + + offsetsCls: Ext.baseCSSPrefix + 'hide-offsets', + + bubbleEvents: [], + + defaultComponentLayoutType: 'autocomponent', + + + + + + + + + + constructor: function(config) { + var me = this; + + config = config || {}; + if (config.initialConfig) { + + + if (config.isAction) { + me.baseAction = config; + } + config = config.initialConfig; + + } + else if (config.tagName || config.dom || Ext.isString(config)) { + + config = { + applyTo: config, + id: config.id || config + }; + } + + me.callParent([config]); + + + + if (me.baseAction){ + me.baseAction.addComponent(me); + } + }, + + + initComponent: function() { + var me = this; + + me.callParent(); + + if (me.listeners) { + me.on(me.listeners); + me.listeners = null; + } + me.enableBubble(me.bubbleEvents); + }, + + afterRender: function() { + var me = this; + + me.callParent(); + + if (!(me.x && me.y) && (me.pageX || me.pageY)) { + me.setPagePosition(me.pageX, me.pageY); + } + }, + + + setAutoScroll : function(scroll) { + var me = this; + + me.autoScroll = !!scroll; + + + + + if (me.rendered) { + me.getOverflowEl().setStyle(me.getOverflowStyle()); + } + me.updateLayout(); + return me; + }, + + + setOverflowXY: function(overflowX, overflowY) { + var me = this, + argCount = arguments.length, + ownerCt = me.ownerCt; + + if (argCount) { + me.overflowX = overflowX || ''; + if (argCount > 1) { + me.overflowY = overflowY || ''; + } + } + + + + + if (me.rendered) { + me.getOverflowEl().setStyle(me.getOverflowStyle()); + } + + + + (ownerCt || me).updateLayout(); + return me; + }, + + beforeRender: function () { + var me = this, + floating = me.floating, + cls; + + if (floating) { + me.addCls(Ext.baseCSSPrefix + 'layer'); + + cls = floating.cls; + if (cls) { + me.addCls(cls); + } + } + + return me.callParent(); + }, + + beforeLayout: function(){ + this.callParent(arguments); + if (this.floating) { + this.onBeforeFloatLayout(); + } + }, + + afterComponentLayout: function(){ + this.callParent(arguments); + if (this.floating) { + this.onAfterFloatLayout(); + } + }, + + + makeFloating : function (dom) { + this.mixins.floating.constructor.call(this, dom); + }, + + wrapPrimaryEl: function (dom) { + if (this.floating) { + this.makeFloating(dom); + } else { + this.callParent(arguments); + } + }, + + initResizable: function(resizable) { + var me = this; + + resizable = Ext.apply({ + target: me, + dynamic: false, + constrainTo: me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : null), + handles: me.resizeHandles + }, resizable); + resizable.target = me; + me.resizer = new Ext.resizer.Resizer(resizable); + }, + + getDragEl: function() { + return this.el; + }, + + initDraggable: function() { + var me = this, + + + + + dragTarget = (me.resizer && me.resizer.el !== me.el) ? me.resizerComponent = new Ext.Component({ + ariaRole: 'presentation', + el: me.resizer.el, + rendered: true, + container: me.container + }) : me, + ddConfig = Ext.applyIf({ + el: dragTarget.getDragEl(), + constrainTo: (me.constrain||me.draggable.constrain) ? (me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : me.container)) : undefined + }, me.draggable); + + + if (me.constrain || me.constrainDelegate) { + ddConfig.constrain = me.constrain; + ddConfig.constrainDelegate = me.constrainDelegate; + } + + me.dd = new Ext.util.ComponentDragger(dragTarget, ddConfig); + }, + + + scrollBy: function(deltaX, deltaY, animate) { + var el; + + if ((el = this.getTargetEl()) && el.dom) { + el.scrollBy.apply(el, arguments); + } + }, + + + setLoading: function (load, targetEl) { + var me = this, + config = { + target: me + }; + + if (me.rendered) { + + if (load !== false) { + if (Ext.isString(load)) { + config.msg = load; + } else { + Ext.apply(config, load); + } + + if (!me.loadMask || !me.loadMask.isLoadMask) { + + + if (targetEl && config.useTargetEl == null) { + config.useTargetEl = true; + } + me.loadMask = new Ext.LoadMask(config); + } + + else { + Ext.apply(me.loadMask, config); + } + + if (me.loadMask.isVisible()) { + me.loadMask.afterShow(); + } + + else { + me.loadMask.show(); + } + } + + else { + if (me.loadMask && me.loadMask.isLoadMask) { + me.loadMask.hide(); + } + } + } + return me.loadMask; + }, + + beforeSetPosition: function () { + var me = this, + pos = me.callParent(arguments), + adj; + + if (pos) { + adj = me.adjustPosition(pos.x, pos.y); + pos.x = adj.x; + pos.y = adj.y; + } + return pos || null; + }, + + afterSetPosition: function(ax, ay) { + this.onPosition(ax, ay); + this.fireEvent('move', this, ax, ay); + }, + + + showAt: function(x, y, animate) { + var me = this; + + + + if (!me.rendered && (me.autoRender || me.floating)) { + me.x = x; + me.y = y; + return me.show(); + } + if (me.floating) { + me.setPosition(x, y, animate); + } else { + me.setPagePosition(x, y, animate); + } + me.show(); + }, + + + showBy: function(cmp, pos, off) { + var me = this; + + if (!me.floating) { + Ext.log.warn('Using showBy on a non-floating component'); + } + + if (me.floating && cmp) { + me.alignTarget = cmp; + + if (pos) { + me.defaultAlign = pos; + } + + if (off) { + me.alignOffset = off; + } + + me.show(); + + + if (!me.hidden) { + me.alignTo(cmp, pos || me.defaultAlign, off || me.alignOffset); + } + } + + return me; + }, + + + setPagePosition: function(x, y, animate) { + var me = this, + p, + floatParentBox; + + if (Ext.isArray(x)) { + y = x[1]; + x = x[0]; + } + me.pageX = x; + me.pageY = y; + + if (me.floating) { + + + if (me.isContainedFloater()) { + floatParentBox = me.floatParent.getTargetEl().getViewRegion(); + if (Ext.isNumber(x) && Ext.isNumber(floatParentBox.left)) { + x -= floatParentBox.left; + } + if (Ext.isNumber(y) && Ext.isNumber(floatParentBox.top)) { + y -= floatParentBox.top; + } + } else { + p = me.el.translateXY(x, y); + x = p.x; + y = p.y; + } + + me.setPosition(x, y, animate); + } else { + p = me.el.translateXY(x, y); + me.setPosition(p.x, p.y, animate); + } + + return me; + }, + + + + isContainedFloater: function() { + return (this.floating && this.floatParent); + }, + + + updateBox : function(box){ + this.setSize(box.width, box.height); + this.setPagePosition(box.x, box.y); + return this; + }, + + + getOuterSize: function() { + var el = this.el; + return { + width: el.getWidth() + el.getMargin('lr'), + height: el.getHeight() + el.getMargin('tb') + }; + }, + + + adjustPosition: function(x, y) { + var me = this, + floatParentBox; + + + if (me.isContainedFloater()) { + floatParentBox = me.floatParent.getTargetEl().getViewRegion(); + x += floatParentBox.left; + y += floatParentBox.top; + } + + return { + x: x, + y: y + }; + }, + + + getPosition: function(local) { + var me = this, + xy, + isContainedFloater = me.isContainedFloater(), + floatParentBox; + + + if ((local === true) && !isContainedFloater) { + return [me.getLocalX(), me.getLocalY()]; + } + + xy = me.getXY(); + + + if ((local === true) && isContainedFloater) { + floatParentBox = me.floatParent.getTargetEl().getViewRegion(); + xy[0] -= floatParentBox.left; + xy[1] -= floatParentBox.top; + } + return xy; + }, + + getId: function() { + var me = this, + xtype; + + if (!me.id) { + xtype = me.getXType(); + if (xtype) { + xtype = xtype.replace(Ext.Component.INVALID_ID_CHARS_Re, '-'); + } else { + xtype = Ext.name.toLowerCase() + '-comp'; + } + me.id = xtype + '-' + me.getAutoId(); + } + return me.id; + }, + + + show: function(animateTarget, cb, scope) { + var me = this, + rendered = me.rendered; + + if (me.hierarchicallyHidden || (me.floating && !rendered && me.isHierarchicallyHidden())) { + + + + if (!rendered) { + + + + + + + + + + me.initHierarchyEvents(); + } + + if (arguments.length > 1) { + arguments[0] = null; + me.pendingShow = arguments; + } else { + me.pendingShow = true; + } + } else if (rendered && me.isVisible()) { + if (me.floating) { + me.onFloatShow(); + } + } else { + if (me.fireEvent('beforeshow', me) !== false) { + me.hidden = false; + delete this.getHierarchyState().hidden; + + + + + + + + + + Ext.suspendLayouts(); + if (!rendered && (me.autoRender || me.floating)) { + me.doAutoRender(); + rendered = me.rendered; + } + + if (rendered) { + me.beforeShow(); + Ext.resumeLayouts(); + me.onShow.apply(me, arguments); + me.afterShow.apply(me, arguments); + } else { + Ext.resumeLayouts(true); + } + } else { + me.onShowVeto(); + } + } + return me; + }, + + onShowVeto: Ext.emptyFn, + + + beforeShow: Ext.emptyFn, + + + onShow: function() { + var me = this; + + me.el.show(); + me.callParent(arguments); + + + if (me.floating) { + if (me.maximized) { + me.fitContainer(); + } + else if (me.constrain) { + me.doConstrain(); + } + } + }, + + getAnimateTarget: function(target){ + target = target || this.animateTarget; + if (target) { + target = target.isComponent ? target.getEl() : Ext.get(target); + } + return target || null; + }, + + + afterShow: function(animateTarget, cb, scope) { + var me = this, + myEl = me.el, + fromBox, + toBox, + ghostPanel; + + + animateTarget = me.getAnimateTarget(animateTarget); + + + if (!me.ghost) { + animateTarget = null; + } + + if (animateTarget) { + toBox = { + x: myEl.getX(), + y: myEl.getY(), + width: myEl.dom.offsetWidth, + height: myEl.dom.offsetHeight + }; + fromBox = { + x: animateTarget.getX(), + y: animateTarget.getY(), + width: animateTarget.dom.offsetWidth, + height: animateTarget.dom.offsetHeight + }; + myEl.addCls(me.offsetsCls); + ghostPanel = me.ghost(); + ghostPanel.el.stopAnimation(); + + + ghostPanel.setX(-10000); + + me.ghostBox = toBox; + ghostPanel.el.animate({ + from: fromBox, + to: toBox, + listeners: { + afteranimate: function() { + delete ghostPanel.componentLayout.lastComponentSize; + me.unghost(); + delete me.ghostBox; + myEl.removeCls(me.offsetsCls); + me.onShowComplete(cb, scope); + } + } + }); + } + else { + me.onShowComplete(cb, scope); + } + me.fireHierarchyEvent('show'); + }, + + + onShowComplete: function(cb, scope) { + var me = this; + if (me.floating) { + me.onFloatShow(); + } + Ext.callback(cb, scope || me); + me.fireEvent('show', me); + delete me.hiddenByLayout; + }, + + + hide: function(animateTarget, cb, scope) { + var me = this, + continueHide; + + if (me.pendingShow) { + + + delete me.pendingShow; + } if (!(me.rendered && !me.isVisible())) { + continueHide = (me.fireEvent('beforehide', me) !== false); + if (me.hierarchicallyHidden || continueHide) { + me.hidden = true; + me.getHierarchyState().hidden = true; + if (me.rendered) { + me.onHide.apply(me, arguments); + } + } + } + return me; + }, + + + onHide: function(animateTarget, cb, scope) { + var me = this, + ghostPanel, + fromSize, + toBox, + activeEl = Ext.Element.getActiveElement(); + + + if (activeEl === me.el || me.el.contains(activeEl)) { + Ext.fly(activeEl).blur(); + } + + + animateTarget = me.getAnimateTarget(animateTarget); + + + if (!me.ghost) { + animateTarget = null; + } + + if (animateTarget) { + toBox = { + x: animateTarget.getX(), + y: animateTarget.getY(), + width: animateTarget.dom.offsetWidth, + height: animateTarget.dom.offsetHeight + }; + ghostPanel = me.ghost(); + ghostPanel.el.stopAnimation(); + fromSize = me.getSize(); + ghostPanel.el.animate({ + to: toBox, + listeners: { + afteranimate: function() { + delete ghostPanel.componentLayout.lastComponentSize; + ghostPanel.el.hide(); + ghostPanel.setHiddenState(true); + ghostPanel.el.setSize(fromSize); + me.afterHide(cb, scope); + } + } + }); + } + me.el.hide(); + if (!animateTarget) { + me.afterHide(cb, scope); + } + }, + + + afterHide: function(cb, scope) { + var me = this; + + me.hiddenByLayout = null; + + + + Ext.AbstractComponent.prototype.onHide.call(me); + + Ext.callback(cb, scope || me); + me.fireEvent('hide', me); + me.fireHierarchyEvent('hide'); + }, + + + onDestroy: function() { + var me = this; + + + if (me.rendered) { + Ext.destroy( + me.dd, + me.resizer, + me.proxy, + me.proxyWrap, + me.resizerComponent, + me.loadMask + ); + } + delete me.focusTask; + me.callParent(); + }, + + deleteMembers: function() { + var args = arguments, + len = args.length, + i = 0; + for (; i < len; ++i) { + delete this[args[i]]; + } + }, + + + focus: function(selectText, delay, callback, scope) { + var me = this, + focusEl, + focusElDom, + containerScrollTop; + + + if (delay) { + me.getFocusTask().delay(Ext.isNumber(delay) ? delay : 10, me.focus, me, [selectText, false, callback, scope]); + return me; + } + + + if (me.focusTask) { + me.focusTask.cancel(); + } + + if (me.rendered && !me.isDestroyed && me.isVisible(true) && (focusEl = me.getFocusEl())) { + + + + if (focusEl.isComponent) { + return focusEl.focus(selectText, delay); + } + + + if ((focusElDom = focusEl.dom)) { + + + if (focusEl.needsTabIndex()) { + focusElDom.tabIndex = -1; + } + + if (me.floating) { + containerScrollTop = me.container.dom.scrollTop; + } + + + + + focusEl.focus(); + if (selectText) { + if (Ext.isArray(selectText)) { + if (me.selectText) { + me.selectText.apply(me, selectText); + } + } else { + focusElDom.select(); + } + } + + + Ext.callback(callback, scope); + } + + + + if (me.floating) { + + + if (me !== me.zIndexManager.getActive()) { + me.toFront(true); + } + + if (containerScrollTop !== undefined) { + me.container.dom.scrollTop = containerScrollTop; + } + } + } + return me; + }, + + + getFocusTask: function() { + if (!this.focusTask) { + + + Ext.Component.prototype.focusTask = new Ext.util.DelayedTask(); + } + return this.focusTask; + }, + + + cancelFocus: function() { + var task = this.focusTask; + if (task) { + task.cancel(); + } + }, + + + blur: function() { + var me = this, + focusEl; + + if (me.rendered && (focusEl = me.getFocusEl())) { + me.blurring = true; + focusEl.blur(); + delete me.blurring; + } + return me; + }, + + getEl: function() { + return this.el; + }, + + + getResizeEl: function() { + return this.el; + }, + + + getPositionEl: function() { + return this.el; + }, + + + getActionEl: function() { + return this.el; + }, + + + getVisibilityEl: function() { + return this.el; + }, + + + getRefOwner: function() { + + return this.ownerCt || this.ownerCmp || this.floatParent; + }, + + + getBubbleTarget: function() { + return this.getRefOwner(); + }, + + + getContentTarget: function() { + return this.el; + }, + + + cloneConfig: function(overrides) { + overrides = overrides || {}; + var id = overrides.id || Ext.id(), + cfg = Ext.applyIf(overrides, this.initialConfig), + self; + + cfg.id = id; + + self = Ext.getClass(this); + + + return new self(cfg); + }, + + + getXType: function() { + return this.self.xtype; + }, + + + findParentBy: function(fn) { + var p; + + + for (p = this.getBubbleTarget(); p && !fn(p, this); p = p.getBubbleTarget()) { + + } + return p || null; + }, + + + findParentByType: function(xtype) { + return Ext.isFunction(xtype) ? + this.findParentBy(function(p) { + return p.constructor === xtype; + }) + : + this.up(xtype); + }, + + + bubble: function(fn, scope, args) { + var p = this; + while (p) { + if (fn.apply(scope || p, args || [p]) === false) { + break; + } + p = p.getBubbleTarget(); + } + return this; + }, + + getProxy: function() { + var me = this, + target; + + if (!me.proxy) { + target = Ext.getBody(); + me.proxy = me.el.createProxy(Ext.baseCSSPrefix + 'proxy-el', target, true); + } + return me.proxy; + }, + + + fireHierarchyEvent: function (ename) { + this.hierarchyEventSource.fireEvent(ename, this); + }, + + onAdded: function() { + this.callParent(arguments); + if (this.hierarchyEventSource.hasListeners.added) { + this.fireHierarchyEvent('added'); + } + } +}, function () { + + this.hierarchyEventSource = this.prototype.hierarchyEventSource = new Ext.util.Observable({ events: { + hide: true, + show: true, + collapse: true, + expand: true, + added: true + }}); +}); + + + +Ext.define('Ext.layout.container.border.Region', { + override: 'Ext.Component', + + + initBorderRegion: function () { + var me = this; + + if (!me._borderRegionInited) { + me._borderRegionInited = true; + + + + me.addStateEvents(['changeregion', 'changeweight']); + + + + + + Ext.override(me, { + getState: function () { + var state = me.callParent(); + + + + state = me.addPropertyToState(state, 'region'); + state = me.addPropertyToState(state, 'weight'); + + return state; + } + }); + } + }, + + + getOwningBorderContainer: function () { + var layout = this.getOwningBorderLayout(); + return layout && layout.owner; + }, + + + getOwningBorderLayout: function () { + + var layout = this.ownerLayout; + return (layout && layout.isBorderLayout) ? layout : null; + }, + + + setBorderRegion: function (region) { + var me = this, + borderLayout, + old = me.region; + + if (region !== old) { + borderLayout = me.getOwningBorderLayout(); + if (borderLayout) { + var regionFlags = borderLayout.regionFlags[region], + placeholder = me.placeholder, + splitter = me.splitter, + owner = borderLayout.owner, + regionMeta = borderLayout.regionMeta, + collapsed = me.collapsed || me.floated, + delta, items, index; + + if (me.fireEventArgs('beforechangeregion', [me, region]) === false) { + return old; + } + Ext.suspendLayouts(); + + me.region = region; + Ext.apply(me, regionFlags); + + if (me.updateCollapseTool) { + me.updateCollapseTool(); + } + + if (splitter) { + + Ext.apply(splitter, regionFlags); + splitter.updateOrientation(); + + items = owner.items; + index = items.indexOf(me); + if (index >= 0) { + delta = regionMeta[region].splitterDelta; + if (items.getAt(index + delta) !== splitter) { + + items.remove(splitter); + index = items.indexOf(me); + if (delta > 0) { + ++index; + } + + items.insert(index, splitter); + + + + + } + } + } + if (placeholder) { + + + + + if (collapsed) { + me.expand(false); + } + + owner.remove(placeholder); + me.placeholder = null; + + if (collapsed) { + me.collapse(null, false); + } + } + + owner.updateLayout(); + Ext.resumeLayouts(true); + + me.fireEventArgs('changeregion', [me, old]); + } else { + me.region = region; + } + } + + return old; + }, + + + setRegionWeight: function (weight) { + var me = this, + ownerCt = me.getOwningBorderContainer(), + placeholder = me.placeholder, + old = me.weight; + + if (weight !== old) { + if (me.fireEventArgs('beforechangeweight', [me, weight]) !== false) { + me.weight = weight; + if (placeholder) { + placeholder.weight = weight; + } + if (ownerCt) { + ownerCt.updateLayout(); + } + me.fireEventArgs('changeweight', [me, old]); + } + } + + return old; + } +}); + + +Ext.define('ExtThemeNeptune.Component', { + override: 'Ext.Component', + + initComponent: function() { + this.callParent(); + + if (this.dock && this.border === undefined) { + this.border = false; + } + }, + + initStyles: function() { + var me = this, + border = me.border; + + if (me.dock) { + + + me.border = null; + } + me.callParent(arguments); + me.border = border; + } +}); + + + +Ext.define('Ext.ElementLoader', { + + + + mixins: { + observable: Ext.util.Observable + }, + + + + + + + statics: { + Renderer: { + Html: function(loader, response, active){ + loader.getTarget().update(response.responseText, active.scripts === true); + return true; + } + } + }, + + + + + url: null, + + + params: null, + + + baseParams: null, + + + autoLoad: false, + + + target: null, + + + loadMask: false, + + + ajaxOptions: null, + + + scripts: false, + + + + + + + + + + + + + isLoader: true, + + constructor: function(config) { + var me = this, + autoLoad; + + config = config || {}; + Ext.apply(me, config); + me.setTarget(me.target); + me.addEvents( + + 'beforeload', + + + 'exception', + + + 'load' + ); + + + me.mixins.observable.constructor.call(me); + + if (me.autoLoad) { + autoLoad = me.autoLoad; + if (autoLoad === true) { + autoLoad = {}; + } + me.load(autoLoad); + } + }, + + + setTarget: function(target){ + var me = this; + target = Ext.get(target); + if (me.target && me.target != target) { + me.abort(); + } + me.target = target; + }, + + + getTarget: function(){ + return this.target || null; + }, + + + abort: function(){ + var active = this.active; + if (active !== undefined) { + Ext.Ajax.abort(active.request); + if (active.mask) { + this.removeMask(); + } + delete this.active; + } + }, + + + removeMask: function(){ + this.target.unmask(); + }, + + + addMask: function(mask){ + this.target.mask(mask === true ? null : mask); + }, + + + load: function(options) { + if (!this.target) { + Ext.Error.raise('A valid target is required when loading content'); + } + + options = Ext.apply({}, options); + + var me = this, + mask = Ext.isDefined(options.loadMask) ? options.loadMask : me.loadMask, + params = Ext.apply({}, options.params), + ajaxOptions = Ext.apply({}, options.ajaxOptions), + callback = options.callback || me.callback, + scope = options.scope || me.scope || me; + + Ext.applyIf(ajaxOptions, me.ajaxOptions); + Ext.applyIf(options, ajaxOptions); + + Ext.applyIf(params, me.params); + Ext.apply(params, me.baseParams); + + Ext.applyIf(options, { + url: me.url + }); + + if (!options.url) { + Ext.Error.raise('You must specify the URL from which content should be loaded'); + } + + Ext.apply(options, { + scope: me, + params: params, + callback: me.onComplete + }); + + if (me.fireEvent('beforeload', me, options) === false) { + return; + } + + if (mask) { + me.addMask(mask); + } + + me.active = { + options: options, + mask: mask, + scope: scope, + callback: callback, + success: options.success || me.success, + failure: options.failure || me.failure, + renderer: options.renderer || me.renderer, + scripts: Ext.isDefined(options.scripts) ? options.scripts : me.scripts + }; + me.active.request = Ext.Ajax.request(options); + me.setOptions(me.active, options); + }, + + + setOptions: Ext.emptyFn, + + + onComplete: function(options, success, response) { + var me = this, + active = me.active, + scope; + + if (active) { + scope = active.scope; + if (success) { + success = me.getRenderer(active.renderer).call(me, me, response, active) !== false; + } + + if (success) { + Ext.callback(active.success, scope, [me, response, options]); + me.fireEvent('load', me, response, options); + } else { + Ext.callback(active.failure, scope, [me, response, options]); + me.fireEvent('exception', me, response, options); + } + Ext.callback(active.callback, scope, [me, success, response, options]); + if (active.mask) { + me.removeMask(); + } + } + + delete me.active; + }, + + + getRenderer: function(renderer){ + if (Ext.isFunction(renderer)) { + return renderer; + } + return this.statics().Renderer.Html; + }, + + + startAutoRefresh: function(interval, options){ + var me = this; + me.stopAutoRefresh(); + me.autoRefresh = setInterval(function(){ + me.load(options); + }, interval); + }, + + + stopAutoRefresh: function(){ + clearInterval(this.autoRefresh); + delete this.autoRefresh; + }, + + + isAutoRefreshing: function(){ + return Ext.isDefined(this.autoRefresh); + }, + + + destroy: function(){ + var me = this; + me.stopAutoRefresh(); + delete me.target; + me.abort(); + me.clearListeners(); + } +}); + + + +Ext.define('Ext.ComponentLoader', { + + + + extend: Ext.ElementLoader , + + statics: { + Renderer: { + Data: function(loader, response, active){ + var success = true; + try { + loader.getTarget().update(Ext.decode(response.responseText)); + } catch (e) { + success = false; + } + return success; + }, + + Component: function(loader, response, active){ + var success = true, + target = loader.getTarget(), + items = []; + + if (!target.isContainer) { + Ext.Error.raise({ + target: target, + msg: 'Components can only be loaded into a container' + }); + } + + try { + items = Ext.decode(response.responseText); + } catch (e) { + success = false; + } + + if (success) { + target.suspendLayouts(); + if (active.removeAll) { + target.removeAll(); + } + target.add(items); + target.resumeLayouts(true); + } + return success; + } + } + }, + + + + + target: null, + + + loadMask: false, + + + + + renderer: 'html', + + + setTarget: function(target){ + var me = this; + + if (Ext.isString(target)) { + target = Ext.getCmp(target); + } + + if (me.target && me.target != target) { + me.abort(); + } + me.target = target; + }, + + + removeMask: function(){ + this.target.setLoading(false); + }, + + + addMask: function(mask){ + this.target.setLoading(mask); + }, + + + setOptions: function(active, options){ + active.removeAll = Ext.isDefined(options.removeAll) ? options.removeAll : this.removeAll; + }, + + + getRenderer: function(renderer){ + if (Ext.isFunction(renderer)) { + return renderer; + } + + var renderers = this.statics().Renderer; + switch (renderer) { + case 'component': + return renderers.Component; + case 'data': + return renderers.Data; + default: + return Ext.ElementLoader.Renderer.Html; + } + } +}); + + + +Ext.define('Ext.layout.SizeModel', { + constructor: function (config) { + var me = this, + SizeModel = me.self, + sizeModelsArray = SizeModel.sizeModelsArray, + name; + + Ext.apply(me, config); + + me[name = me.name] = true; + + me.fixed = !(me.auto = me.natural || me.shrinkWrap); + + + sizeModelsArray[me.ordinal = sizeModelsArray.length] = + SizeModel[name] = + SizeModel.sizeModels[name] = me; + }, + + statics: { + + sizeModelsArray: [], + + + sizeModels: {} + }, + + + + + + + calculated: false, + + + configured: false, + + + constrainedMax: false, + + + constrainedMin: false, + + + + + natural: false, + + + shrinkWrap: false, + + + calculatedFromConfigured: false, + + + calculatedFromNatural: false, + + + calculatedFromShrinkWrap: false, + + + names: null +}, +function () { + var SizeModel = this, + sizeModelsArray = SizeModel.sizeModelsArray, + i, j, n, pairs, sizeModel; + + + + + new SizeModel({ + name: 'calculated' + }); + + new SizeModel({ + name: 'configured', + names: { width: 'width', height: 'height' } + }); + + new SizeModel({ + name: 'natural' + }); + + new SizeModel({ + name: 'shrinkWrap' + }); + + + + + + new SizeModel({ + name: 'calculatedFromConfigured', + configured: true, + names: { width: 'width', height: 'height' } + }); + + new SizeModel({ + name: 'calculatedFromNatural', + natural: true + }); + + new SizeModel({ + name: 'calculatedFromShrinkWrap', + shrinkWrap: true + }); + + new SizeModel({ + name: 'constrainedMax', + configured: true, + constrained: true, + names: { width: 'maxWidth', height: 'maxHeight' } + }); + + new SizeModel({ + name: 'constrainedMin', + configured: true, + constrained: true, + names: { width: 'minWidth', height: 'minHeight' } + }); + + new SizeModel({ + name: 'constrainedDock', + configured: true, + constrained: true, + constrainedByMin: true, + names: { width: 'dockConstrainedWidth', height: 'dockConstrainedHeight' } + }); + + for (i = 0, n = sizeModelsArray.length; i < n; ++i) { + sizeModel = sizeModelsArray[i]; + + + sizeModel.pairsByHeightOrdinal = pairs = []; + + for (j = 0; j < n; ++j) { + pairs.push({ + width: sizeModel, + height: sizeModelsArray[j] + }); + } + } +}); + + + +Ext.define('Ext.layout.Layout', { + + + + + + + + + isLayout: true, + initialized: false, + running: false, + + autoSizePolicy: { + readsWidth: 1, + readsHeight: 1, + setsWidth: 0, + setsHeight: 0 + }, + + statics: { + layoutsByType: {}, + + create: function(layout, defaultType) { + var ClassManager = Ext.ClassManager, + layoutsByType = this.layoutsByType, + alias, className, config, layoutClass, type, load; + + if (!layout || typeof layout === 'string') { + type = layout || defaultType; + config = {}; + } else if (layout.isLayout) { + return layout; + } else { + config = layout; + type = layout.type || defaultType; + } + + if (!(layoutClass = layoutsByType[type])) { + alias = 'layout.' + type; + className = ClassManager.getNameByAlias(alias); + + + if (!className) { + load = true; + } + + layoutClass = ClassManager.get(className); + if (load || !layoutClass) { + return ClassManager.instantiateByAlias(alias, config || {}); + } + layoutsByType[type] = layoutClass; + } + + return new layoutClass(config); + } + }, + + constructor : function(config) { + var me = this; + + me.id = Ext.id(null, me.type + '-'); + Ext.apply(me, config); + me.layoutCount = 0; + }, + + + + + beginLayout: Ext.emptyFn, + + + beginLayoutCycle: function (ownerContext) { + var me = this, + context = me.context, + changed; + + if (me.lastWidthModel != ownerContext.widthModel) { + if (me.lastWidthModel) { + changed = true; + } + me.lastWidthModel = ownerContext.widthModel; + } + + if (me.lastHeightModel != ownerContext.heightModel) { + if (me.lastWidthModel) { + changed = true; + } + me.lastHeightModel = ownerContext.heightModel; + } + + if (changed) { + (context = ownerContext.context).clearTriggers(me, false); + context.clearTriggers(me, true); + me.triggerCount = 0; + } + }, + + + + + + + + + finishedLayout: function (ownerContext) { + this.lastWidthModel = ownerContext.widthModel; + this.lastHeightModel = ownerContext.heightModel; + this.ownerContext = null; + }, + + + + redoLayout: Ext.emptyFn, + undoLayout: Ext.emptyFn, + + getAnimatePolicy: function() { + return this.animatePolicy; + }, + + + getItemSizePolicy: function (item) { + return this.autoSizePolicy; + }, + + isItemBoxParent: function (itemContext) { + return false; + }, + + isItemLayoutRoot: function (item) { + var sizeModel = item.getSizeModel(), + width = sizeModel.width, + height = sizeModel.height; + + + + if (!item.componentLayout.lastComponentSize && (width.calculated || height.calculated)) { + return false; + } + + + return !width.shrinkWrap && !height.shrinkWrap; + }, + + isItemShrinkWrap: function (item) { + return item.shrinkWrap; + }, + + isRunning: function () { + return !!this.ownerContext; + }, + + + + + + getItemsRenderTree: function (items, renderCfgs) { + var length = items.length, + i, item, itemConfig, result; + + if (length) { + result = []; + for (i = 0; i < length; ++i) { + item = items[i]; + + + + if (!item.rendered) { + + + + + + + if (renderCfgs && (renderCfgs[item.id] !== undefined)) { + itemConfig = renderCfgs[item.id]; + } else { + + this.configureItem(item); + itemConfig = item.getRenderTree(); + if (renderCfgs) { + renderCfgs[item.id] = itemConfig; + } + } + + + if (itemConfig) { + result.push(itemConfig); + } + } + } + } + + return result; + }, + + finishRender: Ext.emptyFn, + + finishRenderItems: function (target, items) { + var length = items.length, + i, item; + + for (i = 0; i < length; i++) { + item = items[i]; + + + if (item.rendering) { + + + item.finishRender(i); + + this.afterRenderItem(item); + } + } + }, + + renderChildren: function () { + var me = this, + items = me.getLayoutItems(), + target = me.getRenderTarget(); + + me.renderItems(items, target); + }, + + + renderItems : function(items, target) { + var me = this, + ln = items.length, + i = 0, + item; + + if (ln) { + Ext.suspendLayouts(); + for (; i < ln; i++) { + item = items[i]; + if (item && !item.rendered) { + me.renderItem(item, target, i); + } else if (!me.isValidParent(item, target, i)) { + me.moveItem(item, target, i); + } else { + + me.configureItem(item); + } + } + Ext.resumeLayouts(true); + } + }, + + + isValidParent : function(item, target, position) { + var itemDom = item.el ? item.el.dom : Ext.getDom(item), + targetDom = (target && target.dom) || target, + parentNode = itemDom.parentNode, + className; + + + if (parentNode) { + className = parentNode.className; + if (className && className.indexOf(Ext.baseCSSPrefix + 'resizable-wrap') !== -1) { + itemDom = itemDom.parentNode; + } + } + + + if (itemDom && targetDom) { + if (typeof position == 'number') { + position = this.getPositionOffset(position); + return itemDom === targetDom.childNodes[position]; + } + return itemDom.parentNode === targetDom; + } + + return false; + }, + + getPositionOffset: function(position){ + return position; + }, + + + configureItem: function(item) { + item.ownerLayout = this; + }, + + + renderItem : function(item, target, position) { + var me = this; + if (!item.rendered) { + me.configureItem(item); + item.render(target, position); + me.afterRenderItem(item); + } + }, + + + moveItem : function(item, target, position) { + target = target.dom || target; + if (typeof position == 'number') { + position = target.childNodes[position]; + } + target.insertBefore(item.el.dom, position || null); + item.container = Ext.get(target); + this.configureItem(item); + }, + + + onContentChange: function () { + this.owner.updateLayout(); + return true; + }, + + + initLayout : function() { + this.initialized = true; + }, + + + setOwner : function(owner) { + this.owner = owner; + }, + + + getLayoutItems : function() { + return []; + }, + + onAdd: function (item) { + item.ownerLayout = this; + }, + afterRenderItem: Ext.emptyFn, + onRemove : Ext.emptyFn, + onDestroy : Ext.emptyFn, + + + afterRemove : function(item) { + var me = this, + el = item.el, + owner = me.owner, + removeClasses; + + if (item.rendered) { + removeClasses = [].concat(me.itemCls || []); + if (owner.itemCls) { + removeClasses = Ext.Array.push(removeClasses, owner.itemCls); + } + if (removeClasses.length) { + el.removeCls(removeClasses); + } + } + + delete item.ownerLayout; + }, + + + destroy : function() { + var me = this, + target; + + if (me.targetCls) { + target = me.getTarget(); + if (target) { + target.removeCls(me.targetCls); + } + } + + me.onDestroy(); + }, + + sortWeightedItems: function (items, reverseProp) { + for (var i = 0, length = items.length; i < length; ++i) { + items[i].$i = i; + } + + Ext.Array.sort(items, function (item1, item2) { + var ret = item2.weight - item1.weight; + + if (!ret) { + ret = item1.$i - item2.$i; + if (item1[reverseProp]) { + ret = -ret; + } + } + + return ret; + }); + + for (i = 0; i < length; ++i) { + delete items[i].$i; + } + } +}, function () { + var Layout = this; + + Layout.prototype.sizeModels = Layout.sizeModels = Ext.layout.SizeModel.sizeModels; +}); + + + +Ext.define('Ext.layout.container.Container', { + + + + alias: ['layout.container'], + + extend: Ext.layout.Layout , + + alternateClassName: 'Ext.layout.ContainerLayout', + + mixins: { + elementCt: Ext.util.ElementContainer + }, + + + + + + type: 'container', + + + + + + + beginCollapse: Ext.emptyFn, + + + beginExpand: Ext.emptyFn, + + + animatePolicy: null, + + childEls: [ + + 'overflowPadderEl' + ], + + renderTpl: [ + '{%this.renderBody(out,values)%}' + ], + + usesContainerHeight: true, + usesContainerWidth: true, + usesHeight: true, + usesWidth: true, + + constructor: function () { + this.callParent(arguments); + this.mixins.elementCt.constructor.call(this); + }, + + destroy : function() { + this.callParent(); + this.mixins.elementCt.destroy.call(this); + }, + + + beginLayout: function (ownerContext) { + this.callParent(arguments); + + ownerContext.targetContext = ownerContext.paddingContext = ownerContext.getEl('getTarget', this); + + this.cacheChildItems(ownerContext); + }, + + beginLayoutCycle: function (ownerContext, firstCycle) { + var me = this; + + me.callParent(arguments); + + if (firstCycle) { + if (me.usesContainerHeight) { + ++ownerContext.consumersContainerHeight; + } + if (me.usesContainerWidth) { + ++ownerContext.consumersContainerWidth; + } + } + }, + + cacheChildItems: function (ownerContext) { + var context = ownerContext.context, + childItems = [], + items = this.getVisibleItems(), + length = items.length, + i; + + ownerContext.childItems = childItems; + ownerContext.visibleItems = items; + + for (i = 0; i < length; ++i) { + childItems.push(context.getCmp(items[i])); + } + }, + + cacheElements: function () { + var owner = this.owner; + + this.applyChildEls(owner.el, owner.id); + }, + + + configureItem: function(item) { + var me = this, + itemCls = me.itemCls, + ownerItemCls = me.owner.itemCls, + needsCopy, + addClasses; + + + item.ownerLayout = me; + + if (itemCls) { + + if (typeof itemCls === 'string') { + addClasses = [itemCls]; + } else { + addClasses = itemCls; + needsCopy = !!addClasses; + } + } + if (ownerItemCls) { + + if (needsCopy) { + addClasses = Ext.Array.clone(addClasses); + } + addClasses = Ext.Array.push(addClasses || [], ownerItemCls); + } + if (addClasses) { + item.addCls(addClasses); + } + }, + + doRenderBody: function (out, renderData) { + + + + this.renderItems(out, renderData); + this.renderContent(out, renderData); + }, + + doRenderContainer: function (out, renderData) { + + + + var me = renderData.$comp.layout, + tpl = me.getRenderTpl(), + data = me.getRenderData(); + + tpl.applyOut(data, out); + }, + + doRenderItems: function (out, renderData) { + + + + var me = renderData.$layout, + tree = me.getRenderTree(); + + if (tree) { + Ext.DomHelper.generateMarkup(tree, out); + } + }, + + finishRender: function () { + var me = this, + target, items; + + me.callParent(); + + me.cacheElements(); + + target = me.getRenderTarget(); + items = me.getLayoutItems(); + + if (me.targetCls && !me.getTarget().hasCls(me.targetCls)) { + Ext.log.warn('targetCls is missing. This may mean that getTargetEl() is being overridden but not applyTargetCls(). ' + me.owner.id); + } + + me.finishRenderItems(target, items); + }, + + + notifyOwner: function() { + this.owner.afterLayout(this); + }, + + + getContainerSize : function(ownerContext, inDom) { + + + + + + + var targetContext = ownerContext.targetContext, + frameInfo = targetContext.getFrameInfo(), + padding = ownerContext.paddingContext.getPaddingInfo(), + got = 0, + needed = 0, + gotWidth, gotHeight, width, height; + + + + + + + + + + if (!ownerContext.widthModel.shrinkWrap) { + ++needed; + width = inDom ? targetContext.getDomProp('width') : targetContext.getProp('width'); + gotWidth = (typeof width == 'number'); + if (gotWidth) { + ++got; + width -= frameInfo.width + padding.width; + if (width < 0) { + width = 0; + } + } + } + + if (!ownerContext.heightModel.shrinkWrap) { + ++needed; + height = inDom ? targetContext.getDomProp('height') : targetContext.getProp('height'); + gotHeight = (typeof height == 'number'); + if (gotHeight) { + ++got; + height -= frameInfo.height + padding.height; + if (height < 0) { + height = 0; + } + } + } + + return { + width: width, + height: height, + needed: needed, + got: got, + gotAll: got == needed, + gotWidth: gotWidth, + gotHeight: gotHeight + }; + }, + + + + + + + + + + getPositionOffset: function(position) { + if (!this.createsInnerCt) { + var offset = this.owner.itemNodeOffset; + if (offset) { + position += offset; + } + } + return position; + }, + + + getLayoutItems: function() { + var owner = this.owner, + items = owner && owner.items; + + return (items && items.items) || []; + }, + + getRenderData: function () { + var comp = this.owner; + + return { + $comp: comp, + $layout: this, + ownerId: comp.id + }; + }, + + + getRenderedItems: function() { + var me = this, + target = me.getRenderTarget(), + items = me.getLayoutItems(), + ln = items.length, + renderedItems = [], + i, item; + + for (i = 0; i < ln; i++) { + item = items[i]; + if (item.rendered && me.isValidParent(item, target, i)) { + renderedItems.push(item); + } + } + + return renderedItems; + }, + + + getRenderTarget: function() { + return this.owner.getTargetEl(); + }, + + + getElementTarget: function() { + return this.getRenderTarget(); + }, + + getRenderTpl: function () { + var me = this, + renderTpl = Ext.XTemplate.getTpl(this, 'renderTpl'); + + + + if (!renderTpl.renderContent) { + me.owner.setupRenderTpl(renderTpl); + } + + return renderTpl; + }, + + getRenderTree: function () { + var result, + items = this.owner.items, + itemsGen, + renderCfgs = {}; + + do { + itemsGen = items.generation; + result = this.getItemsRenderTree(this.getLayoutItems(), renderCfgs); + } while (items.generation !== itemsGen); + return result; + }, + + renderChildren: function () { + var me = this, + ownerItems = me.owner.items, + target = me.getRenderTarget(), + itemsGen, items; + + + + + do { + itemsGen = ownerItems.generation; + items = me.getLayoutItems(); + me.renderItems(items, target); + } while (ownerItems.generation !== itemsGen); + }, + + getScrollbarsNeeded: function (width, height, contentWidth, contentHeight) { + var scrollbarSize = Ext.getScrollbarSize(), + hasWidth = typeof width == 'number', + hasHeight = typeof height == 'number', + needHorz = 0, + needVert = 0; + + + if (!scrollbarSize.width) { + return 0; + } + if (hasHeight && height < contentHeight) { + needVert = 2; + width -= scrollbarSize.width; + } + + if (hasWidth && width < contentWidth) { + needHorz = 1; + if (!needVert && hasHeight) { + height -= scrollbarSize.height; + if (height < contentHeight) { + needVert = 2; + } + } + } + + return needVert + needHorz; + }, + + + getTarget: function() { + return this.owner.getTargetEl(); + }, + + + getVisibleItems: function() { + var target = this.getRenderTarget(), + items = this.getLayoutItems(), + ln = items.length, + visibleItems = [], + i, item; + + for (i = 0; i < ln; i++) { + item = items[i]; + if (item.rendered && this.isValidParent(item, target, i) && item.hidden !== true) { + visibleItems.push(item); + } + } + + return visibleItems; + }, + + setupRenderTpl: function (renderTpl) { + var me = this; + + renderTpl.renderBody = me.doRenderBody; + renderTpl.renderContainer = me.doRenderContainer; + renderTpl.renderItems = me.doRenderItems; + }, + + getContentTarget: function(){ + return this.owner.getDefaultContentTarget(); + } + +}); + + + +Ext.define('Ext.layout.container.Auto', { + + + + alias: ['layout.auto', 'layout.autocontainer'], + + extend: Ext.layout.container.Container , + + + + type: 'autocontainer', + + childEls: [ + 'outerCt', + 'innerCt', + 'clearEl' + ], + + + reserveScrollbar: false, + + + managePadding: true, + + + manageOverflow: false, + + + lastOverflowAdjust: { + width: 0, + height: 0 + }, + + + + + + + + + + + + + + renderTpl: [ + '{% if (!(Ext.isIEQuirks || Ext.isIE7m)) { %}', + + + + + + + + '', + + + '', + '', + '{% } else if (values.shrinkWrapWidth) { %}', + + + + '', + '', + '', + '', + '', + '', + '', + '{% } else { %}', + + + + + + + '', + + '{% values.$layout.isShrinkWrapTpl = false %}', + '{% } %}' + ], + + + + + + tableTpl: [ + '', + '', + '', + '', + '', + '', + '' + ], + + isShrinkWrapTpl: true, + + + beginLayout: function(ownerContext) { + var me = this, + bottomPadding, overflowYStyle, overflowXStyle, needsTable; + + me.callParent(arguments); + + me.initContextItems(ownerContext); + + if (!me.isShrinkWrapTpl) { + + + + + if (ownerContext.widthModel.shrinkWrap) { + needsTable = true; + } + + + + if (Ext.isStrict && Ext.isIE7) { + overflowXStyle = me.getOverflowXStyle(ownerContext); + if ((overflowXStyle === 'auto' || overflowXStyle === 'scroll') && + ownerContext.paddingContext.getPaddingInfo().right) { + needsTable = true; + } + } + + if (needsTable) { + me.insertTableCt(ownerContext); + } + } + + + + + if (!me.isShrinkWrapTpl && Ext.isIE7 && Ext.isStrict && !me.clearElHasPadding) { + bottomPadding = ownerContext.paddingContext.getPaddingInfo().bottom; + overflowYStyle = me.getOverflowYStyle(ownerContext); + if (bottomPadding && (overflowYStyle === 'auto' || overflowYStyle === 'scroll')) { + me.clearEl.setStyle('height', bottomPadding); + me.clearElHasPadding = true; + } + } + }, + + beforeLayoutCycle: function(ownerContext){ + var comp = this.owner, + hierarchyState = comp.hierarchyState, + hierarchyStateInner = comp.hierarchyStateInner; + + if (!hierarchyState || hierarchyState.invalid) { + hierarchyState = comp.getHierarchyState(); + hierarchyStateInner = comp.hierarchyStateInner; + } + if (ownerContext.widthModel.shrinkWrap && this.isShrinkWrapTpl) { + hierarchyStateInner.inShrinkWrapTable = true; + } else { + delete hierarchyStateInner.inShrinkWrapTable; + } + }, + + beginLayoutCycle: function(ownerContext) { + var me = this, + outerCt = me.outerCt, + lastOuterCtWidth = me.lastOuterCtWidth || '', + lastOuterCtHeight = me.lastOuterCtHeight || '', + lastOuterCtTableLayout = me.lastOuterCtTableLayout || '', + state = ownerContext.state, + overflowXStyle, overflowYStyle, outerCtWidth, outerCtHeight, outerCtTableLayout, + deferWidth, hierarchyStateInner; + + me.callParent(arguments); + + + outerCtWidth = outerCtHeight = outerCtTableLayout = ''; + + if (!ownerContext.widthModel.shrinkWrap && me.isShrinkWrapTpl) { + + + + if (Ext.isIE7m && Ext.isStrict) { + overflowYStyle = me.getOverflowYStyle(ownerContext); + if (overflowYStyle === 'auto' || overflowYStyle === 'scroll') { + + + + deferWidth = true; + } + } + + if (!deferWidth) { + + outerCtWidth = '100%'; + } + hierarchyStateInner = me.owner.hierarchyStateInner; + + + + overflowXStyle = me.getOverflowXStyle(ownerContext); + outerCtTableLayout = (hierarchyStateInner.inShrinkWrapTable || + overflowXStyle === 'auto' || + overflowXStyle === 'scroll') ? '' : 'fixed'; + } + + if (!ownerContext.heightModel.shrinkWrap && + !Ext.supports.PercentageHeightOverflowBug) { + + + + + + outerCtHeight = '100%'; + } + + + + + if ((outerCtWidth !== lastOuterCtWidth) || me.hasOuterCtPxWidth) { + outerCt.setStyle('width', outerCtWidth); + me.lastOuterCtWidth = outerCtWidth; + me.hasOuterCtPxWidth = false; + } + + + if (outerCtTableLayout !== lastOuterCtTableLayout) { + outerCt.setStyle('table-layout', outerCtTableLayout); + me.lastOuterCtTableLayout = outerCtTableLayout; + } + + + + + if ((outerCtHeight !== lastOuterCtHeight) || me.hasOuterCtPxHeight) { + outerCt.setStyle('height', outerCtHeight); + me.lastOuterCtHeight = outerCtHeight; + me.hasOuterCtPxHeight = false; + } + + if (me.hasInnerCtPxHeight) { + me.innerCt.setStyle('height', ''); + me.hasInnerCtPxHeight = false; + } + + + + + state.overflowAdjust = state.overflowAdjust || me.lastOverflowAdjust; + }, + + calculate: function(ownerContext) { + var me = this, + state = ownerContext.state, + containerSize = me.getContainerSize(ownerContext, true), + + calculatedItems = state.calculatedItems || + (state.calculatedItems = me.calculateItems ? + me.calculateItems(ownerContext, containerSize) : true); + + me.setCtSizeIfNeeded(ownerContext, containerSize); + + if (calculatedItems && ownerContext.hasDomProp('containerChildrenSizeDone')) { + + me.calculateContentSize(ownerContext); + + if (containerSize.gotAll) { + if (me.manageOverflow && !ownerContext.state.secondPass && !me.reserveScrollbar) { + me.calculateOverflow(ownerContext, containerSize); + } + return; + } + } + + me.done = false; + }, + + calculateContentSize: function (ownerContext) { + var me = this, + containerDimensions = ((ownerContext.widthModel.shrinkWrap ? 1 : 0) | + (ownerContext.heightModel.shrinkWrap ? 2 : 0)), + calcWidth = (containerDimensions & 1) || undefined, + calcHeight = (containerDimensions & 2) || undefined, + needed = 0, + props = ownerContext.props; + + if (calcWidth) { + if (isNaN(props.contentWidth)) { + ++needed; + } else { + calcWidth = undefined; + } + } + if (calcHeight) { + if (isNaN(props.contentHeight)) { + ++needed; + } else { + calcHeight = undefined; + } + } + + if (needed) { + if (calcWidth && !ownerContext.setContentWidth(me.measureContentWidth(ownerContext))) { + me.done = false; + } + if (calcHeight && !ownerContext.setContentHeight(me.measureContentHeight(ownerContext))) { + me.done = false; + } + + + + + + + } + }, + + + calculateOverflow: function (ownerContext) { + var me = this, + width, height, scrollbarSize, scrollbars, xauto, yauto, targetEl; + + + + xauto = (me.getOverflowXStyle(ownerContext) === 'auto'); + yauto = (me.getOverflowYStyle(ownerContext) === 'auto'); + + if (xauto || yauto) { + scrollbarSize = Ext.getScrollbarSize(); + targetEl = ownerContext.overflowContext.el.dom; + scrollbars = 0; + + if (targetEl.scrollWidth > targetEl.clientWidth) { + + scrollbars |= 1; + } + + if (targetEl.scrollHeight > targetEl.clientHeight) { + + scrollbars |= 2; + } + + width = (yauto && (scrollbars & 2)) ? scrollbarSize.width : 0; + height = (xauto && (scrollbars & 1)) ? scrollbarSize.height : 0; + + if (width !== me.lastOverflowAdjust.width || height !== me.lastOverflowAdjust.height) { + me.done = false; + + + + ownerContext.invalidate({ + state: { + overflowAdjust: { + width: width, + height: height + }, + overflowState: scrollbars, + secondPass: true + } + }); + } + } + }, + + completeLayout: function(ownerContext) { + this.lastOverflowAdjust = ownerContext.state.overflowAdjust; + }, + + doRenderPadding: function(out, renderData) { + + + + var me = renderData.$layout, + owner = renderData.$layout.owner, + padding = owner[owner.contentPaddingProperty]; + + if (me.managePadding && padding) { + out.push('padding:', owner.unitizeBox(padding)); + } + }, + + finishedLayout: function (ownerContext) { + var innerCt = this.innerCt; + + this.callParent(arguments); + + if (Ext.isIEQuirks || Ext.isIE8m) { + + + + innerCt.repaint(); + } + + if (Ext.isOpera) { + + + + innerCt.setStyle('position', 'relative'); + innerCt.dom.scrollWidth; + innerCt.setStyle('position', ''); + } + }, + + + getContainerSize : function(ownerContext, inDom) { + + + + + + + var size = this.callParent(arguments), + overflowAdjust = ownerContext.state.overflowAdjust; + + if (overflowAdjust) { + size.width -= overflowAdjust.width; + size.height -= overflowAdjust.height; + } + + return size; + }, + + getRenderData: function() { + var owner = this.owner, + data = this.callParent(); + + + + + + + + + + + + + + + + + + + + if ((Ext.isIEQuirks || Ext.isIE7m) && + ((owner.shrinkWrap & 1) || + (owner.floating && !owner.width))) { + data.shrinkWrapWidth = true; + } + + return data; + }, + + + + getRenderTarget: function() { + return this.innerCt; + }, + + + + getElementTarget: function() { + return this.innerCt; + }, + + + getOverflowXStyle: function(ownerContext) { + return ownerContext.overflowXStyle || + (ownerContext.overflowXStyle = this.owner.scrollFlags.overflowX || ownerContext.overflowContext.getStyle('overflow-x')); + }, + + + getOverflowYStyle: function(ownerContext) { + return ownerContext.overflowYStyle || + (ownerContext.overflowYStyle = this.owner.scrollFlags.overflowY || ownerContext.overflowContext.getStyle('overflow-y')); + }, + + initContextItems: function(ownerContext) { + var me = this, + target = ownerContext.target, + customOverflowEl = me.owner.customOverflowEl; + + ownerContext.outerCtContext = ownerContext.getEl('outerCt', me); + ownerContext.innerCtContext = ownerContext.getEl('innerCt', me); + + if (customOverflowEl) { + ownerContext.overflowContext = ownerContext.getEl(customOverflowEl); + } else { + ownerContext.overflowContext = ownerContext.targetContext; + } + + if (target[target.contentPaddingProperty] !== undefined) { + + + + + + ownerContext.paddingContext = me.isShrinkWrapTpl ? + ownerContext.innerCtContext : ownerContext.outerCtContext; + } + }, + + initLayout: function() { + var me = this, + scrollbarWidth = Ext.getScrollbarSize().width, + owner = me.owner; + + me.callParent(); + + + + + if (scrollbarWidth && me.manageOverflow && !me.hasOwnProperty('lastOverflowAdjust')) { + if (owner.autoScroll || me.reserveScrollbar) { + me.lastOverflowAdjust = { + width: scrollbarWidth, + height: 0 + }; + } + } + }, + + + insertTableCt: function(ownerContext) { + var me = this, + owner = me.owner, + i = 0, + renderTpl, fragment, childNodes, childLength, targetEl; + + + renderTpl = Ext.XTemplate.getTpl(this, 'tableTpl'); + renderTpl.renderPadding = me.doRenderPadding; + + + + me.outerCt.dom.removeChild(me.innerCt.dom); + + + fragment = document.createDocumentFragment(); + childNodes = me.innerCt.dom.childNodes; + childLength = childNodes.length; + + for (; i < childLength; i++) { + fragment.appendChild(childNodes[0]); + } + + targetEl = me.getTarget(); + targetEl.dom.innerHTML = renderTpl.apply({ + $layout: me, + ownerId: me.owner.id + }); + + + targetEl.down('td').dom.appendChild(fragment); + + + + me.applyChildEls(owner.el, owner.id) + + + + me.isShrinkWrapTpl = true; + + ownerContext.removeEl(me.outerCt); + ownerContext.removeEl(me.innerCt); + me.initContextItems(ownerContext); + }, + + measureContentHeight: function (ownerContext) { + + var contentHeight = this.outerCt.getHeight(), + target = ownerContext.target; + + if (this.managePadding && (target[target.contentPaddingProperty] === undefined)) { + + + + + contentHeight += ownerContext.targetContext.getPaddingInfo().height; + } + return contentHeight; + }, + + measureContentWidth: function (ownerContext) { + var dom, style, old, contentWidth, target; + + + + + if (this.chromeCellMeasureBug) { + dom = this.innerCt.dom; + style = dom.style; + old = style.display; + + if (old == 'table-cell') { + style.display = ''; + dom.offsetWidth; + style.display = old; + } + } + + if (Ext.isSafari) { + + + + + + dom = this.outerCt.dom; + style = dom.style; + style.display = 'table-cell'; + dom.offsetWidth; + dom.style.display = 'table'; + } + + + contentWidth = this.outerCt.getWidth(); + target = ownerContext.target; + + if (this.managePadding && (target[target.contentPaddingProperty] === undefined)) { + + + + + contentWidth += ownerContext.targetContext.getPaddingInfo().width; + } + return contentWidth; + }, + + + setCtSizeIfNeeded: function(ownerContext, containerSize) { + var me = this, + width = containerSize.width, + height = containerSize.height, + padding = ownerContext.paddingContext.getPaddingInfo(), + targetEl = me.getTarget(), + overflowXStyle = me.getOverflowXStyle(ownerContext), + overflowYStyle = me.getOverflowYStyle(ownerContext), + canOverflowX = (overflowXStyle === 'auto' || overflowXStyle === 'scroll'), + canOverflowY = (overflowYStyle === 'auto' || overflowYStyle === 'scroll'), + scrollbarSize = Ext.getScrollbarSize(), + isShrinkWrapTpl = me.isShrinkWrapTpl, + manageOverflow = me.manageOverflow, + overflowStyleName, needsOuterHeight, needsInnerHeight, needsInnerCtPaddingHeight; + + if (width && !ownerContext.widthModel.shrinkWrap && + + + ((Ext.isIE7m && Ext.isStrict && isShrinkWrapTpl && canOverflowY) || + + + + (Ext.isIEQuirks && !isShrinkWrapTpl && !canOverflowX))) { + + if (!manageOverflow) { + + + + if (canOverflowY && (targetEl.dom.scrollHeight > targetEl.dom.clientHeight)) { + + width -= scrollbarSize.width; + } + } + + ownerContext.outerCtContext.setProp('width', width + padding.width); + me.hasOuterCtPxWidth = true; + } + + if (height && !ownerContext.heightModel.shrinkWrap) { + if (Ext.supports.PercentageHeightOverflowBug) { + + + needsOuterHeight = true; + } + if (((Ext.isIE8 && Ext.isStrict) || + Ext.isIE7m && Ext.isStrict && isShrinkWrapTpl)) { + + + + needsInnerHeight = true; + + + needsInnerCtPaddingHeight = !Ext.isIE8; + } + + if ((needsOuterHeight || needsInnerHeight) && canOverflowX && + (targetEl.dom.scrollWidth > targetEl.dom.clientWidth)) { + + + + height = Math.max(height - scrollbarSize.height, 0); + } + + if (needsOuterHeight) { + ownerContext.outerCtContext.setProp('height', height + padding.height); + me.hasOuterCtPxHeight = true; + } + + if (needsInnerHeight) { + if (needsInnerCtPaddingHeight) { + height += padding.height; + } + ownerContext.innerCtContext.setProp('height', height); + me.hasInnerCtPxHeight = true; + } + } + + if (Ext.isIE7 && Ext.isStrict && !isShrinkWrapTpl && (overflowYStyle === 'auto')) { + + + + + + + + overflowStyleName = (overflowXStyle === 'auto') ? 'overflow-x' : 'overflow-y'; + targetEl.setStyle(overflowStyleName, 'hidden'); + targetEl.setStyle(overflowStyleName, 'auto'); + } + }, + + setupRenderTpl: function (renderTpl) { + this.callParent(arguments); + + renderTpl.renderPadding = this.doRenderPadding; + }, + + getContentTarget: function(){ + return this.innerCt; + } + +}, function(){ + this.prototype.chromeCellMeasureBug = Ext.isChrome && Ext.chromeVersion >= 26; +}); + + + +Ext.define('Ext.ZIndexManager', { + alternateClassName: 'Ext.WindowGroup', + + statics: { + zBase : 9000 + }, + + constructor: function(container) { + var me = this; + + me.map = {}; + me.zIndexStack = []; + me.front = null; + + if (container) { + + + if (container.isContainer) { + container.on('resize', me._onContainerResize, me); + me.zseed = Ext.Number.from(me.rendered ? container.getEl().getStyle('zIndex') : undefined, me.getNextZSeed()); + + me.targetEl = container.getTargetEl(); + me.container = container; + } + + else { + Ext.EventManager.onWindowResize(me._onContainerResize, me); + me.zseed = me.getNextZSeed(); + me.targetEl = Ext.get(container); + } + } + + + else { + Ext.EventManager.onWindowResize(me._onContainerResize, me); + me.zseed = me.getNextZSeed(); + Ext.onDocumentReady(function() { + me.targetEl = Ext.getBody(); + }); + } + }, + + getNextZSeed: function() { + return (Ext.ZIndexManager.zBase += 10000); + }, + + setBase: function(baseZIndex) { + this.zseed = baseZIndex; + var result = this.assignZIndices(); + this._activateLast(); + return result; + }, + + + assignZIndices: function() { + var a = this.zIndexStack, + len = a.length, + i = 0, + zIndex = this.zseed, + comp, + topModal; + + for (; i < len; i++) { + comp = a[i]; + if (comp && !comp.hidden) { + + + + + + + + + zIndex = comp.setZIndex(zIndex); + if (comp.modal) { + topModal = comp; + } + } + } + + + if (topModal) { + this._showModalMask(topModal) + } + return zIndex; + }, + + + _setActiveChild: function(comp, oldFront) { + var front = this.front, + oldPreventFocus = comp.preventFocusOnActivate; + + if (comp !== front) { + + if (front && !front.destroying) { + front.setActive(false, comp); + } + this.front = comp; + if (comp && comp != oldFront) { + + + comp.preventFocusOnActivate = comp.preventFocusOnActivate || oldFront && (oldFront.preventFocusOnActivate || !oldFront.focusOnToFront); + + comp.setActive(true); + + + if (comp.modal) { + this._showModalMask(comp); + } + + + comp.preventFocusOnActivate = oldPreventFocus; + } + } + }, + + onComponentHide: function(comp){ + this._activateLast(); + }, + + + _activateLast: function() { + var me = this, + stack = me.zIndexStack, + i = stack.length - 1, + comp; + + + + + + + + for (; i >= 0 && stack[i].hidden; --i); + + + if ((comp = stack[i])) { + me._setActiveChild(comp, me.front); + if (comp.modal) { + return; + } + } + + else { + if (me.front && !me.front.destroying) { + me.front.setActive(false); + } + me.front = null; + } + + + + for (; i >= 0; --i) { + comp = stack[i]; + + if (comp.isVisible() && comp.modal) { + me._showModalMask(comp); + return; + } + } + + + + me._hideModalMask(); + }, + + _showModalMask: function(comp) { + var me = this, + zIndex = comp.el.getStyle('zIndex') - 4, + maskTarget = comp.floatParent ? comp.floatParent.getTargetEl() : comp.container, + mask = me.mask, + shim = me.maskShim, + viewSize; + + if (!mask) { + if (Ext.isIE6) { + shim = me.maskShim = Ext.getBody().createChild({ + + 'data-sticky': true, + tag: 'iframe', + role: 'presentation', + cls : Ext.baseCSSPrefix + 'shim ' + Ext.baseCSSPrefix + 'mask-shim' + }); + shim.setVisibilityMode(Ext.Element.DISPLAY); + } + + + mask = me.mask = Ext.getBody().createChild({ + + 'data-sticky': true, + role: 'presentation', + cls: Ext.baseCSSPrefix + 'mask', + style: 'height:0;width:0' + }); + mask.setVisibilityMode(Ext.Element.DISPLAY); + mask.on('click', me._onMaskClick, me); + } + + mask.maskTarget = maskTarget; + viewSize = me.getMaskBox(); + + if (shim) { + shim.setStyle('zIndex', zIndex); + shim.show(); + shim.setBox(viewSize); + } + mask.setStyle('zIndex', zIndex); + + + + mask.show(); + mask.setBox(viewSize); + }, + + _hideModalMask: function() { + var mask = this.mask, + maskShim = this.maskShim; + + if (mask && mask.isVisible()) { + mask.maskTarget = undefined; + mask.hide(); + if (maskShim) { + maskShim.hide(); + } + } + }, + + _onMaskClick: function() { + if (this.front) { + this.front.focus(); + } + }, + + getMaskBox: function(){ + var maskTarget = this.mask.maskTarget; + if (maskTarget.dom === document.body) { + return { + height: Math.max(document.body.scrollHeight, Ext.dom.Element.getDocumentHeight()), + width: Math.max(document.body.scrollWidth, document.documentElement.clientWidth), + x: 0, + y: 0 + }; + } else { + return maskTarget.getBox(); + } + }, + + _onContainerResize: function() { + var me = this, + mask = me.mask, + maskShim = me.maskShim, + viewSize; + + if (mask && mask.isVisible()) { + + + + mask.hide(); + if (maskShim) { + maskShim.hide(); + } + + viewSize = me.getMaskBox(); + if (maskShim) { + maskShim.setSize(viewSize); + maskShim.show(); + } + mask.setSize(viewSize); + mask.show(); + } + }, + + + register : function(comp) { + var me = this, + compAfterHide = comp.afterHide; + + if (comp.zIndexManager) { + comp.zIndexManager.unregister(comp); + } + comp.zIndexManager = me; + + me.map[comp.id] = comp; + me.zIndexStack.push(comp); + + + comp.afterHide = function() { + compAfterHide.apply(comp, arguments); + me.onComponentHide(comp); + }; + }, + + + unregister : function(comp) { + var me = this, + map = me.map; + + delete comp.zIndexManager; + if (map && map[comp.id]) { + delete map[comp.id]; + + + delete comp.afterHide; + Ext.Array.remove(me.zIndexStack, comp); + + + me._activateLast(); + } + }, + + + get : function(id) { + return id.isComponent ? id : this.map[id]; + }, + + + bringToFront : function(comp, preventFocus) { + var me = this, + result = false, + zIndexStack = me.zIndexStack; + + comp = me.get(comp); + if (comp !== me.front) { + Ext.Array.remove(zIndexStack, comp); + if (comp.preventBringToFront) { + + zIndexStack.unshift(comp); + } else { + + zIndexStack.push(comp); + } + + me.assignZIndices(); + + + if (!preventFocus) { + me._activateLast(); + } + result = true; + me.front = comp; + + + if (comp.modal) { + me._showModalMask(comp); + } + } + return result; + }, + + + sendToBack : function(comp) { + var me = this; + + comp = me.get(comp); + Ext.Array.remove(me.zIndexStack, comp); + me.zIndexStack.unshift(comp); + me.assignZIndices(); + this._activateLast(); + return comp; + }, + + + hideAll : function() { + var map = this.map, + item, + id; + + for (id in map) { + if (map.hasOwnProperty(id)) { + item = map[id]; + if (item.isComponent && item.isVisible()) { + item.hide(); + } + } + } + }, + + + hide: function() { + var i = 0, + stack = this.zIndexStack, + len = stack.length, + comp; + + this.tempHidden = []; + for (; i < len; i++) { + comp = stack[i]; + if (comp.isVisible()) { + this.tempHidden.push(comp); + comp.el.hide(); + comp.hidden = true; + } + } + }, + + + show: function() { + var i = 0, + tempHidden = this.tempHidden, + len = tempHidden ? tempHidden.length : 0, + comp; + + for (; i < len; i++) { + comp = tempHidden[i]; + comp.el.show(); + comp.hidden = false; + comp.setPosition(comp.x, comp.y); + } + delete this.tempHidden; + }, + + + getActive : function() { + return this.front; + }, + + + getBy : function(fn, scope) { + var r = [], + i = 0, + stack = this.zIndexStack, + len = stack.length, + comp; + + for (; i < len; i++) { + comp = stack[i]; + if (fn.call(scope||comp, comp) !== false) { + r.push(comp); + } + } + return r; + }, + + + each : function(fn, scope) { + var map = this.map, + id, + comp; + + for (id in map) { + if (map.hasOwnProperty(id)) { + comp = map[id]; + if (comp.isComponent && fn.call(scope || comp, comp) === false) { + return; + } + } + } + }, + + + eachBottomUp: function (fn, scope) { + var stack = this.zIndexStack, + i = 0, + len = stack.length, + comp; + + for (; i < len; i++) { + comp = stack[i]; + if (comp.isComponent && fn.call(scope || comp, comp) === false) { + return; + } + } + }, + + + eachTopDown: function (fn, scope) { + var stack = this.zIndexStack, + i = stack.length, + comp; + + for (; i-- > 0; ) { + comp = stack[i]; + if (comp.isComponent && fn.call(scope || comp, comp) === false) { + return; + } + } + }, + + destroy: function() { + var me = this, + map = me.map, + comp, + id; + + for (id in map) { + if (map.hasOwnProperty(id)) { + comp = map[id]; + + if (comp.isComponent) { + comp.destroy(); + } + } + } + + Ext.destroy(me.mask); + Ext.destroy(me.maskShim); + delete me.zIndexStack; + delete me.map; + delete me.container; + delete me.targetEl; + } +}, function() { + + Ext.WindowManager = Ext.WindowMgr = new this(); +}); + + + +Ext.define('Ext.Queryable', { + + isQueryable: true, + + + query : function(selector) { + selector = selector || '*'; + return Ext.ComponentQuery.query(selector, this); + }, + + + queryBy: function(fn, scope) { + var out = [], + items = this.getRefItems(true), + i = 0, + len = items.length, + item; + + for (; i < len; ++i) { + item = items[i]; + if (fn.call(scope || item, item) !== false) { + out.push(item); + } + } + return out; + }, + + + queryById: function(id){ + return this.down('#' + id); + }, + + + child: function (selector) { + var children = this.getRefItems(); + + if (selector && selector.isComponent) { + selector = '#' + Ext.escapeId(selector.getItemId()); + } + + + if (selector) { + children = Ext.ComponentQuery.query(selector, children); + } + + + if (children.length) { + return children[0]; + } + return null; + }, + + + down: function (selector) { + if (selector && selector.isComponent) { + selector = '#' + Ext.escapeId(selector.getItemId()); + } + + selector = selector || ''; + return this.query(selector)[0] || null; + }, + + + visitPreOrder: function(selector, fn, scope, extraArgs) { + Ext.ComponentQuery._visit(true, selector, this, fn, scope, extraArgs); + }, + + + visitPostOrder: function(selector, fn, scope, extraArgs) { + Ext.ComponentQuery._visit(false, selector, this, fn, scope, extraArgs); + }, + + getRefItems: function(){ + return []; + } + +}); + + + +Ext.define('Ext.container.AbstractContainer', { + + + + extend: Ext.Component , + + + + + + + + mixins: { + queryable: Ext.Queryable + }, + + + + renderTpl: '{%this.renderContainer(out,values)%}', + + + + + + + + + + + suspendLayout : false, + + + autoDestroy : true, + + + defaultType: 'panel', + + + detachOnRemove: true, + + + isContainer : true, + + + layoutCounter : 0, + + baseCls: Ext.baseCSSPrefix + 'container', + + + + defaultLayoutType: 'auto', + + ariaRole: 'presentation', + + + initComponent : function(){ + var me = this; + me.addEvents( + + 'afterlayout', + + 'beforeadd', + + 'beforeremove', + + 'add', + + 'remove' + ); + + me.callParent(); + + me.getLayout(); + me.initItems(); + }, + + + initItems : function() { + var me = this, + items = me.items; + + + me.items = new Ext.util.AbstractMixedCollection(false, me.getComponentId); + me.floatingItems = new Ext.util.MixedCollection(false, me.getComponentId); + + if (items) { + if (!Ext.isArray(items)) { + items = [items]; + } + + me.add(items); + } + }, + + + getFocusEl: function() { + return this.getTargetEl(); + }, + + finishRenderChildren: function () { + this.callParent(); + + var layout = this.getLayout(); + + if (layout) { + layout.finishRender(); + } + }, + + beforeRender: function () { + var me = this, + layout = me.getLayout(), + targetCls; + + me.callParent(); + + if (!layout.initialized) { + layout.initLayout(); + } + + targetCls = layout.targetCls; + + if (targetCls) { + me.applyTargetCls(targetCls); + } + }, + + + + + + + applyTargetCls: function(targetCls) { + this.addCls(targetCls); + }, + + afterComponentLayout: function() { + var floaters = this.floatingItems.items, + floaterCount = floaters.length, + i, floater + + this.callParent(arguments); + + + for (i = 0; i < floaterCount; i++) { + floater = floaters[i]; + if (!floater.rendered && floater.autoShow) { + floater.show(); + } + } + }, + + onPosition: function() { + this.callParent(arguments); + this.repositionFloatingItems(); + }, + + onResize: function() { + this.callParent(arguments); + this.repositionFloatingItems(); + }, + + repositionFloatingItems: function() { + var floaters = this.floatingItems.items, + floaterCount = floaters.length, + i, floater; + + + for (i = 0; i < floaterCount; i++) { + floater = floaters[i]; + if (floater.el && !floater.hidden) { + floater.setPosition(floater.x, floater.y); + } + } + }, + + setupRenderTpl: function (renderTpl) { + this.callParent(arguments); + this.getLayout().setupRenderTpl(renderTpl); + }, + + + getDefaultContentTarget: function() { + return this.el; + }, + + + getContentTarget: function(){ + return this.getLayout().getContentTarget(); + }, + + + setLayout : function(layout) { + var currentLayout = this.layout; + + if (currentLayout && currentLayout.isLayout && currentLayout != layout) { + currentLayout.setOwner(null); + } + + this.layout = layout; + layout.setOwner(this); + }, + + + getLayout : function() { + var me = this; + if (!me.layout || !me.layout.isLayout) { + + me.setLayout(Ext.layout.Layout.create(me.layout, me.self.prototype.layout || me.defaultLayoutType)); + } + + return me.layout; + }, + + + doLayout : function() { + this.updateLayout(); + return this; + }, + + + afterLayout : function(layout) { + var me = this; + ++me.layoutCounter; + if (me.hasListeners.afterlayout) { + me.fireEvent('afterlayout', me, layout); + } + }, + + + prepareItems : function(items, applyDefaults) { + + + if (Ext.isArray(items)) { + items = items.slice(); + } else { + items = [items]; + } + + + var me = this, + i = 0, + len = items.length, + item; + + for (; i < len; i++) { + item = items[i]; + if (item == null) { + Ext.Array.erase(items, i, 1); + --i; + --len; + } else { + if (applyDefaults) { + item = this.applyDefaults(item); + } + + + item.isContained = me; + items[i] = me.lookupComponent(item); + + delete item.isContained; + delete items[i].isContained; + } + } + + return items; + }, + + + applyDefaults : function(config) { + var defaults = this.defaults; + + if (defaults) { + if (Ext.isFunction(defaults)) { + defaults = defaults.call(this, config); + } + + if (Ext.isString(config)) { + config = Ext.ComponentManager.get(config); + } + Ext.applyIf(config, defaults); + } + return config; + }, + + + lookupComponent : function(comp) { + return (typeof comp == 'string') ? Ext.ComponentManager.get(comp) + : Ext.ComponentManager.create(comp, this.defaultType); + }, + + + getComponentId : function(comp) { + return comp.getItemId && comp.getItemId(); + }, + + + add : function() { + var me = this, + args = Ext.Array.slice(arguments), + index = (typeof args[0] == 'number') ? args.shift() : -1, + layout = me.getLayout(), + addingArray, items, i, length, item, pos, ret, + needsLayout = false; + + if (args.length == 1 && Ext.isArray(args[0])) { + items = args[0]; + addingArray = true; + } else { + items = args; + } + + if (me.rendered) { + Ext.suspendLayouts(); + } + + ret = items = me.prepareItems(items, true); + length = items.length; + + if (!addingArray && length == 1) { + ret = items[0]; + } + + + for (i = 0; i < length; i++) { + item = items[i]; + if (!item) { + Ext.Error.raise("Cannot add null item to Container with itemId/id: " + me.getItemId()); + } + + pos = (index < 0) ? me.items.length : (index + i); + + + if (item.floating) { + me.floatingItems.add(item); + item.onAdded(me, pos); + + if (me.hasListeners.add) { + me.fireEvent('add', me, item, pos); + } + } else if ((!me.hasListeners.beforeadd || me.fireEvent('beforeadd', me, item, pos) !== false) && me.onBeforeAdd(item) !== false) { + me.items.insert(pos, item); + item.onAdded(me, pos); + me.onAdd(item, pos); + layout.onAdd(item, pos); + + needsLayout = true; + if (me.hasListeners.add) { + me.fireEvent('add', me, item, pos); + } + } + } + + + + if (needsLayout) { + me.updateLayout(); + } + if (me.rendered) { + Ext.resumeLayouts(true); + } + + return ret; + }, + + + onAdd : Ext.emptyFn, + + + onRemove : Ext.emptyFn, + + + insert : function(index, comp) { + var compIdx; + if (comp && comp.isComponent) { + compIdx = this.items.indexOf(comp); + if (compIdx !== -1) { + return this.move(compIdx, index); + } + } + return this.add(index, comp); + }, + + + move : function(fromIdx, toIdx) { + var items = this.items, + item; + + if (fromIdx.isComponent) { + fromIdx = items.indexOf(fromIdx); + } + item = items.getAt(fromIdx); + if (fromIdx !== toIdx) { + item = items.removeAt(fromIdx); + if (item === false) { + return false; + } + items.insert(toIdx, item); + this.onMove(item, fromIdx, toIdx); + this.updateLayout(); + } + return item; + }, + + onMove: Ext.emptyFn, + + + onBeforeAdd : function(item) { + + var owner = item.ownerCt; + if (owner && owner !== this) { + owner.remove(item, false); + } + }, + + + remove : function(comp, autoDestroy) { + var me = this, + c = me.getComponent(comp); + if (Ext.isDefined(Ext.global.console) && !c) { + Ext.global.console.warn("Attempted to remove a component that does not exist. Ext.container.Container: remove takes an argument of the component to remove. cmp.remove() is incorrect usage."); + } + + if (c && (!me.hasListeners.beforeremove || me.fireEvent('beforeremove', me, c) !== false)) { + me.doRemove(c, autoDestroy); + if (me.hasListeners.remove) { + me.fireEvent('remove', me, c); + } + + if (!me.destroying && !c.floating) { + me.updateLayout(); + } + } + + return c; + }, + + + doRemove : function(component, doDestroy) { + + doDestroy = doDestroy === true || (doDestroy !== false && this.autoDestroy); + + var me = this, + layout = me.layout, + hasLayout = layout && me.rendered, + + + isDestroying = component.destroying || doDestroy, + floating = component.floating; + + if (floating) { + me.floatingItems.remove(component); + } else { + me.items.remove(component); + } + + + if (hasLayout && !floating) { + + if (layout.running) { + Ext.AbstractComponent.cancelLayout(component, isDestroying); + } + layout.onRemove(component, isDestroying); + } + + component.onRemoved(isDestroying); + + me.onRemove(component, isDestroying); + + + if (doDestroy) { + component.destroy(); + } + + else { + if (hasLayout && !floating) { + layout.afterRemove(component); + } + if (me.detachOnRemove && component.rendered) { + me.detachComponent(component); + } + } + }, + + + detachComponent: function(component){ + Ext.getDetachedBody().appendChild(component.getEl()); + }, + + + removeAll : function(autoDestroy) { + var me = this, + removeItems = me.items.items.slice().concat(me.floatingItems.items), + items = [], + i = 0, + len = removeItems.length, + item; + + + me.suspendLayouts(); + for (; i < len; i++) { + item = removeItems[i]; + me.remove(item, autoDestroy); + + if (item.ownerCt !== me) { + items.push(item); + } + } + + + me.resumeLayouts(!!len); + return items; + }, + + + getRefItems : function(deep) { + var me = this, + items = me.items.items, + len = items.length, + i = 0, + item, + result = []; + + for (; i < len; i++) { + item = items[i]; + result[result.length] = item; + if (deep && item.getRefItems) { + result.push.apply(result, item.getRefItems(true)); + } + } + + + items = me.floatingItems.items; + len = items.length; + for (i = 0; i < len; i++) { + item = items[i]; + result[result.length] = item; + if (deep && item.getRefItems) { + result.push.apply(result, item.getRefItems(true)); + } + } + + return result; + }, + + + cascade : function(fn, scope, origArgs){ + var me = this, + cs = me.items ? me.items.items : [], + len = cs.length, + i = 0, + c, + args = origArgs ? origArgs.concat(me) : [me], + componentIndex = args.length - 1; + + if (fn.apply(scope || me, args) !== false) { + for (; i < len; i++){ + c = cs[i]; + if (c.cascade) { + c.cascade(fn, scope, origArgs); + } else { + args[componentIndex] = c; + fn.apply(scope || c, args); + } + } + } + return this; + }, + + + isAncestor: function(possibleDescendant) { + while (possibleDescendant) { + if (possibleDescendant.ownerCt === this) { + return true; + } + possibleDescendant = possibleDescendant.ownerCt; + } + }, + + + getComponent : function(comp) { + if (Ext.isObject(comp)) { + comp = comp.getItemId(); + } + + var c = this.items.get(comp); + + + if (!c && typeof comp != 'number') { + c = this.floatingItems.get(comp); + } + + return c; + }, + + + contains: function(comp, deep) { + var result = false; + if (deep) { + this.cascade(function(c) { + + if (c.contains && c.contains(comp)) { + result = true; + return false; + } + }); + return result; + } else { + return this.items.contains(comp) || this.floatingItems.contains(comp); + } + }, + + + nextChild: function(child, selector) { + var me = this, + items = me.items, + childIndex = items.indexOf(child), + i = 0, + len = items.length, + result; + + if (childIndex !== -1) { + if (selector) { + for (; i < len; i++) { + result = items.getAt(childIndex + i); + + if (!result || Ext.ComponentQuery.is(result, selector)) { + break; + } + } + } else { + result = items.getAt(childIndex + 1); + } + + if (!result && me.ownerCt) { + result = me.ownerCt.nextChild(me, selector); + } + } + return result; + }, + + + prevChild: function(child, selector) { + var me = this, + items = me.items, + childIndex = items.indexOf(child), + i = 0, + len = items.length, + result; + + if (childIndex !== -1) { + if (selector) { + for (; i < len; i++) { + result = items.getAt(childIndex - i); + + if (!result || Ext.ComponentQuery.is(result, selector)) { + break; + } + } + } else { + result = items.getAt(childIndex - 1); + } + + if (!result && me.ownerCt) { + result = me.ownerCt.nextChild(me, selector); + } + } + return result; + }, + + + enable: function() { + this.callParent(arguments); + + var itemsToDisable = this.getChildItemsToDisable(), + length = itemsToDisable.length, + item, i; + + for (i = 0; i < length; i++) { + item = itemsToDisable[i]; + + if (item.resetDisable) { + item.enable(); + } + } + + return this; + }, + + + disable: function() { + this.callParent(arguments); + + var itemsToDisable = this.getChildItemsToDisable(), + length = itemsToDisable.length, + item, i; + + for (i = 0; i < length; i++) { + item = itemsToDisable[i]; + + if (item.resetDisable !== false && !item.disabled) { + item.disable(); + item.resetDisable = true; + } + } + + return this; + }, + + + getChildItemsToDisable: function(){ + return this.query('[isFormField],button'); + }, + + + + beforeDestroy : function() { + var me = this, + items = me.items, + floatingItems = me.floatingItems, + c; + + if (items) { + while ((c = items.first())) { + me.doRemove(c, true); + } + } + + if (floatingItems) { + while ((c = floatingItems.first())) { + me.doRemove(c, true); + } + } + + Ext.destroy( + me.layout + ); + me.callParent(); + } +}); + + + +Ext.define('Ext.container.Container', { + extend: Ext.container.AbstractContainer , + alias: 'widget.container', + alternateClassName: 'Ext.Container', + + + getChildByElement: function(el, deep) { + var item, + itemEl, + i = 0, + it = this.getRefItems(), + ln = it.length; + + el = Ext.getDom(el); + for (; i < ln; i++) { + item = it[i]; + itemEl = item.getEl(); + if (itemEl && ((itemEl.dom === el) || itemEl.contains(el))) { + return (deep && item.getChildByElement) ? item.getChildByElement(el, deep) : item; + } + } + return null; + } +}); + + + +Ext.define('Ext.container.DockingContainer', { + + + + + + + + isDockingContainer: true, + + + + + + + defaultDockWeights: { + top: { render: 1, visual: 1 }, + left: { render: 3, visual: 5 }, + right: { render: 5, visual: 7 }, + bottom: { render: 7, visual: 3 } + }, + + + + + + dockOrder: { + top: -1, + left: -1, + right: 1, + bottom: 1 + }, + + + horizontalDocks: 0, + + + addDocked : function(items, pos) { + var me = this, + i = 0, + item, length; + + items = me.prepareItems(items); + length = items.length; + + for (; i < length; i++) { + item = items[i]; + item.dock = item.dock || 'top'; + if (item.dock === 'left' || item.dock === 'right') { + me.horizontalDocks++; + } + + if (pos !== undefined) { + i += pos; + me.dockedItems.insert(i, item); + } else { + me.dockedItems.add(item); + } + + item.onAdded(me, i); + if (me.hasListeners.dockedadd) { + me.fireEvent('dockedadd', me, item, i); + } + if (me.onDockedAdd !== Ext.emptyFn) { + me.onDockedAdd(item); + } + } + + if (me.rendered && !me.suspendLayout) { + me.updateLayout(); + } + return items; + }, + + destroyDockedItems: function(){ + var dockedItems = this.dockedItems, + c; + + if (dockedItems) { + while ((c = dockedItems.first())) { + this.removeDocked(c, true); + } + } + }, + + doRenderDockedItems: function (out, renderData, after) { + + + + + + + + + var me = renderData.$comp, + layout = me.componentLayout, + items, + tree; + + if (layout.getDockedItems && !renderData.$skipDockedItems) { + items = layout.getDockedItems('render', !after); + tree = items && layout.getItemsRenderTree(items); + + if (tree) { + Ext.DomHelper.generateMarkup(tree, out); + } + } + }, + + + getDockedComponent: function(comp) { + if (Ext.isObject(comp)) { + comp = comp.getItemId(); + } + return this.dockedItems.get(comp); + }, + + + getDockedItems : function(selector, beforeBody) { + var dockedItems = this.getComponentLayout().getDockedItems('render', beforeBody); + + if (selector && dockedItems.length) { + dockedItems = Ext.ComponentQuery.query(selector, dockedItems); + } + + return dockedItems; + }, + + getDockingRefItems: function(deep, containerItems) { + + var selector = deep && '*,* *', + + dockedItems = this.getDockedItems(selector, true), + items; + + + dockedItems.push.apply(dockedItems, containerItems); + + + items = this.getDockedItems(selector, false); + dockedItems.push.apply(dockedItems, items); + + return dockedItems; + }, + + initDockingItems: function() { + var me = this, + items = me.dockedItems; + + me.dockedItems = new Ext.util.AbstractMixedCollection(false, me.getComponentId); + if (items) { + me.addDocked(items); + } + }, + + + insertDocked : function(pos, items) { + this.addDocked(items, pos); + }, + + + + onDockedAdd : Ext.emptyFn, + + onDockedRemove : Ext.emptyFn, + + + removeDocked : function(item, autoDestroy) { + var me = this, + layout, + hasLayout; + + autoDestroy = autoDestroy === true || (autoDestroy !== false && me.autoDestroy); + if (!me.dockedItems.contains(item)) { + return item; + } + if (item.dock === 'left' || item.dock === 'right') { + me.horizontalDocks--; + } + + layout = me.componentLayout; + hasLayout = layout && me.rendered; + + if (hasLayout) { + layout.onRemove(item); + } + + me.dockedItems.remove(item); + + item.onRemoved(item.destroying || autoDestroy); + me.onDockedRemove(item); + + if (autoDestroy) { + item.destroy(); + } else if (hasLayout) { + + layout.afterRemove(item); + } + + if (me.hasListeners.dockedremove) { + me.fireEvent('dockedremove', me, item); + } + + if (!me.destroying && !me.suspendLayout) { + me.updateLayout(); + } + + return item; + }, + + setupDockingRenderTpl: function (renderTpl) { + renderTpl.renderDockedItems = this.doRenderDockedItems; + } +}); + + + +Ext.define('Ext.toolbar.Fill', { + extend: Ext.Component , + alias: 'widget.tbfill', + alternateClassName: 'Ext.Toolbar.Fill', + + ariaRole: 'presentation', + + + isFill : true, + flex: 1 +}); + + + +Ext.define('Ext.layout.container.boxOverflow.None', { + alternateClassName: 'Ext.layout.boxOverflow.None', + + constructor: function(layout, config) { + this.layout = layout; + Ext.apply(this, config); + }, + + handleOverflow: Ext.emptyFn, + + clearOverflow: Ext.emptyFn, + + beginLayout: Ext.emptyFn, + beginLayoutCycle: Ext.emptyFn, + + calculate: function(ownerContext) { + var me = this, + plan = ownerContext.state.boxPlan, + overflow; + + if (plan && plan.tooNarrow) { + overflow = me.handleOverflow(ownerContext); + + if (overflow) { + if (overflow.reservedSpace) { + me.layout.publishInnerCtSize(ownerContext, overflow.reservedSpace); + } + + + + + + + + + + + + } + } else { + me.clearOverflow(); + } + }, + + completeLayout: Ext.emptyFn, + + finishedLayout: function (ownerContext) { + var me = this, + owner = me.layout.owner, + hiddens, + hiddenCount; + + + if (owner.hasListeners.overflowchange) { + hiddens = owner.query('>[hidden]'); + hiddenCount = hiddens.length; + if (hiddenCount !== me.lastHiddenCount) { + owner.fireEvent('overflowchange', me.lastHiddenCount, hiddenCount, hiddens); + me.lastHiddenCount = hiddenCount; + } + } + }, + + onRemove: Ext.emptyFn, + + + getItem: function(item) { + return this.layout.owner.getComponent(item); + }, + + getOwnerType: function(owner){ + var type; + if (owner.isToolbar) { + type = 'toolbar'; + } else if (owner.isTabBar) { + type = 'tabbar'; + } else if (owner.isMenu) { + type = 'menu'; + } else { + type = owner.getXType(); + } + + return type; + }, + + getPrefixConfig: Ext.emptyFn, + getSuffixConfig: Ext.emptyFn, + getOverflowCls: function() { + return ''; + } +}); + + + +Ext.define('Ext.toolbar.Item', { + extend: Ext.Component , + alias: 'widget.tbitem', + alternateClassName: 'Ext.Toolbar.Item', + enable:Ext.emptyFn, + disable:Ext.emptyFn, + focus:Ext.emptyFn + +}); + + + +Ext.define('Ext.toolbar.Separator', { + extend: Ext.toolbar.Item , + alias: 'widget.tbseparator', + alternateClassName: 'Ext.Toolbar.Separator', + baseCls: Ext.baseCSSPrefix + 'toolbar-separator', + focusable: false, + + ariaRole: 'separator' +}); + + + +Ext.define('Ext.button.Manager', { + singleton: true, + + alternateClassName: 'Ext.ButtonToggleManager', + + groups: {}, + + pressedButton: null, + + buttonSelector: '.' + Ext.baseCSSPrefix + 'btn', + + init: function() { + var me = this; + if (!me.initialized) { + Ext.getDoc().on({ + keydown: me.onDocumentKeyDown, + mouseup: me.onDocumentMouseUp, + scope: me + }); + me.initialized = true; + } + }, + + + + onDocumentKeyDown: function(e) { + var k = e.getKey(), + btn; + + + if (k === e.SPACE || k === e.ENTER) { + + + btn = e.getTarget(this.buttonSelector); + + + if (btn) { + Ext.getCmp(btn.id).onClick(e); + } + } + }, + + + + + onButtonMousedown: function(button, e) { + var pressed = this.pressedButton; + if (pressed) { + pressed.onMouseUp(e); + } + this.pressedButton = button; + }, + + onDocumentMouseUp: function(e) { + var pressed = this.pressedButton; + + if (pressed) { + pressed.onMouseUp(e); + this.pressedButton = null; + } + }, + + toggleGroup: function(btn, state) { + if (state) { + var g = this.groups[btn.toggleGroup], + length = g.length, + i; + + for (i = 0; i < length; i++) { + if (g[i] !== btn) { + g[i].toggle(false); + } + } + } + }, + + register: function(btn) { + var me = this, + groups = this.groups, + group = groups[btn.toggleGroup]; + + me.init(); + if (!btn.toggleGroup) { + return; + } + + if (!group) { + group = groups[btn.toggleGroup] = []; + } + group.push(btn); + btn.on('toggle', me.toggleGroup, me); + }, + + unregister: function(btn) { + if (!btn.toggleGroup) { + return; + } + var me = this, + group = me.groups[btn.toggleGroup]; + + if (group) { + Ext.Array.remove(group, btn); + btn.un('toggle', me.toggleGroup, me); + } + }, + + + + + getPressed: function(group) { + var g = this.groups[group], + i = 0, + len; + + if (g) { + for (len = g.length; i < len; i++) { + if (g[i].pressed === true) { + return g[i]; + } + } + } + return null; + } +}); + + + +Ext.define('Ext.util.KeyMap', { + alternateClassName: 'Ext.KeyMap', + + + + + + + + + + + eventName: 'keydown', + + constructor: function(config) { + var me = this; + + + + if ((arguments.length !== 1) || (typeof config === 'string') || config.dom || config.tagName || config === document || config.isComponent) { + me.legacyConstructor.apply(me, arguments); + return; + } + + Ext.apply(me, config); + me.bindings = []; + + if (!me.target.isComponent) { + me.target = Ext.get(me.target); + } + + if (me.binding) { + me.addBinding(me.binding); + } else if (config.key) { + me.addBinding(config); + } + me.enable(); + }, + + + legacyConstructor: function(el, binding, eventName){ + var me = this; + + Ext.apply(me, { + target: Ext.get(el), + eventName: eventName || me.eventName, + bindings: [] + }); + if (binding) { + me.addBinding(binding); + } + me.enable(); + }, + + + addBinding : function(binding){ + var me = this, + keyCode = binding.key, + i, + len; + + if (me.processing) { + me.bindings = bindings.slice(0); + } + + if (Ext.isArray(binding)) { + for (i = 0, len = binding.length; i < len; i++) { + me.addBinding(binding[i]); + } + return; + } + + me.bindings.push(Ext.apply({ + keyCode: me.processKeys(keyCode) + }, binding)); + }, + + + removeBinding: function(binding){ + var me = this, + bindings = me.bindings, + len = bindings.length, + i, item, keys; + + if (me.processing) { + me.bindings = bindings.slice(0); + } + + keys = me.processKeys(binding.key); + for (i = 0; i < len; ++i) { + item = bindings[i]; + if ((item.fn || item.handler) === (binding.fn || binding.handler) && item.scope === binding.scope) { + if (binding.alt === item.alt && binding.crtl === item.crtl && binding.shift === item.shift) { + if (Ext.Array.equals(item.keyCode, keys)) { + Ext.Array.erase(me.bindings, i, 1); + return; + } + } + } + } + }, + + processKeys: function(keyCode){ + var processed = false, + key, keys, keyString, len, i; + + if (Ext.isString(keyCode)) { + keys = []; + keyString = keyCode.toUpperCase(); + + for (i = 0, len = keyString.length; i < len; ++i){ + keys.push(keyString.charCodeAt(i)); + } + keyCode = keys; + processed = true; + } + + if (!Ext.isArray(keyCode)) { + keyCode = [keyCode]; + } + + if (!processed) { + for (i = 0, len = keyCode.length; i < len; ++i) { + key = keyCode[i]; + if (Ext.isString(key)) { + keyCode[i] = key.toUpperCase().charCodeAt(0); + } + } + } + return keyCode; + }, + + + handleTargetEvent: (function() { + var tagRe = /input|textarea/i; + + return function(event) { + var me = this, + bindings, i, len, + target, contentEditable; + + if (me.enabled) { + bindings = me.bindings; + i = 0; + len = bindings.length; + + + event = me.processEvent.apply(me||me.processEventScope, arguments); + + + if (me.ignoreInputFields) { + target = event.target; + contentEditable = target.contentEditable; + + + + if (tagRe.test(target.tagName) || (contentEditable === '' || contentEditable === 'true')) { + return; + } + } + + + + if (!event.getKey) { + return event; + } + me.processing = true; + for(; i < len; ++i){ + me.processBinding(bindings[i], event); + } + me.processing = false; + } + }; + }()), + + + processEvent: Ext.identityFn, + + + processBinding: function(binding, event){ + if (this.checkModifiers(binding, event)) { + var key = event.getKey(), + handler = binding.fn || binding.handler, + scope = binding.scope || this, + keyCode = binding.keyCode, + defaultEventAction = binding.defaultEventAction, + i, + len, + keydownEvent = new Ext.EventObjectImpl(event); + + + for (i = 0, len = keyCode.length; i < len; ++i) { + if (key === keyCode[i]) { + if (handler.call(scope, key, event) !== true && defaultEventAction) { + keydownEvent[defaultEventAction](); + } + break; + } + } + } + }, + + + checkModifiers: function(binding, event) { + var keys = ['shift', 'ctrl', 'alt'], + i = 0, + len = keys.length, + val, key; + + for (; i < len; ++i){ + key = keys[i]; + val = binding[key]; + if (!(val === undefined || (val === event[key + 'Key']))) { + return false; + } + } + return true; + }, + + + on: function(key, fn, scope) { + var keyCode, shift, ctrl, alt; + if (Ext.isObject(key) && !Ext.isArray(key)) { + keyCode = key.key; + shift = key.shift; + ctrl = key.ctrl; + alt = key.alt; + } else { + keyCode = key; + } + this.addBinding({ + key: keyCode, + shift: shift, + ctrl: ctrl, + alt: alt, + fn: fn, + scope: scope + }); + }, + + + un: function(key, fn, scope) { + var keyCode, shift, ctrl, alt; + if (Ext.isObject(key) && !Ext.isArray(key)) { + keyCode = key.key; + shift = key.shift; + ctrl = key.ctrl; + alt = key.alt; + } else { + keyCode = key; + } + this.removeBinding({ + key: keyCode, + shift: shift, + ctrl: ctrl, + alt: alt, + fn: fn, + scope: scope + }); + }, + + + isEnabled : function() { + return this.enabled; + }, + + + enable: function() { + var me = this; + + if (!me.enabled) { + me.target.on(me.eventName, me.handleTargetEvent, me); + me.enabled = true; + } + }, + + + disable: function() { + var me = this; + + if (me.enabled) { + me.target.removeListener(me.eventName, me.handleTargetEvent, me); + me.enabled = false; + } + }, + + + setDisabled : function(disabled) { + if (disabled) { + this.disable(); + } else { + this.enable(); + } + }, + + + destroy: function(removeTarget) { + var me = this, + target = me.target; + + me.bindings = []; + me.disable(); + if (removeTarget === true) { + if (target.isComponent) { + target.destroy(); + } else { + target.remove(); + } + } + delete me.target; + } +}); + + + +Ext.define('Ext.layout.component.Component', { + + + + extend: Ext.layout.Layout , + + + + type: 'component', + + isComponentLayout: true, + + nullBox: {}, + + usesContentHeight: true, + usesContentWidth: true, + usesHeight: true, + usesWidth: true, + + beginLayoutCycle: function (ownerContext, firstCycle) { + var me = this, + owner = me.owner, + ownerCtContext = ownerContext.ownerCtContext, + heightModel = ownerContext.heightModel, + widthModel = ownerContext.widthModel, + body = owner.el.dom === document.body, + lastBox = owner.lastBox || me.nullBox, + lastSize = owner.el.lastBox || me.nullBox, + dirty = !body, + ownerLayout, v, widthName, heightName; + + me.callParent(arguments); + + if (firstCycle) { + if (me.usesContentWidth) { + ++ownerContext.consumersContentWidth; + } + if (me.usesContentHeight) { + ++ownerContext.consumersContentHeight; + } + if (me.usesWidth) { + ++ownerContext.consumersWidth; + } + if (me.usesHeight) { + ++ownerContext.consumersHeight; + } + + if (ownerCtContext && !ownerCtContext.hasRawContent) { + ownerLayout = owner.ownerLayout; + + if (ownerLayout.usesWidth) { + ++ownerContext.consumersWidth; + } + if (ownerLayout.usesHeight) { + ++ownerContext.consumersHeight; + } + } + } + + + + + if (widthModel.configured) { + + + + + widthName = widthModel.names.width; + + if (!body) { + dirty = me.setWidthInDom || + (firstCycle ? owner[widthName] !== lastSize.width : widthModel.constrained); + } + + ownerContext.setWidth(owner[widthName], dirty); + } else if (ownerContext.isTopLevel) { + if (widthModel.calculated) { + v = lastBox.width; + ownerContext.setWidth(v, v != lastSize.width); + } + + v = lastBox.x; + ownerContext.setProp('x', v, v != lastSize.x); + } + + if (heightModel.configured) { + heightName = heightModel.names.height; + + if (!body) { + dirty = firstCycle ? owner[heightName] !== lastSize.height + : heightModel.constrained; + } + + ownerContext.setHeight(owner[heightName], dirty); + } else if (ownerContext.isTopLevel) { + if (heightModel.calculated) { + v = lastBox.height; + ownerContext.setHeight(v, v != lastSize.height); + } + + v = lastBox.y; + ownerContext.setProp('y', v, v != lastSize.y); + } + }, + + finishedLayout: function(ownerContext) { + var me = this, + elementChildren = ownerContext.children, + owner = me.owner, + len, i, elContext, lastBox, props; + + + + + + if (elementChildren) { + len = elementChildren.length; + for (i = 0; i < len; i++) { + elContext = elementChildren[i]; + elContext.el.lastBox = elContext.props; + } + } + + + ownerContext.previousSize = me.lastComponentSize; + + + me.lastComponentSize = owner.el.lastBox = props = ownerContext.props; + + + + lastBox = owner.lastBox || (owner.lastBox = {}); + lastBox.x = props.x; + lastBox.y = props.y; + lastBox.width = props.width; + lastBox.height = props.height; + lastBox.invalid = false; + + me.callParent(arguments); + }, + + notifyOwner: function(ownerContext) { + var me = this, + currentSize = me.lastComponentSize, + prevSize = ownerContext.previousSize, + args = [currentSize.width, currentSize.height]; + + if (prevSize) { + args.push(prevSize.width, prevSize.height); + } + + + me.owner.afterComponentLayout.apply(me.owner, args); + }, + + + getTarget : function() { + return this.owner.el; + }, + + + getRenderTarget : function() { + return this.owner.el; + }, + + cacheTargetInfo: function(ownerContext) { + var me = this, + targetInfo = me.targetInfo, + target; + + if (!targetInfo) { + target = ownerContext.getEl('getTarget', me); + + me.targetInfo = targetInfo = { + padding: target.getPaddingInfo(), + border: target.getBorderInfo() + }; + } + + return targetInfo; + }, + + measureAutoDimensions: function (ownerContext, dimensions) { + + + + + + + var me = this, + owner = me.owner, + containerLayout = owner.layout, + heightModel = ownerContext.heightModel, + widthModel = ownerContext.widthModel, + boxParent = ownerContext.boxParent, + isBoxParent = ownerContext.isBoxParent, + props = ownerContext.props, + isContainer, + ret = { + gotWidth: false, + gotHeight: false, + isContainer: (isContainer = !ownerContext.hasRawContent) + }, + hv = dimensions || 3, + zeroWidth, zeroHeight, + needed = 0, + got = 0, + ready, size, temp; + + + + + + + + if (widthModel.shrinkWrap && ownerContext.consumersContentWidth) { + ++needed; + zeroWidth = !(hv & 1); + + if (isContainer) { + + + if (zeroWidth) { + ret.contentWidth = 0; + ret.gotWidth = true; + ++got; + } else if ((ret.contentWidth = ownerContext.getProp('contentWidth')) !== undefined) { + ret.gotWidth = true; + ++got; + } + } else { + size = props.contentWidth; + + if (typeof size == 'number') { + ret.contentWidth = size; + ret.gotWidth = true; + ++got; + } else { + if (zeroWidth) { + ready = true; + } else if (!ownerContext.hasDomProp('containerChildrenSizeDone')) { + ready = false; + } else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) { + + + + ready = true; + } else { + + + + ready = boxParent.hasDomProp('width'); + } + + if (ready) { + if (zeroWidth) { + temp = 0; + } else if (containerLayout && containerLayout.measureContentWidth) { + + + temp = containerLayout.measureContentWidth(ownerContext); + } else { + temp = me.measureContentWidth(ownerContext); + } + + if (!isNaN(ret.contentWidth = temp)) { + ownerContext.setContentWidth(temp, true); + ret.gotWidth = true; + ++got; + } + } + } + } + } else if (widthModel.natural && ownerContext.consumersWidth) { + ++needed; + size = props.width; + + + if (typeof size == 'number') { + ret.width = size; + ret.gotWidth = true; + ++got; + } else { + if (isBoxParent || !boxParent) { + ready = true; + } else { + + + + ready = boxParent.hasDomProp('width'); + } + + if (ready) { + if (!isNaN(ret.width = me.measureOwnerWidth(ownerContext))) { + ownerContext.setWidth(ret.width, false); + ret.gotWidth = true; + ++got; + } + } + } + } + + + + + if (heightModel.shrinkWrap && ownerContext.consumersContentHeight) { + ++needed; + zeroHeight = !(hv & 2); + + if (isContainer) { + + if (zeroHeight) { + ret.contentHeight = 0; + ret.gotHeight = true; + ++got; + } else if ((ret.contentHeight = ownerContext.getProp('contentHeight')) !== undefined) { + ret.gotHeight = true; + ++got; + } + } else { + size = props.contentHeight; + + if (typeof size == 'number') { + ret.contentHeight = size; + ret.gotHeight = true; + ++got; + } else { + if (zeroHeight) { + ready = true; + } else if (!ownerContext.hasDomProp('containerChildrenSizeDone')) { + ready = false; + } else if (owner.noWrap) { + ready = true; + } else if (!widthModel.shrinkWrap) { + + ready = (ownerContext.bodyContext || ownerContext).hasDomProp('width'); + } else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) { + + + + ready = true; + } else { + + + + ready = boxParent.hasDomProp('width'); + } + + if (ready) { + if (zeroHeight) { + temp = 0; + } else if (containerLayout && containerLayout.measureContentHeight) { + + + temp = containerLayout.measureContentHeight(ownerContext); + } else { + temp = me.measureContentHeight(ownerContext); + } + + if (!isNaN(ret.contentHeight = temp)) { + ownerContext.setContentHeight(temp, true); + ret.gotHeight = true; + ++got; + } + } + } + } + } else if (heightModel.natural && ownerContext.consumersHeight) { + ++needed; + size = props.height; + + + if (typeof size == 'number') { + ret.height = size; + ret.gotHeight = true; + ++got; + } else { + if (isBoxParent || !boxParent) { + ready = true; + } else { + + + + ready = boxParent.hasDomProp('width'); + } + + if (ready) { + if (!isNaN(ret.height = me.measureOwnerHeight(ownerContext))) { + ownerContext.setHeight(ret.height, false); + ret.gotHeight = true; + ++got; + } + } + } + } + + if (boxParent) { + ownerContext.onBoxMeasured(); + } + + ret.gotAll = got == needed; + + return ret; + }, + + measureContentWidth: function (ownerContext) { + + return ownerContext.el.getWidth() - ownerContext.getFrameInfo().width; + }, + + measureContentHeight: function (ownerContext) { + + return ownerContext.el.getHeight() - ownerContext.getFrameInfo().height; + }, + + measureOwnerHeight: function (ownerContext) { + return ownerContext.el.getHeight(); + }, + + measureOwnerWidth: function (ownerContext) { + return ownerContext.el.getWidth(); + } +}); + + + +Ext.define('Ext.layout.component.Dock', { + + + + extend: Ext.layout.component.Component , + + alias: 'layout.dock', + + 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 me = this, + target, items; + + me.callParent(); + + target = me.getRenderTarget(); + items = me.getDockedItems(); + + me.finishRenderItems(target, items); + }, + + isItemBoxParent: function (itemContext) { + return true; + }, + + isItemShrinkWrap: function (item) { + return true; + }, + + noBorderClasses: [ + Ext.baseCSSPrefix + 'docked-noborder-top', + Ext.baseCSSPrefix + 'docked-noborder-right', + Ext.baseCSSPrefix + 'docked-noborder-bottom', + Ext.baseCSSPrefix + 'docked-noborder-left' + ], + + noBorderClassesSides: { + top: Ext.baseCSSPrefix + 'docked-noborder-top', + right: Ext.baseCSSPrefix + 'docked-noborder-right', + bottom: Ext.baseCSSPrefix + 'docked-noborder-bottom', + left: Ext.baseCSSPrefix + 'docked-noborder-left' + }, + + borderWidthProps: { + top: 'border-top-width', + right: 'border-right-width', + bottom: 'border-bottom-width', + left: 'border-left-width' + }, + + handleItemBorders: function() { + var me = this, + owner = me.owner, + borders, docked, + lastItems = me.lastDockedItems, + oldBorders = me.borders, + currentGeneration = owner.dockedItems.generation, + noBorderClassesSides = me.noBorderClassesSides, + borderWidthProps = me.borderWidthProps, + i, ln, item, dock, side, + collapsed = me.collapsed; + + if (me.initializedBorders == currentGeneration || (owner.border && !owner.manageBodyBorders)) { + return; + } + + me.initializedBorders = currentGeneration; + + + me.collapsed = false; + me.lastDockedItems = docked = me.getLayoutItems(); + me.collapsed = collapsed; + + borders = { top: [], right: [], bottom: [], left: [] }; + + for (i = 0, ln = docked.length; i < ln; i++) { + item = docked[i]; + dock = item.dock; + + if (item.ignoreBorderManagement) { + continue; + } + + if (!borders[dock].satisfied) { + borders[dock].push(item); + borders[dock].satisfied = true; + } + + if (!borders.top.satisfied && dock !== 'bottom') { + borders.top.push(item); + } + if (!borders.right.satisfied && dock !== 'left') { + borders.right.push(item); + } + if (!borders.bottom.satisfied && dock !== 'top') { + borders.bottom.push(item); + } + if (!borders.left.satisfied && dock !== 'right') { + borders.left.push(item); + } + } + + if (lastItems) { + for (i = 0, ln = lastItems.length; i < ln; i++) { + item = lastItems[i]; + if (!item.isDestroyed && !item.ignoreBorderManagement && !owner.manageBodyBorders) { + item.removeCls(me.noBorderClasses); + } + } + } + + if (oldBorders) { + for (side in oldBorders) { + if (owner.manageBodyBorders && oldBorders[side].satisfied) { + owner.setBodyStyle(borderWidthProps[side], ''); + } + } + } + + for (side in borders) { + ln = borders[side].length; + if (!owner.manageBodyBorders) { + for (i = 0; i < ln; i++) { + borders[side][i].addCls(noBorderClassesSides[side]); + } + if ((!borders[side].satisfied && !owner.bodyBorder) || owner.bodyBorder === false) { + owner.addBodyCls(noBorderClassesSides[side]); + } + } + else if (borders[side].satisfied) { + owner.setBodyStyle(borderWidthProps[side], '1px'); + } + } + + me.borders = borders; + }, + + beforeLayoutCycle: function (ownerContext) { + var me = this, + owner = me.owner, + shrinkWrap = me.sizeModels.shrinkWrap, + shrinkWrapDock = owner.shrinkWrapDock, + collapsedHorz, collapsedVert; + + if (owner.collapsed) { + if (owner.collapsedVertical()) { + collapsedVert = true; + ownerContext.measureDimensions = 1; + } else { + collapsedHorz = true; + ownerContext.measureDimensions = 2; + } + } + + ownerContext.collapsedVert = collapsedVert; + ownerContext.collapsedHorz = collapsedHorz; + + + + + + if (collapsedVert) { + ownerContext.heightModel = shrinkWrap; + } else if (collapsedHorz) { + ownerContext.widthModel = shrinkWrap; + } + + shrinkWrapDock = shrinkWrapDock === true ? 3 : (shrinkWrapDock || 0); + ownerContext.shrinkWrapDockHeight = (shrinkWrapDock & 1) && ownerContext.heightModel.shrinkWrap; + ownerContext.shrinkWrapDockWidth = (shrinkWrapDock & 2) && ownerContext.widthModel.shrinkWrap; + }, + + beginLayout: function(ownerContext) { + var me = this, + owner = me.owner, + docked = me.getLayoutItems(), + layoutContext = ownerContext.context, + dockedItemCount = docked.length, + dockedItems, i, item, itemContext, offsets, + collapsed, dock; + + me.callParent(arguments); + + + + collapsed = owner.getCollapsed(); + if (collapsed !== me.lastCollapsedState && Ext.isDefined(me.lastCollapsedState)) { + + if (me.owner.collapsed) { + ownerContext.isCollapsingOrExpanding = 1; + + owner.addClsWithUI(owner.collapsedCls); + } else { + ownerContext.isCollapsingOrExpanding = 2; + + owner.removeClsWithUI(owner.collapsedCls); + ownerContext.lastCollapsedState = me.lastCollapsedState; + } + } + me.lastCollapsedState = collapsed; + + ownerContext.dockedItems = dockedItems = []; + + for (i = 0; i < dockedItemCount; i++) { + item = docked[i]; + if (item.rendered) { + dock = item.dock; + itemContext = layoutContext.getCmp(item); + itemContext.dockedAt = { x: 0, y: 0 }; + itemContext.offsets = offsets = Ext.Element.parseBox(item.offsets || 0); + itemContext.horizontal = dock == 'top' || dock == 'bottom'; + offsets.width = offsets.left + offsets.right; + offsets.height = offsets.top + offsets.bottom; + dockedItems.push(itemContext); + } + } + + ownerContext.bodyContext = ownerContext.getEl('body'); + }, + + beginLayoutCycle: function(ownerContext) { + var me = this, + docked = ownerContext.dockedItems, + len = docked.length, + owner = me.owner, + frameBody = owner.frameBody, + lastHeightModel = me.lastHeightModel, + i, item, dock; + + me.callParent(arguments); + + if (me.owner.manageHeight) { + + + if (me.lastBodyDisplay) { + owner.body.dom.style.display = me.lastBodyDisplay = ''; + } + } else { + + + + if (me.lastBodyDisplay !== 'inline-block') { + owner.body.dom.style.display = me.lastBodyDisplay = 'inline-block'; + } + + if (lastHeightModel && lastHeightModel.shrinkWrap && + !ownerContext.heightModel.shrinkWrap) { + owner.body.dom.style.marginBottom = ''; + } + } + + if (ownerContext.widthModel.auto) { + if (ownerContext.widthModel.shrinkWrap) { + owner.el.setWidth(null); + } + owner.body.setWidth(null); + if (frameBody) { + frameBody.setWidth(null); + } + } + if (ownerContext.heightModel.auto) { + owner.body.setHeight(null); + + if (frameBody) { + frameBody.setHeight(null); + } + } + + + + if (ownerContext.collapsedVert) { + ownerContext.setContentHeight(0); + } else if (ownerContext.collapsedHorz) { + ownerContext.setContentWidth(0); + } + + + + for (i = 0; i < len; i++) { + item = docked[i].target; + dock = item.dock; + + if (dock == 'right') { + item.setLocalX(0); + } else if (dock != 'left') { + continue; + } + + + } + }, + + calculate: function (ownerContext) { + var me = this, + measure = me.measureAutoDimensions(ownerContext, ownerContext.measureDimensions), + state = ownerContext.state, + horzDone = state.horzDone, + vertDone = state.vertDone, + bodyContext = ownerContext.bodyContext, + framing, horz, vert, forward, backward; + + + ownerContext.borderInfo || ownerContext.getBorderInfo(); + ownerContext.paddingInfo || ownerContext.getPaddingInfo(); + ownerContext.frameInfo || ownerContext.getFrameInfo(); + bodyContext.borderInfo || bodyContext.getBorderInfo(); + bodyContext.paddingInfo || bodyContext.getPaddingInfo(); + + + + + + + + + + + + + + + + + + + + + if (!ownerContext.frameBorder) { + if (!(framing = ownerContext.framing)) { + ownerContext.frameBorder = ownerContext.borderInfo; + ownerContext.framePadding = ownerContext.paddingInfo; + } else { + + ownerContext.frameBorder = framing.border; + ownerContext.framePadding = framing.padding; + } + } + + + + horz = !horzDone && + me.createAxis(ownerContext, measure.contentWidth, ownerContext.widthModel, + me.horzAxisProps, ownerContext.collapsedHorz); + vert = !vertDone && + me.createAxis(ownerContext, measure.contentHeight, ownerContext.heightModel, + me.vertAxisProps, ownerContext.collapsedVert); + + + + + + + + + + for (forward = 0, backward = ownerContext.dockedItems.length; backward--; ++forward) { + if (horz) { + me.dockChild(ownerContext, horz, backward, forward); + } + if (vert) { + me.dockChild(ownerContext, vert, backward, forward); + } + } + + if (horz && me.finishAxis(ownerContext, horz)) { + state.horzDone = horzDone = horz; + } + + if (vert && me.finishAxis(ownerContext, vert)) { + state.vertDone = vertDone = vert; + } + + + + if (horzDone && vertDone && me.finishConstraints(ownerContext, horzDone, vertDone)) { + + + + me.finishPositions(ownerContext, horzDone, vertDone); + } else { + me.done = false; + } + }, + + + createAxis: function (ownerContext, contentSize, sizeModel, axisProps, collapsedAxis) { + var me = this, + begin = 0, + owner = me.owner, + maxSize = owner[axisProps.maxSize], + minSize = owner[axisProps.minSize] || 0, + dockBegin = axisProps.dockBegin, + dockEnd = axisProps.dockEnd, + posProp = axisProps.pos, + sizeProp = axisProps.size, + hasMaxSize = maxSize != null, + shrinkWrap = sizeModel.shrinkWrap, + bodyContext, framing, padding, end; + + if (shrinkWrap) { + + + if (collapsedAxis) { + end = 0; + } else { + bodyContext = ownerContext.bodyContext; + end = contentSize + bodyContext.borderInfo[sizeProp]; + } + } else { + framing = ownerContext.frameBorder; + padding = ownerContext.framePadding; + + begin = framing[dockBegin] + padding[dockBegin]; + end = ownerContext.getProp(sizeProp) - (framing[dockEnd] + padding[dockEnd]); + } + + return { + shrinkWrap: sizeModel.shrinkWrap, + sizeModel: sizeModel, + + initialBegin: begin, + begin: begin, + end: end, + collapsed: collapsedAxis, + horizontal: axisProps.horizontal, + ignoreFrameBegin: null, + ignoreFrameEnd: null, + initialSize: end - begin, + maxChildSize: 0, + hasMinMaxConstraints: (minSize || hasMaxSize) && sizeModel.shrinkWrap, + minSize: minSize, + maxSize: hasMaxSize ? maxSize : 1e9, + bodyPosProp: me.owner.manageHeight ? posProp : axisProps.marginBegin, + dockBegin: dockBegin, + dockEnd: dockEnd, + posProp: posProp, + sizeProp: sizeProp, + setSize: axisProps.setSize, + shrinkWrapDock: ownerContext[axisProps.shrinkWrapDock], + sizeModelName: axisProps.sizeModel, + dockedPixelsEnd: 0 + }; + }, + + + dockChild: function (ownerContext, axis, backward, forward) { + var me = this, + itemContext = ownerContext.dockedItems[axis.shrinkWrap ? backward : forward], + item = itemContext.target, + dock = item.dock, + sizeProp = axis.sizeProp, + pos, size; + + if (item.ignoreParentFrame && ownerContext.isCollapsingOrExpanding) { + + + itemContext.clearMarginCache(); + } + + itemContext.marginInfo || itemContext.getMarginInfo(); + + if (dock == axis.dockBegin) { + if (axis.shrinkWrap) { + pos = me.dockOutwardBegin(ownerContext, itemContext, item, axis); + } else { + pos = me.dockInwardBegin(ownerContext, itemContext, item, axis); + } + } else if (dock == axis.dockEnd) { + if (axis.shrinkWrap) { + pos = me.dockOutwardEnd(ownerContext, itemContext, item, axis); + } else { + pos = me.dockInwardEnd(ownerContext, itemContext, item, axis); + } + } else { + if (axis.shrinkWrapDock) { + + + size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp]; + axis.maxChildSize = Math.max(axis.maxChildSize, size); + pos = 0; + } else { + pos = me.dockStretch(ownerContext, itemContext, item, axis); + } + } + + itemContext.dockedAt[axis.posProp] = pos; + }, + + + dockInwardBegin: function (ownerContext, itemContext, item, axis) { + var pos = axis.begin, + sizeProp = axis.sizeProp, + ignoreParentFrame = item.ignoreParentFrame, + delta, + size, + dock; + + if (ignoreParentFrame) { + axis.ignoreFrameBegin = itemContext; + dock = item.dock; + + + delta = ownerContext.frameBorder[dock]; + + + pos -= delta + ownerContext.framePadding[dock]; + } + + if (!item.overlay) { + size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp]; + axis.begin += size; + if (ignoreParentFrame) { + axis.begin -= delta; + } + } + + return pos; + }, + + + dockInwardEnd: function (ownerContext, itemContext, item, axis) { + var sizeProp = axis.sizeProp, + size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp], + pos = axis.end - size, + frameEnd; + + if (!item.overlay) { + axis.end = pos; + } + + if (item.ignoreParentFrame) { + axis.ignoreFrameEnd = itemContext; + frameEnd = ownerContext.frameBorder[item.dock]; + pos += frameEnd + ownerContext.framePadding[item.dock]; + axis.end += frameEnd; + } + + return pos; + }, + + + dockOutwardBegin: function (ownerContext, itemContext, item, axis) { + var pos = axis.begin, + sizeProp = axis.sizeProp, + size; + + if (axis.collapsed) { + axis.ignoreFrameBegin = axis.ignoreFrameEnd = itemContext; + } else if (item.ignoreParentFrame) { + axis.ignoreFrameBegin = itemContext; + } + + + + if (!item.overlay) { + size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp]; + pos -= size; + axis.begin = pos; + } + + return pos; + }, + + + dockOutwardEnd: function (ownerContext, itemContext, item, axis) { + var pos = axis.end, + sizeProp = axis.sizeProp, + size; + + size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp]; + + if (axis.collapsed) { + axis.ignoreFrameBegin = axis.ignoreFrameEnd = itemContext; + } else if (item.ignoreParentFrame) { + axis.ignoreFrameEnd = itemContext; + } + + + + if (!item.overlay) { + axis.end = pos + size; + axis.dockedPixelsEnd += size; + } + + return pos; + }, + + + dockStretch: function (ownerContext, itemContext, item, axis) { + var dock = item.dock, + sizeProp = axis.sizeProp, + horizontal = dock == 'top' || dock == 'bottom', + border = ownerContext.frameBorder, + offsets = itemContext.offsets, + padding = ownerContext.framePadding, + endProp = horizontal ? 'right' : 'bottom', + startProp = horizontal ? 'left' : 'top', + pos = axis.begin + offsets[startProp], + margin, size; + + if (item.stretch !== false) { + size = axis.end - pos - offsets[endProp]; + + if (item.ignoreParentFrame) { + + + + + pos -= padding[startProp] + border[startProp]; + size += padding[sizeProp] + border[sizeProp]; + } + + margin = itemContext.marginInfo; + size -= margin[sizeProp]; + + itemContext[axis.setSize](size); + } + + return pos; + }, + + + finishAxis: function (ownerContext, axis) { + + + + if (isNaN(axis.maxChildSize)) { + return false; + } + + var axisBegin = axis.begin, + size = axis.end - axisBegin, + collapsed = axis.collapsed, + setSizeMethod = axis.setSize, + beginName = axis.dockBegin, + endName = axis.dockEnd, + padding = ownerContext.framePadding, + border = ownerContext.frameBorder, + borderBegin = border[beginName], + framing = ownerContext.framing, + framingBegin = framing && framing[beginName], + + paddingBegin = collapsed ? 0 : padding[beginName], + sizeProp = axis.sizeProp, + ignoreFrameBegin = axis.ignoreFrameBegin, + ignoreFrameEnd = axis.ignoreFrameEnd, + bodyContext = ownerContext.bodyContext, + extraPaddingBegin = Math.max(borderBegin + paddingBegin - framingBegin, 0), + bodyPos, bodySize, delta, dirty; + + if (axis.shrinkWrap) { + + + + + bodySize = axis.initialSize; + + if (framing) { + + + + + + + + + delta = -axisBegin + borderBegin + paddingBegin; + bodyPos = delta - framingBegin - extraPaddingBegin; + } else { + bodyPos = -axisBegin; + delta = bodyPos + paddingBegin; + } + + if (!collapsed) { + size += padding[sizeProp]; + } + + if (ignoreFrameBegin) { + + + + delta -= borderBegin; + bodyPos -= borderBegin; + + + + + + + + ignoreFrameBegin.dockedAt[axis.posProp] -= paddingBegin; + } else { + size += borderBegin; + } + + if (collapsed) { + + + } else if (ignoreFrameEnd) { + + + + ignoreFrameEnd.dockedAt[axis.posProp] += padding[endName]; + } else { + size += border[endName]; + } + + axis.size = size; + + if (!axis.horizontal && !this.owner.manageHeight) { + + + dirty = false; + } + } else { + + + if (framing) { + + + delta = 0; + bodyPos = axisBegin - framingBegin - extraPaddingBegin; + } else { + delta = -borderBegin; + bodyPos = axisBegin - paddingBegin - borderBegin; + } + + + bodySize = size; + } + + axis.delta = delta; + bodyContext[setSizeMethod](bodySize, dirty); + bodyContext.setProp(axis.bodyPosProp, bodyPos); + + return !isNaN(size); + }, + + beforeInvalidateShrinkWrapDock: function(itemContext, options){ + var sizeModelName = options.axis.sizeModelName; + if (!itemContext[sizeModelName].constrainedMin) { + + + itemContext[sizeModelName] = Ext.layout.SizeModel.calculated; + } + }, + + afterInvalidateShrinkWrapDock: function(itemContext, options){ + var axis = options.axis, + me = options.layout, + pos; + + if (itemContext[axis.sizeModelName].calculated) { + pos = me.dockStretch(options.ownerContext, itemContext, itemContext.target, axis); + itemContext.setProp(axis.posProp, axis.delta + pos); + } + }, + + + finishConstraints: function (ownerContext, horz, vert) { + var me = this, + sizeModels = me.sizeModels, + publishWidth = horz.shrinkWrap, + publishHeight = vert.shrinkWrap, + owner = me.owner, + dirty, height, width, heightModel, widthModel, size, + minSize, maxSize, maxChildSize, desiredSize; + + + + + + + if (publishWidth) { + size = horz.size; + minSize = horz.collapsed ? 0 : horz.minSize; + maxSize = horz.maxSize; + maxChildSize = horz.maxChildSize; + desiredSize = Math.max(size, maxChildSize); + + if (desiredSize > maxSize) { + widthModel = sizeModels.constrainedMax; + width = maxSize; + } else if (desiredSize < minSize) { + widthModel = sizeModels.constrainedMin; + width = minSize; + } else if (size < maxChildSize) { + widthModel = sizeModels.constrainedDock; + owner.dockConstrainedWidth = width = maxChildSize; + } else { + width = size; + } + } + + if (publishHeight) { + size = vert.size; + minSize = vert.collapsed ? 0 : vert.minSize; + maxSize = vert.maxSize; + maxChildSize = vert.maxChildSize; + + + desiredSize = Math.max(size, maxChildSize + size - vert.initialSize); + + if (desiredSize > maxSize) { + heightModel = sizeModels.constrainedMax; + height = maxSize; + } else if (desiredSize < minSize) { + heightModel = sizeModels.constrainedMin; + height = minSize; + } else if (size < maxChildSize) { + heightModel = sizeModels.constrainedDock; + owner.dockConstrainedHeight = height = maxChildSize; + } else { + if (!ownerContext.collapsedVert && !owner.manageHeight) { + + + dirty = false; + + + ownerContext.bodyContext.setProp('margin-bottom', vert.dockedPixelsEnd); + } + + height = size; + } + } + + + + if (widthModel || heightModel) { + + + if (widthModel && heightModel && + widthModel.constrainedMax && heightModel.constrainedByMin) { + ownerContext.invalidate({ widthModel: widthModel }); + return false; + } + + + + + if (!ownerContext.widthModel.calculatedFromShrinkWrap && + !ownerContext.heightModel.calculatedFromShrinkWrap) { + + ownerContext.invalidate({ widthModel: widthModel, heightModel: heightModel }); + return false; + } + + + + + } else { + + + me.invalidateAxes(ownerContext, horz, vert); + + } + + + + if (publishWidth) { + ownerContext.setWidth(width); + if (widthModel) { + ownerContext.widthModel = widthModel; + } + } + if (publishHeight) { + ownerContext.setHeight(height, dirty); + if (heightModel) { + ownerContext.heightModel = heightModel; + } + } + + return true; + }, + + + invalidateAxes: function(ownerContext, horz, vert){ + var before = this.beforeInvalidateShrinkWrapDock, + after = this.afterInvalidateShrinkWrapDock, + horzSize = horz.end - horz.begin, + vertSize = vert.initialSize, + invalidateHorz = horz.shrinkWrapDock && horz.maxChildSize <= horzSize, + invalidateVert = vert.shrinkWrapDock && vert.maxChildSize <= vertSize, + dockedItems, len, i, itemContext, itemSize, isHorz, axis, sizeProp; + + if (invalidateHorz || invalidateVert) { + if (invalidateVert) { + + + vert.begin = vert.initialBegin; + vert.end = vert.begin + vert.initialSize; + } + dockedItems = ownerContext.dockedItems; + for (i = 0, len = dockedItems.length; i < len; ++i) { + itemContext = dockedItems[i]; + isHorz = itemContext.horizontal; + axis = null; + if (invalidateHorz && isHorz) { + sizeProp = horz.sizeProp; + itemSize = horzSize; + axis = horz; + } else if (invalidateVert && !isHorz) { + sizeProp = vert.sizeProp; + itemSize = vertSize; + axis = vert; + } + + if (axis) { + + itemSize -= itemContext.getMarginInfo()[sizeProp]; + if (itemSize !== itemContext.props[sizeProp]) { + itemContext.invalidate({ + before: before, + after: after, + axis: axis, + ownerContext: ownerContext, + layout: this + }); + } + } + } + } + }, + + + finishPositions: function (ownerContext, horz, vert) { + var dockedItems = ownerContext.dockedItems, + length = dockedItems.length, + deltaX = horz.delta, + deltaY = vert.delta, + index, itemContext; + + for (index = 0; index < length; ++index) { + itemContext = dockedItems[index]; + + itemContext.setProp('x', deltaX + itemContext.dockedAt.x); + itemContext.setProp('y', deltaY + itemContext.dockedAt.y); + } + }, + + finishedLayout: function(ownerContext) { + var me = this, + target = ownerContext.target; + + me.callParent(arguments); + + if (!ownerContext.animatePolicy) { + if (ownerContext.isCollapsingOrExpanding === 1) { + target.afterCollapse(false); + } else if (ownerContext.isCollapsingOrExpanding === 2) { + target.afterExpand(false); + } + } + }, + + getAnimatePolicy: function(ownerContext) { + var me = this, + lastCollapsedState, policy; + + if (ownerContext.isCollapsingOrExpanding == 1) { + lastCollapsedState = me.lastCollapsedState; + } else if (ownerContext.isCollapsingOrExpanding == 2) { + lastCollapsedState = ownerContext.lastCollapsedState; + } + + if (lastCollapsedState == 'left' || lastCollapsedState == 'right') { + policy = me.horizontalCollapsePolicy; + } else if (lastCollapsedState == 'top' || lastCollapsedState == 'bottom') { + policy = me.verticalCollapsePolicy; + } + + return policy; + }, + + + getDockedItems: function(order, beforeBody) { + var me = this, + renderedOnly = (order === 'visual'), + all = renderedOnly ? Ext.ComponentQuery.query('[rendered]', me.owner.dockedItems.items) : me.owner.dockedItems.items, + sort = all && all.length && order !== false, + renderOrder, + dock, dockedItems, i, isBefore, length; + + if (beforeBody == null) { + dockedItems = sort && !renderedOnly ? all.slice() : all; + } else { + dockedItems = []; + + for (i = 0, length = all.length; i < length; ++i) { + dock = all[i].dock; + isBefore = (dock == 'top' || dock == 'left'); + if (beforeBody ? isBefore : !isBefore) { + dockedItems.push(all[i]); + } + } + + sort = sort && dockedItems.length; + } + + if (sort) { + renderOrder = (order = order || 'render') == 'render'; + Ext.Array.sort(dockedItems, function(a, b) { + var aw, + bw; + + + + if (renderOrder && ((aw = me.owner.dockOrder[a.dock]) !== (bw = me.owner.dockOrder[b.dock]))) { + + + if (!(aw + bw)) { + return aw - bw; + } + } + + aw = me.getItemWeight(a, order); + bw = me.getItemWeight(b, order); + if ((aw !== undefined) && (bw !== undefined)) { + return aw - bw; + } + return 0; + }); + } + + return dockedItems || []; + }, + + getItemWeight: function (item, order) { + var weight = item.weight || this.owner.defaultDockWeights[item.dock]; + return weight[order] || weight; + }, + + + getLayoutItems : function() { + var me = this, + items, + itemCount, + item, + i, + result; + + if (me.owner.collapsed) { + result = me.owner.getCollapsedDockedItems(); + } else { + items = me.getDockedItems('visual'); + itemCount = items.length; + result = []; + for (i = 0; i < itemCount; i++) { + item = items[i]; + if (!item.hidden) { + result.push(item); + } + } + } + return result; + }, + + + measureContentWidth: function (ownerContext) { + var bodyContext = ownerContext.bodyContext; + return bodyContext.el.getWidth() - bodyContext.getBorderInfo().width; + }, + + measureContentHeight: function (ownerContext) { + var bodyContext = ownerContext.bodyContext; + return bodyContext.el.getHeight() - bodyContext.getBorderInfo().height; + }, + + redoLayout: function(ownerContext) { + var me = this, + owner = me.owner; + + + if (ownerContext.isCollapsingOrExpanding == 1) { + if (owner.reExpander) { + owner.reExpander.el.show(); + } + + owner.addClsWithUI(owner.collapsedCls); + ownerContext.redo(true); + } else if (ownerContext.isCollapsingOrExpanding == 2) { + + owner.removeClsWithUI(owner.collapsedCls); + ownerContext.bodyContext.redo(); + } + }, + + + + renderChildren: function() { + var me = this, + items = me.getDockedItems(), + target = me.getRenderTarget(); + + me.handleItemBorders(); + + me.renderItems(items, target); + }, + + + renderItems: function(items, target) { + var me = this, + dockedItemCount = items.length, + itemIndex = 0, + correctPosition = 0, + staticNodeCount = 0, + targetNodes = me.getRenderTarget().dom.childNodes, + targetChildCount = targetNodes.length, + i, j, targetChildNode, item; + + + for (i = 0, j = 0; i < targetChildCount; i++) { + targetChildNode = targetNodes[i]; + if (Ext.fly(targetChildNode).hasCls(Ext.baseCSSPrefix + 'resizable-handle')) { + break; + } + for (j = 0; j < dockedItemCount; j++) { + item = items[j]; + if (item.rendered && item.el.dom === targetChildNode) { + break; + } + } + + + if (j === dockedItemCount) { + staticNodeCount++; + } + } + + + for (; itemIndex < dockedItemCount; itemIndex++, correctPosition++) { + item = items[itemIndex]; + + + + + + + + + if (itemIndex === correctPosition && (item.dock === 'right' || item.dock === 'bottom')) { + correctPosition += staticNodeCount; + } + + + if (item && !item.rendered) { + me.renderItem(item, target, correctPosition); + } + else if (!me.isValidParent(item, target, correctPosition)) { + me.moveItem(item, target, correctPosition); + } + } + }, + + undoLayout: function(ownerContext) { + var me = this, + owner = me.owner; + + + if (ownerContext.isCollapsingOrExpanding == 1) { + + + if (owner.reExpander) { + owner.reExpander.el.hide(); + } + + owner.removeClsWithUI(owner.collapsedCls); + ownerContext.undo(true); + } else if (ownerContext.isCollapsingOrExpanding == 2) { + + owner.addClsWithUI(owner.collapsedCls); + ownerContext.bodyContext.undo(); + } + }, + + sizePolicy: { + nostretch: { + setsWidth: 0, + setsHeight: 0 + }, + + horz: { + shrinkWrap: { + + + setsWidth: 1, + setsHeight: 0, + readsWidth: 1 + }, + stretch: { + setsWidth: 1, + setsHeight: 0 + } + }, + + vert: { + shrinkWrap: { + setsWidth: 0, + setsHeight: 1, + readsHeight: 1 + }, + stretch: { + setsWidth: 0, + setsHeight: 1 + } + }, + + stretchV: { + setsWidth: 0, + setsHeight: 1 + }, + + + + + + + + + + + + + + + + + + + + + + + autoStretchH: { + readsWidth: 1, + setsWidth: 1, + setsHeight: 0 + }, + autoStretchV: { + readsHeight: 1, + setsWidth: 0, + setsHeight: 1 + } + }, + + getItemSizePolicy: function (item, ownerSizeModel) { + var me = this, + policy = me.sizePolicy, + shrinkWrapDock = me.owner.shrinkWrapDock, + dock, vertical; + + if (item.stretch === false) { + return policy.nostretch; + } + + dock = item.dock; + vertical = (dock == 'left' || dock == 'right'); + + shrinkWrapDock = shrinkWrapDock === true ? 3 : (shrinkWrapDock || 0); + if (vertical) { + policy = policy.vert; + shrinkWrapDock = shrinkWrapDock & 1; + } else { + policy = policy.horz; + shrinkWrapDock = shrinkWrapDock & 2; + } + + if (shrinkWrapDock) { + + if (!ownerSizeModel) { + ownerSizeModel = me.owner.getSizeModel(); + } + if (ownerSizeModel[vertical ? 'height' : 'width'].shrinkWrap) { + return policy.shrinkWrap; + } + } + + return policy.stretch; + }, + + + configureItem : function(item, pos) { + this.callParent(arguments); + + item.addCls(Ext.baseCSSPrefix + 'docked'); + item.addClsWithUI(this.getDockCls(item.dock)); + }, + + + getDockCls: function(dock) { + return 'docked-' + dock; + }, + + afterRemove : function(item) { + this.callParent(arguments); + if (this.itemCls) { + item.el.removeCls(this.itemCls + '-' + item.dock); + } + var dom = item.el.dom; + + if (!item.destroying && dom) { + dom.parentNode.removeChild(dom); + } + this.childrenChanged = true; + }, + + + borderCollapseMap: { + + }, + + + getBorderCollapseTable: function () { + var me = this, + map = me.borderCollapseMap, + owner = me.owner, + baseCls = owner.baseCls, + ui = owner.ui, + table; + + map = map[baseCls] || (map[baseCls] = {}); + table = map[ui]; + + if (!table) { + baseCls += '-' + ui + '-outer-border-'; + map[ui] = table = [ + 0, + baseCls + 'l', + baseCls + 'b', + baseCls + 'bl', + baseCls + 'r', + baseCls + 'rl', + baseCls + 'rb', + baseCls + 'rbl', + baseCls + 't', + baseCls + 'tl', + baseCls + 'tb', + baseCls + 'tbl', + baseCls + 'tr', + baseCls + 'trl', + baseCls + 'trb', + baseCls + 'trbl' + ]; + } + + return table; + } +}); + + + +Ext.define('Ext.menu.Manager', { + singleton: true, + + + + + alternateClassName: 'Ext.menu.MenuMgr', + + + + menuSelector: '.' + Ext.baseCSSPrefix + 'menu', + + menus: {}, + groups: {}, + attached: false, + lastShow: new Date(), + + init: function() { + var me = this; + + me.active = new Ext.util.MixedCollection(); + Ext.getDoc().addKeyListener(27, function() { + if (me.active.length > 0) { + me.hideAll(); + } + }, me); + }, + + + hideAll: function() { + var active = this.active, + menus, m, mLen; + + if (active && active.length > 0) { + menus = Ext.Array.slice(active.items); + mLen = menus.length; + + for (m = 0; m < mLen; m++) { + menus[m].hide(); + } + + return true; + } + return false; + }, + + onHide: function(m) { + var me = this, + active = me.active; + active.remove(m); + if (active.length < 1) { + Ext.getDoc().un('mousedown', me.onMouseDown, me); + me.attached = false; + } + }, + + onShow: function(m) { + var me = this, + active = me.active, + attached = me.attached; + + me.lastShow = new Date(); + active.add(m); + if (!attached) { + Ext.getDoc().on('mousedown', me.onMouseDown, me, { + + + buffer: Ext.isIE9m ? 10 : undefined + }); + me.attached = true; + } + m.toFront(); + }, + + onBeforeHide: function(m) { + if (m.activeChild) { + m.activeChild.hide(); + } + if (m.autoHideTimer) { + clearTimeout(m.autoHideTimer); + delete m.autoHideTimer; + } + }, + + onBeforeShow: function(m) { + var active = this.active, + parentMenu = m.parentMenu; + + active.remove(m); + if (!parentMenu && !m.allowOtherMenus) { + this.hideAll(); + } + else if (parentMenu && parentMenu.activeChild && m != parentMenu.activeChild) { + parentMenu.activeChild.hide(); + } + }, + + + onMouseDown: function(e) { + var me = this, + active = me.active, + activeMenuCount = active.length, + lastShow = me.lastShow, + i; + + if (Ext.Date.getElapsed(lastShow) > 50 && activeMenuCount) { + + + + if (Ext.isIE9m && !Ext.getDoc().contains(e.target)) { + return; + } + else { + + for (i = 0; i < activeMenuCount; i++) { + if (active.items[i].owns(e.target)) { + return; + } + } + } + me.hideAll(); + } + }, + + + register: function(menu) { + var me = this; + + if (!me.active) { + me.init(); + } + + if (menu.floating) { + me.menus[menu.id] = menu; + menu.on({ + beforehide: me.onBeforeHide, + hide: me.onHide, + beforeshow: me.onBeforeShow, + show: me.onShow, + scope: me + }); + } + }, + + + get: function(menu) { + var menus = this.menus; + + if (typeof menu == 'string') { + if (!menus) { + return null; + } + return menus[menu]; + } else if (menu.isMenu) { + return menu; + } else if (Ext.isArray(menu)) { + return new Ext.menu.Menu({items:menu}); + } else { + return Ext.ComponentManager.create(menu, 'menu'); + } + }, + + + unregister: function(menu) { + var me = this, + menus = me.menus, + active = me.active; + + delete menus[menu.id]; + active.remove(menu); + menu.un({ + beforehide: me.onBeforeHide, + hide: me.onHide, + beforeshow: me.onBeforeShow, + show: me.onShow, + scope: me + }); + }, + + + registerCheckable: function(menuItem) { + var groups = this.groups, + groupId = menuItem.group; + + if (groupId) { + if (!groups[groupId]) { + groups[groupId] = []; + } + + groups[groupId].push(menuItem); + } + }, + + + unregisterCheckable: function(menuItem) { + var groups = this.groups, + groupId = menuItem.group; + + if (groupId) { + Ext.Array.remove(groups[groupId], menuItem); + } + }, + + onCheckChange: function(menuItem, state) { + var groups = this.groups, + groupId = menuItem.group, + i = 0, + group, ln, curr; + + if (groupId && state) { + group = groups[groupId]; + ln = group.length; + for (; i < ln; i++) { + curr = group[i]; + if (curr != menuItem) { + curr.setChecked(false); + } + } + } + } +}); + + + +Ext.define('Ext.util.ClickRepeater', { + extend: Ext.util.Observable , + + + constructor : function(el, config){ + var me = this; + + me.el = Ext.get(el); + me.el.unselectable(); + + Ext.apply(me, config); + + me.callParent(); + + me.addEvents( + + "mousedown", + + "click", + + "mouseup" + ); + + if(!me.disabled){ + me.disabled = true; + me.enable(); + } + + + if(me.handler){ + me.on("click", me.handler, me.scope || me); + } + }, + + + + + + + + + interval : 20, + + + delay: 250, + + + preventDefault : true, + + + stopDefault : false, + + timer : 0, + + + enable: function(){ + if(this.disabled){ + this.el.on('mousedown', this.handleMouseDown, this); + + + if (Ext.isIE && !(Ext.isIE10p || (Ext.isStrict && Ext.isIE9))){ + this.el.on('dblclick', this.handleDblClick, this); + } + if(this.preventDefault || this.stopDefault){ + this.el.on('click', this.eventOptions, this); + } + } + this.disabled = false; + }, + + + disable: function( force){ + if(force || !this.disabled){ + clearTimeout(this.timer); + if(this.pressedCls){ + this.el.removeCls(this.pressedCls); + } + Ext.getDoc().un('mouseup', this.handleMouseUp, this); + this.el.removeAllListeners(); + } + this.disabled = true; + }, + + + setDisabled: function(disabled){ + this[disabled ? 'disable' : 'enable'](); + }, + + eventOptions: function(e){ + if(this.preventDefault){ + e.preventDefault(); + } + if(this.stopDefault){ + e.stopEvent(); + } + }, + + + destroy : function() { + this.disable(true); + Ext.destroy(this.el); + this.clearListeners(); + }, + + handleDblClick : function(e){ + clearTimeout(this.timer); + this.el.blur(); + + this.fireEvent("mousedown", this, e); + this.fireEvent("click", this, e); + }, + + + handleMouseDown : function(e){ + clearTimeout(this.timer); + this.el.blur(); + if(this.pressedCls){ + this.el.addCls(this.pressedCls); + } + this.mousedownTime = new Date(); + + Ext.getDoc().on("mouseup", this.handleMouseUp, this); + this.el.on("mouseout", this.handleMouseOut, this); + + this.fireEvent("mousedown", this, e); + this.fireEvent("click", this, e); + + + if (this.accelerate) { + this.delay = 400; + } + + + + e = new Ext.EventObjectImpl(e); + + this.timer = Ext.defer(this.click, this.delay || this.interval, this, [e]); + }, + + + click : function(e){ + this.fireEvent("click", this, e); + this.timer = Ext.defer(this.click, this.accelerate ? + this.easeOutExpo(Ext.Date.getElapsed(this.mousedownTime), + 400, + -390, + 12000) : + this.interval, this, [e]); + }, + + easeOutExpo : function (t, b, c, d) { + return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; + }, + + + handleMouseOut : function(){ + clearTimeout(this.timer); + if(this.pressedCls){ + this.el.removeCls(this.pressedCls); + } + this.el.on("mouseover", this.handleMouseReturn, this); + }, + + + handleMouseReturn : function(){ + this.el.un("mouseover", this.handleMouseReturn, this); + if(this.pressedCls){ + this.el.addCls(this.pressedCls); + } + this.click(); + }, + + + handleMouseUp : function(e){ + clearTimeout(this.timer); + this.el.un("mouseover", this.handleMouseReturn, this); + this.el.un("mouseout", this.handleMouseOut, this); + Ext.getDoc().un("mouseup", this.handleMouseUp, this); + if(this.pressedCls){ + this.el.removeCls(this.pressedCls); + } + this.fireEvent("mouseup", this, e); + } +}); + + + +Ext.define('Ext.layout.component.Auto', { + + + + alias: 'layout.autocomponent', + + extend: Ext.layout.component.Component , + + + + type: 'autocomponent', + + + setHeightInDom: false, + + + setWidthInDom: false, + + waitForOuterHeightInDom: false, + waitForOuterWidthInDom: false, + + beginLayoutCycle: function(ownerContext, firstCycle){ + var me = this, + lastWidthModel = me.lastWidthModel, + lastHeightModel = me.lastHeightModel, + el = me.owner.el; + + me.callParent(arguments); + + if (lastWidthModel && lastWidthModel.fixed && ownerContext.widthModel.shrinkWrap) { + el.setWidth(null); + } + + if (lastHeightModel && lastHeightModel.fixed && ownerContext.heightModel.shrinkWrap) { + el.setHeight(null); + } + }, + + calculate: function(ownerContext) { + var me = this, + measurement = me.measureAutoDimensions(ownerContext), + heightModel = ownerContext.heightModel, + widthModel = ownerContext.widthModel, + width, height; + + + + if (measurement.gotWidth) { + if (widthModel.shrinkWrap) { + me.publishOwnerWidth(ownerContext, measurement.contentWidth); + } else if (me.publishInnerWidth) { + me.publishInnerWidth(ownerContext, measurement.width); + } + } else if (!widthModel.auto && me.publishInnerWidth) { + width = me.waitForOuterWidthInDom ? ownerContext.getDomProp('width') + : ownerContext.getProp('width'); + if (width === undefined) { + me.done = false; + } else { + me.publishInnerWidth(ownerContext, width); + } + } + + if (measurement.gotHeight) { + if (heightModel.shrinkWrap) { + me.publishOwnerHeight(ownerContext, measurement.contentHeight); + } else if (me.publishInnerHeight) { + me.publishInnerHeight(ownerContext, measurement.height); + } + } else if (!heightModel.auto && me.publishInnerHeight) { + height = me.waitForOuterHeightInDom ? ownerContext.getDomProp('height') + : ownerContext.getProp('height'); + if (height === undefined) { + me.done = false; + } else { + me.publishInnerHeight(ownerContext, height); + } + } + + if (!measurement.gotAll) { + me.done = false; + } + }, + + calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) { + return contentHeight + ownerContext.getFrameInfo().height; + }, + + calculateOwnerWidthFromContentWidth: function (ownerContext, contentWidth) { + return contentWidth + ownerContext.getFrameInfo().width; + }, + + publishOwnerHeight: function (ownerContext, contentHeight) { + var me = this, + owner = me.owner, + height = me.calculateOwnerHeightFromContentHeight(ownerContext, contentHeight), + constrainedHeight, dirty, heightModel; + + if (isNaN(height)) { + me.done = false; + } else { + constrainedHeight = Ext.Number.constrain(height, owner.minHeight, owner.maxHeight); + + if (constrainedHeight == height) { + dirty = me.setHeightInDom; + } else { + heightModel = me.sizeModels[ + (constrainedHeight < height) ? 'constrainedMax' : 'constrainedMin']; + height = constrainedHeight; + + if (ownerContext.heightModel.calculatedFromShrinkWrap) { + + + + ownerContext.heightModel = heightModel; + } else { + ownerContext.invalidate({ heightModel: heightModel }); + } + } + + ownerContext.setHeight(height, dirty); + } + }, + + publishOwnerWidth: function (ownerContext, contentWidth) { + var me = this, + owner = me.owner, + width = me.calculateOwnerWidthFromContentWidth(ownerContext, contentWidth), + constrainedWidth, dirty, widthModel; + + if (isNaN(width)) { + me.done = false; + } else { + constrainedWidth = Ext.Number.constrain(width, owner.minWidth, owner.maxWidth); + + if (constrainedWidth == width) { + dirty = me.setWidthInDom; + } else { + widthModel = me.sizeModels[ + (constrainedWidth < width) ? 'constrainedMax' : 'constrainedMin']; + width = constrainedWidth; + + if (ownerContext.widthModel.calculatedFromShrinkWrap) { + + + + ownerContext.widthModel = widthModel; + } else { + ownerContext.invalidate({ widthModel: widthModel }); + } + } + + ownerContext.setWidth(width, dirty); + } + } +}); + + + +Ext.define('Ext.layout.component.Button', { + + + + alias: ['layout.button'], + + extend: Ext.layout.component.Auto , + + + + type: 'button', + + htmlRE: /<.*>/, + + beginLayout: function(ownerContext) { + var me = this, + owner = me.owner, + text = owner.text; + + me.callParent(arguments); + ownerContext.btnWrapContext = ownerContext.getEl('btnWrap'); + ownerContext.btnElContext = ownerContext.getEl('btnEl'); + ownerContext.btnInnerElContext = ownerContext.getEl('btnInnerEl'); + ownerContext.btnIconElContext = ownerContext.getEl('btnIconEl'); + + if (text && me.htmlRE.test(text)) { + ownerContext.isHtmlText = true; + + + + + owner.btnInnerEl.setStyle('line-height', 'normal'); + owner.btnInnerEl.setStyle('padding-top', ''); + } + }, + + beginLayoutCycle: function(ownerContext) { + var owner = this.owner, + lastWidthModel = this.lastWidthModel, + lastHeightModel = this.lastHeightModel, + btnInnerEl = owner.btnInnerEl, + table = owner.getFrameInfo().table; + + this.callParent(arguments); + + + + + + + if (lastWidthModel && !lastWidthModel.shrinkWrap && + ownerContext.widthModel.shrinkWrap) { + + owner.btnWrap.setStyle('height', ''); + owner.btnEl.setStyle('height', ''); + btnInnerEl.setStyle('line-height', ''); + if (table) { + btnInnerEl.setStyle('width', ''); + } + } + + if (table && lastHeightModel && !lastHeightModel.shrinkWrap && + ownerContext.heightModel.shrinkWrap) { + btnInnerEl.setStyle('height', ''); + } + }, + + calculate: function(ownerContext) { + var me = this, + owner = me.owner, + btnElContext = ownerContext.btnElContext, + btnInnerElContext = ownerContext.btnInnerElContext, + btnWrapContext = ownerContext.btnWrapContext, + mmax = Math.max, + ownerHeight, contentHeight, btnElHeight, innerElHeight; + + me.callParent(arguments); + + if (ownerContext.heightModel.shrinkWrap) { + + + + + + + + btnElHeight = owner.btnEl.getHeight(); + if (ownerContext.isHtmlText) { + me.centerInnerEl( + ownerContext, + btnElHeight + ); + me.ieCenterIcon(ownerContext, btnElHeight); + } + } else { + + + ownerHeight = ownerContext.getProp('height'); + + + if (ownerHeight) { + + + contentHeight = ownerHeight - ownerContext.getFrameInfo().height - ownerContext.getPaddingInfo().height; + + + + + btnElHeight = contentHeight; + if ((owner.menu || owner.split) && owner.arrowAlign === 'bottom') { + + + btnElHeight -= btnWrapContext.getPaddingInfo().bottom; + } + + + + + + innerElHeight = btnElHeight; + if ((owner.icon || owner.iconCls || owner.glyph) && + (owner.iconAlign === 'top' || owner.iconAlign === 'bottom')) { + innerElHeight -= btnInnerElContext.getPaddingInfo().height; + } + + btnWrapContext.setProp('height', mmax(0, contentHeight)); + btnElContext.setProp('height', mmax(0, btnElHeight)); + + if (ownerContext.isHtmlText) { + + + me.centerInnerEl(ownerContext, btnElHeight); + } else { + + + + btnInnerElContext.setProp('line-height', mmax(0, innerElHeight) + 'px'); + } + me.ieCenterIcon(ownerContext, btnElHeight); + } else if (ownerHeight !== 0) { + + me.done = false; + } + } + }, + + centerInnerEl: function(ownerContext, btnElHeight) { + var me = this, + btnInnerElContext = ownerContext.btnInnerElContext, + innerElHeight = me.owner.btnInnerEl.getHeight(); + + if (ownerContext.heightModel.shrinkWrap && (btnElHeight < innerElHeight)) { + + + + ownerContext.btnElContext.setHeight(innerElHeight); + } else if (btnElHeight > innerElHeight) { + + + + btnInnerElContext.setProp( + 'padding-top', + Math.round((btnElHeight - innerElHeight) / 2) + + + + + btnInnerElContext.getPaddingInfo().top + ); + } + }, + + ieCenterIcon: function(ownerContext, btnElHeight) { + var iconAlign = this.owner.iconAlign; + + if ((Ext.isIEQuirks || Ext.isIE6) && + (iconAlign === 'left' || iconAlign === 'right')) { + + + + + + + ownerContext.btnIconElContext.setHeight(btnElHeight); + } + }, + + publishInnerWidth: function(ownerContext, width) { + if (this.owner.getFrameInfo().table) { + + + + ownerContext.btnInnerElContext.setWidth( + width - + + ownerContext.getFrameInfo().width - ownerContext.getPaddingInfo().width - + + + ownerContext.btnWrapContext.getPaddingInfo().width + ); + } + } + +}); + + + +Ext.define('Ext.util.TextMetrics', { + statics: { + shared: null, + + measure: function(el, text, fixedWidth){ + var me = this, + shared = me.shared; + + if(!shared){ + shared = me.shared = new me(el, fixedWidth); + } + shared.bind(el); + shared.setFixedWidth(fixedWidth || 'auto'); + return shared.getSize(text); + }, + + + destroy: function(){ + var me = this; + Ext.destroy(me.shared); + me.shared = null; + } + }, + + + constructor: function(bindTo, fixedWidth){ + var me = this, + measure = Ext.getBody().createChild({ + + 'data-sticky': true, + role: 'presentation', + cls: Ext.baseCSSPrefix + 'textmetrics' + }); + + me.measure = measure; + if (bindTo) { + me.bind(bindTo); + } + + measure.position('absolute'); + measure.setLocalXY(-1000, -1000); + measure.hide(); + + if (fixedWidth) { + measure.setWidth(fixedWidth); + } + }, + + + getSize: function(text){ + var measure = this.measure, + size; + + measure.update(text); + size = measure.getSize(); + measure.update(''); + return size; + }, + + + bind: function(el){ + var me = this; + + me.el = Ext.get(el); + me.measure.setStyle( + me.el.getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing', 'word-break') + ); + }, + + + setFixedWidth : function(width){ + this.measure.setWidth(width); + }, + + + getWidth : function(text){ + this.measure.dom.style.width = 'auto'; + return this.getSize(text).width; + }, + + + getHeight : function(text){ + return this.getSize(text).height; + }, + + + destroy: function(){ + var me = this; + me.measure.remove(); + delete me.el; + delete me.measure; + } +}, function(){ + Ext.Element.addMethods({ + + getTextWidth : function(text, min, max){ + return Ext.Number.constrain(Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width, min || 0, max || 1000000); + } + }); +}); + + + +Ext.define('Ext.button.Button', { + + + alias: 'widget.button', + extend: Ext.Component , + + + + + + + + + + + mixins: { + queryable: Ext.Queryable + }, + + alternateClassName: 'Ext.Button', + + + + isButton: true, + componentLayout: 'button', + + + hidden: false, + + + disabled: false, + + + pressed: false, + + + + + + + + + + + + + + + + + + + + + + + tabIndex: 0, + + + + + enableToggle: false, + + + + + + + menuAlign: 'tl-bl?', + + + showEmptyMenu: false, + + + textAlign: 'center', + + + + + + + + + clickEvent: 'click', + + + preventDefault: true, + + + handleMouseEvents: true, + + + tooltipType: 'qtip', + + + baseCls: Ext.baseCSSPrefix + 'btn', + + + pressedCls: 'pressed', + + + overCls: 'over', + + + focusCls: 'focus', + + + menuActiveCls: 'menu-active', + + + + + hrefTarget: '_blank', + + + destroyMenu: true, + + + + + + ariaRole: 'button', + + childEls: [ + 'btnEl', 'btnWrap', 'btnInnerEl', 'btnIconEl' + ], + + + + + renderTpl: [ + ' {splitCls}', + '{childElCls}" unselectable="on">', + '', + '', + '{text}', + '', + 'background-image:url({iconUrl});', + 'font-family:{glyphFontFamily};">', + '&#{glyph}; ', + '', + '', + '', + + '', + '', + ' title="{closeText}" aria-label="{closeText}"', + '', + '>', + '
', + '' + ], + + + scale: 'small', + + + allowedScales: ['small', 'medium', 'large'], + + + + + iconAlign: 'left', + + + 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 me = this; + + + me.addCls(Ext.baseCSSPrefix + 'unselectable'); + + me.callParent(arguments); + + me.addEvents( + + 'click', + + + 'toggle', + + + 'mouseover', + + + 'mouseout', + + + 'menushow', + + + 'menuhide', + + + 'menutriggerover', + + + 'menutriggerout', + + + 'textchange', + + + 'iconchange', + + + 'glyphchange' + ); + + if (me.menu) { + + me.split = true; + me.setMenu(me.menu, false); + } + + + if (me.url) { + me.href = me.url; + } + + + if (me.href && !me.hasOwnProperty('preventDefault')) { + me.preventDefault = false; + } + + if (Ext.isString(me.toggleGroup) && me.toggleGroup !== '') { + me.enableToggle = true; + } + + if (me.html && !me.text) { + me.text = me.html; + delete me.html; + } + + me.glyphCls = me.baseCls + '-glyph'; + }, + + getActionEl: function() { + return this.el; + }, + + getFocusEl: function() { + return this.el; + }, + + + setComponentCls: function() { + var me = this, + cls = me.getComponentCls(); + + if (!Ext.isEmpty(me.oldCls)) { + me.removeClsWithUI(me.oldCls); + me.removeClsWithUI(me.pressedCls); + } + + me.oldCls = cls; + me.addClsWithUI(cls); + }, + + getComponentCls: function() { + var me = this, + cls; + + + if (me.iconCls || me.icon || me.glyph) { + cls = [me.text ? 'icon-text-' + me.iconAlign : 'icon']; + } else if (me.text) { + cls = ['noicon']; + } else { + cls = []; + } + + if (me.pressed) { + cls[cls.length] = me.pressedCls; + } + return cls; + }, + + getElConfig: function() { + var me = this, + config = me.callParent(), + href = me.getHref(), + hrefTarget = me.hrefTarget; + + if (config.tag === 'a') { + if (!me.disabled) { + config.tabIndex = me.tabIndex; + } + if (href) { + config.href = href; + if (hrefTarget) { + config.target = hrefTarget; + } + } + } + return config; + }, + + beforeRender: function () { + var me = this; + + me.callParent(); + + + me.oldCls = me.getComponentCls(); + me.addClsWithUI(me.oldCls); + + + Ext.applyIf(me.renderData, me.getTemplateArgs()); + }, + + + setMenu: function (menu, destroyMenu) { + var me = this, + oldMenu = me.menu; + + if (oldMenu && destroyMenu !== false && me.destroyMenu) { + oldMenu.destroy(); + } + + if (oldMenu) { + oldMenu.ownerCmp = oldMenu.ownerButton = null; + } + + if (menu) { + + menu = Ext.menu.Manager.get(menu); + + + + menu.ownerCmp = menu.ownerButton = me; + + me.mon(menu, { + scope: me, + show: me.onMenuShow, + hide: me.onMenuHide + }); + + + + if (!oldMenu) { + me.split = true; + if (me.rendered) { + me.btnWrap.addCls(me.getSplitCls()); + me.updateLayout(); + } + } + + me.menu = menu; + } else { + if (me.rendered) { + me.btnWrap.removeCls(me.getSplitCls()); + me.updateLayout(); + } + me.split = false; + me.menu = null; + } + }, + + + onRender: function() { + var me = this, + addOnclick, + btn, + btnListeners; + + me.doc = Ext.getDoc(); + me.callParent(arguments); + + + btn = me.el; + + if (me.tooltip) { + me.setTooltip(me.tooltip, true); + } + + + if (me.handleMouseEvents) { + btnListeners = { + scope: me, + mouseover: me.onMouseOver, + mouseout: me.onMouseOut, + mousedown: me.onMouseDown + }; + if (me.split) { + btnListeners.mousemove = me.onMouseMove; + } + } else { + btnListeners = { + scope: me + }; + } + + + if (me.menu) { + me.keyMap = new Ext.util.KeyMap({ + target: me.el, + key: Ext.EventObject.DOWN, + handler: me.onDownKey, + scope: me + }); + } + + + if (me.repeat) { + me.mon(new Ext.util.ClickRepeater(btn, Ext.isObject(me.repeat) ? me.repeat: {}), 'click', me.onRepeatClick, me); + } else { + + + if (btnListeners[me.clickEvent]) { + addOnclick = true; + } else { + btnListeners[me.clickEvent] = me.onClick; + } + } + + + me.mon(btn, btnListeners); + + if (me.hasFrameTable()) { + me.mon(me.frameTable, 'click', me.frameTableListener, me); + } + + + if (addOnclick) { + me.mon(btn, me.clickEvent, me.onClick, me); + } + + Ext.button.Manager.register(me); + }, + + + getTemplateArgs: function() { + var me = this, + glyph = me.glyph, + glyphFontFamily = Ext._glyphFontFamily, + glyphParts; + + if (typeof glyph === 'string') { + glyphParts = glyph.split('@'); + glyph = glyphParts[0]; + glyphFontFamily = glyphParts[1]; + } + + return { + innerCls : me.getInnerCls(), + splitCls : me.getSplitCls(), + iconUrl : me.icon, + iconCls : me.iconCls, + glyph: glyph, + glyphCls: glyph ? me.glyphCls : '', + glyphFontFamily: glyphFontFamily, + text : me.text || ' ', + closeText: me.closeText + }; + }, + + + setHref: function(href) { + this.href = href; + this.el.dom.href = this.getHref(); + }, + + + getHref: function() { + var me = this, + href = me.href; + + return href ? Ext.urlAppend(href, Ext.Object.toQueryString(Ext.apply({}, me.params, me.baseParams))) : false; + }, + + + setParams: function(params) { + this.params = params; + this.el.dom.href = this.getHref(); + }, + + getSplitCls: function() { + var me = this; + return me.split ? (me.baseCls + '-' + me.arrowCls) + ' ' + (me.baseCls + '-' + me.arrowCls + '-' + me.arrowAlign) : ''; + }, + + getInnerCls: function() { + return this.textAlign ? this.baseCls + '-inner-' + this.textAlign : ''; + }, + + + setIcon: function(icon) { + icon = icon || ''; + var me = this, + btnIconEl = me.btnIconEl, + oldIcon = me.icon || ''; + + me.icon = icon; + if (icon != oldIcon) { + if (btnIconEl) { + btnIconEl.setStyle('background-image', icon ? 'url(' + icon + ')': ''); + me.setComponentCls(); + if (me.didIconStateChange(oldIcon, icon)) { + me.updateLayout(); + } + } + me.fireEvent('iconchange', me, oldIcon, icon); + } + return me; + }, + + + setIconCls: function(cls) { + cls = cls || ''; + var me = this, + btnIconEl = me.btnIconEl, + oldCls = me.iconCls || ''; + + me.iconCls = cls; + if (oldCls != cls) { + if (btnIconEl) { + + btnIconEl.removeCls(oldCls); + btnIconEl.addCls(cls); + me.setComponentCls(); + if (me.didIconStateChange(oldCls, cls)) { + me.updateLayout(); + } + } + me.fireEvent('iconchange', me, oldCls, cls); + } + return me; + }, + + + setGlyph: function(glyph) { + glyph = glyph || 0; + var me = this, + btnIconEl = me.btnIconEl, + oldGlyph = me.glyph, + fontFamily, glyphParts; + + me.glyph = glyph; + + if (btnIconEl) { + if (typeof glyph === 'string') { + glyphParts = glyph.split('@'); + glyph = glyphParts[0]; + fontFamily = glyphParts[1] || Ext._glyphFontFamily; + } + + if (!glyph) { + btnIconEl.dom.innerHTML = ''; + } else if (oldGlyph != glyph) { + btnIconEl.dom.innerHTML = '&#' + glyph + ';'; + } + + if (fontFamily) { + btnIconEl.setStyle('font-family', fontFamily); + } + } + + me.fireEvent('glyphchange', me, me.glyph, oldGlyph); + + return me; + }, + + + setTooltip: function(tooltip, initial) { + var me = this; + + if (me.rendered) { + if (!initial || !tooltip) { + me.clearTip(); + } + if (tooltip) { + if (Ext.quickTipsActive && Ext.isObject(tooltip)) { + Ext.tip.QuickTipManager.register(Ext.apply({ + target: me.el.id + }, + tooltip)); + me.tooltip = tooltip; + } else { + me.el.dom.setAttribute(me.getTipAttr(), tooltip); + } + } + } else { + me.tooltip = tooltip; + } + return me; + }, + + + setTextAlign: function(align) { + var me = this, + btnEl = me.btnEl; + + if (btnEl) { + btnEl.removeCls(me.baseCls + '-inner-' + me.textAlign); + btnEl.addCls(me.baseCls + '-inner-' + align); + } + me.textAlign = align; + return me; + }, + + getTipAttr: function(){ + return this.tooltipType == 'qtip' ? 'data-qtip' : 'title'; + }, + + + getRefItems: function(deep){ + var menu = this.menu, + items; + + if (menu) { + items = menu.getRefItems(deep); + items.unshift(menu); + } + return items || []; + }, + + + clearTip: function() { + var me = this, + el = me.el; + + if (Ext.quickTipsActive && Ext.isObject(me.tooltip)) { + Ext.tip.QuickTipManager.unregister(el); + } else { + el.dom.removeAttribute(me.getTipAttr()); + } + }, + + + beforeDestroy: function() { + var me = this; + if (me.rendered) { + me.clearTip(); + } + if (me.menu && me.destroyMenu) { + me.menu.destroy(); + } + Ext.destroy(me.btnInnerEl, me.repeater); + me.callParent(); + }, + + + onDestroy: function() { + var me = this; + if (me.rendered) { + me.doc.un('mouseover', me.monitorMouseOver, me); + delete me.doc; + + Ext.destroy(me.keyMap); + delete me.keyMap; + } + Ext.button.Manager.unregister(me); + me.callParent(); + }, + + + setHandler: function(handler, scope) { + this.handler = handler; + this.scope = scope; + return this; + }, + + + setText: function(text) { + text = text || ''; + var me = this, + oldText = me.text || ''; + + if (text != oldText) { + me.text = text; + if (me.rendered) { + me.btnInnerEl.update(text || ' '); + me.setComponentCls(); + if (Ext.isStrict && Ext.isIE8) { + + me.el.repaint(); + } + me.updateLayout(); + } + me.fireEvent('textchange', me, oldText, text); + } + return me; + }, + + + didIconStateChange: function(old, current) { + var currentEmpty = Ext.isEmpty(current); + return Ext.isEmpty(old) ? !currentEmpty : currentEmpty; + }, + + + getText: function() { + return this.text; + }, + + + toggle: function(state, suppressEvent) { + var me = this; + state = state === undefined ? !me.pressed: !!state; + if (state !== me.pressed) { + if (me.rendered) { + me[state ? 'addClsWithUI': 'removeClsWithUI'](me.pressedCls); + } + me.pressed = state; + if (!suppressEvent) { + me.fireEvent('toggle', me, state); + Ext.callback(me.toggleHandler, me.scope || me, [me, state]); + } + } + return me; + }, + + maybeShowMenu: function(){ + var me = this; + if (me.menu && !me.hasVisibleMenu() && !me.ignoreNextClick) { + me.showMenu(true); + } + }, + + + showMenu: function( fromEvent) { + var me = this, + menu = me.menu; + + if (me.rendered) { + if (me.tooltip && Ext.quickTipsActive && me.getTipAttr() != 'title') { + Ext.tip.QuickTipManager.getQuickTip().cancelShow(me.el); + } + if (menu.isVisible()) { + menu.hide(); + } + + if (!fromEvent || me.showEmptyMenu || menu.items.getCount() > 0) { + menu.showBy(me.el, me.menuAlign); + } + } + return me; + }, + + + hideMenu: function() { + if (this.hasVisibleMenu()) { + this.menu.hide(); + } + return this; + }, + + + hasVisibleMenu: function() { + var menu = this.menu; + return menu && menu.rendered && menu.isVisible(); + }, + + + onRepeatClick: function(repeat, e) { + this.onClick(e); + }, + + + onClick: function(e) { + var me = this; + me.doPreventDefault(e); + + + + if (e.type !== 'keydown' && e.button !== 0) { + return; + } + if (!me.disabled) { + me.doToggle(); + me.maybeShowMenu(); + me.fireHandler(e); + } + }, + + doPreventDefault: function(e) { + if (this.preventDefault || (this.disabled && this.getHref()) && e) { + e.preventDefault(); + } + }, + + fireHandler: function(e) { + var me = this, + handler = me.handler; + + if (me.fireEvent('click', me, e) !== false) { + if (handler) { + handler.call(me.scope || me, me, e); + } + } + }, + + doToggle: function() { + var me = this; + if (me.enableToggle && (me.allowDepress !== false || !me.pressed)) { + me.toggle(); + } + }, + + + onMouseOver: function(e) { + var me = this; + if (!me.disabled && !e.within(me.el, true, true)) { + me.onMouseEnter(e); + } + }, + + + onMouseOut: function(e) { + var me = this; + if (!e.within(me.el, true, true)) { + if (me.overMenuTrigger) { + me.onMenuTriggerOut(e); + } + me.onMouseLeave(e); + } + }, + + + onMouseMove: function(e) { + var me = this, + el = me.el, + over = me.overMenuTrigger, + overPosition, triggerRegion; + + if (me.split) { + overPosition = (me.arrowAlign === 'right') ? + e.getX() - me.getX() : e.getY() - el.getY(); + triggerRegion = me.getTriggerRegion(); + + if (overPosition > triggerRegion.begin && overPosition < triggerRegion.end) { + if (!over) { + me.onMenuTriggerOver(e); + } + } else { + if (over) { + me.onMenuTriggerOut(e); + } + } + } + }, + + + getTriggerRegion: function() { + var me = this, + region = me._triggerRegion, + triggerSize = me.getTriggerSize(), + btnSize = me.arrowAlign === 'right' ? me.getWidth() : me.getHeight(); + + region.begin = btnSize - triggerSize; + region.end = btnSize; + return region; + }, + + + getTriggerSize: function() { + var me = this, + size = me.triggerSize, + side, sideFirstLetter; + + if (size == null) { + side = me.arrowAlign; + sideFirstLetter = side.charAt(0); + size = me.triggerSize = me.el.getFrameWidth(sideFirstLetter) + me.getBtnWrapFrameWidth(sideFirstLetter) + if (me.frameSize) { + size = me.triggerSize += me.frameSize[side]; + } + } + return size; + }, + + + getBtnWrapFrameWidth: function(side) { + return this.btnWrap.getFrameWidth(side); + }, + + addOverCls: function() { + if (!this.disabled) { + this.addClsWithUI(this.overCls); + } + }, + removeOverCls: function() { + this.removeClsWithUI(this.overCls); + }, + + + onMouseEnter: function(e) { + + this.fireEvent('mouseover', this, e); + }, + + + onMouseLeave: function(e) { + + this.fireEvent('mouseout', this, e); + }, + + + onMenuTriggerOver: function(e) { + var me = this, + arrowTip = me.arrowTooltip; + + me.overMenuTrigger = true; + + + if (me.split && arrowTip) { + me.btnWrap.dom.setAttribute(me.getTipAttr(), arrowTip); + } + me.fireEvent('menutriggerover', me, me.menu, e); + }, + + + onMenuTriggerOut: function(e) { + var me = this; + delete me.overMenuTrigger; + + if (me.split && me.arrowTooltip) { + me.btnWrap.dom.setAttribute(me.getTipAttr(), ''); + } + me.fireEvent('menutriggerout', me, me.menu, e); + }, + + enable: function(silent) { + var me = this; + + me.callParent(arguments); + + me.removeClsWithUI('disabled'); + if (me.rendered) { + me.el.dom.setAttribute('tabIndex', me.tabIndex); + } + + return me; + }, + + disable: function(silent) { + var me = this; + + me.callParent(arguments); + + me.addClsWithUI('disabled'); + me.removeClsWithUI(me.overCls); + if (me.rendered) { + me.el.dom.removeAttribute('tabIndex'); + } + + + + if (me.btnInnerEl && Ext.isIE7m) { + me.btnInnerEl.repaint(); + } + + return me; + }, + + + setScale: function(scale) { + var me = this, + ui = me.ui.replace('-' + me.scale, ''); + + + if (!Ext.Array.contains(me.allowedScales, scale)) { + throw('#setScale: scale must be an allowed scale (' + me.allowedScales.join(', ') + ')'); + } + + me.scale = scale; + me.setUI(ui); + }, + + setUI: function(ui) { + var me = this; + + + if (me.scale && !ui.match(me.scale)) { + ui = ui + '-' + me.scale; + } + + me.callParent([ui]); + + + + }, + + + + onMouseDown: function(e) { + var me = this; + + if (Ext.isIE) { + + + me.getFocusEl().focus(); + } + + if (!me.disabled && e.button === 0) { + Ext.button.Manager.onButtonMousedown(me, e); + me.addClsWithUI(me.pressedCls); + } + }, + + onMouseUp: function(e) { + var me = this; + + + if (!me.isDestroyed && e.button === 0) { + if (!me.pressed) { + me.removeClsWithUI(me.pressedCls); + } + } + }, + + onMenuShow: function(e) { + var me = this; + me.ignoreNextClick = 0; + me.addClsWithUI(me.menuActiveCls); + me.fireEvent('menushow', me, me.menu); + }, + + + onMenuHide: function(e) { + var me = this; + me.removeClsWithUI(me.menuActiveCls); + me.ignoreNextClick = Ext.defer(me.restoreClick, 250, me); + me.fireEvent('menuhide', me, me.menu); + me.focus(); + }, + + + restoreClick: function() { + this.ignoreNextClick = 0; + }, + + + onDownKey: function(k, e) { + var me = this; + + if (me.menu && !me.disabled) { + me.showMenu(); + e.stopEvent(); + return false; + } + } +}); + + + +Ext.define('Ext.layout.container.boxOverflow.Menu', { + + + + extend: Ext.layout.container.boxOverflow.None , + + alternateClassName: 'Ext.layout.boxOverflow.Menu', + + + + + + + noItemsMenuText : '', + + constructor: function(layout) { + var me = this; + + me.callParent(arguments); + + me.triggerButtonCls = me.triggerButtonCls || Ext.baseCSSPrefix + 'box-menu-after'; + + me.menuItems = []; + }, + + beginLayout: function (ownerContext) { + this.callParent(arguments); + + + + this.clearOverflow(ownerContext); + }, + + beginLayoutCycle: function (ownerContext, firstCycle) { + this.callParent(arguments); + + if (!firstCycle) { + + + this.clearOverflow(ownerContext); + + this.layout.cacheChildItems(ownerContext); + } + }, + + onRemove: function(comp){ + Ext.Array.remove(this.menuItems, comp); + }, + + + getSuffixConfig: function() { + var me = this, + layout = me.layout, + owner = layout.owner, + oid = owner.id; + + + me.menu = new Ext.menu.Menu({ + listeners: { + scope: me, + beforeshow: me.beforeMenuShow + } + }); + + + me.menuTrigger = new Ext.button.Button({ + id: oid + '-menu-trigger', + cls: Ext.layout.container.Box.prototype.innerCls + ' ' + me.triggerButtonCls + ' ' + Ext.baseCSSPrefix + 'toolbar-item', + plain: owner.usePlainButtons, + ownerCt: owner, + ownerLayout: layout, + iconCls: Ext.baseCSSPrefix + me.getOwnerType(owner) + '-more-icon', + ui: owner instanceof Ext.toolbar.Toolbar ? 'default-toolbar' : 'default', + menu: me.menu, + + showEmptyMenu: true, + getSplitCls: function() { return '';} + }); + + return me.menuTrigger.getRenderTree(); + }, + + getOverflowCls: function() { + return Ext.baseCSSPrefix + this.layout.direction + '-box-overflow-body'; + }, + + handleOverflow: function(ownerContext) { + var me = this, + layout = me.layout, + names = layout.names, + plan = ownerContext.state.boxPlan, + posArgs = [null, null]; + + me.showTrigger(ownerContext); + + + + if (me.layout.direction !== 'vertical') { + posArgs[names.heightIndex] = (plan.maxSize - me.menuTrigger[names.getHeight]()) / 2; + me.menuTrigger.setPosition.apply(me.menuTrigger, posArgs); + } + + return { + reservedSpace: me.triggerTotalWidth + }; + }, + + + captureChildElements: function() { + var me = this, + menuTrigger = me.menuTrigger, + names = me.layout.names; + + + if (menuTrigger.rendering) { + menuTrigger.finishRender(); + me.triggerTotalWidth = menuTrigger[names.getWidth]() + menuTrigger.el.getMargin(names.parallelMargins); + } + }, + + _asLayoutRoot: { isRoot: true }, + + + clearOverflow: function(ownerContext) { + var me = this, + items = me.menuItems, + item, + i = 0, + length = items.length, + owner = me.layout.owner, + asLayoutRoot = me._asLayoutRoot; + + owner.suspendLayouts(); + me.captureChildElements(); + me.hideTrigger(); + owner.resumeLayouts(); + + for (; i < length; i++) { + item = items[i]; + + + + item.suspendLayouts(); + item.show(); + item.resumeLayouts(asLayoutRoot); + } + + items.length = 0; + }, + + + showTrigger: function(ownerContext) { + var me = this, + layout = me.layout, + owner = layout.owner, + names = layout.names, + startProp = names.x, + sizeProp = names.width, + plan = ownerContext.state.boxPlan, + available = plan.targetSize[sizeProp], + childItems = ownerContext.childItems, + len = childItems.length, + menuTrigger = me.menuTrigger, + childContext, + comp, i, props; + + + + menuTrigger.suspendLayouts(); + menuTrigger.show(); + menuTrigger.resumeLayouts(me._asLayoutRoot); + + available -= me.triggerTotalWidth; + + owner.suspendLayouts(); + + + + me.menuItems.length = 0; + for (i = 0; i < len; i++) { + childContext = childItems[i]; + props = childContext.props; + if (props[startProp] + props[sizeProp] > available) { + comp = childContext.target; + me.menuItems.push(comp); + comp.hide(); + } + } + + owner.resumeLayouts(); + }, + + + hideTrigger: function() { + var menuTrigger = this.menuTrigger; + if (menuTrigger) { + menuTrigger.hide(); + } + }, + + + beforeMenuShow: function(menu) { + var me = this, + items = me.menuItems, + i = 0, + len = items.length, + item, + prev, + needsSep = function(group, prev){ + return group.isXType('buttongroup') && !(prev instanceof Ext.toolbar.Separator); + }; + + menu.suspendLayouts(); + me.clearMenu(); + menu.removeAll(); + + for (; i < len; i++) { + item = items[i]; + + + if (!i && (item instanceof Ext.toolbar.Separator)) { + continue; + } + if (prev && (needsSep(item, prev) || needsSep(prev, item))) { + menu.add('-'); + } + + me.addComponentToMenu(menu, item); + prev = item; + } + + + if (menu.items.length < 1) { + menu.add(me.noItemsMenuText); + } + menu.resumeLayouts(); + }, + + + createMenuConfig: function (component, hideOnClick) { + var config = Ext.apply({}, component.initialConfig), + group = component.toggleGroup; + + Ext.copyTo(config, component, [ + 'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu', 'tabIndex' + ]); + + Ext.applyIf(config, { + text: component.overflowText || component.text, + hideOnClick: hideOnClick, + destroyMenu: false, + listeners: {} + }); + + + if (component.isFormField) { + config.value = component.getValue(); + + + + + + + config.listeners.change = function(c, newVal, oldVal) { + component.setValue(newVal); + }; + } + + + else if (group || component.enableToggle) { + Ext.apply(config, { + hideOnClick: false, + group: group, + checked: component.pressed, + handler: function (item, e) { + component.onClick(e); + } + }); + } + + + if (component.isButton && !component.changeListenersAdded) { + component.on({ + textchange: this.onButtonAttrChange, + iconchange: this.onButtonAttrChange, + toggle: this.onButtonToggle + }); + component.changeListenersAdded = true; + } + + + + + delete config.margin; + delete config.ownerCt; + delete config.xtype; + delete config.id; + delete config.itemId; + return config; + }, + + onButtonAttrChange: function(btn) { + var clone = btn.overflowClone; + clone.suspendLayouts(); + clone.setText(btn.text); + clone.setIcon(btn.icon); + clone.setIconCls(btn.iconCls); + clone.resumeLayouts(true); + }, + + onButtonToggle: function(btn, state) { + + if (btn.overflowClone.checked !== state) { + btn.overflowClone.setChecked(state); + } + }, + + + addComponentToMenu : function(menu, component) { + var me = this, + i, items, iLen; + + + if (component instanceof Ext.toolbar.Fill) { + return; + } + + else if (component instanceof Ext.toolbar.Separator) { + menu.add('-'); + } + + else if (component.isComponent) { + if (component.isXType('splitbutton')) { + component.overflowClone = menu.add(me.createMenuConfig(component, true)); + + } else if (component.isXType('button')) { + component.overflowClone = menu.add(me.createMenuConfig(component, !component.menu)); + + } else if (component.isXType('buttongroup')) { + items = component.items.items; + iLen = items.length; + + for (i = 0; i < iLen; i++) { + me.addComponentToMenu(menu, items[i]); + } + } else { + component.overflowClone = menu.add(Ext.create(Ext.getClassName(component), me.createMenuConfig(component))); + } + } + }, + + + clearMenu : function() { + var menu = this.menu, + items, i, iLen, item; + + if (menu && menu.items) { + items = menu.items.items; + iLen = items.length; + + for (i = 0; i < iLen; i++) { + item = items[i]; + if (item.setMenu) { + item.setMenu(null); + } + } + } + }, + + + destroy: function() { + var trigger = this.menuTrigger; + + if (trigger && !this.layout.owner.items.contains(trigger)) { + + + delete trigger.ownerCt; + } + Ext.destroy(this.menu, trigger); + } +}); + + + +Ext.define('Ext.layout.container.boxOverflow.Scroller', { + + + + extend: Ext.layout.container.boxOverflow.None , + + alternateClassName: 'Ext.layout.boxOverflow.Scroller', + mixins: { + observable: Ext.util.Observable + }, + + + + + animateScroll: false, + + + scrollIncrement: 20, + + + wheelIncrement: 10, + + + scrollRepeatInterval: 60, + + + scrollDuration: 400, + + + + + + + scrollerCls: Ext.baseCSSPrefix + 'box-scroller', + + + + + + constructor: function(layout, config) { + var me = this; + + me.layout = layout; + Ext.apply(me, config || {}); + + + me.mixins.observable.constructor.call(me); + + me.addEvents( + + 'scroll' + ); + me.scrollPosition = 0; + me.scrollSize = 0; + }, + + getPrefixConfig: function() { + var me = this, + layout = me.layout, + owner = layout.owner, + cls; + + me.initCSSClasses(); + cls = Ext.layout.container.Box.prototype.innerCls + ' ' + me.beforeCtCls; + if (owner.plain) { + + cls += ' ' + me.scrollerCls + '-plain'; + } + return { + role: 'presentation', + cls: cls, + cn : { + role: 'presentation', + id : owner.id + layout.names.beforeScrollerSuffix, + cls: me.scrollerCls + ' ' + me.beforeScrollerCls, + style: 'display:none' + } + }; + }, + + getSuffixConfig: function() { + var me = this, + layout = me.layout, + owner = layout.owner, + cls = Ext.layout.container.Box.prototype.innerCls + ' ' + me.afterCtCls; + + if (owner.plain) { + + cls += ' ' + me.scrollerCls + '-plain'; + } + return { + role: 'presentation', + cls: cls, + cn : { + role: 'presentation', + id : owner.id + layout.names.afterScrollerSuffix, + cls: me.scrollerCls + ' ' + me.afterScrollerCls, + style: 'display:none' + } + }; + }, + + getOverflowCls: function() { + return Ext.baseCSSPrefix + this.layout.direction + '-box-overflow-body'; + }, + + initCSSClasses: function() { + var me = this, + prefix = Ext.baseCSSPrefix, + layout = me.layout, + names = layout.names, + beforeXName = names.beforeX, + afterXName = names.afterX, + type = me.getOwnerType(layout.owner); + + me.beforeCtCls = me.beforeCtCls || prefix + 'box-scroller-' + beforeXName; + me.afterCtCls = me.afterCtCls || prefix + 'box-scroller-' + afterXName; + + me.beforeScrollerCls = me.beforeScrollerCls || prefix + type + '-scroll-' + beforeXName; + me.afterScrollerCls = me.afterScrollerCls || prefix + type + '-scroll-' + afterXName; + }, + + beginLayout: function (ownerContext) { + var layout = this.layout; + + ownerContext.innerCtScrollPos = this.getScrollPosition(); + + this.callParent(arguments); + }, + + completeLayout: function(ownerContext) { + var me = this, + plan = ownerContext.state.boxPlan, + names = me.layout.names, + last; + + + if (plan && plan.tooNarrow) { + last = ownerContext.childItems[ownerContext.childItems.length - 1]; + + + me.scrollSize = last.props[names.x] + last.props[names.width]; + me.updateScrollButtons(); + } + this.callParent(arguments); + }, + + finishedLayout: function(ownerContext) { + var me = this, + layout = me.layout, + scrollPos = Math.min(me.getMaxScrollPosition(), ownerContext.innerCtScrollPos); + + layout.innerCt[layout.names.setScrollLeft](scrollPos); + }, + + handleOverflow: function(ownerContext) { + var me = this, + methodName = me.layout.names.getWidth; + + me.showScrollers(); + return { + reservedSpace: me.beforeCt[methodName]() + me.afterCt[methodName]() + }; + }, + + + captureChildElements: function() { + var me = this, + el = me.layout.owner.el, + before, after, hoverCls, pressedSuffix, pressedCls, hoverSuffix; + + + if (!me.beforeCt) { + hoverSuffix = '-hover'; + pressedSuffix = '-pressed'; + hoverCls = me.scrollerCls + hoverSuffix; + pressedCls = me.scrollerCls + pressedSuffix; + before = me.beforeScroller = el.getById(me.layout.owner.id + '-before-scroller'); + after = me.afterScroller = el.getById(me.layout.owner.id + '-after-scroller'); + me.beforeCt = before.up(''); + me.afterCt = after.up(''); + me.createWheelListener(); + + before.addClsOnOver(hoverCls); + before.addClsOnOver(me.beforeScrollerCls + hoverSuffix); + before.addClsOnClick(pressedCls); + before.addClsOnClick(me.beforeScrollerCls + pressedSuffix); + after.addClsOnOver(hoverCls); + after.addClsOnOver(me.afterScrollerCls + hoverSuffix); + after.addClsOnClick(pressedCls); + after.addClsOnClick(me.afterScrollerCls + pressedSuffix); + + before.setVisibilityMode(Ext.Element.DISPLAY); + after.setVisibilityMode(Ext.Element.DISPLAY); + + me.beforeRepeater = new Ext.util.ClickRepeater(before, { + interval: me.scrollRepeatInterval, + handler : me.scrollLeft, + scope : me + }); + + me.afterRepeater = new Ext.util.ClickRepeater(after, { + interval: me.scrollRepeatInterval, + handler : me.scrollRight, + scope : me + }); + } + }, + + + createWheelListener: function() { + var me = this; + me.layout.innerCt.on({ + mousewheel: function(e) { + me.scrollBy(me.getWheelDelta(e) * me.wheelIncrement * -1, false); + }, + stopEvent: true + }); + }, + + getWheelDelta: function (e) { + return e.getWheelDelta(); + }, + + + clearOverflow: function () { + this.hideScrollers(); + }, + + + showScrollers: function() { + var me = this; + + me.captureChildElements(); + me.beforeScroller.show(); + me.afterScroller.show(); + me.layout.owner.addClsWithUI(me.layout.direction === 'vertical' ? 'vertical-scroller' : 'scroller'); + + }, + + + hideScrollers: function() { + var me = this; + + if (me.beforeScroller !== undefined) { + me.beforeScroller.hide(); + me.afterScroller.hide(); + me.layout.owner.removeClsWithUI(me.layout.direction === 'vertical' ? 'vertical-scroller' : 'scroller'); + + } + }, + + + destroy: function() { + var me = this; + + Ext.destroy(me.beforeRepeater, me.afterRepeater, me.beforeScroller, me.afterScroller, me.beforeCt, me.afterCt); + }, + + + scrollBy: function(delta, animate) { + this.scrollTo(this.getScrollPosition() + delta, animate); + }, + + + getScrollAnim: function() { + return { + duration: this.scrollDuration, + callback: this.updateScrollButtons, + scope : this + }; + }, + + + updateScrollButtons: function() { + var me = this, + beforeMeth, + afterMeth, + beforeCls, + afterCls, + disabledCls, + suffix = '-disabled'; + + if (me.beforeScroller == null || me.afterScroller == null) { + return; + } + + beforeMeth = me.atExtremeBefore() ? 'addCls' : 'removeCls'; + afterMeth = me.atExtremeAfter() ? 'addCls' : 'removeCls'; + disabledCls = me.scrollerCls + suffix; + beforeCls = [disabledCls, me.beforeScrollerCls + suffix]; + afterCls = [disabledCls, me.afterScrollerCls + suffix]; + + me.beforeScroller[beforeMeth](beforeCls); + me.afterScroller[afterMeth](afterCls); + me.scrolling = false; + }, + + + scrollLeft: function() { + this.scrollBy(-this.scrollIncrement, false); + }, + + + scrollRight: function() { + this.scrollBy(this.scrollIncrement, false); + }, + + + getScrollPosition: function(){ + var me = this, + layout = me.layout, + result; + + + + if (isNaN(me.scrollPosition)) { + result = layout.innerCt[layout.names.getScrollLeft](); + } else { + result = me.scrollPosition; + } + return result; + }, + + + getMaxScrollPosition: function() { + var me = this, + layout = me.layout, + maxScrollPos = me.scrollSize - layout.innerCt[layout.names.getWidth](); + + return (maxScrollPos < 0) ? 0 : maxScrollPos; + }, + + + atExtremeBefore: function() { + return !this.getScrollPosition(); + }, + + + atExtremeAfter: function() { + return this.getScrollPosition() >= this.getMaxScrollPosition(); + }, + + + scrollTo: function(position, animate) { + var me = this, + layout = me.layout, + names = layout.names, + oldPosition = me.getScrollPosition(), + newPosition = Ext.Number.constrain(position, 0, me.getMaxScrollPosition()); + + if (newPosition != oldPosition && !me.scrolling) { + me.scrollPosition = NaN; + if (animate === undefined) { + animate = me.animateScroll; + } + + layout.innerCt[names.scrollTo](names.beforeScrollX, newPosition, animate ? me.getScrollAnim() : false); + if (animate) { + me.scrolling = true; + } else { + me.updateScrollButtons(); + } + me.fireEvent('scroll', me, newPosition, animate ? me.getScrollAnim() : false); + } + }, + + + scrollToItem: function(item, animate) { + var me = this, + layout = me.layout, + owner = layout.owner, + names = layout.names, + visibility, + box, + newPos; + + item = me.getItem(item); + if (item !== undefined) { + if (item == owner.items.first()) { + newPos = 0 + } else if (item === owner.items.last()) { + newPos = me.getMaxScrollPosition(); + } else { + visibility = me.getItemVisibility(item); + if (!visibility.fullyVisible) { + box = item.getBox(false, true); + newPos = box[names.x]; + if (visibility.hiddenEnd) { + newPos -= (me.layout.innerCt[names.getWidth]() - box[names.width]); + } + } + } + if (newPos !== undefined) { + me.scrollTo(newPos, animate); + } + } + }, + + + getItemVisibility: function(item) { + var me = this, + box = me.getItem(item).getBox(true, true), + layout = me.layout, + names = layout.names, + itemStart = box[names.x], + itemEnd = itemStart + box[names.width], + scrollStart = me.getScrollPosition(), + scrollEnd = scrollStart + layout.innerCt[names.getWidth](); + + return { + hiddenStart : itemStart < scrollStart, + hiddenEnd : itemEnd > scrollEnd, + fullyVisible: itemStart > scrollStart && itemEnd < scrollEnd + }; + } +}); + + + +Ext.define('Ext.layout.container.Box', { + + + + alias: ['layout.box'], + extend: Ext.layout.container.Container , + alternateClassName: 'Ext.layout.BoxLayout', + + + + + + + + + + + + + defaultMargins: { + top: 0, + right: 0, + bottom: 0, + left: 0 + }, + + + padding: 0, + + + pack: 'start', + + + flex: undefined, + + + stretchMaxPartner: undefined, + + alignRoundingMethod: 'round', + + type: 'box', + scrollOffset: 0, + itemCls: Ext.baseCSSPrefix + 'box-item', + targetCls: Ext.baseCSSPrefix + 'box-layout-ct', + targetElCls: Ext.baseCSSPrefix + 'box-target', + innerCls: Ext.baseCSSPrefix + 'box-inner', + + + + availableSpaceOffset: 0, + + + reserveOffset: true, + + manageMargins: true, + + createsInnerCt: true, + + childEls: [ + 'innerCt', + 'targetEl' + ], + + renderTpl: [ + '{%var oc,l=values.$comp.layout,oh=l.overflowHandler;', + 'if (oh.getPrefixConfig!==Ext.emptyFn) {', + 'if(oc=oh.getPrefixConfig())dh.generateMarkup(oc, out)', + '}%}', + '', + '{%if (oh.getSuffixConfig!==Ext.emptyFn) {', + 'if(oc=oh.getSuffixConfig())dh.generateMarkup(oc, out)', + '}%}', + { + disableFormats: true, + definitions: 'var dh=Ext.DomHelper;' + } + ], + + constructor: function(config) { + var me = this, + type; + + me.callParent(arguments); + + + me.flexSortFn = Ext.Function.bind(me.flexSort, me); + + me.initOverflowHandler(); + + type = typeof me.padding; + if (type == 'string' || type == 'number') { + me.padding = Ext.util.Format.parseBox(me.padding); + me.padding.height = me.padding.top + me.padding.bottom; + me.padding.width = me.padding.left + me.padding.right; + } + }, + + + + _percentageRe: /^\s*(\d+(?:\.\d*)?)\s*[%]\s*$/, + + getItemSizePolicy: function (item, ownerSizeModel) { + var me = this, + policy = me.sizePolicy, + align = me.align, + flex = item.flex, + key = align, + names = me.names, + width = item[names.width], + height = item[names.height], + percentageRe = me._percentageRe, + percentageWidth = percentageRe.test(width), + isStretch = (align == 'stretch'), + isStretchMax = (align == 'stretchmax'), + constrain = me.constrainAlign; + + + if (!ownerSizeModel && (isStretch || flex || percentageWidth || (constrain && !isStretchMax))) { + ownerSizeModel = me.owner.getSizeModel(); + } + + if (isStretch) { + + + if (!percentageRe.test(height) && ownerSizeModel[names.height].shrinkWrap) { + key = 'stretchmax'; + + + + } + } else if (!isStretchMax) { + if (percentageRe.test(height)) { + + + key = 'stretch'; + } else if (constrain && !ownerSizeModel[names.height].shrinkWrap) { + + + key = 'stretchmax'; + } else { + key = ''; + } + } + + if (flex || percentageWidth) { + + + if (!ownerSizeModel[names.width].shrinkWrap) { + policy = policy.flex; + } + } + + return policy[key]; + }, + + flexSort: function (a, b) { + + + + + + var maxWidthName = this.names.maxWidth, + minWidthName = this.names.minWidth, + infiniteValue = Infinity, + aTarget = a.target, + bTarget = b.target, + aFlex = aTarget.flex, + bFlex = bTarget.flex, + result = 0, + aMin, bMin, aMax, bMax, + hasMin, hasMax; + + aMax = aTarget[maxWidthName] || infiniteValue; + bMax = bTarget[maxWidthName] || infiniteValue; + aMin = aTarget[minWidthName] || 0; + bMin = bTarget[minWidthName] || 0; + + hasMin = isFinite(aMin) || isFinite(bMin); + hasMax = isFinite(aMax) || isFinite(bMax); + + if (hasMin || hasMax) { + if (hasMax) { + result = aMax - bMax; + } + + + + + if (result === 0 && hasMin) { + result = bMin - aMin; + } + + + if (result === 0) { + if (hasMax) { + result = bFlex - aFlex; + } else { + result = aFlex - bFlex; + } + } + } + return result; + }, + + isItemBoxParent: function (itemContext) { + return true; + }, + + isItemShrinkWrap: function (item) { + return true; + }, + + roundFlex: function(width) { + return Math.ceil(width); + }, + + + beginCollapse: function(child) { + var me = this; + + if (me.direction === 'vertical' && child.collapsedVertical()) { + child.collapseMemento.capture(['flex']); + delete child.flex; + } else if (me.direction === 'horizontal' && child.collapsedHorizontal()) { + child.collapseMemento.capture(['flex']); + delete child.flex; + } + }, + + + beginExpand: function(child) { + + + child.collapseMemento.restore(['flex']); + }, + + beginLayout: function (ownerContext) { + var me = this, + owner = me.owner, + smp = owner.stretchMaxPartner, + style = me.innerCt.dom.style, + names = me.names; + + ownerContext.boxNames = names; + + + + me.overflowHandler.beginLayout(ownerContext); + + + if (typeof smp === 'string') { + smp = Ext.getCmp(smp) || owner.query(smp)[0]; + } + + ownerContext.stretchMaxPartner = smp && ownerContext.context.getCmp(smp); + + me.callParent(arguments); + + ownerContext.innerCtContext = ownerContext.getEl('innerCt', me); + + + me.scrollParallel = owner.scrollFlags[names.x]; + + + me.scrollPerpendicular = owner.scrollFlags[names.y]; + + + if (me.scrollParallel) { + me.scrollPos = owner.getTargetEl().dom[names.scrollLeft]; + } + + + style.width = ''; + style.height = ''; + }, + + beginLayoutCycle: function (ownerContext, firstCycle) { + var me = this, + align = me.align, + names = ownerContext.boxNames, + pack = me.pack, + heightModelName = names.heightModel; + + + + me.overflowHandler.beginLayoutCycle(ownerContext, firstCycle); + + me.callParent(arguments); + + + + + ownerContext.parallelSizeModel = ownerContext[names.widthModel]; + ownerContext.perpendicularSizeModel = ownerContext[heightModelName]; + + ownerContext.boxOptions = { + align: align = { + stretch: align == 'stretch', + stretchmax: align == 'stretchmax', + center: align == names.center, + bottom: align == names.afterY + }, + pack: pack = { + center: pack == 'center', + end: pack == 'end' + } + }; + + + + + + + + if (align.stretch && ownerContext.perpendicularSizeModel.shrinkWrap) { + align.stretchmax = true; + align.stretch = false; + } + + + align.nostretch = !(align.stretch || align.stretchmax); + + + + + + if (ownerContext.parallelSizeModel.shrinkWrap) { + pack.center = pack.end = false; + } + + me.cacheFlexes(ownerContext); + + + + + + + + me.targetEl.setWidth(20000); + }, + + + cacheFlexes: function (ownerContext) { + var me = this, + names = ownerContext.boxNames, + widthModelName = names.widthModel, + heightModelName = names.heightModel, + nostretch = ownerContext.boxOptions.align.nostretch, + totalFlex = 0, + childItems = ownerContext.childItems, + i = childItems.length, + flexedItems = [], + minWidth = 0, + minWidthName = names.minWidth, + percentageRe = me._percentageRe, + percentageWidths = 0, + percentageHeights = 0, + child, childContext, flex, match; + + while (i--) { + childContext = childItems[i]; + child = childContext.target; + + + + + if (childContext[widthModelName].calculated) { + childContext.flex = flex = child.flex; + if (flex) { + totalFlex += flex; + flexedItems.push(childContext); + minWidth += child[minWidthName] || 0; + } else { + match = percentageRe.exec(child[names.width]); + childContext.percentageParallel = parseFloat(match[1]) / 100; + ++percentageWidths; + } + } + + + + if (nostretch && childContext[heightModelName].calculated) { + + + match = percentageRe.exec(child[names.height]); + childContext.percentagePerpendicular = parseFloat(match[1]) / 100; + ++percentageHeights; + } + } + + ownerContext.flexedItems = flexedItems; + ownerContext.flexedMinSize = minWidth; + ownerContext.totalFlex = totalFlex; + ownerContext.percentageWidths = percentageWidths; + ownerContext.percentageHeights = percentageHeights; + + + + + Ext.Array.sort(flexedItems, me.flexSortFn); + }, + + calculate: function(ownerContext) { + var me = this, + targetSize = me.getContainerSize(ownerContext), + names = ownerContext.boxNames, + state = ownerContext.state, + plan = state.boxPlan || (state.boxPlan = {}), + targetContext = ownerContext.targetContext; + + plan.targetSize = targetSize; + + if (!state.parallelDone) { + state.parallelDone = me.calculateParallel(ownerContext, names, plan); + } + + if (!state.perpendicularDone) { + state.perpendicularDone = me.calculatePerpendicular(ownerContext, names, plan); + } + + if (state.parallelDone && state.perpendicularDone) { + + + + + if (me.owner.dock && (Ext.isIE7m || Ext.isIEQuirks) && !me.owner.width && !me.horizontal) { + plan.isIEVerticalDock = true; + plan.calculatedWidth = plan.maxSize + ownerContext.getPaddingInfo().width + ownerContext.getFrameInfo().width; + if (targetContext !== ownerContext) { + + + + plan.calculatedWidth += targetContext.getPaddingInfo().width; + } + } + + me.publishInnerCtSize(ownerContext, me.reserveOffset ? me.availableSpaceOffset : 0); + + + + if (me.done && ownerContext.boxOptions.align.stretchmax && !state.stretchMaxDone) { + me.calculateStretchMax(ownerContext, names, plan); + state.stretchMaxDone = true; + } + me.overflowHandler.calculate(ownerContext); + } else { + me.done = false; + } + }, + + calculateParallel: function(ownerContext, names, plan) { + var me = this, + widthName = names.width, + childItems = ownerContext.childItems, + beforeXName = names.beforeX, + afterXName = names.afterX, + setWidthName = names.setWidth, + childItemsLength = childItems.length, + flexedItems = ownerContext.flexedItems, + flexedItemsLength = flexedItems.length, + pack = ownerContext.boxOptions.pack, + padding = me.padding, + targetSize = plan.targetSize, + containerWidth = targetSize[widthName], + totalMargin = 0, + left = padding[beforeXName], + nonFlexWidth = left + padding[afterXName] + me.scrollOffset + + (me.reserveOffset ? me.availableSpaceOffset : 0), + scrollbarWidth = Ext.getScrollbarSize()[names.width], + i, childMargins, remainingWidth, remainingFlex, childContext, flex, flexedWidth, + contentWidth, mayNeedScrollbarAdjust, childWidth, percentageSpace; + + + + + + if (!ownerContext.parallelSizeModel.shrinkWrap && !targetSize[names.gotWidth]) { + return false; + } + + + + + + + + if (scrollbarWidth && + me.scrollPerpendicular && + ownerContext.parallelSizeModel.shrinkWrap && + !ownerContext.boxOptions.align.stretch && + !ownerContext.perpendicularSizeModel.shrinkWrap) { + + + + + if (!ownerContext.state.perpendicularDone) { + return false; + } + mayNeedScrollbarAdjust = true; + } + + + for (i = 0; i < childItemsLength; ++i) { + childContext = childItems[i]; + childMargins = childContext.marginInfo || childContext.getMarginInfo(); + + totalMargin += childMargins[widthName]; + + if (!childContext[names.widthModel].calculated) { + childWidth = childContext.getProp(widthName); + nonFlexWidth += childWidth; + if (isNaN(nonFlexWidth)) { + return false; + } + } + } + + nonFlexWidth += totalMargin; + if (ownerContext.percentageWidths) { + percentageSpace = containerWidth - totalMargin; + if (isNaN(percentageSpace)) { + return false; + } + + for (i = 0; i < childItemsLength; ++i) { + childContext = childItems[i]; + if (childContext.percentageParallel) { + childWidth = Math.ceil(percentageSpace * childContext.percentageParallel); + childWidth = childContext.setWidth(childWidth); + nonFlexWidth += childWidth; + } + } + } + + + + if (ownerContext.parallelSizeModel.shrinkWrap) { + plan.availableSpace = 0; + plan.tooNarrow = false; + } else { + plan.availableSpace = containerWidth - nonFlexWidth; + + + plan.tooNarrow = plan.availableSpace < ownerContext.flexedMinSize; + if (plan.tooNarrow && Ext.getScrollbarSize()[names.height] && me.scrollParallel && ownerContext.state.perpendicularDone) { + ownerContext.state.perpendicularDone = false; + for (i = 0; i < childItemsLength; ++i) { + childItems[i].invalidate(); + } + } + } + + contentWidth = nonFlexWidth; + remainingWidth = plan.availableSpace; + remainingFlex = ownerContext.totalFlex; + + + for (i = 0; i < flexedItemsLength; i++) { + childContext = flexedItems[i]; + flex = childContext.flex; + flexedWidth = me.roundFlex((flex / remainingFlex) * remainingWidth); + flexedWidth = childContext[setWidthName](flexedWidth); + + + + contentWidth += flexedWidth; + + remainingWidth = Math.max(0, remainingWidth - flexedWidth); + remainingFlex -= flex; + } + + if (pack.center) { + left += remainingWidth / 2; + + + if (left < 0) { + left = 0; + } + } else if (pack.end) { + left += remainingWidth; + } + + + for (i = 0; i < childItemsLength; ++i) { + childContext = childItems[i]; + childMargins = childContext.marginInfo; + + left += childMargins[beforeXName]; + + childContext.setProp(names.x, left); + + + + + + left += childMargins[afterXName] + childContext.props[widthName]; + } + + contentWidth += ownerContext.targetContext.getPaddingInfo()[widthName]; + + + ownerContext.state.contentWidth = contentWidth; + + + + if (mayNeedScrollbarAdjust && + (ownerContext.peek(names.contentHeight) > plan.targetSize[names.height])) { + contentWidth += scrollbarWidth; + ownerContext[names.hasOverflowY] = true; + + + ownerContext.target.componentLayout[names.setWidthInDom] = true; + + + + + + ownerContext[names.invalidateScrollY] = Ext.isStrict && Ext.isIE8; + } + ownerContext[names.setContentWidth](contentWidth); + + return true; + }, + + calculatePerpendicular: function(ownerContext, names, plan) { + var me = this, + heightShrinkWrap = ownerContext.perpendicularSizeModel.shrinkWrap, + targetSize = plan.targetSize, + childItems = ownerContext.childItems, + childItemsLength = childItems.length, + mmax = Math.max, + heightName = names.height, + setHeightName = names.setHeight, + beforeYName = names.beforeY, + topPositionName = names.y, + padding = me.padding, + top = padding[beforeYName], + availHeight = targetSize[heightName] - top - padding[names.afterY], + align = ownerContext.boxOptions.align, + isStretch = align.stretch, + isStretchMax = align.stretchmax, + isCenter = align.center, + isBottom = align.bottom, + constrain = me.constrainAlign, + maxHeight = 0, + hasPercentageSizes = 0, + onBeforeInvalidateChild = me.onBeforeConstrainInvalidateChild, + onAfterInvalidateChild = me.onAfterConstrainInvalidateChild, + scrollbarHeight = Ext.getScrollbarSize().height, + childTop, i, childHeight, childMargins, diff, height, childContext, + stretchMaxPartner, stretchMaxChildren, shrinkWrapParallelOverflow, + percentagePerpendicular; + + if (isStretch || ((isCenter || isBottom) && !heightShrinkWrap)) { + if (isNaN(availHeight)) { + return false; + } + } + + + + + + if (!isStretch && !ownerContext.parallelSizeModel.shrinkWrap && !ownerContext.state.parallelDone && me.scrollParallel) { + return false; + } + + + + + + + + if (me.scrollParallel && plan.tooNarrow) { + if (heightShrinkWrap) { + shrinkWrapParallelOverflow = true; + } else { + availHeight -= scrollbarHeight; + plan.targetSize[heightName] -= scrollbarHeight; + } + } + + if (isStretch) { + height = availHeight; + } else { + for (i = 0; i < childItemsLength; i++) { + childContext = childItems[i]; + childMargins = (childContext.marginInfo || childContext.getMarginInfo())[heightName]; + + if (!(percentagePerpendicular = childContext.percentagePerpendicular)) { + childHeight = childContext.getProp(heightName); + } else { + ++hasPercentageSizes; + if (heightShrinkWrap) { + + + continue; + } else { + childHeight = percentagePerpendicular * availHeight - childMargins; + childHeight = childContext[names.setHeight](childHeight); + } + } + + + + + + + if (!heightShrinkWrap && constrain && childContext[names.heightModel].shrinkWrap && childHeight > availHeight) { + childContext.invalidate({ + before: onBeforeInvalidateChild, + after: onAfterInvalidateChild, + layout: me, + childHeight: availHeight, + names: names + }); + + + + ownerContext.state.parallelDone = false; + } + + + if (isNaN(maxHeight = mmax(maxHeight, childHeight + childMargins, + childContext.target[names.minHeight] || 0))) { + return false; + } + } + + + + if (shrinkWrapParallelOverflow) { + maxHeight += scrollbarHeight; + ownerContext[names.hasOverflowX] = true; + + + ownerContext.target.componentLayout[names.setHeightInDom] = true; + + + + + + ownerContext[names.invalidateScrollX] = Ext.isStrict && Ext.isIE8; + } + + + + stretchMaxPartner = ownerContext.stretchMaxPartner; + if (stretchMaxPartner) { + + ownerContext.setProp('maxChildHeight', maxHeight); + stretchMaxChildren = stretchMaxPartner.childItems; + + if (stretchMaxChildren && stretchMaxChildren.length) { + maxHeight = mmax(maxHeight, stretchMaxPartner.getProp('maxChildHeight')); + if (isNaN(maxHeight)) { + return false; + } + } + } + + ownerContext[names.setContentHeight](maxHeight + me.padding[heightName] + + ownerContext.targetContext.getPaddingInfo()[heightName]); + + + + + if (shrinkWrapParallelOverflow) { + maxHeight -= scrollbarHeight; + } + plan.maxSize = maxHeight; + + if (isStretchMax) { + height = maxHeight; + } else if (isCenter || isBottom || hasPercentageSizes) { + if (constrain) { + height = heightShrinkWrap ? maxHeight : availHeight; + } else { + height = heightShrinkWrap ? maxHeight : mmax(availHeight, maxHeight); + } + + + + + + height -= ownerContext.innerCtContext.getBorderInfo()[heightName]; + } + } + + for (i = 0; i < childItemsLength; i++) { + childContext = childItems[i]; + childMargins = childContext.marginInfo || childContext.getMarginInfo(); + + childTop = top + childMargins[beforeYName]; + + if (isStretch) { + childContext[setHeightName](height - childMargins[heightName]); + } else { + percentagePerpendicular = childContext.percentagePerpendicular; + if (heightShrinkWrap && percentagePerpendicular) { + childMargins = childContext.marginInfo || childContext.getMarginInfo(); + childHeight = percentagePerpendicular * height - childMargins[heightName]; + childHeight = childContext.setHeight(childHeight); + } + + if (isCenter) { + diff = height - childContext.props[heightName]; + if (diff > 0) { + childTop = top + Math[me.alignRoundingMethod](diff / 2); + } + } else if (isBottom) { + childTop = mmax(0, height - childTop - childContext.props[heightName]); + } + } + + childContext.setProp(topPositionName, childTop); + } + + return true; + }, + + onBeforeConstrainInvalidateChild: function(childContext, options){ + + var heightModelName = options.names.heightModel; + if (!childContext[heightModelName].constrainedMin) { + + + childContext[heightModelName] = Ext.layout.SizeModel.calculated; + } + }, + + onAfterConstrainInvalidateChild: function(childContext, options){ + + var names = options.names; + + + + + + + childContext.setProp(names.beforeY, 0); + if (childContext[names.heightModel].calculated) { + childContext[names.setHeight](options.childHeight); + } + }, + + calculateStretchMax: function (ownerContext, names, plan) { + var me = this, + heightName = names.height, + widthName = names.width, + childItems = ownerContext.childItems, + length = childItems.length, + height = plan.maxSize, + onBeforeStretchMaxInvalidateChild = me.onBeforeStretchMaxInvalidateChild, + onAfterStretchMaxInvalidateChild = me.onAfterStretchMaxInvalidateChild, + childContext, props, i, childHeight; + + for (i = 0; i < length; ++i) { + childContext = childItems[i]; + + props = childContext.props; + childHeight = height - childContext.getMarginInfo()[heightName]; + + if (childHeight != props[heightName] || + childContext[names.heightModel].constrained) { + + + + + + + + childContext.invalidate({ + before: onBeforeStretchMaxInvalidateChild, + after: onAfterStretchMaxInvalidateChild, + layout: me, + + childWidth: props[widthName], + + childHeight: childHeight, + childX: props.x, + childY: props.y, + names: names + }); + } + } + }, + + onBeforeStretchMaxInvalidateChild: function (childContext, options) { + + var heightModelName = options.names.heightModel; + + + + + + if (!childContext[heightModelName].constrainedMax) { + + + childContext[heightModelName] = Ext.layout.SizeModel.calculated; + } + }, + + onAfterStretchMaxInvalidateChild: function (childContext, options) { + + var names = options.names, + childHeight = options.childHeight, + childWidth = options.childWidth; + + childContext.setProp('x', options.childX); + childContext.setProp('y', options.childY); + + if (childContext[names.heightModel].calculated) { + + + childContext[names.setHeight](childHeight); + } + + if (childContext[names.widthModel].calculated) { + childContext[names.setWidth](childWidth); + } + }, + + completeLayout: function(ownerContext) { + var me = this, + names = ownerContext.boxNames, + invalidateScrollX = ownerContext.invalidateScrollX, + invalidateScrollY = ownerContext.invalidateScrollY, + dom, el, overflowX, overflowY, styles; + + me.overflowHandler.completeLayout(ownerContext); + + if (invalidateScrollX || invalidateScrollY) { + el = me.getTarget(); + dom = el.dom; + styles = dom.style; + + if (invalidateScrollX) { + + overflowX = el.getStyle('overflowX'); + if (overflowX == 'auto') { + + overflowX = styles.overflowX; + styles.overflowX = 'scroll'; + } else { + invalidateScrollX = false; + } + } + + if (invalidateScrollY) { + + overflowY = el.getStyle('overflowY'); + if (overflowY == 'auto') { + + overflowY = styles.overflowY; + styles.overflowY = 'scroll'; + } else { + invalidateScrollY = false; + } + } + + if (invalidateScrollX || invalidateScrollY) { + + dom.scrollWidth; + + if (invalidateScrollX) { + styles.overflowX = overflowX; + } + if (invalidateScrollY) { + styles.overflowY = overflowY; + } + } + } + + + if (me.scrollParallel) { + me.owner.getTargetEl().dom[names.scrollLeft] = me.scrollPos; + } + }, + + finishedLayout: function(ownerContext) { + this.overflowHandler.finishedLayout(ownerContext); + this.callParent(arguments); + + + + + + + + + + + this.targetEl.setWidth(ownerContext.innerCtContext.props.width); + }, + + publishInnerCtSize: function(ownerContext, reservedSpace) { + var me = this, + names = ownerContext.boxNames, + heightName = names.height, + widthName = names.width, + align = ownerContext.boxOptions.align, + dock = me.owner.dock, + padding = me.padding, + plan = ownerContext.state.boxPlan, + targetSize = plan.targetSize, + height = targetSize[heightName], + innerCtContext = ownerContext.innerCtContext, + innerCtWidth = (ownerContext.parallelSizeModel.shrinkWrap || (plan.tooNarrow && me.scrollParallel) + ? ownerContext.state.contentWidth - ownerContext.targetContext.getPaddingInfo()[widthName] + : targetSize[widthName]) - (reservedSpace || 0), + innerCtHeight; + + if (align.stretch) { + innerCtHeight = height; + } else { + innerCtHeight = plan.maxSize + padding[names.beforeY] + padding[names.afterY] + innerCtContext.getBorderInfo()[heightName]; + + if (!ownerContext.perpendicularSizeModel.shrinkWrap && (align.center || align.bottom)) { + innerCtHeight = Math.max(height, innerCtHeight); + } + } + + innerCtContext[names.setWidth](innerCtWidth); + innerCtContext[names.setHeight](innerCtHeight); + + + if (isNaN(innerCtWidth + innerCtHeight)) { + me.done = false; + } + + + + + + + + if (plan.calculatedWidth && (dock == 'left' || dock == 'right')) { + + ownerContext.setWidth(plan.calculatedWidth, true, true); + } + }, + + onRemove: function(comp, isDestroying){ + var me = this, + names = me.names, + el; + + me.callParent(arguments); + if (me.overflowHandler) { + me.overflowHandler.onRemove(comp); + } + if (comp.layoutMarginCap == me.id) { + delete comp.layoutMarginCap; + } + + if (!me.owner.destroying && !isDestroying && comp.rendered) { + + el = comp.getEl(); + el.setStyle(names.beforeY, ''); + el.setStyle(names.beforeX, ''); + + + + el.setStyle('margin', ''); + } + }, + + + initOverflowHandler: function() { + var me = this, + handler = me.overflowHandler, + handlerType, + constructor; + + if (typeof handler == 'string') { + handler = { + type: handler + }; + } + + handlerType = 'None'; + if (handler && handler.type !== undefined) { + handlerType = handler.type; + } + + constructor = Ext.layout.container.boxOverflow[handlerType]; + if (constructor[me.type]) { + constructor = constructor[me.type]; + } + + me.overflowHandler = Ext.create('Ext.layout.container.boxOverflow.' + handlerType, me, handler); + }, + + + + getRenderTarget: function() { + return this.targetEl; + }, + + + + getElementTarget: function() { + return this.innerCt; + }, + + calculateChildBox: Ext.deprecated(), + calculateChildBoxes: Ext.deprecated(), + updateChildBoxes: Ext.deprecated(), + + + destroy: function() { + var me = this; + Ext.destroy(me.innerCt, me.overflowHandler); + me.flexSortFn = me.innerCt = null; + me.callParent(arguments); + }, + + getRenderData: function() { + var data = this.callParent(); + + data.targetElCls = this.targetElCls; + + return data; + } +}); + + + +Ext.define('Ext.layout.container.HBox', { + + + + alias: ['layout.hbox'], + extend: Ext.layout.container.Box , + alternateClassName: 'Ext.layout.HBoxLayout', + + + + + align: 'top', + + + + + constrainAlign: false, + + type : 'hbox', + + direction: 'horizontal', + + horizontal: true, + + names: { + + beforeX: 'left', + beforeScrollX: 'left', + beforeScrollerSuffix: '-before-scroller', + afterScrollerSuffix: '-after-scroller', + leftCap: 'Left', + afterX: 'right', + width: 'width', + contentWidth: 'contentWidth', + minWidth: 'minWidth', + maxWidth: 'maxWidth', + widthCap: 'Width', + widthModel: 'widthModel', + widthIndex: 0, + x: 'x', + scrollLeft: 'scrollLeft', + overflowX: 'overflowX', + hasOverflowX: 'hasOverflowX', + invalidateScrollX: 'invalidateScrollX', + parallelMargins: 'lr', + + + center: 'middle', + beforeY: 'top', + afterY: 'bottom', + height: 'height', + contentHeight: 'contentHeight', + minHeight: 'minHeight', + maxHeight: 'maxHeight', + heightCap: 'Height', + heightModel: 'heightModel', + heightIndex: 1, + y: 'y', + overflowY: 'overflowY', + hasOverflowY: 'hasOverflowY', + invalidateScrollY: 'invalidateScrollY', + perpendicularMargins: 'tb', + + + getWidth: 'getWidth', + getHeight: 'getHeight', + setWidth: 'setWidth', + setHeight: 'setHeight', + gotWidth: 'gotWidth', + gotHeight: 'gotHeight', + setContentWidth: 'setContentWidth', + setContentHeight: 'setContentHeight', + setWidthInDom: 'setWidthInDom', + setHeightInDom: 'setHeightInDom', + getScrollLeft: 'getScrollLeft', + setScrollLeft: 'setScrollLeft', + scrollTo: 'scrollTo' + }, + + sizePolicy: { + flex: { + '': { + readsWidth : 0, + readsHeight: 1, + setsWidth : 1, + setsHeight : 0 + }, + stretch: { + readsWidth : 0, + readsHeight: 0, + setsWidth : 1, + setsHeight : 1 + }, + stretchmax: { + readsWidth : 0, + readsHeight: 1, + setsWidth : 1, + setsHeight : 1 + } + }, + '': { + readsWidth : 1, + readsHeight: 1, + setsWidth : 0, + setsHeight : 0 + }, + stretch: { + readsWidth : 1, + readsHeight: 0, + setsWidth : 0, + setsHeight : 1 + }, + stretchmax: { + readsWidth : 1, + readsHeight: 1, + setsWidth : 0, + setsHeight : 1 + } + } +}); + + + +Ext.define('Ext.layout.container.VBox', { + + + + alias: ['layout.vbox'], + extend: Ext.layout.container.Box , + alternateClassName: 'Ext.layout.VBoxLayout', + + + + + align : 'left', + + + + + constrainAlign: false, + + type: 'vbox', + + direction: 'vertical', + + horizontal: false, + + names: { + + beforeX: 'top', + beforeScrollX: 'top', + beforeScrollerSuffix: '-before-scroller', + afterScrollerSuffix: '-after-scroller', + leftCap: 'Top', + afterX: 'bottom', + width: 'height', + contentWidth: 'contentHeight', + minWidth: 'minHeight', + maxWidth: 'maxHeight', + widthCap: 'Height', + widthModel: 'heightModel', + widthIndex: 1, + x: 'y', + scrollLeft: 'scrollTop', + overflowX: 'overflowY', + hasOverflowX: 'hasOverflowY', + invalidateScrollX: 'invalidateScrollY', + parallelMargins: 'tb', + + + center: 'center', + beforeY: 'left', + afterY: 'right', + height: 'width', + contentHeight: 'contentWidth', + minHeight: 'minWidth', + maxHeight: 'maxWidth', + heightCap: 'Width', + heightModel: 'widthModel', + heightIndex: 0, + y: 'x', + overflowY: 'overflowX', + hasOverflowY: 'hasOverflowX', + invalidateScrollY: 'invalidateScrollX', + perpendicularMargins: 'lr', + + + getWidth: 'getHeight', + getHeight: 'getWidth', + setWidth: 'setHeight', + setHeight: 'setWidth', + gotWidth: 'gotHeight', + gotHeight: 'gotWidth', + setContentWidth: 'setContentHeight', + setContentHeight: 'setContentWidth', + setWidthInDom: 'setHeightInDom', + setHeightInDom: 'setWidthInDom', + getScrollLeft: 'getScrollTop', + setScrollLeft: 'setScrollTop', + scrollTo: 'scrollTo' + }, + + sizePolicy: { + flex: { + '': { + readsWidth : 1, + readsHeight: 0, + setsWidth : 0, + setsHeight : 1 + }, + stretch: { + readsWidth : 0, + readsHeight: 0, + setsWidth : 1, + setsHeight : 1 + }, + stretchmax: { + readsWidth : 1, + readsHeight: 0, + setsWidth : 1, + setsHeight : 1 + } + }, + '': { + readsWidth : 1, + readsHeight: 1, + setsWidth : 0, + setsHeight : 0 + }, + stretch: { + readsWidth : 0, + readsHeight: 1, + setsWidth : 1, + setsHeight : 0 + }, + stretchmax: { + readsWidth : 1, + readsHeight: 1, + setsWidth : 1, + setsHeight : 0 + } + } +}); + + + +Ext.define('Ext.toolbar.Toolbar', { + extend: Ext.container.Container , + + + + + + + + + alias: 'widget.toolbar', + alternateClassName: 'Ext.Toolbar', + + + isToolbar: true, + baseCls : Ext.baseCSSPrefix + 'toolbar', + ariaRole : 'toolbar', + + defaultType: 'button', + + + vertical: false, + + + + + enableOverflow: false, + + + menuTriggerCls: Ext.baseCSSPrefix + 'toolbar-more-icon', + + + + + trackMenus: true, + + itemCls: Ext.baseCSSPrefix + 'toolbar-item', + + statics: { + shortcuts: { + '-' : 'tbseparator', + ' ' : 'tbspacer' + }, + + shortcutsHV: { + + 0: { + '->': { xtype: 'tbfill', height: 0 } + }, + + 1: { + '->': { xtype: 'tbfill', width: 0 } + } + } + }, + + initComponent: function() { + var me = this; + + + if (!me.layout && me.enableOverflow) { + me.layout = { overflowHandler: 'Menu' }; + } + + if (me.dock === 'right' || me.dock === 'left') { + me.vertical = true; + } + + me.layout = Ext.applyIf(Ext.isString(me.layout) ? { + type: me.layout + } : me.layout || {}, { + type: me.vertical ? 'vbox' : 'hbox', + align: me.vertical ? 'stretchmax' : 'middle' + }); + + if (me.vertical) { + me.addClsWithUI('vertical'); + } + + + if (me.ui === 'footer') { + me.ignoreBorderManagement = true; + } + + me.callParent(); + + + me.addEvents('overflowchange'); + }, + + getRefItems: function(deep) { + var me = this, + items = me.callParent(arguments), + layout = me.layout, + handler; + + if (deep && me.enableOverflow) { + handler = layout.overflowHandler; + if (handler && handler.menu) { + items = items.concat(handler.menu.getRefItems(deep)); + } + } + return items; + }, + + + + + + + lookupComponent: function(c) { + var args = arguments; + if (typeof c == 'string') { + var T = Ext.toolbar.Toolbar, + shortcut = T.shortcutsHV[this.vertical ? 1 : 0][c] || T.shortcuts[c]; + + if (typeof shortcut == 'string') { + c = { + xtype: shortcut + }; + } else if (shortcut) { + c = Ext.apply({}, shortcut); + } else { + c = { + xtype: 'tbtext', + text: c + }; + } + + this.applyDefaults(c); + + + args = [c]; + } + + return this.callParent(args); + }, + + + applyDefaults: function(c) { + if (!Ext.isString(c)) { + c = this.callParent(arguments); + } + return c; + }, + + + trackMenu: function(item, remove) { + if (this.trackMenus && item.menu) { + var method = remove ? 'mun' : 'mon', + me = this; + + me[method](item, 'mouseover', me.onButtonOver, me); + me[method](item, 'menushow', me.onButtonMenuShow, me); + me[method](item, 'menuhide', me.onButtonMenuHide, me); + } + }, + + + onBeforeAdd: function(component) { + var me = this, + isButton = component.isButton; + + if (isButton && me.defaultButtonUI && component.ui === 'default' && + !component.hasOwnProperty('ui')) { + component.ui = me.defaultButtonUI; + } else if ((isButton || component.isFormField) && me.ui !== 'footer') { + component.ui = component.ui + '-toolbar'; + component.addCls(component.baseCls + '-toolbar'); + } + + + if (component instanceof Ext.toolbar.Separator) { + component.setUI((me.vertical) ? 'vertical' : 'horizontal'); + } + + me.callParent(arguments); + }, + + + onAdd: function(component) { + this.callParent(arguments); + this.trackMenu(component); + }, + + + onRemove: function(c) { + this.callParent(arguments); + this.trackMenu(c, true); + }, + + getChildItemsToDisable: function() { + return this.items.getRange(); + }, + + + onButtonOver: function(btn){ + if (this.activeMenuBtn && this.activeMenuBtn != btn) { + this.activeMenuBtn.hideMenu(); + btn.showMenu(); + this.activeMenuBtn = btn; + } + }, + + + onButtonMenuShow: function(btn) { + this.activeMenuBtn = btn; + }, + + + onButtonMenuHide: function(btn) { + delete this.activeMenuBtn; + } +}); + + + +Ext.define('Ext.panel.AbstractPanel', { + + + + extend: Ext.container.Container , + + mixins: { + docking: Ext.container.DockingContainer + }, + + + + + + + baseCls : Ext.baseCSSPrefix + 'panel', + + + + + + + + + + + isPanel: true, + + + + + contentPaddingProperty: 'bodyPadding', + + + shrinkWrapDock: false, + + componentLayout: 'dock', + + childEls: [ + 'body' + ], + + renderTpl: [ + + '{% this.renderDockedItems(out,values,0); %}', + + + + + + + + + + + + + (Ext.isIE7m || Ext.isIEQuirks) ? '
 
' : '', + '
{bodyCls}', + ' {baseCls}-body-{ui}', + ' {parent.baseCls}-body-{parent.ui}-{.}', + '{childElCls}"', + ' role="{bodyRole}" role="presentation"', + ' style="{bodyStyle}">', + '{%this.renderContainer(out,values);%}', + '
', + '{% this.renderDockedItems(out,values,1); %}' + ], + + bodyPosProps: { + x: 'x', + y: 'y' + }, + + + + + + border: true, + + + emptyArray: [], + + initComponent : function() { + this.initBorderProps(); + this.callParent(); + }, + + initBorderProps: function() { + var me = this; + + if (me.frame && me.border && me.bodyBorder === undefined) { + me.bodyBorder = false; + } + if (me.frame && me.border && (me.bodyBorder === false || me.bodyBorder === 0)) { + me.manageBodyBorders = true; + } + }, + + beforeDestroy: function(){ + this.destroyDockedItems(); + this.callParent(); + }, + + + initItems : function() { + this.callParent(); + this.initDockingItems(); + }, + + + initRenderData: function() { + var me = this, + data = me.callParent(); + + me.initBodyStyles(); + me.protoBody.writeTo(data); + delete me.protoBody; + + return data; + }, + + + getComponent: function(comp) { + var component = this.callParent(arguments); + if (component === undefined && !Ext.isNumber(comp)) { + + component = this.getDockedComponent(comp); + } + return component; + }, + + getProtoBody: function () { + var me = this, + body = me.protoBody; + + if (!body) { + me.protoBody = body = new Ext.util.ProtoElement({ + cls: me.bodyCls, + style: me.bodyStyle, + clsProp: 'bodyCls', + styleProp: 'bodyStyle', + styleIsText: true + }); + } + + return body; + }, + + + initBodyStyles: function() { + var me = this, + body = me.getProtoBody(); + + if (me.bodyPadding !== undefined) { + if (me.layout.managePadding) { + + + + + + body.setStyle('padding', 0); + } else { + body.setStyle('padding', this.unitizeBox((me.bodyPadding === true) ? 5 : me.bodyPadding)); + } + } + me.initBodyBorder(); + }, + + initBodyBorder: function() { + var me = this; + + if (me.frame && me.bodyBorder) { + if (!Ext.isNumber(me.bodyBorder)) { + me.bodyBorder = 1; + } + me.getProtoBody().setStyle('border-width', this.unitizeBox(me.bodyBorder)); + } + }, + + getCollapsedDockedItems: function () { + var me = this; + return me.header === false || me.collapseMode == 'placeholder' ? me.emptyArray : [ me.getReExpander() ]; + }, + + + setBodyStyle: function(style, value) { + var me = this, + body = me.rendered ? me.body : me.getProtoBody(); + + if (Ext.isFunction(style)) { + style = style(); + } + if (arguments.length == 1) { + if (Ext.isString(style)) { + style = Ext.Element.parseStyles(style); + } + body.setStyle(style); + } else { + body.setStyle(style, value); + } + return me; + }, + + + addBodyCls: function(cls) { + var me = this, + body = me.rendered ? me.body : me.getProtoBody(); + + body.addCls(cls); + return me; + }, + + + removeBodyCls: function(cls) { + var me = this, + body = me.rendered ? me.body : me.getProtoBody(); + + body.removeCls(cls); + return me; + }, + + + addUIClsToElement: function(cls) { + var me = this, + result = me.callParent(arguments); + + me.addBodyCls([Ext.baseCSSPrefix + cls, me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls]); + return result; + }, + + + removeUIClsFromElement: function(cls) { + var me = this, + result = me.callParent(arguments); + + me.removeBodyCls([Ext.baseCSSPrefix + cls, me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls]); + return result; + }, + + + addUIToElement: function() { + var me = this; + + me.callParent(arguments); + me.addBodyCls(me.baseCls + '-body-' + me.ui); + }, + + + removeUIFromElement: function() { + var me = this; + + me.callParent(arguments); + me.removeBodyCls(me.baseCls + '-body-' + me.ui); + }, + + + getTargetEl : function() { + return this.body; + }, + + applyTargetCls: function(targetCls) { + this.getProtoBody().addCls(targetCls); + }, + + getRefItems: function(deep) { + var items = this.callParent(arguments); + + return this.getDockingRefItems(deep, items); + }, + + setupRenderTpl: function (renderTpl) { + this.callParent(arguments); + this.setupDockingRenderTpl(renderTpl); + } +}); + + + +Ext.define('Ext.panel.Header', { + extend: Ext.container.Container , + + alias: 'widget.header', + + + isHeader : true, + defaultType : 'tool', + indicateDrag : false, + weight : -1, + componentLayout: 'body', + + + + childEls: [ + 'body' + ], + + renderTpl: [ + '
{parent.baseCls}-body-{parent.ui}-{.}"', + ' style="{bodyStyle}" role="presentation">', + '{%this.renderContainer(out,values)%}', + '
' + ], + + headingTpl: [ + + '', + ' role="{headerRole}"', + '', + '>{title}' + ], + + shrinkWrap: 3, + + + + + titlePosition: 0, + + + + + + + + + headerCls: Ext.baseCSSPrefix + 'header', + + initComponent: function() { + var me = this, + hasPosition = me.hasOwnProperty('titlePosition'), + items = me.items, + titlePosition = hasPosition ? me.titlePosition : (items ? items.length : 0), + uiClasses = [me.orientation, me.getDockName()], + ownerCt = me.ownerCt; + + me.addEvents( + + 'click', + + + 'dblclick' + ); + + me.indicateDragCls = me.headerCls + '-draggable'; + me.title = me.title || ' '; + me.tools = me.tools || []; + items = me.items = (items ? Ext.Array.slice(items) : []); + me.orientation = me.orientation || 'horizontal'; + me.dock = (me.dock) ? me.dock : (me.orientation == 'horizontal') ? 'top' : 'left'; + + + if (ownerCt ? (ownerCt.border === false && !ownerCt.frame) : me.border === false) { + uiClasses.push(me.orientation + '-noborder'); + } + me.addClsWithUI(uiClasses); + me.addCls([me.headerCls, me.headerCls + '-' + me.orientation]); + + if (me.indicateDrag) { + me.addCls(me.indicateDragCls); + } + + + if (me.iconCls || me.icon || me.glyph) { + me.initIconCmp(); + + + if (!hasPosition && !items.length) { + ++titlePosition; + } + items.push(me.iconCmp); + } + + + me.titleCmp = new Ext.Component({ + ariaRole : 'presentation', + focusable : false, + noWrap : true, + flex : 1, + rtl : me.rtl, + id : me.id + '_hd', + style : me.titleAlign ? ('text-align:' + me.titleAlign) : '', + cls : me.headerCls + '-text-container ' + + me.baseCls + '-text-container ' + + me.baseCls + '-text-container-' + me.ui, + renderTpl : me.getTpl('headingTpl'), + renderData: { + title: me.title, + cls : me.baseCls, + headerCls: me.headerCls, + headerRole: me.headerRole, + ui : me.ui + }, + childEls : ['textEl'], + autoEl: { + + unselectable: 'on' + }, + listeners: { + render: me.onTitleRender, + scope: me + } + }); + me.layout = (me.orientation == 'vertical') ? { + type : 'vbox', + align: 'center', + alignRoundingMethod: 'ceil' + } : { + type : 'hbox', + align: 'middle', + alignRoundingMethod: 'floor' + }; + + + Ext.Array.push(items, me.tools); + + + me.tools.length = 0; + me.callParent(); + + if (items.length < titlePosition) { + titlePosition = items.length; + } + me.titlePosition = titlePosition; + + + me.insert(titlePosition, me.titleCmp); + + me.on({ + dblclick: me.onDblClick, + click: me.onClick, + element: 'el', + scope: me + }); + }, + + + setTitlePosition: function(index) { + var me = this; + + me.titlePosition = index = Math.min(index, me.items.length - 1); + me.insert(index, me.titleCmp); + }, + + initIconCmp: function() { + var me = this, + cls = [me.headerCls + '-icon', me.baseCls + '-icon', me.iconCls], + cfg; + + if (me.glyph) { + cls.push(me.baseCls + '-glyph'); + } + + cfg = { + focusable: false, + src: Ext.BLANK_IMAGE_URL, + cls: cls, + baseCls: me.baseCls + '-icon', + id: me.id + '-iconEl', + iconCls: me.iconCls, + glyph: me.glyph + }; + + if (!Ext.isEmpty(me.icon)) { + delete cfg.iconCls; + cfg.src = me.icon; + } + + me.iconCmp = new Ext.Img(cfg); + }, + + beforeRender: function() { + this.protoEl.unselectable(); + this.callParent(); + }, + + afterLayout: function() { + var me = this, + frameBR, frameTR, frameTL, xPos; + + if (me.orientation === 'vertical') { + me.adjustTitlePosition(); + frameTR = me.frameTR; + if (frameTR) { + + + + frameBR = me.frameBR; + frameTL = me.frameTL; + xPos = (me.getWidth() - frameTR.getPadding('r') - + ((frameTL) ? frameTL.getPadding('l') : me.el.getBorderWidth('l'))) + 'px'; + frameBR.setStyle('background-position-x', xPos); + frameTR.setStyle('background-position-x', xPos); + } + if (Ext.isIE7 && Ext.isStrict && me.frame) { + + + me.el.repaint(); + } + } + }, + + beforeLayout: function () { + this.callParent(); + this.syncBeforeAfterTitleClasses(); + }, + + adjustTitlePosition: function() { + var titleCmp = this.titleCmp, + titleEl; + + if (!Ext.isIE9m && titleCmp) { + + + + + + + + + + + + + + + + titleEl = titleCmp.el; + titleEl.setStyle('left', titleEl.getWidth() + 'px'); + } + }, + + onTitleRender: function() { + if (this.orientation === 'vertical') { + this.titleCmp.el.setVertical(90); + } + }, + + + addUIClsToElement: function(cls) { + var me = this, + result = me.callParent(arguments), + classes = [me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls], + array, i; + + if (me.bodyCls) { + array = me.bodyCls.split(' '); + + for (i = 0; i < classes.length; i++) { + if (!Ext.Array.contains(array, classes[i])) { + array.push(classes[i]); + } + } + + me.bodyCls = array.join(' '); + } else { + me.bodyCls = classes.join(' '); + } + + return result; + }, + + + removeUIClsFromElement: function(cls) { + var me = this, + result = me.callParent(arguments), + classes = [me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls], + array, i; + + if (me.bodyCls) { + array = me.bodyCls.split(' '); + + for (i = 0; i < classes.length; i++) { + Ext.Array.remove(array, classes[i]); + } + + me.bodyCls = array.join(' '); + } + + return result; + }, + + + addUIToElement: function() { + var me = this, + array, cls; + + me.callParent(arguments); + + cls = me.baseCls + '-body-' + me.ui; + if (me.rendered) { + if (me.bodyCls) { + me.body.addCls(me.bodyCls); + } else { + me.body.addCls(cls); + } + } else { + if (me.bodyCls) { + array = me.bodyCls.split(' '); + + if (!Ext.Array.contains(array, cls)) { + array.push(cls); + } + + me.bodyCls = array.join(' '); + } else { + me.bodyCls = cls; + } + } + + if (me.titleCmp && me.titleCmp.rendered) { + me.titleCmp.addCls(me.baseCls + '-text-container-' + me.ui); + } + }, + + + removeUIFromElement: function() { + var me = this, + array, cls; + + me.callParent(arguments); + + cls = me.baseCls + '-body-' + me.ui; + if (me.rendered) { + if (me.bodyCls) { + me.body.removeCls(me.bodyCls); + } else { + me.body.removeCls(cls); + } + } else { + if (me.bodyCls) { + array = me.bodyCls.split(' '); + Ext.Array.remove(array, cls); + me.bodyCls = array.join(' '); + } else { + me.bodyCls = cls; + } + } + + if (me.titleCmp && me.titleCmp.rendered) { + me.titleCmp.removeCls(me.baseCls + '-text-container-' + me.ui); + } + }, + + onClick: function(e) { + this.fireClickEvent('click', e); + }, + + onDblClick: function(e){ + this.fireClickEvent('dblclick', e); + }, + + fireClickEvent: function(type, e){ + var toolCls = '.' + Ext.panel.Tool.prototype.baseCls; + if (!e.getTarget(toolCls)) { + this.fireEvent(type, this, e); + } + }, + + getFocusEl: function() { + return this.el; + }, + + getTargetEl: function() { + return this.body || this.frameBody || this.el; + }, + + applyTargetCls: function(targetCls) { + this.bodyTargetCls = targetCls; + }, + + + setTitle: function(title) { + var me = this, + titleCmp = me.titleCmp; + + me.title = title; + if (titleCmp.rendered) { + titleCmp.textEl.update(me.title || ' '); + titleCmp.updateLayout(); + } else { + me.titleCmp.on({ + render: function() { + me.setTitle(title); + }, + single: true + }); + } + }, + + + getMinWidth: function() { + var me = this, + textEl = me.titleCmp.textEl.dom, + result, + tools = me.tools, + l, i; + + + textEl.style.display = 'inline'; + result = textEl.offsetWidth; + textEl.style.display = ''; + + + if (tools && (l = tools.length)) { + for (i = 0; i < l; i++) { + if (tools[i].el) { + result += tools[i].el.dom.offsetWidth; + } + } + } + + + if (me.iconCmp) { + result += me.iconCmp.el.dom.offsetWidth; + } + + + return result + 10; + }, + + + setIconCls: function(cls) { + var me = this, + isEmpty = !cls || !cls.length, + iconCmp = me.iconCmp; + + me.iconCls = cls; + if (!me.iconCmp && !isEmpty) { + me.initIconCmp(); + me.insert(0, me.iconCmp); + } else if (iconCmp) { + if (isEmpty) { + me.iconCmp.destroy(); + delete me.iconCmp; + } else { + iconCmp.removeCls(iconCmp.iconCls); + iconCmp.addCls(cls); + iconCmp.iconCls = cls; + } + } + }, + + + setIcon: function(icon) { + var me = this, + isEmpty = !icon || !icon.length, + iconCmp = me.iconCmp; + + me.icon = icon; + if (!me.iconCmp && !isEmpty) { + me.initIconCmp(); + me.insert(0, me.iconCmp); + } else if (iconCmp) { + if (isEmpty) { + me.iconCmp.destroy(); + delete me.iconCmp; + } else { + iconCmp.setSrc(me.icon); + } + } + }, + + + setGlyph: function(glyph) { + var me = this, + iconCmp = me.iconCmp; + + if (!me.iconCmp) { + me.initIconCmp(); + me.insert(0, me.iconCmp); + } else if (iconCmp) { + if (glyph) { + me.iconCmp.setGlyph(glyph); + } else { + me.iconCmp.destroy(); + delete me.iconCmp; + } + } + }, + + + getTools: function(){ + return this.tools.slice(); + }, + + + addTool: function(tool) { + + + this.add(Ext.ComponentManager.create(tool, 'tool')); + }, + + syncBeforeAfterTitleClasses: function(force) { + var me = this, + items = me.items, + childItems = items.items, + titlePosition = me.titlePosition, + itemCount = childItems.length, + itemGeneration = items.generation, + syncGen = me.syncBeforeAfterGen, + afterCls, beforeCls, i, item; + + if (!force && (syncGen === itemGeneration)) { + return; + } + me.syncBeforeAfterGen = itemGeneration; + + for (i = 0; i < itemCount; ++i) { + item = childItems[i]; + + afterCls = item.afterTitleCls || (item.afterTitleCls = item.baseCls + '-after-title') + beforeCls = item.beforeTitleCls || (item.beforeTitleCls = item.baseCls + '-before-title') + + if (!me.title || i < titlePosition) { + if (syncGen) { + item.removeCls(afterCls); + } + item.addCls(beforeCls); + } else if (i > titlePosition) { + if (syncGen) { + item.removeCls(beforeCls); + } + item.addCls(afterCls); + } + } + }, + + + onAdd: function(component, index) { + var tools = this.tools; + this.callParent(arguments); + if (component.isTool) { + tools.push(component); + tools[component.type] = component; + } + }, + + + initRenderData: function() { + return Ext.applyIf(this.callParent(), { + bodyCls: this.bodyCls, + bodyTargetCls: this.bodyTargetCls, + headerCls: this.headerCls + }); + }, + + getDockName: function() { + return this.dock; + }, + + getFramingInfoCls: function(){ + var me = this, + cls = me.callParent(), + owner = me.ownerCt; + + if (!me.expanding && owner && (owner.collapsed || me.isCollapsedExpander)) { + cls += '-' + owner.collapsedCls; + } + return cls + '-' + me.dock; + } +}); + + + + + +Ext.define('Ext.dd.DDProxy', { + extend: Ext.dd.DD , + + statics: { + + dragElId: "ygddfdiv" + }, + + + constructor: function(id, sGroup, config) { + if (id) { + this.init(id, sGroup, config); + this.initFrame(); + } + }, + + + resizeFrame: true, + + + centerFrame: false, + + + createFrame: function() { + var self = this, + body = document.body, + div, + s; + + if (!body || !body.firstChild) { + setTimeout( function() { self.createFrame(); }, 50 ); + return; + } + + div = this.getDragEl(); + + if (!div) { + div = document.createElement("div"); + div.id = this.dragElId; + div.setAttribute('role', 'presentation'); + s = div.style; + + s.position = "absolute"; + s.visibility = "hidden"; + s.cursor = "move"; + s.border = "2px solid #aaa"; + s.zIndex = 999; + + + + + body.insertBefore(div, body.firstChild); + } + }, + + + initFrame: function() { + this.createFrame(); + }, + + applyConfig: function() { + this.callParent(); + + this.resizeFrame = (this.config.resizeFrame !== false); + this.centerFrame = (this.config.centerFrame); + this.setDragElId(this.config.dragElId || Ext.dd.DDProxy.dragElId); + }, + + + showFrame: function(iPageX, iPageY) { + var el = this.getEl(), + dragEl = this.getDragEl(), + s = dragEl.style; + + this._resizeProxy(); + + if (this.centerFrame) { + this.setDelta( Math.round(parseInt(s.width, 10)/2), + Math.round(parseInt(s.height, 10)/2) ); + } + + this.setDragElPos(iPageX, iPageY); + + Ext.fly(dragEl).show(); + }, + + + _resizeProxy: function() { + if (this.resizeFrame) { + var el = this.getEl(); + Ext.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight); + } + }, + + + b4MouseDown: function(e) { + var x = e.getPageX(), + y = e.getPageY(); + this.autoOffset(x, y); + this.setDragElPos(x, y); + }, + + + b4StartDrag: function(x, y) { + + this.showFrame(x, y); + }, + + + b4EndDrag: function(e) { + Ext.fly(this.getDragEl()).hide(); + }, + + + + + endDrag: function(e) { + + var lel = this.getEl(), + del = this.getDragEl(); + + + del.style.visibility = ""; + + this.beforeMove(); + + + lel.style.visibility = "hidden"; + Ext.dd.DDM.moveToEl(lel, del); + del.style.visibility = "hidden"; + lel.style.visibility = ""; + + this.afterDrag(); + }, + + beforeMove : function(){ + + }, + + afterDrag : function(){ + + }, + + toString: function() { + return ("DDProxy " + this.id); + } + +}); + + + +Ext.define('Ext.dd.StatusProxy', { + extend: Ext.Component , + animRepair: false, + + childEls: [ + 'ghost' + ], + + renderTpl: [ + '' + + '' + ], + + repairCls: Ext.baseCSSPrefix + 'dd-drag-repair', + + ariaRole: 'presentation', + + + constructor: function(config) { + var me = this; + + config = config || {}; + + Ext.apply(me, { + hideMode: 'visibility', + hidden: true, + floating: true, + id: me.id || Ext.id(), + cls: Ext.baseCSSPrefix + 'dd-drag-proxy ' + this.dropNotAllowed, + shadow: config.shadow || false, + renderTo: Ext.getDetachedBody() + }); + me.callParent(arguments); + this.dropStatus = this.dropNotAllowed; + }, + + + dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok', + + + dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop', + + + setStatus : function(cssClass){ + cssClass = cssClass || this.dropNotAllowed; + if (this.dropStatus != cssClass) { + this.el.replaceCls(this.dropStatus, cssClass); + this.dropStatus = cssClass; + } + }, + + + reset : function(clearGhost){ + var me = this, + clsPrefix = Ext.baseCSSPrefix + 'dd-drag-proxy '; + + me.el.replaceCls(clsPrefix + me.dropAllowed, clsPrefix + me.dropNotAllowed); + me.dropStatus = me.dropNotAllowed; + if (clearGhost) { + me.ghost.update(''); + } + }, + + + update : function(html){ + if (typeof html == "string") { + this.ghost.update(html); + } else { + this.ghost.update(""); + html.style.margin = "0"; + this.ghost.dom.appendChild(html); + } + var el = this.ghost.dom.firstChild; + if (el) { + Ext.fly(el).setStyle('float', 'none'); + } + }, + + + getGhost : function(){ + return this.ghost; + }, + + + hide : function(clear) { + this.callParent(); + if (clear) { + this.reset(true); + } + }, + + + stop : function(){ + if (this.anim && this.anim.isAnimated && this.anim.isAnimated()) { + this.anim.stop(); + } + }, + + + sync : function(){ + this.el.sync(); + }, + + + repair : function(xy, callback, scope) { + var me = this; + + me.callback = callback; + me.scope = scope; + if (xy && me.animRepair !== false) { + me.el.addCls(me.repairCls); + me.el.hideUnders(true); + me.anim = me.el.animate({ + duration: me.repairDuration || 500, + easing: 'ease-out', + to: { + x: xy[0], + y: xy[1] + }, + stopAnimation: true, + callback: me.afterRepair, + scope: me + }); + } else { + me.afterRepair(); + } + }, + + + afterRepair : function() { + var me = this; + + me.hide(true); + me.el.removeCls(me.repairCls); + if (typeof me.callback == "function") { + me.callback.call(me.scope || me); + } + delete me.callback; + delete me.scope; + } +}); + + + +Ext.define('Ext.dd.DragSource', { + extend: Ext.dd.DDProxy , + + + + + + + + + + + dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok', + + dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop', + + + animRepair: true, + + + repairHighlightColor: 'c3daf9', + + + constructor: function(el, config) { + this.el = Ext.get(el); + if(!this.dragData){ + this.dragData = {}; + } + + Ext.apply(this, config); + + if(!this.proxy){ + this.proxy = new Ext.dd.StatusProxy({ + id: this.el.id + '-drag-status-proxy', + animRepair: this.animRepair + }); + } + this.callParent([this.el.dom, this.ddGroup || this.group, + {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true}]); + + this.dragging = false; + }, + + + getDragData : function(e){ + return this.dragData; + }, + + + onDragEnter : function(e, id){ + var target = Ext.dd.DragDropManager.getDDById(id), + status; + this.cachedTarget = target; + if (this.beforeDragEnter(target, e, id) !== false) { + if (target.isNotifyTarget) { + status = target.notifyEnter(this, e, this.dragData); + this.proxy.setStatus(status); + } else { + this.proxy.setStatus(this.dropAllowed); + } + + if (this.afterDragEnter) { + + this.afterDragEnter(target, e, id); + } + } + }, + + + beforeDragEnter: function(target, e, id) { + return true; + }, + + + onDragOver: function(e, id) { + var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id), + status; + if (this.beforeDragOver(target, e, id) !== false) { + if(target.isNotifyTarget){ + status = target.notifyOver(this, e, this.dragData); + this.proxy.setStatus(status); + } + + if (this.afterDragOver) { + + this.afterDragOver(target, e, id); + } + } + }, + + + beforeDragOver: function(target, e, id) { + return true; + }, + + + onDragOut: function(e, id) { + var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id); + if (this.beforeDragOut(target, e, id) !== false) { + if (target.isNotifyTarget) { + target.notifyOut(this, e, this.dragData); + } + this.proxy.reset(); + if (this.afterDragOut) { + + this.afterDragOut(target, e, id); + } + } + this.cachedTarget = null; + }, + + + beforeDragOut: function(target, e, id){ + return true; + }, + + + onDragDrop: function(e, id){ + var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id); + if (this.beforeDragDrop(target, e, id) !== false) { + if (target.isNotifyTarget) { + if (target.notifyDrop(this, e, this.dragData) !== false) { + this.onValidDrop(target, e, id); + } else { + this.onInvalidDrop(target, e, id); + } + } else { + this.onValidDrop(target, e, id); + } + + if (this.afterDragDrop) { + + this.afterDragDrop(target, e, id); + } + } + delete this.cachedTarget; + }, + + + beforeDragDrop: function(target, e, id){ + return true; + }, + + + onValidDrop: function(target, e, id){ + this.hideProxy(); + if(this.afterValidDrop){ + + this.afterValidDrop(target, e, id); + } + }, + + + getRepairXY: function(e, data){ + return this.el.getXY(); + }, + + + onInvalidDrop: function(target, e, id) { + + + + var me = this; + + if (!e) { + e = target; + target = null; + id = e.getTarget().id; + } + if (me.beforeInvalidDrop(target, e, id) !== false) { + if (me.cachedTarget) { + if(me.cachedTarget.isNotifyTarget){ + me.cachedTarget.notifyOut(me, e, me.dragData); + } + me.cacheTarget = null; + } + me.proxy.repair(me.getRepairXY(e, me.dragData), me.afterRepair, me); + + if (me.afterInvalidDrop) { + + me.afterInvalidDrop(e, id); + } + } + }, + + + afterRepair: function() { + var me = this; + if (Ext.enableFx) { + me.el.highlight(me.repairHighlightColor); + } + me.dragging = false; + }, + + + beforeInvalidDrop: function(target, e, id) { + return true; + }, + + + handleMouseDown: function(e) { + if (this.dragging) { + return; + } + var data = this.getDragData(e); + if (data && this.onBeforeDrag(data, e) !== false) { + this.dragData = data; + this.proxy.stop(); + this.callParent(arguments); + } + }, + + + onBeforeDrag: function(data, e){ + return true; + }, + + + onStartDrag: Ext.emptyFn, + + alignElWithMouse: function() { + this.proxy.ensureAttachedToBody(true); + return this.callParent(arguments); + }, + + + startDrag: function(x, y) { + this.proxy.reset(); + this.proxy.hidden = false; + this.dragging = true; + this.proxy.update(""); + this.onInitDrag(x, y); + this.proxy.show(); + }, + + + onInitDrag: function(x, y) { + var clone = this.el.dom.cloneNode(true); + clone.id = Ext.id(); + this.proxy.update(clone); + this.onStartDrag(x, y); + 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(e) { + }, + + + endDrag : function(e){ + this.onEndDrag(this.dragData, e); + }, + + + onEndDrag : function(data, e){ + }, + + + autoOffset : function(x, y) { + this.setDelta(-12, -20); + }, + + destroy: function(){ + this.callParent(); + Ext.destroy(this.proxy); + } +}); + + + +Ext.define('Ext.panel.Proxy', { + + alternateClassName: 'Ext.dd.PanelProxy', + + + moveOnDrag: true, + + + constructor: function(panel, config){ + var me = this; + + + me.panel = panel; + me.id = me.panel.id +'-ddproxy'; + Ext.apply(me, config); + }, + + + 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 me = this; + + if (me.ghost) { + if (me.proxy) { + me.proxy.remove(); + delete me.proxy; + } + + + me.panel.unghost(null, me.moveOnDrag); + delete me.ghost; + } + }, + + + show: function(){ + var me = this, + panelSize; + + if (!me.ghost) { + panelSize = me.panel.getSize(); + me.panel.el.setVisibilityMode(Ext.Element.DISPLAY); + me.ghost = me.panel.ghost(); + if (me.insertProxy) { + + + me.proxy = me.panel.el.insertSibling({ + role: 'presentation', + cls: Ext.baseCSSPrefix + 'panel-dd-spacer' + }); + me.proxy.setSize(panelSize); + } + } + }, + + + repair: function(xy, callback, scope) { + this.hide(); + Ext.callback(callback, scope || this); + }, + + + moveProxy : function(parentNode, before){ + if (this.proxy) { + parentNode.insertBefore(this.proxy.dom, before); + } + } +}); + + + +Ext.define('Ext.panel.DD', { + extend: Ext.dd.DragSource , + + + constructor : function(panel, cfg){ + var me = this; + + me.panel = panel; + me.dragData = {panel: panel}; + me.panelProxy = new Ext.panel.Proxy(panel, cfg); + me.proxy = me.panelProxy.proxy; + + me.callParent([panel.el, cfg]); + me.setupEl(panel); + }, + + setupEl: function(panel){ + var me = this, + header = panel.header, + el = panel.body; + + if (header) { + me.setHandleElId(header.id); + el = header.el; + } + if (el) { + el.setStyle('cursor', 'move'); + me.scroll = false; + } else { + + panel.on('boxready', me.setupEl, me, {single: true}); + } + }, + + showFrame: Ext.emptyFn, + startDrag: Ext.emptyFn, + + b4StartDrag: function(x, y) { + this.panelProxy.show(); + }, + + b4MouseDown: function(e) { + var x = e.getPageX(), + y = e.getPageY(); + + this.autoOffset(x, y); + }, + + onInitDrag : function(x, y){ + this.onStartDrag(x, y); + return true; + }, + + createFrame : Ext.emptyFn, + + getDragEl : function(e){ + var ghost = this.panelProxy.ghost; + if (ghost) { + return ghost.el.dom; + } + }, + + endDrag : function(e){ + this.panelProxy.hide(); + this.panel.saveState(); + }, + + autoOffset : function(x, y) { + x -= this.startPageX; + y -= this.startPageY; + this.setDelta(x, y); + }, + + + + onInvalidDrop: function(target, e, id) { + var me = this; + + if (me.beforeInvalidDrop(target, e, id) !== false) { + if (me.cachedTarget) { + if(me.cachedTarget.isNotifyTarget){ + me.cachedTarget.notifyOut(me, e, me.dragData); + } + me.cacheTarget = null; + } + + if (me.afterInvalidDrop) { + + me.afterInvalidDrop(e, id); + } + } + } +}); + + + +Ext.define('Ext.util.Memento', (function () { + + function captureOne (src, target, prop, prefix) { + src[prefix ? prefix + prop : prop] = target[prop]; + } + + function removeOne (src, target, prop) { + delete src[prop]; + } + + function restoreOne (src, target, prop, prefix) { + var name = prefix ? prefix + prop : prop, + value = src[name]; + + if (value || src.hasOwnProperty(name)) { + restoreValue(target, prop, value); + } + } + + function restoreValue (target, prop, value) { + if (Ext.isDefined(value)) { + target[prop] = value; + } else { + delete target[prop]; + } + } + + function doMany (doOne, src, target, props, prefix) { + if (src) { + if (Ext.isArray(props)) { + var p, pLen = props.length; + for (p = 0; p < pLen; p++) { + doOne(src, target, props[p], prefix); + } + } else { + doOne(src, target, props, prefix); + } + } + } + + return { + + data: null, + + + target: null, + + + constructor: function (target, props) { + if (target) { + this.target = target; + if (props) { + this.capture(props); + } + } + }, + + + capture: function (props, target, prefix) { + var me = this; + doMany(captureOne, me.data || (me.data = {}), target || me.target, props, prefix); + }, + + + remove: function (props) { + doMany(removeOne, this.data, null, props); + }, + + + restore: function (props, clear, target, prefix) { + doMany(restoreOne, this.data, target || this.target, props, prefix); + if (clear !== false) { + this.remove(props); + } + }, + + + restoreAll: function (clear, target) { + var me = this, + t = target || this.target, + data = me.data, + prop; + + for (prop in data) { + if (data.hasOwnProperty(prop)) { + restoreValue(t, prop, data[prop]); + } + } + + if (clear !== false) { + delete me.data; + } + } + }; +}())); + + + +Ext.define('Ext.layout.component.Body', { + + + + alias: ['layout.body'], + + extend: Ext.layout.component.Auto , + + + + 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); + } + }, + + + + + + + + + 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 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); + } +}); + + + +Ext.define('Ext.panel.Panel', { + extend: Ext.panel.AbstractPanel , + + + + + + + + + + alias: 'widget.panel', + alternateClassName: 'Ext.Panel', + + + collapsedCls: 'collapsed', + + + animCollapse: Ext.enableFx, + + + minButtonWidth: 75, + + + collapsed: false, + + + collapseFirst: true, + + + hideCollapseTool: false, + + + titleCollapse: undefined, + + + + + + + floatable: true, + + + + + collapsible: undefined, + + + + + closable: false, + + + closeAction: 'destroy', + + + + + placeholderCollapseHideMode: Ext.Element.VISIBILITY, + + + preventHeader: false, + + + header: undefined, + + + headerPosition: 'top', + + + frame: false, + + + frameHeader: true, + + + + + + + + + manageHeight: true, + + + + + + + + + + + constrain: false, + + + constrainHeader: false, + + + + + maskElement: 'el', + + initComponent: function() { + var me = this; + + me.addEvents( + + + 'beforeclose', + + + 'close', + + + "beforeexpand", + + + "beforecollapse", + + + "expand", + + + "collapse", + + + 'titlechange', + + + 'iconchange', + + + 'iconclschange', + + + 'glyphchange', + + + 'float', + + + 'unfloat' + ); + + if (me.collapsible) { + + me.addStateEvents(['expand', 'collapse']); + } + if (me.unstyled) { + me.setUI('plain'); + } + + if (me.frame) { + me.setUI(me.ui + '-framed'); + } + + + me.bridgeToolbars(); + + me.callParent(); + me.collapseDirection = me.collapseDirection || me.headerPosition || Ext.Component.DIRECTION_TOP; + + + me.hiddenOnCollapse = new Ext.dom.CompositeElement(); + + }, + + beforeDestroy: function() { + var me = this; + Ext.destroy( + me.placeholder, + me.ghostPanel, + me.dd + ); + me.callParent(); + }, + + getFocusEl: function() { + return this.el; + }, + + + getHeader: function() { + return this.header; + }, + + + setTitle: function(newTitle) { + var me = this, + oldTitle = me.title, + header = me.header, + reExpander = me.reExpander, + placeholder = me.placeholder; + + me.title = newTitle; + + if (header) { + if (header.isHeader) { + header.setTitle(newTitle); + } else { + header.title = newTitle; + } + } else if (me.rendered) { + me.updateHeader(); + } + + if (reExpander) { + reExpander.setTitle(newTitle); + } + + if (placeholder && placeholder.setTitle) { + placeholder.setTitle(newTitle); + } + + me.fireEvent('titlechange', me, newTitle, oldTitle); + }, + + + setIconCls: function(newIconCls) { + var me = this, + oldIconCls = me.iconCls, + header = me.header, + placeholder = me.placeholder; + + me.iconCls = newIconCls; + + if (header) { + if (header.isHeader) { + header.setIconCls(newIconCls); + } else { + header.iconCls = newIconCls; + } + } else { + me.updateHeader(); + } + + if (placeholder && placeholder.setIconCls) { + placeholder.setIconCls(newIconCls); + } + + me.fireEvent('iconclschange', me, newIconCls, oldIconCls); + }, + + + setIcon: function(newIcon) { + var me = this, + oldIcon = me.icon, + header = me.header, + placeholder = me.placeholder; + + me.icon = newIcon; + + if (header) { + if (header.isHeader) { + header.setIcon(newIcon); + } else { + header.icon = newIcon; + } + } else { + me.updateHeader(); + } + + if (placeholder && placeholder.setIcon) { + placeholder.setIcon(newIcon); + } + + me.fireEvent('iconchange', me, newIcon, oldIcon); + }, + + + setGlyph: function(newGlyph) { + var me = this, + oldGlyph = me.glyph, + header = me.header, + placeholder = me.placeholder; + + me.glyph = newGlyph; + + if (header) { + if (header.isHeader) { + header.setGlyph(newGlyph); + } else { + header.glyph = newGlyph; + } + } else { + me.updateHeader(); + } + + if (placeholder && placeholder.setGlyph) { + placeholder.setIcon(newGlyph); + } + + me.fireEvent('glyphchange', me, newGlyph, oldGlyph); + }, + + bridgeToolbars: function() { + var me = this, + docked = [], + minButtonWidth = me.minButtonWidth, + fbar, fbarDefaults; + + function initToolbar (toolbar, pos, useButtonAlign) { + if (Ext.isArray(toolbar)) { + toolbar = { + xtype: 'toolbar', + items: toolbar + }; + } + else if (!toolbar.xtype) { + toolbar.xtype = 'toolbar'; + } + toolbar.dock = pos; + if (pos == 'left' || pos == 'right') { + toolbar.vertical = true; + } + + + if (useButtonAlign) { + toolbar.layout = Ext.applyIf(toolbar.layout || {}, { + + pack: { left:'start', center:'center' }[me.buttonAlign] || 'end' + }); + } + return toolbar; + } + + + + + + + if (me.tbar) { + docked.push(initToolbar(me.tbar, 'top')); + me.tbar = null; + } + + + if (me.bbar) { + docked.push(initToolbar(me.bbar, 'bottom')); + me.bbar = null; + } + + + if (me.buttons) { + me.fbar = me.buttons; + me.buttons = null; + } + + + if (me.fbar) { + fbar = initToolbar(me.fbar, 'bottom', true); + fbar.ui = 'footer'; + + + if (minButtonWidth) { + fbarDefaults = fbar.defaults; + fbar.defaults = function(config) { + var defaults = fbarDefaults || {}, + + isButton = !config.xtype || config.isButton, + cls; + + + + if (!isButton) { + cls = Ext.ClassManager.getByAlias('widget.' + config.xtype); + if (cls) { + isButton = cls.prototype.isButton; + } + } + if (isButton && !('minWidth' in defaults)) { + defaults = Ext.apply({minWidth: minButtonWidth}, defaults); + } + return defaults; + }; + } + + docked.push(fbar); + me.fbar = null; + } + + + if (me.lbar) { + docked.push(initToolbar(me.lbar, 'left')); + me.lbar = null; + } + + + if (me.rbar) { + docked.push(initToolbar(me.rbar, 'right')); + me.rbar = null; + } + + if (me.dockedItems) { + if (!Ext.isArray(me.dockedItems)) { + me.dockedItems = [me.dockedItems]; + } + me.dockedItems = me.dockedItems.concat(docked); + } else { + me.dockedItems = docked; + } + }, + + isPlaceHolderCollapse: function(){ + return this.collapseMode == 'placeholder'; + }, + + onBoxReady: function(){ + this.callParent(arguments); + if (this.collapsed) { + this.setHiddenDocked(); + } + }, + + beforeRender: function() { + var me = this, + wasCollapsed; + + me.callParent(); + + + + me.initTools(); + + + if (!(me.preventHeader || (me.header === false))) { + me.updateHeader(); + } + + + if (me.collapsed) { + if (me.isPlaceHolderCollapse()) { + if (!me.hidden) { + me.setHiddenState(true); + + + + + + me.preventCollapseFire = true; + me.placeholderCollapse(); + delete me.preventCollapseFire; + wasCollapsed = me.collapsed; + + + + + me.collapsed = false; + } + } else { + me.beginCollapse(); + me.addClsWithUI(me.collapsedCls); + } + } + + + if (wasCollapsed) { + me.collapsed = wasCollapsed; + } + }, + + + initTools: function() { + var me = this, + tools = me.tools, + i, tool; + + me.tools = []; + for (i = tools && tools.length; i; ) { + --i; + me.tools[i] = tool = tools[i]; + tool.toolOwner = me; + } + + + + if (me.collapsible && !(me.hideCollapseTool || me.header === false || me.preventHeader)) { + me.collapseDirection = me.collapseDirection || me.headerPosition || 'top'; + me.collapseTool = me.expandTool = Ext.widget({ + xtype: 'tool', + handler: me.toggleCollapse, + scope: me + }); + + me.updateCollapseTool(); + + if (me.collapseFirst) { + me.tools.unshift(me.collapseTool); + } + } + + + me.addTools(); + + + if (me.closable) { + me.addClsWithUI('closable'); + me.addTool({ + xtype : 'tool', + type: 'close', + scope: me, + handler: me.close + }); + } + + + if (me.collapseTool && !me.collapseFirst) { + me.addTool(me.collapseTool); + } + }, + + + addTools: Ext.emptyFn, + + updateCollapseTool: function () { + var me = this, + collapseTool = me.collapseTool; + + if (collapseTool) { + if (me.collapsed && !me.isPlaceHolderCollapse()) { + collapseTool.setType('expand-' + me.getOppositeDirection(me.collapseDirection)); + } else { + collapseTool.setType('collapse-' + me.collapseDirection); + } + } + }, + + + close: function() { + if (this.fireEvent('beforeclose', this) !== false) { + this.doClose(); + } + }, + + + doClose: function() { + this.fireEvent('close', this); + this[this.closeAction](); + }, + + + updateHeader: function(force) { + var me = this, + header = me.header, + title = me.title, + tools = me.tools, + icon = me.icon || me.iconCls, + vertical = me.headerPosition === 'left' || me.headerPosition === 'right'; + + if (Ext.isObject(header) || (header !== false && (force || (title || icon) || (tools && tools.length) || (me.collapsible && !me.titleCollapse)))) { + if (header && header.isHeader) { + header.show(); + } else { + + header = me.header = Ext.widget(Ext.apply({ + xtype : 'header', + title : title, + titleAlign : me.titleAlign, + orientation : vertical ? 'vertical' : 'horizontal', + dock : me.headerPosition || 'top', + textCls : me.headerTextCls, + iconCls : me.iconCls, + icon : me.icon, + glyph : me.glyph, + baseCls : me.baseCls + '-header', + tools : tools, + ui : me.ui, + id : me.id + '_header', + overCls: me.headerOverCls, + indicateDrag: me.draggable, + frame : (me.frame || me.alwaysFramed) && me.frameHeader, + ignoreParentFrame : me.frame || me.overlapHeader, + ignoreBorderManagement: me.frame || me.ignoreHeaderBorderManagement, + headerRole : me.headerRole, + ownerCt : me, + listeners : me.collapsible && me.titleCollapse ? { + click: me.toggleCollapse, + scope: me + } : null + }, me.header)); + + + + me.addDocked(header, 0); + } + } else if (header) { + header.hide(); + } + }, + + + setUI: function(ui) { + var me = this; + + me.callParent(arguments); + + if (me.header && me.header.rendered) { + me.header.setUI(ui); + } + }, + + + getDefaultContentTarget: function() { + return this.body; + }, + + getTargetEl: function() { + var me = this; + return me.body || me.protoBody || me.frameBody || me.el; + }, + + + + + isVisible: function(deep){ + var me = this; + if (me.collapsed && me.placeholder) { + return me.placeholder.isVisible(deep); + } + return me.callParent(arguments); + }, + + + onHide: function() { + var me = this, + dd = me.dd; + + if (me.floatedFromCollapse) { + me.slideOutFloatedPanel(true); + } + + if (me.draggable && dd) { + + dd.endDrag(); + } + + if (me.collapsed && me.placeholder) { + me.placeholder.hide(); + } else { + me.callParent(arguments); + } + }, + + + onShow: function() { + var me = this; + if (me.collapsed && me.isPlaceHolderCollapse()) { + + me.setHiddenState(true); + me.placeholderCollapse(); + } else { + me.callParent(arguments); + } + }, + + onRemoved: function(destroying) { + var me = this; + + + + + if (me.placeholder && !destroying) { + me.ownerCt.remove(me.placeholder, false); + } + + me.callParent(arguments); + }, + + + addTool: function(tools) { + if (!Ext.isArray(tools)) { + tools = [tools]; + } + + var me = this, + header = me.header, + t, + tLen = tools.length, + tool; + + for (t = 0; t < tLen; t++) { + tool = tools[t]; + tool.toolOwner = me; + + if (header && header.isHeader) { + header.addTool(tool); + } else { + + + me.tools.push(tool); + } + } + + me.updateHeader(); + }, + + getOppositeDirection: function(d) { + var c = Ext.Component; + switch (d) { + case c.DIRECTION_TOP: + return c.DIRECTION_BOTTOM; + case c.DIRECTION_RIGHT: + return c.DIRECTION_LEFT; + case c.DIRECTION_BOTTOM: + return c.DIRECTION_TOP; + case c.DIRECTION_LEFT: + return c.DIRECTION_RIGHT; + } + }, + + getWidthAuthority: function() { + if (this.collapsed && this.collapsedHorizontal()) { + return 1; + } + + return this.callParent(); + }, + + getHeightAuthority: function() { + if (this.collapsed && this.collapsedVertical()) { + return 1; + } + + return this.callParent(); + }, + + collapsedHorizontal: function () { + var dir = this.getCollapsed(); + return dir === 'left' || dir === 'right'; + }, + + collapsedVertical: function () { + var dir = this.getCollapsed(); + return dir === 'top' || dir === 'bottom'; + }, + + restoreDimension: function(){ + var dir = this.collapseDirection; + + + return (dir === 'top' || dir === 'bottom') ? 'height' : 'width'; + }, + + + getCollapsed: function() { + var me = this; + + + if (me.collapsed === true) { + return me.collapseDirection; + } + return me.collapsed; + }, + + getState: function() { + var me = this, + state = me.callParent(), + memento; + + state = me.addPropertyToState(state, 'collapsed'); + + + if (me.collapsed) { + memento = me.collapseMemento; + memento = memento && memento.data; + + if (me.collapsedVertical()) { + if (state) { + delete state.height; + } + if (memento) { + state = me.addPropertyToState(state, 'height', memento.height); + } + } else { + if (state) { + delete state.width; + } + if (memento) { + state = me.addPropertyToState(state, 'width', memento.width); + } + } + } + + return state; + }, + + findReExpander: function (direction) { + var me = this, + c = Ext.Component, + dockedItems = me.dockedItems.items, + dockedItemCount = dockedItems.length, + comp, i; + + + if (me.collapseMode === 'mini') { + return; + } + + switch (direction) { + case c.DIRECTION_TOP: + case c.DIRECTION_BOTTOM: + + + + for (i = 0; i < dockedItemCount; i++) { + comp = dockedItems[i]; + if (!comp.hidden) { + if (comp.isHeader && (!comp.dock || comp.dock === 'top' || comp.dock === 'bottom')) { + return comp; + } + } + } + break; + + case c.DIRECTION_LEFT: + case c.DIRECTION_RIGHT: + + + + for (i = 0; i < dockedItemCount; i++) { + comp = dockedItems[i]; + if (!comp.hidden) { + if (comp.isHeader && (comp.dock === 'left' || comp.dock === 'right')) { + return comp; + } + } + } + break; + + default: + throw('Panel#findReExpander must be passed a valid collapseDirection'); + } + }, + + getReExpander: function (direction) { + var me = this, + collapseDir = direction || me.collapseDirection, + reExpander = me.reExpander || me.findReExpander(collapseDir); + + me.expandDirection = me.getOppositeDirection(collapseDir); + + if (!reExpander) { + + me.reExpander = reExpander = me.createReExpander(collapseDir, { + dock: collapseDir, + cls: Ext.baseCSSPrefix + 'docked ' + me.baseCls + '-' + me.ui + '-collapsed', + isCollapsedExpander: true + }); + + me.dockedItems.insert(0, reExpander); + } + return reExpander; + }, + + createReExpander: function(direction, defaults) { + var me = this, + isLeft = direction === 'left', + isRight = direction === 'right', + isVertical = isLeft || isRight, + ownerCt = me.ownerCt, + result = Ext.apply({ + hideMode: 'offsets', + title: me.title || ' ', + titleAlign: me.titleAlign, + orientation: isVertical ? 'vertical' : 'horizontal', + textCls: me.headerTextCls, + icon: me.icon, + iconCls: me.iconCls, + glyph: me.glyph, + baseCls: me.self.prototype.baseCls + '-header', + ui: me.ui, + frame: me.frame && me.frameHeader, + ignoreParentFrame: me.frame || me.overlapHeader, + ignoreBorderManagement: me.frame || me.ignoreHeaderBorderManagement, + indicateDrag: me.draggable, + collapseImmune: true, + headerRole: me.headerRole, + ownerCt: (ownerCt && me.collapseMode === 'placeholder') ? ownerCt : me, + ownerLayout: me.componentLayout, + margin: me.margin + }, defaults); + + + + if (me.collapseMode === 'mini') { + if (isVertical) { + result.width = 1; + } else { + result.height = 1; + } + } + + + + + if (!me.hideCollapseTool) { + if (isLeft || (isRight && me.isPlaceHolderCollapse())) { + + + result.titlePosition = 1; + } + result.tools = [{ + xtype: 'tool', + type: 'expand-' + me.getOppositeDirection(direction), + uiCls: ['top'], + handler: me.toggleCollapse, + scope: me + }]; + } + result = new Ext.panel.Header(result); + result.addClsWithUI(me.getHeaderCollapsedClasses(result)); + return result; + }, + + + + getHeaderCollapsedClasses: function(header) { + var me = this, + collapsedCls = me.collapsedCls, + collapsedClasses; + + collapsedClasses = [ collapsedCls, collapsedCls + '-' + header.getDockName()]; + if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) { + collapsedClasses.push(collapsedCls + '-border-' + header.getDockName()); + } + return collapsedClasses; + }, + + + beginCollapse: function() { + var me = this, + lastBox = me.lastBox, + rendered = me.rendered, + collapseMemento = me.collapseMemento || (me.collapseMemento = new Ext.util.Memento(me)), + sizeModel = me.getSizeModel(), + header = me.header, + reExpander; + + + + + + + + + + + + + collapseMemento.capture(['height', 'minHeight', 'width', 'minWidth']); + if (lastBox) { + collapseMemento.capture(me.restoreDimension(), lastBox, 'last.'); + } + + + + + + if (me.collapsedVertical()) { + if (sizeModel.width.shrinkWrap) { + me.width = rendered ? me.getWidth() : me.width || me.minWidth || 100; + } + delete me.height; + me.minHeight = 0; + } else if (me.collapsedHorizontal()) { + if (sizeModel.height.shrinkWrap) { + me.height = rendered ? me.getHeight() : me.height || me.minHeight || 100; + } + delete me.width; + me.minWidth = 0; + } + + if (me.ownerCt) { + me.ownerCt.getLayout().beginCollapse(me); + } + + + + if (!me.isPlaceHolderCollapse() && header !== false) { + if (header === (reExpander = me.getReExpander())) { + header.collapseImmune = true; + header.getHierarchyState().collapseImmune = true; + header.addClsWithUI(me.getHeaderCollapsedClasses(header)); + + + if (header.rendered) { + header.updateFrame(); + } + } else if (reExpander.el) { + + reExpander.el.show(); + reExpander.hidden = false; + } + } + if (me.resizer) { + me.resizer.disable(); + } + }, + + beginExpand: function() { + var me = this, + lastBox = me.lastBox, + collapseMemento = me.collapseMemento, + restoreDimension = me.restoreDimension(), + header = me.header, + reExpander; + + if (collapseMemento) { + collapseMemento.restore(['minHeight', 'minWidth', restoreDimension]); + if (lastBox) { + collapseMemento.restore(restoreDimension, true, lastBox, 'last.'); + } + } + + if (me.ownerCt) { + me.ownerCt.getLayout().beginExpand(me); + } + + if (!me.isPlaceHolderCollapse() && header !== false) { + + if (header === (reExpander = me.getReExpander())) { + delete header.collapseImmune; + delete header.getHierarchyState().collapseImmune; + header.removeClsWithUI(me.getHeaderCollapsedClasses(header)); + + + if (header.rendered) { + header.expanding = true; + header.updateFrame(); + delete header.expanding; + } + } else { + + reExpander.hidden = true; + reExpander.el.hide(); + } + } + if (me.resizer) { + me.resizer.enable(); + } + }, + + + collapse: function(direction, animate) { + var me = this, + collapseDir = direction || me.collapseDirection, + ownerCt = me.ownerCt, + layout = me.ownerLayout; + + if (me.isCollapsingOrExpanding) { + return me; + } + + if (arguments.length < 2) { + animate = me.animCollapse; + } + + if (me.collapsed || me.fireEvent('beforecollapse', me, direction, animate) === false) { + return me; + } + + if (layout && layout.onBeforeComponentCollapse) { + if (layout.onBeforeComponentCollapse(me) === false) { + return me; + } + } + + if (ownerCt && me.isPlaceHolderCollapse()) { + return me.placeholderCollapse(direction, animate); + } + + me.collapsed = collapseDir; + me.beginCollapse(); + + me.getHierarchyState().collapsed = true; + me.fireHierarchyEvent('collapse'); + + return me.doCollapseExpand(1, animate); + }, + + doCollapseExpand: function (flags, animate) { + var me = this, + originalAnimCollapse = me.animCollapse, + ownerLayout = me.ownerLayout; + + + + me.animCollapse = animate; + + + + me.isCollapsingOrExpanding = flags; + + + + if (animate) { + me.addCls(Ext.baseCSSPrefix + 'animating-size'); + } + + if (ownerLayout && !animate) { + ownerLayout.onContentChange(me); + } else { + me.updateLayout({ isRoot: true }); + } + + + me.animCollapse = originalAnimCollapse; + + return me; + }, + + + afterCollapse: function(animated) { + var me = this, + ownerLayout = me.ownerLayout; + + me.isCollapsingOrExpanding = 0; + me.updateCollapseTool(); + + + + if (animated) { + me.removeCls(Ext.baseCSSPrefix + 'animating-size'); + } + + if (ownerLayout && animated) { + ownerLayout.onContentChange(me); + } + + me.setHiddenDocked(); + me.fireEvent('collapse', me); + }, + + setHiddenDocked: function(){ + + + var me = this, + toHide = me.hiddenOnCollapse, + items = me.getDockedItems(), + len = items.length, + i = 0, + item, reExpander; + + if (me.header !== false) { + reExpander = me.getReExpander(); + } + + toHide.add(me.body); + for (; i < len; i++) { + item = items[i]; + if (item && item !== reExpander && item.el) { + toHide.add(item.el); + } + } + toHide.setStyle('visibility', 'hidden'); + }, + + restoreHiddenDocked: function(){ + var toShow = this.hiddenOnCollapse; + + toShow.setStyle('visibility', ''); + toShow.clear(); + }, + + getPlaceholder: function(direction) { + var me = this, + collapseDir = direction || me.collapseDirection, + listeners = null, + placeholder = me.placeholder, + floatable = me.floatable, + titleCollapse = me.titleCollapse; + + if (!placeholder) { + if (floatable || (me.collapsible && titleCollapse)) { + listeners = { + click: { + + fn: (!titleCollapse && floatable) ? me.floatCollapsedPanel : me.toggleCollapse, + element: 'el', + scope: me + } + }; + } + + me.placeholder = placeholder = Ext.widget(me.createReExpander(collapseDir, { + id: me.id + '-placeholder', + listeners: listeners + })); + } + + + if (!placeholder.placeholderFor) { + + if (!placeholder.isComponent) { + me.placeholder = placeholder = me.lookupComponent(placeholder); + } + Ext.applyIf(placeholder, { + margins: me.margins, + placeholderFor: me + }); + + placeholder.addCls([Ext.baseCSSPrefix + 'region-collapsed-placeholder', Ext.baseCSSPrefix + 'region-collapsed-' + collapseDir + '-placeholder', me.collapsedCls]); + } + + return placeholder; + }, + + placeholderCollapse: function(direction, animate) { + var me = this, + ownerCt = me.ownerCt, + collapseDir = direction || me.collapseDirection, + floatCls = Ext.baseCSSPrefix + 'border-region-slide-in', + placeholder = me.getPlaceholder(collapseDir), + slideInDirection; + + me.isCollapsingOrExpanding = 1; + + + me.setHiddenState(true); + me.collapsed = collapseDir; + + if (placeholder.rendered) { + + if (placeholder.el.dom.parentNode !== me.el.dom.parentNode) { + me.el.dom.parentNode.insertBefore(placeholder.el.dom, me.el.dom); + } + + placeholder.hidden = false; + placeholder.setHiddenState(false); + placeholder.el.show(); + ownerCt.updateLayout(); + } else { + ownerCt.insert(ownerCt.items.indexOf(me), placeholder); + } + + if (me.rendered) { + + me.el.setVisibilityMode(me.placeholderCollapseHideMode); + if (animate) { + me.el.addCls(floatCls); + placeholder.el.hide(); + slideInDirection = me.convertCollapseDir(collapseDir); + + me.el.slideOut(slideInDirection, { + preserveScroll: true, + duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration), + listeners: { + afteranimate: function() { + me.el.removeCls(floatCls); + + placeholder.el.show().setStyle('display', 'none').slideIn(slideInDirection, { + easing: 'linear', + duration: 100, + listeners: { + afteranimate: function() { + placeholder.focus(); + placeholder.setHiddenState(false); + me.isCollapsingOrExpanding = 0; + me.fireEvent('collapse', me); + } + } + }); + } + } + }); + } else { + me.el.hide(); + placeholder.setHiddenState(false); + me.isCollapsingOrExpanding = 0; + me.fireEvent('collapse', me); + } + } else { + me.isCollapsingOrExpanding = 0; + if (!me.preventCollapseFire) { + me.fireEvent('collapse', me); + } + } + + return me; + }, + + floatCollapsedPanel: function() { + var me = this, + placeholder = me.placeholder, + ps = placeholder.getSize(), + myBox, + floatCls = Ext.baseCSSPrefix + 'border-region-slide-in', + collapsed = me.collapsed, + layoutOwner = me.ownerCt || me, + slideDirection; + + if (me.isSliding) { + return; + } + + + if (me.el.hasCls(floatCls)) { + me.slideOutFloatedPanel(); + return; + } + me.isSliding = true; + + + placeholder.el.hide(); + placeholder.hidden = true; + me.el.show(); + me.setHiddenState(false); + me.collapsed = false; + layoutOwner.updateLayout(); + myBox = me.getBox(false, true); + + + placeholder.el.show(); + placeholder.hidden = false; + me.el.hide(); + me.setHiddenState(true); + me.collapsed = collapsed; + layoutOwner.updateLayout(); + + me.slideOutTask = me.slideOutTask || new Ext.util.DelayedTask(me.slideOutFloatedPanel, me); + placeholder.el.on('mouseleave', me.onMouseLeaveFloated, me); + me.el.on('mouseleave', me.onMouseLeaveFloated, me); + placeholder.el.on('mouseenter', me.onMouseEnterFloated, me); + me.el.on('mouseenter', me.onMouseEnterFloated, me); + + me.el.addCls(floatCls); + me.floated = true; + + + if (me.collapseTool) { + me.collapseTool.el.hide(); + } + + switch (me.collapsed) { + case 'top': + me.setLocalXY(myBox.x, myBox.y + ps.height - 1); + break; + case 'right': + me.setLocalXY(myBox.x - ps.width + 1, myBox.y); + break; + case 'bottom': + me.setLocalXY(myBox.x, myBox.y - ps.height + 1); + break; + case 'left': + me.setLocalXY(myBox.x + ps.width - 1, myBox.y); + break; + } + slideDirection = me.convertCollapseDir(me.collapsed); + + + + me.floatedFromCollapse = me.collapsed; + me.collapsed = false; + me.setHiddenState(false); + + me.el.slideIn(slideDirection, { + preserveScroll: true, + duration: Ext.Number.from(me.animCollapse, Ext.fx.Anim.prototype.duration), + listeners: { + afteranimate: function() { + me.isSliding = false; + me.fireEvent('float', me); + } + } + }); + }, + + onMouseLeaveFloated: function(e) { + this.slideOutTask.delay(500); + }, + + onMouseEnterFloated: function(e) { + this.slideOutTask.cancel(); + }, + + isLayoutRoot: function() { + if (this.floatedFromCollapse) { + return true; + } + return this.callParent(); + }, + + slideOutFloatedPanel: function(preventAnimate) { + var me = this, + compEl = me.el, + collapseDirection; + + if (me.isSliding || me.isDestroyed) { + return; + } + + me.isSliding = true; + me.floated = false; + + me.slideOutFloatedPanelBegin(); + + if (typeof me.collapsed == 'string') { + collapseDirection = me.convertCollapseDir(me.collapsed); + } + + compEl.slideOut(collapseDirection, { + preserveScroll: true, + duration: Ext.Number.from(me.animCollapse, Ext.fx.Anim.prototype.duration), + autoEnd: preventAnimate === true, + listeners: { + afteranimate: function() { + me.slideOutFloatedPanelEnd(); + + + me.el.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in'); + } + } + }); + }, + + + slideOutFloatedPanelBegin: function() { + var me = this, + placeholderEl = me.placeholder.el, + el = me.el; + + me.collapsed = me.floatedFromCollapse; + me.setHiddenState(true); + me.floatedFromCollapse = null; + + + placeholderEl.un('mouseleave', me.onMouseLeaveFloated, me); + el.un('mouseleave', me.onMouseLeaveFloated, me); + placeholderEl.un('mouseenter', me.onMouseEnterFloated, me); + el.un('mouseenter', me.onMouseEnterFloated, me); + }, + + + slideOutFloatedPanelEnd: function(suppressEvents) { + var me = this; + + if (me.collapseTool) { + me.collapseTool.el.show(); + } + me.slideOutTask.cancel(); + me.isSliding = false; + if (!suppressEvents) { + me.fireEvent('unfloat', me); + } + }, + + + expand: function(animate) { + var me = this, + layout = me.ownerLayout; + + if (me.isCollapsingOrExpanding) { + return me; + } + + if (!arguments.length) { + animate = me.animCollapse; + } + + if (!me.collapsed && !me.floatedFromCollapse) { + return me; + } + + + if (me.fireEvent('beforeexpand', me, animate) === false) { + return me; + } + + if (layout && layout.onBeforeComponentExpand) { + if (layout.onBeforeComponentExpand(me) === false) { + return me; + } + } + + delete me.getHierarchyState().collapsed; + + if (me.isPlaceHolderCollapse()) { + return me.placeholderExpand(animate); + } + + me.restoreHiddenDocked(); + me.beginExpand(); + me.collapsed = false; + + return me.doCollapseExpand(2, animate); + }, + + placeholderExpand: function(animate) { + var me = this, + collapseDir = me.collapsed, + floatCls = Ext.baseCSSPrefix + 'border-region-slide-in', + finalPos, + floatedPos, + center = me.ownerLayout ? me.ownerLayout.centerRegion: null; + + + if (Ext.AbstractComponent.layoutSuspendCount) { + animate = false; + } + + if (me.floatedFromCollapse) { + floatedPos = me.getPosition(true); + + me.slideOutFloatedPanelBegin(); + me.slideOutFloatedPanelEnd(); + me.floated = false; + } + + if (animate) { + + + Ext.suspendLayouts(); + me.placeholder.hide(); + me.el.show(); + me.collapsed = false; + me.setHiddenState(false); + + + + if (center && !floatedPos) { + center.hidden = true; + } + + Ext.resumeLayouts(true); + center.hidden = false; + me.el.addCls(floatCls); + + + + + + + + + + + + + me.isCollapsingOrExpanding = 2; + + + if (floatedPos) { + finalPos = me.getXY(); + me.setLocalXY(floatedPos[0], floatedPos[1]); + me.setXY([finalPos[0], finalPos[1]], { + duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration), + listeners: { + afteranimate: function() { + me.el.removeCls(floatCls); + me.isCollapsingOrExpanding = 0; + me.fireEvent('expand', me); + } + } + }); + } + + else { + me.el.hide(); + me.placeholder.el.show(); + me.placeholder.hidden = false; + + + me.setHiddenState(false); + me.el.slideIn(me.convertCollapseDir(collapseDir), { + preserveScroll: true, + duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration), + listeners: { + afteranimate: function() { + + + + + + me.el.removeCls(floatCls); + me.placeholder.hide(); + + + me.updateLayout(); + + me.isCollapsingOrExpanding = 0; + me.fireEvent('expand', me); + } + } + }); + } + + } else { + me.floated = me.collapsed = false; + me.el.removeCls(floatCls); + Ext.suspendLayouts(); + me.placeholder.hide(); + me.show(); + Ext.resumeLayouts(true); + me.fireEvent('expand', me); + } + + return me; + }, + + + afterExpand: function(animated) { + var me = this, + ownerLayout = me.ownerLayout; + + me.isCollapsingOrExpanding = 0; + me.updateCollapseTool(); + + + + if (animated) { + me.removeCls(Ext.baseCSSPrefix + 'animating-size'); + } + + if (ownerLayout && animated) { + ownerLayout.onContentChange(me); + } + + me.fireEvent('expand', me); + me.fireHierarchyEvent('expand'); + }, + + + setBorder: function(border, targetEl) { + if (targetEl) { + + return; + } + + var me = this, + header = me.header; + + if (!border) { + border = 0; + } else if (border === true) { + border = '1px'; + } else { + border = me.unitizeBox(border); + } + + if (header) { + if (header.isHeader) { + header.setBorder(border); + } else { + header.border = border; + } + } + + if (me.rendered && me.bodyBorder !== false) { + me.body.setStyle('border-width', border); + } + me.updateLayout(); + + me.border = border; + }, + + + toggleCollapse: function() { + return (this.collapsed || this.floatedFromCollapse) ? this.expand() : this.collapse(); + }, + + + getKeyMap : function() { + return this.keyMap || (this.keyMap = new Ext.util.KeyMap(Ext.apply({ + target: this.el + }, this.keys))); + }, + + + initDraggable : function() { + var me = this; + + + if (me.simpleDrag) { + me.initSimpleDraggable(); + } + + else { + + me.dd = new Ext.panel.DD(me, Ext.isBoolean(me.draggable) ? null : me.draggable); + } + }, + + + initSimpleDraggable: function() { + var me = this, + ddConfig, dd; + + if (!me.header) { + me.updateHeader(true); + } + + + if (me.header) { + ddConfig = Ext.applyIf({ + el: me.el, + delegate: '#' + Ext.escapeId(me.header.id) + }, me.draggable); + + + if (me.constrain || me.constrainHeader) { + ddConfig.constrain = me.constrain; + ddConfig.constrainDelegate = me.constrainHeader; + ddConfig.constrainTo = me.constrainTo || me.container; + } + + dd = me.dd = new Ext.util.ComponentDragger(me, ddConfig); + me.relayEvents(dd, ['dragstart', 'drag', 'dragend']); + if (me.maximized) { + dd.disable(); + } + } + }, + + + + ghostTools : function() { + var tools = [], + header = this.header, + headerTools = header ? header.query('tool[hidden=false]') : [], + t, tLen, tool; + + if (headerTools.length) { + t = 0; + tLen = headerTools.length; + + for (; t < tLen; t++) { + tool = headerTools[t]; + + + + + + tools.push({ + type: tool.type + }); + } + } else { + tools = [{ + type: 'placeholder' + }]; + } + return tools; + }, + + + + ghost: function(cls) { + var me = this, + ghostPanel = me.ghostPanel, + box = me.getBox(), + header = me.header, + ghostHeader, tools, i; + + if (!ghostPanel) { + me.ghostPanel = ghostPanel = Ext.widget(me.createGhost(cls)); + } else { + ghostPanel.el.show(); + } + + ghostPanel.setHiddenState(false); + ghostPanel.floatParent = me.floatParent; + + ghostPanel.toFront(); + if (header && !me.preventHeader) { + ghostHeader = ghostPanel.header; + + ghostHeader.suspendLayouts(); + tools = ghostHeader.query('tool'); + for (i = tools.length; i--;) { + ghostHeader.remove(tools[i]); + } + ghostPanel.addTool(me.ghostTools()); + ghostPanel.setTitle(me.title); + ghostHeader.setTitlePosition(header.titlePosition); + + if (me.iconCls) { + ghostPanel.setIconCls(me.iconCls); + } else if (me.icon) { + ghostPanel.setIcon(me.icon); + } else if (me.glyph) { + ghostPanel.setGlyph(me.glyph); + } + + ghostHeader.addCls(Ext.baseCSSPrefix + 'header-ghost'); + ghostHeader.resumeLayouts(); + } + + ghostPanel.setPagePosition(box.x, box.y); + ghostPanel.setSize(box.width, box.height); + me.el.hide(); + return ghostPanel; + }, + + createGhost: function(cls) { + var me = this, + header = me.header, + frame = me.frame && !me.alwaysFramed; + + return { + xtype: 'panel', + hidden: false, + header: header ? { + titleAlign: header.titleAlign + } : null, + ui: frame ? me.ui.replace(/-framed$/, '') : me.ui, + id: me.id + '-ghost', + renderTo: Ext.getBody(), + + + resizable: false, + + + draggable: false, + + + closable: false, + + floating: { + shadow: false + }, + frame: frame, + alwaysFramed: me.alwaysFramed, + overlapHeader: me.overlapHeader, + headerPosition: me.headerPosition, + baseCls: me.baseCls, + getRefOwner: function () { + return me.getRefOwner(); + }, + cls: me.baseCls + '-ghost ' + (cls || '') + }; + }, + + + unghost: function(show, matchPosition) { + var me = this, + ghostPanel = me.ghostPanel; + + if (!ghostPanel) { + return; + } + + if (show !== false) { + + + me.el.show(); + if (matchPosition !== false) { + me.setPagePosition(ghostPanel.getXY()); + if (me.hideMode == 'offsets') { + + delete me.el.hideModeStyles; + } + } + Ext.defer(me.focus, 10, me); + } + + ghostPanel.el.hide(); + ghostPanel.setHiddenState(true); + }, + + beginDrag: function() { + if (this.floatingDescendants) { + this.floatingDescendants.hide(); + } + }, + + endDrag: function() { + if (this.floatingDescendants) { + this.floatingDescendants.show(); + } + }, + + initResizable: function() { + this.callParent(arguments); + if (this.collapsed) { + this.resizer.disable(); + } + }, + + + convertCollapseDir: function(collapseDir) { + return collapseDir.substr(0, 1); + }, + + getAnimationProps: function() { + var me = this, + animCollapse = me.animCollapse, + props; + + props = me.callParent(); + + if (typeof animCollapse === 'number') { + props.duration = animCollapse; + } + + return props; + } +}, function() { + this.prototype.animCollapse = Ext.enableFx; +}); + + + +Ext.define('Ext.tip.Tip', { + extend: Ext.panel.Panel , + alias: 'widget.tip', + + alternateClassName: 'Ext.Tip', + + + + + + + minWidth : 40, + + maxWidth : 500, + + shadow : "sides", + + + defaultAlign : "tl-bl?", + + constrainPosition : true, + + + autoRender: true, + hidden: true, + baseCls: Ext.baseCSSPrefix + 'tip', + floating: { + shadow: true, + shim: true + }, + focusOnToFront: false, + + + closeAction: 'hide', + + + alwaysFramed: true, + + frameHeader: false, + + initComponent: function() { + var me = this; + + me.floating = Ext.apply( {}, { + shadow: me.shadow, + constrain: me.constrainPosition + }, me.self.prototype.floating); + me.callParent(arguments); + + + me.constrain = me.constrain || me.constrainPosition; + }, + + + showAt : function(xy){ + var me = this; + this.callParent(arguments); + + if (me.isVisible()) { + me.setPagePosition(xy[0], xy[1]); + if (me.constrainPosition || me.constrain) { + me.doConstrain(); + } + me.toFront(true); + } + }, + + + initDraggable : function(){ + var me = this; + me.draggable = { + el: me.getDragEl(), + delegate: me.header.el, + constrain: me, + constrainTo: me.el.dom.parentNode + }; + + Ext.Component.prototype.initDraggable.call(me); + }, + + + ghost: undefined, + unghost: undefined +}); + + + +Ext.define('Ext.tip.ToolTip', { + extend: Ext.tip.Tip , + alias: 'widget.tooltip', + alternateClassName: 'Ext.ToolTip', + + + + autoHide: true, + + + showDelay: 500, + + hideDelay: 200, + + dismissDelay: 5000, + + + trackMouse: false, + + + anchorToTarget: true, + + anchorOffset: 0, + + + + targetCounter: 0, + quickShowInterval: 250, + + ariaRole: 'tooltip', + + + initComponent: function() { + var me = this; + me.callParent(arguments); + me.lastActive = new Date(); + me.setTarget(me.target); + me.origAnchor = me.anchor; + }, + + + onRender: function(ct, position) { + var me = this; + me.callParent(arguments); + me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition(); + if (me.sticky) { + + me.el.dom.setAttribute('data-sticky', true); + } + me.anchorEl = me.el.createChild({ + role: 'presentation', + cls: Ext.baseCSSPrefix + 'tip-anchor ' + me.anchorCls + }); + }, + + + setTarget: function(target) { + var me = this, + t = Ext.get(target), + tg; + + if (me.target) { + tg = Ext.get(me.target); + me.mun(tg, 'mouseover', me.onTargetOver, me); + me.mun(tg, 'mouseout', me.onTargetOut, me); + me.mun(tg, 'mousemove', me.onMouseMove, me); + } + + me.target = t; + if (t) { + + me.mon(t, { + + + freezeEvent: true, + + mouseover: me.onTargetOver, + mouseout: me.onTargetOut, + mousemove: me.onMouseMove, + scope: me + }); + } + if (me.anchor) { + me.anchorTarget = me.target; + } + }, + + + onMouseMove: function(e) { + var me = this, + t = me.delegate ? e.getTarget(me.delegate) : me.triggerElement = true, + xy; + if (t) { + me.targetXY = e.getXY(); + if (t === me.triggerElement) { + if (!me.hidden && me.trackMouse) { + xy = me.getTargetXY(); + if (me.constrainPosition) { + xy = me.el.adjustForConstraints(xy, me.el.parent()); + } + me.setPagePosition(xy); + } + } else { + me.hide(); + me.lastActive = new Date(0); + me.onTargetOver(e); + } + } else if ((!me.closable && me.isVisible()) && me.autoHide !== false) { + me.hide(); + } + }, + + + getTargetXY: function() { + var me = this, + mouseOffset, + offsets, xy, dw, dh, de, bd, scrollX, scrollY, axy, sz, constrainPosition; + if (me.delegate) { + me.anchorTarget = me.triggerElement; + } + if (me.anchor) { + me.targetCounter++; + offsets = me.getOffsets(); + xy = (me.anchorToTarget && !me.trackMouse) ? me.getAlignToXY(me.anchorTarget, me.getAnchorAlign()) : me.targetXY; + dw = Ext.Element.getViewWidth() - 5; + dh = Ext.Element.getViewHeight() - 5; + de = document.documentElement; + bd = document.body; + scrollX = (de.scrollLeft || bd.scrollLeft || 0) + 5; + scrollY = (de.scrollTop || bd.scrollTop || 0) + 5; + axy = [xy[0] + offsets[0], xy[1] + offsets[1]]; + sz = me.getSize(); + constrainPosition = me.constrainPosition; + + me.anchorEl.removeCls(me.anchorCls); + + if (me.targetCounter < 2 && constrainPosition) { + if (axy[0] < scrollX) { + if (me.anchorToTarget) { + me.defaultAlign = 'l-r'; + if (me.mouseOffset) { + me.mouseOffset[0] *= -1; + } + } + me.anchor = 'left'; + return me.getTargetXY(); + } + if (axy[0] + sz.width > dw) { + if (me.anchorToTarget) { + me.defaultAlign = 'r-l'; + if (me.mouseOffset) { + me.mouseOffset[0] *= -1; + } + } + me.anchor = 'right'; + return me.getTargetXY(); + } + if (axy[1] < scrollY) { + if (me.anchorToTarget) { + me.defaultAlign = 't-b'; + if (me.mouseOffset) { + me.mouseOffset[1] *= -1; + } + } + me.anchor = 'top'; + return me.getTargetXY(); + } + if (axy[1] + sz.height > dh) { + if (me.anchorToTarget) { + me.defaultAlign = 'b-t'; + if (me.mouseOffset) { + me.mouseOffset[1] *= -1; + } + } + me.anchor = 'bottom'; + return me.getTargetXY(); + } + } + + me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition(); + me.anchorEl.addCls(me.anchorCls); + me.targetCounter = 0; + return axy; + } else { + mouseOffset = me.getMouseOffset(); + return (me.targetXY) ? [me.targetXY[0] + mouseOffset[0], me.targetXY[1] + mouseOffset[1]] : mouseOffset; + } + }, + + getMouseOffset: function() { + var me = this, + offset = me.anchor ? [0, 0] : [15, 18]; + if (me.mouseOffset) { + offset[0] += me.mouseOffset[0]; + offset[1] += me.mouseOffset[1]; + } + return offset; + }, + + + getAnchorPosition: function() { + var me = this, + m; + if (me.anchor) { + me.tipAnchor = me.anchor.charAt(0); + } else { + m = me.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/); + if (!m) { + Ext.Error.raise('The AnchorTip.defaultAlign value "' + me.defaultAlign + '" is invalid.'); + } + me.tipAnchor = m[1].charAt(0); + } + + switch (me.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 me = this, + mouseOffset, + offsets, + ap = me.getAnchorPosition().charAt(0); + if (me.anchorToTarget && !me.trackMouse) { + switch (ap) { + case 't': + offsets = [0, 9]; + break; + case 'b': + offsets = [0, -13]; + break; + case 'r': + offsets = [ - 13, 0]; + break; + default: + offsets = [9, 0]; + break; + } + } else { + switch (ap) { + case 't': + offsets = [ - 15 - me.anchorOffset, 30]; + break; + case 'b': + offsets = [ - 19 - me.anchorOffset, -13 - me.el.dom.offsetHeight]; + break; + case 'r': + offsets = [ - 15 - me.el.dom.offsetWidth, -13 - me.anchorOffset]; + break; + default: + offsets = [25, -13 - me.anchorOffset]; + break; + } + } + mouseOffset = me.getMouseOffset(); + offsets[0] += mouseOffset[0]; + offsets[1] += mouseOffset[1]; + + return offsets; + }, + + + onTargetOver: function(e) { + var me = this, + delegate = me.delegate, + t; + + if (me.disabled || e.within(me.target.dom, true)) { + return; + } + t = delegate ? e.getTarget(delegate) : true; + if (t) { + me.triggerElement = t; + me.triggerEvent = e; + me.clearTimer('hide'); + me.targetXY = e.getXY(); + me.delayShow(); + } + }, + + + delayShow: function (trackMouse) { + + + + + + + + + var me = this, + xy = me.el && (trackMouse === false || !me.trackMouse) && me.getTargetXY(); + + if (me.hidden && !me.showTimer) { + if (Ext.Date.getElapsed(me.lastActive) < me.quickShowInterval) { + me.show(); + } else { + me.showTimer = Ext.defer(me.showFromDelay, me.showDelay, me, [xy]); + } + } + else if (!me.hidden && me.autoHide !== false) { + me.show(xy); + } + }, + + showFromDelay: function (xy) { + this.fromDelayShow = true; + this.show(xy); + delete this.fromDelayShow; + }, + + onShowVeto: function(){ + this.callParent(); + delete this.triggerElement; + this.clearTimer('show'); + }, + + + onTargetOut: function(e) { + var me = this, + triggerEl = me.triggerElement, + + + target = triggerEl === true ? me.target : triggerEl; + + + + if (me.disabled || !triggerEl || e.within(target, true)) { + return; + } + if (me.showTimer) { + me.clearTimer('show'); + me.triggerElement = null; + } + if (me.autoHide !== false) { + me.delayHide(); + } + }, + + + delayHide: function() { + var me = this; + if (!me.hidden && !me.hideTimer) { + me.hideTimer = Ext.defer(me.hide, me.hideDelay, me); + } + }, + + + hide: function() { + var me = this; + me.clearTimer('dismiss'); + me.lastActive = new Date(); + if (me.anchorEl) { + me.anchorEl.hide(); + } + me.callParent(arguments); + delete me.triggerElement; + }, + + + show: function (xy) { + var me = this; + + + + this.callParent(); + if (this.hidden === false) { + me.setPagePosition(-10000, -10000); + + if (me.anchor) { + me.anchor = me.origAnchor; + } + + if (!me.calledFromShowAt) { + + me.showAt(xy || me.getTargetXY()); + } + + if (me.anchor) { + me.syncAnchor(); + me.anchorEl.show(); + } else { + me.anchorEl.hide(); + } + } + }, + + + showAt: function(xy) { + var me = this; + me.lastActive = new Date(); + me.clearTimers(); + me.calledFromShowAt = true; + + + if (!me.isVisible()) { + this.callParent(arguments); + } + + + if (me.isVisible()) { + me.setPagePosition(xy[0], xy[1]); + if (me.constrainPosition || me.constrain) { + me.doConstrain(); + } + me.toFront(true); + me.el.sync(true); + if (me.dismissDelay && me.autoHide !== false) { + me.dismissTimer = Ext.defer(me.hide, me.dismissDelay, me); + } + if (me.anchor) { + me.syncAnchor(); + if (!me.anchorEl.isVisible()) { + me.anchorEl.show(); + } + } else { + me.anchorEl.hide(); + } + } + delete me.calledFromShowAt; + }, + + + syncAnchor: function() { + var me = this, + anchorPos, + targetPos, + offset; + switch (me.tipAnchor.charAt(0)) { + case 't': + anchorPos = 'b'; + targetPos = 'tl'; + offset = [20 + me.anchorOffset, 1]; + break; + case 'r': + anchorPos = 'l'; + targetPos = 'tr'; + offset = [ - 1, 12 + me.anchorOffset]; + break; + case 'b': + anchorPos = 't'; + targetPos = 'bl'; + offset = [20 + me.anchorOffset, -1]; + break; + default: + anchorPos = 'r'; + targetPos = 'tl'; + offset = [1, 12 + me.anchorOffset]; + break; + } + me.anchorEl.alignTo(me.el, anchorPos + '-' + targetPos, offset); + me.anchorEl.setStyle('z-index', parseInt(me.el.getZIndex(), 10) || 0 + 1).setVisibilityMode(Ext.Element.DISPLAY); + }, + + + setPagePosition: function(x, y) { + var me = this; + me.callParent(arguments); + if (me.anchor) { + me.syncAnchor(); + } + }, + + _timerNames: {}, + + clearTimer: function (name) { + var me = this, + names = me._timerNames, + propName = names[name] || (names[name] = name + 'Timer'), + timer = me[propName]; + + if (timer) { + clearTimeout(timer); + me[propName] = null; + } + }, + + + clearTimers: function() { + var me = this; + me.clearTimer('show'); + me.clearTimer('dismiss'); + me.clearTimer('hide'); + }, + + + onShow: function() { + var me = this; + me.callParent(); + me.mon(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me); + }, + + + onHide: function() { + var me = this; + me.callParent(); + me.mun(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me); + }, + + + onDocMouseDown: function(e) { + var me = this; + if (!me.closable && !e.within(me.el.dom)) { + me.disable(); + Ext.defer(me.doEnable, 100, me); + } + }, + + + doEnable: function() { + if (!this.isDestroyed) { + this.enable(); + } + }, + + + onDisable: function() { + this.callParent(); + this.clearTimers(); + this.hide(); + }, + + beforeDestroy: function() { + var me = this; + me.clearTimers(); + Ext.destroy(me.anchorEl); + delete me.anchorEl; + delete me.target; + delete me.anchorTarget; + delete me.triggerElement; + me.callParent(); + }, + + + onDestroy: function() { + Ext.getDoc().un('mousedown', this.onDocMouseDown, this); + this.callParent(); + } +}); + + +Ext.define('ExtThemeNeptune.panel.Panel', { + override: 'Ext.panel.Panel', + border: false, + bodyBorder: false, + + initBorderProps: Ext.emptyFn, + + initBodyBorder: function() { + + + + + if (this.bodyBorder !== true) { + this.callParent(); + } + } +}); + + +Ext.define('ExtThemeNeptune.toolbar.Toolbar', { + override: 'Ext.toolbar.Toolbar', + usePlainButtons: false, + border: false +}); + + +Ext.define('ExtThemeNeptune.layout.component.Dock', { + override: 'Ext.layout.component.Dock', + + + noBorderClassTable: [ + 0, + Ext.baseCSSPrefix + 'noborder-l', + Ext.baseCSSPrefix + 'noborder-b', + Ext.baseCSSPrefix + 'noborder-bl', + Ext.baseCSSPrefix + 'noborder-r', + Ext.baseCSSPrefix + 'noborder-rl', + Ext.baseCSSPrefix + 'noborder-rb', + Ext.baseCSSPrefix + 'noborder-rbl', + Ext.baseCSSPrefix + 'noborder-t', + Ext.baseCSSPrefix + 'noborder-tl', + Ext.baseCSSPrefix + 'noborder-tb', + Ext.baseCSSPrefix + 'noborder-tbl', + Ext.baseCSSPrefix + 'noborder-tr', + Ext.baseCSSPrefix + 'noborder-trl', + Ext.baseCSSPrefix + 'noborder-trb', + Ext.baseCSSPrefix + 'noborder-trbl' + ], + + + edgeMasks: { + top: 8, + right: 4, + bottom: 2, + left: 1 + }, + + handleItemBorders: function() { + var me = this, + edges = 0, + maskT = 8, + maskR = 4, + maskB = 2, + maskL = 1, + owner = me.owner, + bodyBorder = owner.bodyBorder, + ownerBorder = owner.border, + collapsed = me.collapsed, + edgeMasks = me.edgeMasks, + noBorderCls = me.noBorderClassTable, + dockedItemsGen = owner.dockedItems.generation, + b, borderCls, docked, edgesTouched, i, ln, item, dock, lastValue, mask, + addCls, removeCls; + + if (me.initializedBorders === dockedItemsGen) { + return; + } + + addCls = []; + removeCls = []; + + borderCls = me.getBorderCollapseTable(); + noBorderCls = me.getBorderClassTable ? me.getBorderClassTable() : noBorderCls; + + me.initializedBorders = dockedItemsGen; + + + me.collapsed = false; + docked = me.getDockedItems(); + me.collapsed = collapsed; + + for (i = 0, ln = docked.length; i < ln; i++) { + item = docked[i]; + if (item.ignoreBorderManagement) { + + + continue; + } + + dock = item.dock; + mask = edgesTouched = 0; + addCls.length = 0; + removeCls.length = 0; + + if (dock !== 'bottom') { + if (edges & maskT) { + b = item.border; + } else { + b = ownerBorder; + if (b !== false) { + edgesTouched += maskT; + } + } + if (b === false) { + mask += maskT; + } + } + if (dock !== 'left') { + if (edges & maskR) { + b = item.border; + } else { + b = ownerBorder; + if (b !== false) { + edgesTouched += maskR; + } + } + if (b === false) { + mask += maskR; + } + } + if (dock !== 'top') { + if (edges & maskB) { + b = item.border; + } else { + b = ownerBorder; + if (b !== false) { + edgesTouched += maskB; + } + } + if (b === false) { + mask += maskB; + } + } + if (dock !== 'right') { + if (edges & maskL) { + b = item.border; + } else { + b = ownerBorder; + if (b !== false) { + edgesTouched += maskL; + } + } + if (b === false) { + mask += maskL; + } + } + + if ((lastValue = item.lastBorderMask) !== mask) { + item.lastBorderMask = mask; + if (lastValue) { + removeCls[0] = noBorderCls[lastValue]; + } + if (mask) { + addCls[0] = noBorderCls[mask]; + } + } + + if ((lastValue = item.lastBorderCollapse) !== edgesTouched) { + item.lastBorderCollapse = edgesTouched; + if (lastValue) { + removeCls[removeCls.length] = borderCls[lastValue]; + } + if (edgesTouched) { + addCls[addCls.length] = borderCls[edgesTouched]; + } + } + + if (removeCls.length) { + item.removeCls(removeCls); + } + if (addCls.length) { + item.addCls(addCls); + } + + + + + edges |= edgeMasks[dock]; + } + + mask = edgesTouched = 0; + addCls.length = 0; + removeCls.length = 0; + + if (edges & maskT) { + b = bodyBorder; + } else { + b = ownerBorder; + if (b !== false) { + edgesTouched += maskT; + } + } + if (b === false) { + mask += maskT; + } + + if (edges & maskR) { + b = bodyBorder; + } else { + b = ownerBorder; + if (b !== false) { + edgesTouched += maskR; + } + } + if (b === false) { + mask += maskR; + } + + if (edges & maskB) { + b = bodyBorder; + } else { + b = ownerBorder; + if (b !== false) { + edgesTouched += maskB; + } + } + if (b === false) { + mask += maskB; + } + + if (edges & maskL) { + b = bodyBorder; + } else { + b = ownerBorder; + if (b !== false) { + edgesTouched += maskL; + } + } + if (b === false) { + mask += maskL; + } + + if ((lastValue = me.lastBodyBorderMask) !== mask) { + me.lastBodyBorderMask = mask; + if (lastValue) { + removeCls[0] = noBorderCls[lastValue]; + } + if (mask) { + addCls[0] = noBorderCls[mask]; + } + } + + if ((lastValue = me.lastBodyBorderCollapse) !== edgesTouched) { + me.lastBodyBorderCollapse = edgesTouched; + if (lastValue) { + removeCls[removeCls.length] = borderCls[lastValue]; + } + if (edgesTouched) { + addCls[addCls.length] = borderCls[edgesTouched]; + } + } + + if (removeCls.length) { + owner.removeBodyCls(removeCls); + } + if (addCls.length) { + owner.addBodyCls(addCls); + } + }, + + onRemove: function (item) { + var lastBorderMask = item.lastBorderMask; + + if (!item.isDestroyed && !item.ignoreBorderManagement && lastBorderMask) { + item.lastBorderMask = 0; + item.removeCls(this.noBorderClassTable[lastBorderMask]); + } + + this.callParent([item]); + } +}); + + + +Ext.define('Ext.dd.DragTracker', { + + + + mixins: { + observable: Ext.util.Observable + }, + + + active: false, + + + + + trackOver: false, + + + + + + + tolerance: 5, + + + autoStart: false, + + + + + + + + constructor : function(config){ + var me = this; + Ext.apply(me, config); + me.addEvents( + + 'mouseover', + + + 'mouseout', + + + 'mousedown', + + + 'mouseup', + + + 'mousemove', + + + 'beforedragstart', + + + 'dragstart', + + + 'dragend', + + + 'drag' + ); + + me.dragRegion = new Ext.util.Region(0,0,0,0); + + if (me.el) { + me.initEl(me.el); + } + + + me.mixins.observable.constructor.call(me); + if (me.disabled) { + me.disable(); + } + }, + + + initEl: function(el) { + var me = this; + + me.el = Ext.get(el); + + + me.handle = Ext.get(me.delegate); + + + me.delegate = me.handle ? undefined : me.delegate; + + if (!me.handle) { + me.handle = me.el; + } + + + + me.handleListeners = { + scope: me, + delegate: me.delegate, + mousedown: me.onMouseDown + }; + + + + + if (me.trackOver || me.overCls) { + Ext.apply(me.handleListeners, { + mouseover: me.onMouseOver, + mouseout: me.onMouseOut + }); + } + me.mon(me.handle, me.handleListeners); + }, + + disable: function() { + this.disabled = true; + }, + + enable: function() { + this.disabled = false; + }, + + destroy : function() { + var me = this; + + if (me.active) { + + me.endDrag({}); + } + me.clearListeners(); + me.mun(me.handle, me.handleListeners); + me.el = me.handle = null; + }, + + + + onMouseOver: function(e, target) { + var me = this, + handleCls, el, i, len, cls; + + if (!me.disabled) { + + + + + + if (e.within(target, true, true) || me.delegate) { + handleCls = me.handleCls; + me.mouseIsOut = false; + + if (handleCls) { + for (i = 0, len = me.handleEls.length; i < len; i++) { + el = me.handleEls[i]; + cls = el.delegateCls; + + if (!cls) { + cls = el.delegateCls = [handleCls, '-', el.region, '-over'].join(''); + } + + el.addCls([cls, me.overCls]); + } + } + + me.fireEvent('mouseover', me, e, me.delegate ? e.getTarget(me.delegate, target) : me.handle); + } + } + }, + + + + onMouseOut: function(e) { + var me = this, + el, i, len; + + if (me.mouseIsDown) { + me.mouseIsOut = true; + } else { + if (me.handleCls) { + for (i = 0, len = me.handleEls.length; i < len; i++) { + el = me.handleEls[i]; + el.removeCls([el.delegateCls, me.overCls]); + } + } + + me.fireEvent('mouseout', me, e); + } + }, + + onMouseDown: function(e, target){ + var me = this, + el; + + + if (me.disabled ||e.dragTracked) { + return; + } + + + me.dragTarget = me.delegate ? target : me.handle.dom; + me.startXY = me.lastXY = e.getXY(); + me.startRegion = Ext.fly(me.dragTarget).getRegion(); + + if (me.fireEvent('mousedown', me, e) === false || + me.fireEvent('beforedragstart', me, e) === false || + me.onBeforeStart(e) === false) { + return; + } + + + + me.mouseIsDown = true; + + + e.dragTracked = true; + + + el = me.el.dom; + if (Ext.isIE && el.setCapture) { + el.setCapture(); + } + + if (me.preventDefault !== false) { + e.preventDefault(); + } + Ext.getDoc().on({ + scope: me, + mouseup: me.onMouseUp, + mousemove: me.onMouseMove, + selectstart: me.stopSelect + }); + + + + + me.dragEnded = false; + + if (!me.tolerance) { + me.triggerStart(); + } else if (me.autoStart) { + me.timer = Ext.defer(me.triggerStart, me.autoStart === true ? 1000 : me.autoStart, me, [e]); + } + }, + + onMouseMove: function(e, target){ + var me = this, + xy = e.getXY(), + s = me.startXY; + + + + + if (me.dragEnded) { + return; + } + + me.lastXY = xy; + if (!me.active) { + if (Math.max(Math.abs(s[0]-xy[0]), Math.abs(s[1]-xy[1])) > me.tolerance) { + me.triggerStart(e); + } else { + return; + } + } + + + if (me.fireEvent('mousemove', me, e) === false) { + me.onMouseUp(e); + } else { + me.onDrag(e); + me.fireEvent('drag', me, e); + } + }, + + onMouseUp: function(e) { + var me = this; + + + me.mouseIsDown = false; + + + if (me.mouseIsOut) { + me.mouseIsOut = false; + me.onMouseOut(e); + } + e.preventDefault(); + + + if (Ext.isIE && document.releaseCapture) { + document.releaseCapture(); + } + + me.fireEvent('mouseup', me, e); + me.endDrag(e); + }, + + + endDrag: function(e) { + var me = this, + wasActive = me.active; + + Ext.getDoc().un({ + mousemove: me.onMouseMove, + mouseup: me.onMouseUp, + selectstart: me.stopSelect, + scope: me + }); + me.clearStart(); + me.active = false; + if (wasActive) { + me.dragEnded = true; + me.onEnd(e); + me.fireEvent('dragend', me, e); + } + + + me._constrainRegion = Ext.EventObject.dragTracked = null; + }, + + triggerStart: function(e) { + var me = this; + me.clearStart(); + me.active = true; + me.onStart(e); + me.fireEvent('dragstart', me, e); + }, + + clearStart : function() { + var timer = this.timer; + if (timer) { + clearTimeout(timer); + this.timer = null; + } + }, + + stopSelect : function(e) { + e.stopEvent(); + return false; + }, + + + onBeforeStart : function(e) { + + }, + + + onStart : function(xy) { + + }, + + + onDrag : function(e) { + + }, + + + onEnd : function(e) { + + }, + + + getDragTarget : function(){ + return this.dragTarget; + }, + + + getDragCt : function(){ + return this.el; + }, + + + getConstrainRegion: function() { + var me = this; + + if (me.constrainTo) { + if (me.constrainTo instanceof Ext.util.Region) { + return me.constrainTo; + } + if (!me._constrainRegion) { + me._constrainRegion = Ext.fly(me.constrainTo).getViewRegion(); + } + } else { + if (!me._constrainRegion) { + me._constrainRegion = me.getDragCt().getViewRegion(); + } + } + return me._constrainRegion; + }, + + getXY : function(constrain){ + return constrain ? this.constrainModes[constrain](this, this.lastXY) : this.lastXY; + }, + + + getOffset : function(constrain){ + var xy = this.getXY(constrain), + s = this.startXY; + + return [xy[0]-s[0], xy[1]-s[1]]; + }, + + constrainModes: { + + point: function(me, xy) { + var dr = me.dragRegion, + constrainTo = me.getConstrainRegion(); + + + if (!constrainTo) { + return xy; + } + + dr.x = dr.left = dr[0] = dr.right = xy[0]; + dr.y = dr.top = dr[1] = dr.bottom = xy[1]; + dr.constrainTo(constrainTo); + + return [dr.left, dr.top]; + }, + + + dragTarget: function(me, xy) { + var s = me.startXY, + dr = me.startRegion.copy(), + constrainTo = me.getConstrainRegion(), + adjust; + + + if (!constrainTo) { + return xy; + } + + + + + dr.translateBy(xy[0]-s[0], xy[1]-s[1]); + + + if (dr.right > constrainTo.right) { + xy[0] += adjust = (constrainTo.right - dr.right); + dr.left += adjust; + } + if (dr.left < constrainTo.left) { + xy[0] += (constrainTo.left - dr.left); + } + + + if (dr.bottom > constrainTo.bottom) { + xy[1] += adjust = (constrainTo.bottom - dr.bottom); + dr.top += adjust; + } + if (dr.top < constrainTo.top) { + xy[1] += (constrainTo.top - dr.top); + } + return xy; + } + } +}); + + + +Ext.define('Ext.dd.DragZone', { + extend: Ext.dd.DragSource , + + + constructor : function(el, config){ + var me = this, + scroll = me.containerScroll; + + me.callParent([el, config]); + if (scroll) { + el = me.scrollEl || el; + el = Ext.get(el); + if (Ext.isObject(scroll)) { + el.ddScrollConfig = scroll; + } + Ext.dd.ScrollManager.register(el); + } + }, + + + + + + + getDragData : function(e){ + return Ext.dd.Registry.getHandleFromEvent(e); + }, + + + onInitDrag : function(x, y){ + this.proxy.update(this.dragData.ddel.cloneNode(true)); + this.onStartDrag(x, y); + return true; + }, + + + getRepairXY : function(e){ + return Ext.fly(this.dragData.ddel).getXY(); + }, + + destroy : function(){ + this.callParent(); + if (this.containerScroll) { + Ext.dd.ScrollManager.unregister(this.scrollEl || this.el); + } + } +}); + + + +Ext.define('Ext.layout.container.Editor', { + + + + alias: 'layout.editor', + + extend: Ext.layout.container.Container , + + + + autoSizeDefault: { + width: 'field', + height: 'field' + }, + + sizePolicies: { + + $: { + + $: { + readsWidth: 1, + readsHeight: 1, + setsWidth: 0, + setsHeight: 0 + }, + boundEl: { + readsWidth: 1, + readsHeight: 0, + setsWidth: 0, + setsHeight: 1 + } + }, + + boundEl: { + + $: { + readsWidth: 0, + readsHeight: 1, + setsWidth: 1, + setsHeight: 0 + }, + boundEl: { + readsWidth: 0, + readsHeight: 0, + setsWidth: 1, + setsHeight: 1 + } + } + }, + + getItemSizePolicy: function (item) { + var me = this, + autoSize = me.owner.autoSize, + key = autoSize && autoSize.width, + policy = me.sizePolicies; + + policy = policy[key] || policy.$; + + key = autoSize && autoSize.height; + policy = policy[key] || policy.$; + + return policy; + }, + + calculate: function(ownerContext) { + var me = this, + owner = me.owner, + autoSize = owner.autoSize, + fieldWidth, + fieldHeight; + + if (autoSize === true) { + autoSize = me.autoSizeDefault; + } + + + if (autoSize) { + fieldWidth = me.getDimension(owner, autoSize.width, 'getWidth', owner.width); + fieldHeight = me.getDimension(owner, autoSize.height, 'getHeight', owner.height); + } + + + ownerContext.childItems[0].setSize(fieldWidth, fieldHeight); + + + ownerContext.setWidth(fieldWidth); + ownerContext.setHeight(fieldHeight); + + + ownerContext.setContentSize(fieldWidth || owner.field.getWidth(), + fieldHeight || owner.field.getHeight()); + }, + + getDimension: function(owner, type, getMethod, ownerSize){ + switch (type) { + + case 'boundEl': + return owner.boundEl[getMethod](); + + + case 'field': + return undefined; + + + default: + return ownerSize; + } + } +}); + + + +Ext.define('Ext.Editor', { + + + + extend: Ext.container.Container , + + alias: 'widget.editor', + + + + + + layout: 'editor', + + + + + allowBlur: true, + + + + + revertInvalid: true, + + + + + + + value : '', + + + alignment: 'c-c?', + + + offsets: [0, 0], + + + shadow : 'frame', + + + constrain : false, + + + swallowKeys : true, + + + completeOnEnter : true, + + + cancelOnEsc : true, + + + updateEl : false, + + + + + focusOnToFront: false, + + + + + hidden: true, + baseCls: Ext.baseCSSPrefix + 'editor', + + initComponent : function() { + var me = this, + field = me.field = Ext.ComponentManager.create(me.field, 'textfield'); + + field.inEditor = true; + field.msgTarget = field.msgTarget || 'qtip'; + me.mon(field, { + scope: me, + blur: me.onFieldBlur, + specialkey: me.onSpecialKey + }); + + if (field.grow) { + me.mon(field, 'autosize', me.onFieldAutosize, me, {delay: 1}); + } + me.floating = { + constrain: me.constrain + }; + me.items = field; + + me.callParent(arguments); + + me.addEvents( + + 'beforestartedit', + + + 'startedit', + + + 'beforecomplete', + + 'complete', + + 'canceledit', + + 'specialkey' + ); + }, + + + onFieldAutosize: function(){ + this.updateLayout(); + }, + + + afterRender : function(ct, position) { + var me = this, + field = me.field, + inputEl = field.inputEl; + + me.callParent(arguments); + + + if (inputEl) { + inputEl.dom.name = ''; + if (me.swallowKeys) { + inputEl.swallowEvent([ + 'keypress', + 'keydown' + ]); + } + } + }, + + + onSpecialKey : function(field, event) { + var me = this, + key = event.getKey(), + complete = me.completeOnEnter && key == event.ENTER, + cancel = me.cancelOnEsc && key == event.ESC; + + if (complete || cancel) { + event.stopEvent(); + + + Ext.defer(function() { + + if (complete) { + me.completeEdit(); + } else { + me.cancelEdit(); + } + }, 1); + } + + me.fireEvent('specialkey', me, field, event); + }, + + + startEdit : function(el, value) { + var me = this, + field = me.field, + dom; + + me.completeEdit(); + me.boundEl = Ext.get(el); + dom = me.boundEl.dom; + value = Ext.isDefined(value) ? value : Ext.String.trim(dom.textContent || dom.innerText || dom.innerHTML); + + + + + if (!me.rendered && !me.renderTo && me.ownerCt) { + (me.renderTo = me.ownerCt.el).position(); + } + + if (me.fireEvent('beforestartedit', me, me.boundEl, value) !== false) { + me.startValue = value; + me.show(); + + field.suspendEvents(); + field.reset(); + field.setValue(value); + field.resumeEvents(); + me.realign(true); + field.focus([field.getRawValue().length]); + if (field.autoSize) { + field.autoSize(); + } + me.editing = true; + } + }, + + + realign : function(autoSize) { + var me = this; + if (autoSize === true) { + me.updateLayout(); + } + me.alignTo(me.boundEl, me.alignment, me.offsets); + }, + + + completeEdit : function(remainVisible) { + var me = this, + field = me.field, + value; + + if (!me.editing) { + return; + } + + + if (field.assertValue) { + field.assertValue(); + } + + value = me.getValue(); + if (!field.isValid()) { + if (me.revertInvalid !== false) { + me.cancelEdit(remainVisible); + } + return; + } + + if (String(value) === String(me.startValue) && me.ignoreNoChange) { + me.hideEdit(remainVisible); + return; + } + + if (me.fireEvent('beforecomplete', me, value, me.startValue) !== false) { + + value = me.getValue(); + if (me.updateEl && me.boundEl) { + me.boundEl.update(value); + } + me.hideEdit(remainVisible); + me.fireEvent('complete', me, value, me.startValue); + } + }, + + + onShow : function() { + var me = this; + + me.callParent(arguments); + if (me.hideEl !== false) { + me.boundEl.hide(); + } + me.fireEvent('startedit', me, me.boundEl, me.startValue); + }, + + + cancelEdit : function(remainVisible) { + var me = this, + startValue = me.startValue, + field = me.field, + value; + + if (me.editing) { + if (field) { + value = me.editedValue = me.getValue(); + + field.suspendEvents(); + me.setValue(startValue); + field.resumeEvents(); + } + me.hideEdit(remainVisible); + me.fireEvent('canceledit', me, value, startValue); + delete me.editedValue; + } + }, + + + hideEdit: function(remainVisible) { + if (remainVisible !== true) { + this.editing = false; + this.hide(); + } + }, + + + onFieldBlur : function(field, e) { + var me = this, + target = Ext.Element.getActiveElement(); + + + if(me.allowBlur === true && me.editing && me.selectSameEditor !== true) { + me.completeEdit(); + } + + + if (Ext.fly(target).isFocusable() || target.getAttribute('tabIndex')) { + target.focus(); + } + }, + + + onHide : function() { + var me = this, + field = me.field; + + if (me.editing) { + me.completeEdit(); + return; + } + + + if (field.hasFocus && field.triggerBlur) { + field.triggerBlur(); + } + if (field.collapse) { + field.collapse(); + } + + + if (me.hideEl !== false) { + me.boundEl.show(); + } + me.callParent(arguments); + }, + + + setValue : function(value) { + this.field.setValue(value); + }, + + + getValue : function() { + return this.field.getValue(); + }, + + beforeDestroy : function() { + var me = this; + + Ext.destroy(me.field); + delete me.field; + delete me.boundEl; + + me.callParent(arguments); + } +}); + + + +Ext.define("Ext.form.Labelable", { + + + isLabelable: true, + + autoEl: { + tag: 'table', + cellpadding: 0, + role: 'presentation' + }, + + childEls: [ + + 'labelCell', + + + 'labelEl', + + + 'bodyEl', + + + 'sideErrorCell', + + + 'errorEl', + + 'inputRow' + ], + + + labelableRenderTpl: [ + + + 'id="{id}" class="{inputRowCls}">', + + + '', + '', + '{beforeLabelTpl}', + '', + ' class="{labelCls}"', + ' style="{labelStyle}"', + + ' unselectable="on"', + '>', + '{beforeLabelTextTpl}', + '{fieldLabel}', + '', + '{labelSeparator}', + '', + '', + '{afterLabelTextTpl}', + '', + '{afterLabelTpl}', + '', + '', + + + '', + '{beforeBodyEl}', + + + '', + '{beforeLabelTpl}', + '', + '{afterLabelTpl}', + '', + + '{beforeSubTpl}', + '{[values.$comp.getSubTplMarkup(values)]}', + '{afterSubTpl}', + + + '', + '{afterBodyEl}', + '', + '', + '', + '', + '', + '', + '{afterBodyEl}', + '', + '', + '', + { + disableFormats: true + } + ], + + + activeErrorsTpl: undefined, + + htmlActiveErrorsTpl: [ + '', + '
    ', + '', + '
    {fieldLabel}
    ', + '
    ', + '
  • {.}
  • ', + '
', + '
' + ], + + plaintextActiveErrorsTpl: [ + '', + '', + '{fieldLabel}\n', + '', + '\n{.}', + '' + ], + + + isFieldLabelable: true, + + + formItemCls: Ext.baseCSSPrefix + 'form-item', + + + labelCls: Ext.baseCSSPrefix + 'form-item-label', + + + + + errorMsgCls: Ext.baseCSSPrefix + 'form-error-msg', + + + baseBodyCls: Ext.baseCSSPrefix + 'form-item-body', + + + inputRowCls: Ext.baseCSSPrefix + 'form-item-input-row', + + + fieldBodyCls: '', + + + clearCls: Ext.baseCSSPrefix + 'clear', + + + invalidCls : Ext.baseCSSPrefix + 'form-invalid', + + + fieldLabel: undefined, + + + labelAlign : 'left', + + + labelWidth: 100, + + + labelPad : 5, + + + + labelSeparator : ':', + + + + + + hideLabel: false, + + + hideEmptyLabel: true, + + + preventMark: false, + + + autoFitErrors: true, + + + msgTarget: 'qtip', + + + + + noWrap: true, + + labelableInsertions: [ + + + 'beforeBodyEl', + + + 'afterBodyEl', + + + 'beforeLabelTpl', + + + 'afterLabelTpl', + + + 'beforeSubTpl', + + + 'afterSubTpl', + + + 'beforeLabelTextTpl', + + + 'afterLabelTextTpl', + + + 'labelAttrTpl' + ], + + + labelableRenderProps: ['allowBlank', 'id', 'labelAlign', 'fieldBodyCls', 'extraFieldBodyCls', + 'baseBodyCls', 'clearCls', 'labelSeparator', 'msgTarget', 'inputRowCls'], + + + initLabelable: function() { + var me = this, + padding = me.padding; + + + + + if (padding) { + me.padding = undefined; + me.extraMargins = Ext.Element.parseBox(padding); + } + + if (!me.activeErrorsTpl) { + if (me.msgTarget == 'title') { + me.activeErrorsTpl = me.plaintextActiveErrorsTpl; + } else { + me.activeErrorsTpl = me.htmlActiveErrorsTpl; + } + } + + me.addCls(Ext.plainTableCls); + me.addCls(me.formItemCls); + + + me.lastActiveError = ''; + + me.addEvents( + + 'errorchange' + ); + + + me.enableBubble('errorchange'); + }, + + + trimLabelSeparator: function() { + var me = this, + separator = me.labelSeparator, + label = me.fieldLabel || '', + lastChar = label.substr(label.length - 1); + + + return lastChar === separator ? label.slice(0, -1) : label; + }, + + + getFieldLabel: function() { + return this.trimLabelSeparator(); + }, + + + setFieldLabel: function(label){ + label = label || ''; + + var me = this, + separator = me.labelSeparator, + labelEl = me.labelEl; + + me.fieldLabel = label; + if (me.rendered) { + if (Ext.isEmpty(label) && me.hideEmptyLabel) { + labelEl.parent().setDisplayed('none'); + } else { + if (separator) { + label = me.trimLabelSeparator() + separator; + } + labelEl.update(label); + labelEl.parent().setDisplayed(''); + } + me.updateLayout(); + } + }, + + getInsertionRenderData: function (data, names) { + var i = names.length, + name, value; + + while (i--) { + name = names[i]; + value = this[name]; + + if (value) { + if (typeof value != 'string') { + if (!value.isTemplate) { + value = Ext.XTemplate.getTpl(this, name); + } + value = value.apply(data); + } + } + + data[name] = value || ''; + } + + return data; + }, + + + getLabelableRenderData: function() { + var me = this, + data, + tempEl, + topLabel = me.labelAlign === 'top'; + + if (!Ext.form.Labelable.errorIconWidth) { + tempEl = Ext.getBody().createChild({style: 'position:absolute', cls: Ext.baseCSSPrefix + 'form-invalid-icon'}); + Ext.form.Labelable.errorIconWidth = tempEl.getWidth() + tempEl.getMargin('l'); + tempEl.remove(); + } + + data = Ext.copyTo({ + inFormLayout : me.ownerLayout && me.ownerLayout.type === 'form', + inputId : me.getInputId(), + labelOnLeft : !topLabel, + hideLabel : !me.hasVisibleLabel(), + fieldLabel : me.getFieldLabel(), + labelCellStyle : me.getLabelCellStyle(), + labelCellAttrs : me.getLabelCellAttrs(), + labelCls : me.getLabelCls(), + labelStyle : me.getLabelStyle(), + bodyColspan : me.getBodyColspan(), + externalError : !me.autoFitErrors, + errorMsgCls : me.getErrorMsgCls(), + errorIconWidth : Ext.form.Labelable.errorIconWidth, + boxLabel : me.boxLabel + }, + me, me.labelableRenderProps, true); + + me.getInsertionRenderData(data, me.labelableInsertions); + + return data; + }, + + xhooks: { + beforeRender: function() { + var me = this; + me.setFieldDefaults(me.getHierarchyState().fieldDefaults); + if (me.ownerLayout) { + me.addCls(Ext.baseCSSPrefix + me.ownerLayout.type + '-form-item'); + } + }, + + onRender: function() { + var me = this, + margins, + side, + style = {}; + + if (me.extraMargins) { + margins = me.el.getMargin(); + for (side in margins) { + if (margins.hasOwnProperty(side)) { + style['margin-' + side] = (margins[side] + me.extraMargins[side]) + 'px'; + } + } + me.el.setStyle(style); + } + } + }, + + + hasVisibleLabel: function(){ + if (this.hideLabel) { + return false; + } + return !(this.hideEmptyLabel && !this.getFieldLabel()); + }, + + + getLabelWidth: function(){ + var me = this; + if (!me.hasVisibleLabel()) { + return 0; + } + return me.labelWidth + me.labelPad; + }, + + + getBodyColspan: function() { + var me = this, + result; + + if (me.msgTarget === 'side' && (!me.autoFitErrors || me.hasActiveError())) { + result = 1; + } else { + result = 2; + } + if (me.labelAlign !== 'top' && !me.hasVisibleLabel()) { + result++; + } + return result; + }, + + getLabelCls: function() { + var labelCls = this.labelCls + ' ' + Ext.dom.Element.unselectableCls, + labelClsExtra = this.labelClsExtra; + + return labelClsExtra ? labelCls + ' ' + labelClsExtra : labelCls; + }, + + getLabelCellStyle: function() { + var me = this, + hideLabelCell = me.hideLabel || (!me.getFieldLabel() && me.hideEmptyLabel); + + return hideLabelCell ? 'display:none;' : ''; + }, + + getErrorMsgCls: function() { + var me = this, + hideLabelCell = (me.hideLabel || (!me.fieldLabel && me.hideEmptyLabel)); + + return me.errorMsgCls + (!hideLabelCell && me.labelAlign === 'top' ? ' ' + Ext.baseCSSPrefix + 'lbl-top-err-icon' : ''); + }, + + getLabelCellAttrs: function() { + var me = this, + labelAlign = me.labelAlign, + result = ''; + + if (labelAlign !== 'top') { + result = 'valign="top" halign="' + labelAlign + '" width="' + (me.labelWidth + me.labelPad) + '"'; + } + return result + ' class="' + Ext.baseCSSPrefix + 'field-label-cell"'; + }, + + + getLabelStyle: function () { + var me = this, + labelPad = me.labelPad, + labelWidth = me.labelWidth, + labelStyle = ''; + + + + if (me.labelAlign !== 'top') { + if (labelWidth) { + labelStyle = 'width:' + labelWidth + 'px;'; + } + if (labelPad) { + labelStyle += me.getLabelStyleMarginProp() + labelPad + 'px;'; + } + } else if (labelPad) { + + labelStyle = 'margin-bottom:' + labelPad + 'px;'; + } + + return labelStyle + (me.labelStyle || ''); + }, + + getLabelStyleMarginProp: function () { + return 'margin-right:'; + }, + + + getSubTplMarkup: function() { + return ''; + }, + + + getInputId: function() { + return ''; + }, + + + getActiveError : function() { + return this.activeError || ''; + }, + + + hasActiveError: function() { + return !!this.getActiveError(); + }, + + + setActiveError: function(msg) { + this.setActiveErrors(msg); + }, + + + getActiveErrors: function() { + return this.activeErrors || []; + }, + + + setActiveErrors: function(errors) { + var me = this, + tpl; + + errors = Ext.Array.from(errors); + tpl = me.getTpl('activeErrorsTpl'); + + me.activeErrors = errors; + me.activeError = tpl.apply({ + fieldLabel: me.fieldLabel, + errors: errors, + listCls: Ext.plainListCls + }); + + me.renderActiveError(); + }, + + + unsetActiveError: function() { + delete this.activeError; + delete this.activeErrors; + this.renderActiveError(); + }, + + + renderActiveError: function() { + var me = this, + activeError = me.getActiveError(), + hasError = !!activeError; + + if (activeError !== me.lastActiveError) { + me.fireEvent('errorchange', me, activeError); + me.lastActiveError = activeError; + } + + if (me.rendered && !me.isDestroyed && !me.preventMark) { + + me.el[hasError ? 'addCls' : 'removeCls'](me.invalidCls); + + + if (me.errorEl) { + me.errorEl.dom.innerHTML = activeError; + } + } + }, + + + setFieldDefaults: function(defaults) { + var key; + + for (key in defaults) { + if (!this.hasOwnProperty(key)) { + this[key] = defaults[key]; + } + } + } +}); + + + +Ext.define('Ext.form.field.Field', { + + isFormField : true, + + + + + + + disabled : false, + + + submitValue: true, + + + validateOnChange: true, + + + suspendCheckChange: 0, + + + initField: function() { + this.addEvents( + + 'change', + + 'validitychange', + + 'dirtychange' + ); + + this.initValue(); + + var badNames = [ + 'tagName', + 'nodeName', + 'children', + 'childNodes' + ], name = this.name; + + if (name && Ext.Array.indexOf(badNames, name) > -1) { + Ext.log.warn( + ['It is recommended to not use "', name, '" as a field name, because it ', + 'can cause naming collisions during form submission.'].join('') + ); + } + }, + + + initValue: function() { + var me = this; + + me.value = me.transformOriginalValue(me.value); + + me.originalValue = me.lastValue = me.value; + + + me.suspendCheckChange++; + me.setValue(me.value); + me.suspendCheckChange--; + }, + + + transformOriginalValue: Ext.identityFn, + + + + + getFieldIdentifier: function () { + return this.isEditorComponent ? this.dataIndex : this.name; + }, + + + getName: function() { + return this.name; + }, + + + getValue: function() { + return this.value; + }, + + + setValue: function(value) { + var me = this; + me.value = value; + me.checkChange(); + return me; + }, + + + isEqual: function(value1, value2) { + return String(value1) === String(value2); + }, + + + isEqualAsString: function(value1, value2){ + return String(Ext.value(value1, '')) === String(Ext.value(value2, '')); + }, + + + getSubmitData: function() { + var me = this, + data = null; + if (!me.disabled && me.submitValue) { + data = {}; + data[me.getName()] = '' + me.getValue(); + } + return data; + }, + + + getModelData: function(includeEmptyText, isSubmitting) { + var me = this, + data = null; + + + + if (!me.disabled && (me.submitValue || !isSubmitting)) { + data = {}; + data[me.getFieldIdentifier()] = me.getValue(); + } + return data; + }, + + + reset : function(){ + var me = this; + + me.beforeReset(); + me.setValue(me.originalValue); + me.clearInvalid(); + + delete me.wasValid; + }, + + + beforeReset: Ext.emptyFn, + + + resetOriginalValue: function() { + this.originalValue = this.getValue(); + this.checkDirty(); + }, + + + checkChange: function() { + var me = this, + newVal, oldVal; + + if (!me.suspendCheckChange) { + newVal = me.getValue(); + oldVal = me.lastValue; + + if (!me.isDestroyed && me.didValueChange(newVal, oldVal)) { + me.lastValue = newVal; + me.fireEvent('change', me, newVal, oldVal); + me.onChange(newVal, oldVal); + } + } + }, + + + didValueChange: function(newVal, oldVal){ + return !this.isEqual(newVal, oldVal); + }, + + + onChange: function(newVal, oldVal) { + if (this.validateOnChange) { + this.validate(); + } + this.checkDirty(); + }, + + + isDirty : function() { + var me = this; + return !me.disabled && !me.isEqual(me.getValue(), me.originalValue); + }, + + + checkDirty: function() { + var me = this, + isDirty = me.isDirty(); + if (isDirty !== me.wasDirty) { + me.fireEvent('dirtychange', me, isDirty); + me.onDirtyChange(isDirty); + me.wasDirty = isDirty; + } + }, + + + onDirtyChange: Ext.emptyFn, + + + getErrors: function(value) { + return []; + }, + + + isValid : function() { + var me = this; + return me.disabled || Ext.isEmpty(me.getErrors()); + }, + + + validate : function() { + var me = this, + isValid = me.isValid(); + if (isValid !== me.wasValid) { + me.wasValid = isValid; + me.fireEvent('validitychange', me, isValid); + } + return isValid; + }, + + + batchChanges: function(fn) { + try { + this.suspendCheckChange++; + fn(); + } catch(e){ + throw e; + } finally { + this.suspendCheckChange--; + } + this.checkChange(); + }, + + + isFileUpload: function() { + return false; + }, + + + extractFileInput: function() { + return null; + }, + + + markInvalid: Ext.emptyFn, + + + clearInvalid: Ext.emptyFn + +}); + + + +Ext.define('Ext.layout.component.field.Field', { + + + + extend: Ext.layout.component.Auto , + + alias: 'layout.field', + + + + + + type: 'field', + + naturalSizingProp: 'size', + + beginLayout: function(ownerContext) { + var me = this, + owner = me.owner; + + me.callParent(arguments); + + ownerContext.labelStrategy = me.getLabelStrategy(); + ownerContext.errorStrategy = me.getErrorStrategy(); + + ownerContext.labelContext = ownerContext.getEl('labelEl'); + ownerContext.bodyCellContext = ownerContext.getEl('bodyEl'); + ownerContext.inputContext = ownerContext.getEl('inputEl'); + ownerContext.errorContext = ownerContext.getEl('errorEl'); + + + + if (Ext.isIE7m && Ext.isStrict && ownerContext.inputContext) { + me.ieInputWidthAdjustment = ownerContext.inputContext.getPaddingInfo().width + ownerContext.inputContext.getBorderInfo().width; + } + + + ownerContext.labelStrategy.prepare(ownerContext, owner); + ownerContext.errorStrategy.prepare(ownerContext, owner); + }, + + beginLayoutCycle: function(ownerContext){ + var me = this, + owner = me.owner, + widthModel = ownerContext.widthModel, + ownerNaturalSize = owner[me.naturalSizingProp], + width; + + me.callParent(arguments); + + if (widthModel.shrinkWrap) { + + me.beginLayoutShrinkWrap(ownerContext); + } else if (widthModel.natural) { + + + if (typeof ownerNaturalSize == 'number' && !owner.inputWidth) { + me.beginLayoutFixed(ownerContext, (width = ownerNaturalSize * 6.5 + 20), 'px'); + } + + + else { + me.beginLayoutShrinkWrap(ownerContext); + } + ownerContext.setWidth(width, false); + } else { + me.beginLayoutFixed(ownerContext, '100', '%'); + } + }, + + beginLayoutFixed: function (ownerContext, width, suffix) { + var owner = ownerContext.target, + inputEl = owner.inputEl, + inputWidth = owner.inputWidth; + + owner.el.setStyle('table-layout', 'fixed'); + owner.bodyEl.setStyle('width', width + suffix); + if (inputEl) { + if (inputWidth) { + inputEl.setStyle('width', inputWidth + 'px'); + } else { + inputEl.setStyle('width', owner.stretchInputElFixed ? '100%' : ''); + } + } + ownerContext.isFixed = true; + }, + + beginLayoutShrinkWrap: function (ownerContext) { + var owner = ownerContext.target, + inputEl = owner.inputEl, + inputWidth = owner.inputWidth; + + if (inputEl && inputEl.dom) { + inputEl.dom.removeAttribute('size'); + if (inputWidth) { + inputEl.setStyle('width', inputWidth + 'px'); + } else { + inputEl.setStyle('width', ''); + } + } + owner.el.setStyle('table-layout', 'auto'); + owner.bodyEl.setStyle('width', ''); + }, + + finishedLayout: function(ownerContext){ + var owner = this.owner; + + this.callParent(arguments); + ownerContext.labelStrategy.finishedLayout(ownerContext, owner); + ownerContext.errorStrategy.finishedLayout(ownerContext, owner); + }, + + calculateOwnerHeightFromContentHeight: function(ownerContext, contentHeight) { + return contentHeight; + }, + + measureContentHeight: function (ownerContext) { + return ownerContext.el.getHeight(); + }, + + measureContentWidth: function (ownerContext) { + return ownerContext.el.getWidth(); + }, + + measureLabelErrorHeight: function (ownerContext) { + return ownerContext.labelStrategy.getHeight(ownerContext) + + ownerContext.errorStrategy.getHeight(ownerContext); + }, + + onFocus: function() { + this.getErrorStrategy().onFocus(this.owner); + }, + + + getLabelStrategy: function() { + var me = this, + strategies = me.labelStrategies, + labelAlign = me.owner.labelAlign; + return strategies[labelAlign] || strategies.base; + }, + + + getErrorStrategy: function() { + var me = this, + owner = me.owner, + strategies = me.errorStrategies, + msgTarget = owner.msgTarget; + return !owner.preventMark && Ext.isString(msgTarget) ? + (strategies[msgTarget] || strategies.elementId) : + strategies.none; + }, + + + labelStrategies: (function() { + var base = { + prepare: function(ownerContext, owner) { + var cls = owner.labelCls + '-' + owner.labelAlign, + labelEl = owner.labelEl; + + if (labelEl) { + labelEl.addCls(cls); + } + }, + + getHeight: function () { + return 0; + }, + + finishedLayout: Ext.emptyFn + }; + + return { + base: base, + + + top: Ext.applyIf({ + + getHeight: function (ownerContext) { + var labelContext = ownerContext.labelContext, + props = labelContext.props, + height = props.height; + + if (height === undefined) { + props.height = height = labelContext.el.getHeight() + labelContext.getMarginInfo().height; + } + + return height; + } + }, base), + + + left: base, + + + right: base + }; + }()), + + + errorStrategies: (function() { + function showTip(owner) { + var tip = Ext.layout.component.field.Field.tip, + target; + + if (tip && tip.isVisible()) { + target = tip.activeTarget; + if (target && target.el === owner.getActionEl().dom) { + tip.toFront(true); + } + } + } + + var applyIf = Ext.applyIf, + emptyFn = Ext.emptyFn, + iconCls = Ext.baseCSSPrefix + 'form-invalid-icon', + iconWidth, + base = { + prepare: function(ownerContext, owner) { + var el = owner.errorEl; + if (el) { + el.setDisplayed(false); + } + }, + getHeight: function () { + return 0; + }, + onFocus: emptyFn, + finishedLayout: emptyFn + }; + + return { + none: base, + + + side: applyIf({ + prepare: function(ownerContext, owner) { + var errorEl = owner.errorEl, + sideErrorCell = owner.sideErrorCell, + displayError = owner.hasActiveError(), + tempEl; + + + if (!iconWidth) { + iconWidth = (tempEl = Ext.getBody().createChild({style: 'position:absolute', cls: iconCls})).getWidth(); + tempEl.remove(); + } + + errorEl.addCls(iconCls); + errorEl.set({'data-errorqtip': owner.getActiveError() || ''}); + if (owner.autoFitErrors) { + errorEl.setDisplayed(displayError); + } + + else { + errorEl.setVisible(displayError); + } + + + if (sideErrorCell && owner.autoFitErrors) { + sideErrorCell.setDisplayed(displayError); + } + owner.bodyEl.dom.colSpan = owner.getBodyColspan(); + + + Ext.layout.component.field.Field.initTip(); + }, + onFocus: showTip + }, base), + + + under: applyIf({ + prepare: function(ownerContext, owner) { + var errorEl = owner.errorEl, + cls = Ext.baseCSSPrefix + 'form-invalid-under'; + + errorEl.addCls(cls); + errorEl.setDisplayed(owner.hasActiveError()); + }, + getHeight: function (ownerContext) { + var height = 0, + errorContext, props; + + if (ownerContext.target.hasActiveError()) { + errorContext = ownerContext.errorContext; + props = errorContext.props; + height = props.height; + + if (height === undefined) { + props.height = height = errorContext.el.getHeight(); + } + } + + return height; + } + }, base), + + + qtip: applyIf({ + prepare: function(ownerContext, owner) { + Ext.layout.component.field.Field.initTip(); + owner.getActionEl().dom.setAttribute('data-errorqtip', owner.getActiveError() || ''); + }, + onFocus: showTip + }, base), + + + title: applyIf({ + prepare: function(ownerContext, owner) { + owner.getActionEl().dom.setAttribute('title', owner.getActiveError() || ''); + } + }, base), + + + elementId: applyIf({ + prepare: function(ownerContext, owner) { + var targetEl = Ext.fly(owner.msgTarget); + if (targetEl) { + targetEl.dom.innerHTML = owner.getActiveError() || ''; + targetEl.setDisplayed(owner.hasActiveError()); + } + } + }, base) + }; + }()), + + statics: { + + initTip: function() { + var tip = this.tip; + if (!tip) { + tip = this.tip = Ext.create('Ext.tip.QuickTip', { + + sticky: true, + ui: 'form-invalid' + }); + tip.tagConfig = Ext.apply({}, {attribute: 'errorqtip'}, tip.tagConfig); + } + }, + + + destroyTip: function() { + var tip = this.tip; + if (tip) { + tip.destroy(); + delete this.tip; + } + } + } +}); + + + +Ext.define('Ext.form.field.Base', { + extend: Ext.Component , + mixins: { + labelable: Ext.form.Labelable , + field: Ext.form.field.Field + }, + alias: 'widget.field', + alternateClassName: ['Ext.form.Field', 'Ext.form.BaseField'], + + + + fieldSubTpl: [ + ' 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} {editableCls} {inputCls}" autocomplete="off"/>', + { + disableFormats: true + } + ], + + subTplInsertions: [ + + 'inputAttrTpl' + ], + + + + + inputType: 'text', + + + + + + invalidText : 'The value in this field is invalid', + + + + fieldCls : Ext.baseCSSPrefix + 'form-field', + + + + + focusCls : 'form-focus', + + + dirtyCls : Ext.baseCSSPrefix + '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, + + componentLayout: 'field', + + + readOnly : false, + + + readOnlyCls: Ext.baseCSSPrefix + 'form-readonly', + + + + + validateOnBlur: true, + + + hasFocus : false, + + baseCls: Ext.baseCSSPrefix + 'field', + + maskOnDisable: false, + + + + + stretchInputElFixed: true, + + + initComponent : function() { + var me = this; + + me.callParent(); + + me.subTplData = me.subTplData || {}; + + me.addEvents( + + 'specialkey', + + + 'writeablechange' + ); + + + me.initLabelable(); + me.initField(); + + + if (!me.name) { + me.name = me.getInputId(); + } + + if (me.readOnly) { + me.addCls(me.readOnlyCls); + } + + me.addCls(Ext.baseCSSPrefix + 'form-type-' + me.inputType); + }, + + + getInputId: function() { + return this.inputId || (this.inputId = this.id + '-inputEl'); + }, + + + getSubTplData: function() { + var me = this, + type = me.inputType, + inputId = me.getInputId(), + data; + + data = Ext.apply({ + id : inputId, + cmpId : me.id, + name : me.name || inputId, + disabled : me.disabled, + readOnly : me.readOnly, + value : me.getRawValue(), + type : type, + fieldCls : me.fieldCls, + fieldStyle : me.getFieldStyle(), + tabIdx : me.tabIndex, + inputCls : me.inputCls, + typeCls : Ext.baseCSSPrefix + 'form-' + (type === 'password' ? 'text' : type), + role : me.ariaRole + }, me.subTplData); + + me.getInsertionRenderData(data, me.subTplInsertions); + + return data; + }, + + applyRenderSelectors: function() { + var me = this; + + me.callParent(); + + + + me.addChildEls('inputEl'); + + + me.inputEl = me.el.getById(me.getInputId()); + }, + + + getSubTplMarkup: function() { + return this.getTpl('fieldSubTpl').apply(this.getSubTplData()); + }, + + initRenderTpl: function() { + var me = this; + if (!me.hasOwnProperty('renderTpl')) { + me.renderTpl = me.getTpl('labelableRenderTpl'); + } + return me.callParent(); + }, + + initRenderData: function() { + return Ext.applyIf(this.callParent(), this.getLabelableRenderData()); + }, + + + setFieldStyle: function(style) { + var me = this, + inputEl = me.inputEl; + if (inputEl) { + inputEl.applyStyles(style); + } + me.fieldStyle = style; + }, + + getFieldStyle: function() { + var style = this.fieldStyle; + return Ext.isObject(style) ? Ext.DomHelper.generateStyles(style, null, true) : style || ''; + }, + + + onRender : function() { + this.callParent(arguments); + this.renderActiveError(); + }, + + getFocusEl: function() { + return this.inputEl; + }, + + isFileUpload: function() { + return this.inputType === 'file'; + }, + + + getSubmitData: function() { + var me = this, + data = null, + val; + if (!me.disabled && me.submitValue) { + val = me.getSubmitValue(); + if (val !== null) { + data = {}; + data[me.getName()] = val; + } + } + return data; + }, + + + getSubmitValue: function() { + return this.processRawValue(this.getRawValue()); + }, + + + getRawValue: function() { + var me = this, + v = (me.inputEl ? me.inputEl.getValue() : Ext.value(me.rawValue, '')); + me.rawValue = v; + return v; + }, + + + setRawValue: function(value) { + var me = this; + value = Ext.value(me.transformRawValue(value), ''); + me.rawValue = value; + + + if (me.inputEl) { + me.bindPropertyChange(false); + me.inputEl.dom.value = value; + me.bindPropertyChange(true); + } + return value; + }, + + + transformRawValue: Ext.identityFn, + + + valueToRaw: function(value) { + return '' + Ext.value(value, ''); + }, + + + rawToValue: Ext.identityFn, + + + processRawValue: Ext.identityFn, + + + getValue: function() { + var me = this, + val = me.rawToValue(me.processRawValue(me.getRawValue())); + me.value = val; + return val; + }, + + + setValue: function(value) { + var me = this; + me.setRawValue(me.valueToRaw(value)); + return me.mixins.field.setValue.call(me, value); + }, + + onBoxReady: function() { + var me = this; + me.callParent(arguments); + + if (me.setReadOnlyOnBoxReady) { + me.setReadOnly(me.readOnly); + } + + }, + + + onDisable: function() { + var me = this, + inputEl = me.inputEl; + + me.callParent(); + if (inputEl) { + inputEl.dom.disabled = true; + if (me.hasActiveError()) { + + me.clearInvalid(); + me.needsValidateOnEnable = true; + } + } + }, + + + onEnable: function() { + var me = this, + inputEl = me.inputEl; + + me.callParent(); + if (inputEl) { + inputEl.dom.disabled = false; + if (me.needsValidateOnEnable) { + delete me.needsValidateOnEnable; + + me.forceValidation = true; + me.isValid(); + delete me.forceValidation; + } + } + }, + + + setReadOnly: function(readOnly) { + var me = this, + inputEl = me.inputEl; + readOnly = !!readOnly; + me[readOnly ? 'addCls' : 'removeCls'](me.readOnlyCls); + me.readOnly = readOnly; + if (inputEl) { + inputEl.dom.readOnly = readOnly; + } else if (me.rendering) { + me.setReadOnlyOnBoxReady = true; + } + me.fireEvent('writeablechange', me, readOnly); + }, + + + fireKey: function(e){ + if(e.isSpecialKey()){ + this.fireEvent('specialkey', this, new Ext.EventObjectImpl(e)); + } + }, + + + initEvents : function(){ + var me = this, + inputEl = me.inputEl, + onChangeTask, + onChangeEvent, + events = me.checkChangeEvents, + ignoreChangeRe = me.ignoreChangeRe, + eLen = events.length, + e, event; + + if (inputEl) { + me.mon(inputEl, Ext.EventManager.getKeyEvent(), me.fireKey, me); + + + onChangeTask = new Ext.util.DelayedTask(me.checkChange, me); + me.onChangeEvent = onChangeEvent = function(e) { + + + if (!(e.type == 'propertychange' && ignoreChangeRe.test(e.browserEvent.propertyName))) { + onChangeTask.delay(me.checkChangeBuffer); + } + }; + + for (e = 0; e < eLen; e++) { + event = events[e]; + + if (event === 'propertychange') { + me.usesPropertychange = true; + } + + me.mon(inputEl, event, onChangeEvent); + } + } + me.callParent(); + }, + + doComponentLayout: function() { + + + + this.bindPropertyChange(false); + this.callParent(arguments); + this.bindPropertyChange(true); + }, + + + bindPropertyChange: function(active) { + var me = this, + usesPropertychange = me.usesPropertychange; + + if (usesPropertychange) { + me[active ? 'mon' : 'mun'](me.inputEl, 'propertychange', me.onChangeEvent); + } + }, + + + onDirtyChange: function(isDirty) { + this[isDirty ? 'addCls' : 'removeCls'](this.dirtyCls); + }, + + + + isValid : function() { + var me = this, + disabled = me.disabled, + validate = me.forceValidation || !disabled; + + + return validate ? me.validateValue(me.processRawValue(me.getRawValue())) : disabled; + }, + + + + validateValue: function(value) { + var me = this, + errors = me.getErrors(value), + isValid = Ext.isEmpty(errors); + if (!me.preventMark) { + if (isValid) { + me.clearInvalid(); + } else { + me.markInvalid(errors); + } + } + + return isValid; + }, + + + markInvalid : function(errors) { + + var me = this, + oldMsg = me.getActiveError(), + active; + + me.setActiveErrors(Ext.Array.from(errors)); + active = me.getActiveError(); + if (oldMsg !== active) { + me.setError(active); + } + }, + + + clearInvalid : function() { + + var me = this, + hadError = me.hasActiveError(); + + delete me.needsValidateOnEnable; + me.unsetActiveError(); + if (hadError) { + me.setError(''); + } + }, + + + setError: function(active){ + var me = this, + msgTarget = me.msgTarget, + prop; + + if (me.rendered) { + if (msgTarget == 'title' || msgTarget == 'qtip') { + if (me.rendered) { + prop = msgTarget == 'qtip' ? 'data-errorqtip' : 'title'; + } + me.getActionEl().dom.setAttribute(prop, active || ''); + } else { + me.updateLayout(); + } + } + }, + + + renderActiveError: function() { + var me = this, + hasError = me.hasActiveError(); + if (me.inputEl) { + + me.inputEl[hasError ? 'addCls' : 'removeCls'](me.invalidCls + '-field'); + } + me.mixins.labelable.renderActiveError.call(me); + }, + + + getActionEl: function() { + return this.inputEl || this.el; + } + +}); + + + +Ext.define('Ext.form.field.VTypes', (function(){ + + var alpha = /^[a-zA-Z_]+$/, + alphanum = /^[a-zA-Z0-9_]+$/, + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + email = /^(")?(?:[^\."])(?:(?:[\.])?(?:[\w\-!#$%&'*+/=?^_`{|}~]))*\1@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/, + url = /(((^https?)|(^ftp)):\/\/((([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*)|(localhost|LOCALHOST))\/?)/i; + + + return { + singleton: true, + alternateClassName: 'Ext.form.VTypes', + + + 'email' : function(v){ + return email.test(v); + }, + + + 'emailText' : 'This field should be an e-mail address in the format "user@example.com"', + + + 'emailMask' : /[\w.\-@'"!#$%&'*+/=?^_`{|}~]/i, + + + 'url' : function(v){ + return url.test(v); + }, + + + 'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"', + + + + 'alpha' : function(v){ + return alpha.test(v); + }, + + + 'alphaText' : 'This field should only contain letters and _', + + + 'alphaMask' : /[a-z_]/i, + + + 'alphanum' : function(v){ + return alphanum.test(v); + }, + + + 'alphanumText' : 'This field should only contain letters, numbers and _', + + + 'alphanumMask' : /[a-z0-9_]/i + }; +}())); + + + +Ext.define('Ext.layout.component.field.Text', { + extend: Ext.layout.component.field.Field , + alias: 'layout.textfield', + + + type: 'textfield', + + + + setWidthInDom: true, + + canGrowWidth: true, + + beginLayoutCycle: function(ownerContext) { + this.callParent(arguments); + + + if (ownerContext.heightModel.shrinkWrap) { + ownerContext.inputContext.el.setStyle('height', ''); + } + }, + + measureContentWidth: function (ownerContext) { + var me = this, + owner = me.owner, + width = me.callParent(arguments), + inputContext = ownerContext.inputContext, + inputEl, value, calcWidth, max, min; + + if (owner.grow && me.canGrowWidth && !ownerContext.state.growHandled) { + inputEl = owner.inputEl; + + + value = Ext.util.Format.htmlEncode(inputEl.dom.value || (owner.hasFocus ? '' : owner.emptyText) || ''); + value += owner.growAppend; + calcWidth = inputEl.getTextWidth(value) + inputContext.getFrameInfo().width; + + max = owner.growMax; + min = Math.min(max, width); + max = Math.max(owner.growMin, max, min); + + + calcWidth = Ext.Number.constrain(calcWidth, owner.growMin, max); + inputContext.setWidth(calcWidth); + ownerContext.state.growHandled = true; + + + inputContext.domBlock(me, 'width'); + width = NaN; + } + return width; + }, + + publishInnerHeight: function(ownerContext, height) { + ownerContext.inputContext.setHeight(height - this.measureLabelErrorHeight(ownerContext)); + }, + + beginLayoutFixed: function(ownerContext, width, suffix) { + var me = this, + ieInputWidthAdjustment = me.ieInputWidthAdjustment; + + if (ieInputWidthAdjustment) { + me.adjustIEInputPadding(ownerContext); + if(suffix === 'px') { + width -= ieInputWidthAdjustment; + } + } + + me.callParent(arguments); + }, + + adjustIEInputPadding: function(ownerContext) { + + this.owner.bodyEl.setStyle('padding-right', this.ieInputWidthAdjustment + 'px'); + } +}); + + + +Ext.define('Ext.form.field.Text', { + extend: Ext.form.field.Base , + alias: 'widget.textfield', + + alternateClassName: ['Ext.form.TextField', 'Ext.form.Text'], + + + + + + + size: 20, + + + + + 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 : '', + + + + + emptyCls : Ext.baseCSSPrefix + 'form-empty-field', + + + requiredCls : Ext.baseCSSPrefix + 'form-required-field', + + + + componentLayout: 'textfield', + + + valueContainsPlaceholder : false, + + ariaRole: 'textbox', + + initComponent: function () { + var me = this; + + if (me.allowOnlyWhitespace === false) { + me.allowBlank = false; + } + + me.callParent(); + + me.addEvents( + + 'autosize', + + + 'keydown', + + 'keyup', + + 'keypress' + ); + me.addStateEvents('change'); + me.setGrowSizePolicy(); + }, + + + setGrowSizePolicy: function(){ + if (this.grow) { + this.shrinkWrap |= 1; + } + }, + + + initEvents : function(){ + var me = this, + el = me.inputEl; + + me.callParent(); + if(me.selectOnFocus || me.emptyText){ + me.mon(el, 'mousedown', me.onMouseDown, me); + } + if(me.maskRe || (me.vtype && me.disableKeyFilter !== true && (me.maskRe = Ext.form.field.VTypes[me.vtype+'Mask']))){ + me.mon(el, 'keypress', me.filterKeys, me); + } + + if (me.enableKeyEvents) { + me.mon(el, { + scope: me, + keyup: me.onKeyUp, + keydown: me.onKeyDown, + keypress: me.onKeyPress + }); + } + }, + + + isEqual: function(value1, value2) { + return this.isEqualAsString(value1, value2); + }, + + + onChange: function(newVal, oldVal) { + this.callParent(arguments); + this.autoSize(); + }, + + getSubTplData: function() { + var me = this, + value = me.getRawValue(), + isEmpty = me.emptyText && value.length < 1, + maxLength = me.maxLength, + placeholder; + + + + + if (me.enforceMaxLength) { + if (maxLength === Number.MAX_VALUE) { + maxLength = undefined; + } + } else { + maxLength = undefined; + } + + if (isEmpty) { + if (Ext.supports.Placeholder) { + placeholder = me.emptyText; + } else { + value = me.emptyText; + me.valueContainsPlaceholder = true; + } + } + + return Ext.apply(me.callParent(), { + maxLength : maxLength, + readOnly : me.readOnly, + placeholder : placeholder, + value : value, + fieldCls : me.fieldCls + ((isEmpty && (placeholder || value)) ? ' ' + me.emptyCls : '') + (me.allowBlank ? '' : ' ' + me.requiredCls) + }); + }, + + afterRender: function(){ + this.autoSize(); + this.callParent(); + }, + + onMouseDown: function(e){ + var me = this; + if(!me.hasFocus){ + me.mon(me.inputEl, 'mouseup', Ext.emptyFn, me, { single: true, preventDefault: true }); + } + }, + + + processRawValue: function(value) { + var me = this, + stripRe = me.stripCharsRe, + newValue; + + if (stripRe) { + newValue = value.replace(stripRe, ''); + if (newValue !== value) { + me.setRawValue(newValue); + value = newValue; + } + } + return value; + }, + + + onDisable: function(){ + this.callParent(); + if (Ext.isIE) { + this.inputEl.dom.unselectable = 'on'; + } + }, + + + onEnable: function(){ + this.callParent(); + if (Ext.isIE) { + this.inputEl.dom.unselectable = ''; + } + }, + + onKeyDown: function(e) { + this.fireEvent('keydown', this, e); + }, + + onKeyUp: function(e) { + this.fireEvent('keyup', this, e); + }, + + onKeyPress: function(e) { + this.fireEvent('keypress', this, e); + }, + + + reset : function(){ + this.callParent(); + this.applyEmptyText(); + }, + + applyEmptyText : function(){ + var me = this, + emptyText = me.emptyText, + isEmpty; + + if (me.rendered && emptyText) { + isEmpty = me.getRawValue().length < 1 && !me.hasFocus; + + if (Ext.supports.Placeholder) { + me.inputEl.dom.placeholder = emptyText; + } else if (isEmpty) { + me.setRawValue(emptyText); + me.valueContainsPlaceholder = true; + } + + + + if (isEmpty) { + me.inputEl.addCls(me.emptyCls); + } + + me.autoSize(); + } + }, + + afterFirstLayout: function() { + this.callParent(); + if (Ext.isIE && this.disabled) { + var el = this.inputEl; + if (el) { + el.dom.unselectable = 'on'; + } + } + }, + + + beforeFocus : function(){ + var me = this, + inputEl = me.inputEl, + emptyText = me.emptyText, + isEmpty; + + me.callParent(arguments); + if ((emptyText && !Ext.supports.Placeholder) && (inputEl.dom.value === me.emptyText && me.valueContainsPlaceholder)) { + me.setRawValue(''); + isEmpty = true; + inputEl.removeCls(me.emptyCls); + me.valueContainsPlaceholder = false; + } else if (Ext.supports.Placeholder) { + inputEl.removeCls(me.emptyCls); + } + + if (me.selectOnFocus || isEmpty) { + + if (Ext.isWebKit) { + if (!me.inputFocusTask) { + me.inputFocusTask = new Ext.util.DelayedTask(me.focusInput, me); + } + me.inputFocusTask.delay(1); + } else { + me.focusInput(); + } + } + }, + + focusInput: function(){ + var input = this.inputEl; + if (input) { + input = input.dom; + if (input) { + input.select(); + } + } + }, + + onFocus: function() { + var me = this; + me.callParent(arguments); + if (me.emptyText) { + me.autoSize(); + } + }, + + + postBlur : function(){ + var task = this.inputFocusTask; + + this.callParent(arguments); + this.applyEmptyText(); + + + if (task) { + task.cancel(); + } + + }, + + + filterKeys : function(e){ + + if (e.ctrlKey && !e.altKey) { + return; + } + var key = e.getKey(), + charCode = String.fromCharCode(e.getCharCode()); + + if((Ext.isGecko || Ext.isOpera) && (e.isNavKeyPress() || key === e.BACKSPACE || (key === e.DELETE && e.button === -1))){ + return; + } + + if((!Ext.isGecko && !Ext.isOpera) && e.isSpecialKey() && !charCode){ + return; + } + if(!this.maskRe.test(charCode)){ + e.stopEvent(); + } + }, + + getState: function() { + return this.addPropertyToState(this.callParent(), 'value'); + }, + + applyState: function(state) { + this.callParent(arguments); + if(state.hasOwnProperty('value')) { + this.setValue(state.value); + } + }, + + + getRawValue: function() { + var me = this, + v = me.callParent(); + if (v === me.emptyText && me.valueContainsPlaceholder) { + v = ''; + } + return v; + }, + + + setValue: function(value) { + var me = this, + inputEl = me.inputEl; + + if (inputEl && me.emptyText && !Ext.isEmpty(value)) { + inputEl.removeCls(me.emptyCls); + me.valueContainsPlaceholder = false; + } + + me.callParent(arguments); + + me.applyEmptyText(); + return me; + }, + + + getErrors: function(value) { + var me = this, + errors = me.callParent(arguments), + validator = me.validator, + vtype = me.vtype, + vtypes = Ext.form.field.VTypes, + regex = me.regex, + format = Ext.String.format, + msg, trimmed, isBlank; + + value = value || me.processRawValue(me.getRawValue()); + + if (Ext.isFunction(validator)) { + msg = validator.call(me, value); + if (msg !== true) { + errors.push(msg); + } + } + + trimmed = me.allowOnlyWhitespace ? value : Ext.String.trim(value); + + if (trimmed.length < 1 || (value === me.emptyText && me.valueContainsPlaceholder)) { + if (!me.allowBlank) { + errors.push(me.blankText); + } + + if (!me.validateBlank) { + return errors; + } + isBlank = true; + } + + + + if (!isBlank && value.length < me.minLength) { + errors.push(format(me.minLengthText, me.minLength)); + } + + if (value.length > me.maxLength) { + errors.push(format(me.maxLengthText, me.maxLength)); + } + + if (vtype) { + if (!vtypes[vtype](value, me)) { + errors.push(me.vtypeText || vtypes[vtype +'Text']); + } + } + + if (regex && !regex.test(value)) { + errors.push(me.regexText || me.invalidText); + } + + return errors; + }, + + + selectText : function(start, end){ + var me = this, + v = me.getRawValue(), + doFocus = true, + el = me.inputEl.dom, + undef, + range; + + if (v.length > 0) { + start = start === undef ? 0 : start; + end = end === undef ? v.length : end; + if (el.setSelectionRange) { + el.setSelectionRange(start, end); + } + else if(el.createTextRange) { + range = el.createTextRange(); + range.moveStart('character', start); + range.moveEnd('character', end - v.length); + range.select(); + } + doFocus = Ext.isGecko || Ext.isOpera; + } + if (doFocus) { + me.focus(); + } + }, + + + autoSize: function() { + var me = this; + if (me.grow && me.rendered) { + me.autoSizing = true; + me.updateLayout(); + } + }, + + afterComponentLayout: function() { + var me = this, + width; + + me.callParent(arguments); + if (me.autoSizing) { + width = me.inputEl.getWidth(); + if (width !== me.lastInputWidth) { + me.fireEvent('autosize', me, width); + me.lastInputWidth = width; + delete me.autoSizing; + } + } + }, + + onDestroy: function(){ + var me = this; + me.callParent(); + + if (me.inputFocusTask) { + me.inputFocusTask.cancel(); + me.inputFocusTask = null; + } + } +}); + + + +Ext.define('Ext.util.KeyNav', { + alternateClassName: 'Ext.KeyNav', + + + + statics: { + keyOptions: { + left: 37, + right: 39, + up: 38, + down: 40, + space: 32, + pageUp: 33, + pageDown: 34, + del: 46, + backspace: 8, + home: 36, + end: 35, + enter: 13, + esc: 27, + tab: 9 + } + }, + + constructor: function(config) { + var me = this; + if (arguments.length === 2) { + me.legacyConstructor.apply(me, arguments); + return; + } + me.setConfig(config); + }, + + + legacyConstructor: function(el, config) { + this.setConfig(Ext.apply({ + target: el + }, config)); + }, + + + setConfig: function(config) { + var me = this, + keymapCfg = { + target: config.target, + ignoreInputFields: config.ignoreInputFields, + eventName: me.getKeyEvent('forceKeyDown' in config ? config.forceKeyDown : me.forceKeyDown, config.eventName) + }, + map, keyCodes, defaultScope, keyName, binding; + + if (me.map) { + me.map.destroy(); + } + + if (config.processEvent) { + keymapCfg.processEvent = config.processEvent; + keymapCfg.processEventScope = config.processEventScope||me; + } + + + if (config.keyMap) { + map = me.map = config.keyMap; + } + + else { + map = me.map = new Ext.util.KeyMap(keymapCfg); + me.destroyKeyMap = true; + } + keyCodes = Ext.util.KeyNav.keyOptions; + defaultScope = config.scope || me; + + for (keyName in config) { + binding = config[keyName]; + + + + + if (binding && (keyName.length === 1 || (keyName = keyCodes[keyName]) || (!isNaN(keyName = parseInt(keyName, 10))))) { + if (typeof binding === 'function') { + binding = { + handler: binding, + defaultEventAction: (config.defaultEventAction !== undefined) ? config.defaultEventAction : me.defaultEventAction + }; + } + map.addBinding({ + key: keyName, + ctrl: binding.ctrl, + shift: binding.shift, + alt: binding.alt, + handler: Ext.Function.bind(me.handleEvent, binding.scope||defaultScope, binding.handler||binding.fn, true), + defaultEventAction: (binding.defaultEventAction !== undefined) ? binding.defaultEventAction : me.defaultEventAction + }); + } + } + + map.disable(); + if (!config.disabled) { + map.enable(); + } + }, + + + handleEvent: function(keyCode, event, handler){ + return handler.call(this, event); + }, + + + disabled: false, + + + defaultEventAction: "stopEvent", + + + forceKeyDown: false, + + + + + eventName: 'keypress', + + + + + + + + + + + destroy: function(removeEl) { + if (this.destroyKeyMap) { + this.map.destroy(removeEl); + } + delete this.map; + }, + + + enable: function() { + + if (this.map) { + this.map.enable(); + this.disabled = false; + } + }, + + + disable: function() { + + if (this.map) { + this.map.disable(); + } + this.disabled = true; + }, + + + setDisabled : function(disabled) { + this.map.setDisabled(disabled); + this.disabled = disabled; + }, + + + getKeyEvent: function(forceKeyDown, configuredEventName) { + if (forceKeyDown || (Ext.EventManager.useKeyDown && !configuredEventName)) { + return 'keydown'; + } else { + return configuredEventName||this.eventName; + } + } +}); + + + +Ext.define('Ext.util.ComponentDragger', { + extend: Ext.dd.DragTracker , + + + + + + + + autoStart: 500, + + + constructor: function(comp, config) { + this.comp = comp; + this.initialConstrainTo = config.constrainTo; + this.callParent([ config ]); + }, + + onStart: function(e) { + var me = this, + comp = me.comp; + + + me.startPosition = comp.getXY(); + + + + if (comp.ghost && !comp.liveDrag) { + me.proxy = comp.ghost(); + me.dragTarget = me.proxy.header.el; + } + + + if (me.constrain || me.constrainDelegate) { + me.constrainTo = me.calculateConstrainRegion(); + } + + if (comp.beginDrag) { + comp.beginDrag(); + } + }, + + calculateConstrainRegion: function() { + var me = this, + comp = me.comp, + constrainTo = me.initialConstrainTo, + constraintInsets = comp.constraintInsets, + constrainEl, + delegateRegion, + elRegion, + dragEl = me.proxy ? me.proxy.el : comp.el, + shadowSize = (!me.constrainDelegate && dragEl.shadow && comp.constrainShadow && !dragEl.shadowDisabled) ? dragEl.shadow.getShadowSize() : 0; + + + if (!(constrainTo instanceof Ext.util.Region)) { + constrainEl = Ext.fly(constrainTo); + constrainTo = constrainEl.getViewRegion(); + + + constrainTo.right = constrainTo.left + constrainEl.dom.clientWidth; + } else { + + constrainTo = constrainTo.copy(); + } + + + if (constraintInsets) { + constraintInsets = Ext.isObject(constraintInsets) ? constraintInsets : Ext.Element.parseBox(constraintInsets); + constrainTo.adjust(constraintInsets.top, constraintInsets.right, constraintInsets.bottom, constraintInsets.length); + } + + + if (shadowSize) { + constrainTo.adjust(shadowSize[0], -shadowSize[1], -shadowSize[2], shadowSize[3]); + } + + + + + if (!me.constrainDelegate) { + delegateRegion = Ext.fly(me.dragTarget).getRegion(); + elRegion = dragEl.getRegion(); + + constrainTo.adjust( + delegateRegion.top - elRegion.top, + delegateRegion.right - elRegion.right, + delegateRegion.bottom - elRegion.bottom, + delegateRegion.left - elRegion.left + ); + } + return constrainTo; + }, + + + onDrag: function(e) { + var me = this, + comp = (me.proxy && !me.comp.liveDrag) ? me.proxy : me.comp, + offset = me.getOffset(me.constrain || me.constrainDelegate ? 'dragTarget' : null); + + comp.setPagePosition(me.startPosition[0] + offset[0], me.startPosition[1] + offset[1]); + }, + + onEnd: function(e) { + var comp = this.comp; + if (comp.isDestroyed || comp.destroying) { + return; + } + + if (this.proxy && !comp.liveDrag) { + comp.unghost(); + } + if (comp.endDrag) { + comp.endDrag(); + } + } +}); + + + +Ext.define('Ext.window.Window', { + extend: Ext.panel.Panel , + + alternateClassName: 'Ext.Window', + + + + alias: 'widget.window', + + + + + + + + + + + + + + + + + + + + + + + baseCls: Ext.baseCSSPrefix + '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: Ext.baseCSSPrefix + 'window-item', + + initialAlphaNum: /^[a-z0-9]/, + + overlapHeader: true, + + ignoreHeaderBorderManagement: true, + + + alwaysFramed: true, + + + isRootCfg: { + isRoot: true + }, + + + isWindow: true, + + ariaRole: 'dialog', + + + initComponent: function() { + var me = this; + + + me.frame = false; + me.callParent(); + me.addEvents( + + + + + + 'resize', + + + 'maximize', + + + 'minimize', + + + 'restore' + ); + + if (me.plain) { + me.addClsWithUI('plain'); + } + + me.addStateEvents(['maximize', 'restore', 'resize', 'dragend']); + }, + + getElConfig: function () { + var me = this, + elConfig; + + elConfig = me.callParent(); + elConfig.tabIndex = -1; + return elConfig; + }, + + + + + getState: function() { + var me = this, + state = me.callParent() || {}, + maximized = !!me.maximized, + ghostBox = me.ghostBox, + pos; + + + state.maximized = maximized; + if (maximized) { + pos = me.restorePos; + } else if (ghostBox) { + + + pos = [ghostBox.x, ghostBox.y]; + } else { + pos = me.getPosition(); + } + Ext.apply(state, { + size: maximized ? me.restoreSize : me.getSize(), + pos: pos + }); + return state; + }, + + applyState: function(state){ + var me = this; + + if (state) { + me.maximized = state.maximized; + if (me.maximized) { + me.hasSavedRestore = true; + me.restoreSize = state.size; + me.restorePos = state.pos; + } else { + Ext.apply(me, { + width: state.size.width, + height: state.size.height, + x: state.pos[0], + y: state.pos[1] + }); + } + } + }, + + + onRender: function(ct, position) { + var me = this; + me.callParent(arguments); + me.focusEl = me.el; + + + if (me.maximizable) { + me.header.on({ + scope: me, + dblclick: me.toggleMaximize + }); + } + }, + + + afterRender: function() { + var me = this, + header = me.header, + keyMap; + + me.callParent(); + + + if (me.maximized) { + me.maximized = false; + me.maximize(); + if (header) { + header.removeCls(header.indicateDragCls); + } + } + + if (me.closable) { + keyMap = me.getKeyMap(); + keyMap.on(27, me.onEsc, me); + } else { + keyMap = me.keyMap; + } + if (keyMap && me.hidden) { + keyMap.disable(); + } + }, + + + + initDraggable: function() { + + this.initSimpleDraggable(); + }, + + initResizable: function(){ + this.callParent(arguments); + if (this.maximized) { + this.resizer.disable(); + } + }, + + + onEsc: function(k, e) { + var mgr = Ext['FocusManager']; + + + if (!Ext.enableFocusManager || mgr.focusedCmp === this) { + e.stopEvent(); + this.close(); + } + }, + + + beforeDestroy: function() { + var me = this; + if (me.rendered) { + delete me.animateTarget; + me.hide(); + Ext.destroy( + me.keyMap + ); + } + me.callParent(); + }, + + + addTools: function() { + var me = this, + tools = me.tools, + noArgs = []; + + + me.callParent(); + + if (me.minimizable) { + tools.push({ + type: 'minimize', + handler: Ext.Function.bind(me.minimize, me, noArgs) + }); + } + if (me.maximizable) { + tools.push({ + type: 'maximize', + handler: Ext.Function.bind(me.toggleMaximize, me, noArgs) + }); + } + }, + + + getFocusEl: function() { + return this.getDefaultFocus(); + }, + + + getDefaultFocus: function() { + var me = this, + result, + defaultComp = me.defaultButton || me.defaultFocus, + selector; + + if (defaultComp !== undefined) { + + if (Ext.isNumber(defaultComp)) { + result = me.query('button')[defaultComp]; + } + + else if (Ext.isString(defaultComp)) { + selector = defaultComp; + + + if (selector.match(me.initialAlphaNum)) { + result = me.down('#' + selector); + } + + if (!result) { + result = me.down(selector); + } + } + + else if (defaultComp.focus) { + result = defaultComp; + } + } + return result || me.el; + }, + + + onFocus: function() { + var me = this, + focusDescendant; + + + if (Ext.enableFocusManager || ((focusDescendant = me.getDefaultFocus()) === me)) { + me.callParent(arguments); + } else { + focusDescendant.focus(); + } + }, + + onShow: function() { + var me = this; + + me.callParent(arguments); + if (me.expandOnShow) { + me.expand(false); + } + me.syncMonitorWindowResize(); + + if (me.keyMap) { + me.keyMap.enable(); + } + }, + + + doClose: function() { + var me = this; + + + if (me.hidden) { + me.fireEvent('close', me); + if (me.closeAction == 'destroy') { + me.destroy(); + } + } else { + + me.hide(me.animateTarget, me.doClose, me); + } + }, + + + afterHide: function() { + var me = this; + + + me.syncMonitorWindowResize(); + + + if (me.keyMap) { + me.keyMap.disable(); + } + + + me.callParent(arguments); + }, + + + onWindowResize: function() { + var me = this, + sizeModel; + + if (me.maximized) { + me.fitContainer(); + } else { + sizeModel = me.getSizeModel(); + if (sizeModel.width.natural || sizeModel.height.natural) { + me.updateLayout(); + } + me.doConstrain(); + } + + }, + + + minimize: function() { + this.fireEvent('minimize', this); + return this; + }, + + resumeHeaderLayout: function(changed) { + this.header.resumeLayouts(changed ? this.isRootCfg : null); + }, + + afterCollapse: function() { + var me = this, + header = me.header, + tools = me.tools; + + if (header && me.maximizable) { + header.suspendLayouts(); + tools.maximize.hide(); + this.resumeHeaderLayout(true); + } + if (me.resizer) { + me.resizer.disable(); + } + me.callParent(arguments); + }, + + afterExpand: function() { + var me = this, + header = me.header, + tools = me.tools, + changed; + + + if (header) { + header.suspendLayouts(); + if (me.maximizable) { + tools.maximize.show(); + changed = true; + } + this.resumeHeaderLayout(changed); + } + if (me.resizer) { + me.resizer.enable(); + } + me.callParent(arguments); + }, + + + maximize: function(animate) { + var me = this, + header = me.header, + tools = me.tools, + width = me.width, + height = me.height, + restore, changed; + + if (!me.maximized) { + me.expand(false); + if (!me.hasSavedRestore) { + restore = me.restoreSize = { + width: Ext.isNumber(width) ? width : null, + height: Ext.isNumber(height) ? height : null + }; + + me.restorePos = me.getPosition(true); + } + + + if (header) { + header.suspendLayouts(); + if (tools.maximize) { + tools.maximize.setType('restore'); + } + if (me.collapseTool) { + me.collapseTool.hide(); + changed = true; + } + me.resumeHeaderLayout(changed); + } + + me.el.disableShadow(); + + if (me.dd) { + me.dd.disable(); + if (header) { + header.removeCls(header.indicateDragCls); + } + } + if (me.resizer) { + me.resizer.disable(); + } + + me.el.addCls(Ext.baseCSSPrefix + 'window-maximized'); + me.container.addCls(Ext.baseCSSPrefix + 'window-maximized-ct'); + + me.syncMonitorWindowResize(); + me.fitContainer(animate = (animate || !!me.animateTarget) ? { + callback: function() { + me.maximized = true; + me.fireEvent('maximize', me); + } + } : null); + if (!animate) { + me.maximized = true; + me.fireEvent('maximize', me); + } + } + return me; + }, + + + restore: function(animate) { + var me = this, + tools = me.tools, + header = me.header, + newBox = me.restoreSize, + changed; + + if (me.maximized) { + me.hasSavedRestore = null; + me.removeCls(Ext.baseCSSPrefix + 'window-maximized'); + + + if (header) { + header.suspendLayouts(); + if (tools.maximize) { + tools.maximize.setType('maximize'); + } + if (me.collapseTool) { + me.collapseTool.show(); + changed = true; + } + me.resumeHeaderLayout(changed); + } + + + newBox.x = me.restorePos[0]; + newBox.y = me.restorePos[1]; + me.setBox(newBox, animate = (animate || !!me.animateTarget) ? { + callback: function() { + me.el.enableShadow(true); + me.maximized = false; + me.fireEvent('restore', me); + } + } : null); + + + me.restorePos = me.restoreSize = null; + + + if (me.dd) { + me.dd.enable(); + if (header) { + header.addCls(header.indicateDragCls); + } + } + + if (me.resizer) { + me.resizer.enable(); + } + + me.container.removeCls(Ext.baseCSSPrefix + 'window-maximized-ct'); + + me.syncMonitorWindowResize(); + + if (!animate) { + me.el.enableShadow(true); + me.maximized = false; + me.fireEvent('restore', me); + } + } + return me; + }, + + + syncMonitorWindowResize: function () { + var me = this, + currentlyMonitoring = me._monitoringResize, + + yes = me.monitorResize || me.constrain || me.constrainHeader || me.maximized, + + veto = me.hidden || me.destroying || me.isDestroyed; + + if (yes && !veto) { + + if (!currentlyMonitoring) { + + + Ext.EventManager.onWindowResize(me.onWindowResize, me, {buffer: 1}); + me._monitoringResize = true; + } + } else if (currentlyMonitoring) { + + Ext.EventManager.removeResizeListener(me.onWindowResize, me); + me._monitoringResize = false; + } + }, + + + toggleMaximize: function() { + return this[this.maximized ? 'restore': 'maximize'](); + }, + + createGhost: function() { + var ghost = this.callParent(arguments); + ghost.xtype = 'window'; + return ghost; + } + +}); + + + +Ext.define('Ext.layout.component.field.TextArea', { + extend: Ext.layout.component.field.Text , + alias: 'layout.textareafield', + + type: 'textareafield', + + canGrowWidth: false, + + naturalSizingProp: 'cols', + + beginLayout: function(ownerContext){ + this.callParent(arguments); + ownerContext.target.inputEl.setStyle('height', ''); + }, + + measureContentHeight: function (ownerContext) { + var me = this, + owner = me.owner, + height = me.callParent(arguments), + inputContext, inputEl, value, max, curWidth, calcHeight; + + if (owner.grow && !ownerContext.state.growHandled) { + inputContext = ownerContext.inputContext; + inputEl = owner.inputEl; + curWidth = inputEl.getWidth(true); + + + value = Ext.util.Format.htmlEncode(inputEl.dom.value) || ' '; + value += owner.growAppend; + + + value = value.replace(/\n/g, '
'); + + + calcHeight = Ext.util.TextMetrics.measure(inputEl, value, curWidth).height + + inputContext.getBorderInfo().height + inputContext.getPaddingInfo().height; + + + calcHeight = Ext.Number.constrain(calcHeight, owner.growMin, owner.growMax); + inputContext.setHeight(calcHeight); + ownerContext.state.growHandled = true; + + + inputContext.domBlock(me, 'height'); + height = NaN; + } + return height; + } +}); + + + +Ext.define('Ext.form.field.TextArea', { + extend: Ext.form.field.Text , + alias: ['widget.textareafield', 'widget.textarea'], + alternateClassName: 'Ext.form.TextArea', + + + + + + + + + + + + + + + + + fieldSubTpl: [ + '', + { + disableFormats: true + } + ], + + + growMin: 60, + + + growMax: 1000, + + + growAppend: '\n-', + + + cols: 20, + + + rows: 4, + + + enterIsSpecial: false, + + + preventScrollbars: false, + + + componentLayout: 'textareafield', + + setGrowSizePolicy: Ext.emptyFn, + + returnRe: /\r/g, + + inputCls: Ext.baseCSSPrefix + 'form-textarea', + + + getSubTplData: function() { + var me = this, + fieldStyle = me.getFieldStyle(), + ret = me.callParent(); + + if (me.grow) { + if (me.preventScrollbars) { + ret.fieldStyle = (fieldStyle||'') + ';overflow:hidden;height:' + me.growMin + 'px'; + } + } + + Ext.applyIf(ret, { + cols: me.cols, + rows: me.rows + }); + + return ret; + }, + + afterRender: function () { + var me = this; + + me.callParent(arguments); + + me.needsMaxCheck = me.enforceMaxLength && me.maxLength !== Number.MAX_VALUE && !Ext.supports.TextAreaMaxLength; + if (me.needsMaxCheck) { + me.inputEl.on('paste', me.onPaste, me); + } + }, + + + + + + + + transformRawValue: function(value){ + return this.stripReturns(value); + }, + + transformOriginalValue: function(value){ + return this.stripReturns(value); + }, + + getValue: function(){ + return this.stripReturns(this.callParent()); + }, + + valueToRaw: function(value){ + value = this.stripReturns(value); + return this.callParent([value]); + }, + + stripReturns: function(value){ + if (value && typeof value === 'string') { + value = value.replace(this.returnRe, ''); + } + return value; + }, + + onPaste: function(e){ + var me = this; + if (!me.pasteTask) { + me.pasteTask = new Ext.util.DelayedTask(me.pasteCheck, me); + } + + me.pasteTask.delay(1); + }, + + pasteCheck: function(){ + var me = this, + value = me.getValue(), + max = me.maxLength; + + if (value.length > max) { + value = value.substr(0, max); + me.setValue(value); + } + }, + + + fireKey: function(e) { + var me = this, + key = e.getKey(), + value; + + if (e.isSpecialKey() && (me.enterIsSpecial || (key !== e.ENTER || e.hasModifier()))) { + me.fireEvent('specialkey', me, e); + } + + if (me.needsMaxCheck && key !== e.BACKSPACE && key !== e.DELETE && !e.isNavKeyPress() && !me.isCutCopyPasteSelectAll(e, key)) { + value = me.getValue(); + if (value.length >= me.maxLength) { + e.stopEvent(); + } + } + }, + + isCutCopyPasteSelectAll: function(e, key) { + if (e.ctrlKey) { + return key === e.A || key === e.C || key === e.V || key === e.X; + } + return false; + }, + + + autoSize: function() { + var me = this, + height; + + if (me.grow && me.rendered) { + me.updateLayout(); + height = me.inputEl.getHeight(); + if (height !== me.lastInputHeight) { + + me.fireEvent('autosize', me, height); + me.lastInputHeight = height; + } + } + }, + + beforeDestroy: function(){ + var task = this.pasteTask; + if (task) { + task.cancel(); + this.pasteTask = null; + } + this.callParent(); + } +}); + + + +Ext.define('Ext.form.field.Display', { + extend: Ext.form.field.Base , + alias: 'widget.displayfield', + + alternateClassName: ['Ext.form.DisplayField', 'Ext.form.Display'], + + ariaRole: 'textbox', + + fieldSubTpl: [ + '
style="{fieldStyle}"', + ' class="{fieldCls}">{value}
', + { + compiled: true, + disableFormats: true + } + ], + + + readOnly: true, + + + fieldCls: Ext.baseCSSPrefix + 'form-display-field', + + fieldBodyCls: Ext.baseCSSPrefix + 'form-display-field-body', + + + htmlEncode: false, + + + + + + noWrap: false, + + + validateOnChange: false, + + initEvents: Ext.emptyFn, + + submitValue: false, + + valueToRaw: function(value) { + if (!value && value !== 0) { + return ''; + } else { + return value; + } + }, + + isDirty: function(){ + return false; + }, + + isValid: function() { + return true; + }, + + validate: function() { + return true; + }, + + getRawValue: function() { + return this.rawValue; + }, + + setRawValue: function(value) { + var me = this; + + value = Ext.value(value, ''); + me.rawValue = value; + if (me.rendered) { + me.inputEl.dom.innerHTML = me.getDisplayValue(); + me.updateLayout(); + } + return value; + }, + + + getDisplayValue: function() { + var me = this, + value = this.getRawValue(), + display; + if (me.renderer) { + display = me.renderer.call(me.scope || me, value, me); + } else { + display = me.htmlEncode ? Ext.util.Format.htmlEncode(value) : value; + } + return display; + }, + + getSubTplData: function() { + var ret = this.callParent(arguments); + + ret.value = this.getDisplayValue(); + + return ret; + } + + + + + +}); + + + +Ext.define('Ext.layout.container.Anchor', { + + + + alias: 'layout.anchor', + extend: Ext.layout.container.Auto , + alternateClassName: 'Ext.layout.AnchorLayout', + + + + type: 'anchor', + + + + + defaultAnchor: '100%', + + parseAnchorRE: /^(r|right|b|bottom)$/i, + + manageOverflow: true, + + beginLayoutCycle: function (ownerContext) { + var me = this, + dimensions = 0, + anchorSpec, childContext, childItems, i, length, target; + + me.callParent(arguments); + + childItems = ownerContext.childItems; + length = childItems.length; + + for (i = 0; i < length; ++i) { + childContext = childItems[i]; + anchorSpec = childContext.target.anchorSpec; + + if (anchorSpec) { + if (childContext.widthModel.calculated && anchorSpec.right) { + dimensions |= 1; + } + if (childContext.heightModel.calculated && anchorSpec.bottom) { + dimensions |= 2; + } + + if (dimensions == 3) { + break; + } + } + } + + ownerContext.anchorDimensions = dimensions; + + me.sanityCheck(ownerContext); + }, + + calculateItems: function (ownerContext, containerSize) { + var me = this, + childItems = ownerContext.childItems, + length = childItems.length, + gotHeight = containerSize.gotHeight, + gotWidth = containerSize.gotWidth, + ownerHeight = containerSize.height, + ownerWidth = containerSize.width, + knownDimensions = (gotWidth ? 1 : 0) | (gotHeight ? 2 : 0), + anchorDimensions = ownerContext.anchorDimensions, + anchorSpec, childContext, childMargins, height, i, width; + + if (!anchorDimensions) { + return true; + } + + for (i = 0; i < length; i++) { + childContext = childItems[i]; + childMargins = childContext.getMarginInfo(); + anchorSpec = childContext.target.anchorSpec; + + + + + + if (gotWidth && childContext.widthModel.calculated) { + width = anchorSpec.right(ownerWidth) - childMargins.width; + width = me.adjustWidthAnchor(width, childContext); + + childContext.setWidth(width); + } + + + if (gotHeight && childContext.heightModel.calculated) { + height = anchorSpec.bottom(ownerHeight) - childMargins.height; + height = me.adjustHeightAnchor(height, childContext); + + childContext.setHeight(height); + } + } + + + return (knownDimensions & anchorDimensions) === anchorDimensions; + }, + + sanityCheck: function (ownerContext) { + var shrinkWrapWidth = ownerContext.widthModel.shrinkWrap, + shrinkWrapHeight = ownerContext.heightModel.shrinkWrap, + children = ownerContext.childItems, + anchorSpec, comp, childContext, + i, length; + + for (i = 0, length = children.length; i < length; ++i) { + childContext = children[i]; + comp = childContext.target; + anchorSpec = comp.anchorSpec; + + if (anchorSpec) { + if (childContext.widthModel.calculated && anchorSpec.right) { + if (shrinkWrapWidth) { + Ext.log({ + level: 'warn', + msg: 'Right anchor on '+comp.id+' in shrinkWrap width container' + }); + } + } + + if (childContext.heightModel.calculated && anchorSpec.bottom) { + if (shrinkWrapHeight) { + Ext.log({ + level: 'warn', + msg: 'Bottom anchor on '+comp.id+' in shrinkWrap height container' + }); + } + } + } + } + }, + + + anchorFactory: { + offset: function (delta) { + return function(v) { + return v + delta; + }; + }, + ratio: function (ratio) { + return function(v) { + return Math.floor(v * ratio); + }; + }, + standard: function (diff) { + return function(v) { + return v - diff; + }; + } + }, + + parseAnchor: function(a, start, cstart) { + if (a && a != 'none') { + var factory = this.anchorFactory, + delta; + + if (this.parseAnchorRE.test(a)) { + return factory.standard(cstart - start); + } + if (a.indexOf('%') != -1) { + return factory.ratio(parseFloat(a.replace('%', '')) * 0.01); + } + delta = parseInt(a, 10); + if (!isNaN(delta)) { + return factory.offset(delta); + } + } + return null; + }, + + + adjustWidthAnchor: function(value, childContext) { + return value; + }, + + + adjustHeightAnchor: function(value, childContext) { + return value; + }, + + configureItem: function(item) { + var me = this, + owner = me.owner, + anchor= item.anchor, + anchorsArray, + anchorWidth, + anchorHeight; + + me.callParent(arguments); + + if (!item.anchor && item.items && !Ext.isNumber(item.width) && !(Ext.isIE6 && Ext.isStrict)) { + item.anchor = anchor = me.defaultAnchor; + } + + + if (owner.anchorSize) { + if (typeof owner.anchorSize == 'number') { + anchorWidth = owner.anchorSize; + } else { + anchorWidth = owner.anchorSize.width; + anchorHeight = owner.anchorSize.height; + } + } else { + anchorWidth = owner.initialConfig.width; + anchorHeight = owner.initialConfig.height; + } + + if (anchor) { + + anchorsArray = anchor.split(' '); + item.anchorSpec = { + right: me.parseAnchor(anchorsArray[0], item.initialConfig.width, anchorWidth), + bottom: me.parseAnchor(anchorsArray[1], item.initialConfig.height, anchorHeight) + }; + } + }, + + sizePolicy: { + $: { + readsWidth: 1, + readsHeight: 1, + setsWidth: 0, + setsHeight: 0 + }, + b: { + readsWidth: 1, + readsHeight: 0, + setsWidth: 0, + setsHeight: 1 + }, + r: { + $: { + readsWidth: 0, + readsHeight: 1, + setsWidth: 1, + setsHeight: 0 + }, + b: { + readsWidth: 0, + readsHeight: 0, + setsWidth: 1, + setsHeight: 1 + } + } + }, + + getItemSizePolicy: function (item) { + var anchorSpec = item.anchorSpec, + key = '$', + policy = this.sizePolicy, + sizeModel; + + if (anchorSpec) { + sizeModel = this.owner.getSizeModel(); + if (anchorSpec.right && !sizeModel.width.shrinkWrap) { + policy = policy.r; + } + if (anchorSpec.bottom && !sizeModel.height.shrinkWrap) { + key = 'b'; + } + } + + return policy[key]; + } +}); + + + +Ext.define('Ext.layout.component.ProgressBar', { + + + + alias: ['layout.progressbar'], + + extend: Ext.layout.component.Auto , + + + + type: 'progressbar', + + beginLayout: function (ownerContext) { + var me = this, + i, textEls; + + me.callParent(arguments); + + if (!ownerContext.textEls) { + textEls = me.owner.textEl; + + if (textEls.isComposite) { + ownerContext.textEls = []; + textEls = textEls.elements; + for (i = textEls.length; i--; ) { + ownerContext.textEls[i] = ownerContext.getEl(Ext.get(textEls[i])); + } + } else { + ownerContext.textEls = [ ownerContext.getEl('textEl') ]; + } + } + }, + + calculate: function(ownerContext) { + var me = this, + i, textEls, width; + + me.callParent(arguments); + + if (Ext.isNumber(width = ownerContext.getProp('width'))) { + width -= ownerContext.getBorderInfo().width; + textEls = ownerContext.textEls; + + for (i = textEls.length; i--; ) { + textEls[i].setWidth(width); + } + } else { + me.done = false; + } + } +}); + + + +Ext.define('Ext.ProgressBar', { + extend: Ext.Component , + alias: 'widget.progressbar', + + + + + + + + + + + + + + + + + + baseCls: Ext.baseCSSPrefix + 'progress', + + + animate: false, + + + text: '', + + + waitTimer: null, + + childEls: [ + 'bar' + ], + + renderTpl: [ + '', + '
{text}
', + '
', + '' + ], + + componentLayout: 'progressbar', + + ariaRole: 'progressbar', + + + initComponent: function() { + this.callParent(); + + this.addEvents( + + "update" + ); + }, + + initRenderData: function() { + var me = this; + return Ext.apply(me.callParent(), { + internalText : !me.hasOwnProperty('textEl'), + text : me.text || ' ', + percentage : me.value ? me.value * 100 : 0 + }); + }, + + onRender : function() { + var me = this; + + me.callParent(arguments); + + + if (me.textEl) { + me.textEl = Ext.get(me.textEl); + me.updateText(me.text); + } + + else { + + + me.textEl = me.el.select('.' + me.baseCls + '-text'); + } + }, + + + updateProgress: function(value, text, animate) { + var me = this, + oldValue = me.value; + + me.value = value || 0; + if (text) { + me.updateText(text); + } + if (me.rendered && !me.isDestroyed) { + if (animate === true || (animate !== false && me.animate)) { + me.bar.stopAnimation(); + me.bar.animate(Ext.apply({ + from: { + width: (oldValue * 100) + '%' + }, + to: { + width: (me.value * 100) + '%' + } + }, me.animate)); + } else { + me.bar.setStyle('width', (me.value * 100) + '%'); + } + } + me.fireEvent('update', me, me.value, text); + return me; + }, + + + updateText: function(text) { + var me = this; + + me.text = text; + if (me.rendered) { + me.textEl.update(me.text); + } + return me; + }, + + applyText : function(text) { + this.updateText(text); + }, + + getText: function(){ + return this.text; + }, + + + wait: function(o) { + var me = this, scope; + + if (!me.waitTimer) { + scope = me; + o = o || {}; + me.updateText(o.text); + me.waitTimer = Ext.TaskManager.start({ + run: function(i){ + var inc = o.increment || 10; + i -= 1; + me.updateProgress(((((i+inc)%inc)+1)*(100/inc))*0.01, null, o.animate); + }, + interval: o.interval || 1000, + duration: o.duration, + onStop: function(){ + if (o.fn) { + o.fn.apply(o.scope || me); + } + me.reset(); + }, + scope: scope + }); + } + return me; + }, + + + isWaiting: function(){ + return this.waitTimer !== null; + }, + + + reset: function(hide){ + var me = this; + + me.updateProgress(0); + me.clearTimer(); + if (hide === true) { + me.hide(); + } + return me; + }, + + + clearTimer: function(){ + var me = this; + + if (me.waitTimer) { + me.waitTimer.onStop = null; + Ext.TaskManager.stop(me.waitTimer); + me.waitTimer = null; + } + }, + + onDestroy: function(){ + var me = this, + bar = me.bar; + + me.clearTimer(); + if (me.rendered) { + if (me.textEl.isComposite) { + me.textEl.clear(); + } + Ext.destroyMembers(me, 'textEl', 'progressBar'); + if (bar && me.animate) { + bar.stopAnimation(); + } + } + me.callParent(); + } +}); + + + + + +Ext.define('Ext.window.MessageBox', { + extend: Ext.window.Window , + + + + + + + + + + + + + alias: 'widget.messagebox', + + + OK : 1, + + YES : 2, + + NO : 4, + + CANCEL : 8, + + OKCANCEL : 9, + + YESNO : 6, + + YESNOCANCEL : 14, + + INFO : Ext.baseCSSPrefix + 'message-box-info', + + WARNING : Ext.baseCSSPrefix + 'message-box-warning', + + QUESTION : Ext.baseCSSPrefix + 'message-box-question', + + ERROR : Ext.baseCSSPrefix + 'message-box-error', + + + hideMode: 'offsets', + closeAction: 'hide', + resizable: false, + title: ' ', + + defaultMinWidth: 250, + defaultMaxWidth: 600, + defaultMinHeight: 110, + defaultMaxHeight: 500, + + + + minWidth: null, + maxWidth: null, + minHeight: null, + maxHeight: null, + constrain: true, + + cls: [Ext.baseCSSPrefix + 'message-box', Ext.baseCSSPrefix + 'hide-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' + }, + + + iconHeight: 35, + iconWidth: 50, + + ariaRole: 'alertdialog', + + makeButton: function(btnIdx) { + var btnId = this.buttonIds[btnIdx]; + return new Ext.button.Button({ + handler: this.btnCallback, + itemId: btnId, + scope: this, + text: this.buttonText[btnId], + minWidth: 75 + }); + }, + + btnCallback: function(btn) { + var me = this, + value, + field; + + if (me.cfg.prompt || me.cfg.multiline) { + if (me.cfg.multiline) { + field = me.textArea; + } else { + field = me.textField; + } + value = field.getValue(); + field.reset(); + } + + + me.hide(); + me.userCallback(btn.itemId, value, me.cfg); + }, + + hide: function() { + var me = this, + cls = me.cfg ? me.cfg.cls : ''; + + me.progressBar.reset(); + if (cls) { + me.removeCls(cls); + } + me.callParent(arguments); + }, + + constructor: function(cfg) { + var me = this; + + me.callParent(arguments); + + + + me.minWidth = me.defaultMinWidth = (me.minWidth || me.defaultMinWidth); + me.maxWidth = me.defaultMaxWidth = (me.maxWidth || me.defaultMaxWidth); + me.minHeight = me.defaultMinHeight = (me.minHeight || me.defaultMinHeight); + me.maxHeight = me.defaultMaxHeight = (me.maxHeight || me.defaultMaxHeight); + }, + + initComponent: function(cfg) { + var me = this, + baseId = me.id, + i, button; + + + me.title = me.title || ' '; + me.iconCls = me.iconCls || ''; + + me.topContainer = new Ext.container.Container({ + layout: 'hbox', + padding: 10, + style: { + overflow: 'hidden' + }, + items: [ + me.iconComponent = new Ext.Component({ + width: me.iconWidth, + height: me.iconHeight + }), + me.promptContainer = new Ext.container.Container({ + flex: 1, + layout: 'anchor', + items: [ + me.msg = new Ext.form.field.Display({ + id: baseId + '-displayfield', + cls: me.baseCls + '-text' + }), + me.textField = new Ext.form.field.Text({ + id: baseId + '-textfield', + anchor: '100%', + enableKeyEvents: true, + listeners: { + keydown: me.onPromptKey, + scope: me + } + }), + me.textArea = new Ext.form.field.TextArea({ + id: baseId + '-textarea', + anchor: '100%', + height: 75 + }) + ] + }) + ] + }); + me.progressBar = new Ext.ProgressBar({ + id: baseId + '-progressbar', + margin: '0 10 10 10' + }); + + me.items = [me.topContainer, me.progressBar]; + + + me.msgButtons = []; + for (i = 0; i < 4; i++) { + button = me.makeButton(i); + me.msgButtons[button.itemId] = button; + me.msgButtons.push(button); + } + me.bottomTb = new Ext.toolbar.Toolbar({ + id: baseId + '-toolbar', + ui: 'footer', + dock: 'bottom', + layout: { + pack: 'center' + }, + items: [ + me.msgButtons[0], + me.msgButtons[1], + me.msgButtons[2], + me.msgButtons[3] + ] + }); + me.dockedItems = [me.bottomTb]; + me.on('close', me.onClose, me); + me.callParent(); + }, + + onClose: function(){ + var btn = this.header.child('[type=close]'); + + btn.itemId = 'cancel'; + this.btnCallback(btn); + delete btn.itemId; + }, + + onPromptKey: function(textField, e) { + var me = this; + + if (e.keyCode === e.RETURN || e.keyCode === 10) { + if (me.msgButtons.ok.isVisible()) { + me.msgButtons.ok.handler.call(me, me.msgButtons.ok); + } else if (me.msgButtons.yes.isVisible()) { + me.msgButtons.yes.handler.call(me, me.msgButtons.yes); + } + } + }, + + reconfigure: function(cfg) { + var me = this, + buttons = 0, + hideToolbar = true, + oldButtonText = me.buttonText, + resizer = me.resizer, + header = me.header, + headerCfg = header && !header.isHeader, + resizeTracker, title, width, height, i, textArea, + textField, msg, progressBar, msgButtons; + + + me.updateButtonText(); + + cfg = cfg || {}; + me.cfg = cfg; + if (cfg.width) { + width = cfg.width; + } + + if (cfg.height) { + height = cfg.height; + } + + me.minWidth = cfg.minWidth || me.defaultMinWidth; + me.maxWidth = cfg.maxWidth || me.defaultMaxWidth; + me.minHeight = cfg.minHeight || me.defaultMinHeight; + me.maxHeight = cfg.maxHeight || me.defaultMaxHeight; + + if (resizer) { + resizeTracker = resizer.resizeTracker; + resizer.minWidth = resizeTracker.minWidth = me.minWidth; + resizer.maxWidth = resizeTracker.maxWidth = me.maxWidth; + resizer.minHeight = resizeTracker.minHeight = me.minHeight; + resizer.maxHeight = resizeTracker.maxHeight = me.maxHeight; + } + + + delete me.defaultFocus; + if (cfg.defaultFocus) { + me.defaultFocus = cfg.defaultFocus; + } + + + me.animateTarget = cfg.animateTarget || undefined; + + + me.modal = cfg.modal !== false; + + + + + + + me.setTitle(cfg.title || (headerCfg && header.title) || me.title); + me.setIconCls(cfg.iconCls || (headerCfg && header.iconCls) || me.iconCls); + + + if (Ext.isObject(cfg.buttons)) { + me.buttonText = cfg.buttons; + buttons = 0; + } else { + me.buttonText = cfg.buttonText || me.buttonText; + buttons = Ext.isNumber(cfg.buttons) ? cfg.buttons : 0; + } + + + + buttons = buttons | me.updateButtonText(); + + + me.buttonText = oldButtonText; + + + + Ext.suspendLayouts(); + delete me.width; + delete me.height; + if (width || height) { + if (width) { + me.setWidth(width); + } + + if (height) { + me.setHeight(height); + } + } + me.hidden = false; + if (!me.rendered) { + me.render(Ext.getBody()); + } + + + me.closable = cfg.closable !== false && !cfg.wait; + + + header = me.header; + + if (header) { + header.child('[type=close]').setVisible(me.closable); + + + if (!cfg.title && !me.closable && !cfg.iconCls) { + header.hide(); + } else { + header.show(); + } + } + + + me.liveDrag = !cfg.proxyDrag; + + + me.userCallback = Ext.Function.bind(cfg.callback ||cfg.fn || Ext.emptyFn, cfg.scope || Ext.global); + + + me.setIcon(cfg.icon, cfg.iconWidth, cfg.iconHeight); + + + msg = me.msg; + if (cfg.msg) { + msg.setValue(cfg.msg); + msg.show(); + } else { + msg.hide(); + } + + + textArea = me.textArea; + textField = me.textField; + if (cfg.prompt || cfg.multiline) { + me.multiline = cfg.multiline; + if (cfg.multiline) { + textArea.setValue(cfg.value); + textArea.setHeight(cfg.defaultTextHeight || me.defaultTextHeight); + textArea.show(); + textField.hide(); + me.defaultFocus = textArea; + } else { + textField.setValue(cfg.value); + textArea.hide(); + textField.show(); + me.defaultFocus = textField; + } + } else { + textArea.hide(); + textField.hide(); + } + + + progressBar = me.progressBar; + if (cfg.progress || cfg.wait) { + progressBar.show(); + me.updateProgress(0, cfg.progressText); + if(cfg.wait === true){ + progressBar.wait(cfg.waitConfig); + } + } else { + progressBar.hide(); + } + + + msgButtons = me.msgButtons; + for (i = 0; i < 4; i++) { + if (buttons & Math.pow(2, i)) { + + + if (!me.defaultFocus) { + me.defaultFocus = msgButtons[i]; + } + msgButtons[i].show(); + hideToolbar = false; + } else { + msgButtons[i].hide(); + } + } + + + if (hideToolbar) { + me.bottomTb.hide(); + } else { + me.bottomTb.show(); + } + Ext.resumeLayouts(true); + }, + + + updateButtonText: function() { + var me = this, + buttonText = me.buttonText, + buttons = 0, + btnId, + btn; + + for (btnId in buttonText) { + if (buttonText.hasOwnProperty(btnId)) { + btn = me.msgButtons[btnId]; + if (btn) { + if (me.cfg && me.cfg.buttonText) { + buttons = buttons | Math.pow(2, Ext.Array.indexOf(me.buttonIds, btnId)); + } + if (btn.text != buttonText[btnId]) { + btn.setText(buttonText[btnId]); + } + } + } + } + return buttons; + }, + + + show: function(cfg) { + var me = this, + visibleFocusables; + + cfg = cfg || {}; + + + if (Ext.AbstractComponent.layoutSuspendCount) { + Ext.on({ + resumelayouts: function() { + me.show(cfg); + }, + single: true + }); + return me; + } + + me.reconfigure(cfg); + if (cfg.cls) { + me.addCls(cfg.cls); + } + + + + visibleFocusables = me.query('textfield:not([hidden]),textarea:not([hidden]),button:not([hidden])'); + me.preventFocusOnActivate = !visibleFocusables.length; + + + + me.hidden = true; + me.callParent(); + return me; + }, + + onShow: function() { + this.callParent(arguments); + this.center(); + }, + + updateText: function(text) { + this.msg.setValue(text); + }, + + + setIcon : function(icon, width, height) { + var me = this, + iconCmp = me.iconComponent, + cls = me.messageIconCls; + + if (cls) { + iconCmp.removeCls(cls); + } + + if (icon) { + iconCmp.show(); + iconCmp.setSize(width || me.iconWidth, height || me.iconHeight); + iconCmp.addCls(Ext.baseCSSPrefix + 'dlg-icon'); + iconCmp.addCls(me.messageIconCls = icon); + } else { + iconCmp.removeCls(Ext.baseCSSPrefix + 'dlg-icon'); + iconCmp.hide(); + } + return me; + }, + + + updateProgress : function(value, progressText, msg){ + this.progressBar.updateProgress(value, progressText); + if (msg){ + this.updateText(msg); + } + return this; + }, + + onEsc: function() { + if (this.closable !== false) { + this.callParent(arguments); + } + }, + + + confirm: function(cfg, msg, fn, scope) { + if (Ext.isString(cfg)) { + cfg = { + title: cfg, + icon: this.QUESTION, + msg: msg, + buttons: this.YESNO, + callback: fn, + scope: scope + }; + } + return this.show(cfg); + }, + + + prompt : function(cfg, msg, fn, scope, multiline, value){ + if (Ext.isString(cfg)) { + cfg = { + prompt: true, + title: cfg, + minWidth: this.minPromptWidth, + msg: msg, + buttons: this.OKCANCEL, + callback: fn, + scope: scope, + multiline: multiline, + value: value + }; + } + return this.show(cfg); + }, + + + wait : function(cfg, title, config){ + if (Ext.isString(cfg)) { + cfg = { + title : title, + msg : cfg, + closable: false, + wait: true, + modal: true, + minWidth: this.minProgressWidth, + waitConfig: config + }; + } + return this.show(cfg); + }, + + + alert: function(cfg, msg, fn, scope) { + if (Ext.isString(cfg)) { + cfg = { + title : cfg, + msg : msg, + buttons: this.OK, + fn: fn, + scope : scope, + minWidth: this.minWidth + }; + } + return this.show(cfg); + }, + + + progress : function(cfg, msg, progressText){ + if (Ext.isString(cfg)) { + cfg = { + title: cfg, + msg: msg, + progress: true, + progressText: progressText + }; + } + return this.show(cfg); + } +}, function() { + + Ext.MessageBox = Ext.Msg = new this(); +}); + + + +Ext.define('Ext.layout.container.Fit', { + + + extend: Ext.layout.container.Container , + alternateClassName: 'Ext.layout.FitLayout', + + alias: 'layout.fit', + + + + itemCls: Ext.baseCSSPrefix + 'fit-item', + targetCls: Ext.baseCSSPrefix + 'layout-fit', + type: 'fit', + + + defaultMargins: { + top: 0, + right: 0, + bottom: 0, + left: 0 + }, + + 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) { + + var sizeModel = ownerSizeModel || this.owner.getSizeModel(), + mode = (sizeModel.width.shrinkWrap ? 0 : 1) | + (sizeModel.height.shrinkWrap ? 0 : 2); + + return this.sizePolicies[mode]; + }, + + beginLayoutCycle: function (ownerContext, firstCycle) { + var me = this, + + 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); + + + + + + 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]; + + + + + if (firstCycle) { + c = item.target; + minHeight = c.minHeight; + minWidth = c.minWidth; + + if (minWidth || minHeight) { + margins = item.marginInfo || item.getMarginInfo(); + + + minHeight += margins.height; + minWidth += margins.height; + + + + 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; + } + + + + + 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; + + if (overflowX || overflowY) { + + + + scrollbars = me.getScrollbarsNeeded( + overflowX && containerSize.width, overflowY && containerSize.height, + ownerContext.maxChildMinWidth, ownerContext.maxChildMinHeight); + + if (scrollbars) { + scrollbarSize = Ext.getScrollbarSize(); + if (scrollbars & 1) { + containerSize.height -= scrollbarSize.height; + } + if (scrollbars & 2) { + containerSize.width -= scrollbarSize.width; + } + } + } + + + if (length > 0) { + + 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) { + + + + me.done = false; + } else { + contentWidth = info.contentWidth + padding.width; + + + + if (scrollbars & 2) { + contentWidth += scrollbarSize.width; + } + if (!ownerContext.setContentWidth(contentWidth)) { + me.done = false; + } + } + } + + if (shrinkWrapHeight) { + if (overflowX && !containerSize.gotWidth) { + + + + me.done = false; + } else { + contentHeight = info.contentHeight + padding.height; + + + + if (scrollbars & 1) { + 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 (info.got != info.needed) { + me.done = false; + } + }, + + fitItemWidth: function (itemContext, info) { + var contentWidth, width; + + if (info.ownerContext.widthModel.shrinkWrap) { + + width = itemContext.getProp('width') + info.margins.width; + + + 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); + } + } + + this.positionItemX(itemContext, info); + }, + + fitItemHeight: function (itemContext, info) { + var contentHeight, height; + if (info.ownerContext.heightModel.shrinkWrap) { + + height = itemContext.getProp('height') + info.margins.height; + + + 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); + } + } + + this.positionItemY(itemContext, info); + }, + + positionItemX: function (itemContext, info) { + var margins = info.margins; + + + + if (info.index || margins.left) { + itemContext.setProp('x', margins.left); + } + + if (margins.width) { + + 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) { + + 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); + } +}); + + + +Ext.define('Ext.panel.Table', { + extend: Ext.panel.Panel , + + alias: 'widget.tablepanel', + + + + + + + + + + + + + + + extraBaseCls: Ext.baseCSSPrefix + 'grid', + extraBodyCls: Ext.baseCSSPrefix + 'grid-body', + + layout: 'fit', + + hasView: false, + + + + viewType: null, + + + + + + + selType: 'rowmodel', + + + + + + + + + + + scroll: true, + + + + + + + + + + + + + + deferRowRender: true, + + + sortableColumns: true, + + + multiColumnSort: false, + + + enableLocking: false, + + + + scrollerOwner: true, + + + enableColumnMove: true, + + + sealedColumns: false, + + + enableColumnResize: true, + + + + + + + rowLines: true, + + + + + + + + + + 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', + + initComponent: function() { + if (!this.viewType) { + Ext.Error.raise("You must specify a viewType config."); + } + if (this.headers) { + Ext.Error.raise("The headers config is not supported. Please specify columns instead."); + } + + var me = this, + headerCtCfg = me.columns || me.colModel, + view, + i, len, + + store = me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store'), + columns; + + + + + + + + + + if (me.plugins) { + me.plugins = me.constructPlugins(); + } + + + if (me.columnLines) { + me.addBodyCls(me.colLinesCls); + } + + me.addBodyCls(me.rowLines ? me.rowLinesCls : me.noRowLinesCls); + me.addBodyCls(me.extraBodyCls); + + if (!headerCtCfg) { + Ext.Error.raise("A column configuration must be specified"); + } + + + if (headerCtCfg.isRootHeader) { + me.headerCt = headerCtCfg; + + me.headerCt.forceFit = !!me.forceFit; + + + me.columnManager = headerCtCfg.columnManager; + me.visibleColumnManager = headerCtCfg.visibleColumnManager; + + + + if (me.columnLines) { + me.headerCt.cascade(function(h) { + if (!h.isColumn) { + h.columnLines = true; + } + }); + } + } else { + + + + if (me.enableLocking || me.hasLockedColumns(headerCtCfg)) { + me.self.mixin('lockable', Ext.grid.locking.Lockable); + me.injectLockable(); + } + + else { + if (Ext.isArray(headerCtCfg)) { + headerCtCfg = { + items: headerCtCfg + }; + } + Ext.apply(headerCtCfg, { + grid: me, + forceFit: me.forceFit, + sortable: me.sortableColumns, + enableColumnMove: me.enableColumnMove, + enableColumnResize: me.enableColumnResize, + columnLines: me.columnLines, + sealed: me.sealedColumns + }); + + if (Ext.isDefined(me.enableColumnHide)) { + headerCtCfg.enableColumnHide = me.enableColumnHide; + } + + + if (!me.headerCt) { + me.headerCt = new Ext.grid.header.Container(headerCtCfg); + } + } + } + + + me.columns = columns = me.headerCt.getGridColumns(); + + me.scrollTask = new Ext.util.DelayedTask(me.syncHorizontalScroll, me); + + me.addEvents( + + 'reconfigure', + + 'viewready' + ); + + me.cls = (me.cls || '') + (' ' + me.extraBaseCls); + + + delete me.autoScroll; + + + + if (!me.hasView) { + + + if (store.buffered && !store.remoteSort) { + for (i = 0, len = columns.length; i < len; i++) { + columns[i].sortable = false; + } + } + + if (me.hideHeaders) { + me.headerCt.height = 0; + + me.headerCt.hiddenHeaders = true; + me.headerCt.addCls(me.hiddenHeaderCtCls); + me.addCls(me.hiddenHeaderCls); + + + if (Ext.isIEQuirks) { + me.headerCt.style = { + display: 'none' + }; + } + } + + me.relayHeaderCtEvents(me.headerCt); + me.features = me.features || []; + if (!Ext.isArray(me.features)) { + me.features = [me.features]; + } + me.dockedItems = [].concat(me.dockedItems || []); + me.dockedItems.unshift(me.headerCt); + me.viewConfig = me.viewConfig || {}; + + + + view = me.getView(); + + me.items = [view]; + me.hasView = true; + + + + if (!me.hideHeaders) { + view.on({ + scroll: { + fn: me.onHorizontalScroll, + element: 'el', + scope: me + } + }); + } + + + me.bindStore(store, true); + + me.mon(view, { + viewready: me.onViewReady, + refresh: me.onRestoreHorzScroll, + scope: me + }); + } + + + me.relayEvents(me.view, [ + + 'beforeitemmousedown', + + 'beforeitemmouseup', + + 'beforeitemmouseenter', + + 'beforeitemmouseleave', + + 'beforeitemclick', + + 'beforeitemdblclick', + + 'beforeitemcontextmenu', + + 'itemmousedown', + + 'itemmouseup', + + 'itemmouseenter', + + 'itemmouseleave', + + 'itemclick', + + 'itemdblclick', + + 'itemcontextmenu', + + 'beforecellclick', + + 'cellclick', + + 'beforecelldblclick', + + 'celldblclick', + + 'beforecellcontextmenu', + + 'cellcontextmenu', + + 'beforecellmousedown', + + 'cellmousedown', + + 'beforecellmouseup', + + 'cellmouseup', + + 'beforecellkeydown', + + 'cellkeydown', + + 'beforeitemkeydown', + + 'itemkeydown', + + 'beforecontainermousedown', + + 'beforecontainermouseup', + + 'beforecontainermouseover', + + 'beforecontainermouseout', + + 'beforecontainerclick', + + 'beforecontainerdblclick', + + 'beforecontainercontextmenu', + + 'beforecontainerkeydown', + + 'containermouseup', + + 'containermousedown', + + 'containermouseover', + + 'containermouseout', + + 'containerclick', + + 'containerdblclick', + + 'containercontextmenu', + + 'containerkeydown', + + 'selectionchange', + + 'beforeselect', + + 'select', + + 'beforedeselect', + + 'deselect' + ]); + + me.callParent(arguments); + me.addStateEvents(['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange', 'filterchange', 'groupchange']); + }, + + + hasLockedColumns: function(columns) { + var i, + len, + column; + + + 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, [ + + 'columnresize', + + 'columnmove', + + 'columnhide', + + 'columnshow', + + 'columnschanged', + + 'sortchange', + + 'headerclick', + + 'headercontextmenu', + + '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; + + + + me.callParent(arguments); + + if (columns) { + me.headerCt.applyColumnsState(columns); + } + + + if (sorter) { + if (store.remoteSort) { + + store.sort({ + property: sorter.property, + direction: sorter.direction, + root: sorter.root + }, null, false); + } else { + store.sort(sorter.property, sorter.direction); + } + } + + else if (storeState) { + store.applyState(storeState); + } + }, + + + getStore: function(){ + return this.store; + }, + + + getView: function() { + var me = this, + sm; + + if (!me.view) { + sm = me.getSelectionModel(); + + + Ext.widget(Ext.apply({ + + + grid: me, + deferInitialRefresh: me.deferRowRender !== false, + trackOver: me.trackMouseOver !== false, + scroll: me.scroll, + xtype: me.viewType, + store: me.store, + headerCt: me.headerCt, + columnLines: me.columnLines, + rowLines: me.rowLines, + selModel: sm, + features: me.features, + panel: me, + emptyText: me.emptyText || '' + }, me.viewConfig)); + + + + + + if (me.view.emptyText) { + me.view.emptyText = '
' + me.view.emptyText + '
'; + } + + + me.view.getComponentLayout().headerCt = me.headerCt; + + me.mon(me.view, { + uievent: me.processEvent, + scope: me + }); + sm.view = me.view; + me.headerCt.view = me.view; + + + 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(){ + var ownerLock = this.ownerLockable; + return ownerLock ? ownerLock.getColumnManager() : this.getColumnManager(); + }, + + getTopLevelVisibleColumnManager: function(){ + var ownerLock = this.ownerLockable; + return ownerLock ? ownerLock.getVisibleColumnManager() : this.getVisibleColumnManager(); + }, + + + setAutoScroll: Ext.emptyFn, + + + processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) { + var me = this, + header; + + if (cellIndex !== -1) { + header = me.getColumnManager().getHeaderAtIndex(cellIndex); + return header.processEvent.apply(header, arguments); + } + }, + + + determineScrollbars: function () { + Ext.log.warn('Obsolete'); + }, + + + invalidateScroller: function () { + Ext.log.warn('Obsolete'); + }, + + 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: Ext.emptyFn, + + + onHeaderMove: function(headerCt, header, colsToMove, fromIdx, toIdx) { + var me = this; + + + + if (me.optimizedColumnMove === false) { + me.view.refreshView(); + } + + + else { + me.view.moveColumn(fromIdx, toIdx, colsToMove); + } + me.delayScroll(); + }, + + + onHeaderHide: function(headerCt, header) { + if (this.view.refreshCounter) { + this.view.refreshView(); + } + }, + + onHeaderShow: function(headerCt, header) { + if (this.view.refreshCounter) { + this.view.refreshView(); + } + }, + + + onHeadersChanged: function(headerCt, header) { + var me = this; + if (me.rendered && !me.reconfiguring) { + me.view.refreshView(); + me.delayScroll(); + } + }, + + delayScroll: function(){ + var target = this.getScrollTarget().el; + if (target) { + + + this.scrollTask.delay(10, null, null, [target.dom]); + } + }, + + + onViewReady: function() { + this.fireEvent('viewready', this); + }, + + + onRestoreHorzScroll: function() { + var left = this.scrollLeft; + if (left) { + + this.syncHorizontalScroll(this, true); + } + }, + + getScrollerOwner: function() { + var rootCmp = this; + if (!this.scrollerOwner) { + rootCmp = this.up('[scrollerOwner]'); + } + return rootCmp; + }, + + + getLhsMarker: function() { + var me = this; + return me.lhsMarker || (me.lhsMarker = Ext.DomHelper.append(me.el, { + role: 'presentation', + cls: me.resizeMarkerCls + }, true)); + }, + + + getRhsMarker: function() { + var me = this; + + return me.rhsMarker || (me.rhsMarker = Ext.DomHelper.append(me.el, { + role: 'presentation', + cls: me.resizeMarkerCls + }, true)); + }, + + + getSelectionModel: function(){ + var me = this, + selModel = me.selModel, + applyMode, mode, type; + + if (!selModel) { + selModel = {}; + + applyMode = true; + } + + if (!selModel.events) { + + type = selModel.selType || me.selType; + applyMode = !selModel.mode; + selModel = me.selModel = Ext.create('selection.' + type, selModel); + } + + if (me.simpleSelect) { + mode = 'SIMPLE'; + } else if (me.multiSelect) { + mode = 'MULTI'; + } + + Ext.applyIf(selModel, { + allowDeselect: me.allowDeselect + }); + + if (mode && applyMode) { + selModel.setSelectionMode(mode); + } + + if (!selModel.hasRelaySetup) { + me.relayEvents(selModel, [ + 'selectionchange', 'beforeselect', 'beforedeselect', 'select', 'deselect' + ]); + selModel.hasRelaySetup = true; + } + + + + if (me.disableSelection) { + selModel.locked = true; + } + return selModel; + }, + + getScrollTarget: function(){ + var owner = this.getScrollerOwner(), + items = owner.query('tableview'); + + return items[1] || items[0]; + }, + + onHorizontalScroll: function(event, target) { + this.syncHorizontalScroll(target); + }, + + syncHorizontalScroll: function(target, setBody) { + var me = this, + left = target.scrollLeft, + scrollTarget; + + setBody = setBody === true; + + + if (me.rendered && (setBody || left !== me.scrollLeft)) { + + + if (setBody) { + scrollTarget = me.getScrollTarget(); + + scrollTarget.el.dom.scrollLeft = left; + } + + me.headerCt.el.dom.scrollLeft = left; + me.scrollLeft = left; + } + }, + + + onStoreLoad: Ext.emptyFn, + + getEditorParent: function() { + return this.body; + }, + + bindStore: function(store, initial) { + var me = this, + view = me.getView(), + bufferedStore = store && store.buffered, + bufferedRenderer; + + + + + if (store) { + + me.store = store; + + + + + + bufferedRenderer = me.findPlugin('bufferedrenderer'); + if (bufferedRenderer) { + me.verticalScroller = bufferedRenderer; + + if (bufferedRenderer.store) { + bufferedRenderer.bindStore(store); + } + } else if (bufferedStore) { + me.verticalScroller = bufferedRenderer = me.addPlugin(Ext.apply({ + ptype: 'bufferedrenderer' + }, me.initialConfig.verticalScroller)); + } + + if (view.store !== store) { + + + + + + view.bindStore(store, false); + } + + me.mon(store, { + load: me.onStoreLoad, + scope: me + }); + me.storeRelayers = me.relayEvents(store, [ + + 'filterchange', + + 'groupchange' + ]); + + + if (bufferedRenderer) { + me.invalidateScrollerOnRefresh = false; + } + + if (me.invalidateScrollerOnRefresh !== undefined) { + view.preserveScrollOnRefresh = !me.invalidateScrollerOnRefresh; + } + } else { + me.unbindStore(); + } + }, + + unbindStore: function() { + var me = this, + store = me.store, + view; + + if (store) { + me.store = null; + me.mun(store, { + load: me.onStoreLoad, + scope: me + }); + Ext.destroy(me.storeRelayers); + + view = me.view; + if (view.store) { + view.bindStore(null); + } + } + }, + + + reconfigure: function(store, columns) { + var me = this, + view = me.getView(), + originalDeferinitialRefresh, + oldStore = me.store, + headerCt = me.headerCt, + oldColumns = headerCt ? headerCt.items.getRange() : me.columns; + + + if (arguments.length === 1 && Ext.isArray(store)) { + columns = store; + store = null; + } + + + if (columns) { + columns = Ext.Array.slice(columns); + } + + me.reconfiguring = true; + me.fireEvent('beforereconfigure', me, store, columns, oldStore, oldColumns); + if (me.lockable) { + me.reconfigureLockable(store, columns); + } else { + Ext.suspendLayouts(); + if (columns) { + + delete me.scrollLeft; + headerCt.removeAll(); + headerCt.add(columns); + } + + + if (store && (store = Ext.StoreManager.lookup(store)) !== oldStore) { + + if (me.store) { + me.unbindStore(); + } + + + originalDeferinitialRefresh = view.deferInitialRefresh; + view.deferInitialRefresh = false; + me.bindStore(store); + view.deferInitialRefresh = originalDeferinitialRefresh; + } else { + me.getView().refreshView(); + } + headerCt.setSortState(); + Ext.resumeLayouts(true); + } + me.fireEvent('reconfigure', me, store, columns, oldStore, oldColumns); + delete me.reconfiguring; + }, + + beforeDestroy: function(){ + var task = this.scrollTask; + if (task) { + task.cancel(); + this.scrollTask = null; + } + this.callParent(); + }, + + onDestroy: function(){ + var me = this; + if (me.lockable) { + me.destroyLockable(); + } + me.callParent(); + me.columns = me.storeRelayers = me.columnManager = me.visibleColumnManager = null; + }, + + destroy: function() { + + var me = this; + me.callParent(); + if (me.isDestroyed) { + me.view = me.selModel = me.headerCt = null; + } + } +}); + + + +Ext.define('Ext.util.Bindable', { + + + bindStore: function(store, initial, propertyName) { + + + + propertyName = propertyName || 'store'; + + var me = this, + oldStore = me[propertyName]; + + if (!initial && oldStore) { + + me.onUnbindStore(oldStore, initial, propertyName); + + if (store !== oldStore && oldStore.autoDestroy) { + oldStore.destroyStore(); + } else { + me.unbindStoreListeners(oldStore); + } + } + if (store) { + store = Ext.data.StoreManager.lookup(store); + me.bindStoreListeners(store); + me.onBindStore(store, initial, propertyName); + } + me[propertyName] = store || null; + return me; + }, + + + getStore: function(){ + return this.store; + }, + + + unbindStoreListeners: function(store) { + + var listeners = this.storeListeners; + if (listeners) { + store.un(listeners); + } + }, + + + bindStoreListeners: function(store) { + + var me = this, + listeners = Ext.apply({}, me.getStoreListeners(store)); + + if (!listeners.scope) { + listeners.scope = me; + } + me.storeListeners = listeners; + store.on(listeners); + }, + + + getStoreListeners: Ext.emptyFn, + + + onUnbindStore: Ext.emptyFn, + + + onBindStore: Ext.emptyFn +}); + + + +Ext.define('Ext.LoadMask', { + + extend: Ext.Component , + + alias: 'widget.loadmask', + + + + mixins: { + bindable: Ext.util.Bindable + }, + + + + + + + isLoadMask: true, + + + + + + + + msg: 'Loading...', + + + + msgCls: Ext.baseCSSPrefix + 'mask-loading', + + + maskCls: Ext.baseCSSPrefix + 'mask', + + cls: Ext.baseCSSPrefix + 'mask-msg', + + useMsg: true, + + + useTargetEl: false, + + ariaRole: 'presentation', + + childEls: [ + 'msgEl', + 'msgTextEl' + ], + + renderTpl: [ + '
{ariaAttr}', + ' class="{[values.$comp.msgCls]} ', + Ext.baseCSSPrefix, 'mask-msg-inner {childElCls}">', + '
{msg}
', + '
' + ], + + + constructor : function(config) { + var me = this, + comp; + + if (arguments.length === 2) { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn('Ext.LoadMask: LoadMask now uses a standard 1 arg constructor: use the target config'); + } + comp = me.target = config; + config = arguments[1]; + } else { + comp = config.target; + } + + + me.callParent([config]); + + + if (comp.isComponent) { + me.ownerCt = comp; + me.hidden = true; + + + + + me.renderTo = me.getMaskTarget(); + me.external = me.renderTo === Ext.getBody(); + me.bindComponent(comp); + } + + else { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn('Ext.LoadMask: LoadMask for elements has been deprecated, use Ext.dom.Element.mask & Ext.dom.Element.unmask'); + } + comp = Ext.get(comp); + me.isElement = true; + me.renderTo = me.target; + } + me.render(me.renderTo); + if (me.store) { + me.bindStore(me.store, true); + } + }, + + + getRenderTree: function() { + return [ + { + cls: this.maskCls, + style: 'display:none' + }, + this.callParent() + ]; + }, + + onRender: function() { + this.callParent(arguments); + this.maskEl = this.el.prev(); + }, + + initRenderData: function() { + var result = this.callParent(arguments); + result.msg = this.msg || ''; + return result; + }, + + bindComponent: function(comp) { + var me = this, + listeners = { + scope: this, + resize: me.sizeMask + }; + + if (me.external) { + listeners.added = me.onComponentAdded; + listeners.removed = me.onComponentRemoved; + if (comp.floating) { + listeners.move = me.sizeMask; + me.activeOwner = comp; + } else if (comp.ownerCt) { + me.onComponentAdded(comp.ownerCt); + } + } + + me.mon(comp, listeners); + + + + if (me.external) { + me.mon(me.hierarchyEventSource, { + show: me.onContainerShow, + hide: me.onContainerHide, + expand: me.onContainerExpand, + collapse: me.onContainerCollapse, + scope: me + }); + } + }, + + onComponentAdded: function(owner) { + var me = this; + delete me.activeOwner; + me.floatParent = owner; + if (!owner.floating) { + owner = owner.up('[floating]'); + } + if (owner) { + me.activeOwner = owner; + me.mon(owner, 'move', me.sizeMask, me); + me.mon(owner, 'tofront', me.onOwnerToFront, me); + } else { + me.preventBringToFront = true; + } + owner = me.floatParent.ownerCt; + if (me.rendered && me.isVisible() && owner) { + me.floatOwner = owner; + me.mon(owner, 'afterlayout', me.sizeMask, me, {single: true}); + } + }, + + onComponentRemoved: function(owner) { + var me = this, + activeOwner = me.activeOwner, + floatOwner = me.floatOwner; + + if (activeOwner) { + me.mun(activeOwner, 'move', me.sizeMask, me); + me.mun(activeOwner, 'tofront', me.onOwnerToFront, me); + } + if (floatOwner) { + me.mun(floatOwner, 'afterlayout', me.sizeMask, me); + } + delete me.activeOwner; + delete me.floatOwner; + }, + + afterRender: function() { + this.callParent(arguments); + + + + this.el.$cache.skipGarbageCollection = true; + this.maskEl.$cache.skipGarbageCollection = true; + }, + + onOwnerToFront: function(owner, zIndex) { + this.maskEl.setStyle('zIndex', zIndex + 1); + this.el.setStyle('zIndex', zIndex + 2); + }, + + + + onContainerShow: function(container) { + this.onComponentShow(); + }, + + + + onContainerHide: function(container) { + this.onComponentHide(); + }, + + + + onContainerExpand: function(container) { + this.onComponentShow(); + }, + + + + onContainerCollapse: function(container) { + this.onComponentHide(); + }, + + onComponentHide: function() { + var me = this; + + if (me.rendered && me.isVisible()) { + me.hide(); + me.showNext = true; + } + }, + + onComponentShow: function() { + if (this.showNext) { + this.show(); + } + delete this.showNext; + }, + + + sizeMask: function() { + var me = this, + target = me.target, + boxTarget = me.external ? me.getOwner().el : me.getMaskTarget(); + + if (me.rendered && me.isVisible()) { + + + + if (me.external) { + if (!me.isElement && target.floating) { + me.onOwnerToFront(target, target.el.getZIndex()); + } + me.maskEl.setSize(boxTarget.getSize()).alignTo(boxTarget, 'tl-tl'); + } + + me.el.center(me.maskEl); + } + }, + + + bindStore : function(store, initial) { + var me = this; + me.mixins.bindable.bindStore.apply(me, arguments); + store = me.store; + if (store && store.isLoading()) { + me.onBeforeLoad(); + } + }, + + getStoreListeners: function(store) { + var load = this.onLoad, + beforeLoad = this.onBeforeLoad, + result = { + + cachemiss: beforeLoad, + + + cachefilled: load + }; + + + if (!store.proxy.isSynchronous) { + result.beforeLoad = beforeLoad; + result.load = load; + } + return result; + }, + + onDisable : function() { + this.callParent(arguments); + if (this.loading) { + this.onLoad(); + } + }, + + getOwner: function() { + return this.ownerCt || this.floatParent; + }, + + getMaskTarget: function() { + var owner = this.getOwner(); + if (this.isElement) { + return this.target; + } + return this.useTargetEl ? owner.getTargetEl() : (owner.getMaskTarget() || Ext.getBody()); + }, + + + onBeforeLoad : function() { + var me = this, + owner = me.getOwner(), + origin; + + if (!me.disabled) { + me.loading = true; + + + if (owner.componentLayoutCounter) { + me.maybeShow(); + } else { + + origin = owner.afterComponentLayout; + owner.afterComponentLayout = function() { + owner.afterComponentLayout = origin; + origin.apply(owner, arguments); + me.maybeShow(); + }; + } + } + }, + + maybeShow: function() { + var me = this, + owner = me.getOwner(); + + if (!owner.isVisible(true)) { + me.showNext = true; + } + else if (me.loading && owner.rendered) { + me.show(); + } + }, + + hide: function() { + + if (this.isElement) { + this.ownerCt.unmask(); + this.fireEvent('hide', this); + return; + } + delete this.showNext; + this.maskEl.setDisplayed(false); + return this.callParent(arguments); + }, + + show: function() { + + if (this.isElement) { + this.ownerCt.mask(this.useMsg ? this.msg : '', this.msgCls); + this.fireEvent('show', this); + return; + } + this.maskEl.setDisplayed(true); + return this.callParent(arguments); + }, + + afterShow: function() { + var me = this; + + me.loading = true; + me.callParent(arguments); + + + if (me.hasOwnProperty('maskCls')) { + me.maskEl.dom.className = me.maskCls; + } + + if (me.useMsg) { + me.msgTextEl.update(me.msg); + } else { + + me.el.hide(); + } + me.sizeMask(); + }, + + + onLoad : function() { + this.loading = false; + this.hide(); + }, + + beforeDestroy: function() { + + + this.ownerCt = null; + this.callParent(); + }, + + onDestroy: function() { + var me = this; + + if (me.isElement) { + me.ownerCt.unmask(); + } + + Ext.destroy(me.maskEl); + me.callParent(); + } +}); + + + +Ext.define('Ext.data.ResultSet', { + + loaded: true, + + + count: 0, + + + total: 0, + + + success: false, + + + + + constructor: function(config) { + Ext.apply(this, config); + + + this.totalRecords = this.total; + + if (config.count === undefined) { + this.count = this.records.length; + } + } +}); + + + +Ext.define('Ext.data.reader.Reader', { + + alternateClassName: ['Ext.data.Reader', 'Ext.data.DataReader'], + + mixins: { + observable: Ext.util.Observable + }, + + + + + totalProperty: 'total', + + + successProperty: 'success', + + + root: '', + + + + + implicitIncludes: true, + + + readRecordsOnFailure: true, + + + + + isReader: true, + + + + + applyDefaults: true, + + lastFieldGeneration: null, + + + constructor: function(config) { + var me = this; + + me.mixins.observable.constructor.call(me, config); + me.model = Ext.ModelManager.getModel(me.model); + + + + + + + if (me.model && me.model.prototype.fields) { + me.buildExtractors(); + } + + this.addEvents( + + 'exception' + ); + }, + + + setModel: function(model, setOnProxy) { + var me = this, + oldModel = me.model, + force = true; + + model = me.model = Ext.ModelManager.getModel(model); + if (model && oldModel === model) { + + + + force = me.lastFieldGeneration !== model.prototype.fields.generation; + } + + if (model) { + me.buildExtractors(force); + } + + if (setOnProxy && me.proxy) { + me.proxy.setModel(model, true); + } + }, + + + read: function(response) { + var data; + + if (response) { + data = response.responseText ? this.getResponseData(response) : this.readRecords(response); + } + + return data || this.nullResultSet; + }, + + + readRecords: function(data) { + var me = this, + success, + recordCount, + records, + root, + total, + value, + message; + + + if (me.lastFieldGeneration !== me.model.prototype.fields.generation) { + me.buildExtractors(true); + } + + + me.rawData = data; + + data = me.getData(data); + + success = true; + recordCount = 0; + records = []; + + if (me.successProperty) { + value = me.getSuccess(data); + if (value === false || value === 'false') { + success = false; + } + } + + if (me.messageProperty) { + message = me.getMessage(data); + } + + + + if (me.readRecordsOnFailure || success) { + + + root = Ext.isArray(data) ? data : me.getRoot(data); + + if (root) { + total = root.length; + } + + if (me.totalProperty) { + value = parseInt(me.getTotal(data), 10); + if (!isNaN(value)) { + total = value; + } + } + + if (root) { + records = me.extractData(root); + recordCount = records.length; + } + } + + return new Ext.data.ResultSet({ + total : total || recordCount, + count : recordCount, + records: records, + success: success, + message: message + }); + }, + + + extractData : function(root) { + var me = this, + ModelClass = me.model, + length = root.length, + records = new Array(length), + dataConverter, + convertedValues, node, record, i; + + if (!root.length && Ext.isObject(root)) { + root = [root]; + length = 1; + } + + for (i = 0; i < length; i++) { + node = root[i]; + if (node.isModel) { + + + records[i] = node; + } else { + + + + + records[i] = record = new ModelClass(undefined, me.getId(node), node, convertedValues = {}); + + + + record.phantom = false; + + + me.convertRecordData(convertedValues, node, record); + + if (me.implicitIncludes && record.associations.length) { + me.readAssociated(record, node); + } + } + } + + return records; + }, + + + readAssociated: function(record, data) { + var associations = record.associations.items, + i = 0, + length = associations.length, + association, associationData, proxy, reader; + + for (; i < length; i++) { + association = associations[i]; + associationData = this.getAssociatedDataRoot(data, association.associationKeyFunction || association.associationKey || association.name); + + if (associationData) { + reader = association.getReader(); + if (!reader) { + proxy = association.associatedModel.getProxy(); + + if (proxy) { + reader = proxy.getReader(); + } else { + reader = new this.constructor({ + model: association.associatedName + }); + } + } + association.read(record, reader, associationData); + } + } + }, + + + getAssociatedDataRoot: function(data, associationName) { + if (Ext.isFunction(associationName)) { + return associationName(data); + } + + return data[associationName]; + }, + + getFields: function() { + return this.model.prototype.fields.items; + }, + + + getData: Ext.identityFn, + + + getRoot: Ext.identityFn, + + + getResponseData: function(response) { + Ext.Error.raise("getResponseData must be implemented in the Ext.data.reader.Reader subclass"); + }, + + + onMetaChange : function(meta) { + var me = this, + fields = meta.fields || me.getFields(), + newModel, + clientIdProperty; + + + me.metaData = meta; + + + me.root = meta.root || me.root; + me.idProperty = meta.idProperty || me.idProperty; + me.totalProperty = meta.totalProperty || me.totalProperty; + me.successProperty = meta.successProperty || me.successProperty; + me.messageProperty = meta.messageProperty || me.messageProperty; + clientIdProperty = meta.clientIdProperty; + + if (me.model) { + me.model.setFields(fields, me.idProperty, clientIdProperty); + me.setModel(me.model, true); + } + else { + newModel = Ext.define("Ext.data.reader.Json-Model" + Ext.id(), { + extend: 'Ext.data.Model', + fields: fields, + clientIdProperty: clientIdProperty + }); + if (me.idProperty) { + + + + + newModel.idProperty = me.idProperty; + } + me.setModel(newModel, true); + } + }, + + + getIdProperty: function() { + var idField = this.model.prototype.idField, + idProperty = this.idProperty; + + if (!idProperty && idField && (idProperty = idField.mapping) == null) { + idProperty = idField.name; + } + return idProperty; + }, + + + buildExtractors: function(force) { + var me = this, + idProp = me.getIdProperty(), + totalProp = me.totalProperty, + successProp = me.successProperty, + messageProp = me.messageProperty, + accessor; + + if (force === true) { + delete me.convertRecordData; + } + + if (me.convertRecordData) { + return; + } + + + if (totalProp) { + me.getTotal = me.createAccessor(totalProp); + } + + if (successProp) { + me.getSuccess = me.createAccessor(successProp); + } + + if (messageProp) { + me.getMessage = me.createAccessor(messageProp); + } + + + if (idProp) { + accessor = me.createAccessor(idProp); + me.getId = function(record) { + var id = accessor.call(me, record); + return (id === undefined || id === '') ? null : id; + }; + } else { + me.getId = function() { + return null; + }; + } + me.convertRecordData = me.buildRecordDataExtractor(); + me.lastFieldGeneration = me.model.prototype.fields.generation; + }, + + + + recordDataExtractorTemplate : [ + 'var me = this\n', + ' ,value\n', + ' ,internalId\n', + '', + ' ,__field{#} = fields.map["{name}"]\n', + '', ';\n', + + 'return function(dest, source, record) {\n', + '', + '{% var fieldAccessExpression = this.createFieldAccessExpression(values, "__field" + xindex, "source");', + ' if (fieldAccessExpression) { %}', + + ' value = {[ this.createFieldAccessExpression(values, "__field" + xindex, "source") ]};\n', + + + '', + ' dest["{name}"] = value === undefined ? __field{#}.convert(__field{#}.defaultValue, record) : __field{#}.convert(value, record);\n', + + + '', + ' if (value === undefined) {\n', + ' if (me.applyDefaults) {\n', + '', + ' dest["{name}"] = __field{#}.convert(__field{#}.defaultValue, record);\n', + '', + ' dest["{name}"] = __field{#}.defaultValue\n', + '', + ' };\n', + ' } else {\n', + '', + ' dest["{name}"] = __field{#}.convert(value, record);\n', + '', + ' dest["{name}"] = value;\n', + '', + ' };\n', + + + '', + ' if (value !== undefined) {\n', + '', + ' dest["{name}"] = __field{#}.convert(value, record);\n', + '', + ' dest["{name}"] = value;\n', + '', + ' }\n', + '', + + + + + '{% } else { %}', + '', + '', + ' dest["{name}"] = __field{#}.convert(__field{#}.defaultValue, record);\n', + '', + ' dest["{name}"] = __field{#}.defaultValue\n', + '', + '', + '{% } %}', + '', + + + + + '', + ' if (record && (internalId = {[ this.createFieldAccessExpression(\{mapping: values.clientIdProp\}, null, "source") ]})) {\n', + ' record.{["internalId"]} = internalId;\n', + ' }\n', + '', + + '};' + ], + + + buildRecordDataExtractor: function() { + var me = this, + modelProto = me.model.prototype, + templateData = { + clientIdProp: modelProto.clientIdProperty, + fields: modelProto.fields.items + }; + + me.recordDataExtractorTemplate.createFieldAccessExpression = function() { + return me.createFieldAccessExpression.apply(me,arguments); + }; + + + + + + return Ext.functionFactory('fields', me.recordDataExtractorTemplate.apply(templateData)).call(me, me.model.prototype.fields); + }, + + destroyReader: function() { + var me = this; + delete me.proxy; + delete me.model; + delete me.convertRecordData; + delete me.getId; + delete me.getTotal; + delete me.getSuccess; + delete me.getMessage; + } +}, function() { + var proto = this.prototype; + Ext.apply(proto, { + + nullResultSet: new Ext.data.ResultSet({ + total : 0, + count : 0, + records: [], + success: true, + message: '' + }), + recordDataExtractorTemplate: new Ext.XTemplate(proto.recordDataExtractorTemplate) + }); +}); + + + +Ext.define('Ext.data.reader.Json', { + extend: Ext.data.reader.Reader , + alternateClassName: 'Ext.data.JsonReader', + alias : 'reader.json', + + root: '', + + + + + metaProperty: 'metaData', + + + useSimpleAccessors: false, + + + readRecords: function(data) { + var me = this, + meta; + + + if (me.getMeta) { + meta = me.getMeta(data); + if (meta) { + me.onMetaChange(meta); + } + } else if (data.metaData) { + me.onMetaChange(data.metaData); + } + + + me.jsonData = data; + return me.callParent([data]); + }, + + + getResponseData: function(response) { + var data, error; + + try { + data = Ext.decode(response.responseText); + return this.readRecords(data); + } catch (ex) { + error = new Ext.data.ResultSet({ + total : 0, + count : 0, + records: [], + success: false, + message: ex.message + }); + + this.fireEvent('exception', this, response, error); + + Ext.Logger.warn('Unable to parse the JSON returned by the server'); + + return error; + } + }, + + + buildExtractors : function() { + var me = this, + metaProp = me.metaProperty; + + me.callParent(arguments); + + if (me.root) { + me.getRoot = me.createAccessor(me.root); + } else { + me.getRoot = Ext.identityFn; + } + + if (metaProp) { + me.getMeta = me.createAccessor(metaProp); + } + }, + + + extractData: function(root) { + var recordName = this.record, + data = [], + length, i; + + if (recordName) { + length = root.length; + + if (!length && Ext.isObject(root)) { + length = 1; + root = [root]; + } + + for (i = 0; i < length; i++) { + data[i] = root[i][recordName]; + } + } else { + data = root; + } + return this.callParent([data]); + }, + + + createAccessor: (function() { + var re = /[\[\.]/; + + return function(expr) { + if (Ext.isEmpty(expr)) { + return Ext.emptyFn; + } + if (Ext.isFunction(expr)) { + return expr; + } + if (this.useSimpleAccessors !== true) { + var i = String(expr).search(re); + if (i >= 0) { + return Ext.functionFactory('obj', 'return obj' + (i > 0 ? '.' : '') + expr); + } + } + return function(obj) { + return obj[expr]; + }; + }; + }()), + + + createFieldAccessExpression: (function() { + var re = /[\[\.]/; + + return function(field, fieldVarName, dataName) { + var mapping = field.mapping, + hasMap = mapping || mapping === 0, + map = hasMap ? mapping : field.name, + result, + operatorIndex; + + + if (mapping === false) { + return; + } + + if (typeof map === 'function') { + result = fieldVarName + '.mapping(' + dataName + ', this)'; + } else if (this.useSimpleAccessors === true || ((operatorIndex = String(map).search(re)) < 0)) { + if (!hasMap || isNaN(map)) { + + map = '"' + map + '"'; + } + result = dataName + "[" + map + "]"; + } else if (operatorIndex === 0) { + + + result = dataName + map; + } else { + + + + + + + var parts = map.split('.'), + len = parts.length, + i = 1, + tempResult = dataName + '.' + parts[0], + buffer = [tempResult]; + + for (; i < len; i++) { + tempResult += '.' + parts[i]; + buffer.push(tempResult); + } + result = buffer.join(' && '); + } + return result; + }; + }()) +}); + + + +Ext.define('Ext.data.writer.Writer', { + alias: 'writer.base', + alternateClassName: ['Ext.data.DataWriter', 'Ext.data.Writer'], + + + writeAllFields: true, + + + + + nameProperty: 'name', + + + writeRecordId: true, + + + isWriter: true, + + + constructor: function(config) { + Ext.apply(this, config); + }, + + + write: function(request) { + var operation = request.operation, + records = operation.records || [], + len = records.length, + i = 0, + data = []; + + for (; i < len; i++) { + data.push(this.getRecordData(records[i], operation)); + } + return this.writeRecords(request, data); + }, + + + getRecordData: function(record, operation) { + var isPhantom = record.phantom === true, + writeAll = this.writeAllFields || isPhantom, + fields = record.fields, + fieldItems = fields.items, + data = {}, + clientIdProperty = record.clientIdProperty, + changes, + field, + key, + mappedIdProperty, + f, fLen; + + if (writeAll) { + fLen = fieldItems.length; + + for (f = 0; f < fLen; f++) { + field = fieldItems[f]; + if (field.persist) { + this.writeValue(data, field, record); + } + } + } else if (operation && operation.action === 'destroy') { + this.writeValue(data, record.idField, record); + } else { + + changes = record.getChanges(); + for (key in changes) { + if (changes.hasOwnProperty(key)) { + field = fields.get(key); + if (field.persist) { + this.writeValue(data, field, record); + } + } + } + } + if (isPhantom) { + if (clientIdProperty && operation && operation.records.length > 1) { + + + data[clientIdProperty] = record.internalId; + } + } else if (this.writeRecordId) { + + mappedIdProperty = fields.get(record.idProperty)[this.nameProperty] || record.idProperty; + data[mappedIdProperty] = record.getId(); + } + + return data; + }, + + writeValue: function(data, field, record){ + var name = field[this.nameProperty], + dateFormat = this.dateFormat || field.dateWriteFormat || field.dateFormat, + value = record.get(field.name); + + + + if (name == null) { + name = field.name; + } + + if (field.serialize) { + data[name] = field.serialize(value, record); + } else if (field.type === Ext.data.Types.DATE && dateFormat && Ext.isDate(value)) { + data[name] = Ext.Date.format(value, dateFormat); + } else { + data[name] = value; + } + } +}); + + + +Ext.define('Ext.data.writer.Json', { + extend: Ext.data.writer.Writer , + alternateClassName: 'Ext.data.JsonWriter', + alias: 'writer.json', + + + root: undefined, + + + encode: false, + + + allowSingle: true, + + + expandData: false, + + + getExpandedData: function(data) { + var dataLength = data.length, + i = 0, + item, + prop, + nameParts, + j, + tempObj, + + toObject = function(name, value) { + var o = {}; + o[name] = value; + return o; + }; + + for (; i < dataLength; i++) { + item = data[i]; + + for (prop in item) { + if (item.hasOwnProperty(prop)) { + + nameParts = prop.split('.'); + j = nameParts.length - 1; + + if (j > 0) { + + + tempObj = item[prop]; + + for (; j > 0; j--) { + + + + tempObj = toObject(nameParts[j], tempObj); + } + + + + + item[nameParts[0]] = item[nameParts[0]] || {}; + + + Ext.Object.merge(item[nameParts[0]], tempObj); + + delete item[prop]; + } + } + } + } + return data; + }, + + + writeRecords: function(request, data) { + var root = this.root; + + if (this.expandData) { + data = this.getExpandedData(data); + } + + if (this.allowSingle && data.length === 1) { + + data = data[0]; + } + + if (this.encode) { + if (root) { + + request.params[root] = Ext.encode(data); + } else { + Ext.Error.raise('Must specify a root when using encode'); + } + } else { + + request.jsonData = request.jsonData || {}; + if (root) { + request.jsonData[root] = data; + } else { + request.jsonData = data; + } + } + return request; + } +}); + + + +Ext.define('Ext.data.proxy.Proxy', { + alias: 'proxy.proxy', + alternateClassName: ['Ext.data.DataProxy', 'Ext.data.Proxy'], + + + + + + + + + + + + + mixins: { + observable: Ext.util.Observable + }, + + + batchOrder: 'create,update,destroy', + + + batchActions: true, + + + defaultReaderType: 'json', + + + defaultWriterType: 'json', + + + + + + + + + isProxy: true, + + + isSynchronous: false, + + + constructor: function(config) { + var me = this; + + config = config || {}; + me.proxyConfig = config; + + me.mixins.observable.constructor.call(me, config); + + if (me.model !== undefined && !(me.model instanceof Ext.data.Model)) { + me.setModel(me.model); + } else { + if (me.reader) { + me.setReader(me.reader); + } + + if (me.writer) { + me.setWriter(me.writer); + } + } + + + }, + + + setModel: function(model, setOnStore) { + var me = this; + + me.model = Ext.ModelManager.getModel(model); + + me.setReader(this.reader); + me.setWriter(this.writer); + + if (setOnStore && me.store) { + me.store.setModel(me.model); + } + }, + + + getModel: function() { + return this.model; + }, + + + setReader: function(reader) { + var me = this, + needsCopy = true, + current = me.reader; + + if (reader === undefined || typeof reader == 'string') { + reader = { + type: reader + }; + needsCopy = false; + } + + if (reader.isReader) { + reader.setModel(me.model); + } else { + if (needsCopy) { + reader = Ext.apply({}, reader); + } + Ext.applyIf(reader, { + proxy: me, + model: me.model, + type : me.defaultReaderType + }); + + reader = Ext.createByAlias('reader.' + reader.type, reader); + } + + if (reader !== current && reader.onMetaChange) { + reader.onMetaChange = Ext.Function.createSequence(reader.onMetaChange, this.onMetaChange, this); + } + + me.reader = reader; + return me.reader; + }, + + + getReader: function() { + return this.reader; + }, + + + onMetaChange: function(meta) { + this.fireEvent('metachange', this, meta); + }, + + + setWriter: function(writer) { + var me = this, + needsCopy = true; + + if (writer === undefined || typeof writer == 'string') { + writer = { + type: writer + }; + needsCopy = false; + } + + if (!writer.isWriter) { + if (needsCopy) { + writer = Ext.apply({}, writer); + } + Ext.applyIf(writer, { + model: me.model, + type : me.defaultWriterType + }); + + writer = Ext.createByAlias('writer.' + writer.type, writer); + } + + me.writer = writer; + + return me.writer; + }, + + + getWriter: function() { + return this.writer; + }, + + + create: Ext.emptyFn, + + + read: Ext.emptyFn, + + + update: Ext.emptyFn, + + + destroy: Ext.emptyFn, + + + batch: function(options, listeners) { + var me = this, + useBatch = me.batchActions, + batch, + records, + actions, aLen, action, a, r, rLen, record; + + if (options.operations === undefined) { + + + options = { + operations: options, + listeners: listeners + }; + } + + if (options.batch) { + if (Ext.isDefined(options.batch.runOperation)) { + batch = Ext.applyIf(options.batch, { + proxy: me, + listeners: {} + }); + } + } else { + options.batch = { + proxy: me, + listeners: options.listeners || {} + }; + } + + if (!batch) { + batch = new Ext.data.Batch(options.batch); + } + + batch.on('complete', Ext.bind(me.onBatchComplete, me, [options], 0)); + + actions = me.batchOrder.split(','); + aLen = actions.length; + + for (a = 0; a < aLen; a++) { + action = actions[a]; + records = options.operations[action]; + + if (records) { + if (useBatch) { + batch.add(new Ext.data.Operation({ + action : action, + records : records + })); + } else { + rLen = records.length; + + for (r = 0; r < rLen; r++) { + record = records[r]; + + batch.add(new Ext.data.Operation({ + action : action, + records : [record] + })); + } + } + } + } + + batch.start(); + return batch; + }, + + + onBatchComplete: function(batchOptions, batch) { + var scope = batchOptions.scope || this; + + if (batch.hasException) { + if (Ext.isFunction(batchOptions.failure)) { + Ext.callback(batchOptions.failure, scope, [batch, batchOptions]); + } + } else if (Ext.isFunction(batchOptions.success)) { + Ext.callback(batchOptions.success, scope, [batch, batchOptions]); + } + + if (Ext.isFunction(batchOptions.callback)) { + Ext.callback(batchOptions.callback, scope, [batch, batchOptions]); + } + }, + + clone: function() { + return new this.self(this.proxyConfig); + } +}); + + + +Ext.define('Ext.data.proxy.Client', { + extend: Ext.data.proxy.Proxy , + alternateClassName: 'Ext.data.ClientProxy', + + + isSynchronous: true, + + + clear: function() { + Ext.Error.raise("The Ext.data.proxy.Client subclass that you are using has not defined a 'clear' function. See src/data/ClientProxy.js for details."); + } +}); + + + +Ext.define('Ext.data.proxy.Memory', { + extend: Ext.data.proxy.Client , + alias: 'proxy.memory', + alternateClassName: 'Ext.data.MemoryProxy', + + + + + + constructor: function(config) { + this.callParent([config]); + + + this.setReader(this.reader); + }, + + + updateOperation: function(operation, callback, scope) { + var i = 0, + recs = operation.getRecords(), + len = recs.length; + + for (i; i < len; i++) { + recs[i].commit(); + } + operation.setCompleted(); + operation.setSuccessful(); + + Ext.callback(callback, scope || this, [operation]); + }, + + + create: function() { + this.updateOperation.apply(this, arguments); + }, + + + update: function() { + this.updateOperation.apply(this, arguments); + }, + + + destroy: function() { + this.updateOperation.apply(this, arguments); + }, + + + read: function(operation, callback, scope) { + var me = this, + resultSet = operation.resultSet = me.getReader().read(me.data), + records = resultSet.records, + sorters = operation.sorters, + groupers = operation.groupers, + filters = operation.filters; + + operation.setCompleted(); + + + if (resultSet.success) { + + + if (filters && filters.length) { + records = resultSet.records = Ext.Array.filter(records, Ext.util.Filter.createFilterFn(filters)); + resultSet.total = records.length; + } + + + if (groupers && groupers.length) { + + sorters = sorters ? sorters.concat(groupers) : sorters; + } + + + if (sorters && sorters.length) { + resultSet.records = Ext.Array.sort(records, Ext.util.Sortable.createComparator(sorters)); + } + + + + if (me.enablePaging && operation.start !== undefined && operation.limit !== undefined) { + + + if (operation.start >= resultSet.total) { + resultSet.success = false; + resultSet.count = 0; + resultSet.records = []; + } + + else { + resultSet.records = Ext.Array.slice(resultSet.records, operation.start, operation.start + operation.limit); + resultSet.count = resultSet.records.length; + } + } + } + + if (resultSet.success) { + operation.setSuccessful(); + } else { + me.fireEvent('exception', me, null, operation); + } + Ext.callback(callback, scope || me, [operation]); + }, + + clear: Ext.emptyFn +}); + + + +Ext.define('Ext.data.Operation', { + + synchronous: true, + + + action: undefined, + + + filters: undefined, + + + sorters: undefined, + + + groupers: undefined, + + + start: undefined, + + + limit: undefined, + + + batch: undefined, + + + + + callback: undefined, + + + scope: undefined, + + + started: false, + + + running: false, + + + complete: false, + + + success: undefined, + + + exception: false, + + + error: undefined, + + + actionCommitRecordsRe: /^(?:create|update)$/i, + + + actionSkipSyncRe: /^destroy$/i, + + + constructor: function(config) { + Ext.apply(this, config || {}); + }, + + + commitRecords: function(serverRecords) { + var me = this, + commitRecords = me.actionCommitRecordsRe.test(me.action), + mc, index, clientRecords, serverRec, clientRec, i, len, + modifiedFields, recordModifiedFields; + + if (!me.actionSkipSyncRe.test(me.action)) { + clientRecords = me.records; + + if (clientRecords && clientRecords.length) { + + + + + + if (commitRecords) { + recordModifiedFields = []; + } + if (clientRecords.length > 1) { + + + + + if (me.action == 'update' || clientRecords[0].clientIdProperty) { + mc = new Ext.util.MixedCollection(); + mc.addAll(serverRecords); + + for (index = clientRecords.length; index--; ) { + clientRec = clientRecords[index]; + serverRec = mc.findBy(me.matchClientRec, clientRec); + + + modifiedFields = clientRec.copyFrom(serverRec); + + + if (commitRecords) { + recordModifiedFields.push(modifiedFields); + } + } + } else { + for (i = 0, len = clientRecords.length; i < len; ++i) { + clientRec = clientRecords[i]; + serverRec = serverRecords[i]; + if (clientRec && serverRec) { + modifiedFields = me.updateRecord(clientRec, serverRec); + + + if (commitRecords) { + recordModifiedFields.push(modifiedFields); + } + } + } + } + } else { + + modifiedFields = me.updateRecord(clientRecords[0], serverRecords[0]); + + + if (commitRecords) { + recordModifiedFields[0] = modifiedFields; + } + } + + if (commitRecords) { + for (index = clientRecords.length; index--; ) { + + + + + clientRecords[index].commit(false, recordModifiedFields[index]); + } + } + } + } + }, + + updateRecord: function(clientRec, serverRec) { + + if (serverRec && (clientRec.phantom || clientRec.getId() === serverRec.getId())) { + return clientRec.copyFrom(serverRec); + } + + + return []; + }, + + + + + matchClientRec: function(record) { + var clientRec = this, + clientRecordId = clientRec.getId(); + + if(clientRecordId && record.getId() === clientRecordId) { + return true; + } + + + + return record.internalId === clientRec.internalId; + }, + + + setStarted: function() { + this.started = true; + this.running = true; + }, + + + setCompleted: function() { + this.complete = true; + this.running = false; + }, + + + setSuccessful: function() { + this.success = true; + }, + + + setException: function(error) { + this.exception = true; + this.success = false; + this.running = false; + this.error = error; + }, + + + hasException: function() { + return this.exception === true; + }, + + + getError: function() { + return this.error; + }, + + + getRecords: function() { + var resultSet = this.getResultSet(); + return this.records || (resultSet ? resultSet.records : null); + }, + + + getResultSet: function() { + return this.resultSet; + }, + + + isStarted: function() { + return this.started === true; + }, + + + isRunning: function() { + return this.running === true; + }, + + + isComplete: function() { + return this.complete === true; + }, + + + wasSuccessful: function() { + return this.isComplete() && this.success === true; + }, + + + setBatch: function(batch) { + this.batch = batch; + }, + + + allowWrite: function() { + return this.action != 'read'; + } +}); + + + +Ext.define('Ext.data.AbstractStore', { + + + + + + + + + mixins: { + observable: Ext.util.Observable , + sortable: Ext.util.Sortable + }, + + statics: { + + create: function(store) { + if (!store.isStore) { + if (!store.type) { + store.type = 'store'; + } + store = Ext.createByAlias('store.' + store.type, store); + } + return store; + } + }, + + onClassExtended: function(cls, data, hooks) { + var model = data.model, + onBeforeClassCreated; + + if (typeof model == 'string') { + onBeforeClassCreated = hooks.onBeforeCreated; + + hooks.onBeforeCreated = function() { + var me = this, + args = arguments; + + Ext.require(model, function() { + onBeforeClassCreated.apply(me, args); + }); + }; + } + }, + + + remoteSort : false, + + + remoteFilter: false, + + + + + autoLoad: undefined, + + + autoSync: false, + + + batchUpdateMode: 'operation', + + + filterOnLoad: true, + + + sortOnLoad: true, + + + implicitModel: false, + + + defaultProxyType: 'memory', + + + isDestroyed: false, + + + isStore: true, + + + + + + + + + + + + + + sortRoot: 'data', + + + constructor: function(config) { + var me = this, + filters; + + + + + + + + + + + + + + + + + + + + + + + + Ext.apply(me, config); + + + + me.removed = []; + + me.mixins.observable.constructor.apply(me, arguments); + + var configModel = me.model; + + me.model = Ext.ModelManager.getModel(me.model); + + + Ext.applyIf(me, { + modelDefaults: null + }); + + + if (!me.model && me.fields) { + me.model = Ext.define(null, { + extend: 'Ext.data.Model', + fields: me.fields, + proxy: me.proxy || me.defaultProxyType + }); + + delete me.fields; + + me.implicitModel = true; + } + + if (!me.model && me.useModelWarning !== false) { + + var logMsg = [ + Ext.getClassName(me) || 'Store', + ' created with no model.' + ]; + + if (typeof configModel === 'string') { + logMsg.push(" The name '", configModel, "'", ' does not correspond to a valid model.'); + } + + Ext.log.warn(logMsg.join('')); + } + + + me.setProxy(me.proxy || me.model.getProxy()); + + if (me.id && !me.storeId) { + me.storeId = me.id; + delete me.id; + } + + if (me.storeId) { + Ext.data.StoreManager.register(me); + } + + me.mixins.sortable.initSortable.call(me); + + + filters = me.decodeFilters(me.filters); + me.filters = new Ext.util.MixedCollection(); + me.filters.addAll(filters); + }, + + + setProxy: function(proxy) { + var me = this, + model = me.model; + + if (proxy instanceof Ext.data.proxy.Proxy) { + proxy.setModel(model); + } else { + if (Ext.isString(proxy)) { + proxy = { + type: proxy, + model: model + }; + } else if (!proxy.model) { + proxy = Ext.apply({ + model: model + }, proxy) + } + + proxy = Ext.createByAlias('proxy.' + proxy.type, proxy); + } + + if (!me.disableMetaChangeEvent) { + proxy.on('metachange', me.onMetaChange, me); + } + + me.proxy = proxy; + + return me.proxy; + }, + + + getProxy: function() { + return this.proxy; + }, + + + onMetaChange: function(proxy, meta) { + this.fireEvent('metachange', this, meta); + }, + + + create: function(data, options) { + var me = this, + instance = Ext.ModelManager.create(Ext.applyIf(data, me.modelDefaults), me.model.modelName), + operation; + + options = options || {}; + + Ext.applyIf(options, { + action : 'create', + records: [instance] + }); + + operation = new Ext.data.Operation(options); + + me.proxy.create(operation, me.onProxyWrite, me); + + return instance; + }, + + read: function() { + return this.load.apply(this, arguments); + }, + + update: function(options) { + var me = this, + operation; + options = options || {}; + + Ext.applyIf(options, { + action : 'update', + records: me.getUpdatedRecords() + }); + + operation = new Ext.data.Operation(options); + + return me.proxy.update(operation, me.onProxyWrite, me); + }, + + + onProxyWrite: function(operation) { + var me = this, + success = operation.wasSuccessful(), + records = operation.getRecords(); + + switch (operation.action) { + case 'create': + me.onCreateRecords(records, operation, success); + break; + case 'update': + me.onUpdateRecords(records, operation, success); + break; + case 'destroy': + me.onDestroyRecords(records, operation, success); + break; + } + + if (success) { + me.fireEvent('write', me, operation); + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + } + + Ext.callback(operation.callback, operation.scope || me, [records, operation, success]); + }, + + + onCreateRecords: Ext.emptyFn, + + + onUpdateRecords: Ext.emptyFn, + + + onDestroyRecords: function(records, operation, success) { + if (success) { + this.removed = []; + } + }, + + + + destroy: function(options) { + var me = this, + operation; + + options = options || {}; + + Ext.applyIf(options, { + action : 'destroy', + records: me.getRemovedRecords() + }); + + operation = new Ext.data.Operation(options); + + return me.proxy.destroy(operation, me.onProxyWrite, me); + }, + + + onBatchOperationComplete: function(batch, operation) { + return this.onProxyWrite(operation); + }, + + + onBatchComplete: function(batch, operation) { + var me = this, + operations = batch.operations, + length = operations.length, + i; + + me.suspendEvents(); + + for (i = 0; i < length; i++) { + me.onProxyWrite(operations[i]); + } + + me.resumeEvents(); + + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + }, + + + onBatchException: function(batch, operation) { + + + + + + }, + + + filterNew: function(item) { + + return item.phantom === true && item.isValid(); + }, + + + getNewRecords: function() { + return []; + }, + + + getUpdatedRecords: function() { + return []; + }, + + + getModifiedRecords : function(){ + return [].concat(this.getNewRecords(), this.getUpdatedRecords()); + }, + + + filterUpdated: function(item) { + + return item.dirty === true && item.phantom !== true && item.isValid(); + }, + + + getRemovedRecords: function() { + return this.removed; + }, + + filter: function(filters, value) { + + }, + + + decodeFilters: function(filters) { + if (!Ext.isArray(filters)) { + if (filters === undefined) { + filters = []; + } else { + filters = [filters]; + } + } + + var length = filters.length, + Filter = Ext.util.Filter, + config, i; + + for (i = 0; i < length; i++) { + config = filters[i]; + + if (!(config instanceof Filter)) { + Ext.apply(config, { + root: 'data' + }); + + + if (config.fn) { + config.filterFn = config.fn; + } + + + if (typeof config == 'function') { + config = { + filterFn: config + }; + } + + filters[i] = new Filter(config); + } + } + + return filters; + }, + + clearFilter: function(supressEvent) { + + }, + + isFiltered: function() { + + }, + + filterBy: function(fn, scope) { + + }, + + + sync: function(options) { + var me = this, + operations = {}, + toCreate = me.getNewRecords(), + toUpdate = me.getUpdatedRecords(), + toDestroy = me.getRemovedRecords(), + needsSync = false; + + if (toCreate.length > 0) { + operations.create = toCreate; + needsSync = true; + } + + if (toUpdate.length > 0) { + operations.update = toUpdate; + needsSync = true; + } + + if (toDestroy.length > 0) { + operations.destroy = toDestroy; + needsSync = true; + } + + if (needsSync && me.fireEvent('beforesync', operations) !== false) { + options = options || {}; + + me.proxy.batch(Ext.apply(options, { + operations: operations, + listeners: me.getBatchListeners() + })); + } + + return me; + }, + + + getBatchListeners: function() { + var me = this, + listeners = { + scope: me, + exception: me.onBatchException + }; + + if (me.batchUpdateMode == 'operation') { + listeners.operationcomplete = me.onBatchOperationComplete; + } else { + listeners.complete = me.onBatchComplete; + } + + return listeners; + }, + + + save: function() { + return this.sync.apply(this, arguments); + }, + + + load: function(options) { + var me = this, + operation = { + action: 'read' + }; + + + if (me.remoteFilter) { + operation.filters = me.filters.items; + } + if (me.remoteSort) { + operation.sorters = me.getSorters(); + } + Ext.apply(operation, options); + me.lastOptions = operation; + + operation = new Ext.data.Operation(operation); + + if (me.fireEvent('beforeload', me, operation) !== false) { + me.loading = true; + me.proxy.read(operation, me.onProxyLoad, me); + } + + return me; + }, + + + reload: function(options) { + var o = Ext.apply({}, options, this.lastOptions); + return this.load(o); + }, + + + afterEdit : function(record, modifiedFieldNames) { + var me = this, + i, shouldSync; + + if (me.autoSync && !me.autoSyncSuspended) { + for (i = modifiedFieldNames.length; i--;) { + + if (record.fields.get(modifiedFieldNames[i]).persist) { + shouldSync = true; + break; + } + } + if (shouldSync) { + me.sync(); + } + } + me.onUpdate(record, Ext.data.Model.EDIT, modifiedFieldNames); + me.fireEvent('update', me, record, Ext.data.Model.EDIT, modifiedFieldNames); + }, + + + afterReject : function(record) { + + + + + + this.onUpdate(record, Ext.data.Model.REJECT, null); + this.fireEvent('update', this, record, Ext.data.Model.REJECT, null); + }, + + + afterCommit : function(record, modifiedFieldNames) { + if (!modifiedFieldNames) { + modifiedFieldNames = null; + } + this.onUpdate(record, Ext.data.Model.COMMIT, modifiedFieldNames); + this.fireEvent('update', this, record, Ext.data.Model.COMMIT, modifiedFieldNames); + }, + + onUpdate: Ext.emptyFn, + + onIdChanged: function(model, oldId, newId, oldInternalId){ + this.fireEvent('idchanged', this, model, oldId, newId, oldInternalId); + }, + + + destroyStore: function() { + var me = this; + + if (!me.isDestroyed) { + me.clearListeners(); + if (me.storeId) { + Ext.data.StoreManager.unregister(me); + } + me.destroyClear(); + if (me.reader) { + me.reader.destroyReader(); + } + me.data = me.tree = me.sorters = me.filters = me.groupers = me.proxy = me.reader = me.writer = me.model = null; + me.isDestroyed = true; + } + }, + + destroyClear: function() { + this.clearData(); + }, + + + getState: function() { + var me = this, + hasState, + result, + hasGroupers = !!me.groupers, + groupers = [], + sorters = [], + filters = []; + + if (hasGroupers) { + me.groupers.each(function(g) { + groupers[groupers.length] = g.serialize(); + hasState = true; + }); + } + + if (me.sorters) { + + me.sorters.each(function(s) { + + if (hasGroupers && !me.groupers.contains(s)) { + sorters[sorters.length] = s.serialize(); + hasState = true; + } + }); + } + + + + if (me.filters && me.statefulFilters) { + me.filters.each(function(f) { + filters[filters.length] = f.serialize(); + hasState = true; + }); + } + + + if (hasState) { + result = {}; + if (groupers.length) { + result.groupers = groupers; + } + if (sorters.length) { + result.sorters = sorters; + } + if (filters.length) { + result.filters = filters; + } + return result; + } + }, + + + applyState: function(state) { + var me = this, + hasSorters = !!me.sorters, + hasGroupers = !!me.groupers, + hasFilters = !!me.filters, + locallySorted; + + if (hasGroupers && state.groupers) { + me.groupers.clear(); + me.groupers.addAll(me.decodeGroupers(state.groupers)); + } + + if (hasSorters && state.sorters) { + me.sorters.clear(); + me.sorters.addAll(me.decodeSorters(state.sorters)); + } + + if (hasFilters && state.filters) { + me.filters.clear(); + me.filters.addAll(me.decodeFilters(state.filters)); + } + + if (hasSorters && hasGroupers) { + + me.sorters.insert(0, me.groupers.getRange()); + } + + + if (me.autoLoad && (me.remoteSort || me.remoteGroup || me.remoteFilter)) { + if (me.autoLoadTask) { + me.autoLoadTask.cancel(); + delete me.autoLoadTask; + } + + if (me.autoLoad === true) { + me.reload(); + } else { + me.reload(me.autoLoad); + } + } + + + if (hasFilters && me.filters.length && !me.remoteFilter) { + me.filter(); + locallySorted = me.sortOnFilter; + } + + + if (hasSorters && me.sorters.length && !me.remoteSort && !locallySorted) { + me.sort(); + } + }, + + + doSort: function(sorterFn) { + var me = this; + if (me.remoteSort) { + + me.load(); + } else { + me.data.sortBy(sorterFn); + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + } + me.fireEvent('sort', me, me.sorters.getRange()); + }, + + + clearData: Ext.emptyFn, + + + getCount: Ext.emptyFn, + + + getById: Ext.emptyFn, + + + removeAll: Ext.emptyFn, + + + + + isLoading: function() { + return !!this.loading; + }, + + + suspendAutoSync: function() { + this.autoSyncSuspended = true; + }, + + + resumeAutoSync: function() { + this.autoSyncSuspended = false; + } + +}); + + + +Ext.define('Ext.data.StoreManager', { + extend: Ext.util.MixedCollection , + alternateClassName: ['Ext.StoreMgr', 'Ext.data.StoreMgr', 'Ext.StoreManager'], + singleton: true, + + + + + + + register : function() { + for (var i = 0, s; (s = arguments[i]); i++) { + this.add(s); + } + }, + + + unregister : function() { + for (var i = 0, s; (s = arguments[i]); i++) { + this.remove(this.lookup(s)); + } + }, + + + lookup : function(store) { + + if (Ext.isArray(store)) { + var fields = ['field1'], + expand = !Ext.isArray(store[0]), + data = store, + i, + len; + + if(expand){ + data = []; + for (i = 0, len = store.length; i < len; ++i) { + data.push([store[i]]); + } + } else { + for(i = 2, len = store[0].length; i <= len; ++i){ + fields.push('field' + i); + } + } + return new Ext.data.ArrayStore({ + data : data, + fields: fields, + autoDestroy: true, + autoCreated: true, + expanded: expand + }); + } + + if (Ext.isString(store)) { + + return this.get(store); + } else { + + return Ext.data.AbstractStore.create(store); + } + }, + + + getKey : function(o) { + return o.storeId; + } +}, function() { + + Ext.regStore = function(name, config) { + var store; + + if (Ext.isObject(name)) { + config = name; + } else { + config.storeId = name; + } + + if (config instanceof Ext.data.Store) { + store = config; + } else { + store = new Ext.data.Store(config); + } + + return Ext.data.StoreManager.register(store); + }; + + + Ext.getStore = function(name) { + return Ext.data.StoreManager.lookup(name); + }; +}); + + + +Ext.define('Ext.selection.Model', { + extend: Ext.util.Observable , + alternateClassName: 'Ext.AbstractSelectionModel', + + mixins: { + bindable: Ext.util.Bindable + }, + + + + + + allowDeselect: undefined, + + + toggleOnClick: true, + + + selected: null, + + + pruneRemoved: true, + + suspendChange: 0, + + constructor: function(cfg) { + var me = this; + + cfg = cfg || {}; + Ext.apply(me, cfg); + + me.addEvents( + + 'selectionchange', + + 'focuschange' + ); + + me.modes = { + SINGLE: true, + SIMPLE: true, + MULTI: true + }; + + + me.setSelectionMode(cfg.mode || me.mode); + + + me.selected = new Ext.util.MixedCollection(null, me.getSelectionId); + + me.callParent(arguments); + }, + + + bindStore: function(store, initial){ + var me = this; + me.mixins.bindable.bindStore.apply(me, arguments); + if(me.store && !initial) { + me.refresh(); + } + }, + + getStoreListeners: function() { + var me = this; + return { + add: me.onStoreAdd, + clear: me.onStoreClear, + bulkremove: me.onStoreRemove, + update: me.onStoreUpdate, + load: me.onStoreLoad, + idchanged: me.onModelIdChanged, + refresh: me.onStoreRefresh + }; + }, + + suspendChanges: function(){ + ++this.suspendChange; + }, + + resumeChanges: function(){ + if (this.suspendChange) { + --this.suspendChange; + } + }, + + + selectAll: function(suppressEvent) { + var me = this, + selections = me.store.getRange(), + i = 0, + len = selections.length, + start = me.getSelection().length; + + me.suspendChanges(); + for (; i < len; i++) { + me.doSelect(selections[i], true, suppressEvent); + } + me.resumeChanges(); + + if (!suppressEvent) { + me.maybeFireSelectionChange(me.getSelection().length !== start); + } + }, + + + deselectAll: function(suppressEvent) { + var me = this, + selections = me.getSelection(), + selIndexes = {}, + store = me.store, + start = selections.length, + i, l, rec; + + + + + + + + for (i = 0, l = selections.length; i < l; i++) { + rec = selections[i]; + + selIndexes[rec.internalId] = store.indexOf(rec); + } + + + + selections = Ext.Array.sort(selections, function(r1, r2){ + var idx1 = selIndexes[r1.internalId], + idx2 = selIndexes[r2.internalId]; + + + return idx1 < idx2 ? -1 : 1; + }); + + me.suspendChanges(); + me.doDeselect(selections, suppressEvent); + me.resumeChanges(); + + if (!suppressEvent) { + me.maybeFireSelectionChange(me.getSelection().length !== start); + } + }, + + getSelectionStart: function () { + if (!this.selectionStart) { + this.setSelectionStart(this.lastFocused); + } + + return this.selectionStart; + }, + + setSelectionStart: function (selection) { + this.selectionStart = selection; + }, + + + + + selectWithEvent: function(record, e) { + var me = this, + isSelected = me.isSelected(record), + shift = e.shiftKey, + ctrl = e.ctrlKey, + start = shift && me.getSelectionStart(), + selected = me.getSelection(), + len = selected.length, + allowDeselect = me.allowDeselect, + toDeselect, i, item; + + switch (me.selectionMode) { + case 'MULTI': + if (shift && start) { + me.selectRange(start, record, ctrl); + } else if (ctrl && isSelected) { + me.doDeselect(record, false); + } else if (ctrl) { + me.doSelect(record, true, false); + } else if (isSelected && !shift && !ctrl && len > 1) { + toDeselect = []; + + for (i = 0; i < len; ++i) { + item = selected[i]; + if (item !== record) { + toDeselect.push(item); + } + } + + me.doDeselect(toDeselect); + } else if (!isSelected) { + me.doSelect(record, false); + } + break; + case 'SIMPLE': + if (isSelected) { + me.doDeselect(record); + } else { + me.doSelect(record, true); + } + break; + case 'SINGLE': + if (allowDeselect && !ctrl) { + allowDeselect = me.toggleOnClick; + } + if (allowDeselect && isSelected) { + me.doDeselect(record); + } else { + me.doSelect(record, false); + } + break; + } + + + + + + if (!shift) { + if (me.isSelected(record)) { + me.selectionStart = record; + } else { + me.selectionStart = null; + } + } + }, + + + + + afterKeyNavigate: function(e, record) { + var me = this, + recIdx, + fromIdx, + isSelected = me.isSelected(record), + from = (me.selectionStart && me.isSelected(me.lastFocused)) ? me.selectionStart : (me.selectionStart = me.lastFocused), + key = e.getCharCode(), + isSpace = key === e.SPACE, + direction = key === e.UP || key === e.PAGE_UP ? 'up' : (key === e.DOWN || key === e.DOWN ? 'down' : null); + + switch (me.selectionMode) { + case 'MULTI': + + if (isSpace) { + + if (e.shiftKey) { + me.selectRange(from, record, e.ctrlKey); + } else { + + + if (isSelected) { + me.doDeselect(record, e.ctrlKey); + + + + me.setLastFocused(null); + me.setLastFocused(record); + } + + else { + me.doSelect(record, e.ctrlKey); + } + } + } + + + else if (e.shiftKey && from) { + + + fromIdx = me.store.indexOf(from); + recIdx = me.store.indexOf(record); + + + if (direction === 'up' && fromIdx <= recIdx) { + me.deselectRange(me.lastFocused, recIdx + 1); + } + else if (direction === 'down' && fromIdx >= recIdx) { + me.deselectRange(me.lastFocused, recIdx - 1); + } + + + else if (from !== record) { + me.selectRange(from, record, e.ctrlKey); + } + me.lastSelected = record; + me.setLastFocused(record); + } + + + else if (e.ctrlKey && isSelected) { + me.setLastFocused(record); + } + + + else if (e.ctrlKey) { + me.setLastFocused(record); + } + + + else { + me.doSelect(record, false); + } + break; + case 'SIMPLE': + if (isSelected) { + if (me.allowDeselect) { + me.doDeselect(record); + } + } else { + me.doSelect(record, true); + } + break; + case 'SINGLE': + + if (isSpace) { + if (isSelected) { + if (me.allowDeselect) { + me.doDeselect(record); + me.setLastFocused(record); + } + } else { + me.doSelect(record); + } + } + + + else if (e.ctrlKey) { + me.setLastFocused(record); + } + + + else if (isSpace && me.allowDeselect && isSelected) { + me.doDeselect(record); + } + + + else { + me.doSelect(record, false); + + + + + + + me.setLastFocused(record); + } + break; + } + + + + + + if (!e.shiftKey) { + if (me.isSelected(record)) { + me.selectionStart = record; + } + } + }, + + + selectRange : function(startRow, endRow, keepExisting) { + var me = this, + store = me.store, + selected = me.selected.items, + result, i, len, toSelect, toDeselect, idx, rec; + + if (me.isLocked()){ + return; + } + + result = me.normalizeRowRange(startRow, endRow); + startRow = result[0]; + endRow = result[1]; + + toSelect = []; + for (i = startRow; i <= endRow; i++){ + if (!me.isSelected(store.getAt(i))) { + toSelect.push(store.getAt(i)); + } + } + + if (!keepExisting) { + + toDeselect = []; + me.suspendChanges(); + + for (i = 0, len = selected.length; i < len; ++i) { + rec = selected[i]; + idx = store.indexOf(rec); + if (idx < startRow || idx > endRow) { + toDeselect.push(rec) + } + } + + for (i = 0, len = toDeselect.length; i < len; ++i) { + me.doDeselect(toDeselect[i]); + } + me.resumeChanges(); + } + + if (toSelect.length) { + me.doMultiSelect(toSelect, true); + } else { + me.maybeFireSelectionChange(toDeselect.length > 0); + } + }, + + + deselectRange : function(startRow, endRow) { + var me = this, + store = me.store, + result, i, toDeselect, record; + + if (me.isLocked()){ + return; + } + + result = me.normalizeRowRange(startRow, endRow); + startRow = result[0]; + endRow = result[1]; + + toDeselect = []; + for (i = startRow; i <= endRow; i++) { + record = store.getAt(i); + if (me.isSelected(record)) { + toDeselect.push(record); + } + } + if (toDeselect.length) { + me.doDeselect(toDeselect); + } + }, + + normalizeRowRange: function(startRow, endRow) { + var store = this.store, + tmp; + + if (!Ext.isNumber(startRow)) { + startRow = store.indexOf(startRow); + } + startRow = Math.max(0, startRow); + + if (!Ext.isNumber(endRow)) { + endRow = store.indexOf(endRow); + } + endRow = Math.min(endRow, store.getCount() - 1); + + + if (startRow > endRow){ + tmp = endRow; + endRow = startRow; + startRow = tmp; + } + + return [startRow, endRow]; + }, + + onModelIdChanged: function(store, model, oldId, newId, oldInternalId) { + this.selected.updateKey(oldInternalId, newId); + }, + + + select: function(records, keepExisting, suppressEvent) { + + if (Ext.isDefined(records) && !(Ext.isArray(records) && !records.length)) { + this.doSelect(records, keepExisting, suppressEvent); + } + }, + + + deselect: function(records, suppressEvent) { + this.doDeselect(records, suppressEvent); + }, + + doSelect: function(records, keepExisting, suppressEvent) { + var me = this, + record; + + if (me.locked || !me.store) { + return; + } + if (typeof records === "number") { + record = me.store.getAt(records); + + if (!record) { + return; + } + records = [record]; + } + if (me.selectionMode == "SINGLE" && records) { + record = records.length ? records[0] : records; + me.doSingleSelect(record, suppressEvent); + } else { + me.doMultiSelect(records, keepExisting, suppressEvent); + } + }, + + doMultiSelect: function(records, keepExisting, suppressEvent) { + var me = this, + selected = me.selected, + change = false, + result, i, len, record, commit; + + if (me.locked) { + return; + } + + records = !Ext.isArray(records) ? [records] : records; + len = records.length; + if (!keepExisting && selected.getCount() > 0) { + result = me.deselectDuringSelect(records, selected.getRange(), suppressEvent); + if (result[0]) { + + + me.maybeFireSelectionChange(result[1] > 0 && !suppressEvent); + return; + } + } + + commit = function() { + selected.add(record); + change = true; + }; + + for (i = 0; i < len; i++) { + record = records[i]; + if (me.isSelected(record)) { + continue; + } + me.lastSelected = record; + + me.onSelectChange(record, true, suppressEvent, commit); + } + if (!me.preventFocus) { + me.setLastFocused(record, suppressEvent); + } + + me.maybeFireSelectionChange(change && !suppressEvent); + }, + + deselectDuringSelect: function(toSelect, selected, suppressEvent) { + var me = this, + len = selected.length, + changed = 0, + failed = false, + item, i; + + + me.suspendChanges(); + for (i = 0; i < len; ++i) { + item = selected[i]; + if (!Ext.Array.contains(toSelect, item)) { + if (me.doDeselect(item, suppressEvent)) { + ++changed; + } else { + failed = true; + } + } + } + me.resumeChanges(); + + return [failed, changed]; + }, + + + doDeselect: function(records, suppressEvent) { + var me = this, + selected = me.selected, + i = 0, + len, record, + attempted = 0, + accepted = 0, + commit; + + if (me.locked || !me.store) { + return false; + } + + if (typeof records === "number") { + + record = me.store.getAt(records); + if (!record) { + return false; + } + records = [record]; + } else if (!Ext.isArray(records)) { + records = [records]; + } + + commit = function() { + ++accepted; + selected.remove(record); + }; + + len = records.length; + + me.suspendChanges(); + for (; i < len; i++) { + record = records[i]; + if (me.isSelected(record)) { + if (me.lastSelected === record) { + me.lastSelected = selected.last(); + if (me.lastFocused === record) { + me.setLastFocused(null); + } + } + ++attempted; + me.onSelectChange(record, false, suppressEvent, commit); + } + } + me.resumeChanges(); + + + me.maybeFireSelectionChange(accepted > 0 && !suppressEvent); + return accepted === attempted; + }, + + doSingleSelect: function(record, suppressEvent) { + var me = this, + changed = false, + selected = me.selected, + commit; + + if (me.locked) { + return; + } + + + if (me.isSelected(record)) { + return; + } + + if (selected.getCount()) { + me.suspendChanges(); + if (!me.doDeselect(me.lastSelected, suppressEvent)) { + me.resumeChanges(); + return; + } + me.resumeChanges(); + } + + commit = function() { + selected.add(record); + me.lastSelected = record; + changed = true; + }; + + me.onSelectChange(record, true, suppressEvent, commit); + + if (changed) { + if (!suppressEvent && !me.preventFocus) { + me.setLastFocused(record); + } + me.maybeFireSelectionChange(!suppressEvent); + } + }, + + + setLastFocused: function(record, supressFocus) { + var me = this, + recordBeforeLast = me.lastFocused; + + + if (record !== recordBeforeLast) { + me.lastFocused = record; + me.onLastFocusChanged(recordBeforeLast, record, supressFocus); + } + }, + + + isFocused: function(record) { + return record === this.getLastFocused(); + }, + + + + maybeFireSelectionChange: function(fireEvent) { + var me = this; + if (fireEvent && !me.suspendChange) { + me.fireEvent('selectionchange', me, me.getSelection()); + } + }, + + + getLastSelected: function() { + return this.lastSelected; + }, + + getLastFocused: function() { + return this.lastFocused; + }, + + + getSelection: function() { + return this.selected.getRange(); + }, + + + getSelectionMode: function() { + return this.selectionMode; + }, + + + setSelectionMode: function(selMode) { + selMode = selMode ? selMode.toUpperCase() : 'SINGLE'; + + + this.selectionMode = this.modes[selMode] ? selMode : 'SINGLE'; + }, + + + isLocked: function() { + return this.locked; + }, + + + setLocked: function(locked) { + this.locked = !!locked; + }, + + + isRangeSelected: function(startRow, endRow) { + var me = this, + store = me.store, + i, result; + + result = me.normalizeRowRange(startRow, endRow); + startRow = result[0]; + endRow = result[1]; + + + for (i = startRow; i <= endRow; i++) { + if (!me.isSelected(store.getAt(i))) { + return false; + } + } + return true; + }, + + + isSelected: function(record) { + record = Ext.isNumber(record) ? this.store.getAt(record) : record; + return this.selected.contains(record); + }, + + + hasSelection: function() { + return this.selected.getCount() > 0; + }, + + getSelectionId: function(record){ + return record.internalId; + }, + + pruneIf: function() { + var me = this, + selected = me.selected, + toRemove = [], + len = selected.length, + i, item; + + if (me.pruneRemoved) { + for (i = 0; i < len; i++) { + item = selected.getAt(i); + if (!me.getStoreRecord(item)) { + toRemove.push(item); + } + } + if (toRemove.length) { + for (i = 0, len = toRemove.length; i < len; i++) { + selected.remove(toRemove[i]); + } + me.maybeFireSelectionChange(true); + } + } + }, + + + + + getStoreRecord: function(record) { + var store = this.store, + records, rec, len, id, i; + + if (record) { + if (record.hasId()) { + return store.getById(record.getId()); + } else { + records = store.data.items; + len = records.length; + id = record.internalId; + + for (i = 0; i < len; ++i) { + rec = records[i]; + if (id === rec.internalId) { + return rec; + } + } + } + } + return null; + }, + + refresh: function() { + var me = this, + store = me.store, + rec, + toBeSelected = [], + toBeReAdded = [], + oldSelections = me.getSelection(), + len = oldSelections.length, + selection, + change, + i = 0, + lastFocused = me.getLastFocused(); + + + if (!store) { + return; + } + + + + for (; i < len; i++) { + selection = oldSelections[i]; + if (store.indexOf(selection) !== -1) { + toBeSelected.push(selection); + } + + + else if (!me.pruneRemoved) { + + rec = store.getById(selection.getId()); + if (rec) { + toBeSelected.push(rec); + } + + else { + toBeReAdded.push(selection) + } + } + + + if (me.mode === 'SINGLE' && toBeReAdded.length) { + break; + } + } + + + + if (me.selected.getCount() != (toBeSelected.length + toBeReAdded.length)) { + change = true; + } + + me.clearSelections(); + + if (store.indexOf(lastFocused) !== -1) { + + me.setLastFocused(lastFocused, true); + } + + if (toBeSelected.length) { + + me.doSelect(toBeSelected, false, true); + } + + + if (toBeReAdded.length) { + me.selected.addAll(toBeReAdded); + + + if (!me.lastSelected) { + me.lastSelected = toBeReAdded[toBeReAdded.length - 1]; + } + } + + me.maybeFireSelectionChange(change); + }, + + + clearSelections: function() { + + this.selected.clear(); + this.lastSelected = null; + this.setLastFocused(null); + }, + + + onStoreAdd: Ext.emptyFn, + + + + onStoreClear: function() { + if (this.selected.getCount() > 0) { + this.clearSelections(); + this.maybeFireSelectionChange(true); + } + }, + + + + + onStoreRemove: function(store, records, indexes, isMove) { + var me = this; + + + if (me.selectionStart && Ext.Array.contains(records, me.selectionStart)) { + me.selectionStart = null; + } + + if (isMove || me.locked || !me.pruneRemoved) { + return; + } + me.deselectDeletedRecords(records); + }, + + + + deselectDeletedRecords: function(records) { + var me = this, + selected = me.selected, + i, length = records.length, + removed = 0, + record; + + + for (i = 0; i < length; i++) { + record = records[i]; + if (selected.remove(record)) { + if (me.lastSelected == record) { + me.lastSelected = null; + } + if (me.getLastFocused() == record) { + me.setLastFocused(null); + } + ++removed; + } + } + if (removed) { + me.maybeFireSelectionChange(true); + } + }, + + + getCount: function() { + return this.selected.getCount(); + }, + + + onUpdate: Ext.emptyFn, + + + destroy: function(){ + this.clearListeners(); + }, + + + onStoreUpdate: Ext.emptyFn, + + onStoreRefresh: function(){ + var me = this, + selected = me.selected, + items, length, i, rec, storeRec; + + if (me.store.buffered) { + return; + } + + items = selected.items; + length = items.length; + + me.lastSelected = me.getStoreRecord(me.lastSelected); + + for (i = 0; i < length; ++i) { + rec = items[i]; + storeRec = me.getStoreRecord(rec); + if (storeRec) { + if (rec.hasId()) { + + + me.selected.replace(storeRec); + } + } else { + + me.selected.remove(rec); + } + } + }, + + + onStoreLoad: Ext.emptyFn, + + + onSelectChange: function(record, isSelected, suppressEvent, commitFn) { + var me = this, + eventName = isSelected ? 'select' : 'deselect'; + + if ((suppressEvent || me.fireEvent('before' + eventName, me, record)) !== false && + commitFn() !== false) { + + if (!suppressEvent) { + me.fireEvent(eventName, me, record); + } + } + }, + + + onLastFocusChanged: function(oldFocused, newFocused) { + this.fireEvent('focuschange', this, oldFocused, newFocused); + }, + + + onEditorKey: Ext.emptyFn, + + + beforeViewRender: function(view) { + this.views = this.views || []; + this.views.push(view); + this.bindStore(view.getStore(), true); + }, + + + bindComponent: Ext.emptyFn +}); + + + +Ext.define('Ext.selection.DataViewModel', { + extend: Ext.selection.Model , + + + + deselectOnContainerClick: true, + + + enableKeyNav: true, + + constructor: function(cfg){ + this.addEvents( + + 'beforedeselect', + + + 'beforeselect', + + + 'deselect', + + + 'select' + ); + this.callParent(arguments); + }, + + bindComponent: function(view) { + var me = this, + eventListeners = { + refresh: me.refresh, + scope: me + }; + + me.view = view; + me.bindStore(view.getStore()); + + eventListeners[view.triggerEvent] = me.onItemClick; + eventListeners[view.triggerCtEvent] = me.onContainerClick; + + view.on(eventListeners); + + if (me.enableKeyNav) { + me.initKeyNav(view); + } + }, + + onUpdate: function(record){ + var view = this.view; + if (view && this.isSelected(record)) { + view.onItemSelect(record); + } + }, + + onItemClick: function(view, record, item, index, e) { + this.selectWithEvent(record, e); + }, + + onContainerClick: function() { + if (this.deselectOnContainerClick) { + this.deselectAll(); + } + }, + + initKeyNav: function(view) { + var me = this; + + if (!view.rendered) { + view.on({ + render: Ext.Function.bind(me.initKeyNav, me, [view]), + single: true + }); + return; + } + + view.el.set({ + tabIndex: -1 + }); + me.keyNav = new Ext.util.KeyNav({ + target: view.el, + ignoreInputFields: true, + down: Ext.pass(me.onNavKey, [1], me), + right: Ext.pass(me.onNavKey, [1], me), + left: Ext.pass(me.onNavKey, [-1], me), + up: Ext.pass(me.onNavKey, [-1], me), + scope: me + }); + }, + + onNavKey: function(step) { + step = step || 1; + var me = this, + view = me.view, + selected = me.getSelection()[0], + numRecords = me.view.store.getCount(), + idx; + + if (selected) { + idx = view.indexOf(view.getNode(selected)) + step; + } else { + idx = 0; + } + + if (idx < 0) { + idx = numRecords - 1; + } else if (idx >= numRecords) { + idx = 0; + } + + me.select(idx); + }, + + + onSelectChange: function(record, isSelected, suppressEvent, commitFn) { + var me = this, + view = me.view, + eventName = isSelected ? 'select' : 'deselect'; + + if ((suppressEvent || me.fireEvent('before' + eventName, me, record)) !== false && + commitFn() !== false) { + + if (view) { + if (isSelected) { + view.onItemSelect(record); + } else { + view.onItemDeselect(record); + } + } + + if (!suppressEvent) { + me.fireEvent(eventName, me, record); + } + } + }, + + onLastFocusChanged: function(oldFocus, newFocus, suppressFocus){ + var view = this.view; + if (view && !suppressFocus && newFocus) { + view.focusNode(newFocus); + this.fireEvent('focuschange', this, oldFocus, newFocus); + } + }, + + destroy: function(){ + Ext.destroy(this.keyNav); + this.callParent(); + } +}); + + + +Ext.define('Ext.view.AbstractView', { + extend: Ext.Component , + + + + + + + + mixins: { + bindable: Ext.util.Bindable + }, + + inheritableStatics: { + getRecord: function(node) { + return this.getBoundView(node).getRecord(node); + }, + + getBoundView: function(node) { + return Ext.getCmp(node.boundView); + } + }, + + + + + + deferInitialRefresh: true, + + + + + itemCls: Ext.baseCSSPrefix + 'dataview-item', + + + + + + + + loadingText: 'Loading...', + + + + loadMask: true, + + + + + loadingUseMsg: true, + + + + + + selectedItemCls: Ext.baseCSSPrefix + 'item-selected', + + + + emptyText: "", + + + + deferEmptyText: true, + + + trackOver: false, + + + blockRefresh: false, + + + + + preserveScrollOnRefresh: false, + + ariaRole: 'listbox', + itemAriaRole: 'option', + + + last: false, + + triggerEvent: 'itemclick', + triggerCtEvent: 'containerclick', + + + + + + refreshNeeded: true, + + addCmpEvents: Ext.emptyFn, + + + initComponent : function(){ + var me = this, + isDef = Ext.isDefined, + itemTpl = me.itemTpl, + memberFn = {}; + + if (itemTpl) { + if (Ext.isArray(itemTpl)) { + + itemTpl = itemTpl.join(''); + } else if (Ext.isObject(itemTpl)) { + + memberFn = Ext.apply(memberFn, itemTpl.initialConfig); + itemTpl = itemTpl.html; + } + + if (!me.itemSelector) { + me.itemSelector = '.' + me.itemCls; + } + + itemTpl = Ext.String.format('
{1}
', me.itemCls, itemTpl, me.itemAriaRole); + me.tpl = new Ext.XTemplate(itemTpl, memberFn); + } + + if (!isDef(me.tpl) || !isDef(me.itemSelector)) { + Ext.Error.raise({ + sourceClass: 'Ext.view.View', + tpl: me.tpl, + itemSelector: me.itemSelector, + msg: "DataView requires both tpl and itemSelector configurations to be defined." + }); + } + + me.callParent(); + me.tpl = me.getTpl('tpl'); + + + + if (isDef(me.overCls) || isDef(me.overClass)) { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn('Ext.view.View: Using the deprecated overCls or overClass configuration. Use overItemCls instead.'); + } + me.overItemCls = me.overCls || me.overClass; + delete me.overCls; + delete me.overClass; + } + + if (isDef(me.selectedCls) || isDef(me.selectedClass)) { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn('Ext.view.View: Using the deprecated selectedCls or selectedClass configuration. Use selectedItemCls instead.'); + } + me.selectedItemCls = me.selectedCls || me.selectedClass; + delete me.selectedCls; + delete me.selectedClass; + } + + if (me.overItemCls) { + me.trackOver = true; + } + + me.addEvents( + + 'beforerefresh', + + 'refresh', + + 'viewready', + + 'itemupdate', + + 'itemadd', + + 'itemremove' + ); + + me.addCmpEvents(); + + + me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store'); + + + if (!me.dataSource) { + me.dataSource = me.store; + } + + + me.bindStore(me.dataSource, true, 'dataSource'); + if (!me.all) { + me.all = new Ext.CompositeElementLite(); + } + + + me.scrollState = { + top: 0, + left: 0 + }; + me.on({ + scroll: me.onViewScroll, + element: 'el', + scope: me + }); + }, + + onRender: function() { + var me = this, + mask = me.loadMask, + maskStore = me.getMaskStore(), + cfg = { + target: me, + msg: me.loadingText, + useMsg: me.loadingUseMsg, + + + store: maskStore + }; + + me.callParent(arguments); + + if (mask && !maskStore.proxy.isSynchronous) { + + if (me.loadingCls) { + cfg.msgCls = me.loadingCls; + } + + if (Ext.isObject(mask)) { + cfg = Ext.apply(cfg, mask); + } + + + + + me.loadMask = new Ext.LoadMask(cfg); + me.loadMask.on({ + scope: me, + beforeshow: me.onMaskBeforeShow, + hide: me.onMaskHide + }); + } + }, + + beforeLayout: function() { + var me = this; + + me.callParent(arguments); + + + + if (me.refreshNeeded && !me.pendingRefresh) { + + if (me.refreshCounter) { + me.refresh(); + } + + else { + me.doFirstRefresh(me.dataSource); + } + } + }, + + getMaskStore: function(){ + return this.store; + }, + + onMaskBeforeShow: function(){ + var me = this, + loadingHeight = me.loadingHeight; + + if (loadingHeight && loadingHeight > me.getHeight()) { + me.hasLoadingHeight = true; + me.oldMinHeight = me.minHeight; + me.minHeight = loadingHeight; + me.updateLayout(); + } + }, + + onMaskHide: function(){ + var me = this; + + if (!me.destroying && me.hasLoadingHeight) { + me.minHeight = me.oldMinHeight; + me.updateLayout(); + delete me.hasLoadingHeight; + } + }, + + beforeRender: function() { + this.callParent(arguments); + this.getSelectionModel().beforeViewRender(this); + }, + + afterRender: function() { + this.callParent(arguments); + + + + + this.getSelectionModel().bindComponent(this); + }, + + + getSelectionModel: function(){ + var me = this, + mode = 'SINGLE'; + + if (me.simpleSelect) { + mode = 'SIMPLE'; + } else if (me.multiSelect) { + mode = 'MULTI'; + } + + + if (!me.selModel || !me.selModel.events) { + me.selModel = new Ext.selection.DataViewModel(Ext.apply({ + allowDeselect: me.allowDeselect, + mode: mode + }, me.selModel)); + } + + if (!me.selModel.hasRelaySetup) { + me.relayEvents(me.selModel, [ + 'selectionchange', 'beforeselect', 'beforedeselect', 'select', 'deselect', 'focuschange' + ]); + me.selModel.hasRelaySetup = true; + } + + + + if (me.disableSelection) { + me.selModel.locked = true; + } + + return me.selModel; + }, + + + refresh: function() { + var me = this, + targetEl, + targetParent, + oldDisplay, + nextSibling, + dom, + records, + hasFirstRefresh; + + if (!me.rendered || me.isDestroyed) { + return; + } + + if (!me.hasListeners.beforerefresh || me.fireEvent('beforerefresh', me) !== false) { + targetEl = me.getTargetEl(); + records = me.getViewRange(); + dom = targetEl.dom; + + + + if (!me.preserveScrollOnRefresh) { + targetParent = dom.parentNode; + oldDisplay = dom.style.display; + dom.style.display = 'none'; + nextSibling = dom.nextSibling; + targetParent.removeChild(dom); + } + + if (me.refreshCounter) { + hasFirstRefresh = true; + me.clearViewEl(); + } else { + me.fixedNodes = targetEl.dom.childNodes.length; + me.refreshCounter = 1; + } + + + + + + me.tpl.append(targetEl, me.collectData(records, me.all.startIndex || 0)); + + + if (!me.preserveScrollOnRefresh) { + targetParent.insertBefore(dom, nextSibling); + dom.style.display = oldDisplay; + } + + + + if (records.length < 1) { + + if (!this.store.loading && (!me.deferEmptyText || hasFirstRefresh)) { + Ext.core.DomHelper.insertHtml('beforeEnd', targetEl.dom, me.emptyText); + } + me.all.clear(); + } else { + me.collectNodes(targetEl.dom); + me.updateIndexes(0); + } + + + if (hasFirstRefresh) { + + if (me.refreshSelmodelOnRefresh !== false) { + me.selModel.refresh(); + } else { + + me.selModel.pruneIf(); + } + } + + me.refreshNeeded = false; + + + this.refreshSize(); + + me.fireEvent('refresh', me); + + + + if (!me.viewReady) { + + + me.viewReady = true; + me.fireEvent('viewready', me); + } + } + }, + + + + collectNodes: function(targetEl) { + var all = this.all; + all.fill(Ext.query(this.getItemSelector(), Ext.getDom(targetEl)), all.startIndex || 0); + }, + + getViewRange: function() { + return this.dataSource.getRange(); + }, + + + refreshSize: function() { + var sizeModel = this.getSizeModel(); + if (sizeModel.height.shrinkWrap || sizeModel.width.shrinkWrap) { + this.updateLayout(); + } + }, + + clearViewEl: function(){ + + + + + + + + + var me = this, + el = me.getTargetEl(); + + if (me.fixedNodes) { + while (el.dom.childNodes[me.fixedNodes]) { + el.dom.removeChild(el.dom.childNodes[me.fixedNodes]); + } + } else { + el.update(''); + } + me.refreshCounter++; + }, + + + onViewScroll: Ext.emptyFn, + + onIdChanged: Ext.emptyFn, + + + saveScrollState: function() { + if (this.rendered) { + var dom = this.el.dom, + state = this.scrollState; + + state.left = dom.scrollLeft; + state.top = dom.scrollTop; + } + }, + + + restoreScrollState: function() { + if (this.rendered) { + var dom = this.el.dom, + state = this.scrollState; + + dom.scrollLeft = state.left; + dom.scrollTop = state.top; + } + }, + + + prepareData: function(data, index, record) { + var associatedData, attr, hasCopied; + if (record) { + associatedData = record.getAssociatedData(); + for (attr in associatedData) { + if (associatedData.hasOwnProperty(attr)) { + + + + + if (!hasCopied) { + data = Ext.Object.chain(data); + hasCopied = true; + } + data[attr] = associatedData[attr]; + } + } + } + return data; + }, + + + collectData: function(records, startIndex){ + var data = [], + i = 0, + len = records.length, + record; + + for (; i < len; i++) { + record = records[i]; + data[i] = this.prepareData(record.data, startIndex + i, record); + } + return data; + }, + + + bufferRender : function(records, index) { + var me = this, + div = me.renderBuffer || (me.renderBuffer = document.createElement('div')); + + me.tpl.overwrite(div, me.collectData(records, index)); + return Ext.DomQuery.select(me.getItemSelector(), div); + }, + + getNodeContainer: function() { + return this.getTargetEl(); + }, + + + onUpdate : function(ds, record){ + var me = this, + index, + node; + + if (me.viewReady) { + index = me.dataSource.indexOf(record); + if (index > -1) { + node = me.bufferRender([record], index)[0]; + + if (me.getNode(record)) { + me.all.replaceElement(index, node, true); + me.updateIndexes(index, index); + + me.selModel.onUpdate(record); + if (me.hasListeners.itemupdate) { + me.fireEvent('itemupdate', record, index, node); + } + return node; + } + } + } + + }, + + + + + onReplace: function(store, startIndex, oldRecords, newRecords) { + var me = this, + endIndex, + rows = me.all, + nodes, item, + i, j; + + if (me.rendered) { + + + nodes = me.bufferRender(newRecords, startIndex, true); + item = rows.item(startIndex); + if (item) { + rows.item(startIndex).insertSibling(nodes, 'before', true); + } else { + me.appendNodes(nodes); + } + rows.insert(startIndex, nodes); + + startIndex += newRecords.length; + endIndex = startIndex + oldRecords.length - 1; + + + rows.removeRange(startIndex, endIndex, true); + + + if (me.refreshSelmodelOnRefresh !== false) { + me.selModel.refresh(); + } + + + me.updateIndexes(startIndex); + + + me.refreshSize(); + + + if (me.hasListeners.itemremove) { + for (i = oldRecords.length, j = endIndex; i >= 0; --i, --j) { + me.fireEvent('itemremove', oldRecords[i], j); + } + } + + if (me.hasListeners.itemadd) { + me.fireEvent('itemadd', newRecords, startIndex, nodes); + } + } + }, + + + onAdd : function(store, records, index) { + var me = this, + nodes; + + if (me.rendered) { + + + if (me.all.getCount() === 0) { + me.refresh(); + nodes = me.all.slice(); + } else { + nodes = me.doAdd(records, index); + + if (me.refreshSelmodelOnRefresh !== false) { + me.selModel.refresh(); + } + me.updateIndexes(index); + + + me.refreshSize(); + } + + if (me.hasListeners.itemadd) { + me.fireEvent('itemadd', records, index, nodes); + } + } + + }, + + appendNodes: function(nodes) { + var fragment = document.createDocumentFragment(), + len = nodes.length, + i; + + for (i = 0; i < len; ++i) { + fragment.appendChild(nodes[i]); + } + this.getNodeContainer().appendChild(fragment); + }, + + doAdd: function(records, index) { + var me = this, + nodes = me.bufferRender(records, index, true), + all = me.all, + count = all.getCount(), + firstRowIndex = all.startIndex || 0, + lastRowIndex = all.endIndex || count - 1; + + + if (count === 0 || index > lastRowIndex) { + me.appendNodes(nodes); + } + + + else if (index <= firstRowIndex) { + all.item(firstRowIndex).insertSibling(nodes, 'before', true); + } + + + else { + all.item(index).insertSibling(nodes, 'before', true); + } + + all.insert(index, nodes); + return nodes; + }, + + + onRemove : function(ds, records, indices) { + var me = this, + fireItemRemove = me.hasListeners.itemremove, + i, + record, + index; + + if (me.all.getCount()) { + if (me.dataSource.getCount() === 0) { + + if (fireItemRemove) { + for (i = indices.length - 1; i >= 0; --i) { + me.fireEvent('itemremove', records[i], indices[i]); + } + } + me.refresh(); + } else { + + + for (i = indices.length - 1; i >= 0; --i) { + record = records[i]; + index = indices[i]; + + + if (me.all.item(index)) { + me.doRemove(record, index); + if (fireItemRemove) { + me.fireEvent('itemremove', record, index); + } + } + } + me.updateIndexes(indices[0]); + } + + + this.refreshSize(); + } + }, + + + doRemove: function(record, index) { + this.all.removeElement(index, true); + }, + + + refreshNode : function(index) { + this.onUpdate(this.dataSource, this.store.getAt(index)); + }, + + + updateIndexes : function(startIndex, endIndex) { + var nodes = this.all.elements, + records = this.getViewRange(), + i; + + startIndex = startIndex || 0; + endIndex = endIndex || ((endIndex === 0) ? 0 : (nodes.length - 1)); + for (i = startIndex; i <= endIndex; i++) { + nodes[i].viewIndex = i; + nodes[i].viewRecordId = records[i].internalId; + if (!nodes[i].boundView) { + nodes[i].boundView = this.id; + } + } + }, + + + getStore : function() { + return this.store; + }, + + + bindStore: function(store, initial, propName) { + var me = this; + me.mixins.bindable.bindStore.apply(me, arguments); + + + + if (!initial) { + me.getSelectionModel().bindStore(store); + } + + + + + if (me.componentLayoutCounter) { + me.doFirstRefresh(store); + } + }, + + + doFirstRefresh: function(store) { + var me = this; + + + + + + + if (store && !store.loading) { + if (me.deferInitialRefresh) { + me.applyFirstRefresh(); + } else { + me.refresh(); + } + } + }, + + applyFirstRefresh: function(){ + var me = this; + if (me.isDestroyed) { + return; + } + me.pendingRefresh = 0; + + + + + + + + + + if (me.up('[isCollapsingOrExpanding]')) { + me.pendingRefresh = Ext.Function.defer(me.applyFirstRefresh, 100, me); + } else { + me.pendingRefresh = Ext.Function.defer(function () { + me.pendingRefresh = 0; + if (!me.isDestroyed) { + me.refresh(); + } + }, 1); + } + }, + + onUnbindStore: function(store) { + this.setMaskBind(null); + }, + + onBindStore: function(store, initial, propName) { + this.setMaskBind(store); + + + + if (!initial && propName === 'store') { + this.bindStore(store, false, 'dataSource'); + } + }, + + setMaskBind: function(store) { + var mask = this.loadMask; + if (mask && mask.bindStore) { + mask.bindStore(store); + } + }, + + getStoreListeners: function() { + var me = this; + return { + idchanged: me.onIdChanged, + refresh: me.onDataRefresh, + replace: me.onReplace, + add: me.onAdd, + bulkremove: me.onRemove, + update: me.onUpdate, + clear: me.refresh + }; + }, + + + onDataRefresh: function() { + this.refreshView(); + }, + + refreshView: function() { + var me = this, + + + blocked = me.blockRefresh || !me.rendered || me.up('[collapsed],[isCollapsingOrExpanding],[hidden]'); + + + + if (blocked) { + me.refreshNeeded = true; + me.deferInitialRefresh = false; + } else { + me.refresh(); + } + }, + + + findItemByChild: function(node){ + return Ext.fly(node).findParent(this.getItemSelector(), this.getTargetEl()); + }, + + + findTargetByEvent: function(e) { + return e.getTarget(this.getItemSelector(), this.getTargetEl()); + }, + + + + getSelectedNodes: function(){ + var nodes = [], + records = this.selModel.getSelection(), + ln = records.length, + i = 0; + + for (; i < ln; i++) { + nodes.push(this.getNode(records[i])); + } + + return nodes; + }, + + + getRecords: function(nodes) { + var records = [], + i = 0, + len = nodes.length, + data = this.dataSource.data; + + for (; i < len; i++) { + records[records.length] = data.getByKey(nodes[i].viewRecordId); + } + + return records; + }, + + + getRecord: function(node){ + return this.dataSource.data.getByKey(Ext.getDom(node).viewRecordId); + }, + + + + isSelected : function(node) { + + var r = this.getRecord(node); + return this.selModel.isSelected(r); + }, + + + select: function(records, keepExisting, suppressEvent) { + this.selModel.select(records, keepExisting, suppressEvent); + }, + + + deselect: function(records, suppressEvent) { + this.selModel.deselect(records, suppressEvent); + }, + + + getNode : function(nodeInfo) { + if ((!nodeInfo && nodeInfo !== 0) || !this.rendered) { + return null; + } + + if (Ext.isString(nodeInfo)) { + return document.getElementById(nodeInfo); + } + if (Ext.isNumber(nodeInfo)) { + return this.all.elements[nodeInfo]; + } + if (nodeInfo.isModel) { + return this.getNodeByRecord(nodeInfo); + } + return nodeInfo; + }, + + + getNodeByRecord: function(record) { + var ns = this.all.elements, + ln = ns.length, + i = 0; + + for (; i < ln; i++) { + if (ns[i].viewRecordId === record.internalId) { + return ns[i]; + } + } + + return null; + }, + + + getNodes: function(start, end) { + var all = this.all; + + if (end === undefined) { + end = all.getCount(); + } else { + end++; + } + return all.slice(start||0, end); + }, + + + indexOf: function(node) { + node = this.getNode(node); + if (!node && node !== 0) { + return -1; + } + if (Ext.isNumber(node.viewIndex)) { + return node.viewIndex; + } + return this.all.indexOf(node); + }, + + onDestroy : function() { + var me = this; + + me.all.clear(); + me.callParent(); + me.bindStore(null); + me.selModel.destroy(); + }, + + + onItemSelect: function(record) { + var node = this.getNode(record); + + if (node) { + Ext.fly(node).addCls(this.selectedItemCls); + } + }, + + + onItemDeselect: function(record) { + var node = this.getNode(record); + + if (node) { + Ext.fly(node).removeCls(this.selectedItemCls); + } + }, + + getItemSelector: function() { + return this.itemSelector; + } +}, function() { + + + + + Ext.deprecate('extjs', '4.0', function() { + Ext.view.AbstractView.override({ + + + + + + getSelectionCount : function(){ + if (Ext.global.console) { + Ext.global.console.warn("DataView: getSelectionCount will be removed, please interact with the Ext.selection.DataViewModel"); + } + return this.selModel.getSelection().length; + }, + + + getSelectedRecords : function(){ + if (Ext.global.console) { + Ext.global.console.warn("DataView: getSelectedRecords will be removed, please interact with the Ext.selection.DataViewModel"); + } + return this.selModel.getSelection(); + }, + + + + select: function(records, keepExisting, supressEvents) { + if (Ext.global.console) { + Ext.global.console.warn("DataView: select will be removed, please access select through a DataView's SelectionModel, ie: view.getSelectionModel().select()"); + } + var sm = this.getSelectionModel(); + return sm.select.apply(sm, arguments); + }, + + + clearSelections: function() { + if (Ext.global.console) { + Ext.global.console.warn("DataView: clearSelections will be removed, please access deselectAll through DataView's SelectionModel, ie: view.getSelectionModel().deselectAll()"); + } + var sm = this.getSelectionModel(); + return sm.deselectAll(); + } + }); + }); +}); + + + +Ext.define('Ext.view.View', { + extend: Ext.view.AbstractView , + alternateClassName: 'Ext.DataView', + alias: 'widget.dataview', + + + + deferHighlight: Ext.isIE7m ? 100 : 0, + + + mouseOverOutBuffer: 20, + + inputTagRe: /^textarea$|^input$/i, + + inheritableStatics: { + EventMap: { + mousedown: 'MouseDown', + mouseup: 'MouseUp', + click: 'Click', + dblclick: 'DblClick', + contextmenu: 'ContextMenu', + mouseover: 'MouseOver', + mouseout: 'MouseOut', + mouseenter: 'MouseEnter', + mouseleave: 'MouseLeave', + keydown: 'KeyDown', + focus: 'Focus' + } + }, + + initComponent: function() { + var me = this; + me.callParent(); + + + if (me.mouseOverOutBuffer) { + me.handleMouseOver = + Ext.Function.createBuffered(me.handleMouseOver, me.mouseOverOutBuffer, me); + me.handleMouseOut = + Ext.Function.createBuffered(me.handleMouseOut, me.mouseOverOutBuffer, me); + me.lastMouseOverEvent = new Ext.EventObjectImpl(); + me.lastMouseOutEvent = new Ext.EventObjectImpl(); + } + + + else if (me.deferHighlight){ + me.setHighlightedItem = + Ext.Function.createBuffered(me.setHighlightedItem, me.deferHighlight, me); + } + }, + + addCmpEvents: function() { + this.addEvents( + + 'beforeitemmousedown', + + 'beforeitemmouseup', + + 'beforeitemmouseenter', + + 'beforeitemmouseleave', + + 'beforeitemclick', + + 'beforeitemdblclick', + + 'beforeitemcontextmenu', + + 'beforeitemkeydown', + + 'itemmousedown', + + 'itemmouseup', + + 'itemmouseenter', + + 'itemmouseleave', + + 'itemclick', + + 'itemdblclick', + + 'itemcontextmenu', + + 'itemkeydown', + + 'beforecontainermousedown', + + 'beforecontainermouseup', + + 'beforecontainermouseover', + + 'beforecontainermouseout', + + 'beforecontainerclick', + + 'beforecontainerdblclick', + + 'beforecontainercontextmenu', + + 'beforecontainerkeydown', + + 'containermousedown', + + 'containermouseup', + + 'containermouseover', + + 'containermouseout', + + 'containerclick', + + 'containerdblclick', + + 'containercontextmenu', + + 'containerkeydown', + + + 'selectionchange', + + 'beforeselect', + + 'beforedeselect', + + 'select', + + 'deselect', + + 'focuschange', + + + 'highlightitem', + + + 'unhighlightitem' + ); + }, + + getFocusEl: function() { + return this.getTargetEl(); + }, + + + afterRender: function() { + var me = this; + + me.callParent(); + me.mon(me.getTargetEl(), { + scope: me, + + freezeEvent: true, + click: me.handleEvent, + mousedown: me.handleEvent, + mouseup: me.handleEvent, + dblclick: me.handleEvent, + contextmenu: me.handleEvent, + keydown: me.handleEvent, + mouseover: me.onMouseOver, + mouseout: me.onMouseOut + }); + }, + + onMouseOver: function(e) { + var me = this; + + + + if (me.mouseOverOutBuffer) { + e = me.lastMouseOverEvent.setEvent(e.browserEvent, true); + } + + me.handleMouseOver(e); + }, + + onMouseOut: function (e) { + var me = this, + + itemSelector = me.getTargetSelector(), + item = e.getTarget(itemSelector), + computedRelatedTarget = e.getRelatedTarget(itemSelector); + + + + if ((item === computedRelatedTarget) && !(item === null && computedRelatedTarget === null)) { + return; + } + + + + + + + + + + + + + if (me.mouseOverOutBuffer) { + e = me.lastMouseOutEvent.setEvent(e.browserEvent, true); + } + + me.handleMouseOut(e, item); + }, + + + getTargetSelector: function () { + return this.dataRowSelector || this.itemSelector; + }, + + handleMouseOver: function(e) { + var me = this, + + itemSelector = me.getTargetSelector(), + item = e.getTarget(itemSelector); + + + if (!me.isDestroyed) { + if (item) { + if (me.mouseOverItem !== item && me.el.contains(item)) { + me.mouseOverItem = e.item = item; + e.newType = 'mouseenter'; + me.handleEvent(e); + } + } else { + + me.handleEvent(e); + } + } + }, + + handleMouseOut: function (e, item) { + var me = this, + sourceView; + + + + if (!me.isDestroyed) { + + if (item && (sourceView = me.self.getBoundView(item))) { + e.item = item; + e.newType = 'mouseleave'; + sourceView.handleEvent(e); + sourceView.mouseOverItem = null; + } else { + + me.handleEvent(e); + } + } + }, + + handleEvent: function(e) { + var me = this, + key = e.type === 'keydown' && e.getKey(); + + if (me.processUIEvent(e) !== false) { + me.processSpecialEvent(e); + } + + + + + if (key === e.SPACE) { + if (!me.inputTagRe.test(e.getTarget().tagName)) { + e.stopEvent(); + } + } + }, + + + processItemEvent: Ext.emptyFn, + processContainerEvent: Ext.emptyFn, + processSpecialEvent: Ext.emptyFn, + + processUIEvent: function(e) { + + + + if (!Ext.getBody().isAncestor(e.target)) { + return; + } + + var me = this, + + item = e.getTarget(me.getTargetSelector(), me.getTargetEl()), + map = me.statics().EventMap, + index, record, + type = e.type, + newType = type, + sm; + + + + if (e.newType) { + newType = e.newType; + item = e.item; + + e.newType = e.item = null; + } + + + + if (!item && type == 'keydown') { + sm = me.getSelectionModel(); + record = sm.lastFocused || sm.getLastSelected(); + if (record) { + item = me.getNode(record, true); + } + } + + if (item) { + if (!record) { + record = me.getRecord(item); + } + index = me.indexInStore ? me.indexInStore(record) : me.indexOf(item); + + + + + + if (!record || me.processItemEvent(record, item, index, e) === false) { + return false; + } + + if ( + (me['onBeforeItem' + map[newType]](record, item, index, e) === false) || + (me.fireEvent('beforeitem' + newType, me, record, item, index, e) === false) || + (me['onItem' + map[newType]](record, item, index, e) === false) + ) { + return false; + } + + me.fireEvent('item' + newType, me, record, item, index, e); + } + else { + if ( + (me.processContainerEvent(e) === false) || + (me['onBeforeContainer' + map[type]](e) === false) || + (me.fireEvent('beforecontainer' + type, me, e) === false) || + (me['onContainer' + map[type]](e) === false) + ) { + return false; + } + + me.fireEvent('container' + type, me, e); + } + + return true; + }, + + + onItemMouseEnter: function(record, item, index, e) { + if (this.trackOver) { + this.highlightItem(item); + } + }, + + + onItemMouseLeave : function(record, item, index, e) { + if (this.trackOver) { + this.clearHighlight(); + } + }, + + + onItemMouseDown: Ext.emptyFn, + onItemMouseUp: Ext.emptyFn, + onItemFocus: Ext.emptyFn, + onItemClick: Ext.emptyFn, + onItemDblClick: Ext.emptyFn, + onItemContextMenu: Ext.emptyFn, + onItemKeyDown: Ext.emptyFn, + onBeforeItemMouseDown: Ext.emptyFn, + onBeforeItemMouseUp: Ext.emptyFn, + onBeforeItemFocus: Ext.emptyFn, + onBeforeItemMouseEnter: Ext.emptyFn, + onBeforeItemMouseLeave: Ext.emptyFn, + onBeforeItemClick: Ext.emptyFn, + onBeforeItemDblClick: Ext.emptyFn, + onBeforeItemContextMenu: Ext.emptyFn, + onBeforeItemKeyDown: Ext.emptyFn, + + + onContainerMouseDown: Ext.emptyFn, + onContainerMouseUp: Ext.emptyFn, + onContainerMouseOver: Ext.emptyFn, + onContainerMouseOut: Ext.emptyFn, + onContainerClick: Ext.emptyFn, + onContainerDblClick: Ext.emptyFn, + onContainerContextMenu: Ext.emptyFn, + onContainerKeyDown: Ext.emptyFn, + onBeforeContainerMouseDown: Ext.emptyFn, + onBeforeContainerMouseUp: Ext.emptyFn, + onBeforeContainerMouseOver: Ext.emptyFn, + onBeforeContainerMouseOut: Ext.emptyFn, + onBeforeContainerClick: Ext.emptyFn, + onBeforeContainerDblClick: Ext.emptyFn, + onBeforeContainerContextMenu: Ext.emptyFn, + onBeforeContainerKeyDown: Ext.emptyFn, + + + setHighlightedItem: function(item){ + var me = this, + highlighted = me.highlightedItem, + overItemCls = me.overItemCls, + beforeOverItemCls = me.beforeOverItemCls, + previous; + + if (highlighted != item){ + if (highlighted) { + Ext.fly(highlighted).removeCls(overItemCls); + previous = highlighted.previousSibling; + if (beforeOverItemCls && previous) { + Ext.fly(previous).removeCls(beforeOverItemCls); + } + me.fireEvent('unhighlightitem', me, highlighted); + } + + me.highlightedItem = item; + + if (item) { + Ext.fly(item).addCls(me.overItemCls); + previous = item.previousSibling; + if (beforeOverItemCls && previous) { + Ext.fly(previous).addCls(beforeOverItemCls); + } + me.fireEvent('highlightitem', me, item); + } + } + }, + + + highlightItem: function(item) { + this.setHighlightedItem(item); + }, + + + clearHighlight: function() { + this.setHighlightedItem(undefined); + }, + + onUpdate: function(store, record){ + var me = this, + node, + newNode, + highlighted; + + if (me.viewReady) { + node = me.getNode(record); + newNode = me.callParent(arguments); + highlighted = me.highlightedItem; + + if (highlighted && highlighted === node) { + delete me.highlightedItem; + if (newNode) { + me.highlightItem(newNode); + } + } + } + }, + + refresh: function() { + this.clearHighlight(); + this.callParent(arguments); + }, + + + focusNode: function(rec){ + var me = this, + node = me.getNode(rec, true), + el = me.el, + adjustmentY = 0, + adjustmentX = 0, + elRegion = el.getRegion(), + nodeRegion; + + + + elRegion.bottom = elRegion.top + el.dom.clientHeight; + elRegion.right = elRegion.left + el.dom.clientWidth; + if (node) { + nodeRegion = Ext.fly(node).getRegion(); + + if (nodeRegion.top < elRegion.top) { + adjustmentY = nodeRegion.top - elRegion.top; + + } else if (nodeRegion.bottom > elRegion.bottom) { + adjustmentY = nodeRegion.bottom - elRegion.bottom; + } + + + if (nodeRegion.left < elRegion.left) { + adjustmentX = nodeRegion.left - elRegion.left; + + } else if (nodeRegion.right > elRegion.right) { + adjustmentX = nodeRegion.right - elRegion.right; + } + + if (adjustmentX || adjustmentY) { + me.scrollBy(adjustmentX, adjustmentY, false); + } + + + Ext.fly(node).set({ + tabIndex: -1 + }); + + node.focus(); + } + }, + + bindStore: function (store, initial, propertyName) { + + var dataSource = this[propertyName]; + + if (dataSource && dataSource.isFeatureStore) { + + + + + + if (store.isFeatureStore) { + + this.bindStoreListeners(store); + + dataSource.bindStore(dataSource.store); + } else { + dataSource.bindStore(store); + } + } else { + this.callParent(arguments); + } + } +}); + + + +Ext.define('Ext.grid.CellContext', { + + + isCellContext: true, + + constructor: function(view) { + this.view = view; + }, + + isEqual: function(other) { + if (other) { + return this.record === other.record && this.columnHeader === other.columnHeader; + } + return false; + }, + + + setPosition: function(row, col) { + var me = this; + + + if (arguments.length === 1) { + if (row.view) { + me.view = row.view; + } + col = row.column; + row = row.row; + } + + me.setRow(row); + me.setColumn(col); + return me; + }, + + setRow: function(row) { + var me = this; + if (row !== undefined) { + + if (typeof row === 'number') { + me.row = Math.max(Math.min(row, me.view.dataSource.getCount() - 1), 0); + me.record = me.view.dataSource.getAt(row); + } + + else if (row.isModel) { + me.record = row; + me.row = me.view.indexOf(row); + } + + else if (row.tagName) { + me.record = me.view.getRecord(row); + me.row = me.view.indexOf(me.record); + } + } + }, + + setColumn: function(col) { + var me = this, + mgr = me.view.ownerCt.getColumnManager(); + + if (col !== undefined) { + if (typeof col === 'number') { + me.column = col; + me.columnHeader = mgr.getHeaderAtIndex(col); + } else if (col.isHeader) { + me.columnHeader = col; + me.column = mgr.getHeaderIndex(col); + } + } + } +}); + + + +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, + + constructor: function() { + + CSS = this; + }, + + + 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); + } + + 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; + }, + + + removeStyleSheet : function(id) { + var existing = doc.getElementById(id); + if (existing) { + existing.parentNode.removeChild(existing); + } + }, + + + 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); + }, + + + cacheStyleSheet : function(ss) { + if (!rules) { + rules = CSS.rules = {}; + } + try { + var ssRules = ss.cssRules || ss.rules, + i = ssRules.length - 1, + imports = ss.imports, + len = imports ? imports.length : 0, + rule, j; + + + for (j = 0; j < len; ++j) { + CSS.cacheStyleSheet(imports[j]); + } + + for (; i >= 0; --i) { + rule = ssRules[i]; + + if (rule.styleSheet) { + CSS.cacheStyleSheet(rule.styleSheet); + } + CSS.cacheRule(rule, ss); + } + } catch(e) {} + }, + + cacheRule: function(cssRule, styleSheet) { + + if (cssRule.styleSheet) { + return CSS.cacheStyleSheet(cssRule.styleSheet); + } + + var selectorText = cssRule.selectorText, + selectorCount, j; + + if (selectorText) { + + + selectorText = selectorText.split(','); + selectorCount = selectorText.length; + for (j = 0; j < selectorCount; j++) { + + + rules[Ext.String.trim(selectorText[j]).toLowerCase()] = { + parentStyleSheet: styleSheet, + cssRule: cssRule + }; + } + } + }, + + + getRules : function(refreshCache) { + var result = {}, + selector; + + if (rules === null || refreshCache) { + CSS.refreshCache(); + } + for (selector in rules) { + result[selector] = rules[selector].cssRule; + } + return result; + }, + + + 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) {} + } + }, + + + 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; + }, + + + 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; + }, + + + updateRule : function(selector, property, value) { + var rule, i, styles; + if (!Ext.isArray(selector)) { + rule = CSS.getRule(selector); + if (rule) { + + 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]; + } + } + }; +}); + + + +Ext.define('Ext.view.TableLayout', { + extend: Ext.layout.component.Auto , + + + alias: ['layout.tableview'], + type: 'tableview', + + beginLayout: function(ownerContext) { + var me = this, + otherSide = me.owner.lockingPartner, + owner = me.owner; + + me.callParent(arguments); + + + if (otherSide) { + me.lockedGrid = me.owner.up('[lockable]'); + me.lockedGrid.needsRowHeightSync = true; + if (!ownerContext.lockingPartner) { + ownerContext.lockingPartner = ownerContext.context.getItem(otherSide, otherSide.el); + if (ownerContext.lockingPartner && !ownerContext.lockingPartner.lockingPartner) { + ownerContext.lockingPartner.lockingPartner = ownerContext; + } + } + } + + + ownerContext.headerContext = ownerContext.context.getCmp(me.headerCt); + + + if (me.owner.body.dom) { + ownerContext.bodyContext = ownerContext.getEl(me.owner.body); + } + if (Ext.isWebKit) { + owner.el.select(owner.getBodySelector()).setStyle('table-layout', 'auto'); + } + }, + + calculate: function(ownerContext) { + var me = this, + lockingPartner = me.lockingPartner, + owner = me.owner, + contentHeight = 0, + emptyEl; + + + + if (ownerContext.headerContext.hasProp('columnWidthsDone')) { + if (!me.setColumnWidths(ownerContext)) { + me.done = false; + return; + } + ownerContext.state.columnWidthsSynced = true; + if (ownerContext.bodyContext) { + emptyEl = me.owner.el.down('.' + owner.ownerCt.emptyCls, true); + if (!emptyEl) { + contentHeight = ownerContext.bodyContext.el.dom.offsetHeight; + ownerContext.bodyContext.setHeight(contentHeight, false); + } else { + contentHeight = emptyEl.offsetHeight; + } + + if (ownerContext.headerContext.state.boxPlan.tooNarrow && ownerContext.ownerCtContext.sizeModel.height.shrinkWrap) { + contentHeight += Ext.getScrollbarSize().height; + } + ownerContext.setProp('contentHeight', contentHeight); + } + + + + if (lockingPartner && !lockingPartner.state.columnWidthsSynced) { + me.done = false; + } else { + me.callParent(arguments); + } + + } else { + me.done = false; + } + }, + + measureContentHeight: function(ownerContext) { + var lockingPartner = ownerContext.lockingPartner; + + + + if (!ownerContext.bodyContext || (ownerContext.state.columnWidthsSynced && (!lockingPartner || lockingPartner.state.columnWidthsSynced))) { + return this.callParent(arguments); + } + }, + + setColumnWidths: function(ownerContext) { + + if (!this.owner.body.dom) { + return true; + } + + var me = this, + owner = me.owner, + context = ownerContext.context, + columns = me.headerCt.getVisibleGridColumns(), + column, + i, + len = columns.length, + tableWidth = 0, + columnLineWidth = 0, + childContext, + colWidth, + isContentBox = !Ext.isBorderBox, + colGroup = owner.body.dom.firstChild, + isColGroup = colGroup.tagName.toUpperCase() === 'COLGROUP', + changedColumns = []; + + + if (context) { + context.currentLayout = me; + } + + + for (i = 0; i < len; i++) { + column = columns[i]; + childContext = context.getCmp(column); + + + if (!column.lastBox || column.lastBox.invalid || childContext.props.width !== column.lastBox.width || (childContext.cellWidth && childContext.cellWidth != childContext.props.width)) { + colWidth = childContext.props.width; + if (isNaN(colWidth)) { + + + + + childContext.getProp('width'); + return false; + } + tableWidth += colWidth; + childContext.columnIndex = i; + changedColumns.push(childContext); + } else { + tableWidth += column.lastBox.width; + } + + + childContext.cellWidth = childContext.props.width; + } + + + len = changedColumns.length; + if (!len) { + return true; + } + + + owner.body.setWidth(tableWidth); + + + for (i = 0; i < len; i++) { + childContext = changedColumns[i]; + colWidth = childContext.props.width; + + + + if (isContentBox && owner.columnLines) { + if (!columnLineWidth) { + columnLineWidth = ownerContext.headerContext.childItems[0].borderInfo.width; + } + colWidth -= columnLineWidth; + } + + + + + + + if (isColGroup) { + colGroup.childNodes[childContext.columnIndex].style.width = colWidth + 'px'; + } + + + + if (owner.features.length) { + owner.body.select(owner.getColumnSizerSelector(childContext.target)).setWidth(colWidth); + } + } + + return true; + }, + + finishedLayout: function() { + var me = this, + owner = me.owner; + + me.callParent(arguments); + + if (Ext.isWebKit) { + owner.el.select(owner.getBodySelector()).setStyle('table-layout', ''); + } + + + if (owner.refreshCounter && me.lockedGrid && me.lockedGrid.syncRowHeight && me.lockedGrid.needsRowHeightSync) { + me.lockedGrid.syncRowHeights(); + me.lockedGrid.needsRowHeightSync = false; + } + } +}); + + + +Ext.define('Ext.view.NodeCache', { + constructor: function(view) { + this.view = view; + this.clear(); + this.el = new Ext.dom.AbstractElement.Fly(); + }, + + + clear: function(removeDom) { + var me = this, + elements = this.elements, + i, el; + + if (removeDom) { + for (i in elements) { + el = elements[i]; + el.parentNode.removeChild(el); + } + } + me.elements = {}; + me.count = me.startIndex = 0; + me.endIndex = -1; + }, + + + fill: function(newElements, startIndex) { + var me = this, + elements = me.elements = {}, + i, + len = newElements.length; + + if (!startIndex) { + startIndex = 0; + } + for (i = 0; i < len; i++) { + elements[startIndex + i] = newElements[i]; + } + 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 (me.count) { + if (insertPoint > me.endIndex + 1 || insertPoint + nodes.length - 1 < me.startIndex) { + Ext.Error.raise('Discontiguous range would result from inserting ' + nodes.length + ' nodes at ' + insertPoint); + } + + + 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 { + me.startIndex = insertPoint; + me.endIndex = insertPoint + nodeCount - 1; + } + + + for (i = 0; i < nodeCount; i++, insertPoint++) { + elements[insertPoint] = nodes[i]; + elements[insertPoint].setAttribute('data-recordIndex', insertPoint); + } + me.count += nodeCount; + }, + + 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); + }, + + getCount : function() { + return this.count; + }, + + slice: function(start, end) { + var elements = this.elements, + result = [], + i; + + if (arguments.length < 2) { + 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; + }, + + + 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; + }, + + + 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, + 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++) { + + if (removeDom && i < end) { + Ext.removeNode(elements[i]); + } + + if (fromPos <= me.endIndex) { + el = elements[i] = elements[fromPos]; + el.setAttribute('data-recordIndex', i); + } + + else { + delete elements[i]; + } + } + me.count -= removeCount; + me.endIndex -= removeCount; + }, + + + removeElement: function(keys, removeDom) { + var me = this, + inKeys, + key, + elements = me.elements, + el, + deleteCount, + keyIndex = 0, index, + fromIndex; + + + + 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); + } + + + if (key >= me.startIndex && key <= me.endIndex) { + keys[keys.length] = key; + } + } + Ext.Array.sort(keys); + deleteCount = keys.length; + } else { + + if (keys < me.startIndex || keys > me.endIndex) { + return; + } + deleteCount = 1; + keys = [keys]; + } + + + + for (index = fromIndex = keys[0], keyIndex = 0; index <= me.endIndex; index++, fromIndex++) { + + + + + if (keyIndex < deleteCount && index === keys[keyIndex]) { + fromIndex++; + keyIndex++; + if (removeDom) { + Ext.removeNode(elements[index]); + } + } + + + 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; + }, + + + scroll: function(newRecords, direction, removeCount) { + var me = this, + elements = me.elements, + recCount = newRecords.length, + i, el, removeEnd, + newNodes, + nodeContainer = me.view.getNodeContainer(), + frag = document.createDocumentFragment(); + + + if (direction == -1) { + for (i = (me.endIndex - removeCount) + 1; i <= me.endIndex; i++) { + el = elements[i]; + delete elements[i]; + el.parentNode.removeChild(el); + } + me.endIndex -= removeCount; + + + newNodes = me.view.bufferRender(newRecords, me.startIndex -= recCount); + for (i = 0; i < recCount; i++) { + elements[me.startIndex + i] = newNodes[i]; + frag.appendChild(newNodes[i]); + } + nodeContainer.insertBefore(frag, nodeContainer.firstChild); + } + + + else { + removeEnd = me.startIndex + removeCount; + for (i = me.startIndex; i < removeEnd; i++) { + el = elements[i]; + delete elements[i]; + el.parentNode.removeChild(el); + } + me.startIndex = i; + + + newNodes = me.view.bufferRender(newRecords, me.endIndex + 1); + for (i = 0; i < recCount; i++) { + elements[me.endIndex += 1] = newNodes[i]; + frag.appendChild(newNodes[i]); + } + nodeContainer.appendChild(frag); + } + + me.count = me.endIndex - me.startIndex + 1; + } +}); + + + +Ext.define('Ext.view.Table', { + extend: Ext.view.View , + alias: 'widget.tableview', + + + + + + + + + componentLayout: 'tableview', + + baseCls: Ext.baseCSSPrefix + 'grid-view', + + unselectableCls: Ext.baseCSSPrefix + 'unselectable', + + + firstCls: Ext.baseCSSPrefix + 'grid-cell-first', + + + lastCls: Ext.baseCSSPrefix + 'grid-cell-last', + + headerRowSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-header-row', + + selectedItemCls: Ext.baseCSSPrefix + 'grid-row-selected', + beforeSelectedItemCls: Ext.baseCSSPrefix + 'grid-row-before-selected', + selectedCellCls: Ext.baseCSSPrefix + 'grid-cell-selected', + focusedItemCls: Ext.baseCSSPrefix + 'grid-row-focused', + beforeFocusedItemCls: Ext.baseCSSPrefix + 'grid-row-before-focused', + tableFocusedFirstCls: Ext.baseCSSPrefix + 'grid-table-focused-first', + tableSelectedFirstCls: Ext.baseCSSPrefix + 'grid-table-selected-first', + tableOverFirstCls: Ext.baseCSSPrefix + 'grid-table-over-first', + overItemCls: Ext.baseCSSPrefix + 'grid-row-over', + beforeOverItemCls: Ext.baseCSSPrefix + 'grid-row-before-over', + altRowCls: Ext.baseCSSPrefix + 'grid-row-alt', + dirtyCls: Ext.baseCSSPrefix + 'grid-dirty-cell', + rowClsRe: new RegExp('(?:^|\\s*)' + Ext.baseCSSPrefix + 'grid-row-(first|last|alt)(?:\\s+|$)', 'g'), + cellRe: new RegExp(Ext.baseCSSPrefix + 'grid-cell-headerId-([^\\s]+)(?:\\s|$)', ''), + positionBody: true, + + + trackOver: true, + + + getRowClass: null, + + + stripeRows: true, + + + markDirty : true, + + + + ariaRole: 'grid', + + + tpl: '{%values.view.tableTpl.applyOut(values, out)%}', + + tableTpl: [ + '{%', + 'var view=values.view,tableCls="' + Ext.baseCSSPrefix + '" + view.id + "-table ' + Ext.baseCSSPrefix + 'grid-table";', + 'values.fullWidth', + '%}', + '', + '{[view.renderColumnSizer(out)]}', + '{[view.renderTHead(values, out)]}', + '{[view.renderTFoot(values, out)]}', + '', + '{%', + 'view.renderRows(values.rows, values.viewStartIndex, out);', + '%}', + '', + '
', + { + priority: 0 + } + ], + + rowTpl: [ + '{%', + 'var dataRowCls = values.recordIndex === -1 ? "" : " ' + Ext.baseCSSPrefix + 'grid-data-row";', + '%}', + '', + '' + + '{%', + 'parent.view.renderCell(values, parent.record, parent.recordIndex, parent.rowIndex, xindex - 1, out, parent)', + '%}', + '', + '', + { + priority: 0 + } + ], + + cellTpl: [ + '', + '
{style}" {ariaCellInnerAttr}>{value}
', + '', { + priority: 0 + } + ], + + + refreshSelmodelOnRefresh: false, + + tableValues: {}, + + + + rowValues: { + itemClasses: [], + rowClasses: [] + }, + cellValues: { + classes: [ + Ext.baseCSSPrefix + 'grid-cell ' + Ext.baseCSSPrefix + 'grid-td' + ] + }, + + + renderBuffer: document.createElement('div'), + + constructor: function(config) { + + if (config.grid.isTree) { + config.baseCls = Ext.baseCSSPrefix + 'tree-view'; + } + this.callParent([config]); + }, + + initComponent: function() { + var me = this, + scroll = me.scroll; + + this.addEvents( + + 'beforecellclick', + + 'cellclick', + + 'beforecelldblclick', + + 'celldblclick', + + 'beforecellcontextmenu', + + 'cellcontextmenu', + + 'beforecellmousedown', + + 'cellmousedown', + + 'beforecellmouseup', + + 'cellmouseup', + + 'beforecellkeydown', + + 'cellkeydown' + ); + + + me.body = new Ext.dom.Element.Fly(); + me.body.id = me.id + 'gridBody'; + + + + me.autoScroll = undefined; + + + + if (!me.trackOver) { + me.overItemCls = null; + me.beforeOverItemCls = null; + } + + + if (scroll === true || scroll === 'both') { + me.autoScroll = true; + } else if (scroll === 'horizontal') { + me.overflowX = 'auto'; + } else if (scroll === 'vertical') { + me.overflowY = 'auto'; + } + me.selModel.view = me.headerCt.view = me; + + + + me.grid.view = me; + me.initFeatures(me.grid); + delete me.grid; + + me.itemSelector = me.getItemSelector(); + me.all = new Ext.view.NodeCache(me); + me.callParent(); + }, + + getVisibleColumnManager: function(){ + var owner = this.ownerCt, + lock = owner.ownerLockable; + + return lock ? lock.getVisibleColumnManager() : owner.getVisibleColumnManager(); + + }, + + beforeLayout: function() { + var me = this, + needsContextInjection = !me.firstRefreshDone && me.headerCt.layout.running; + + me.callParent(arguments); + + + + + if (needsContextInjection && me.body.dom) { + me.headerCt.layout.injectViewContext(me.headerCt.layout.ownerContext, me); + } + }, + + + moveColumn: function(fromIdx, toIdx, colsToMove) { + var me = this, + fragment = (colsToMove > 1) ? document.createDocumentFragment() : undefined, + 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, + tables; + + + + if (me.rendered && toIdx !== fromIdx) { + + + rows = me.el.query(me.getDataRowSelector()); + + if (toIdx > fromIdx && fragment) { + destinationCellIdx -= 1; + } + + for (i = 0, len = rows.length; i < len; i++) { + tr = rows[i]; + cells = tr.childNodes; + + + 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); + } + } + + if (fragment) { + for (j = 0; j < colsToMove; j++) { + fragment.appendChild(cells[fromIdx]); + } + tr.insertBefore(fragment, cells[destinationCellIdx] || null); + } else { + tr.insertBefore(cells[fromIdx], cells[destinationCellIdx] || null); + } + } + + + tables = me.el.query(me.getBodySelector()); + for (i = 0, len = tables.length; i < len; i++) { + tr = tables[i].firstChild; + if (tr.tagName.toUpperCase() === 'COLGROUP') { + if (fragment) { + 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); + } + } + } + } + }, + + + scrollToTop: Ext.emptyFn, + + + addElListener: function(eventName, fn, scope){ + this.mon(this, eventName, fn, scope, { + element: 'el' + }); + }, + + + getGridColumns: function() { + return this.ownerCt.getVisibleColumnManager().getColumns(); + }, + + + getHeaderAtIndex: function(index) { + return this.ownerCt.getVisibleColumnManager().getHeaderAtIndex(index); + }, + + + getCell: function(record, column) { + var row = this.getNode(record, true); + return Ext.fly(row).down(column.getCellSelector()); + }, + + + getFeature: function(id) { + var features = this.featuresMC; + if (features) { + return features.get(id); + } + }, + + + + findFeature: function(ftype) { + if (this.features) { + return Ext.Array.findBy(this.features, function(feature) { + if (feature.ftype === ftype) { + return true; + } + }); + } + }, + + + initFeatures: function(grid) { + var me = this, + i, + features, + feature, + len; + + me.tableTpl = Ext.XTemplate.getTpl(this, 'tableTpl'); + me.rowTpl = Ext.XTemplate.getTpl(this, 'rowTpl'); + 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]; + + + feature.view = me; + feature.grid = grid; + me.featuresMC.add(feature); + feature.init(grid); + } + }, + + renderTHead: function(values, out) { + var headers = values.view.headerFns, + len, i; + + if (headers) { + for (i = 0, len = headers.length; i < len; ++i) { + headers[i].call(this, values, out); + } + } + }, + + + + + + addHeaderFn: function(){ + var headers = this.headerFns; + if (!headers) { + headers = this.headerFns = []; + } + headers.push(fn); + }, + + renderTFoot: function(values, out){ + var footers = values.view.footerFns, + len, i; + + if (footers) { + for (i = 0, len = footers.length; i < len; ++i) { + footers[i].call(this, values, out); + } + } + }, + + addFooterFn: function(fn){ + var footers = this.footerFns; + if (!footers) { + footers = this.footerFns = []; + } + footers.push(fn); + }, + + addTableTpl: function(newTpl) { + return this.addTpl('tableTpl', newTpl); + }, + + addRowTpl: function(newTpl) { + return this.addTpl('rowTpl', newTpl); + }, + + addCellTpl: function(newTpl) { + return this.addTpl('cellTpl', newTpl); + }, + + addTpl: function(which, newTpl) { + var me = this, + tpl, + prevTpl; + + newTpl = Ext.Object.chain(newTpl); + + + + + + + if (!newTpl.isTemplate) { + newTpl.applyOut = me.tplApplyOut; + } + + + for (tpl = me[which]; newTpl.priority < tpl.priority; tpl = tpl.nextTpl) { + prevTpl = tpl; + } + + + if (prevTpl) { + prevTpl.nextTpl = newTpl; + } + + else { + me[which] = newTpl; + } + newTpl.nextTpl = tpl; + return newTpl; + }, + + tplApplyOut: function(values, out) { + if (this.before) { + if (this.before(values, out) === false) { + return; + } + } + this.nextTpl.applyOut(values, out); + if (this.after) { + this.after(values, out); + } + }, + + + 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() { + var me = this; + me.callParent(); + + if (!me.enableTextSelection) { + me.protoEl.unselectable(); + } + }, + + + onViewScroll: function(e, t) { + + if (!this.ignoreScroll) { + this.callParent(arguments); + this.fireEvent('bodyscroll', e, t); + } + }, + + + + + createRowElement: function(record, index) { + var me = this, + div = me.renderBuffer; + + me.tpl.overwrite(div, me.collectData([record], index)); + + return Ext.fly(div).down(me.getNodeContainerSelector(), true).firstChild; + }, + + + + + bufferRender: function(records, index) { + var me = this, + div = me.renderBuffer; + + me.tpl.overwrite(div, me.collectData(records, index)); + return Ext.Array.toArray(Ext.fly(div).down(me.getNodeContainerSelector(), true).childNodes); + }, + + collectData: function(records, startIndex) { + this.rowValues.view = this; + + return { + view: this, + rows: records, + viewStartIndex: startIndex, + tableStyle: 'width:' + this.headerCt.getTableWidth() + 'px' + (this.bufferedRenderer ? (';position:absolute;top:' + this.bufferedRenderer.bodyTop + 'px') : '') + }; + }, + + + + + collectNodes: function(targetEl) { + this.all.fill(this.getNodeContainer().childNodes, this.all.startIndex); + }, + + + + + + + + refreshSize: function() { + var me = this, + grid, + bodySelector = me.getBodySelector(); + + + + if (bodySelector) { + me.body.attach(me.el.child(bodySelector, true)); + } + + if (!me.hasLoadingHeight) { + grid = me.up('tablepanel'); + + + + Ext.suspendLayouts(); + + me.callParent(); + + + + if (me.dataSource.getCount()) { + grid.updateLayout(); + } + + Ext.resumeLayouts(true); + } + }, + + + getMaskTarget: function() { + var grid = this.ownerCt; + if (grid.ownerLockable) { + grid = grid.ownerLockable; + } + return grid.getMaskTarget(); + }, + + statics: { + getBoundView: function(node) { + return Ext.getCmp(node.getAttribute('data-boundView')); + } + }, + + getRecord: function(node) { + var me = this, + recordIndex; + + + if (me.store.isDestroyed) { + return; + } + + node = me.getNode(node); + if (node) { + + if (!me.hasActiveFeature()) { + recordIndex = node.getAttribute('data-recordIndex'); + if (recordIndex) { + recordIndex = parseInt(recordIndex, 10); + if (recordIndex > -1) { + + + return me.store.data.getAt(recordIndex); + } + } + } + + return me.dataSource.getByInternalId(node.getAttribute('data-recordId')); + } + }, + + indexOf: function(node) { + node = this.getNode(node, false); + if (!node && node !== 0) { + return -1; + } + return this.all.indexOf(node); + }, + + indexInStore: function(node) { + + + return this.dataSource.indexOf(this.getRecord(node)); + }, + + renderRows: function(rows, viewStartIndex, out) { + var rowValues = this.rowValues, + rowCount = rows.length, + i; + + rowValues.view = this; + rowValues.columns = this.ownerCt.getVisibleColumnManager().getColumns(); + + for (i = 0; i < rowCount; i++, viewStartIndex++) { + rowValues.itemClasses.length = rowValues.rowClasses.length = 0; + this.renderRow(rows[i], viewStartIndex, out); + } + + + rowValues.view = rowValues.columns = rowValues.record = null; + }, + + + + renderColumnSizer: function(out) { + var columns = this.getGridColumns(), + len = columns.length, i, + column; + + out.push(''); + for (i = 0; i < len; i++) { + column = columns[i]; + out.push(''); + } + out.push(''); + }, + + + renderRow: function(record, rowIdx, out) { + var me = this, + isMetadataRecord = rowIdx === -1, + selModel = me.selModel, + rowValues = me.rowValues, + itemClasses = rowValues.itemClasses, + rowClasses = rowValues.rowClasses, + cls, + rowTpl = me.rowTpl; + + + + + + + + + + + rowValues.rowAttr = {}; + + + rowValues.record = record; + rowValues.recordId = record.internalId; + + + rowValues.recordIndex = me.store.indexOf(record); + + + 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 (!isMetadataRecord) { + itemClasses[0] = Ext.baseCSSPrefix + "grid-row"; + + if (!me.ownerCt.disableSelection && selModel.isRowSelected) { + + if (selModel.isRowSelected(record)) { + itemClasses.push(me.selectedItemCls); + } + + + + if (me.rowValues.recordIndex < me.store.getTotalCount() - 1 && selModel.isRowSelected(me.rowValues.recordIndex + 1) && !me.isRowStyleFirst(rowIdx + 1)) { + rowClasses.push(me.beforeSelectedItemCls); + } + } + + 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); + } else { + return rowTpl.apply(rowValues); + } + }, + + + renderCell: function (column, record, recordIndex, rowIndex, columnIndex, out) { + var me = this, + selModel = me.selModel, + cellValues = me.cellValues, + classes = cellValues.classes, + fieldValue = record.data[column.dataIndex], + cellTpl = me.cellTpl, + fullIndex, value, clsInsertPoint; + + cellValues.record = record; + cellValues.column = column; + cellValues.recordIndex = recordIndex; + cellValues.rowIndex = rowIndex; + cellValues.columnIndex = columnIndex; + cellValues.cellIndex = columnIndex; + cellValues.align = column.align; + cellValues.tdCls = column.tdCls; + cellValues.innerCls = column.innerCls; + cellValues.style = cellValues.tdAttr = ""; + cellValues.unselectableAttr = me.enableTextSelection ? '' : 'unselectable="on"'; + + 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) { + + + record.cssWarning = true; + cellValues.tdCls += ' ' + cellValues.css; + delete cellValues.css; + } + } else { + value = fieldValue; + } + + cellValues.value = (value == null || value === '') ? column.emptyCellText : value; + + + classes[1] = column.getCellId(); + + + + clsInsertPoint = 2; + + if (column.tdCls) { + classes[clsInsertPoint++] = column.tdCls; + } + if (me.markDirty && 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 (cellValues.tdCls) { + classes[clsInsertPoint++] = cellValues.tdCls; + } + if (selModel && selModel.isCellModel && selModel.isCellSelected(me, recordIndex, column)) { + classes[clsInsertPoint++] = (me.selectedCellCls); + } + + + classes.length = clsInsertPoint; + + cellValues.tdCls = classes.join(' '); + + cellTpl.applyOut(cellValues, out); + + + cellValues.column = null; + }, + + + getNode: function(nodeInfo, dataRow) { + var fly, + result = this.callParent(arguments); + + if (result && result.tagName) { + if (dataRow) { + if (!(fly = Ext.fly(result)).is(this.dataRowSelector)) { + return fly.down(this.dataRowSelector, true); + } + } else if (dataRow === false) { + if (!(fly = Ext.fly(result)).is(this.itemSelector)) { + return fly.up(this.itemSelector, null, true); + } + } + } + return result; + }, + + getRowId: function(record){ + return this.id + '-record-' + record.internalId; + }, + + constructRowId: function(internalId){ + return this.id + '-record-' + internalId; + }, + + getNodeById: function(id, dataRow){ + id = this.constructRowId(id); + return this.retrieveNode(id, dataRow); + }, + + getNodeByRecord: function(record, dataRow) { + var id = this.getRowId(record); + return this.retrieveNode(id, dataRow); + }, + + retrieveNode: function(id, dataRow){ + var result = this.el.getById(id, true), + itemSelector = this.itemSelector, + fly; + + if (dataRow === false && result) { + if (!(fly = Ext.fly(result)).is(itemSelector)) { + return fly.up(itemSelector, null, true); + } + } + return result; + }, + + + updateIndexes: Ext.emptyFn, + + + bodySelector: 'table', + + + nodeContainerSelector: 'tbody', + + + itemSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-row', + + + dataRowSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-data-row', + + + cellSelector: 'td.' + Ext.baseCSSPrefix + 'grid-cell', + + + sizerSelector: 'col.' + Ext.baseCSSPrefix + 'grid-cell-headerId', + + innerSelector: 'div.' + Ext.baseCSSPrefix + 'grid-cell-inner', + + getNodeContainer: function() { + return this.el.down(this.nodeContainerSelector, true); + }, + + + getBodySelector: function() { + return this.bodySelector + '.' + Ext.baseCSSPrefix + this.id + '-table'; + }, + + + getNodeContainerSelector: function() { + return this.nodeContainerSelector; + }, + + + getColumnSizerSelector: function(header) { + return this.sizerSelector + '-' + header.getItemId(); + }, + + + getItemSelector: function() { + return this.itemSelector; + }, + + + getDataRowSelector: function() { + return this.dataRowSelector; + }, + + + getCellSelector: function(header) { + return header ? header.getCellSelector() : this.cellSelector; + }, + + + getCellInnerSelector: function(header) { + return this.getCellSelector(header) + ' ' + this.innerSelector; + }, + + + addRowCls: function(rowInfo, cls) { + var row = this.getNode(rowInfo, false); + if (row) { + Ext.fly(row).addCls(cls); + } + }, + + + removeRowCls: function(rowInfo, cls) { + var row = this.getNode(rowInfo, false); + if (row) { + Ext.fly(row).removeCls(cls); + } + }, + + setHighlightedItem: function(item) { + var me = this, + highlighted = me.highlightedItem; + + if (highlighted && me.el.isAncestor(highlighted) && me.isRowStyleFirst(highlighted)) { + me.toggleRowTableCls(highlighted, me.tableOverFirstCls, false); + } + + item = me.getNode(item, false); + + if (item && me.isRowStyleFirst(item)) { + me.toggleRowTableCls(item, me.tableOverFirstCls, true); + } + + me.callParent(arguments); + }, + + + onRowSelect: function(rowIdx) { + var me = this, + beforeSelectedItemCls = me.beforeSelectedItemCls; + + me.addRowCls(rowIdx, me.selectedItemCls); + if (me.isRowStyleFirst(rowIdx)) { + me.toggleRowTableCls(rowIdx, me.tableSelectedFirstCls, true); + if (rowIdx > 0) { + me.removeRowCls(rowIdx - 1, beforeSelectedItemCls); + } + } else { + me.addRowCls(rowIdx - 1, beforeSelectedItemCls); + } + }, + + + onRowDeselect: function(rowIdx) { + var me = this; + + me.removeRowCls(rowIdx, [me.selectedItemCls, me.focusedItemCls]); + if (me.isRowStyleFirst(rowIdx)) { + me.toggleRowTableCls(rowIdx, [me.tableFocusedFirstCls, me.tableSelectedFirstCls], false); + } else { + me.removeRowCls(rowIdx - 1, [me.beforeFocusedItemCls, me.beforeSelectedItemCls]); + } + }, + + onCellSelect: function(position) { + var cell = this.getCellByPosition(position); + if (cell) { + cell.addCls(this.selectedCellCls); + } + }, + + onCellDeselect: function(position) { + var cell = this.getCellByPosition(position, true); + if (cell) { + Ext.fly(cell).removeCls(this.selectedCellCls); + } + + }, + + getCellByPosition: function(position, returnDom) { + if (position) { + var row = this.getNode(position.row, true), + header = this.ownerCt.getColumnManager().getHeaderAtIndex(position.column); + + if (header && row) { + return Ext.fly(row).down(this.getCellSelector(header), returnDom); + } + } + return false; + }, + + getFocusEl: function() { + return this.focusEl; + }, + + + + onRowFocus: function(rowIdx, highlight, supressFocus) { + var me = this; + + if (highlight) { + me.addRowCls(rowIdx, me.focusedItemCls); + if (me.isRowStyleFirst(rowIdx)) { + me.toggleRowTableCls(rowIdx, me.tableFocusedFirstCls, true); + } else { + me.addRowCls(rowIdx - 1, me.beforeFocusedItemCls); + } + if (!supressFocus) { + me.focusRow(rowIdx); + } + + } else { + me.removeRowCls(rowIdx, me.focusedItemCls); + if (me.isRowStyleFirst(rowIdx)) { + me.toggleRowTableCls(rowIdx, me.tableFocusedFirstCls, false); + } else { + me.removeRowCls(rowIdx - 1, me.beforeFocusedItemCls); + } + } + + if ((Ext.isIE6 || Ext.isIE7) && !me.ownerCt.rowLines) { + me.repaintRow(rowIdx) + } + }, + + + focusRow: function(row, delay) { + var me = this, + record, + focusTask = me.getFocusTask(); + + if (delay) { + focusTask.delay(Ext.isNumber(delay) ? delay : 10, me.focusRow, me, [row, false]); + return; + } + + + focusTask.cancel(); + + + + + if (me.isVisible(true) && (row = me.getNode(row, true))) { + me.scrollRowIntoView(row); + record = me.getRecord(row); + me.selModel.setLastFocused(record); + me.doFocus(row); + me.fireEvent('rowfocus', record, row, me.indexInStore(row)); + } + }, + + scrollRowIntoView: function(row) { + row = this.getNode(row, true); + if (row) { + Ext.fly(row).scrollIntoView(this.el, false); + } + }, + + + 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; + } + + + focusTask.cancel(); + + + + + if (me.isVisible(true) && (cell = me.getCellByPosition(position))) { + me.scrollCellIntoView(cell); + + + + me.doFocus(me.getNode(position.row, true)); + me.fireEvent('cellfocus', position.record, cell, position); + } + }, + + + + doFocus: function(rowDom) { + var me = this, + saveScroll = Ext.isIE, + scrollLeft; + + if (saveScroll) { + scrollLeft = me.el.getScrollLeft(); + me.ignoreScroll = true; + } + + (me.focusEl = Ext.get(rowDom)).focus(); + + if (saveScroll) { + me.el.setScrollLeft(scrollLeft); + me.ignoreScroll = false; + } + }, + + scrollCellIntoView: function(cell) { + + + if (cell.row != null && cell.column != null) { + cell = this.getCellByPosition(cell); + } + if (cell) { + Ext.fly(cell).scrollIntoView(this.el); + } + }, + + + scrollByDelta: function(delta, dir) { + dir = dir || 'scrollTop'; + var elDom = this.el.dom; + elDom[dir] = (elDom[dir] += delta); + }, + + + isDataRow: function(row) { + return Ext.fly(row).hasCls(Ext.baseCSSPrefix + 'grid-data-row'); + }, + + syncRowHeights: function(firstRow, secondRow) { + firstRow = Ext.get(firstRow); + secondRow = Ext.get(secondRow); + firstRow.dom.style.height = secondRow.dom.style.height = ''; + var me = this, + rowTpl = me.rowTpl, + firstRowHeight = firstRow.dom.offsetHeight, + secondRowHeight = secondRow.dom.offsetHeight; + + + if (firstRowHeight !== secondRowHeight) { + + + while (rowTpl) { + if (rowTpl.syncRowHeights) { + + if (rowTpl.syncRowHeights(firstRow, secondRow) === false) { + break; + } + } + rowTpl = rowTpl.nextTpl; + } + + + firstRowHeight = firstRow.dom.offsetHeight; + secondRowHeight = secondRow.dom.offsetHeight; + if (firstRowHeight !== secondRowHeight) { + + + firstRow = firstRow.down('[data-recordId]') || firstRow; + secondRow = secondRow.down('[data-recordId]') || secondRow; + + + if (firstRow && secondRow) { + firstRow.dom.style.height = secondRow.dom.style.height = ''; + firstRowHeight = firstRow.dom.offsetHeight; + secondRowHeight = secondRow.dom.offsetHeight; + + if (firstRowHeight > secondRowHeight) { + firstRow.setHeight(firstRowHeight); + secondRow.setHeight(firstRowHeight); + } else if (secondRowHeight > firstRowHeight) { + firstRow.setHeight(secondRowHeight); + secondRow.setHeight(secondRowHeight); + } + } + } + } + }, + + onIdChanged: function(store, rec, oldId, newId, oldInternalId){ + var me = this, + rowDom; + + if (me.viewReady) { + rowDom = me.getNodeById(oldInternalId); + if (rowDom) { + rowDom.setAttribute('data-recordId', rec.internalId); + rowDom.id = me.getRowId(rec); + } + } + }, + + + onUpdate: function(store, record, operation, changedFieldNames) { + var me = this, + rowTpl = me.rowTpl, + oldRow, oldRowDom, oldDataRow, + newRowDom, + newAttrs, attLen, attName, attrIndex, + overItemCls, beforeOverItemCls, + focusedItemCls, beforeFocusedItemCls, + selectedItemCls, beforeSelectedItemCls, + columns; + + if (me.viewReady) { + + oldRowDom = me.getNodeByRecord(record, false); + + + if (oldRowDom) { + overItemCls = me.overItemCls; + beforeOverItemCls = me.beforeOverItemCls; + focusedItemCls = me.focusedItemCls; + beforeFocusedItemCls = me.beforeFocusedItemCls; + selectedItemCls = me.selectedItemCls; + beforeSelectedItemCls = me.beforeSelectedItemCls; + + oldRow = Ext.fly(oldRowDom, '_internal'); + newRowDom = me.createRowElement(record, me.dataSource.data.indexOf(record)); + if (oldRow.hasCls(overItemCls)) { + Ext.fly(newRowDom).addCls(overItemCls); + } + if (oldRow.hasCls(beforeOverItemCls)) { + Ext.fly(newRowDom).addCls(beforeOverItemCls); + } + if (oldRow.hasCls(focusedItemCls)) { + Ext.fly(newRowDom).addCls(focusedItemCls); + } + if (oldRow.hasCls(beforeFocusedItemCls)) { + Ext.fly(newRowDom).addCls(beforeFocusedItemCls); + } + if (oldRow.hasCls(selectedItemCls)) { + Ext.fly(newRowDom).addCls(selectedItemCls); + } + if (oldRow.hasCls(beforeSelectedItemCls)) { + Ext.fly(newRowDom).addCls(beforeSelectedItemCls); + } + columns = me.ownerCt.getVisibleColumnManager().getColumns(); + + + + + + if (Ext.isIE9m && 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); + } + } + } + + + + if (columns.length && (oldDataRow = me.getNode(oldRowDom, true))) { + me.updateColumns(record, oldDataRow, me.getNode(newRowDom, true), columns, changedFieldNames); + } + + + while (rowTpl) { + if (rowTpl.syncContent) { + if (rowTpl.syncContent(oldRowDom, newRowDom) === false) { + break; + } + } + rowTpl = rowTpl.nextTpl; + } + + + + me.fireEvent('itemupdate', record, me.store.indexOf(record), oldRowDom); + me.refreshSize(); + } + } + }, + + updateColumns: function(record, oldRowDom, newRowDom, columns, changedFieldNames) { + var me = this, + newAttrs, attLen, attName, attrIndex, + colCount = columns.length, + colIndex, + column, + oldCell, newCell, + row, + + + + + editingPlugin = me.editingPlugin || (me.lockingPartner && me.ownerCt.ownerLockable.view.editingPlugin), + + + isEditing = editingPlugin && editingPlugin.editing, + cellSelector = me.getCellSelector(); + + + + 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); + } + } + } + + + for (colIndex = 0; colIndex < colCount; colIndex++) { + column = columns[colIndex]; + + + + if (me.shouldUpdateCell(record, column, changedFieldNames)) { + + + + cellSelector = me.getCellSelector(column); + oldCell = Ext.DomQuery.selectNode(cellSelector, oldRowDom); + newCell = Ext.DomQuery.selectNode(cellSelector, newRowDom); + + + if (isEditing) { + Ext.fly(oldCell).syncContent(newCell); + } + + else { + + row = oldCell.parentNode; + row.insertBefore(newCell, oldCell); + row.removeChild(oldCell); + } + } + } + }, + + shouldUpdateCell: function(record, column, changedFieldNames){ + + + + + if (column.hasCustomRenderer || !changedFieldNames) { + return true; + } + + if (changedFieldNames) { + var len = changedFieldNames.length, + i, field; + + for (i = 0; i < len; ++i) { + field = changedFieldNames[i]; + if (field === column.dataIndex || field === record.idProperty) { + return true; + } + } + } + return false; + }, + + + refresh: function() { + var me = this; + + me.callParent(arguments); + me.headerCt.setSortState(); + + + + if (me.el && me.headerCt && !me.ownerCt.hideHeaders && me.headerCt.tooNarrow && !me.all.getCount()) { + me.el.createChild({ + role: 'presentation', + style:'position:absolute;height:1px;width:1px;left:' + (me.headerCt.getTableWidth() - 1) + 'px' + }); + } + + me.refreshSelection(); + }, + + refreshSelection: function() { + var me = this, + selModel = me.selModel, + selected, len, i; + + if (selModel.isRowModel) { + selected = selModel.selected.items; + len = selected.length; + for (i = 0; i < len; i++) { + me.onRowSelect(me.indexOf(me.getNode(selected[i]))); + } + } + me.selModel.onLastFocusChanged(null, me.selModel.lastFocused, true); + }, + + processItemEvent: function(record, row, rowIndex, e) { + me = this; + + + + + if (Ext.isIE && e.type === 'mouseup' && !e.within(me.el)) { + return false; + } + + + if (this.indexInStore(row) !== -1) { + var me, + cell = e.getTarget(me.getCellSelector(), row), + cellIndex, + map = me.statics().EventMap, + selModel = me.getSelectionModel(), + type = e.type, + features = me.features, + len = features.length, + i, result, feature, header; + + if (type == 'keydown' && !cell && selModel.getCurrentPosition) { + + cell = me.getCellByPosition(selModel.getCurrentPosition(), true); + } + + + if (cell) { + if (!cell.parentNode) { + + + return false; + } + + header = me.getHeaderByCell(cell); + + + + cellIndex = me.ownerCt.getColumnManager().getHeaderIndex(header); + } else { + cellIndex = -1; + } + + result = me.fireEvent('uievent', type, me, cell, rowIndex, cellIndex, e, record, row); + + + + + if ((result === false || me.callParent(arguments) === false)) { + if (selModel.onVetoUIEvent) { + selModel.onVetoUIEvent(type, me, cell, rowIndex, cellIndex, e, record, row); + } + return false; + } + + for (i = 0; i < len; ++i) { + feature = features[i]; + + + if (feature.wrapsItem) { + if (feature.vetoEvent(record, row, rowIndex, e) === false) { + + + me.processSpecialEvent(e); + return false; + } + } + } + + + if (type == 'mouseover' || type == 'mouseout') { + return true; + } + + if(!cell) { + + + return true; + } + + return !( + + (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) + ); + } else { + + this.processSpecialEvent(e); + 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; + } + + for (i = 0; i < ln; i++) { + feature = features[i]; + if (feature.hasFeatureEvent) { + featureTarget = e.getTarget(feature.eventSelector, me.getTargetEl()); + if (featureTarget) { + prefix = feature.eventPrefix; + + + beforeArgs = feature.getFireEventArgs('before' + prefix + type, me, featureTarget, e); + args = feature.getFireEventArgs(prefix + type, me, featureTarget, e); + + if ( + + (me.fireEvent.apply(me, beforeArgs) === false) || + + (panel.fireEvent.apply(panel, beforeArgs) === false) || + + (me.fireEvent.apply(me, args) === false) || + + (panel.fireEvent.apply(panel, args) === false) + ) { + return false; + } + } + } + } + return true; + }, + + onCellMouseDown: Ext.emptyFn, + onCellMouseUp: Ext.emptyFn, + onCellClick: Ext.emptyFn, + onCellDblClick: Ext.emptyFn, + onCellContextMenu: Ext.emptyFn, + onCellKeyDown: Ext.emptyFn, + onBeforeCellMouseDown: Ext.emptyFn, + onBeforeCellMouseUp: Ext.emptyFn, + onBeforeCellClick: Ext.emptyFn, + onBeforeCellDblClick: Ext.emptyFn, + onBeforeCellContextMenu: Ext.emptyFn, + onBeforeCellKeyDown: Ext.emptyFn, + + + expandToFit: function(header) { + this.autoSizeColumn(header); + }, + + + 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)); + } + }, + + + 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'); + } + } + + + columnSizer.setWidth(1); + + + + + + header.titleEl.setStyle('text-overflow', 'clip'); + + + maxWidth = header.textEl.dom.offsetWidth + header.titleEl.getPadding('lr'); + + + header.titleEl.setStyle('text-overflow', ''); + + for (; i < ln; i++) { + maxWidth = max(maxWidth, cells[i].scrollWidth); + } + + + maxWidth += widthAdjust; + + + maxWidth = max(maxWidth, 40); + + + 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) { + var match = cell.className.match(this.cellRe); + if (match && match[1]) { + return this.ownerCt.getVisibleColumnManager().getHeaderById(match[1]); + } + } + return false; + }, + + + walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) { + + + + if (!pos) { + return false; + } + + var me = this, + row = pos.row, + column = pos.column, + rowCount = me.dataSource.getCount(), + allCols = me.ownerCt.getColumnManager(), + visible = me.ownerCt.getVisibleColumnManager(), + firstIndex = allCols.getHeaderIndex(visible.getFirst()), + lastIndex = allCols.getHeaderIndex(visible.getLast()), + newRow = row, + newColumn = column, + activeHeader = allCols.getHeaderAtIndex(column); + + + if (!activeHeader || activeHeader.hidden || !rowCount) { + return false; + } + + e = e || {}; + direction = direction.toLowerCase(); + switch (direction) { + case 'right': + + if (column === lastIndex) { + + if (preventWrap || row === rowCount - 1) { + return false; + } + if (!e.ctrlKey) { + + newRow = me.walkRows(row, 1); + if (newRow !== row) { + newColumn = firstIndex; + } + } + + } else { + if (!e.ctrlKey) { + newColumn = allCols.getHeaderIndex(visible.getNextSibling(activeHeader)); + } else { + newColumn = lastIndex; + } + } + break; + + case 'left': + + if (column === firstIndex) { + + if (preventWrap || row === 0) { + return false; + } + if (!e.ctrlKey) { + + newRow = me.walkRows(row, -1); + if (newRow !== row) { + newColumn = lastIndex; + } + } + + } else { + if (!e.ctrlKey) { + newColumn = allCols.getHeaderIndex(visible.getPreviousSibling(activeHeader)); + } else { + newColumn = firstIndex; + } + } + break; + + case 'up': + + if (row === 0) { + return false; + + } else { + if (!e.ctrlKey) { + newRow = me.walkRows(row, -1); + } else { + + newRow = me.walkRows(-1, 1); + } + } + break; + + case 'down': + + if (row === rowCount - 1) { + return false; + + } else { + if (!e.ctrlKey) { + newRow = me.walkRows(row, 1); + } else { + + newRow = me.walkRows(rowCount, -1); + } + } + break; + } + + if (verifierFn && verifierFn.call(scope || me, {row: newRow, column: newColumn}) !== true) { + return false; + } + + newColumn = allCols.getHeaderAtIndex(newColumn); + return new Ext.grid.CellContext(me).setPosition(newRow, newColumn); + }, + + + walkRows: function(startRow, distance) { + + + var me = this, + store = me.dataSource, + moved = 0, + lastValid = startRow, + node, + limit = (distance < 0) ? 0 : (store.buffered ? store.getTotalCount() : 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.getNode(result, true))) && node.isVisible(true)) { + moved += increment; + lastValid = result; + } + } while (moved !== distance); + return result; + }, + + + walkRecs: function(startRec, distance) { + + + var me = this, + store = me.dataSource, + moved = 0, + lastValid = startRec, + node, + limit = (distance < 0) ? 0 : (store.buffered ? 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, true))) && node.isVisible(true)) { + moved += increment; + lastValid = rec; + } + } while (moved !== distance); + return lastValid; + }, + + getFirstVisibleRowIndex: function() { + var me = this, + count = (me.dataSource.buffered ? me.dataSource.getTotalCount() : me.dataSource.getCount()), + result = me.indexOf(me.all.first()) - 1; + + do { + result += 1; + if (result === count) { + return; + } + } while (!Ext.fly(me.getNode(result, true)).isVisible(true)); + return result; + }, + + getLastVisibleRowIndex: function() { + var me = this, + result = me.indexOf(me.all.last()); + + do { + result -= 1; + if (result === -1) { + return; + } + } while (!Ext.fly(me.getNode(result, true)).isVisible(true)); + return result; + }, + + getHeaderCt: function() { + return this.headerCt; + }, + + getPosition: function(record, header) { + return new Ext.grid.CellContext(this).setPosition(record, header); + }, + + beforeDestroy: function() { + var me = this; + + if (me.rendered) { + me.el.removeAllListeners(); + } + me.callParent(arguments); + }, + + 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.featuresMC = null; + this.callParent(arguments); + }, + + + + + onReplace: function(store, startIndex, oldRecords, newRecords) { + var me = this, + selModel = me.selModel, + nextIndex, isNextRowSelected, isNextRowFocused; + + me.callParent(arguments); + me.doStripeRows(startIndex); + + if (me.rendered && selModel.isRowModel && !newRecords[0].isCollapsedPlaceholder) { + nextIndex = startIndex + newRecords.length; + isNextRowSelected = selModel.isRowSelected(nextIndex); + isNextRowFocused = me.indexOf(selModel.lastFocused) === (nextIndex); + if (isNextRowSelected || isNextRowFocused) { + + + me.onRowDeselect(startIndex); + } + if (isNextRowSelected) { + me.onRowSelect(nextIndex); + } + if (selModel.isRowSelected(startIndex)) { + me.onRowSelect(startIndex); + } + } + me.selModel.onLastFocusChanged(null, me.selModel.lastFocused, true); + }, + + + onAdd: function(ds, records, index) { + var me = this, + selModel = me.selModel, + nextIndex, isNextRowSelected, isNextRowFocused; + + me.callParent(arguments); + me.doStripeRows(index); + + if (me.rendered && selModel.isRowModel && !records[0].isCollapsedPlaceholder) { + nextIndex = index + records.length; + isNextRowSelected = selModel.isRowSelected(nextIndex); + isNextRowFocused = me.indexOf(selModel.lastFocused) === (nextIndex); + if (isNextRowSelected || isNextRowFocused) { + + + me.onRowDeselect(index); + } + if (isNextRowSelected) { + me.onRowSelect(nextIndex); + } + if (selModel.isRowSelected(index)) { + me.onRowSelect(index); + } + } + me.selModel.onLastFocusChanged(null, me.selModel.lastFocused, true); + }, + + + onRemove: function(ds, records, indexes) { + var me = this, + index, + selModel = me.selModel, + len = indexes.length, + i = 0, + delta = 0, + record; + + + + me.callParent(arguments); + me.doStripeRows(indexes[0]); + + if (me.rendered && selModel.isRowModel && !records[0].isCollapsedPlaceholder) { + for (; i < len; i++, delta++) { + index = indexes[i] - delta; + record = me.store.getAt(index); + me.onRowDeselect(index); + if (selModel.isRowSelected(record) && me.getNode(record)) { + me.onRowSelect(index); + } + } + } + selModel.onLastFocusChanged(null, selModel.lastFocused, true); + }, + + + doStripeRows: function(startRow, endRow) { + var me = this, + rows, + rowsLn, + i, + row; + + + if (me.rendered && me.stripeRows) { + rows = me.getNodes(startRow, endRow); + + for (i = 0, rowsLn = rows.length; i < rowsLn; i++) { + row = rows[i]; + + row.className = row.className.replace(me.rowClsRe, ' '); + startRow++; + + if (startRow % 2 === 0) { + row.className += (' ' + me.altRowCls); + } + } + } + }, + + repaintRow: function(rowIdx) { + var node = this.getNode(rowIdx), + tds, + i; + + if (node) { + tds = node.childNodes; + i = tds.length; + while (i--) { + tds[i].className = tds[i].className; + } + } + }, + + + + + + getRowStyleTableEl: function(item ) { + var me = this; + + if (!item.tagName) { + item = this.getNode(item); + } + return (me.hasActiveFeature() ? Ext.fly(item) : this.el).down('table.' + Ext.baseCSSPrefix + 'grid-table'); + }, + + + + + + toggleRowTableCls: function(item , cls, enabled) { + var me = this, + table, root; + + if (!item.tagName) { + item = this.getNode(item); + } + + root = me.isGrouping ? Ext.fly(item) : this.el; + + if (root) { + table = root.down('table.' + Ext.baseCSSPrefix + 'grid-table'); + } + + if (table) { + table[enabled ? 'addCls' : 'removeCls'](cls); + } + }, + + + + + + isRowStyleFirst: function(item ) { + var me = this, + index; + + + if (item === -1) { + return false; + } + + if (!item.tagName) { + index = item; + item = this.getNode(item); + } else { + index = me.indexOf(item); + } + + return (!index || me.hasActiveFeature() && item && Ext.fly(item).hasCls(Ext.baseCSSPrefix + 'grid-group-row')); + }, + + hasActiveFeature: function(){ + return (this.isGrouping && this.store.isGrouped()) || this.isRowWrapped; + }, + + getCellPaddingAfter: function(cell) { + return Ext.fly(cell).getPadding('r'); + }, + + hasVerticalScroll: function(){ + var me = this, + first; + + if (me.ownerCt.isLocked || !me.scrollFlags.y) { + return false; + } + + first = me.el.down('table'); + if (!first) { + return false; + } + + return this.getHeight() < first.getHeight(); + } + +}); + + + +Ext.define('Ext.data.association.Association', { + alternateClassName: 'Ext.data.Association', + + + + + + + + primaryKey: 'id', + + + + + + associationKeyFunction : null, + + defaultReaderType: 'json', + + isAssociation: true, + + initialConfig: null, + + statics: { + AUTO_ID: 1000, + + create: function(association){ + if (Ext.isString(association)) { + association = { + type: association + }; + } + + switch (association.type) { + case 'belongsTo': + return new Ext.data.association.BelongsTo(association); + case 'hasMany': + return new Ext.data.association.HasMany(association); + case 'hasOne': + return new Ext.data.association.HasOne(association); + + + + default: + Ext.Error.raise('Unknown Association type: "' + association.type + '"'); + } + return association; + } + }, + + + constructor: function(config) { + Ext.apply(this, config); + + var me = this, + types = Ext.ModelManager.types, + ownerName = config.ownerModel, + associatedName = config.associatedModel, + ownerModel = types[ownerName], + associatedModel = types[associatedName], + associationKey = config.associationKey, + keyReIdx; + + if (associationKey) { + keyReIdx = String(associationKey).search(/[\[\.]/); + + if (keyReIdx >= 0) { + me.associationKeyFunction = Ext.functionFactory('obj', 'return obj' + (keyReIdx > 0 ? '.' : '') + associationKey); + } + } + + me.initialConfig = config; + + if (ownerModel === undefined) { + Ext.Error.raise("The configured ownerModel was not valid (you tried " + ownerName + ")"); + } + if (associatedModel === undefined) { + Ext.Error.raise("The configured associatedModel was not valid (you tried " + associatedName + ")"); + } + + me.ownerModel = ownerModel; + me.associatedModel = associatedModel; + + + + + + Ext.applyIf(me, { + ownerName : ownerName, + associatedName: associatedName + }); + + me.associationId = 'association' + (++me.statics().AUTO_ID); + }, + + + getReader: function(){ + var me = this, + reader = me.reader, + model = me.associatedModel; + + if (reader) { + if (Ext.isString(reader)) { + reader = { + type: reader + }; + } + if (reader.isReader) { + reader.setModel(model); + } else { + Ext.applyIf(reader, { + model: model, + type : me.defaultReaderType + }); + } + me.reader = Ext.createByAlias('reader.' + reader.type, reader); + } + return me.reader || null; + } +}); + + + +Ext.define('Ext.ModelManager', { + extend: Ext.AbstractManager , + alternateClassName: 'Ext.ModelMgr', + + + singleton: true, + + typeName: 'mtype', + + + associationStack: [], + + + registerType: function(name, config) { + var proto = config.prototype, + model; + if (proto && proto.isModel) { + + model = config; + } else { + + if (!config.extend) { + config.extend = 'Ext.data.Model'; + } + model = Ext.define(name, config); + } + this.types[name] = model; + return model; + }, + + + unregisterType: function(name) { + delete this.types[name]; + }, + + + onModelDefined: function(model) { + var stack = this.associationStack, + length = stack.length, + create = [], + association, i, created; + + for (i = 0; i < length; i++) { + association = stack[i]; + + if (association.associatedModel == model.modelName) { + create.push(association); + } + } + + for (i = 0, length = create.length; i < length; i++) { + created = create[i]; + this.types[created.ownerModel].prototype.associations.add(Ext.data.association.Association.create(created)); + Ext.Array.remove(stack, created); + } + }, + + + registerDeferredAssociation: function(association){ + this.associationStack.push(association); + }, + + + getModel: function(id) { + var model = id; + if (typeof model == 'string') { + model = this.types[model]; + } + return model; + }, + + + create: function(config, name, id) { + var Con = typeof name == 'function' ? name : this.types[name || config.name]; + + return new Con(config, id); + } +}, function() { + + + Ext.regModel = function() { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn('Ext.regModel has been deprecated. Models can now be created by extending Ext.data.Model: Ext.define("MyModel", {extend: "Ext.data.Model", fields: []});.'); + } + return this.ModelManager.registerType.apply(this.ModelManager, arguments); + }; +}); + + + +Ext.define('Ext.data.IdGenerator', { + + + isGenerator: true, + + + constructor: function(config) { + var me = this; + + Ext.apply(me, config); + + if (me.id) { + Ext.data.IdGenerator.all[me.id] = me; + } + }, + + + + getRecId: function (rec) { + return rec.modelName + '-' + rec.internalId; + }, + + + + statics: { + + all: {}, + + + get: function (config) { + var generator, + id, + type; + + if (typeof config == 'string') { + id = type = config; + config = null; + } else if (config.isGenerator) { + return config; + } else { + id = config.id || config.type; + type = config.type; + } + + generator = this.all[id]; + if (!generator) { + generator = Ext.create('idgen.' + type, config); + } + + return generator; + } + } +}); + + + +Ext.define('Ext.data.SortTypes', { + + singleton: true, + + + none : Ext.identityFn, + + + stripTagsRE : /<\/?[^>]+>/gi, + + + asText : function(s) { + return String(s).replace(this.stripTagsRE, ""); + }, + + + asUCText : function(s) { + return String(s).toUpperCase().replace(this.stripTagsRE, ""); + }, + + + asUCString : function(s) { + return String(s).toUpperCase(); + }, + + + asDate : function(s) { + if(!s){ + return 0; + } + if(Ext.isDate(s)){ + return s.getTime(); + } + return Date.parse(String(s)); + }, + + + asFloat : function(s) { + var val = parseFloat(String(s).replace(/,/g, "")); + return isNaN(val) ? 0 : val; + }, + + + asInt : function(s) { + var val = parseInt(String(s).replace(/,/g, ""), 10); + return isNaN(val) ? 0 : val; + } +}); + + + +Ext.define('Ext.data.Types', { + singleton: true + +}, function(Types) { + var SortTypes = Ext.data.SortTypes; + + Ext.apply(Types, { + + stripRe: /[\$,%]/g, + + + AUTO: { + sortType: SortTypes.none, + type: 'auto' + }, + + + STRING: { + convert: function(v) { + var defaultValue = this.useNull ? null : ''; + return (v === undefined || v === null) ? defaultValue : String(v); + }, + sortType: SortTypes.asUCString, + type: 'string' + }, + + + INT: { + convert: function(v) { + + + + if (typeof v == 'number') { + return parseInt(v); + } + return v !== undefined && v !== null && v !== '' ? + parseInt(String(v).replace(Types.stripRe, ''), 10) : (this.useNull ? null : 0); + }, + sortType: SortTypes.none, + type: 'int' + }, + + + FLOAT: { + convert: function(v) { + if (typeof v === 'number') { + return v; + } + return v !== undefined && v !== null && v !== '' ? + parseFloat(String(v).replace(Types.stripRe, ''), 10) : (this.useNull ? null : 0); + }, + sortType: SortTypes.none, + type: 'float' + }, + + + BOOL: { + convert: function(v) { + if (typeof v === 'boolean') { + return v; + } + if (this.useNull && (v === undefined || v === null || v === '')) { + return null; + } + return v === 'true' || v == 1; + }, + sortType: SortTypes.none, + type: 'bool' + }, + + + DATE: { + convert: function(v) { + var df = this.dateReadFormat || this.dateFormat, + parsed; + + if (!v) { + return null; + } + + if (v instanceof Date) { + return v; + } + if (df) { + return Ext.Date.parse(v, df); + } + + parsed = Date.parse(v); + return parsed ? new Date(parsed) : null; + }, + sortType: SortTypes.asDate, + type: 'date' + } + }); + + + Types.BOOLEAN = Types.BOOL; + + + Types.INTEGER = Types.INT; + + + Types.NUMBER = Types.FLOAT; +}); + + + +Ext.define('Ext.data.Field', { + + alias: 'data.field', + + isField: true, + + constructor : function(config) { + var me = this, + types = Ext.data.Types, + st; + + if (Ext.isString(config)) { + config = {name: config}; + } + Ext.apply(me, config); + + st = me.sortType; + + if (me.type) { + if (Ext.isString(me.type)) { + me.type = types[me.type.toUpperCase()] || types.AUTO; + } + } else { + me.type = types.AUTO; + } + + + if (Ext.isString(st)) { + me.sortType = Ext.data.SortTypes[st]; + } else if(Ext.isEmpty(st)) { + me.sortType = me.type.sortType; + } + + + if (!config.hasOwnProperty('convert')) { + me.convert = me.type.convert; + } else if (!me.convert && me.type.convert && !config.hasOwnProperty('defaultValue')) { + + + me.defaultValue = me.type.convert(me.defaultValue); + } + + if (config.convert) { + me.hasCustomConvert = true; + } + }, + + + + + + + + + + + dateFormat: null, + + + dateReadFormat: null, + + + dateWriteFormat: null, + + + useNull: false, + + + defaultValue: "", + + + mapping: null, + + + sortType : null, + + + sortDir : "ASC", + + + allowBlank : true, + + + persist: true +}); + + + +Ext.define('Ext.data.Errors', { + extend: Ext.util.MixedCollection , + + + isValid: function() { + return this.length === 0; + }, + + + getByField: function(fieldName) { + var errors = [], + error, i; + + for (i = 0; i < this.length; i++) { + error = this.items[i]; + + if (error.field == fieldName) { + errors.push(error); + } + } + + return errors; + } +}); + + + +Ext.define('Ext.data.validations', { + singleton: true, + + + presenceMessage: 'must be present', + + + lengthMessage: 'is the wrong length', + + + formatMessage: 'is the wrong format', + + + inclusionMessage: 'is not included in the list of acceptable values', + + + exclusionMessage: 'is not an acceptable value', + + + emailMessage: 'is not a valid email address', + + + emailRe: /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/, + + + presence: function(config, value) { + + if (arguments.length === 1) { + value = config; + } + + + return !!value || value === 0 || value === false; + }, + + + length: function(config, value) { + if (value === undefined || value === null) { + return false; + } + + var length = value.length, + min = config.min, + max = config.max; + + if ((min && length < min) || (max && length > max)) { + return false; + } else { + return true; + } + }, + + + email: function(config, email) { + return Ext.data.validations.emailRe.test(email); + }, + + + format: function(config, value) { + return !!(config.matcher && config.matcher.test(value)); + }, + + + inclusion: function(config, value) { + return config.list && Ext.Array.indexOf(config.list,value) != -1; + }, + + + exclusion: function(config, value) { + return config.list && Ext.Array.indexOf(config.list,value) == -1; + } +}); + + + +Ext.define('Ext.data.Model', { + alternateClassName: 'Ext.data.Record', + + mixins: { + observable: Ext.util.Observable + }, + + + + + + + + + + + + compareConvertFields: function(f1, f2) { + var f1SpecialConvert = f1.convert && f1.type && f1.convert !== f1.type.convert, + f2SpecialConvert = f2.convert && f2.type && f2.convert !== f2.type.convert; + + if (f1SpecialConvert && !f2SpecialConvert) { + return 1; + } + + if (!f1SpecialConvert && f2SpecialConvert) { + return -1; + } + return 0; + }, + + itemNameFn: function(item) { + return item.name; + }, + + onClassExtended: function(cls, data, hooks) { + var onBeforeClassCreated = hooks.onBeforeCreated; + + hooks.onBeforeCreated = function(cls, data) { + var me = this, + name = Ext.getClassName(cls), + prototype = cls.prototype, + superCls = cls.prototype.superclass, + + validations = data.validations || [], + fields = data.fields || [], + field, + associationsConfigs = data.associations || [], + addAssociations = function(items, type) { + var i = 0, + len, + item; + + if (items) { + items = Ext.Array.from(items); + + for (len = items.length; i < len; ++i) { + item = items[i]; + + if (!Ext.isObject(item)) { + item = {model: item}; + } + + item.type = type; + associationsConfigs.push(item); + } + } + }, + idgen = data.idgen, + + fieldsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn), + + associationsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn), + + superValidations = superCls.validations, + superFields = superCls.fields, + superAssociations = superCls.associations, + + associationConfig, i, ln, + dependencies = [], + + + idProperty = 'idProperty' in data ? data.idProperty : prototype.idProperty, + + + idField = idProperty ? (idProperty.isField ? idProperty : new Ext.data.Field(idProperty)) : null, + + + idFieldDefined = false, + + + onFieldAddReplace = function(arg0, arg1, arg2) { + var newField, + pos; + + if (fieldsMixedCollection.events.add.firing) { + + pos = arg0; + newField = arg1; + } else { + + newField = arg2; + pos = arg1.originalIndex; + } + + + + newField.originalIndex = pos; + + + + + if (idField && ((newField.mapping && (newField.mapping === idField.mapping)) || (newField.name === idField.name))) { + prototype.idField = newField; + idFieldDefined = true; + newField.defaultValue = undefined; + } + }, + + + clsProxy = data.proxy, + + + fieldConvertSortFn = function() { + fieldsMixedCollection.sortBy(prototype.compareConvertFields); + }; + + + cls.modelName = name; + prototype.modelName = name; + + + if (superValidations) { + validations = superValidations.concat(validations); + } + + data.validations = validations; + + + if (superFields) { + fields = superFields.items.concat(fields); + } + + fieldsMixedCollection.on({ + add: onFieldAddReplace, + replace: onFieldAddReplace + }); + + for (i = 0, ln = fields.length; i < ln; ++i) { + field = fields[i]; + fieldsMixedCollection.add(field.isField ? field : new Ext.data.Field(field)); + } + + + + + if (idField && !idFieldDefined) { + prototype.idField = idField; + idField.defaultValue = undefined; + fieldsMixedCollection.add(idField); + } + + + fieldConvertSortFn(); + fieldsMixedCollection.on({ + add: fieldConvertSortFn, + replace: fieldConvertSortFn + }); + + data.fields = fieldsMixedCollection; + + if (idgen) { + data.idgen = Ext.data.IdGenerator.get(idgen); + } + + + + addAssociations(data.belongsTo, 'belongsTo'); + delete data.belongsTo; + addAssociations(data.hasMany, 'hasMany'); + delete data.hasMany; + addAssociations(data.hasOne, 'hasOne'); + delete data.hasOne; + + if (superAssociations) { + associationsConfigs = superAssociations.items.concat(associationsConfigs); + } + + Ext.Array.forEach(Ext.ModelManager.associationStack, function(item) { + var checkItem = function (item, Cls) { + Cls = Cls; + if (item.ownerModel === Cls.modelName) { + return true; + } else if (Cls.superclass) { + return checkItem(item, Cls.superclass); + } + return false; + }, newItem; + + if (checkItem(item, superCls)) { + newItem = Ext.clone(item); + newItem.ownerModel = this.modelName; + associationsConfigs.push(newItem); + }; + }, this); + + for (i = 0, ln = associationsConfigs.length; i < ln; ++i) { + dependencies.push('association.' + associationsConfigs[i].type.toLowerCase()); + } + + + if (clsProxy) { + if (!clsProxy.isProxy) { + dependencies.push('proxy.' + (clsProxy.type || clsProxy)); + } + } + + + else if (!cls.prototype.proxy) { + cls.prototype.proxy = cls.prototype.defaultProxyType; + dependencies.push('proxy.' + cls.prototype.defaultProxyType); + } + + Ext.require(dependencies, function() { + + + if (name) { + Ext.ModelManager.registerType(name, cls); + + for (i = 0, ln = associationsConfigs.length; i < ln; ++i) { + associationConfig = associationsConfigs[i]; + if (associationConfig.isAssociation) { + associationConfig = Ext.applyIf({ + ownerModel: name, + associatedModel: associationConfig.model + }, associationConfig.initialConfig); + } else { + Ext.apply(associationConfig, { + ownerModel: name, + associatedModel: associationConfig.model + }); + } + + if (Ext.ModelManager.getModel(associationConfig.model) === undefined) { + Ext.ModelManager.registerDeferredAssociation(associationConfig); + } else { + associationsMixedCollection.add(Ext.data.association.Association.create(associationConfig)); + } + } + } + data.associations = associationsMixedCollection; + + + + + + + onBeforeClassCreated.call(me, cls, data, hooks); + + + if (clsProxy && clsProxy.isProxy) { + cls.setProxy(clsProxy); + } + + + if (name) { + Ext.ModelManager.onModelDefined(cls); + } + }); + }; + }, + + inheritableStatics: { + + setProxy: function(proxy) { + + if (!proxy.isProxy) { + if (typeof proxy == "string") { + proxy = { + type: proxy + }; + } + proxy = Ext.createByAlias("proxy." + proxy.type, proxy); + } + proxy.setModel(this); + this.proxy = this.prototype.proxy = proxy; + + return proxy; + }, + + + getProxy: function() { + + var proxy = this.proxy; + + + if (!proxy) { + proxy = this.prototype.proxy; + + + if (proxy.isProxy) { + proxy = proxy.clone() + } + + return this.setProxy(proxy); + } + + return proxy; + }, + + + setFields: function(fields, idProperty, clientIdProperty) { + var me = this, + newField, + idField, + idFieldDefined = false, + proto = me.prototype, + prototypeFields = proto.fields, + superFields = proto.superclass.fields, + len, + i; + + if (idProperty) { + proto.idProperty = idProperty; + idField = idProperty.isField ? idProperty : new Ext.data.Field(idProperty); + + } + if (clientIdProperty) { + proto.clientIdProperty = clientIdProperty; + } + + if (prototypeFields) { + prototypeFields.clear(); + } + else { + prototypeFields = me.prototype.fields = new Ext.util.MixedCollection(false, function(field) { + return field.name; + }); + } + + + if (superFields) { + fields = superFields.items.concat(fields); + } + + for (i = 0, len = fields.length; i < len; i++) { + newField = new Ext.data.Field(fields[i]); + + + + + if (idField && ((newField.mapping && (newField.mapping === idField.mapping)) || (newField.name === idField.name))) { + idFieldDefined = true; + newField.defaultValue = undefined; + } + prototypeFields.add(newField); + } + + + + + if (idField && !idFieldDefined) { + idField.defaultValue = undefined; + prototypeFields.add(idField); + } + + me.fields = prototypeFields; + + return prototypeFields; + }, + + + getFields: function() { + return this.prototype.fields.items; + }, + + + load: function(id, config) { + config = Ext.apply({}, config); + config = Ext.applyIf(config, { + action: 'read', + id : id + }); + + var operation = new Ext.data.Operation(config), + scope = config.scope || this, + callback; + + callback = function(operation) { + var record = null, + success = operation.wasSuccessful(); + + if (success) { + record = operation.getRecords()[0]; + + if (!record.hasId()) { + record.setId(id); + } + Ext.callback(config.success, scope, [record, operation]); + } else { + Ext.callback(config.failure, scope, [record, operation]); + } + Ext.callback(config.callback, scope, [record, operation, success]); + }; + + this.getProxy().read(operation, callback, this); + } + }, + + statics: { + + PREFIX : 'ext-record', + + AUTO_ID: 1, + + EDIT : 'edit', + + REJECT : 'reject', + + COMMIT : 'commit', + + + id: function(rec) { + var id = [this.PREFIX, '-', this.AUTO_ID++].join(''); + rec.phantom = true; + rec.internalId = id; + return id; + } + }, + + + idgen: { + isGenerator: true, + type: 'default', + + generate: function () { + return null; + }, + getRecId: function (rec) { + return rec.modelName + '-' + rec.internalId; + } + }, + + + editing : false, + + + dirty : false, + + + persistenceProperty: 'data', + + evented: false, + + + isModel: true, + + + phantom : false, + + + idProperty: 'id', + + + clientIdProperty: null, + + + defaultProxyType: 'ajax', + + + emptyData: [], + + + + + + + + + + + + + + + + + constructor: function(data, id, raw, convertedData) { + + + + + + + + + var me = this, + passedId = (id || id === 0), + hasId, + fields, + length, + field, + name, + value, + newId, + persistenceProperty, + idProperty = me.idProperty, + idField = me.idField, + i; + + + me.raw = raw || data; + + + me.modified = {}; + + + if (me.persistenceProperty !== 'data') { + Ext.log.warn(this.$className, 'The persistenceProperty will be deprecated, all data will be stored in the underlying data property.'); + } + persistenceProperty = me[me.persistenceProperty] = convertedData || {}; + + + me.data = me[me.persistenceProperty]; + + me.mixins.observable.constructor.call(me); + + if (!convertedData) { + + if (data) { + + if (!passedId && idProperty) { + id = data[idProperty]; + hasId = (id || id === 0); + } + } + + else { + data = me.emptyData; + } + + + fields = me.fields.items; + length = fields.length; + i = 0; + + if (Ext.isArray(data)) { + for (; i < length; i++) { + field = fields[i]; + name = field.name; + + + + value = data[field.originalIndex]; + + if (value === undefined) { + value = field.defaultValue; + } + + + if (field.convert) { + value = field.convert(value, me); + } + + if (value !== undefined) { + persistenceProperty[name] = value; + } + } + + } else { + for (; i < length; i++) { + field = fields[i]; + name = field.name; + value = data[name]; + if (value === undefined) { + value = field.defaultValue; + } + if (field.convert) { + value = field.convert(value, me); + } + + if (value !== undefined) { + persistenceProperty[name] = value; + } + } + } + } + + + me.stores = []; + + + + if (passedId) { + hasId = true; + persistenceProperty[idProperty] = idField && idField.convert ? idField.convert(id, me) : id; + } + + + else if (!hasId) { + + newId = me.idgen.generate(); + if (newId != null) { + me.preventInternalUpdate = true; + me.setId(newId); + delete me.preventInternalUpdate; + } + } + + + me.internalId = hasId ? id : Ext.data.Model.id(me); + + + if (typeof me.init == 'function') { + me.init(); + } + + + me.id = me.idgen.getRecId(me); + }, + + + get: function(field) { + return this[this.persistenceProperty][field]; + }, + + + + + _singleProp: {}, + + + set: function (fieldName, newValue) { + var me = this, + data = me[me.persistenceProperty], + fields = me.fields, + modified = me.modified, + single = (typeof fieldName == 'string'), + currentValue, field, idChanged, key, modifiedFieldNames, name, oldId, + newId, value, values; + + if (single) { + values = me._singleProp; + values[fieldName] = newValue; + } else { + values = fieldName; + } + + for (name in values) { + if (values.hasOwnProperty(name)) { + value = values[name]; + + if (fields && (field = fields.get(name)) && field.convert) { + value = field.convert(value, me); + } + + currentValue = data[name]; + if (me.isEqual(currentValue, value)) { + continue; + } + + data[name] = value; + (modifiedFieldNames || (modifiedFieldNames = [])).push(name); + + if (field && field.persist) { + if (modified.hasOwnProperty(name)) { + if (me.isEqual(modified[name], value)) { + + + delete modified[name]; + + + + + me.dirty = false; + for (key in modified) { + if (modified.hasOwnProperty(key)){ + me.dirty = true; + break; + } + } + } + } else { + me.dirty = true; + modified[name] = currentValue; + } + } + + if (name == me.idProperty) { + idChanged = true; + oldId = currentValue; + newId = value; + } + } + } + + if (single) { + + + delete values[fieldName]; + } + + if (idChanged) { + me.changeId(oldId, newId); + } + + if (!me.editing && modifiedFieldNames) { + me.afterEdit(modifiedFieldNames); + } + + return modifiedFieldNames || null; + }, + + + copyFrom: function(sourceRecord) { + var me = this, + fields = me.fields.items, + fieldCount = fields.length, + modifiedFieldNames = [], + field, i = 0, + myData, + sourceData, + idProperty = me.idProperty, + name, + value; + + if (sourceRecord) { + myData = me[me.persistenceProperty]; + sourceData = sourceRecord[sourceRecord.persistenceProperty]; + for (; i < fieldCount; i++) { + field = fields[i]; + name = field.name; + + + + + + + + if (name != idProperty) { + value = sourceData[name]; + + + + if (value !== undefined && !me.isEqual(myData[name], value)) { + myData[name] = value; + modifiedFieldNames.push(name); + } + } + } + + + if (me.phantom && !sourceRecord.phantom) { + + + me.beginEdit(); + me.setId(sourceRecord.getId()); + me.endEdit(true); + me.commit(true); + } + } + return modifiedFieldNames; + }, + + + isEqual: function(a, b) { + + if (a instanceof Date && b instanceof Date) { + return a.getTime() === b.getTime(); + } + return a === b; + }, + + + beginEdit : function(){ + var me = this, + key, + data, + o; + + if (!me.editing) { + me.editing = true; + me.dirtySave = me.dirty; + + o = me[me.persistenceProperty]; + data = me.dataSave = {}; + for (key in o) { + if (o.hasOwnProperty(key)) { + data[key] = o[key]; + } + } + + o = me.modified; + data = me.modifiedSave = {}; + for (key in o) { + if (o.hasOwnProperty(key)) { + data[key] = o[key]; + } + } + } + }, + + + cancelEdit : function(){ + var me = this; + if (me.editing) { + me.editing = false; + + me.modified = me.modifiedSave; + me[me.persistenceProperty] = me.dataSave; + me.dirty = me.dirtySave; + me.modifiedSave = me.dataSave = me.dirtySave = null; + } + }, + + + endEdit : function(silent, modifiedFieldNames){ + var me = this, + dataSave, + changed; + + silent = silent === true; + if (me.editing) { + me.editing = false; + dataSave = me.dataSave; + me.modifiedSave = me.dataSave = me.dirtySave = null; + if (!silent) { + if (!modifiedFieldNames) { + modifiedFieldNames = me.getModifiedFieldNames(dataSave); + } + changed = me.dirty || modifiedFieldNames.length > 0; + if (changed) { + me.afterEdit(modifiedFieldNames); + } + } + } + }, + + + getModifiedFieldNames: function(saved){ + var me = this, + data = me[me.persistenceProperty], + modified = [], + key; + + saved = saved || me.dataSave; + for (key in data) { + if (data.hasOwnProperty(key)) { + if (!me.isEqual(data[key], saved[key])) { + modified.push(key); + } + } + } + return modified; + }, + + + getChanges : function(){ + var modified = this.modified, + changes = {}, + field; + + for (field in modified) { + if (modified.hasOwnProperty(field)){ + changes[field] = this.get(field); + } + } + + return changes; + }, + + + isModified : function(fieldName) { + return this.modified.hasOwnProperty(fieldName); + }, + + + setDirty : function() { + var me = this, + fields = me.fields.items, + fLen = fields.length, + field, name, f; + + me.dirty = true; + + for (f = 0; f < fLen; f++) { + field = fields[f]; + + if (field.persist) { + name = field.name; + me.modified[name] = me.get(name); + } + } + }, + + markDirty : function() { + Ext.log.warn('Ext.data.Model: markDirty has been deprecated. Use setDirty instead.'); + + return this.setDirty.apply(this, arguments); + }, + + + reject : function(silent) { + var me = this, + modified = me.modified, + field; + + for (field in modified) { + if (modified.hasOwnProperty(field)) { + if (typeof modified[field] != "function") { + me[me.persistenceProperty][field] = modified[field]; + } + } + } + + me.dirty = false; + me.editing = false; + me.modified = {}; + + if (silent !== true) { + me.afterReject(); + } + }, + + + commit : function(silent, modifiedFieldNames) { + var me = this; + + me.phantom = me.dirty = me.editing = false; + me.modified = {}; + + if (silent !== true) { + me.afterCommit(modifiedFieldNames); + } + }, + + + copy : function(newId) { + var me = this; + return new me.self(me.raw, newId, null, Ext.apply({}, me[me.persistenceProperty])); + }, + + + setProxy: function(proxy) { + + if (!proxy.isProxy) { + if (typeof proxy === "string") { + proxy = { + type: proxy + }; + } + proxy = Ext.createByAlias("proxy." + proxy.type, proxy); + } + proxy.setModel(this.self); + this.proxy = proxy; + + return proxy; + }, + + + getProxy: function() { + return this.hasOwnProperty('proxy') ? this.proxy : this.self.getProxy(); + }, + + + validate: function() { + var errors = new Ext.data.Errors(), + validations = this.validations, + validators = Ext.data.validations, + length, validation, field, valid, type, i; + + if (validations) { + length = validations.length; + + for (i = 0; i < length; i++) { + validation = validations[i]; + field = validation.field || validation.name; + type = validation.type; + valid = validators[type](validation, this.get(field)); + + if (!valid) { + errors.add({ + field : field, + message: validation.message || validators[type + 'Message'] + }); + } + } + } + + return errors; + }, + + + isValid: function(){ + return this.validate().isValid(); + }, + + + save: function(options) { + options = Ext.apply({}, options); + + var me = this, + action = me.phantom ? 'create' : 'update', + scope = options.scope || me, + stores = me.stores, + i = 0, + storeCount, + store, + operation, + callback; + + Ext.apply(options, { + records: [me], + action : action + }); + + operation = new Ext.data.Operation(options); + + callback = function(operation) { + var success = operation.wasSuccessful(); + + if (success) { + for(storeCount = stores.length; i < storeCount; i++) { + store = stores[i]; + store.fireEvent('write', store, operation); + store.fireEvent('datachanged', store); + + } + Ext.callback(options.success, scope, [me, operation]); + } + else { + Ext.callback(options.failure, scope, [me, operation]); + } + + Ext.callback(options.callback, scope, [me, operation, success]); + }; + + me.getProxy()[action](operation, callback, me); + + return me; + }, + + + destroy: function(options) { + options = Ext.apply({ + records: [this], + action : 'destroy' + }, options); + + var me = this, + isNotPhantom = me.phantom !== true, + scope = options.scope || me, + stores, + i = 0, + storeCount, + store, + args, + operation, + callback; + + operation = new Ext.data.Operation(options); + + callback = function(operation) { + args = [me, operation]; + + + stores = Ext.Array.clone(me.stores); + if (operation.wasSuccessful()) { + for (storeCount = stores.length; i < storeCount; i++) { + store = stores[i]; + + + if (store.remove) { + store.remove(me, true); + } else { + store.fireEvent('bulkremove', store, [me], [store.indexOf(me)], false); + } + + if (isNotPhantom) { + store.fireEvent('write', store, operation); + } + } + me.clearListeners(); + Ext.callback(options.success, scope, args); + } else { + Ext.callback(options.failure, scope, args); + } + Ext.callback(options.callback, scope, args); + }; + + + + if (isNotPhantom) { + me.getProxy().destroy(operation, callback, me); + } + + else { + operation.complete = operation.success = true; + operation.resultSet = me.getProxy().reader.nullResultSet; + callback(operation); + } + return me; + }, + + + getId: function() { + return this.get(this.idField.name); + }, + + + getObservableId: function() { + return this.id; + }, + + + setId: function(id) { + this.set(this.idProperty, id); + }, + + changeId: function(oldId, newId) { + var me = this, + hasOldId, hasId, oldInternalId; + + if (!me.preventInternalUpdate) { + hasOldId = me.hasId(oldId); + hasId = me.hasId(newId); + oldInternalId = me.internalId; + me.phantom = !hasId; + + + + + if (hasId !== hasOldId || (hasId && hasOldId)) { + me.internalId = hasId ? newId : Ext.data.Model.id(me); + } + + me.fireEvent('idchanged', me, oldId, newId, oldInternalId); + me.callStore('onIdChanged', oldId, newId, oldInternalId); + } + }, + + + hasId: function(id) { + if (arguments.length === 0) { + id = this.getId(); + } + return !!(id || id === 0); + }, + + + join : function(store) { + var me = this; + + + if (!me.stores.length) { + me.stores[0] = store; + } else { + Ext.Array.include(this.stores, store); + } + + + this.store = this.stores[0]; + }, + + + unjoin: function(store) { + Ext.Array.remove(this.stores, store); + this.store = this.stores[0] || null; + }, + + + afterEdit : function(modifiedFieldNames) { + this.callStore('afterEdit', modifiedFieldNames); + }, + + + afterReject : function() { + this.callStore('afterReject'); + }, + + + afterCommit: function(modifiedFieldNames) { + this.callStore('afterCommit', modifiedFieldNames); + }, + + + callStore: function(fn) { + var args = Ext.Array.clone(arguments), + stores = this.stores, + i = 0, + len = stores.length, + store; + + args[0] = this; + for (; i < len; ++i) { + store = stores[i]; + if (store && Ext.isFunction(store[fn])) { + store[fn].apply(store, args); + } + } + }, + + + getData: function(includeAssociated){ + var me = this, + fields = me.fields.items, + fLen = fields.length, + data = {}, + name, f; + + for (f = 0; f < fLen; f++) { + name = fields[f].name; + data[name] = me.get(name); + } + + if (includeAssociated === true) { + Ext.apply(data, me.getAssociatedData()); + } + return data; + }, + + + getAssociatedData: function(){ + return this.prepareAssociatedData({}, 1); + }, + + + prepareAssociatedData: function(seenKeys, depth) { + + var me = this, + associations = me.associations.items, + associationCount = associations.length, + associationData = {}, + + + + toRead = [], + toReadKey = [], + toReadIndex = [], + associatedStore, associatedRecords, associatedRecord, o, index, result, seenDepth, + associationId, associatedRecordCount, association, i, j, type, name; + + for (i = 0; i < associationCount; i++) { + association = associations[i]; + associationId = association.associationId; + + seenDepth = seenKeys[associationId]; + if (seenDepth && seenDepth !== depth) { + continue; + } + seenKeys[associationId] = depth; + + type = association.type; + name = association.name; + if (type == 'hasMany') { + + associatedStore = me[association.storeName]; + + + associationData[name] = []; + + + if (associatedStore && associatedStore.getCount() > 0) { + associatedRecords = associatedStore.data.items; + associatedRecordCount = associatedRecords.length; + + + + for (j = 0; j < associatedRecordCount; j++) { + associatedRecord = associatedRecords[j]; + associationData[name][j] = associatedRecord.getData(); + toRead.push(associatedRecord); + toReadKey.push(name); + toReadIndex.push(j); + } + } + } else if (type == 'belongsTo' || type == 'hasOne') { + associatedRecord = me[association.instanceName]; + + if (associatedRecord !== undefined) { + associationData[name] = associatedRecord.getData(); + toRead.push(associatedRecord); + toReadKey.push(name); + toReadIndex.push(-1); + } + } + } + + for (i = 0, associatedRecordCount = toRead.length; i < associatedRecordCount; ++i) { + associatedRecord = toRead[i]; + o = associationData[toReadKey[i]]; + index = toReadIndex[i]; + result = associatedRecord.prepareAssociatedData(seenKeys, depth + 1); + if (index === -1) { + Ext.apply(o, result); + } else { + Ext.apply(o[index], result); + } + } + + return associationData; + } +}); + + + +Ext.define('Ext.data.proxy.Server', { + extend: Ext.data.proxy.Proxy , + alias : 'proxy.server', + alternateClassName: 'Ext.data.ServerProxy', + + + + + + pageParam: 'page', + + + startParam: 'start', + + + limitParam: 'limit', + + + groupParam: 'group', + + + groupDirectionParam: 'groupDir', + + + sortParam: 'sort', + + + filterParam: 'filter', + + + directionParam: 'dir', + + + idParam: 'id', + + + simpleSortMode: false, + + + simpleGroupMode: false, + + + noCache : true, + + + cacheString: "_dc", + + + timeout : 30000, + + + + constructor: function(config) { + var me = this; + + config = config || {}; + + me.callParent([config]); + + + me.extraParams = config.extraParams || {}; + + me.api = Ext.apply({}, config.api || me.api); + + + + me.nocache = me.noCache; + }, + + + create: function() { + return this.doRequest.apply(this, arguments); + }, + + read: function() { + return this.doRequest.apply(this, arguments); + }, + + update: function() { + return this.doRequest.apply(this, arguments); + }, + + destroy: function() { + return this.doRequest.apply(this, arguments); + }, + + + setExtraParam: function(name, value) { + this.extraParams[name] = value; + }, + + + buildRequest: function(operation) { + var me = this, + + params = operation.params = Ext.apply({}, operation.params, me.extraParams), + request; + + + Ext.applyIf(params, me.getParams(operation)); + + + + + if (operation.id !== undefined && params[me.idParam] === undefined) { + params[me.idParam] = operation.id; + } + + request = new Ext.data.Request({ + params : params, + action : operation.action, + records : operation.records, + operation: operation, + url : operation.url, + + + + proxy: me + }); + + request.url = me.buildUrl(request); + + + operation.request = request; + + return request; + }, + + + processResponse: function(success, operation, request, response, callback, scope) { + var me = this, + reader, + result; + + if (success === true) { + reader = me.getReader(); + + + + + reader.applyDefaults = operation.action === 'read'; + + result = reader.read(me.extractResponseData(response)); + + if (result.success !== false) { + + Ext.apply(operation, { + response: response, + resultSet: result + }); + + operation.commitRecords(result.records); + operation.setCompleted(); + operation.setSuccessful(); + } else { + operation.setException(result.message); + me.fireEvent('exception', this, response, operation); + } + } else { + me.setException(operation, response); + me.fireEvent('exception', this, response, operation); + } + + + if (typeof callback == 'function') { + callback.call(scope || me, operation); + } + + me.afterRequest(request, success); + }, + + + setException: function(operation, response) { + operation.setException({ + status: response.status, + statusText: response.statusText + }); + }, + + + extractResponseData: Ext.identityFn, + + + applyEncoding: function(value) { + return Ext.encode(value); + }, + + + encodeSorters: function(sorters) { + var min = [], + length = sorters.length, + i = 0; + + for (; i < length; i++) { + min[i] = { + property : sorters[i].property, + direction: sorters[i].direction + }; + } + return this.applyEncoding(min); + + }, + + + encodeFilters: function(filters) { + var min = [], + length = filters.length, + i = 0; + + for (; i < length; i++) { + min[i] = { + property: filters[i].property, + value : filters[i].value + }; + } + return this.applyEncoding(min); + }, + + + getParams: function(operation) { + var me = this, + params = {}, + isDef = Ext.isDefined, + groupers = operation.groupers, + sorters = operation.sorters, + filters = operation.filters, + page = operation.page, + start = operation.start, + limit = operation.limit, + simpleSortMode = me.simpleSortMode, + simpleGroupMode = me.simpleGroupMode, + pageParam = me.pageParam, + startParam = me.startParam, + limitParam = me.limitParam, + groupParam = me.groupParam, + groupDirectionParam = me.groupDirectionParam, + sortParam = me.sortParam, + filterParam = me.filterParam, + directionParam = me.directionParam, + hasGroups, index; + + if (pageParam && isDef(page)) { + params[pageParam] = page; + } + + if (startParam && isDef(start)) { + params[startParam] = start; + } + + if (limitParam && isDef(limit)) { + params[limitParam] = limit; + } + + hasGroups = groupParam && groupers && groupers.length > 0; + if (hasGroups) { + + if (simpleGroupMode) { + params[groupParam] = groupers[0].property; + params[groupDirectionParam] = groupers[0].direction || 'ASC'; + } else { + params[groupParam] = me.encodeSorters(groupers); + } + } + + if (sortParam && sorters && sorters.length > 0) { + if (simpleSortMode) { + index = 0; + + if (sorters.length > 1 && hasGroups) { + index = 1; + } + params[sortParam] = sorters[index].property; + params[directionParam] = sorters[index].direction; + } else { + params[sortParam] = me.encodeSorters(sorters); + } + + } + + if (filterParam && filters && filters.length > 0) { + params[filterParam] = me.encodeFilters(filters); + } + + return params; + }, + + + buildUrl: function(request) { + var me = this, + url = me.getUrl(request); + + if (!url) { + Ext.Error.raise("You are using a ServerProxy but have not supplied it with a url."); + } + + if (me.noCache) { + url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.cacheString, Ext.Date.now())); + } + + return url; + }, + + + getUrl: function(request) { + return request.url || this.api[request.action] || this.url; + }, + + + doRequest: function(operation, callback, scope) { + Ext.Error.raise("The doRequest function has not been implemented on your Ext.data.proxy.Server subclass. See src/data/ServerProxy.js for details"); + }, + + + afterRequest: Ext.emptyFn, + + onDestroy: function() { + Ext.destroy(this.reader, this.writer); + } +}); + + + +Ext.define('Ext.data.proxy.Ajax', { + + extend: Ext.data.proxy.Server , + alias: 'proxy.ajax', + alternateClassName: ['Ext.data.HttpProxy', 'Ext.data.AjaxProxy'], + + + actionMethods: { + create : 'POST', + read : 'GET', + update : 'POST', + destroy: 'POST' + }, + + + + + + defaultActionMethods: { + create : 'POST', + read : 'GET', + update : 'POST', + destroy: 'POST' + }, + + + binary: false, + + + + + paramsAsJson: false, + + doRequest: function(operation, callback, scope) { + var me = this, + writer = me.getWriter(), + request = me.buildRequest(operation), + method = me.getMethod(request); + + if (operation.allowWrite()) { + request = writer.write(request); + } + + Ext.apply(request, { + binary : me.binary, + headers : me.headers, + timeout : me.timeout, + scope : me, + callback : me.createRequestCallback(request, operation, callback, scope), + method : method, + disableCaching: false + }); + + if (method.toUpperCase() !== 'GET' && me.paramsAsJson) { + + + + request = Ext.apply({ + jsonData: request.params + }, request); + delete request.params; + } + + Ext.Ajax.request(request); + + return request; + }, + + + getMethod: function(request) { + var actions = this.actionMethods, + action = request.action, + method; + + if (actions) { + method = actions[action]; + } + return method || this.defaultActionMethods[action]; + }, + + + createRequestCallback: function(request, operation, callback, scope) { + var me = this; + + return function(options, success, response) { + me.processResponse(success, operation, request, response, callback, scope); + }; + } +}); + + + +Ext.define('Ext.data.PageMap', { + extend: Ext.util.LruCache , + + + clear: function(initial) { + var me = this; + me.pageMapGeneration = (me.pageMapGeneration || 0) + 1; + me.callParent(arguments); + }, + + forEach: function(fn, scope) { + var me = this, + pageNumbers = Ext.Object.getKeys(me.map), + pageCount = pageNumbers.length, + i, j, + pageNumber, + page, + pageSize; + + for (i = 0; i < pageCount; i++) { + pageNumbers[i] = +pageNumbers[i]; + } + Ext.Array.sort(pageNumbers, function(a, b) { + return a - b; + }); + scope = scope || me; + for (i = 0; i < pageCount; i++) { + pageNumber = pageNumbers[i]; + page = me.getPage(pageNumber); + pageSize = page.length; + for (j = 0; j < pageSize; j++) { + if (fn.call(scope, page[j], (pageNumber - 1) * me.pageSize + j) === false) { + return; + } + } + } + }, + + + findBy: function(fn, scope) { + var me = this, + result = null; + + scope = scope || me; + me.forEach(function(rec, index) { + if (fn.call(scope, rec, index)) { + result = rec; + return false; + } + }); + return result; + }, + + + findIndexBy: function(fn, scope) { + var me = this, + result = -1; + + scope = scope || me; + me.forEach(function(rec, index) { + if (fn.call(scope, rec)) { + result = index; + return false; + } + }); + return result; + }, + + getPageFromRecordIndex: function() { + return Ext.data.Store.prototype.getPageFromRecordIndex.apply(this, arguments); + }, + + addAll: function(records) { + if (this.getCount()) { + Ext.Error.raise('Cannot addAll to a non-empty PageMap'); + } + this.addPage(1, records); + }, + + addPage: function(pageNumber, records) { + var me = this, + lastPage = pageNumber + Math.floor((records.length - 1) / me.pageSize), + startIdx, + page; + + + + for (startIdx = 0; pageNumber <= lastPage; pageNumber++, startIdx += me.pageSize) { + page = Ext.Array.slice(records, startIdx, startIdx + me.pageSize); + me.add(pageNumber, page); + me.fireEvent('pageAdded', pageNumber, page); + } + }, + + getCount: function() { + var result = this.callParent(); + if (result) { + result = (result - 1) * this.pageSize + this.last.value.length; + } + return result; + }, + + indexOf: function(record) { + return record ? record.index : -1; + }, + + insert: function() { + Ext.Error.raise('insert operation not suppported into buffered Store'); + }, + + remove: function() { + Ext.Error.raise('remove operation not suppported from buffered Store'); + }, + + removeAt: function() { + Ext.Error.raise('removeAt operation not suppported from buffered Store'); + }, + + getPage: function(pageNumber) { + return this.get(pageNumber); + }, + + hasRange: function(start, end) { + var pageNumber = this.getPageFromRecordIndex(start), + endPageNumber = this.getPageFromRecordIndex(end); + + for (; pageNumber <= endPageNumber; pageNumber++) { + if (!this.hasPage(pageNumber)) { + return false; + } + } + return true; + }, + + hasPage: function(pageNumber) { + + return !!this.get(pageNumber); + }, + + getAt: function(index) { + return this.getRange(index, index)[0]; + }, + + getRange: function(start, end) { + if (!this.hasRange(start, end)) { + Ext.Error.raise('PageMap asked for range which it does not have'); + } + var me = this, + startPageNumber = me.getPageFromRecordIndex(start), + endPageNumber = me.getPageFromRecordIndex(end), + dataStart = (startPageNumber - 1) * me.pageSize, + dataEnd = (endPageNumber * me.pageSize) - 1, + pageNumber = startPageNumber, + result = [], + sliceBegin, sliceEnd, doSlice, + i = 0, len; + + for (; pageNumber <= endPageNumber; pageNumber++) { + + + if (pageNumber == startPageNumber) { + sliceBegin = start - dataStart; + doSlice = true; + } else { + sliceBegin = 0; + doSlice = false; + } + if (pageNumber == endPageNumber) { + sliceEnd = me.pageSize - (dataEnd - end); + doSlice = true; + } + + + if (doSlice) { + Ext.Array.push(result, Ext.Array.slice(me.getPage(pageNumber), sliceBegin, sliceEnd)); + } else { + Ext.Array.push(result, me.getPage(pageNumber)); + } + } + + + for (len = result.length; i < len; i++) { + result[i].index = start++; + } + return result; + } +}); + + + +Ext.define('Ext.data.Group', { + + extend: Ext.util.Observable , + + key: undefined, + + dirty: true, + + constructor: function(){ + this.callParent(arguments); + this.records = []; + }, + + contains: function(record){ + return Ext.Array.indexOf(this.records, record) !== -1; + }, + + add: function(records) { + Ext.Array.push(this.records, records); + this.dirty = true; + }, + + remove: function(records) { + if (!Ext.isArray(records)) { + records = [records]; + } + + var len = records.length, + i; + + for (i = 0; i < len; ++i) { + Ext.Array.remove(this.records, records[i]); + } + this.dirty = true; + }, + + isDirty: function(){ + return this.dirty; + }, + + hasAggregate: function(){ + return !!this.aggregate; + }, + + setDirty: function(){ + this.dirty = true; + }, + + commit: function(){ + this.dirty = false; + }, + + isCollapsed: function(){ + return this.collapsed; + }, + + getAggregateRecord: function(forceNew){ + var me = this, + Model; + + if (forceNew === true || me.dirty || !me.aggregate) { + Model = me.store.model; + me.aggregate = new Model(); + me.aggregate.isSummary = true; + } + return me.aggregate; + } + +}); + + + +Ext.define('Ext.data.Store', { + extend: Ext.data.AbstractStore , + + alias: 'store.store', + + + + + + + + + + + + + + + + + + + + + remoteSort: false, + + + remoteFilter: false, + + + remoteGroup : false, + + + + + + + + + groupField: undefined, + + + groupDir: 'ASC', + + + trailingBufferZone: 25, + + + leadingBufferZone: 200, + + + pageSize: undefined, + + + currentPage: 1, + + + clearOnPageLoad: true, + + + loading: false, + + + sortOnFilter: true, + + + buffered: false, + + + purgePageCount: 5, + + + clearRemovedOnLoad: true, + + defaultPageSize: 25, + + + defaultViewSize: 100, + + + addRecordsOptions: { + addRecords: true + }, + + statics: { + recordIdFn: function(record) { + return record.internalId; + }, + recordIndexFn: function(record) { + return record.index; + }, + grouperIdFn: function(grouper) { + return grouper.id || grouper.property; + }, + groupIdFn: function(group) { + return group.key; + } + }, + + + constructor: function(config) { + + config = Ext.apply({}, config); + + var me = this, + groupers = config.groupers || me.groupers, + groupField = config.groupField || me.groupField, + proxy, + data; + + + + + + data = config.data || me.data; + + if (data) { + me.inlineData = data; + delete config.data; + } + + if (!groupers && groupField) { + groupers = [{ + property : groupField, + direction: config.groupDir || me.groupDir + }]; + + + if (config.getGroupString || (me.getGroupString !== Ext.data.Store.prototype.getGroupString)) { + groupers[0].getGroupString = function(record) { + return me.getGroupString(record); + }; + } + } + delete config.groupers; + + + me.groupers = new Ext.util.MixedCollection(false, Ext.data.Store.grouperIdFn); + me.groupers.addAll(me.decodeGroupers(groupers)); + + me.groups = new Ext.util.MixedCollection(false, Ext.data.Store.groupIdFn); + + + me.callParent([config]); + + + if (me.buffered) { + me.data = new Ext.data.PageMap({ + store: me, + keyFn: Ext.data.Store.recordIdFn, + pageSize: me.pageSize, + maxSize: me.purgePageCount, + listeners: { + + + clear: me.onPageMapClear, + scope: me + } + }); + me.pageRequests = {}; + + + me.remoteSort = me.remoteGroup = me.remoteFilter = true; + + me.sortOnLoad = false; + me.filterOnLoad = false; + } else { + + me.data = new Ext.util.MixedCollection({ + getKey: Ext.data.Store.recordIdFn, + maintainIndices: true + }); + } + + + if (me.remoteGroup) { + me.remoteSort = true; + } + + proxy = me.proxy; + data = me.inlineData; + + + + if (!me.buffered && !me.pageSize) { + me.pageSize = me.defaultPageSize; + } + + + + + if (data) { + if (proxy instanceof Ext.data.proxy.Memory) { + proxy.data = data; + me.read(); + } else { + me.add.apply(me, [data]); + } + + delete me.inlineData; + } + else if (me.autoLoad) { + + me.autoLoadTask = new Ext.util.DelayedTask(me.load, me, [ typeof me.autoLoad === 'object' ? me.autoLoad : undefined ]); + me.autoLoadTask.delay(1); + } + }, + + + decodeGroupers: function(groupers) { + if (!Ext.isArray(groupers)) { + if (groupers === undefined) { + groupers = []; + } else { + groupers = [groupers]; + } + } + + var length = groupers.length, + Grouper = Ext.util.Grouper, + config, i, result = []; + + for (i = 0; i < length; i++) { + config = groupers[i]; + + if (!(config instanceof Grouper)) { + if (Ext.isString(config)) { + config = { + property: config + }; + } + + config = Ext.apply({ + root : 'data', + direction: "ASC" + }, config); + + + if (config.fn) { + config.sorterFn = config.fn; + } + + + if (typeof config == 'function') { + config = { + sorterFn: config + }; + } + + + result.push(new Grouper(config)); + } else { + result.push(config); + } + } + return result; + }, + + + group: function(groupers, direction, suppressEvent) { + var me = this, + grouper, + newGroupers; + + + if (groupers) { + + + me.sorters.removeAll(me.groupers.items); + + if (Ext.isArray(groupers)) { + newGroupers = groupers; + } else if (Ext.isObject(groupers)) { + newGroupers = [groupers]; + } else if (Ext.isString(groupers)) { + grouper = me.groupers.get(groupers); + + if (!grouper) { + grouper = { + property : groupers, + direction: direction || me.groupDir + }; + newGroupers = [grouper]; + } else if (direction === undefined) { + grouper.toggle(); + } else { + grouper.setDirection(direction); + } + } + + + if (newGroupers && newGroupers.length) { + me.groupers.clear(); + me.groupers.addAll(me.decodeGroupers(newGroupers)); + } + + + me.sorters.insert(0, me.groupers.items); + } + + if (me.remoteGroup) { + if (me.buffered) { + me.data.clear(); + me.loadPage(1, { groupChange: true }); + } else { + me.load({ + scope: me, + callback: suppressEvent ? null : me.fireGroupChange + }); + } + } else { + me.doSort(me.generateComparator()); + me.constructGroups(); + if (!suppressEvent) { + me.fireGroupChange(); + } + } + }, + + getGroupField: function(){ + var first = this.groupers.first(), + group; + + if (first) { + group = first.property; + } + return group; + }, + + constructGroups: function(){ + var me = this, + data = this.data.items, + len = data.length, + groups = me.groups, + groupValue, i, group, rec; + + groups.clear(); + + if (me.isGrouped()) { + for (i = 0; i < len; ++i) { + rec = data[i]; + groupValue = me.getGroupString(rec); + group = groups.get(groupValue); + if (!group) { + group = new Ext.data.Group({ + key: groupValue, + store: me + }); + groups.add(group); + } + group.add(rec); + } + } + }, + + + clearGrouping: function() { + var me = this, + groupers = me.groupers.items, + gLen = groupers.length, + g; + + + for (g = 0; g < gLen; g++) { + me.sorters.remove(groupers[g]); + } + me.groupers.clear(); + if (me.remoteGroup) { + if (me.buffered) { + me.data.clear(); + me.loadPage(1, { groupChange: true }); + } else { + me.load({ + scope: me, + callback: me.fireGroupChange + }); + } + } else { + me.groups.clear(); + if (me.sorters.length) { + me.sort(); + } else { + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + } + me.fireGroupChange(); + } + }, + + + isGrouped: function() { + return this.groupers.getCount() > 0; + }, + + + fireGroupChange: function() { + this.fireEvent('groupchange', this, this.groupers); + }, + + + getGroups: function(requestGroupString) { + var records = this.data.items, + length = records.length, + groups = [], + pointers = {}, + record, + groupStr, + group, + i; + + for (i = 0; i < length; i++) { + record = records[i]; + groupStr = this.getGroupString(record); + group = pointers[groupStr]; + + if (group === undefined) { + group = { + name: groupStr, + children: [] + }; + + groups.push(group); + pointers[groupStr] = group; + } + + group.children.push(record); + } + + return requestGroupString ? pointers[requestGroupString] : groups; + }, + + + getGroupsForGrouper: function(records, grouper) { + var length = records.length, + groups = [], + oldValue, + newValue, + record, + group, + i; + + for (i = 0; i < length; i++) { + record = records[i]; + newValue = grouper.getGroupString(record); + + if (newValue !== oldValue) { + group = { + name: newValue, + grouper: grouper, + records: [] + }; + groups.push(group); + } + + group.records.push(record); + + oldValue = newValue; + } + + return groups; + }, + + + getGroupsForGrouperIndex: function(records, grouperIndex) { + var me = this, + groupers = me.groupers, + grouper = groupers.getAt(grouperIndex), + groups = me.getGroupsForGrouper(records, grouper), + length = groups.length, + i; + + if (grouperIndex + 1 < groupers.length) { + for (i = 0; i < length; i++) { + groups[i].children = me.getGroupsForGrouperIndex(groups[i].records, grouperIndex + 1); + } + } + + for (i = 0; i < length; i++) { + groups[i].depth = grouperIndex; + } + + return groups; + }, + + + getGroupData: function(sort) { + var me = this; + if (sort !== false) { + me.sort(); + } + + return me.getGroupsForGrouperIndex(me.data.items, 0); + }, + + + getGroupString: function(instance) { + var group = this.groupers.first(); + if (group) { + return group.getGroupString(instance); + } + return ''; + }, + + + insert: function(index, records) { + var me = this, + sync = false, + i, len, record, + defaults = me.modelDefaults, + out; + + + if (!Ext.isIterable(records)) { + out = records = [records]; + } else { + out = []; + } + len = records.length; + + if (len) { + for (i = 0; i < len; i++) { + record = records[i]; + if (!record.isModel) { + record = me.createModel(record); + } + out[i] = record; + if (defaults) { + record.set(defaults); + } + + record.join(me); + sync = sync || record.phantom === true; + } + + me.data.insert(index, out); + + if (me.snapshot) { + me.snapshot.addAll(out); + } + + if (me.requireSort) { + + me.suspendEvents(); + me.sort(); + me.resumeEvents(); + } + + if (me.isGrouped()) { + me.updateGroupsOnAdd(out); + } + + me.fireEvent('add', me, out, index); + me.fireEvent('datachanged', me); + if (me.autoSync && sync && !me.autoSyncSuspended) { + me.sync(); + } + } + return out; + }, + + updateGroupsOnAdd: function(records) { + var me = this, + groups = me.groups, + len = records.length, + i, groupName, group, rec; + + for (i = 0; i < len; ++i) { + rec = records[i]; + groupName = me.getGroupString(rec); + group = groups.getByKey(groupName); + if (!group) { + group = groups.add(new Ext.data.Group({ + key: groupName, + store: me + })); + } + group.add(rec); + } + }, + + updateGroupsOnRemove: function(records) { + var me = this, + groups = me.groups, + len = records.length, + i, groupName, group, rec; + + for (i = 0; i < len; ++i) { + rec = records[i]; + groupName = me.getGroupString(rec); + group = groups.getByKey(groupName); + + if (group) { + group.remove(rec); + if (group.records.length === 0) { + groups.remove(group); + } + } + } + }, + + updateGroupsOnUpdate: function(record, modifiedFieldNames){ + var me = this, + groupField = me.getGroupField(), + groupName = me.getGroupString(record), + groups = me.groups, + len, i, items, group; + + if (modifiedFieldNames && Ext.Array.contains(modifiedFieldNames, groupField)) { + + + if (me.buffered) { + Ext.Error.raise({ + msg: 'Cannot move records between groups in a buffered store record' + }); + } + + + items = groups.items; + for (i = 0, len = items.length; i < len; ++i) { + group = items[i]; + if (group.contains(record)) { + group.remove(record); + break; + } + } + group = groups.getByKey(groupName); + if (!group) { + group = groups.add(new Ext.data.Group({ + key: groupName, + store: me + })); + } + group.add(record); + + + + me.data.remove(record); + me.data.insert(me.data.findInsertionIndex(record, me.generateComparator()), record); + + } else { + + groups.getByKey(groupName).setDirty(); + } + }, + + + add: function(arg) { + var me = this, + records, + length, isSorted; + + if (me.buffered) { + Ext.Error.raise({ + msg: 'add method may not be called on a buffered store' + }); + } + + + if (Ext.isArray(arg)) { + records = arg; + } else { + records = arguments; + } + + length = records.length; + isSorted = !me.remoteSort && me.getSorterCount(); + + + + if (isSorted && length === 1) { + return [ me.addSorted(me.createModel(records[0])) ]; + } + + + + if (isSorted) { + me.requireSort = true; + } + + records = me.insert(me.data.length, records); + delete me.requireSort; + + return records; + }, + + + addSorted: function(record) { + var me = this, + index = me.data.findInsertionIndex(record, me.generateComparator()); + + me.insert(index, record); + return record; + }, + + + createModel: function(record) { + if (!record.isModel) { + record = Ext.ModelManager.create(record, this.model); + } + + return record; + }, + + onUpdate: function(record, type, modifiedFieldNames){ + if (this.isGrouped()) { + this.updateGroupsOnUpdate(record, modifiedFieldNames); + } + }, + + + each: function(fn, scope) { + var data = this.data.items, + dLen = data.length, + record, d; + + for (d = 0; d < dLen; d++) { + record = data[d]; + if (fn.call(scope || record, record, d, dLen) === false) { + break; + } + } + }, + + + remove: function(records, isMove, silent) { + + isMove = isMove === true; + + var me = this, + sync = false, + snapshot = me.snapshot, + data = me.data, + i = 0, + length, + info = [], + allRecords = [], + indexes = [], + + grouped = [], + groupsLn = me.groups.length, + item, + isNotPhantom, + index, + record, + removeRange, + removeCount, + fireRemoveEvent = !silent && me.hasListeners.remove; + + + if (records.isModel) { + records = [records]; + length = 1; + } + + + else if (Ext.isIterable(records)) { + length = records.length; + } + + + + else if (typeof records === 'object') { + removeRange = true; + i = records.start; + length = records.end + 1; + removeCount = length - i; + } + + + + if (!removeRange) { + for (i = 0; i < length; ++i) { + + record = records[i]; + + + if (typeof record == 'number') { + index = record; + record = data.getAt(index); + } + + else { + index = me.indexOf(record); + } + + + if (record && index > -1) { + info.push({ + record: record, + index: index + }); + } + + + if (snapshot) { + snapshot.remove(record); + } + } + + + info = Ext.Array.sort(info, function(o1, o2) { + var index1 = o1.index, + index2 = o2.index; + + return index1 === o2.index2 ? 0 : (index1 < index2 ? -1 : 1); + }); + + + i = 0; + length = info.length; + } + + + + if (!length) { + return; + } + + + + + for (; i < length; i++) { + if (removeRange) { + record = data.getAt(i); + index = i; + } else { + item = info[i]; + record = item.record; + index = item.index; + } + + allRecords.push(record); + indexes.push(index); + + isNotPhantom = record.phantom !== true; + + if (!isMove && isNotPhantom) { + + + + record.removedFrom = index; + me.removed.push(record); + } else if (groupsLn) { + + + grouped.push(record); + } + + record.unjoin(me); + + + + index -= i; + sync = sync || isNotPhantom; + + + + if (!removeRange) { + data.removeAt(index); + + + if (fireRemoveEvent) { + me.fireEvent('remove', me, record, index, !!isMove); + } + } + } + + if (groupsLn) { + me.updateGroupsOnRemove(me.removed.concat(grouped)); + } + + + + if (removeRange) { + data.removeRange(records.start, removeCount); + } + + if (!silent) { + me.fireEvent('bulkremove', me, allRecords, indexes, !!isMove, removeRange); + me.fireEvent('datachanged', me); + } + if (!isMove && me.autoSync && sync && !me.autoSyncSuspended) { + me.sync(); + } + }, + + + removeAt: function(index, count) { + var me = this, + storeCount = me.getCount(); + + + index = Math.max(index, 0); + + if (index <= storeCount) { + if (arguments.length === 1) { + me.remove([ index ]); + } else if (count) { + me.remove({ + start: index, + end: Math.min(index + count, storeCount) - 1 + }); + } + } + }, + + + removeAll: function(silent) { + var me = this, + snapshot = me.snapshot, + data = me.data; + + if (snapshot) { + snapshot.removeAll(data.getRange()); + } + + if (me.buffered) { + if (data) { + if (silent) { + me.suspendEvent('clear'); + } + data.clear(); + if (silent) { + me.resumeEvent('clear'); + } + } + } + + else { + + + me.remove({ + start: 0, + end: me.getCount() - 1 + }, false, silent); + if (silent !== true) { + me.fireEvent('clear', me); + } + } + }, + + + load: function(options) { + var me = this; + + if (typeof options == 'function') { + options = { + callback: options + }; + } else { + options = Ext.apply({}, options); + } + + + if (me.remoteGroup && !options.groupers && me.groupers.items.length) { + options.groupers = me.groupers.items; + } + options.page = options.page || me.currentPage; + options.start = (options.start !== undefined) ? options.start : (options.page - 1) * me.pageSize; + options.limit = options.limit || me.pageSize; + options.addRecords = options.addRecords || false; + + if (me.buffered) { + options.limit = me.viewSize || me.defaultViewSize; + + + options.loadCallback = options.callback; + delete options.callback; + return me.loadToPrefetch(options); + } + return me.callParent([options]); + }, + + reload: function(options) { + var me = this, + startIdx, + endIdx, + startPage, + endPage, + i, + waitForReload, + bufferZone, + records; + + if (!options) { + options = {}; + } + + + + if (me.buffered) { + + + delete me.totalCount; + + + me.data.clear(true); + + waitForReload = function() { + if (me.rangeCached(startIdx, endIdx)) { + me.loading = false; + me.data.un('pageAdded', waitForReload); + records = me.data.getRange(startIdx, endIdx); + me.fireEvent('load', me, records, true); + } + }; + bufferZone = Math.ceil((me.leadingBufferZone + me.trailingBufferZone) / 2); + + + if (!me.lastRequestStart) { + startIdx = options.start || 0; + endIdx = startIdx + (options.count || me.pageSize) - 1; + } else { + startIdx = me.lastRequestStart; + endIdx = me.lastRequestEnd; + } + + + startPage = me.getPageFromRecordIndex(Math.max(startIdx - bufferZone, 0)); + endPage = me.getPageFromRecordIndex(endIdx + bufferZone); + + if (me.fireEvent('beforeload', me, options) !== false) { + me.loading = true; + + + + me.data.on('pageAdded', waitForReload); + + + for (i = startPage; i <= endPage; i++) { + me.prefetchPage(i, options); + } + } + } else { + return me.callParent(arguments); + } + }, + + + onProxyLoad: function(operation) { + var me = this, + resultSet = operation.getResultSet(), + records = operation.getRecords(), + successful = operation.wasSuccessful(); + + if (me.isDestroyed) { + return; + } + + if (resultSet) { + me.totalCount = resultSet.total; + } + + + + + me.loading = false; + if (successful) { + me.loadRecords(records, operation); + } + + if (me.hasListeners.load) { + me.fireEvent('load', me, records, successful); + } + + + + if (me.hasListeners.read) { + me.fireEvent('read', me, records, successful); + } + + + Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]); + }, + + + getNewRecords: function() { + return (this.snapshot || this.data).filterBy(this.filterNew).items; + }, + + + getUpdatedRecords: function() { + return (this.snapshot || this.data).filterBy(this.filterUpdated).items; + }, + + + filter: function(filters, value) { + if (Ext.isString(filters)) { + filters = { + property: filters, + value: value + }; + } + + var me = this, + decoded = me.decodeFilters(filters), + i, + doLocalSort = me.sorters.length && me.sortOnFilter && !me.remoteSort, + length = decoded.length; + + + for (i = 0; i < length; i++) { + me.filters.replace(decoded[i]); + } + + filters = me.filters.items; + + + + + if (filters.length) { + if (me.remoteFilter) { + + delete me.totalCount; + + + + + if (me.buffered) { + me.data.clear(); + me.loadPage(1); + } else { + + me.currentPage = 1; + + me.load(); + } + } else { + + me.snapshot = me.snapshot || me.data.clone(); + + + me.data = me.snapshot.filter(filters); + + + me.constructGroups(); + + if (doLocalSort) { + me.sort(); + } else { + + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + } + } + me.fireEvent('filterchange', me, filters); + } + }, + + + clearFilter: function(suppressEvent) { + var me = this; + + me.filters.clear(); + + if (me.remoteFilter) { + + + if (suppressEvent) { + return; + } + + + delete me.totalCount; + + + + + if (me.buffered) { + me.data.clear(); + me.loadPage(1); + } else { + + me.currentPage = 1; + me.load(); + } + } else if (me.isFiltered()) { + me.data = me.snapshot; + delete me.snapshot; + + + me.constructGroups(); + + if (suppressEvent !== true) { + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + } + } + me.fireEvent('filterchange', me, me.filters.items); + }, + + + removeFilter: function(toRemove, applyFilters) { + var me = this; + + if (!me.remoteFilter && me.isFiltered()) { + if (toRemove instanceof Ext.util.Filter) { + me.filters.remove(toRemove); + } else { + me.filters.removeAtKey(toRemove); + } + + if (applyFilters !== false) { + + + if (me.filters.length) { + me.filter(); + } + + + else { + me.clearFilter(); + } + } else { + me.fireEvent('filterchange', me, me.filters.items); + } + } + }, + + + addFilter: function(filters, applyFilters) { + var me = this, + decoded, + i, + length; + + + decoded = me.decodeFilters(filters); + length = decoded.length; + for (i = 0; i < length; i++) { + me.filters.replace(decoded[i]); + } + + if (applyFilters !== false && me.filters.length) { + me.filter(); + } else { + me.fireEvent('filterchange', me, me.filters.items); + } + }, + + + isFiltered: function() { + var snapshot = this.snapshot; + return !!(snapshot && snapshot !== this.data); + }, + + + filterBy: function(fn, scope) { + var me = this; + + me.snapshot = me.snapshot || me.data.clone(); + me.data = me.queryBy(fn, scope || me); + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + }, + + + queryBy: function(fn, scope) { + var me = this; + return (me.snapshot || me.data).filterBy(fn, scope || me); + }, + + + query: function(property, value, anyMatch, caseSensitive, exactMatch) { + var me = this, + queryFn = me.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch), + results = me.queryBy(queryFn); + + + if(!results) { + results = new Ext.util.MixedCollection(); + } + + return results; + }, + + + loadData: function(data, append) { + var length = data.length, + newData = [], + i; + + + for (i = 0; i < length; i++) { + newData.push(this.createModel(data[i])); + } + + this.loadRecords(newData, append ? this.addRecordsOptions : undefined); + }, + + + loadRawData : function(data, append) { + var me = this, + result = me.proxy.reader.read(data), + records = result.records; + + if (result.success) { + me.totalCount = result.total; + me.loadRecords(records, append ? me.addRecordsOptions : undefined); + } + }, + + + loadRecords: function(records, options) { + var me = this, + i = 0, + length = records.length, + start, + addRecords, + snapshot = me.snapshot; + + if (options) { + start = options.start; + addRecords = options.addRecords; + } + + if (!addRecords) { + delete me.snapshot; + me.clearData(true); + } else if (snapshot) { + snapshot.addAll(records); + } + + me.data.addAll(records); + + if (start !== undefined) { + for (; i < length; i++) { + records[i].index = start + i; + records[i].join(me); + } + } else { + for (; i < length; i++) { + records[i].join(me); + } + } + + + me.suspendEvents(); + + if (me.filterOnLoad && !me.remoteFilter) { + me.filter(); + } + + if (me.sortOnLoad && !me.remoteSort) { + me.sort(); + } + + me.resumeEvents(); + if (me.isGrouped()) { + me.constructGroups(); + } + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + }, + + + + loadPage: function(page, options) { + var me = this; + + me.currentPage = page; + + + options = Ext.apply({ + page: page, + start: (page - 1) * me.pageSize, + limit: me.pageSize, + addRecords: !me.clearOnPageLoad + }, options); + + if (me.buffered) { + options.limit = me.viewSize || me.defaultViewSize; + options.loadCallback = options.callback; + delete options.callback; + return me.loadToPrefetch(options); + } + me.read(options); + }, + + + nextPage: function(options) { + this.loadPage(this.currentPage + 1, options); + }, + + + previousPage: function(options) { + this.loadPage(this.currentPage - 1, options); + }, + + + clearData: function(isLoad, data) { + var me = this, + records, + i; + + data = data || me.data; + + + + + + + if (!me.buffered && data) { + records = data.items; + i = records.length; + while (i--) { + records[i].unjoin(me); + } + } + + + if (data) { + data.clear(); + } + + if (isLoad !== true || me.clearRemovedOnLoad) { + me.removed.length = 0; + } + }, + + destroyClear: function() { + + + this.clearData(null, this.snapshot); + }, + + loadToPrefetch: function(options) { + var me = this, + i, + records, + dataSetSize, + prefetchOptions = options, + + + startIdx = options.start, + endIdx = options.start + options.limit - 1, + + + loadEndIdx = Math.min(endIdx, options.start + (me.viewSize || options.limit) - 1), + + + + startPage = me.getPageFromRecordIndex(Math.max(startIdx - me.trailingBufferZone, 0)), + endPage = me.getPageFromRecordIndex(endIdx + me.leadingBufferZone), + + + waitForRequestedRange = function() { + if (me.rangeCached(startIdx, loadEndIdx)) { + me.loading = false; + records = me.data.getRange(startIdx, loadEndIdx); + me.data.un('pageAdded', waitForRequestedRange); + + + if (me.hasListeners.guaranteedrange) { + me.guaranteeRange(startIdx, loadEndIdx, options.callback, options.scope); + } + + + if (options.loadCallback) { + options.loadCallback.call(options.scope || me, records, operation, true); + } + + if (options.callback) { + options.callback.call(options.scope||me, records, startIdx, endIdx, options); + } + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + me.fireEvent('load', me, records, true); + if (options.groupChange) { + me.fireGroupChange(); + } + } + }, operation; + + if (isNaN(me.pageSize) || !me.pageSize) { + Ext.Error.raise('Buffered store configured without a pageSize', me); + } + + + + if (me.purgePageCount) { + me.data.maxSize = me.purgePageCount = Math.max(me.purgePageCount, endPage - startPage + 1); + } + + if (me.fireEvent('beforeload', me, options) !== false) { + + + delete me.totalCount; + + me.loading = true; + + + + if (options.callback) { + prefetchOptions = Ext.apply({}, options); + delete prefetchOptions.callback; + } + + + + + + me.on('prefetch', function(store, records, successful, op) { + if (successful) { + + operation = op; + + + if ((dataSetSize = me.getTotalCount())) { + + + me.data.on('pageAdded', waitForRequestedRange); + + + loadEndIdx = Math.min(loadEndIdx, dataSetSize - 1); + + + endPage = me.getPageFromRecordIndex(Math.min(loadEndIdx + me.leadingBufferZone, dataSetSize - 1)); + + for (i = startPage + 1; i <= endPage; ++i) { + me.prefetchPage(i, prefetchOptions); + } + } else { + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + me.fireEvent('load', me, records, true); + } + } + + else { + me.fireEvent('load', me, records, false); + } + }, null, {single: true}); + + me.prefetchPage(startPage, prefetchOptions); + } + }, + + + + prefetch: function(options) { + var me = this, + pageSize = me.pageSize, + proxy, + operation; + + + if (pageSize) { + if (me.lastPageSize && pageSize != me.lastPageSize) { + Ext.Error.raise("pageSize cannot be dynamically altered"); + } + if (!me.data.pageSize) { + me.data.pageSize = pageSize; + } + } + + + else { + me.pageSize = me.data.pageSize = pageSize = options.limit; + } + + + me.lastPageSize = pageSize; + + + if (!options.page) { + options.page = me.getPageFromRecordIndex(options.start); + options.start = (options.page - 1) * pageSize; + options.limit = Math.ceil(options.limit / pageSize) * pageSize; + } + + + if (!me.pageRequests[options.page]) { + + + options = Ext.apply({ + action : 'read', + filters: me.filters.items, + sorters: me.sorters.items, + groupers: me.groupers.items, + + + + pageMapGeneration: me.data.pageMapGeneration + }, options); + + operation = new Ext.data.Operation(options); + + if (me.fireEvent('beforeprefetch', me, operation) !== false) { + proxy = me.proxy; + me.pageRequests[options.page] = proxy.read(operation, me.onProxyPrefetch, me); + if (proxy.isSynchronous) { + delete me.pageRequests[options.page]; + } + } + } + + return me; + }, + + + onPageMapClear: function() { + var me = this, + loadingFlag = me.wasLoading, + reqs = me.pageRequests, + req, + page; + + + if (me.data.events.pageadded) { + me.data.events.pageadded.clearListeners(); + } + + + + + me.loading = true; + me.totalCount = 0; + + + for (page in reqs) { + if (reqs.hasOwnProperty(page)) { + req = reqs[page]; + delete reqs[page]; + delete req.callback; + } + } + + + me.fireEvent('clear', me); + + + + me.loading = loadingFlag; + }, + + + prefetchPage: function(page, options) { + var me = this, + pageSize = me.pageSize || me.defaultPageSize, + start = (page - 1) * me.pageSize, + total = me.totalCount; + + + if (total !== undefined && me.getCount() === total) { + return; + } + + + me.prefetch(Ext.applyIf({ + page : page, + start : start, + limit : pageSize + }, options)); + }, + + + onProxyPrefetch: function(operation) { + var me = this, + resultSet = operation.getResultSet(), + records = operation.getRecords(), + successful = operation.wasSuccessful(), + page = operation.page; + + + + if (operation.pageMapGeneration === me.data.pageMapGeneration) { + + if (resultSet) { + me.totalCount = resultSet.total; + me.fireEvent('totalcountchange', me.totalCount); + } + + + if (page !== undefined) { + delete me.pageRequests[page]; + } + + + me.loading = false; + me.fireEvent('prefetch', me, records, successful, operation); + + + + if (successful) { + me.cachePage(records, operation.page); + } + + + Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]); + } + }, + + + cachePage: function(records, page) { + var me = this, + len = records.length, i; + + if (!Ext.isDefined(me.totalCount)) { + me.totalCount = records.length; + me.fireEvent('totalcountchange', me.totalCount); + } + + + for (i = 0; i < len; i++) { + records[i].join(me); + } + me.data.addPage(page, records); + }, + + + rangeCached: function(start, end) { + return this.data && this.data.hasRange(start, end); + }, + + + pageCached: function(page) { + return this.data && this.data.hasPage(page); + }, + + + pagePending: function(page) { + return !!this.pageRequests[page]; + }, + + + rangeSatisfied: function(start, end) { + return this.rangeCached(start, end); + }, + + + getPageFromRecordIndex: function(index) { + return Math.floor(index / this.pageSize) + 1; + }, + + + onGuaranteedRange: function(options) { + var me = this, + totalCount = me.getTotalCount(), + start = options.prefetchStart, + end = (options.prefetchEnd > totalCount - 1) ? totalCount - 1 : options.prefetchEnd, + range; + + end = Math.max(0, end); + + if (start > end) { + Ext.log({ + level: 'warn', + msg: 'Start (' + start + ') was greater than end (' + end + + ') for the range of records requested (' + start + '-' + + options.prefetchEnd + ')' + (this.storeId ? ' from store "' + this.storeId + '"' : '') + }); + } + + range = me.data.getRange(start, end); + if (options.fireEvent !== false) { + me.fireEvent('guaranteedrange', range, start, end, options); + } + if (options.callback) { + options.callback.call(options.scope || me, range, start, end, options); + } + }, + + + guaranteeRange: function(start, end, callback, scope, options) { + options = Ext.apply({ + callback: callback, + scope: scope + }, options); + this.getRange(start, end, options) + }, + + + prefetchRange: function(start, end) { + var me = this, + startPage, endPage, page; + if (!me.rangeCached(start, end)) { + startPage = me.getPageFromRecordIndex(start); + endPage = me.getPageFromRecordIndex(end); + + + + + me.data.maxSize = me.purgePageCount ? (endPage - startPage + 1) + me.purgePageCount : 0; + + + for (page = startPage; page <= endPage; page++) { + if (!me.pageCached(page)) { + me.prefetchPage(page); + } + } + } + }, + + primeCache: function(start, end, direction) { + var me = this; + + + if (direction === -1) { + start = Math.max(start - me.leadingBufferZone, 0); + end = Math.min(end + me.trailingBufferZone, me.totalCount - 1); + } + + else if (direction === 1) { + start = Math.max(Math.min(start - me.trailingBufferZone, me.totalCount - me.pageSize), 0); + end = Math.min(end + me.leadingBufferZone, me.totalCount - 1); + } + + else { + start = Math.min(Math.max(Math.floor(start - ((me.leadingBufferZone + me.trailingBufferZone) / 2)), 0), me.totalCount - me.pageSize); + end = Math.min(Math.max(Math.ceil (end + ((me.leadingBufferZone + me.trailingBufferZone) / 2)), 0), me.totalCount - 1); + } + me.prefetchRange(start, end); + }, + + + sort: function(sorters) { + var me = this; + + + if (sorters && me.buffered && me.remoteSort) { + me.data.clear(); + } + return me.callParent(arguments); + }, + + + generateComparator: function() { + var me = this, + sorters = me.sorters.items, + numSorters = sorters.length, + + groupers = numSorters ? me.groupers.getRange() : me.groupers.items, + i, + sorter, matchingGrouper; + + + + if (groupers.length) { + for (i = 0; i < numSorters; i++) { + sorter = sorters[i]; + + + + if (sorter.property && (matchingGrouper = me.groupers.get(sorter.property))) { + matchingGrouper.setDirection(sorter.direction); + } + + + else { + Ext.Array.push(groupers, sorter); + } + } + sorters = groupers; + } + + return sorters.length ? this.createComparator(sorters) : this.emptyComparator; + }, + + + getSorterCount: function() { + return this.groupers.items.length + this.sorters.items.length; + }, + + + doSort: function(sorterFn) { + var me = this, + range, + ln, + i, + afterSort = function () { + me.fireEvent('sort', me, me.sorters.getRange()); + }; + + if (me.remoteSort) { + + + + if (me.buffered) { + me.data.clear(); + me.loadPage(1, { + callback: afterSort + }); + } else { + + me.load({ + callback: afterSort + }); + } + } else { + if (me.buffered) { + Ext.Error.raise({ + msg: 'Local sorting may not be used on a buffered store' + }); + } + me.data.sortBy(sorterFn); + if (!me.buffered) { + range = me.getRange(); + ln = range.length; + for (i = 0; i < ln; i++) { + range[i].index = i; + } + } + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + afterSort(); + } + }, + + + find: function(property, value, start, anyMatch, caseSensitive, exactMatch) { + var fn = this.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch); + return fn ? this.data.findIndexBy(fn, null, start) : -1; + }, + + + findRecord: function() { + var me = this, + index = me.find.apply(me, arguments); + return index !== -1 ? me.getAt(index) : null; + }, + + + createFilterFn: function(property, value, anyMatch, caseSensitive, exactMatch) { + if (Ext.isEmpty(value)) { + return false; + } + + + value = Ext.util.AbstractMixedCollection.prototype.createValueMatcher(value, anyMatch, caseSensitive, exactMatch); + return function(r) { + return value.test(r.data[property]); + }; + }, + + + findExact: function(property, value, start) { + return this.data.findIndexBy(function(rec) { + return rec.isEqual(rec.get(property), value); + }, + this, start); + }, + + + findBy: function(fn, scope, start) { + return this.data.findIndexBy(fn, scope, start); + }, + + + collect: function(dataIndex, allowNull, bypassFilter) { + var me = this, + data = (bypassFilter === true && me.snapshot) ? me.snapshot : me.data; + + return data.collect(dataIndex, 'data', allowNull); + }, + + + getCount: function() { + return this.data.getCount(); + }, + + + getTotalCount: function() { + return this.totalCount || 0; + }, + + + getAt: function(index) { + return this.data.getAt(index); + }, + + + getRange: function(start, end, options) { + if (options && options.cb) { + options.callback = options.cb; + Ext.Error.raise({ + msg: 'guaranteeRange options.cb is deprecated, use options.callback' + }); + } + + var me = this, + requiredStart, + requiredEnd, + maxIndex = me.totalCount - 1, + lastRequestStart = me.lastRequestStart, + result = [], + pageAddHandler; + + options = Ext.apply({ + prefetchStart: start, + prefetchEnd: end + }, options); + + if (me.buffered) { + + end = (end >= me.totalCount) ? maxIndex : end; + + + + + + requiredStart = start === 0 ? 0 : start - 1; + requiredEnd = end === maxIndex ? end : end + 1; + + + me.lastRequestStart = start; + me.lastRequestEnd = end; + + + if (me.rangeCached(requiredStart, requiredEnd)) { + me.onGuaranteedRange(options); + result = me.data.getRange(start, end); + } + + else { + + me.fireEvent('cachemiss', me, start, end); + + + pageAddHandler = function(page, records) { + if (me.rangeCached(requiredStart, requiredEnd)) { + + me.fireEvent('cachefilled', me, start, end); + me.data.un('pageAdded', pageAddHandler); + me.onGuaranteedRange(options); + } + }; + me.data.on('pageAdded', pageAddHandler); + + + + + me.prefetchRange(start, end); + + } + + me.primeCache(start, end, start < lastRequestStart ? -1 : 1); + } else { + result = me.data.getRange(start, end); + + + if (options.callback) { + options.callback.call(options.scope || me, result, start, end, options) + } + } + + return result; + }, + + + getById: function(id) { + var result = (this.snapshot || this.data).findBy(function(record) { + return record.getId() === id; + }); + if (this.buffered && !result) { + Ext.Error.raise('getById called for ID that is not present in local cache'); + } + return result; + }, + + + getByInternalId: function(internalId) { + var result; + + if (this.buffered) { + result = (this.snapshot || this.data).findBy(function(record) { + return record.internalId === internalId; + }); + if (!result) { + Ext.Error.raise('getByInternalId called for internalId that is not present in local cache'); + } + } else { + result = this.data.get(internalId); + } + return result; + }, + + + indexOf: function(record) { + return this.data.indexOf(record); + }, + + + indexOfTotal: function(record) { + var index = record.index; + if (index || index === 0) { + return index; + } + return this.indexOf(record); + }, + + + indexOfId: function(id) { + return this.indexOf(this.getById(id)); + }, + + + + + first: function(grouped) { + var me = this; + + if (grouped && me.isGrouped()) { + return me.aggregate(function(records) { + return records.length ? records[0] : undefined; + }, me, true); + } else { + return me.data.first(); + } + }, + + + last: function(grouped) { + var me = this; + + if (grouped && me.isGrouped()) { + return me.aggregate(function(records) { + var len = records.length; + return len ? records[len - 1] : undefined; + }, me, true); + } else { + return me.data.last(); + } + }, + + + sum: function(field, grouped) { + var me = this; + + if (grouped && me.isGrouped()) { + return me.aggregate(me.getSum, me, true, [field]); + } else { + return me.getSum(me.data.items, field); + } + }, + + + getSum: function(records, field) { + var total = 0, + i = 0, + len = records.length; + + for (; i < len; ++i) { + total += records[i].get(field); + } + + return total; + }, + + + count: function(grouped) { + var me = this; + + if (grouped && me.isGrouped()) { + return me.aggregate(function(records) { + return records.length; + }, me, true); + } else { + return me.getCount(); + } + }, + + + min: function(field, grouped) { + var me = this; + + if (grouped && me.isGrouped()) { + return me.aggregate(me.getMin, me, true, [field]); + } else { + return me.getMin(me.data.items, field); + } + }, + + + getMin: function(records, field) { + var i = 1, + len = records.length, + value, min; + + if (len > 0) { + min = records[0].get(field); + } + + for (; i < len; ++i) { + value = records[i].get(field); + if (value < min) { + min = value; + } + } + return min; + }, + + + max: function(field, grouped) { + var me = this; + + if (grouped && me.isGrouped()) { + return me.aggregate(me.getMax, me, true, [field]); + } else { + return me.getMax(me.data.items, field); + } + }, + + + getMax: function(records, field) { + var i = 1, + len = records.length, + value, + max; + + if (len > 0) { + max = records[0].get(field); + } + + for (; i < len; ++i) { + value = records[i].get(field); + if (value > max) { + max = value; + } + } + return max; + }, + + + average: function(field, grouped) { + var me = this; + if (grouped && me.isGrouped()) { + return me.aggregate(me.getAverage, me, true, [field]); + } else { + return me.getAverage(me.data.items, field); + } + }, + + + getAverage: function(records, field) { + var i = 0, + len = records.length, + sum = 0; + + if (records.length > 0) { + for (; i < len; ++i) { + sum += records[i].get(field); + } + return sum / len; + } + return 0; + }, + + + aggregate: function(fn, scope, grouped, args) { + args = args || []; + if (grouped && this.isGrouped()) { + var groups = this.getGroups(), + len = groups.length, + out = {}, + group, i; + + for (i = 0; i < len; ++i) { + group = groups[i]; + out[group.name] = this.getAggregate(fn, scope || this, group.children, args); + } + return out; + } else { + return this.getAggregate(fn, scope, this.data.items, args); + } + }, + + getAggregate: function(fn, scope, records, args){ + args = args || []; + return fn.apply(scope || this, [records].concat(args)); + }, + + onIdChanged: function(rec, oldId, newId, oldInternalId){ + var snapshot = this.snapshot; + if (snapshot) { + snapshot.updateKey(oldInternalId, newId); + } + this.data.updateKey(oldInternalId, newId); + this.callParent(arguments); + }, + + + commitChanges : function(){ + var me = this, + recs = me.getModifiedRecords(), + len = recs.length, + i = 0; + + for (; i < len; i++){ + recs[i].commit(); + } + + + + me.removed.length = 0; + }, + + filterNewOnly: function(item){ + return item.phantom === true; + }, + + + + + getRejectRecords: function() { + + return Ext.Array.push(this.data.filterBy(this.filterNewOnly).items, this.getUpdatedRecords()); + }, + + + rejectChanges : function() { + var me = this, + recs = me.getRejectRecords(), + len = recs.length, + i = 0, + rec; + + for (; i < len; i++) { + rec = recs[i]; + rec.reject(); + if (rec.phantom) { + me.remove(rec); + } + } + + + recs = me.removed; + len = recs.length; + for (i = len-1; i >= 0; i--) { + rec = recs[i]; + me.insert(rec.removedFrom || 0, rec); + rec.reject(); + } + + + + me.removed.length = 0; + } +}, function() { + + + + Ext.regStore('ext-empty-store', {fields: [], proxy: 'memory'}); +}); + + + +Ext.define('Ext.data.NodeInterface', { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + statics: { + + decorate: function(modelClass) { + var idName, idField, idType; + + + if (typeof modelClass == 'string') { + modelClass = Ext.ModelManager.getModel(modelClass); + } else if (modelClass.isModel) { + modelClass = Ext.ModelManager.getModel(modelClass.modelName); + } + + + if (modelClass.prototype.isNode) { + return; + } + + idName = modelClass.prototype.idProperty; + idField = modelClass.prototype.fields.get(idName); + idType = modelClass.prototype.fields.get(idName).type.type; + + modelClass.override(this.getPrototypeBody()); + this.applyFields(modelClass, [ + { name : 'parentId', type : idType, defaultValue : null, useNull : idField.useNull }, + { name : 'index', type : 'int', defaultValue : -1, persist : false , convert: null }, + { name : 'depth', type : 'int', defaultValue : 0, persist : false , convert: null }, + { name : 'expanded', type : 'bool', defaultValue : false, persist : false , convert: null }, + { name : 'expandable', type : 'bool', defaultValue : true, persist : false , convert: null }, + { name : 'checked', type : 'auto', defaultValue : null, persist : false , convert: null }, + { name : 'leaf', type : 'bool', defaultValue : false }, + { name : 'cls', type : 'string', defaultValue : '', persist : false , convert: null }, + { name : 'iconCls', type : 'string', defaultValue : '', persist : false , convert: null }, + { name : 'icon', type : 'string', defaultValue : '', persist : false , convert: null }, + { name : 'root', type : 'boolean', defaultValue : false, persist : false , convert: null }, + { name : 'isLast', type : 'boolean', defaultValue : false, persist : false , convert: null }, + { name : 'isFirst', type : 'boolean', defaultValue : false, persist : false , convert: null }, + { name : 'allowDrop', type : 'boolean', defaultValue : true, persist : false , convert: null }, + { name : 'allowDrag', type : 'boolean', defaultValue : true, persist : false , convert: null }, + { name : 'loaded', type : 'boolean', defaultValue : false, persist : false , convert: null }, + { name : 'loading', type : 'boolean', defaultValue : false, persist : false , convert: null }, + { name : 'href', type : 'string', defaultValue : '', persist : false , convert: null }, + { name : 'hrefTarget', type : 'string', defaultValue : '', persist : false , convert: null }, + { name : 'qtip', type : 'string', defaultValue : '', persist : false , convert: null }, + { name : 'qtitle', type : 'string', defaultValue : '', persist : false , convert: null }, + { name : 'qshowDelay', type : 'int', defaultValue : 0, persist : false , convert: null }, + { name : 'children', type : 'auto', defaultValue : null, persist : false , convert: null }, + { name : 'visible', type : 'boolean', defaultValue : true, persist : false , convert: null } + ]); + }, + + applyFields: function(modelClass, addFields) { + var modelPrototype = modelClass.prototype, + fields = modelPrototype.fields, + keys = fields.keys, + ln = addFields.length, + addField, i; + + for (i = 0; i < ln; i++) { + addField = addFields[i]; + if (!Ext.Array.contains(keys, addField.name)) { + fields.add(new Ext.data.Field(addField)); + } + } + + }, + + getPrototypeBody: function() { + var bubbledEvents = { + idchanged : true, + append : true, + remove : true, + bulkremove : true, + move : true, + insert : true, + beforeappend : true, + beforeremove : true, + beforemove : true, + beforeinsert : true, + expand : true, + collapse : true, + beforeexpand : true, + beforecollapse: true, + sort : true, + rootchange : true + }; + return { + + isNode: true, + + constructor: function() { + var me = this; + me.callParent(arguments); + me.firstChild = me.lastChild = me.parentNode = me.previousSibling = me.nextSibling = null; + me.childNodes = []; + + + + + + + + + + + + + + + + + return me; + }, + + createNode: function(node) { + var me = this; + + + if (!node.isModel) { + node = new this.self(node); + } + + + + if (!node.childNodes) { + node.firstChild = node.lastChild = node.parentNode = node.previousSibling = node.nextSibling = null; + node.childNodes = []; + } + return node; + }, + + + isLeaf : function() { + return this.get('leaf') === true; + }, + + + setFirstChild : function(node) { + this.firstChild = node; + }, + + + setLastChild : function(node) { + this.lastChild = node; + }, + + + updateInfo: function(commit, info) { + var me = this, + oldDepth = me.data.depth, + childInfo = {}, + children = me.childNodes, + childCount = children.length, + i, + phantom = me.phantom, + dataObject = me[me.persistenceProperty], + fields = me.fields, + modified = me.modified, + propName, newValue, + field, currentValue, key, + newParentId = info.parentId, + settingIndexInNewParent, + persistentField; + + if (!info) { + Ext.Error.raise('NodeInterface expects update info to be passed'); + } + + + + + for (propName in info) { + field = fields.get(propName); + newValue = info[propName]; + persistentField = field && field.persist; + + currentValue = dataObject[propName]; + + + + + + + settingIndexInNewParent = persistentField && (propName === 'index') && (currentValue !== -1) && (newParentId && newParentId !== modified.parentId); + + + if (!settingIndexInNewParent && me.isEqual(currentValue, newValue)) { + continue; + } + dataObject[propName] = newValue; + + + if (persistentField) { + + + if (!settingIndexInNewParent && modified.hasOwnProperty(propName)) { + + + if (me.isEqual(modified[propName], newValue)) { + + + delete modified[propName]; + + + + + me.dirty = false; + for (key in modified) { + if (modified.hasOwnProperty(key)){ + me.dirty = true; + break; + } + } + } + } + + + else { + me.dirty = true; + modified[propName] = currentValue; + } + } + } + if (commit) { + me.commit(); + me.phantom = phantom; + } + + + if (me.data.depth !== oldDepth) { + childInfo = { + depth: me.data.depth + 1 + }; + for (i = 0; i < childCount; i++) { + children[i].updateInfo(commit, childInfo); + } + } + }, + + + isLast : function() { + return this.get('isLast'); + }, + + + isFirst : function() { + return this.get('isFirst'); + }, + + + hasChildNodes : function() { + return !this.isLeaf() && this.childNodes.length > 0; + }, + + + isExpandable : function() { + var me = this; + + if (me.get('expandable')) { + return !(me.isLeaf() || (me.isLoaded() && !me.phantom && !me.hasChildNodes())); + } + return false; + }, + + triggerUIUpdate: function() { + + + this.afterEdit([]); + }, + + + appendChild: function(node, suppressEvents, commit) { + var me = this, + i, ln, + index, + oldParent, + previousSibling, + childInfo = { + isLast: true, + parentId: me.getId(), + depth: (me.data.depth||0) + 1 + }; + + + if (Ext.isArray(node)) { + + me.callStore('suspendAutoSync'); + for (i = 0, ln = node.length - 1; i < ln; i++) { + me.appendChild(node[i], suppressEvents, commit); + } + + me.callStore('resumeAutoSync'); + me.appendChild(node[ln], suppressEvents, commit); + } else { + + node = me.createNode(node); + + if (suppressEvents !== true && me.fireEventArgs('beforeappend', [me, node]) === false) { + return false; + } + + index = me.childNodes.length; + oldParent = node.parentNode; + + + if (oldParent) { + if (suppressEvents !== true && node.fireEventArgs('beforemove', [node, oldParent, me, index]) === false) { + return false; + } + oldParent.removeChild(node, false, false, true); + } + + + if (Ext.suspendLayouts) { + Ext.suspendLayouts(); + } + + index = me.childNodes.length; + if (index === 0) { + me.setFirstChild(node); + } + + me.childNodes[index] = node; + node.parentNode = me; + node.nextSibling = null; + + me.setLastChild(node); + + previousSibling = me.childNodes[index - 1]; + if (previousSibling) { + node.previousSibling = previousSibling; + previousSibling.nextSibling = node; + previousSibling.updateInfo(commit, { + isLast: false + }); + previousSibling.triggerUIUpdate(); + } else { + node.previousSibling = null; + } + + + childInfo.isFirst = index === 0; + childInfo.index = index; + node.updateInfo(commit, childInfo); + + + if (me.isLeaf()) { + me.set('leaf', false); + } + + + if (!me.isLoaded()) { + me.set('loaded', true); + } else if (me.childNodes.length === 1) { + me.triggerUIUpdate(); + } + + + if (index && me.childNodes[index - 1].isExpanded()) { + me.childNodes[index - 1].cascadeBy(me.triggerUIUpdate); + } + + + if (Ext.resumeLayouts) { + Ext.resumeLayouts(true); + } + + if (suppressEvents !== true) { + me.fireEventArgs('append', [me, node, index]); + + if (oldParent) { + node.fireEventArgs('move', [node, oldParent, me, index]); + } + } + + + me.callStore('onNodeAdded', node, index); + + return node; + } + }, + + + getOwnerTree: function() { + var node = this, + store; + + while (node.parentNode) { + node = node.parentNode; + } + store = node.store; + if (store) { + if (store.treeStore) { + store = store.treeStore; + } + + if (store.tree) { + return store.ownerTree; + } + } + return undefined; + }, + + + getTreeStore: function() { + var store = this.stores[0]; + + + return store.treeStore || store; + }, + + + removeChild : function(node, destroy, suppressEvents, isMove) { + var me = this, + index = me.indexOf(node), + i, childCount, + previousSibling; + + if (index === -1 || (suppressEvents !== true && me.fireEventArgs('beforeremove', [me, node, !!isMove]) === false)) { + return false; + } + + + if (Ext.suspendLayouts) { + Ext.suspendLayouts(); + } + + + Ext.Array.erase(me.childNodes, index, 1); + + + if (me.firstChild === node) { + me.setFirstChild(node.nextSibling); + } + if (me.lastChild === node) { + me.setLastChild(node.previousSibling); + } + + + previousSibling = node.previousSibling + if (previousSibling) { + node.previousSibling.nextSibling = node.nextSibling; + } + + + if (node.nextSibling) { + node.nextSibling.previousSibling = node.previousSibling; + + + if (index === 0) { + node.nextSibling.updateInfo(false, { + isFirst: true + }); + } + + + for (i = index, childCount = me.childNodes.length; i < childCount; i++) { + me.childNodes[i].updateInfo(false, { + index: i + }); + } + } + + + + else if (previousSibling) { + previousSibling.updateInfo(false, { + isLast: true + }); + + + + if (previousSibling.isExpanded()) { + previousSibling.cascadeBy(me.triggerUIUpdate); + } + + else { + previousSibling.triggerUIUpdate(); + } + } + + + if (!me.childNodes.length) { + me.triggerUIUpdate(); + } + + + if (Ext.resumeLayouts) { + Ext.resumeLayouts(true); + } + + if (suppressEvents !== true) { + + node.removeContext = { + parentNode: node.parentNode, + previousSibling: node.previousSibling, + nextSibling: node.nextSibling + }; + + node.previousSibling = node.nextSibling = node.parentNode = null; + + me.fireEventArgs('remove', [me, node, !!isMove]); + + + me.callStore('onNodeRemove', node, !!isMove); + + + node.removeContext = null; + } + + + + if (destroy) { + node.destroy(true); + } else { + node.clear(); + } + + return node; + }, + + + copy: function(newId, deep) { + var me = this, + result = me.callParent(arguments), + len = me.childNodes ? me.childNodes.length : 0, + i; + + + if (deep) { + for (i = 0; i < len; i++) { + result.appendChild(me.childNodes[i].copy(undefined, true)); + } + } + return result; + }, + + + clear : function(destroy) { + var me = this; + + + me.parentNode = me.previousSibling = me.nextSibling = null; + if (destroy) { + me.firstChild = me.lastChild = null; + } + }, + + + destroy : function(silent) { + + var me = this, + options = me.destroyOptions, + nodes = me.childNodes, + nLen = nodes.length, + n; + + if (silent === true) { + me.clear(true); + + for (n = 0; n < nLen; n++) { + nodes[n].destroy(true); + } + + me.childNodes = null; + delete me.destroyOptions; + me.callParent([options]); + } else { + me.destroyOptions = silent; + + me.remove(true); + } + }, + + + insertBefore: function(node, refNode, suppressEvents) { + var me = this, + index = me.indexOf(refNode), + oldParent = node.parentNode, + refIndex = index, + childCount, previousSibling, i; + + if (!refNode) { + return me.appendChild(node); + } + + + if (node === refNode) { + return false; + } + + + node = me.createNode(node); + + if (suppressEvents !== true && me.fireEventArgs('beforeinsert', [me, node, refNode]) === false) { + return false; + } + + + if (oldParent === me && me.indexOf(node) < index) { + refIndex--; + } + + + if (oldParent) { + if (suppressEvents !== true && node.fireEventArgs('beforemove', [node, oldParent, me, index, refNode]) === false) { + return false; + } + oldParent.removeChild(node, false, false, true); + } + + if (refIndex === 0) { + me.setFirstChild(node); + } + + Ext.Array.splice(me.childNodes, refIndex, 0, node); + node.parentNode = me; + + node.nextSibling = refNode; + refNode.previousSibling = node; + + previousSibling = me.childNodes[refIndex - 1]; + if (previousSibling) { + node.previousSibling = previousSibling; + previousSibling.nextSibling = node; + } else { + node.previousSibling = null; + } + + + node.updateInfo(false, { + parentId: me.getId(), + index: refIndex, + isFirst: refIndex === 0, + isLast: false, + depth: (me.data.depth||0) + 1 + }); + + + for (i = refIndex + 1, childCount = me.childNodes.length; i < childCount; i++) { + me.childNodes[i].updateInfo(false, { + index: i + }); + } + + if (!me.isLoaded()) { + me.set('loaded', true); + } + + else if (me.childNodes.length === 1) { + me.triggerUIUpdate(); + } + + if (suppressEvents !== true) { + me.fireEventArgs('insert', [me, node, refNode]); + + if (oldParent) { + node.fireEventArgs('move', [node, oldParent, me, refIndex, refNode]); + } + } + + + me.callStore('onNodeAdded', node, refNode); + + return node; + }, + + + insertChild: function(index, node) { + var sibling = this.childNodes[index]; + if (sibling) { + return this.insertBefore(node, sibling); + } + else { + return this.appendChild(node); + } + }, + + + remove : function(destroy, suppressEvents) { + var me = this, + parentNode = me.parentNode; + + if (parentNode) { + parentNode.removeChild(me, destroy, suppressEvents); + } else if (destroy) { + + me.destroy(true); + } + return me; + }, + + + removeAll : function(destroy, suppressEvents, fromParent) { + + + + var me = this, + childNodes = me.childNodes, + i = 0, + len = childNodes.length, + node; + + + if (!len) { + return; + } + + + + me.fireEventArgs('bulkremove', [me, childNodes, false]); + + for (; i < len; ++i) { + node = childNodes[i]; + + + node.removeContext = { + parentNode: node.parentNode, + previousSibling: node.previousSibling, + nextSibling: node.nextSibling + }; + + node.previousSibling = node.nextSibling = node.parentNode = null; + + me.fireEventArgs('remove', [me, node, false]); + + + me.callStore('onNodeRemove', node, false); + + + node.removeContext = null; + + + if (destroy) { + node.destroy(true); + } + + else { + node.removeAll(false, suppressEvents, true); + } + } + + me.firstChild = me.lastChild = null; + + + if (fromParent) { + + me.childNodes = null; + } else { + + me.childNodes.length = 0; + me.triggerUIUpdate(); + } + + return me; + }, + + + getChildAt : function(index) { + return this.childNodes[index]; + }, + + + replaceChild : function(newChild, oldChild, suppressEvents) { + var s = oldChild ? oldChild.nextSibling : null; + + this.removeChild(oldChild, false, suppressEvents); + this.insertBefore(newChild, s, suppressEvents); + return oldChild; + }, + + + indexOf : function(child) { + return Ext.Array.indexOf(this.childNodes, child); + }, + + + indexOfId: function(id) { + var childNodes = this.childNodes, + len = childNodes.length, + i = 0; + + for (; i < len; ++i) { + if (childNodes[i].getId() === id) { + return i; + } + } + return -1; + }, + + + getPath: function(field, separator) { + field = field || this.idProperty; + separator = separator || '/'; + + var path = [this.get(field)], + parent = this.parentNode; + + while (parent) { + path.unshift(parent.get(field)); + parent = parent.parentNode; + } + return separator + path.join(separator); + }, + + + getDepth : function() { + return this.get('depth'); + }, + + + bubble : function(fn, scope, args) { + var p = this; + while (p) { + if (fn.apply(scope || p, args || [p]) === false) { + break; + } + p = p.parentNode; + } + }, + + + + cascadeBy : function(before, scope, args, after) { + var me = this; + + if (arguments.length === 1 && !Ext.isFunction(before)) { + after = before.after; + scope = before.scope; + args = before.args; + before = before.before; + } + if (!before || before.apply(scope || me, args || [me]) !== false) { + var childNodes = me.childNodes, + length = childNodes.length, + i; + + for (i = 0; i < length; i++) { + childNodes[i].cascadeBy.call(childNodes[i], before, scope, args, after); + } + + if (after) { + after.apply(scope || me, args || [me]); + } + } + }, + + + eachChild : function(fn, scope, args) { + var childNodes = this.childNodes, + length = childNodes.length, + i; + + for (i = 0; i < length; i++) { + if (fn.apply(scope || this, args || [childNodes[i]]) === false) { + break; + } + } + }, + + + findChild : function(attribute, value, deep) { + return this.findChildBy(function() { + return this.get(attribute) == value; + }, null, deep); + }, + + + findChildBy : function(fn, scope, deep) { + var cs = this.childNodes, + len = cs.length, + i = 0, n, res; + + for (; i < len; i++) { + n = cs[i]; + if (fn.call(scope || n, n) === true) { + return n; + } + else if (deep) { + res = n.findChildBy(fn, scope, deep); + if (res !== null) { + return res; + } + } + } + + return null; + }, + + + contains : function(node) { + return node.isAncestor(this); + }, + + + isAncestor : function(node) { + var p = this.parentNode; + while (p) { + if (p === node) { + return true; + } + p = p.parentNode; + } + return false; + }, + + + sort: function(sortFn, recursive, suppressEvent) { + var me = this, + childNodes = me.childNodes, + ln = childNodes.length, + i, n, info = { + isFirst: true + }; + + if (ln > 0) { + if (!sortFn) { + sortFn = me.getTreeStore().generateComparator(); + } + Ext.Array.sort(childNodes, sortFn); + me.setFirstChild(childNodes[0]); + me.setLastChild(childNodes[ln - 1]); + + for (i = 0; i < ln; i++) { + n = childNodes[i]; + n.previousSibling = childNodes[i-1]; + n.nextSibling = childNodes[i+1]; + + + info.isLast = (i === ln - 1); + info.index = i; + n.updateInfo(false, info); + info.isFirst = false; + + if (recursive && !n.isLeaf()) { + n.sort(sortFn, true, true); + } + } + + + if (suppressEvent !== true) { + me.fireEventArgs('sort', [me, childNodes]); + + + me.callStore('onNodeSort', childNodes); + } + } + }, + + + isExpanded: function() { + return this.get('expanded'); + }, + + + isLoaded: function() { + return this.get('loaded'); + }, + + + isLoading: function() { + return this.get('loading'); + }, + + + isRoot: function() { + return !this.parentNode; + }, + + + isVisible: function() { + var parent = this.parentNode; + while (parent) { + if (!parent.isExpanded()) { + return false; + } + parent = parent.parentNode; + } + return true; + }, + + + expand: function(recursive, callback, scope) { + var me = this; + + + + + + if (!me.isLeaf()) { + + if (me.isLoading()) { + me.on('expand', function() { + me.expand(recursive, callback, scope); + }, me, {single: true}); + } else { + + if (!me.isExpanded()) { + + me.fireEventArgs('beforeexpand', [me]); + + + + me.callStore('onBeforeNodeExpand', me.onChildNodesAvailable, me, [recursive, callback, scope]); + + } else if (recursive) { + + me.expandChildren(true, callback, scope); + } else { + Ext.callback(callback, scope || me, [me.childNodes]); + } + } + } else { + + Ext.callback(callback, scope || me); + } + }, + + + onChildNodesAvailable: function(records, recursive, callback, scope) { + var me = this; + + + + if (Ext.suspendLayouts) { + Ext.suspendLayouts(); + } + + + me.set('expanded', true); + + + me.fireEventArgs('expand', [me, me.childNodes, false]); + + + if (recursive) { + me.expandChildren(true, callback, scope); + } else { + Ext.callback(callback, scope || me, [me.childNodes]); + } + + if (Ext.resumeLayouts) { + Ext.resumeLayouts(true); + } + }, + + + expandChildren: function(recursive, callback, scope, singleExpand) { + var me = this, + origCallback, i, allNodes, expandNodes, ln, node; + + + + + + if (Ext.isBoolean(callback)) { + origCallback = callback; + callback = scope; + scope = singleExpand; + singleExpand = origCallback; + } + + if (singleExpand === undefined) { + singleExpand = me.getTreeStore().singleExpand; + } + allNodes = me.childNodes; + expandNodes = []; + ln = singleExpand ? Math.min(allNodes.length, 1) : allNodes.length; + + for (i = 0; i < ln; ++i) { + node = allNodes[i]; + if (!node.isLeaf()) { + expandNodes[expandNodes.length] = node; + } + } + ln = expandNodes.length; + + for (i = 0; i < ln; ++i) { + expandNodes[i].expand(recursive); + } + + if (callback) { + Ext.callback(callback, scope || me, [me.childNodes]); + } + }, + + + collapse: function(recursive, callback, scope) { + var me = this, + expanded = me.isExpanded(), + len = me.childNodes.length, + i, collapseChildren; + + + + + + if (!me.isLeaf() && ((!expanded && recursive) || me.fireEventArgs('beforecollapse', [me]) !== false)) { + + + + if (Ext.suspendLayouts) { + Ext.suspendLayouts(); + } + + + if (me.isExpanded()) { + + + + + + + if (recursive) { + collapseChildren = function() { + for (i = 0; i < len; i++) { + me.childNodes[i].setCollapsed(true); + } + }; + if (callback) { + callback = Ext.Function.createSequence(collapseChildren, callback); + } else { + callback = collapseChildren; + } + } + + + me.set('expanded', false); + + + + + me.fireEventArgs('collapse', [me, me.childNodes, false, callback ? Ext.Function.bind(callback, scope, [me.childNodes]) : null, null]); + + + callback = null; + } + + + + + + else if (recursive) { + for (i = 0; i < len; i++) { + me.childNodes[i].setCollapsed(true); + } + } + + if (Ext.resumeLayouts) { + Ext.resumeLayouts(true); + } + } + + + Ext.callback(callback, scope || me, [me.childNodes]); + }, + + + setCollapsed: function(recursive) { + var me = this, + len = me.childNodes.length, + i; + + + if (!me.isLeaf() && me.fireEventArgs('beforecollapse', [me, Ext.emptyFn]) !== false) { + + + me.data.expanded = false; + + + + + me.fireEventArgs('collapse', [me, me.childNodes, false, null, null]); + + if (recursive) { + for (i = 0; i < len; i++) { + me.childNodes[i].setCollapsed(true); + } + } + } + }, + + + collapseChildren: function(recursive, callback, scope) { + var me = this, + i, + allNodes = me.childNodes, + ln = allNodes.length, + collapseNodes = [], + node; + + + for (i = 0; i < ln; ++i) { + node = allNodes[i]; + if (!node.isLeaf() && node.isLoaded() && node.isExpanded()) { + collapseNodes.push(node); + } + } + ln = collapseNodes.length; + + if (ln) { + + + for (i = 0; i < ln; ++i) { + node = collapseNodes[i]; + if (i === ln - 1) { + node.collapse(recursive, callback, scope); + } else { + node.collapse(recursive); + } + } + } else { + + Ext.callback(callback, scope); + } + }, + + + + + fireEventArgs: function(eventName, args) { + + + + var fireEventArgs = Ext.data.Model.prototype.fireEventArgs, + result, eventSource, tree, treeStore, rootNode; + + + if (bubbledEvents[eventName]) { + for (eventSource = this; result !== false && eventSource; eventSource = (rootNode = eventSource).parentNode) { + if (eventSource.hasListeners[eventName]) { + result = fireEventArgs.call(eventSource, eventName, args); + } + } + + + tree = rootNode.rootOf; + if (result !== false && tree) { + treeStore = tree.treeStore; + if (treeStore && treeStore.hasListeners[eventName]) { + result = treeStore.fireEventArgs.call(treeStore, eventName, args); + } + if (result !== false && tree.hasListeners[eventName]) { + result = tree.fireEventArgs.call(tree, eventName, args); + } + } + return result; + } + + else { + return fireEventArgs.apply(this, arguments) + } + }, + + + serialize: function() { + var result = Ext.data.writer.Json.prototype.getRecordData(this), + childNodes = this.childNodes, + len = childNodes.length, + children, i; + + if (len > 0) { + children = []; + for (i = 0; i < len; i++) { + children.push(childNodes[i].serialize()); + } + result.children = children; + } + return result; + } + }; + } + } +}); + + + +Ext.define('Ext.data.NodeStore', { + extend: Ext.data.Store , + alias: 'store.node', + + + + isNodeStore: true, + + + node: null, + + + recursive: false, + + + rootVisible: false, + + + + + isExpandingOrCollapsing: 0, + + constructor: function(config) { + var me = this, + node; + + config = config || {}; + Ext.apply(me, config); + + if (Ext.isDefined(me.proxy)) { + Ext.Error.raise("A NodeStore cannot be bound to a proxy. Instead bind it to a record " + + "decorated with the NodeInterface by setting the node config."); + } + me.useModelWarning = false; + + config.proxy = {type: 'proxy'}; + me.callParent([config]); + + node = me.node; + if (node) { + me.node = null; + me.setNode(node); + } + }, + + + + + getTotalCount: function() { + return this.getCount(); + }, + + setNode: function(node) { + var me = this; + if (me.node && me.node != node) { + + me.mun(me.node, { + expand: me.onNodeExpand, + collapse: me.onNodeCollapse, + append: me.onNodeAppend, + insert: me.onNodeInsert, + bulkremove: me.onBulkRemove, + remove: me.onNodeRemove, + sort: me.onNodeSort, + filterchange: me.onNodeFilter, + scope: me + }); + me.node = null; + } + + if (node) { + Ext.data.NodeInterface.decorate(node.self); + me.removeAll(); + if (me.rootVisible) { + me.add(node); + } else if (!node.isExpanded() && me.treeStore.autoLoad !== false) { + node.expand(); + } + + me.mon(node, { + expand: me.onNodeExpand, + collapse: me.onNodeCollapse, + append: me.onNodeAppend, + insert: me.onNodeInsert, + bulkremove: me.onBulkRemove, + remove: me.onNodeRemove, + sort: me.onNodeSort, + filterchange: me.onNodeFilter, + scope: me + }); + me.node = node; + if (node.isExpanded() && node.isLoaded()) { + me.onNodeExpand(node, node.childNodes, true); + } + } + }, + + onNodeFilter: function(root, childNodes) { + var me = this, + toAdd = []; + + this.data.clear(); + me.handleNodeExpand(root, childNodes, toAdd); + this.data.addAll(toAdd); + this.fireEvent('refresh', this); + }, + + onNodeSort: function(node, childNodes) { + var me = this; + + if ((me.indexOf(node) !== -1 || (node === me.node && !me.rootVisible) && node.isExpanded())) { + Ext.suspendLayouts(); + me.onNodeCollapse(node, childNodes, true); + me.onNodeExpand(node, childNodes, true); + Ext.resumeLayouts(true); + } + }, + + + onNodeExpand: function(parent, records, suppressEvent) { + var me = this, + insertIndex = me.indexOf(parent) + 1, + toAdd = []; + + + + + if (!suppressEvent) { + me.fireEvent('beforeexpand', parent, records, insertIndex); + } + + me.handleNodeExpand(parent, records, toAdd); + + + + + me.insert(insertIndex, toAdd); + + + + if (!suppressEvent) { + me.fireEvent('expand', parent, records); + } + }, + + + + handleNodeExpand: function(parent, records, toAdd) { + var me = this, + ln = records ? records.length : 0, + i, record; + + + if (!me.recursive && parent !== me.node) { + return; + } + + if (parent !== this.node && !me.isVisible(parent)) { + return; + } + + if (ln) { + + + for (i = 0; i < ln; i++) { + record = records[i]; + + + if (record.get('visible')) { + + + + toAdd.push(record); + + if (record.isExpanded()) { + if (record.isLoaded()) { + + me.handleNodeExpand(record, record.childNodes, toAdd); + } + else { + + record.set('expanded', false); + record.expand(); + } + } + } + } + } + }, + + + onBulkRemove: function(parent, childNodes, isMove) { + this.onNodeCollapse(parent, childNodes, true); + }, + + + onNodeCollapse: function(parent, records, suppressEvent, callback, scope) { + var me = this, + collapseIndex = me.indexOf(parent) + 1, + node, lastNodeIndexPlus, sibling, found; + + if (!me.recursive && parent !== me.node) { + return; + } + + if (parent.store.filterFn) { + records = Ext.Array.filter(records, me.filterVisible); + } + + + + + + if (!suppressEvent) { + me.fireEvent('beforecollapse', parent, records, collapseIndex, callback, scope); + } + + + + + + if (records.length && me.data.contains(records[0])) { + + + + + node = parent; + while (node.parentNode) { + + for (sibling = node.nextSibling; sibling && !sibling.get('visible'); sibling = sibling.nextSibling); + if (sibling) { + found = true; + lastNodeIndexPlus = me.indexOf(sibling); + break; + } else { + node = node.parentNode; + } + } + if (!found) { + lastNodeIndexPlus = me.getCount(); + } + + + me.removeAt(collapseIndex, lastNodeIndexPlus - collapseIndex); + } + + + + if (!suppressEvent) { + me.fireEvent('collapse', parent, records, collapseIndex); + } + }, + + onNodeAppend: function(parent, node, index) { + var me = this, + refNode, sibling; + + + if (me.isVisible(node)) { + if (index === 0) { + refNode = parent; + } else { + + for (sibling = node.previousSibling; sibling && !sibling.get('visible'); sibling = sibling.previousSibling); + while (sibling.isExpanded() && sibling.lastChild) { + sibling = sibling.lastChild; + } + refNode = sibling; + } + me.insert(me.indexOf(refNode) + 1, node); + if (!node.isLeaf() && node.isExpanded()) { + if (node.isLoaded()) { + + me.onNodeExpand(node, node.childNodes, true); + } else if (!me.treeStore.fillCount ) { + + + + + node.set('expanded', false); + node.join(me.treeStore); + node.expand(); + } + } + } + }, + + onNodeInsert: function(parent, node, refNode) { + var me = this, + index = this.indexOf(refNode); + + if (index !== -1 && me.isVisible(node)) { + me.insert(index, node); + if (!node.isLeaf() && node.isExpanded()) { + if (node.isLoaded()) { + + me.onNodeExpand(node, node.childNodes, true); + } + else { + node.set('expanded', false); + node.join(me.treeStore); + node.expand(); + } + } + } + }, + + onNodeRemove: function(parent, node, isMove) { + var me = this; + if (me.indexOf(node) != -1) { + + + + if (!node.isLeaf() && node.isExpanded()) { + + + + + + node.parentNode = node.removeContext.parentNode; + node.nextSibling = node.removeContext.nextSibling; + me.onNodeCollapse(node, node.childNodes, true); + node.parentNode = node.nextSibling = null; + } + me.remove(node); + } + }, + + filterVisible: function(node) { + return node.get('visible'); + }, + + isVisible: function(node) { + if (node.get('visible')) { + var parent = node.parentNode; + while (parent) { + + if (parent === this.node && parent.data.expanded) { + return true; + } + + + if (!parent.data.expanded) { + return false; + } + + parent = parent.parentNode; + } + } + + return false; + } +}); + + + +Ext.define('Ext.tree.View', { + extend: Ext.view.Table , + alias: 'widget.treeview', + + + + + + + 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', + + + + + nodeAnimWrapCls: Ext.baseCSSPrefix + 'tree-animator-wrap', + + ariaRole: 'tree', + + + loadMask: false, + + + rootVisible: true, + + + deferInitialRefresh: false, + + + + expandDuration: 250, + collapseDuration: 250, + + toggleOnDblClick: true, + + stripeRows: false, + + + uiFields: ['expanded', 'loaded', 'checked', 'expandable', 'leaf', 'icon', 'iconCls', 'loading', 'qtip', 'qtitle'], + + + treeRowTpl: [ + '{%', + 'this.processRowValues(values);', + 'this.nextTpl.applyOut(values, out, parent);', + '%}', { + priority: 10, + processRowValues: function(rowValues) { + var record = rowValues.record, + view = rowValues.view; + + + + + 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); + } + } + } + ], + + initComponent: function() { + var me = this, + treeStore = me.panel.getStore(), + store = me.store; + + if (me.initialConfig.animate === undefined) { + me.animate = Ext.enableFx; + } + + if (!store || store === treeStore) { + me.store = store = new Ext.data.NodeStore({ + treeStore: treeStore, + recursive: true, + rootVisible: me.rootVisible + }); + } + + if (me.node) { + me.setRootNode(me.node); + } + me.animQueue = {}; + me.animWraps = {}; + me.addEvents( + + 'afteritemexpand', + + 'afteritemcollapse', + + 'nodedragover' + ); + me.callParent(arguments); + me.addRowTpl(Ext.XTemplate.getTpl(me, 'treeRowTpl')); + }, + + onBeforeFill: function(treeStore, fillRoot) { + this.store.suspendEvents(); + }, + + onFillComplete: function(treeStore, fillRoot, newNodes) { + var me = this, + store = me.store, + start = store.indexOf(newNodes[0]); + + store.resumeEvents(); + + + + fillRoot.triggerUIUpdate(); + + + + if (!newNodes.length || start === -1) { + return; + } + + + me.onAdd(me.store, newNodes, start); + + me.refreshPartner(); + }, + + onBeforeSort: function() { + this.store.suspendEvents(); + }, + + onSort: function(o) { + + + if (o.isStore) { + this.store.resumeEvents(); + this.refresh(); + this.refreshPartner(); + } + }, + + refreshPartner: function() { + var partner = this.lockingPartner; + if (partner) { + partner.refresh(); + } + }, + + getMaskStore: function() { + return this.panel.getStore(); + }, + + afterRender: function() { + var me = this; + me.callParent(arguments); + + me.el.on({ + scope: me, + delegate: me.expanderSelector, + mouseover: me.onExpanderMouseOver, + mouseout: me.onExpanderMouseOut, + click: { + delegate: me.checkboxSelector, + fn: me.onCheckboxChange, + scope: me + } + }); + }, + + afterComponentLayout: function() { + this.callParent(arguments); + var stretcher = this.stretcher; + if (stretcher) { + stretcher.setWidth((this.getWidth() - Ext.getScrollbarSize().width)); + } + }, + + processUIEvent: function(e) { + + + + if (e.getTarget('.' + this.nodeAnimWrapCls, this.el)) { + return false; + } + return this.callParent(arguments); + }, + + onClear: function() { + this.store.removeAll(); + }, + + setRootNode: function(node) { + var me = this; + me.store.setNode(node); + me.node = node; + }, + + onCheckboxChange: function(e, t) { + var me = this, + item = e.getTarget(me.getItemSelector(), me.getTargetEl()); + + if (item) { + me.onCheckChange(me.getRecord(item)); + } + }, + + onCheckChange: function(record) { + var checked = record.get('checked'); + if (Ext.isBoolean(checked)) { + checked = !checked; + record.set('checked', checked); + this.fireEvent('checkchange', record, checked); + } + }, + + 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'); + }, + + + createAnimWrap: function(record, index) { + var me = this, + + node = me.getNode(record, !me.isRowWrapped), + tmpEl, nodeEl, + columnSizer = []; + + me.renderColumnSizer(columnSizer); + nodeEl = Ext.get(node); + tmpEl = nodeEl.insertSibling({ + role: 'presentation', + tag: 'tr', + html: [ + '', + '', + '' + ].join('') + }, 'after'); + + return { + record: record, + node: node, + el: tmpEl, + expanding: false, + collapsing: false, + animating: false, + animateEl: tmpEl.down('div'), + targetEl: tmpEl.down('tbody') + }; + }, + + + 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, + nodes = me.bufferRender(records, index, true), + record = records[0], + parent = record.parentNode, + all = me.all, + relativeIndex, + animWrap = me.getAnimWrap(parent), + targetEl, children, len; + + if (!animWrap || !animWrap.expanding) { + return me.callParent(arguments); + } + + + parent = animWrap.record; + + + targetEl = animWrap.targetEl; + children = targetEl.dom.childNodes; + len = children.length; + + + relativeIndex = index - me.indexInStore(parent) - 1; + + + + if (!len || relativeIndex >= len) { + targetEl.appendChild(nodes); + } + + + else { + Ext.fly(children[relativeIndex]).insertSibling(nodes, 'before', true); + } + + + all.insert(index, nodes); + + + + if (animWrap.isAnimating) { + me.onExpand(parent); + } + }, + + onRemove : function(ds, records, indexes) { + var me = this, + empty, i; + + if (me.viewReady) { + empty = me.store.getCount() === 0; + + + if (empty) { + me.refresh(); + } + else { + + for (i = indexes.length - 1; i >= 0; --i) { + me.doRemove(records[i], indexes[i]); + } + } + + + if (me.hasListeners.itemremove) { + for (i = indexes.length - 1; i >= 0; --i) { + me.fireEvent('itemremove', records[i], indexes[i]); + } + } + } + }, + + doRemove: function(record, index) { + + + 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(arguments); + } + + + + 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) { + + + animWrap.targetEl.select(me.itemSelector).remove(); + } + 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, + fromHeight = Ext.isIEQuirks ? 1 : 0 + + if (me.singleExpand) { + me.ensureSingleExpand(parent); + } + + + if (index === -1) { + return; + } + + animWrap = me.getAnimWrap(parent, false); + + if (!animWrap) { + me.refreshSelection(); + parent.isExpandingOrCollapsing = false; + me.fireEvent('afteritemexpand', parent, index, node); + me.refreshSize(); + return; + } + + animateEl = animWrap.animateEl; + targetEl = animWrap.targetEl; + + animateEl.stopAnimation(); + queue[id] = true; + + + animateEl.dom.style.height = fromHeight + 'px'; + animateEl.animate({ + from: { + height: fromHeight + }, + to: { + height: targetEl.getHeight() + }, + duration: me.expandDuration, + listeners: { + afteranimate: function() { + + + + var items = targetEl.query(me.itemSelector); + if (items.length) { + animWrap.el.insertSibling(items, 'before', true); + } + animWrap.el.remove(); + me.refreshSize(); + delete me.animWraps[animWrap.record.internalId]; + delete queue[id]; + } + }, + callback: function() { + me.refreshSelection(); + parent.isExpandingOrCollapsing = false; + me.fireEvent('afteritemexpand', parent, index, node); + } + }); + + animWrap.isAnimating = true; + }, + + + onBeforeCollapse: function(parent, records, index, callback, scope) { + var me = this, + animWrap; + + if (me.rendered && me.all.getCount()) { + if (me.animate) { + + + + if (Ext.Array.contains(parent.stores, me.store)) { + animWrap = me.getAnimWrap(parent); + if (!animWrap) { + animWrap = me.animWraps[parent.internalId] = me.createAnimWrap(parent, index); + } + else if (animWrap.expanding) { + + + animWrap.targetEl.select(this.itemSelector).remove(); + } + animWrap.expanding = false; + animWrap.collapsing = true; + animWrap.callback = callback; + animWrap.scope = scope; + } + } else { + + me.onCollapseCallback = callback; + me.onCollapseScope = scope; + } + + + me.onRowDeselect(me.indexOf(parent.firstChild)); + } + }, + + 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 (!me.all.getCount() || !Ext.Array.contains(parent.stores, me.store)) { + return; + } + + + if (!animWrap) { + me.refreshSelection(); + parent.isExpandingOrCollapsing = false; + me.fireEvent('afteritemcollapse', parent, index, node); + me.refreshSize(); + + + Ext.callback(me.onCollapseCallback, me.onCollapseScope); + me.onCollapseCallback = me.onCollapseScope = null; + return; + } + + animateEl = animWrap.animateEl; + + queue[id] = true; + + animateEl.stopAnimation(); + animateEl.animate({ + to: { + height: Ext.isIEQuirks ? 1 : 0 + }, + duration: me.collapseDuration, + listeners: { + afteranimate: function() { + + animWrap.el.remove(); + + if (!me.isDestroyed) { + me.refreshSize(); + } + + delete me.animWraps[animWrap.record.internalId]; + delete queue[id]; + } + }, + callback: function() { + me.refreshSelection(); + parent.isExpandingOrCollapsing = false; + me.fireEvent('afteritemcollapse', parent, index, node); + + + Ext.callback(animWrap.callback, animWrap.scope); + animWrap.callback = animWrap.scope = null; + } + }); + animWrap.isAnimating = true; + }, + + + isAnimating: function(node) { + return !!this.animQueue[node.getId()]; + }, + + + expand: function(record, deep, callback, scope) { + var me = this, + doAnimate = !!me.animate, + result; + + + if (!doAnimate || !record.isExpandingOrCollapsing) { + if (!record.isLeaf()) { + record.isExpandingOrCollapsing = doAnimate; + } + + + + + Ext.suspendLayouts(); + result = record.expand(deep, callback, scope); + Ext.resumeLayouts(true); + return result; + } + }, + + + collapse: function(record, deep, callback, scope) { + var me = this, + doAnimate = !!me.animate; + + + if (!doAnimate || !record.isExpandingOrCollapsing) { + if (!record.isLeaf()) { + record.isExpandingOrCollapsing = doAnimate; + } + return record.collapse(deep, callback, scope); + } + }, + + + 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) { + var me = this, + editingPlugin = me.editingPlugin; + + me.callParent(arguments); + if (me.toggleOnDblClick && record.isExpandable() && !(editingPlugin && editingPlugin.clicksToEdit === 2)) { + me.toggle(record); + } + }, + + onBeforeItemMouseDown: function(record, item, index, e) { + if (e.getTarget(this.expanderSelector, item)) { + return false; + } + return this.callParent(arguments); + }, + + onItemClick: function(record, item, index, e) { + if (e.getTarget(this.expanderSelector, item) && record.isExpandable()) { + this.toggle(record, e.ctrlKey); + return false; + } + return this.callParent(arguments); + }, + + onExpanderMouseOver: function(e, t) { + e.getTarget(this.cellSelector, 10, true).addCls(this.expanderIconOverCls); + }, + + onExpanderMouseOut: function(e, t) { + e.getTarget(this.cellSelector, 10, true).removeCls(this.expanderIconOverCls); + }, + + getStoreListeners: function(){ + var me = this, + listeners = me.callParent(arguments); + + return Ext.apply(listeners, { + beforeexpand: me.onBeforeExpand, + expand: me.onExpand, + beforecollapse: me.onBeforeCollapse, + collapse: me.onCollapse, + write: me.onStoreWrite, + datachanged: me.onStoreDataChanged + }); + }, + + onBindStore: function(){ + var me = this, + treeStore = me.getTreeStore(); + + me.callParent(arguments); + + me.mon(treeStore, { + scope: me, + beforefill: me.onBeforeFill, + fillcomplete: me.onFillComplete + }); + + if (!treeStore.remoteSort) { + + + me.mon(treeStore, { + scope: me, + beforesort: me.onBeforeSort, + sort: me.onSort + }); + } + }, + + onUnbindStore: function(){ + var me = this, + treeStore = me.getTreeStore(); + + me.callParent(arguments); + + me.mun(treeStore, { + scope: me, + beforefill: me.onBeforeFill, + fillcomplete: me.onFillComplete + }); + + if (!treeStore.remoteSort) { + me.mun(treeStore, { + scope: me, + beforesort: me.onBeforeSort, + sort: me.onSort + }); + } + }, + + + getTreeStore: function() { + return this.panel.store; + }, + + ensureSingleExpand: function(node) { + var parent = node.parentNode; + if (parent) { + parent.eachChild(function(child) { + if (child !== node && child.isExpanded()) { + child.collapse(); + } + }); + } + }, + + shouldUpdateCell: function(record, column, changedFieldNames){ + if (changedFieldNames) { + var i = 0, + len = changedFieldNames.length; + + for (; i < len; ++i) { + if (Ext.Array.contains(this.uiFields, changedFieldNames[i])) { + return true; + } + } + } + return this.callParent(arguments); + }, + + + onStoreWrite: function(store, operation) { + var treeStore = this.panel.store; + treeStore.fireEvent('write', treeStore, operation); + }, + + + onStoreDataChanged: function(store, operation) { + var treeStore = this.panel.store; + treeStore.fireEvent('datachanged', treeStore); + } +}); + + + +Ext.define('Ext.selection.RowModel', { + extend: Ext.selection.Model , + alias: 'selection.rowmodel', + + + + deltaScroll: 5, + + + enableKeyNav: true, + + + ignoreRightMouseSelection: false, + + isRowModel: true, + + constructor: function() { + this.addEvents( + + 'beforedeselect', + + + 'beforeselect', + + + 'deselect', + + + 'select' + ); + this.views = []; + this.callParent(arguments); + }, + + bindComponent: function(view) { + var me = this; + + view.on({ + + + + itemcontextmenu: me.onRowClick, + itemclick: me.onRowClick, + scope: me + }); + + if (me.enableKeyNav) { + me.initKeyNav(view); + } + }, + + initKeyNav: function(view) { + var me = this; + + if (!view.rendered) { + view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true}); + return; + } + + + + view.el.set({ + tabIndex: -1 + }); + + + me.keyNav = new Ext.util.KeyNav({ + target: view, + ignoreInputFields: true, + eventName: 'itemkeydown', + processEvent: function(view, record, node, index, event) { + event.record = record; + event.recordIndex = index; + return event; + }, + 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, + A: { + ctrl: true, + handler: me.selectAll + }, + scope: me + }); + }, + + 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); + } + } + }, + + + + + getRowsVisible: function() { + var rowsVisible = false, + view = this.views[0], + firstRow = view.all.first(), + rowHeight, gridViewHeight; + + if (firstRow) { + rowHeight = firstRow.getHeight(); + gridViewHeight = view.el.getHeight(); + rowsVisible = Math.floor(gridViewHeight / rowHeight); + } + + return rowsVisible; + }, + + + onKeyEnd: function(e) { + var me = this, + view = me.views[0]; + + if (view.bufferedRenderer) { + + + + view.bufferedRenderer.scrollTo(me.store.getCount() - 1, false, function(newIdx, newRecord) { + me.afterKeyNavigate(e, newRecord); + }); + } else { + + me.afterKeyNavigate(e, view.walkRecs(e.record, view.dataSource.getCount() - 1 - view.dataSource.indexOf(e.record))); + } + }, + + + onKeyHome: function(e) { + var me = this, + view = me.views[0]; + + if (view.bufferedRenderer) { + + + + view.bufferedRenderer.scrollTo(0, false, function(newIdx, newRecord) { + me.afterKeyNavigate(e, newRecord); + }); + } else { + + me.afterKeyNavigate(e, view.walkRecs(e.record, -view.dataSource.indexOf(e.record))); + } + }, + + + onKeyPageUp: function(e) { + var me = this, + view = me.views[0], + rowsVisible = me.getRowsVisible(), + newIdx, + newRecord; + + if (rowsVisible) { + + + + if (view.bufferedRenderer) { + newIdx = Math.max(e.recordIndex - rowsVisible, 0); + (me.lastKeyEvent || (me.lastKeyEvent = new Ext.EventObjectImpl())).setEvent(e.browserEvent); + view.bufferedRenderer.scrollTo(newIdx, false, me.afterBufferedScrollTo, me); + } else { + newRecord = view.walkRecs(e.record, -rowsVisible); + me.afterKeyNavigate(e, newRecord); + } + } + }, + + + onKeyPageDown: function(e) { + var me = this, + view = me.views[0], + rowsVisible = me.getRowsVisible(), + newIdx, + newRecord; + + if (rowsVisible) { + + + + if (view.bufferedRenderer) { + newIdx = Math.min(e.recordIndex + rowsVisible, me.store.getCount() - 1); + (me.lastKeyEvent || (me.lastKeyEvent = new Ext.EventObjectImpl())).setEvent(e.browserEvent); + view.bufferedRenderer.scrollTo(newIdx, false, me.afterBufferedScrollTo, me); + } else { + newRecord = view.walkRecs(e.record, rowsVisible); + me.afterKeyNavigate(e, newRecord); + } + } + }, + + + onKeySpace: function(e) { + + var record = e.record || this.lastFocused; + + if (record) { + this.afterKeyNavigate(e, record); + } + }, + + onKeyEnter: Ext.emptyFn, + + + + + onKeyUp: function(e) { + var newRecord = this.views[0].walkRecs(e.record, -1); + + if (newRecord) { + this.afterKeyNavigate(e, newRecord); + } + }, + + + + + onKeyDown: function(e) { + + + var newRecord = e.record.isExpandingOrCollapsing ? null : this.views[0].walkRecs(e.record, 1); + + if (newRecord) { + this.afterKeyNavigate(e, newRecord); + } + }, + + afterBufferedScrollTo: function(newIdx, newRecord) { + this.afterKeyNavigate(this.lastKeyEvent, newRecord); + }, + + scrollByDeltaX: function(delta) { + var view = this.views[0], + section = view.up(), + hScroll = section.horizontalScroller; + + if (hScroll) { + hScroll.scrollByDeltaX(delta); + } + }, + + onKeyLeft: function(e) { + this.scrollByDeltaX(-this.deltaScroll); + }, + + onKeyRight: function(e) { + this.scrollByDeltaX(this.deltaScroll); + }, + + + + onRowClick: function (view, record, item, index, e) { + + if (index !== -1) { + if (!this.allowRightMouseSelection(e)) { + return; + } + + + if (!(e.type === 'contextmenu' && this.isSelected(record))) { + this.processSelection(view, record, item, index, e); + } + } + }, + + + processSelection: function(view, record, item, index, e) { + this.selectWithEvent(record, e); + }, + + + allowRightMouseSelection: function(e) { + var disallow = this.ignoreRightMouseSelection && e.button !== 0; + if (disallow) { + disallow = this.hasSelection(); + } + return !disallow; + }, + + + + onSelectChange: function(record, isSelected, suppressEvent, commitFn) { + var me = this, + views = me.views, + viewsLn = views.length, + rowIdx = views[0].indexOf(record), + eventName = isSelected ? 'select' : 'deselect', + i = 0; + + if ((suppressEvent || me.fireEvent('before' + eventName, me, record, rowIdx)) !== false && + commitFn() !== false) { + + for (; i < viewsLn; i++) { + if (isSelected) { + views[i].onRowSelect(rowIdx, suppressEvent); + } else { + views[i].onRowDeselect(rowIdx, suppressEvent); + } + } + + if (!suppressEvent) { + me.fireEvent(eventName, me, record, rowIdx); + } + } + }, + + + + onLastFocusChanged: function(oldFocused, newFocused, supressFocus) { + var views = this.views, + viewsLn = views.length, + rowIdx, + i = 0; + + if (oldFocused) { + rowIdx = views[0].indexOf(oldFocused); + if (rowIdx !== -1) { + for (; i < viewsLn; i++) { + views[i].onRowFocus(rowIdx, false, true); + } + } + } + + if (newFocused) { + rowIdx = views[0].indexOf(newFocused); + if (rowIdx !== -1) { + for (i = 0; i < viewsLn; i++) { + views[i].onRowFocus(rowIdx, true, supressFocus); + } + } + } + this.callParent(arguments); + }, + + onEditorTab: function(editingPlugin, e) { + var me = this, + view = editingPlugin.context.view, + record = editingPlugin.getActiveRecord(), + header = editingPlugin.getActiveColumn(), + position = view.getPosition(record, header), + direction = e.shiftKey ? 'left' : 'right', + lastPos; + + + + + + + do { + lastPos = position; + position = view.walkCells(position, direction, e, me.preventWrap); + if (lastPos && lastPos.isEqual(position)) { + + + + return; + } + } while (position && (!position.columnHeader.getEditor(record) || !editingPlugin.startEditByPosition(position))); + }, + + + 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) { + var context = new Ext.grid.CellContext(this.view); + + context.setPosition(position.row, position.column); + this.select(context.record, keepExisting); + }, + + + selectNext: function(keepExisting, suppressEvent) { + var me = this, + store = me.store, + selection = me.getSelection(), + record = selection[selection.length - 1], + index = me.views[0].indexOf(record) + 1, + success; + + if (index === store.getCount() || index === 0) { + success = false; + } else { + me.doSelect(index, keepExisting, suppressEvent); + success = true; + } + return success; + }, + + + selectPrevious: function(keepExisting, suppressEvent) { + var me = this, + selection = me.getSelection(), + record = selection[0], + index = me.views[0].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); + } +}); + + + +Ext.define('Ext.selection.TreeModel', { + extend: Ext.selection.RowModel , + alias: 'selection.treemodel', + + + + constructor: function(config) { + this.callParent(arguments); + + + + if (this.pruneRemoved) { + this.pruneRemoved = false; + this.pruneRemovedNodes = true; + } + }, + + + bindStore: function(store, initial) { + var me = this; + me.callParent(arguments); + + + + if (me.pruneRemovedNodes) { + me.view.mon(me.treeStore, { + remove: me.onNodeRemove, + scope: me + }); + } + }, + + onNodeRemove: function(parent, node, isMove) { + + if (!isMove) { + this.deselectDeletedRecords([node]); + } + }, + + onKeyRight: function(e, t) { + this.navExpand(e, t); + }, + + navExpand: function(e, t) { + var me = this, + focused = me.getLastFocused(), + view = me.view; + + if (focused) { + + + + if (focused.isExpanded()) { + me.onKeyDown(e, t); + + } else if (focused.isExpandable()) { + + if (!view.isTreeView) { + view = view.lockingPartner; + } + + view.expand(focused); + if (focused) { + me.onLastFocusChanged(null, focused); + } + } + } + }, + + onKeyLeft: function(e, t) { + this.navCollapse(e, t); + }, + + navCollapse: function(e, t) { + var me = this, + focused = me.getLastFocused(), + view = me.view, + parentNode; + + if (focused) { + parentNode = focused.parentNode; + + if (focused.isExpanded()) { + + if (!view.isTreeView) { + view = view.lockingPartner; + } + + view.collapse(focused); + me.onLastFocusChanged(null, focused); + + + } else if (parentNode && !parentNode.isRoot()) { + + if (e.shiftKey) { + me.selectRange(parentNode, focused, e.ctrlKey, 'up'); + me.setLastFocused(parentNode); + + } else if (e.ctrlKey) { + me.setLastFocused(parentNode); + + } else { + me.select(parentNode); + } + } + this.onLastFocusChanged(null, focused); + } + }, + + onKeySpace: function(e, t) { + if (e.record.data.checked != null) { + this.toggleCheck(e); + } else { + this.callParent(arguments); + } + }, + + onKeyEnter: function(e, t) { + if (e.record.data.checked != null) { + this.toggleCheck(e); + } else { + this.callParent(arguments); + } + }, + + toggleCheck: function(e) { + var view = this.view, + selected = this.getLastSelected(); + + e.stopEvent(); + if (selected) { + + if (!view.isTreeView) { + view = view.lockingPartner; + } + + view.onCheckChange(selected); + } + } +}); + + + +Ext.define('Ext.grid.ColumnLayout', { + extend: Ext.layout.container.HBox , + alias: 'layout.gridcolumn', + type : 'gridcolumn', + + reserveOffset: false, + + firstHeaderCls: Ext.baseCSSPrefix + 'column-header-first', + lastHeaderCls: Ext.baseCSSPrefix + 'column-header-last', + + initLayout: function() { + var me = this; + if (me.scrollbarWidth === undefined) { + me.self.prototype.scrollbarWidth = Ext.getScrollbarSize().width; + } + + + me.grid = me.owner.up('tablepanel'); + + + + + me.controllingGrid = me.owner.up('[scrollerOwner]'); + me.callParent(); + }, + + + beginLayout: function (ownerContext) { + var me = this, + owner = me.owner, + grid = me.controllingGrid, + view = me.grid.view, + items = me.getVisibleItems(), + len = items.length, + firstCls = me.firstHeaderCls, + lastCls = me.lastHeaderCls, + removeCls = [firstCls, lastCls], + i, item; + + + + if (!view.scrollFlags.y) { + me.scrollbarWidth = 0; + } + + else { + delete me.scrollbarWidth; + } + + for (i = 0; i < len; i++) { + item = items[i]; + item.margin = null; + item.removeCls(removeCls); + if (i === 0) { + item.addCls(firstCls); + } + + if (i === len - 1) { + item.addCls(lastCls); + } + } + + + + + + + + + + + + + + + + + + me.scrollbarAdjustment = me.scrollbarWidth; + + me.callParent(arguments); + + + + if (!owner.isColumn && !grid.collapsed && view && view.rendered && (ownerContext.viewTable = view.body.dom) && me.scrollbarWidth) { + ownerContext.viewContext = ownerContext.context.getCmp(view); + } + }, + + + + + + injectViewContext: function(ownerContext, view) { + if (!this.controllingGrid.collapsed && view.rendered && (ownerContext.viewTable = view.body.dom) && this.scrollbarWidth) { + ownerContext.viewContext = ownerContext.context.getCmp(view); + } + }, + + roundFlex: function(width) { + return Math.floor(width); + }, + + calculate: function(ownerContext) { + this.callParent(arguments); + + if (ownerContext.state.parallelDone && !(this.owner.forceFit && !ownerContext.state.flexesCalculated)) { + ownerContext.setProp('columnWidthsDone', true); + } + + + if (ownerContext.viewContext) { + ownerContext.state.tableHeight = ownerContext.viewTable.offsetHeight; + } + }, + + completeLayout: function(ownerContext) { + var me = this, + owner = me.owner, + state = ownerContext.state; + + me.callParent(arguments); + + + + + + if (ownerContext.viewTable && !state.flexesCalculated && !ownerContext.flexedItems.length && owner.forceFit && + + + + me.convertWidthsToFlexes(ownerContext)) { + me.cacheFlexes(ownerContext); + + + + ownerContext.invalidate({ + state: { + flexesCalculated: true, + tableHeight: ownerContext.viewTable.offsetHeight + } + }); + } else { + ownerContext.setProp('columnWidthsDone', true); + } + }, + + finishedLayout: function(ownerContext) { + var view = this.grid.getView(); + + this.callParent(arguments); + + + + + + if (!this.owner.isColumn && view.scrollFlags.x && this.owner.tooNarrow && this.owner.componentLayoutCounter) { + this.owner.el.dom.scrollLeft = view.el.dom.scrollLeft; + } + }, + + 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; + + + if (!(item.fixed || item.resizable === false)) { + + + + item.flex = ownerContext.childItems[i].flex = childContext.props.width; + item.width = null; + childContext.widthModel = calculated; + } + } + + + return totalWidth !== ownerContext.props.width; + }, + + + getContainerSize: function(ownerContext) { + var me = this, + result, + viewContext = ownerContext.viewContext, + viewHeight, + viewLayoutContext, + shrinkWrapHeight = viewContext && viewContext.heightModel.shrinkWrap; + + + if (me.owner.isColumn) { + result = me.getColumnContainerSize(ownerContext); + } + + + else { + result = me.callParent(arguments); + + + + + + if (me.scrollbarWidth && !me.controllingGrid.reserveScrollbar && viewContext) { + viewLayoutContext = viewContext.target.componentLayout.ownerContext; + if (!shrinkWrapHeight && viewContext.target.scrollFlags.y && + (ownerContext.flexedItems && ownerContext.flexedItems.length || me.owner.forceFit) && viewLayoutContext) { + viewHeight = viewContext.getProp('height'); + if (isNaN(viewHeight)) { + me.done = false; + } else if (ownerContext.state.tableHeight <= viewHeight && viewContext.target.scrollFlags.y) { + ownerContext.state.parallelDone = false; + viewLayoutContext.invalidate(); + return result; + } + } + } + } + + + if (!shrinkWrapHeight) { + result.width -= me.scrollbarWidth; + } + return result; + }, + + getColumnContainerSize : function(ownerContext) { + var padding = ownerContext.paddingContext.getPaddingInfo(), + got = 0, + needed = 0, + gotWidth, gotHeight, width, height; + + + + + + + + + + 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 + }; + }, + + publishInnerCtSize: function(ownerContext) { + var me = this, + owner = me.owner, + size = ownerContext.state.boxPlan.targetSize, + cw = ownerContext.peek('contentWidth'); + + + me.owner.tooNarrow = ownerContext.state.boxPlan.tooNarrow; + + if ((cw != null) && !owner.isColumn) { + size.width = cw; + + + + + + + + + + + + + + + + + if (owner.ownerCt.view.scrollFlags.y) { + size.width += me.scrollbarAdjustment; + } + } + + return me.callParent(arguments); + } +}); + + + +Ext.define('Ext.grid.plugin.HeaderResizer', { + extend: Ext.AbstractPlugin , + + alias: 'plugin.gridheaderresizer', + + disabled: false, + + config: { + + dynamic: false + }, + + colHeaderCls: Ext.baseCSSPrefix + 'column-header', + + minColWidth: 40, + maxColWidth: 1000, + wResizeCursor: 'col-resize', + eResizeCursor: 'col-resize', + + + + + + init: function(headerCt) { + this.headerCt = headerCt; + headerCt.on('render', this.afterHeaderRender, this, {single: true}); + }, + + + destroy: function() { + var tracker = this.tracker; + if (tracker) { + delete tracker.onBeforeStart; + delete tracker.onStart; + delete tracker.onDrag; + delete tracker.onEnd; + tracker.destroy(); + this.tracker = null; + } + }, + + afterHeaderRender: function() { + var me = this, + headerCt = this.headerCt, + el = headerCt.el; + + headerCt.mon(el, 'mousemove', this.onHeaderCtMouseMove, this); + me.markerOwner = me.ownerGrid = me.headerCt.up('tablepanel'); + if (me.markerOwner.ownerLockable) { + me.markerOwner = me.markerOwner.ownerLockable; + } + + me.tracker = new Ext.dd.DragTracker({ + disabled: me.disabled, + onBeforeStart: Ext.Function.bind(me.onBeforeStart, me), + onStart: Ext.Function.bind(me.onStart, me), + onDrag: Ext.Function.bind(me.onDrag, me), + onEnd: Ext.Function.bind(me.onEnd, me), + tolerance: 3, + autoStart: 300, + el: el + }); + }, + + + + + onHeaderCtMouseMove: function(e) { + var me = this, + headerEl, overHeader, resizeHeader, + headers; + + if (me.headerCt.dragging || me.disabled) { + if (me.activeHd) { + me.activeHd.el.dom.style.cursor = ''; + delete me.activeHd; + } + } else { + headerEl = e.getTarget('.' + me.colHeaderCls, 3, true); + + if (headerEl) { + overHeader = Ext.getCmp(headerEl.id); + + + if (overHeader.isOnRightEdge(e)) { + + + if (me.headerCt.visibleColumnManager.getColumns().length === 1 && me.headerCt.forceFit) { + return; + } + + resizeHeader = overHeader; + } + + else if (overHeader.isOnLeftEdge(e)) { + + headers = me.headerCt.visibleColumnManager.getColumns(); + resizeHeader = headers[Ext.Array.indexOf(headers, overHeader) - 1]; + + + + if (!resizeHeader && me.ownerGrid.ownerLockable && !me.ownerGrid.isLocked) { + headers = me.ownerGrid.ownerLockable.lockedGrid.headerCt.visibleColumnManager.getColumns(); + resizeHeader = headers[headers.length - 1]; + } + } + + if (resizeHeader) { + + + + + if (resizeHeader.isGroupHeader) { + headers = resizeHeader.getGridColumns(); + resizeHeader = headers[headers.length - 1]; + } + + + + 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 { + overHeader.el.dom.style.cursor = ''; + if (overHeader.triggerEl) { + overHeader.triggerEl.dom.style.cursor = ''; + } + me.activeHd = null; + } + } + } + }, + + + onBeforeStart : function(e) { + var me = this; + + + me.dragHd = me.activeHd; + + if (!!me.dragHd && !me.headerCt.dragging) { + + + + me.xDelta = me.dragHd.getX() + me.dragHd.getWidth() - me.tracker.getXY()[0]; + this.tracker.constrainTo = this.getConstrainRegion(); + return true; + } else { + me.headerCt.dragging = false; + return false; + } + }, + + + getConstrainRegion: function() { + var me = this, + dragHdEl = me.dragHd.el, + rightAdjust = 0, + nextHd, + lockedGrid, + maxColWidth = me.headerCt.getWidth() - me.headerCt.visibleColumnManager.getColumns().length * me.minColWidth; + + + + if (me.headerCt.forceFit) { + nextHd = me.dragHd.nextNode('gridcolumn:not([hidden]):not([isGroupHeader])'); + if (nextHd && me.headerInSameGrid(nextHd)) { + rightAdjust = nextHd.getWidth() - me.minColWidth; + } + } + + + else if ((lockedGrid = me.dragHd.up('tablepanel')).isLocked) { + rightAdjust = me.dragHd.up('[scrollerOwner]').getTargetEl().getWidth() - lockedGrid.getWidth() - (lockedGrid.ownerLockable.normalGrid.visibleColumnManager.getColumns().length * me.minColWidth + Ext.getScrollbarSize().width); + } + + + else { + rightAdjust = maxColWidth - dragHdEl.getWidth(); + } + + return me.adjustConstrainRegion( + dragHdEl.getRegion(), + 0, + rightAdjust - me.xDelta, + 0, + me.minColWidth - me.xDelta + ); + }, + + + + onStart: function(e){ + var me = this, + dragHd = me.dragHd, + width = dragHd.el.getWidth(), + headerCt = dragHd.getOwnerHeaderCt(), + x, y, markerOwner, lhsMarker, rhsMarker, markerHeight; + + me.headerCt.dragging = true; + me.origWidth = width; + + + if (!me.dynamic) { + markerOwner = me.markerOwner; + + + + + + 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'); + + lhsMarker.setLocalY(y); + rhsMarker.setLocalY(y); + lhsMarker.setHeight(markerHeight); + rhsMarker.setHeight(markerHeight); + me.setMarkerX(lhsMarker, x); + me.setMarkerX(rhsMarker, x + width); + } + }, + + + 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) { + this.headerCt.dragging = false; + if (this.dragHd) { + if (!this.dynamic) { + var markerOwner = this.headerCt.up('tablepanel'); + + + if (markerOwner.ownerLockable) { + markerOwner = markerOwner.ownerLockable; + } + + if ('gridOverflowSetting' in this) { + markerOwner.el.dom.style.overflow = this.gridOverflowSetting; + } + + this.setMarkerX(markerOwner.getLhsMarker(), -9999); + this.setMarkerX(markerOwner.getRhsMarker(), -9999); + } + this.doResize(); + } + + this.onHeaderCtMouseMove(e); + }, + + doResize: function() { + var me = this, + dragHd = me.dragHd, + nextHd, + offset = me.tracker.getOffset('point'); + + + if (dragHd && offset[0]) { + + if (dragHd.flex) { + delete dragHd.flex; + } + + Ext.suspendLayouts(); + + + me.adjustColumnWidth(offset[0]); + + + + 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]); + } + } + + + Ext.resumeLayouts(true); + } + }, + + + headerInSameGrid: function(header) { + var grid = this.dragHd.up('tablepanel'); + + return !!header.up(grid); + }, + + disable: function() { + this.disabled = true; + if (this.tracker) { + this.tracker.disable(); + } + }, + + enable: function() { + this.disabled = false; + if (this.tracker) { + this.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); + } +}); + + + +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.isOnLeftEdge(e) || headerCmp.isOnRightEdge(e))) { + ddel = document.createElement('div'); + ddel.role = 'presentation'; + ddel.innerHTML = Ext.getCmp(header.id).text; + return { + ddel: ddel, + header: headerCmp + }; + } + } + } + return false; + }, + + onBeforeDrag: function() { + return !(this.headerCt.dragging || this.disabled); + }, + + onInitDrag: function() { + this.headerCt.dragging = true; + 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; + } +}); + + + + + + +Ext.define('Ext.dd.DDTarget', { + extend: Ext.dd.DragDrop , + + + constructor: function(id, sGroup, config) { + if (id) { + this.initTarget(id, sGroup, config); + } + }, + + + getDragEl: Ext.emptyFn, + + isValidHandleChild: Ext.emptyFn, + + startDrag: Ext.emptyFn, + + endDrag: Ext.emptyFn, + + onDrag: Ext.emptyFn, + + onDragDrop: Ext.emptyFn, + + onDragEnter: Ext.emptyFn, + + onDragOut: Ext.emptyFn, + + onDragOver: Ext.emptyFn, + + onInvalidDrop: Ext.emptyFn, + + onMouseDown: Ext.emptyFn, + + onMouseUp: Ext.emptyFn, + + setXConstraint: Ext.emptyFn, + + setYConstraint: Ext.emptyFn, + + resetConstraints: Ext.emptyFn, + + clearConstraints: Ext.emptyFn, + + clearTicks: Ext.emptyFn, + + setInitPosition: Ext.emptyFn, + + setDragElId: Ext.emptyFn, + + setHandleElId: Ext.emptyFn, + + setOuterHandleElId: Ext.emptyFn, + + addInvalidHandleClass: Ext.emptyFn, + + addInvalidHandleId: Ext.emptyFn, + + addInvalidHandleType: Ext.emptyFn, + + removeInvalidHandleClass: Ext.emptyFn, + + removeInvalidHandleId: Ext.emptyFn, + + removeInvalidHandleType: Ext.emptyFn, + + toString: function() { + return ("DDTarget " + this.id); + } +}); + + + +Ext.define('Ext.dd.ScrollManager', { + singleton: true, + + + + + constructor: function() { + var ddm = Ext.dd.DragDropManager; + ddm.fireEvents = Ext.Function.createSequence(ddm.fireEvents, this.onFire, this); + ddm.stopDrag = Ext.Function.createSequence(ddm.stopDrag, this.onStop, this); + this.doScroll = Ext.Function.bind(this.doScroll, this); + this.ddmInstance = ddm; + this.els = {}; + this.dragEl = null; + this.proc = {}; + }, + + onStop: function(e){ + var sm = Ext.dd.ScrollManager; + sm.dragEl = null; + sm.clearProc(); + }, + + triggerRefresh: function() { + if (this.ddmInstance.dragCurrent) { + this.ddmInstance.refreshCache(this.ddmInstance.dragCurrent.groups); + } + }, + + doScroll: function() { + if (this.ddmInstance.dragCurrent) { + var proc = this.proc, + procEl = proc.el, + ddScrollConfig = proc.el.ddScrollConfig, + inc = ddScrollConfig && ddScrollConfig.increment ? ddScrollConfig.increment : this.increment, + animate = ddScrollConfig && 'animate' in ddScrollConfig ? ddScrollConfig.animate : this.animate; + + if (!animate) { + if (procEl.scroll(proc.dir, inc)) { + this.triggerRefresh(); + } + } else { + procEl.scroll(proc.dir, inc, true, this.animDuration, this.triggerRefresh); + } + } + }, + + clearProc: function() { + var proc = this.proc; + if (proc.id) { + clearInterval(proc.id); + } + proc.id = 0; + proc.el = null; + proc.dir = ""; + }, + + startProc: function(el, dir) { + this.clearProc(); + this.proc.el = el; + this.proc.dir = dir; + var group = el.ddScrollConfig ? el.ddScrollConfig.ddGroup : undefined, + freq = (el.ddScrollConfig && el.ddScrollConfig.frequency) + ? el.ddScrollConfig.frequency + : this.frequency; + + if (group === undefined || this.ddmInstance.dragCurrent.ddGroup == group) { + this.proc.id = setInterval(this.doScroll, freq); + } + }, + + onFire: function(e, isDrop) { + if (isDrop || !this.ddmInstance.dragCurrent) { + return; + } + if (!this.dragEl || this.dragEl != this.ddmInstance.dragCurrent) { + this.dragEl = this.ddmInstance.dragCurrent; + + this.refreshCache(); + } + + var pt = e.getPoint(), + proc = this.proc, + els = this.els, + id, el, r, c; + + for (id in els) { + el = els[id]; + r = el._region; + c = el.ddScrollConfig ? el.ddScrollConfig : this; + if (r && r.contains(pt) && el.isScrollable()) { + if (r.bottom - pt.y <= c.vthresh) { + if(proc.el != el){ + this.startProc(el, "down"); + } + return; + }else if (r.right - pt.x <= c.hthresh) { + if (proc.el != el) { + this.startProc(el, "right"); + } + return; + } else if(pt.y - r.top <= c.vthresh) { + if (proc.el != el) { + this.startProc(el, "up"); + } + return; + } else if(pt.x - r.left <= c.hthresh) { + if (proc.el != el) { + this.startProc(el, "left"); + } + return; + } + } + } + this.clearProc(); + }, + + + register : function(el){ + if (Ext.isArray(el)) { + for(var i = 0, len = el.length; i < len; i++) { + this.register(el[i]); + } + } else { + el = Ext.get(el); + this.els[el.id] = el; + } + }, + + + unregister : function(el){ + if(Ext.isArray(el)){ + for (var i = 0, len = el.length; i < len; i++) { + this.unregister(el[i]); + } + }else{ + el = Ext.get(el); + delete this.els[el.id]; + } + }, + + + vthresh : 25, + + + hthresh : 25, + + + increment : 100, + + + frequency : 500, + + + animate: true, + + + animDuration: 0.4, + + + ddGroup: undefined, + + + refreshCache : function(){ + var els = this.els, + id; + for (id in els) { + if(typeof els[id] == 'object'){ + els[id]._region = els[id].getRegion(); + } + } + } +}); + + + +Ext.define('Ext.dd.DropTarget', { + extend: Ext.dd.DDTarget , + + + + constructor : function(el, config){ + this.el = Ext.get(el); + + Ext.apply(this, config); + + if(this.containerScroll){ + Ext.dd.ScrollManager.register(this.el); + } + + this.callParent([this.el.dom, this.ddGroup || this.group, + {isTarget: true}]); + }, + + + + + dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok', + + dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop', + + + isTarget : true, + + + isNotifyTarget : true, + + + notifyEnter : function(dd, e, data){ + if(this.overClass){ + this.el.addCls(this.overClass); + } + return this.dropAllowed; + }, + + + notifyOver : function(dd, e, data){ + return this.dropAllowed; + }, + + + notifyOut : function(dd, e, data){ + if(this.overClass){ + this.el.removeCls(this.overClass); + } + }, + + + notifyDrop : function(dd, e, data){ + return false; + }, + + destroy : function(){ + this.callParent(); + if(this.containerScroll){ + Ext.dd.ScrollManager.unregister(this.el); + } + } +}); + + + +Ext.define('Ext.dd.Registry', { + singleton: true, + constructor: function() { + this.elements = {}; + this.handles = {}; + this.autoIdSeed = 0; + }, + + getId: function(el, autogen){ + if(typeof el == "string"){ + return el; + } + var id = el.id; + if(!id && autogen !== false){ + id = "extdd-" + (++this.autoIdSeed); + el.id = id; + } + return id; + }, + + + register : function(el, data){ + data = data || {}; + if (typeof el == "string") { + el = document.getElementById(el); + } + data.ddel = el; + this.elements[this.getId(el)] = data; + if (data.isHandle !== false) { + this.handles[data.ddel.id] = data; + } + if (data.handles) { + var hs = data.handles, + i, len; + for (i = 0, len = hs.length; i < len; i++) { + this.handles[this.getId(hs[i])] = data; + } + } + }, + + + unregister : function(el){ + var id = this.getId(el, false), + data = this.elements[id], + hs, i, len; + if(data){ + delete this.elements[id]; + if(data.handles){ + hs = data.handles; + for (i = 0, len = hs.length; i < len; i++) { + delete this.handles[this.getId(hs[i], false)]; + } + } + } + }, + + + getHandle : function(id){ + if(typeof id != "string"){ + id = id.id; + } + return this.handles[id]; + }, + + + getHandleFromEvent : function(e){ + var t = e.getTarget(); + return t ? this.handles[t.id] : null; + }, + + + getTarget : function(id){ + if(typeof id != "string"){ + id = id.id; + } + return this.elements[id]; + }, + + + getTargetFromEvent : function(e){ + var t = e.getTarget(); + return t ? this.elements[t.id] || this.handles[t.id] : null; + } +}); + + + +Ext.define('Ext.dd.DropZone', { + extend: Ext.dd.DropTarget , + + + + getTargetFromEvent : function(e){ + return Ext.dd.Registry.getTargetFromEvent(e); + }, + + + onNodeEnter : function(n, dd, e, data){ + + }, + + + onNodeOver : function(n, dd, e, data){ + return this.dropAllowed; + }, + + + onNodeOut : function(n, dd, e, data){ + + }, + + + onNodeDrop : function(n, dd, e, data){ + return false; + }, + + + onContainerOver : function(dd, e, data){ + return this.dropNotAllowed; + }, + + + onContainerDrop : function(dd, e, data){ + return false; + }, + + + notifyEnter : function(dd, e, data){ + return this.dropNotAllowed; + }, + + + notifyOver : function(dd, e, data){ + var n = this.getTargetFromEvent(e); + if(!n) { + if(this.lastOverNode){ + this.onNodeOut(this.lastOverNode, dd, e, data); + this.lastOverNode = null; + } + return this.onContainerOver(dd, e, data); + } + if(this.lastOverNode != n){ + if(this.lastOverNode){ + this.onNodeOut(this.lastOverNode, dd, e, data); + } + this.onNodeEnter(n, dd, e, data); + this.lastOverNode = n; + } + return this.onNodeOver(n, dd, e, data); + }, + + + notifyOut : function(dd, e, data){ + if(this.lastOverNode){ + this.onNodeOut(this.lastOverNode, dd, e, data); + this.lastOverNode = null; + } + }, + + + notifyDrop : function(dd, e, data){ + var me = this, + n = me.getTargetFromEvent(e), + result = n ? + me.onNodeDrop(n, dd, e, data) : + me.onContainerDrop(dd, e, data); + + + + if (me.lastOverNode) { + me.onNodeOut(me.lastOverNode, dd, e, data); + me.lastOverNode = null; + } + return result; + }, + + + triggerCacheRefresh : function() { + Ext.dd.DDM.refreshCache(this.groups); + } +}); + + + +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.DomHelper.append(Ext.getBody(), { + role: 'presentation', + cls: "col-move-top", + + "data-sticky": true, + html: " " + }, true); + this.indicatorXOffset = Math.floor((this.topIndicator.dom.offsetWidth + 1) / 2); + } + return this.topIndicator; + }, + + getBottomIndicator: function() { + if (!this.bottomIndicator) { + this.bottomIndicator = Ext.DomHelper.append(Ext.getBody(), { + role: 'presentation', + cls: "col-move-bottom", + + "data-sticky": true, + html: " " + }, true); + } + 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; + + + 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; + + + 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)) { + + + + + 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'; + } + + + topXY = topIndicator.getAlignToXY(targetHeader.el, topAnchor); + bottomXY = bottomIndicator.getAlignToXY(targetHeader.el, bottomAnchor); + + + 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); + + + topIndicator.setXY(topXY); + bottomIndicator.setXY(bottomXY); + topIndicator.show(); + bottomIndicator.show(); + + + } else { + 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; + + + doPosition = (from.ownerCt === to.ownerCt); + + + if (!doPosition && (!from.ownerCt.sealed && !to.ownerCt.sealed)) { + + doPosition = true; + fromPanel = from.up('tablepanel'); + toPanel = to.up('tablepanel'); + if (fromPanel !== toPanel) { + data.crossPanel = true; + + + 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(); + }, + + onNodeDrop: function(node, dragZone, e, data) { + + if (this.valid) { + var dragHeader = data.header, + dropLocation = data.dropLocation, + targetHeader = dropLocation.header, + fromCt = dragHeader.ownerCt, + toCt = targetHeader.ownerCt, + sameCt = fromCt === toCt, + + + localFromIdx = fromCt.items.indexOf(data.header), + localToIdx = toCt.items.indexOf(targetHeader), + headerCt = this.headerCt, + + + columns = headerCt.visibleColumnManager, + visibleFromIdx = columns.getHeaderIndex(dragHeader), + + visibleToIdx = targetHeader.isGroupHeader ? toCt.items.indexOf(targetHeader) : columns.getHeaderIndex(targetHeader), + colsToMove = dragHeader.isGroupHeader ? dragHeader.query(':not([hidden]):not([isGroupHeader])').length : 1, + + + + + direction = targetHeader.isGroupHeader ? (dropLocation.pos === 'after') : columns.getHeaderIndex(targetHeader) > columns.getHeaderIndex(dragHeader), + scrollerOwner, savedWidth; + + + + if (dropLocation.pos === 'after') { + localToIdx++; + + + + visibleToIdx += targetHeader.isGroupHeader ? targetHeader.query(':not([hidden]):not([isGroupHeader])').length : 1; + } + + + + + if (data.isLock) { + scrollerOwner = fromCt.up('[scrollerOwner]'); + scrollerOwner.lock(dragHeader, localToIdx, toCt); + } else if (data.isUnlock) { + scrollerOwner = fromCt.up('[scrollerOwner]'); + scrollerOwner.unlock(dragHeader, localToIdx, toCt); + } + + + else { + this.invalidateDrop(); + + savedWidth = dragHeader.getWidth(); + + + if (sameCt) { + + if (localToIdx > localFromIdx) { + localToIdx -= 1; + } + + + + if (localToIdx === localFromIdx) { + + headerCt.onHeaderMoved(dragHeader, colsToMove, visibleFromIdx, visibleToIdx); + return; + } + } + + + Ext.suspendLayouts(); + + if (sameCt) { + toCt.move(localFromIdx, localToIdx); + } else { + + + + + + + + if (direction && (visibleToIdx === localToIdx)) { + localToIdx -= 1; + } + + + + + + fromCt.isDDMoveInGrid = toCt.isDDMoveInGrid = !data.crossPanel; + fromCt.remove(dragHeader, false); + toCt.insert(localToIdx, dragHeader); + fromCt.isDDMoveInGrid = toCt.isDDMoveInGrid = false; + } + + + + + if (toCt.isGroupHeader) { + + if (!sameCt) { + dragHeader.savedFlex = dragHeader.flex; + delete dragHeader.flex; + dragHeader.width = savedWidth; + } + } else { + if (dragHeader.savedFlex) { + dragHeader.flex = dragHeader.savedFlex; + delete dragHeader.width; + } + } + + Ext.resumeLayouts(true); + + + + if (!sameCt) { + headerCt.onHeaderMoved(dragHeader, colsToMove, visibleFromIdx, visibleToIdx); + } + + + } + } + } +}); + + + +Ext.define('Ext.grid.plugin.HeaderReorderer', { + extend: Ext.AbstractPlugin , + + alias: 'plugin.gridheaderreorderer', + + init: function(headerCt) { + this.headerCt = headerCt; + headerCt.on({ + render: this.onHeaderCtRender, + single: true, + scope: this + }); + }, + + + destroy: function() { + Ext.destroy(this.dragZone, this.dropZone); + }, + + 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(); + } + } +}); + + + +Ext.define('Ext.grid.header.Container', { + extend: Ext.container.Container , + + + + + + + + + + + + + border: true, + + alias: 'widget.headercontainer', + + baseCls: Ext.baseCSSPrefix + '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: Ext.baseCSSPrefix + 'column-header-open', + + menuSortAscCls: Ext.baseCSSPrefix + 'hmenu-sort-asc', + + menuSortDescCls: Ext.baseCSSPrefix + 'hmenu-sort-desc', + + menuColsIcon: Ext.baseCSSPrefix + 'cols-icon', + + ddLock: false, + + dragging: false, + + + + + sortable: true, + + + enableColumnHide: true, + + initComponent: function() { + var me = this; + + me.headerCounter = 0; + me.plugins = me.plugins || []; + me.defaults = me.defaults || {}; + + + + + + + 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 (me.isColumn && !me.isGroupHeader) { + if (!me.items || me.items.length === 0) { + me.isContainer = false; + me.layout = { + type: 'container', + calculate: Ext.emptyFn + }; + } + } + + + else { + me.layout = Ext.apply({ + type: 'gridcolumn', + align: 'stretch' + }, me.initialConfig.layout); + + + me.defaults.columnLines = me.columnLines; + + + if (!me.isGroupHeader) { + me.isRootHeader = true; + + + me.columnManager = new Ext.grid.ColumnManager(false, me); + me.visibleColumnManager = new Ext.grid.ColumnManager(true, me); + + + + + + + + if (me.grid) { + me.grid.columnManager = me.columnManager; + me.grid.visibleColumnManager = me.visibleColumnManager; + } + } else { + + me.visibleColumnManager = new Ext.grid.ColumnManager(true, me); + me.columnManager = new Ext.grid.ColumnManager(false, me); + } + } + + Ext.applyIf(me.defaults, { + sortable: me.sortable + }); + + me.menuTask = new Ext.util.DelayedTask(me.updateMenuDisabledState, me); + me.callParent(); + me.addEvents( + + 'columnresize', + + + 'headerclick', + + + 'headercontextmenu', + + + 'headertriggerclick', + + + 'columnmove', + + 'columnhide', + + 'columnshow', + + 'columnschanged', + + 'sortchange', + + 'menucreate' + ); + }, + + initEvents: function() { + var me = this; + + me.callParent(); + + + if (!me.isColumn && !me.isGroupHeader) { + me.mon(me.el, { + click: me.onHeaderCtEvent, + dblclick: me.onHeaderCtEvent, + contextmenu: me.onHeaderCtEvent, + mouseover: me.onHeaderCtMouseOver, + mouseout: me.onHeaderCtMouseOut, + scope: me + }); + } + }, + + onHeaderCtEvent: function(e, t) { + var me = this, + headerEl = e.getTarget('.' + Ext.grid.column.Column.prototype.baseCls), + header, + targetEl, + isTriggerClick; + + if (headerEl && !me.ddLock) { + header = Ext.getCmp(headerEl.id); + if (header) { + targetEl = header[header.clickTargetName]; + if (e.within(targetEl)) { + if (e.type === 'click') { + isTriggerClick = header.onTitleElClick(e, targetEl); + if (isTriggerClick) { + me.onHeaderTriggerClick(header, e, t); + } 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); + } + } + } + } + }, + + onHeaderCtMouseOver: function(e, t) { + var headerEl, + header, + targetEl; + + + + 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; + + + 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); + } + } + } + }, + + isLayoutRoot: function(){ + + + + + + if (this.hiddenHeaders) { + return false; + } + return this.callParent(); + }, + + + getOwnerHeaderCt: 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) { + 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; + + for (c = 0; c < length; c++) { + columnState = columns[c]; + + for (index = count; index--; ) { + col = items[index]; + if (col.getStateId && col.getStateId() == columnState.id) { + + + + + + if (i !== index) { + this.items.insert(i, this.items.getAt(index)); + moved = true; + } + + if (col.applyColumnState) { + col.applyColumnState(columnState); + } + ++i; + break; + } + } + } + + + if (moved) { + 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; + }, + + + + + onAdd: function(c) { + var me = this; + + if (!c.headerId) { + c.headerId = c.initialConfig.id || Ext.id(null, 'header-'); + } + + + if (!c.getStateId()) { + + + + + c.stateId = c.initialConfig.id || ('h' + (++me.headerCounter)); + } + + if (Ext.global.console && Ext.global.console.warn) { + if (!me._usedIDs) { + me._usedIDs = {}; + } + if (me._usedIDs[c.headerId]) { + Ext.global.console.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, + headerToMove = me.items.items[fromIdx]; + + + headerToMove.visibleFromIdx = me.getOwnerHeaderCt().visibleColumnManager.indexOf(headerToMove); + + me.callParent(arguments); + }, + + onMove: function(headerToMove, fromIdx, toIdx) { + var me = this, + gridHeaderCt = me.getOwnerHeaderCt(), + gridVisibleColumnManager = gridHeaderCt.visibleColumnManager, + numColsToMove = 1, + visibleToIdx; + + + me.onHeadersChanged(headerToMove, true); + + visibleToIdx = gridVisibleColumnManager.indexOf(headerToMove); + if (visibleToIdx >= headerToMove.visibleFromIdx) { + visibleToIdx++; + } + + me.callParent(arguments); + + + if (headerToMove.isGroupHeader) { + numColsToMove = headerToMove.visibleColumnManager.getColumns().length; + } + + gridHeaderCt.onHeaderMoved(headerToMove, numColsToMove, headerToMove.visibleFromIdx, visibleToIdx); + }, + + + + + onRemove: function(c) { + var me = this, + ownerCt = me.ownerCt; + + me.callParent(arguments); + + if (!me._usedIDs) { + me._usedIDs = {}; + } + delete me._usedIDs[c.headerId]; + + if (!me.destroying) { + + + if (!me.isDDMoveInGrid) { + me.onHeadersChanged(c, false); + } + if (me.isGroupHeader && !me.items.getCount() && ownerCt) { + + + + me.detachComponent(c); + + + Ext.suspendLayouts(); + ownerCt.remove(me); + Ext.resumeLayouts(true); + } + } + }, + + + + + + + onHeadersChanged: function(c, isMove) { + var gridPanel, + gridHeaderCt = this.getOwnerHeaderCt(); + + + this.purgeHeaderCtCache(this); + + if (gridHeaderCt) { + gridHeaderCt.onColumnsChanged(); + if (!c.isGroupHeader) { + gridPanel = gridHeaderCt.ownerCt; + + + + if (gridPanel && !isMove) { + gridPanel.onHeadersChanged(gridHeaderCt, c); + } + } + } + }, + + + 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); + } + }, + + + + + + onColumnsChanged: function() { + var me = this, + menu = me.menu, + columnItemSeparator, + columnItem; + + if (me.rendered) { + me.fireEvent('columnschanged', me); + + + + if (menu && (columnItemSeparator = menu.child('#columnItemSeparator'))) { + columnItem = menu.child('#columnItem'), + + + + columnItemSeparator.destroy(); + columnItem.destroy(); + } + } + }, + + + applyDefaults: function(config) { + var ret; + + if (config && !config.isComponent && config.xtype == 'rownumberer') { + ret = config; + } else { + ret = this.callParent(arguments); + + + if (!config.isGroupHeader && !('width' in ret) && !ret.flex) { + ret.width = this.defaultWidth; + } + } + return ret; + }, + + + + 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]; + sorter = store.sorters.get(header.getSortParam()); + 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; + + + me.purgeHeaderCtCache(header.ownerCt); + + if (menu) { + + item = me.getMenuItemForHeader(menu, header); + if (item) { + item.setChecked(visible, true); + } + + if (menu.isVisible()) { + me.menuTask.delay(50); + } + } + }, + + updateMenuDisabledState: function(menu) { + var me = this, + columns = me.query(':not([hidden])'), + i, + len = columns.length, + item, + checkItem, + method; + + + 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, + gridSection = me.ownerCt; + + if (me.forceFit) { + delete me.flex; + + } + + me.onHeaderVisibilityChange(header, true); + + + + if (!header.isGroupHeader) { + if (gridSection) { + gridSection.onHeaderShow(me, header); + } + } + me.fireEvent('columnshow', me, header); + me.fireEvent('columnschanged', this); + }, + + onHeaderHide: function(header) { + + var me = this, + gridSection = me.ownerCt; + + me.onHeaderVisibilityChange(header, false); + + + if (!header.isGroupHeader) { + if (gridSection) { + gridSection.onHeaderHide(me, header); + } + } + me.fireEvent('columnhide', me, header); + me.fireEvent('columnschanged', this); + }, + + + tempLock: function() { + this.ddLock = true; + Ext.Function.defer(function() { + this.ddLock = false; + }, 200, 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) { + header.fireEvent('headerclick', this, header, e, t); + this.fireEvent('headerclick', this, header, e, t); + }, + + 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) { + me.showMenuBy(t, header); + } + }, + + + showMenuBy: function(t, header) { + var menu = this.getMenu(), + ascItem = menu.down('#ascItem'), + descItem = menu.down('#descItem'), + sortableMth; + + + + menu.activeHeader = menu.ownerCmp = menu.ownerButton = header; + header.setMenuActive(true); + + + sortableMth = header.sortable ? 'enable' : 'disable'; + if (ascItem) { + ascItem[sortableMth](); + } + if (descItem) { + descItem[sortableMth](); + } + menu.showBy(t, 'tl-bl?'); + }, + + + onMenuHide: function(menu) { + menu.activeHeader.setMenuActive(false); + }, + + moveHeader: function(fromIdx, toIdx) { + + this.tempLock(); + this.move(fromIdx, toIdx); + }, + + 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; + + + me.gridVisibleColumns = me.gridDataColumns = me.hideableColumns = null; + + + if (visibleColumnManager) { + visibleColumnManager.invalidate(); + columnManager.invalidate(); + } + }, + + + getMenu: function() { + var me = this; + + if (!me.menu) { + me.menu = new Ext.menu.Menu({ + hideOnParentHide: false, + items: me.getMenuItems(), + listeners: { + beforeshow: me.beforeMenuShow, + hide: me.onMenuHide, + scope: me + } + }); + me.fireEvent('menucreate', me, me.menu); + } + return me.menu; + }, + + + beforeMenuShow: function(menu) { + var me = this, + columnItem = menu.child('#columnItem'), + hideableColumns, + insertPoint, + menu; + + + + + if (!columnItem) { + hideableColumns = me.enableColumnHide ? me.getColumnMenu(me) : null; + + + 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); + if (!menu.rendered) { + menu.render(this.el.up('{overflow=auto}') || document.body); + } + }, + + + 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; + }, + + + onSortAscClick: function() { + var menu = this.getMenu(), + activeHeader = menu.activeHeader; + + activeHeader.sort('ASC'); + }, + + + onSortDescClick: function() { + var menu = this.getMenu(), + activeHeader = menu.activeHeader; + + activeHeader.sort('DESC'); + }, + + + 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); + } + return menuItems; + }, + + onColumnCheckChange: function(checkItem, checked) { + var header = Ext.getCmp(checkItem.headerId); + + if (header.rendered) { + header[checked ? 'show' : 'hide'](); + } else { + header.hidden = !checked; + } + }, + + + getColumnCount: function() { + return this.getGridColumns().length; + }, + + + 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; + }, + + + getVisibleGridColumns: function() { + if (this.gridVisibleColumns) { + return this.gridVisibleColumns; + } + + var allColumns = this.getGridColumns(), + result = [], + len = allColumns.length, i; + + + + for (i = 0; i < len; i++) { + if (!allColumns[i].hidden) { + result[result.length] = allColumns[i]; + } + } + this.gridVisibleColumns = result; + return result; + }, + + + getGridColumns: function(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; + for (i = 0, len = items.length; i < len; i++) { + item = items[i]; + if (item.isGroupHeader) { + item.getGridColumns(result, hiddenAncestor); + } else { + item.hiddenAncestor = hiddenAncestor; + result.push(item); + } + } + } + if (!inResult) { + me.gridDataColumns = result; + } + + + if (!inResult && len) { + + for (i = 0, len = result.length; i < len; i++) { + item = result[i]; + item.isFirstVisible = item.isLastVisible = false; + if (!(item.hidden || item.hiddenAncestor)) { + if (!lastVisibleColumn) { + item.isFirstVisible = true; + } + lastVisibleColumn = item; + } + } + + if (lastVisibleColumn) { + lastVisibleColumn.isLastVisible = true; + } + } + + return result; + }, + + + getHideableColumns: function() { + var me = this, + result = me.hideableColumns; + + if (!result) { + result = me.hideableColumns = me.query('[hideable]'); + } + return result; + }, + + + getHeaderIndex: function (header) { + + + if (!this.columnManager) { + this.columnManager = this.getOwnerHeaderCt().columnManager; + } + + return this.columnManager.getHeaderIndex(header); + }, + + + getHeaderAtIndex: function (index) { + + + if (!this.columnManager) { + this.columnManager = this.getOwnerHeaderCt().columnManager; + } + + return this.columnManager.getHeaderAtIndex(index); + }, + + + getVisibleHeaderClosestToIndex: function (index) { + + + if (!this.visibleColumnManager) { + this.visibleColumnManager = this.getOwnerHeaderCt().visibleColumnManager; + } + + return this.visibleColumnManager.getVisibleHeaderClosestToIndex(index); + }, + + applyForceFit: function (header) { + var me = this, + view = me.view, + minWidth = Ext.grid.plugin.HeaderResizer.prototype.minColWidth, + + useMinWidthForFlex = false, + defaultWidth = Ext.grid.header.Container.prototype.defaultWidth, + availFlex = me.el.getViewSize().width - (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]; + + + if (item === header) { + continue; + } + + item.flex = item.flex || item.width || item.getWidth(); + totalFlex += item.flex; + item.width = null; + } + } + + function applyWidth() { + + + var isCurrentHeader; + + for (i = 0, len = items.length; i < len; i++) { + item = items[i]; + isCurrentHeader = (item === header); + + if (useMinWidthForFlex && !isCurrentHeader) { + + item.flex = minWidth; + item.width = null; + } else if (!isCurrentHeader) { + + + + + + 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(); + + + maxAvailFlexOneColumn = (availFlex - ((items.length + 1) * minWidth)); + + + + header.flex = null; + + if (hidden) { + myWidth = header.width || header.savedWidth; + header.savedWidth = null; + } else { + myWidth = view.getMaxContentWidth(header); + } + + + + if (myWidth > maxAvailFlexOneColumn) { + header.width = maxAvailFlexOneColumn; + useMinWidthForFlex = true; + } else { + header.width = myWidth; + + + + 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); + } + } + } +}); + + + +Ext.define('Ext.grid.ColumnComponentLayout', { + extend: Ext.layout.component.Auto , + alias: 'layout.columncomponent', + + type: 'columncomponent', + + setWidthInDom: true, + + beginLayout: function(ownerContext) { + this.callParent(arguments); + ownerContext.titleContext = ownerContext.getEl('titleEl'); + ownerContext.triggerContext = ownerContext.getEl('triggerEl'); + }, + + beginLayoutCycle: function(ownerContext) { + var owner = this.owner; + + this.callParent(arguments); + + + if (ownerContext.widthModel.shrinkWrap) { + owner.el.setWidth(''); + } + + owner.titleEl.setStyle({ + paddingTop: '', + paddingBottom: '' + }); + }, + + + publishInnerHeight: function(ownerContext, outerHeight) { + var me = this, + owner = me.owner, + innerHeight, availableHeight, + textHeight, titleHeight, paddingTop, paddingBottom; + + + + + if (owner.getOwnerHeaderCt().hiddenHeaders) { + ownerContext.setProp('innerHeight', 0); + return; + } + + innerHeight = outerHeight - ownerContext.getBorderInfo().height; + availableHeight = innerHeight; + + + if (!owner.noWrap && !ownerContext.hasDomProp('width')) { + me.done = false; + return; + } + + + if (ownerContext.hasRawContent) { + titleHeight = availableHeight; + + + textHeight = owner.textEl.getHeight(); + if (textHeight) { + availableHeight -= textHeight; + if (availableHeight > 0) { + paddingTop = Math.floor(availableHeight / 2); + paddingBottom = availableHeight - paddingTop; + ownerContext.titleContext.setProp('padding-top', paddingTop); + ownerContext.titleContext.setProp('padding-bottom', paddingBottom); + } + } + } + + + else { + titleHeight = owner.titleEl.getHeight(); + ownerContext.setProp('innerHeight', innerHeight - titleHeight, false); + } + + + if ((Ext.isIE6 || Ext.isIEQuirks) && ownerContext.triggerContext) { + ownerContext.triggerContext.setHeight(titleHeight); + } + + }, + + + + + measureContentHeight: function(ownerContext) { + return ownerContext.el.dom.offsetHeight; + }, + + publishOwnerHeight: function(ownerContext, contentHeight) { + this.callParent(arguments); + + + if ((Ext.isIE6 || Ext.isIEQuirks) && ownerContext.triggerContext) { + ownerContext.triggerContext.setHeight(contentHeight); + } + }, + + + publishInnerWidth: function(ownerContext, outerWidth) { + + if (!ownerContext.hasRawContent) { + ownerContext.setProp('innerWidth', outerWidth - ownerContext.getBorderInfo().width, false); + } + }, + + + calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) { + var result = this.callParent(arguments); + + + if (!ownerContext.hasRawContent) { + if (this.owner.noWrap || ownerContext.hasDomProp('width')) { + return contentHeight + this.owner.titleEl.getHeight() + ownerContext.getBorderInfo().height; + } + + + + return null; + } + return result; + }, + + + calculateOwnerWidthFromContentWidth: function (ownerContext, contentWidth) { + var owner = this.owner, + padWidth = ownerContext.getPaddingInfo().width, + triggerOffset = this.getTriggerOffset(owner, ownerContext), + inner; + + + + 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 (owner.query('>:not([hidden])').length === 0) { + width = owner.self.triggerElWidth; + } + } + return width; + } +}); + + + +Ext.define('Ext.grid.column.Column', { + extend: Ext.grid.header.Container , + alias: 'widget.gridcolumn', + + alternateClassName: 'Ext.grid.Column', + + baseCls: Ext.baseCSSPrefix + 'column-header', + + + hoverCls: Ext.baseCSSPrefix + 'column-header-over', + + handleWidth: 4, + + ariaRole: 'columnheader', + + sortState: null, + + possibleSortStates: ['ASC', 'DESC'], + + childEls: [ + 'titleEl', 'triggerEl', 'textEl' + ], + + + noWrap: true, + + renderTpl: [ + '', + '{%this.renderContainer(out,values)%}' + ], + + + + + + + dataIndex: null, + + + text: ' ', + + + + + menuText: null, + + + emptyCellText: ' ', + + + sortable: true, + + + + + + + + + + + resizable: true, + + + hideable: true, + + + menuDisabled: false, + + + renderer: false, + + + + + + + editRenderer: false, + + + align: 'left', + + + draggable: true, + + + + + tooltipType: 'qtip', + + + + initDraggable: Ext.emptyFn, + + + tdCls: '', + + + + + + + + + + + isHeader: true, + + + isColumn: true, + + 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', + + + detachOnRemove : true, + + + initResizable: Ext.emptyFn, + + initComponent: function() { + var me = this, + renderer; + + if (me.header != null) { + me.text = me.header; + me.header = null; + } + + + if (me.columns != null) { + me.isGroupHeader = true; + + if (me.dataIndex) { + Ext.Error.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.Error.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.'); + } + + + me.items = me.columns; + me.columns = me.flex = me.width = null; + me.cls = (me.cls||'') + ' ' + me.groupHeaderCls; + + + me.sortable = me.resizable = false; + me.align = 'center'; + } else { + + + + if (me.flex) { + me.minWidth = me.minWidth || Ext.grid.plugin.HeaderResizer.prototype.minColWidth; + } + } + me.addCls(Ext.baseCSSPrefix + 'column-header-align-' + me.align); + + renderer = me.renderer; + if (renderer) { + + + if (typeof renderer == 'string') { + me.renderer = Ext.util.Format[renderer]; + } + me.hasCustomRenderer = true; + } else if (me.defaultRenderer) { + me.renderer = me.defaultRenderer; + me.usingDefaultRenderer = true; + } + + + me.callParent(arguments); + }, + + initItems: function() { + var me = this; + + me.callParent(arguments); + + if (me.isGroupHeader) { + if (!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, + ownerHeaderCt = me.getOwnerHeaderCt(); + + if (child.isColumn) { + child.isSubHeader = true; + child.addCls(this.groupSubHeaderCls); + } + + if (me.hidden && child.isColumn) { + + if (!ownerHeaderCt) { + child.hide(); + } else if (!child.hidden) { + me.show(); + } + } + me.callParent(arguments); + }, + + onRemove: function(child) { + var me = this; + + if (child.isSubHeader) { + child.isSubHeader = false; + child.removeCls(me.groupSubHeaderCls); + } + me.callParent(arguments); + + + if (me.isGroupHeader && !me.hasVisibleChildColumns()) { + 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 + }); + }, + + applyColumnState: function (state) { + var me = this; + + + me.applyColumnsState(state.columns); + + + + 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, + + 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; + } + } else if (me.isSubHeader && me.ownerCt.hidden) { + + delete me.hidden; + } + + if ('width' in state) { + delete state.flex; + } + return state; + }, + + getStateId: function () { + return this.stateId || this.headerId; + }, + + + setText: function(text) { + this.text = text; + if (this.rendered) { + this.textEl.update(text); + } + }, + + + getIndex: function() { + return this.isGroupColumn ? false : this.getOwnerHeaderCt().getHeaderIndex(this); + }, + + + getVisibleIndex: function() { + return this.isGroupColumn ? false : Ext.Array.indexOf(this.getOwnerHeaderCt().getVisibleGridColumns(), this); + }, + + beforeRender: function() { + var me = this, + grid = me.up('tablepanel'); + + me.callParent(); + + + + if (grid && (!me.sortable || grid.sortableColumns === false) && !me.groupable && + !me.lockable && (grid.enableColumnHide === false || + !me.getOwnerHeaderCt().getHideableColumns().length)) { + me.menuDisabled = true; + } + + me.protoEl.unselectable(); + }, + + afterRender: function() { + var me = this, + triggerEl = me.triggerEl; + + me.callParent(arguments); + + if (triggerEl && me.self.triggerElWidth === undefined) { + triggerEl.setStyle('display', 'block'); + me.self.triggerElWidth = triggerEl.getWidth(); + triggerEl.setStyle('display', ''); + } + }, + + + + afterComponentLayout: function(width, height, oldWidth, oldHeight) { + var me = this, + ownerHeaderCt = me.getOwnerHeaderCt(); + + me.callParent(arguments); + + if (ownerHeaderCt && (oldWidth != null || me.flex) && width !== oldWidth) { + ownerHeaderCt.onHeaderResize(me, width); + } + }, + + onDestroy: function() { + var me = this; + + Ext.destroy(me.textEl, me.keyNav, me.field); + me.keyNav = 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); + }, + + + onTitleElDblClick: function(e, t) { + var me = this, + prev, + leafColumns, + headerCt; + + + if (me.isOnLeftEdge(e)) { + + + + prev = me.previousNode('gridcolumn:not([hidden]):not([isGroupHeader])'); + + + if (prev && prev.getOwnerHeaderCt() === me.getOwnerHeaderCt()) { + prev.autoSize(); + } + } + + else if (me.isOnRightEdge(e)) { + + + if (me.isGroupHeader && e.getPoint().isContainedBy(me.layout.innerCt)) { + leafColumns = me.query('gridcolumn:not([hidden]):not([isGroupHeader])'); + me.getOwnerHeaderCt().autoSizeColumn(leafColumns[leafColumns.length - 1]); + return; + } else { + headerCt = me.getOwnerHeaderCt(); + + + if (headerCt.visibleColumnManager.getColumns().length === 1 && headerCt.forceFit) { + return; + } + } + me.autoSize(); + } + }, + + + autoSize: function() { + var me = this, + leafColumns, + numLeaves, i, + headerCt; + + + if (me.isGroupHeader) { + leafColumns = me.query('gridcolumn:not([hidden]):not([isGroupHeader])'); + numLeaves = leafColumns.length; + headerCt = this.getOwnerHeaderCt(); + Ext.suspendLayouts(); + for (i = 0; i < numLeaves; i++) { + headerCt.autoSizeColumn(leafColumns[i]); + } + Ext.resumeLayouts(true); + return; + } + this.getOwnerHeaderCt().autoSizeColumn(this); + }, + + onTitleElClick: function(e, t) { + var me = this, + isTriggerClick; + + + + isTriggerClick = me.triggerEl && (e.target === me.triggerEl.dom || t === me.triggerEl.dom || e.within(me.triggerEl)); + + + if (!isTriggerClick && !me.isOnLeftEdge(e) && !me.isOnRightEdge(e) || e.getKey()) { + me.toggleSortState(); + } + return isTriggerClick; + }, + + + processEvent: function(type, view, cell, recordIndex, cellIndex, e) { + return this.fireEvent.apply(this, arguments); + }, + + toggleSortState: function() { + var me = this, + idx, + nextIdx; + + if (me.sortable) { + idx = Ext.Array.indexOf(me.possibleSortStates, me.sortState); + + nextIdx = (idx + 1) % me.possibleSortStates.length; + me.sort(); + } + }, + + sort: function(direction) { + var me = this, + grid = me.up('tablepanel'), + store = grid.store; + + + + + if (grid.ownerLockable && store.isNodeStore) { + store = grid.ownerLockable.lockedGrid.store; + } + + + + + me.sorting = true; + store.sort(me.getSortParam(), direction, grid.multiColumnSort ? 'multi' : 'replace'); + delete me.sorting; + }, + + + getSortParam: function() { + return this.dataIndex; + }, + + + + + setSortState: function(sorter) { + var me = this, + direction = sorter && sorter.direction, + ascCls = me.ascSortCls, + descCls = me.descSortCls, + ownerHeaderCt = me.getOwnerHeaderCt(); + + switch (direction) { + case 'DESC': + me.addCls(descCls); + me.removeCls(ascCls); + break; + case 'ASC': + me.addCls(ascCls); + me.removeCls(descCls); + break; + default: + me.removeCls([ascCls, descCls]); + } + + if (direction) { + ownerHeaderCt.fireEvent('sortchange', ownerHeaderCt, me, direction); + } + }, + + + 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) { + var visibleChildren, + count; + + + + if (!this.isXType('headercontainer')) { + result.result = false; + return false; + } + + + + + visibleChildren = this.query('>:not([hidden]):not([menuDisabled])'); + count = visibleChildren.length; + if (Ext.Array.contains(visibleChildren, result.hideCandidate)) { + count--; + } + if (count) { + return false; + } + + result.hideCandidate = this; + }, + + + isLockable: function() { + var result = { + result: this.lockable !== false + }; + + if (result.result) { + this.ownerCt.bubble(this.hasMultipleVisibleChildren, null, [result]); + } + return result.result; + }, + + + isLocked: function() { + return this.locked || !!this.up('[isColumn][locked]', '[isRootHeader]'); + }, + + + + hasMultipleVisibleChildren: function(result) { + + if (!this.isXType('headercontainer')) { + result.result = false; + return false; + } + + if (this.query('>:not([hidden])').length > 1) { + return false; + } + }, + + hide: function(fromOwner) { + var me = this, + ownerHeaderCt = me.getOwnerHeaderCt(), + owner = me.ownerCt, + ownerIsGroup, + item, items, len, i; + + + + if (!ownerHeaderCt) { + me.callParent(); + return me; + } + + if (me.rendered && !me.isVisible()) { + + return me; + } + + + + + if (ownerHeaderCt.forceFit) { + me.visibleSiblingCount = ownerHeaderCt.getVisibleGridColumns().length - 1; + if (me.flex) { + me.savedWidth = me.getWidth(); + me.flex = null; + } + } + + ownerIsGroup = owner.isGroupHeader; + + + if (ownerIsGroup && !fromOwner) { + items = owner.query('>:not([hidden])'); + + if (items.length === 1 && items[0] === me) { + me.ownerCt.hide(); + return; + } + } + + Ext.suspendLayouts(); + + if (me.isGroupHeader) { + items = me.items.items; + for (i = 0, len = items.length; i < len; i++) { + item = items[i]; + if (!item.hidden) { + item.hide(true); + } + } + } + + me.callParent(); + + + ownerHeaderCt.onHeaderHide(me); + + Ext.resumeLayouts(true); + return me; + }, + + show: function(fromOwner, fromChild) { + var me = this, + ownerHeaderCt = me.getOwnerHeaderCt(), + ownerCt = me.ownerCt, + items, + len, i, + item; + + if (me.isVisible()) { + return me; + } + + if (me.rendered) { + + if (ownerHeaderCt.forceFit) { + ownerHeaderCt.applyForceFit(me); + } + } + + Ext.suspendLayouts(); + + + if (me.isSubHeader && ownerCt.hidden) { + ownerCt.show(false, true); + } + + me.callParent(arguments); + + + if (me.isGroupHeader && fromChild !== true && !me.query(':not([hidden])').length) { + items = me.items.items; + for (i = 0, len = items.length; i < len; i++) { + item = items[i]; + if (item.hidden) { + item.show(true); + } + } + } + + + ownerCt = me.getOwnerHeaderCt(); + if (ownerCt) { + ownerCt.onHeaderShow(me); + } + + Ext.resumeLayouts(true); + return me; + + }, + + getCellWidth: function() { + var me = this, + result, + checkBorderBox = true; + + if (me.rendered && me.componentLayout && me.componentLayout.lastComponentSize) { + + + + + + + result = me.componentLayout.lastComponentSize.width; + } else if (me.width) { + result = me.width; + } + + + + else if (!me.isColumn) { + result = me.getTableWidth(); + checkBorderBox = false; + } + + + + if (checkBorderBox && !Ext.isBorderBox && me.ownerCt.columnLines) { + + + if (me.columnBorderWidth == null && me.rendered) { + me.self.prototype.columnBorderWidth = me.el.getBorderWidth('lr'); + } + result -= me.columnBorderWidth; + } + return result; + }, + + getCellId: function() { + return Ext.baseCSSPrefix + 'grid-cell-headerId-' + this.getItemId(); + }, + + getCellSelector: function() { + return '.' + this.getCellId(); + }, + + getCellInnerSelector: function() { + return this.getCellSelector() + ' .' + Ext.baseCSSPrefix + 'grid-cell-inner'; + }, + + isOnLeftEdge: function(e) { + return (e.getXY()[0] - this.getX() < this.handleWidth); + }, + + isOnRightEdge: function(e) { + return (this.getX() + this.getWidth() - e.getXY()[0] <= this.handleWidth); + }, + + + + setMenuActive: function(isMenuOpen) { + this.titleEl[isMenuOpen ? 'addCls' : 'removeCls'](this.headerOpenCls); + } + + + + + + +}); + + + +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', + isTreeColumn: true, + + cellTpl: [ + '', + 'lineempty" role="presentation"/>', + '', + '-end
-plus {expanderCls}" role="presentation"/>', + '', + ' {checkboxCls}-checked"/>', + '
', + 'leafparent {iconCls}"', + 'style="background-image:url({icon})"/>', + '', + '{value}', + '', + '{value}', + '' + ], + + initComponent: function() { + var me = this, + renderer = me.renderer; + + if (typeof renderer == 'string') { + renderer = Ext.util.Format[renderer]; + } + me.origRenderer = renderer; + me.origScope = me.scope || window; + + me.renderer = me.treeRenderer; + me.scope = me; + + me.callParent(); + }, + + treeRenderer: function(value, metaData, record, rowIdx, colIdx, store, view){ + var me = this, + cls = record.get('cls'), + rendererData; + + if (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, + renderer = me.origRenderer, + 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, + 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(), + isLast: data.isLast, + blankUrl: Ext.BLANK_IMAGE_URL, + href: data.href, + hrefTarget: data.hrefTarget, + lines: lines, + metaData: metaData, + + + + + childCls: me.getChildCls ? me.getChildCls() + ' ' : '', + value: renderer ? renderer.apply(me.origScope, arguments) : value + }; + } +}); + + + +Ext.define('Ext.data.Tree', { + alias: 'data.tree', + + mixins: { + observable: Ext.util.Observable + }, + + + root: null, + + + constructor: function(root) { + var me = this; + + me.mixins.observable.constructor.call(me); + + if (root) { + me.setRootNode(root); + } + + + me.on({ + scope: me, + idchanged: me.onNodeIdChanged, + insert: me.onNodeInsert, + append: me.onNodeAppend, + remove: me.onNodeRemove + }); + }, + + + getRootNode: function() { + return this.root; + }, + + + setRootNode: function(node) { + var me = this; + + me.root = node; + + + if (node.rootOf) { + node.rootOf.removeRootNode(); + } + + + else if (node.parentNode) { + node.parentNode.removeChild(node); + } + + + node.rootOf = me; + + if (node.fireEventArgs('beforeappend', [null, node]) !== false) { + node.set('root', true); + + node.updateInfo(true, { + isFirst: true, + isLast: true, + depth: 0, + index: 0, + parentId: null + }); + + + + + + + + + + + + + + + + + + me.nodeHash = {}; + node.fireEvent('append', null, node); + node.fireEvent('rootchange', node); + } + + return node; + }, + + + removeRootNode: function() { + var me = this, + root = me.root; + + root.set('root', false); + root.fireEvent('remove', null, root, false); + root.fireEvent('rootchange', null); + + + + root.rootOf = me.root = null; + return root; + }, + + + flatten: function(){ + return Ext.Object.getValues(this.nodeHash); + }, + + + onNodeInsert: function(parent, node) { + this.registerNode(node, true); + }, + + + onNodeAppend: function(parent, node) { + this.registerNode(node, true); + }, + + + onNodeRemove: function(parent, node) { + this.unregisterNode(node, true); + }, + + + onNodeIdChanged: function(node, oldId, newId, oldInternalId) { + var nodeHash = this.nodeHash; + + delete nodeHash[oldId || oldInternalId]; + nodeHash[newId] = node; + }, + + + getNodeById: function(id) { + return this.nodeHash[id]; + }, + + + registerNode: function(node, includeChildren) { + var me = this, + children, length, i; + + + me.nodeHash[node.getId() || node.internalId] = node; + if (includeChildren === true) { + children = node.childNodes; + length = children.length; + for (i = 0; i < length; i++) { + me.registerNode(children[i], true); + } + } + }, + + + unregisterNode: function(node, includeChildren) { + var me = this, + children, length, i; + + delete me.nodeHash[node.getId() || node.internalId]; + if (includeChildren === true) { + children = node.childNodes; + length = children.length; + for (i = 0; i < length; i++) { + me.unregisterNode(children[i], true); + } + } + }, + + + sort: function(sorterFn, recursive) { + this.getRootNode().sort(sorterFn, recursive); + }, + + + filter: function(filters, recursive) { + this.getRootNode().filter(filters, recursive); + } +}); + + + +Ext.define('Ext.data.TreeModel', { + extend: Ext.data.Model , + + + + + mixins: { + queryable: Ext.Queryable + }, + + getRefItems: function() { + return this.childNodes; + }, + + getRefOwner: function() { + return this.parentNode; + } +}, +function () { + Ext.data.NodeInterface.decorate(this); +}); + + + +Ext.define('Ext.data.TreeStore', { + extend: Ext.data.AbstractStore , + alias: 'store.tree', + + + + + + + + + + + clearOnLoad : true, + + + clearRemovedOnLoad: true, + + + nodeParam: 'node', + + + defaultRootId: 'root', + + + defaultRootText: 'Root', + + + defaultRootProperty: 'children', + + fillCount: 0, + + + folderSort: false, + + constructor: function(config) { + var me = this, + root, + fields, + model; + + config = Ext.apply({}, config); + + + fields = config.fields || me.fields; + model = config.model || me.model; + + + if (!model) { + if (!fields) { + fields = [{ + name: 'text', type: 'string' + }]; + } + if (me.defaultRootProperty !== me.self.prototype.defaultRootProperty) { + fields.push({ + name: me.defaultRootProperty, + type: 'auto', + defaultValue: null, + persist: false + }); + } + + + config.model = Ext.define(null, { + extend: 'Ext.data.TreeModel', + fields: fields, + proxy: me.proxy || me.defaultProxyType + }); + delete me.fields; + me.implicitModel = true; + } + + me.callParent([config]); + + + me.tree = new Ext.data.Tree(); + + + me.tree.treeStore = me; + + + + + + + + + + + + + + + + + me.onBeforeSort(); + + root = me.root; + if (root) { + delete me.root; + me.setRootNode(root); + } + + }, + + + setProxy: function(proxy) { + var reader, + needsRoot; + + if (proxy instanceof Ext.data.proxy.Proxy) { + + needsRoot = Ext.isEmpty(proxy.getReader().root); + } else if (Ext.isString(proxy)) { + + needsRoot = true; + } else { + + reader = proxy.reader; + needsRoot = !(reader && !Ext.isEmpty(reader.root)); + } + proxy = this.callParent(arguments); + + + + + proxy.idParam = this.nodeParam; + + if (needsRoot) { + reader = proxy.getReader(); + reader.root = this.defaultRootProperty; + + reader.buildExtractors(true); + } + return proxy; + }, + + + filter: function(filters, value) { + if (Ext.isString(filters)) { + filters = { + property: filters, + value: value + }; + } + + var me = this, + decoded = me.decodeFilters(filters), + i, + length = decoded.length, + root = me.getRootNode(), + filteredNodes; + + + for (i = 0; i < length; i++) { + me.filters.replace(decoded[i]); + } + + filters = me.filters.items; + + if (filters.length) { + filteredNodes = []; + me.filterFn = Ext.util.Filter.createFilterFn(filters); + + root.cascadeBy({ + after: function(node) { + node.set('visible', me.filterFn(node)); + } + }); + for (i = 0, length = root.childNodes.length; i < length; i++) { + if (root.childNodes[i].get('visible')) { + filteredNodes.push(root.childNodes[i]); + } + } + + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + } else { + filteredNodes = root.childNodes; + } + root.fireEvent('filterchange', root, filteredNodes); + me.fireEvent('filterchange', me, filters); + }, + + + clearFilter: function() { + var me = this, + root = me.getRootNode(); + + me.filters.clear(); + me.filterFn = null; + root.cascadeBy(function(node) { + node.set('visible', true); + }); + root.fireEvent('filterchange', root, root.childNodes); + me.fireEvent('filterchange', me, []); + }, + + onBeforeSort: function() { + if (this.folderSort) { + this.sort({ + property: 'leaf', + direction: 'ASC' + }, 'prepend', false); + } + }, + + + onBeforeNodeExpand: function(node, callback, scope, args) { + var me = this, + reader = me.proxy.getReader(), + children, + callbackArgs; + + + + if (node.isLoaded()) { + callbackArgs = [node.childNodes]; + if (args) { + callbackArgs.push.apply(callbackArgs, args); + } + Ext.callback(callback, scope || node, callbackArgs); + } + + else if (node.isLoading()) { + me.on('load', function() { + callbackArgs = [node.childNodes]; + if (args) { + callbackArgs.push.apply(callbackArgs, args); + } + Ext.callback(callback, scope || node, callbackArgs); + }, me, { + single: true, + priority: 1001 + }); + } + + else { + + + + children = reader.getRoot(node.raw || node[node.persistenceProperty]); + + if (children || node.phantom) { + if (children) { + me.fillNode(node, reader.extractData(children)); + } + + callbackArgs = [node.childNodes]; + + if (args) { + callbackArgs.push.apply(callbackArgs, args); + } + + Ext.callback(callback, scope || node, callbackArgs); + } + + else { + me.read({ + node: node, + + + internalCallback: function() { + + + delete me.lastOptions.internalCallback; + callbackArgs = [node.childNodes]; + if (args) { + callbackArgs.push.apply(callbackArgs, args); + } + Ext.callback(callback, scope || node, callbackArgs); + } + }); + } + } + }, + + + getNewRecords: function() { + return Ext.Array.filter(this.tree.flatten(), this.filterNew); + }, + + + getUpdatedRecords: function() { + return Ext.Array.filter(this.tree.flatten(), this.filterUpdated); + }, + + onNodeRemove: function(parent, node, isMove) { + var me = this; + + node.unjoin(me); + + + + if (!node.phantom && !isMove && !me.loading) { + Ext.Array.include(me.removed, node); + } + + if (me.autoSync && !me.autoSyncSuspended && !isMove) { + me.sync(); + } + }, + + onNodeAdded: function(parent, node) { + var me = this, + reader = me.proxy.getReader(), + data = node.raw || node[node.persistenceProperty], + dataRoot, + isVisible; + + if (me.filterFn) { + isVisible = me.filterFn(node); + node.set('visible', isVisible); + + + if (isVisible) { + parent.set('visible', me.filterFn(parent)); + } + } + + Ext.Array.remove(me.removed, node); + node.join(me); + + if (!node.isLeaf() && !node.isLoaded() && !me.lazyFill) { + dataRoot = reader.getRoot(data); + if (dataRoot) { + me.fillNode(node, reader.extractData(dataRoot)); + } + } + + if (me.autoSync && !me.autoSyncSuspended && (node.phantom || node.dirty)) { + me.sync(); + } + }, + + onNodeSort: function() { + if (this.autoSync && !this.autoSyncSuspended) { + this.sync(); + } + }, + + + setRootNode: function(root, preventLoad) { + var me = this, + model = me.model, + idProperty = model.prototype.idProperty; + + + if (!model.prototype.isNode) { + Ext.data.NodeInterface.decorate(model); + } + + + + + if (!root || !root.isNode) { + + root = Ext.apply({ + id: me.defaultRootId, + text: me.defaultRootText, + allowDrag: false + }, root); + if (root[idProperty] === undefined) { + root[idProperty] = me.defaultRootId; + } + + + root = Ext.ModelManager.create(root, model); + } + + + + me.getProxy().getReader().buildExtractors(true); + + + me.onNodeAdded(null, root); + + + me.tree.setRootNode(root); + + + + + + + if (preventLoad !== true && !root.isLoaded() && (me.autoLoad === true || root.isExpanded())) { + root.data.expanded = false; + root.expand(); + } + + return root; + }, + + + getRootNode: function() { + return this.tree.getRootNode(); + }, + + + getNodeById: function(id) { + return this.tree.getNodeById(id); + }, + + getById: function(id) { + return this.getNodeById(id); + }, + + + load: function(options) { + options = options || {}; + options.params = options.params || {}; + + var me = this, + node = options.node || me.tree.getRootNode(), + callback = options.callback, + scope = options.scope, + operation; + + + + if (!node) { + node = me.setRootNode({ + expanded: true + }, true); + } + + + if (node.data.expanded) { + node.data.loaded = false; + + + + if (me.clearOnLoad) { + node.data.expanded = false; + } + options.callback = function() { + + + + + if (!me.clearOnLoad) { + node.collapse(); + } + node.expand(); + + + Ext.callback(callback, scope, arguments); + }; + } + + + + options.id = node.getId(); + + options = Ext.apply({ + action: 'read', + filters: me.filters.items, + sorters: me.getSorters(), + node: options.node || node + }, options); + + me.lastOptions = options; + + operation = new Ext.data.Operation(options); + + if (me.fireEvent('beforeload', me, operation) !== false) { + + + + me.loading = true; + if (me.clearOnLoad) { + if (me.clearRemovedOnLoad) { + + me.clearRemoved(node); + } + + node.removeAll(false); + } + me.proxy.read(operation, me.onProxyLoad, me); + } + + if (me.loading && node) { + node.set('loading', true); + } + + return me; + }, + + + clearRemoved: function(node) { + var me = this, + removed = me.removed, + id = node.getId(), + removedLength = removed.length, + i = removedLength, + recordsToClear = {}, + newRemoved = [], + removedHash = {}, + removedNode, + targetNode, + targetId; + + if(node === me.getRootNode()) { + + me.removed = []; + return; + } + + + for(; i--;) { + removedNode = removed[i]; + removedHash[removedNode.getId()] = removedNode; + } + + for(i = removedLength; i--;) { + removedNode = removed[i]; + targetNode = removedNode; + while(targetNode && targetNode.getId() !== id) { + + targetId = targetNode.get('parentId'); + targetNode = targetNode.parentNode || me.getNodeById(targetId) || removedHash[targetId]; + } + if(targetNode) { + + recordsToClear[removedNode.getId()] = removedNode; + } + } + + + for(i = 0; i < removedLength; i++) { + removedNode = removed[i]; + if(!recordsToClear[removedNode.getId()]) { + newRemoved.push(removedNode); + } + } + + me.removed = newRemoved; + }, + + + fillNode: function(node, newNodes) { + var me = this, + newNodeCount = newNodes ? newNodes.length : 0, + sorters = me.sorters, + i, sortCollection, + needsIndexSort = false, + performLocalSort = me.sortOnLoad && !me.remoteSort && sorters && sorters.items && sorters.items.length, + node1, node2, rootFill; + + if (newNodeCount) { + + + if (me.filterFn) { + newNodes[0].set('visible', me.filterFn(newNodes[0])); + } + + + for (i = 1; !needsIndexSort && i < newNodeCount; i++) { + + node1 = newNodes[i]; + node2 = newNodes[i - 1]; + + + if (me.filterFn) { + node1.set('visible', me.filterFn(node1)); + } + needsIndexSort = node1[node1.persistenceProperty].index !== node2[node2.persistenceProperty].index; + } + + + if (performLocalSort) { + + if (needsIndexSort) { + me.sorters.insert(0, me.indexSorter); + } + sortCollection = new Ext.util.MixedCollection(); + sortCollection.addAll(newNodes); + sortCollection.sort(me.sorters.items); + newNodes = sortCollection.items; + + + me.sorters.remove(me.indexSorter); + } else if (needsIndexSort) { + Ext.Array.sort(newNodes, me.sortByIndex); + } + } + + node.set('loaded', true); + + + + + + + + rootFill = me.fillCount === 0; + if (rootFill) { + + me.fireEvent('beforefill', me, node, newNodes); + } + ++me.fillCount; + + if (newNodes.length) { + node.appendChild(newNodes, undefined, true); + } + + if (rootFill) { + + me.fireEvent('fillcomplete', me, node, newNodes); + } + --me.fillCount; + + return newNodes; + }, + + + sortByIndex: function(node1, node2) { + return node1[node1.persistenceProperty].index - node2[node2.persistenceProperty].index; + }, + + onIdChanged: function(model, oldId, newId, oldInternalId){ + this.tree.onNodeIdChanged(model, oldId, newId, oldInternalId); + this.callParent(arguments); + }, + + onProxyLoad: function(operation) { + var me = this, + successful = operation.wasSuccessful(), + records = operation.getRecords(), + node = operation.node, + scope = operation.scope || me, + args = [records, operation, successful]; + + me.loading = false; + node.set('loading', false); + if (successful) { + if (!me.clearOnLoad) { + records = me.cleanRecords(node, records); + } + records = me.fillNode(node, records); + } + + + + + Ext.callback(operation.internalCallback, scope, args); + me.fireEvent('read', me, operation.node, records, successful); + me.fireEvent('load', me, operation.node, records, successful); + + Ext.callback(operation.callback, scope, args); + }, + + cleanRecords: function(node, records){ + var nodeHash = {}, + childNodes = node.childNodes, + i = 0, + len = childNodes.length, + out = [], + rec; + + + for (; i < len; ++i) { + nodeHash[childNodes[i].getId()] = true; + } + + for (i = 0, len = records.length; i < len; ++i) { + rec = records[i]; + if (!nodeHash[rec.getId()]) { + out.push(rec); + } + } + + return out; + }, + + + removeAll: function() { + var root = this.getRootNode(); + if (root) { + root.destroy(true); + } + this.fireEvent('clear', this); + }, + + doSort: function(sorterFn) { + var me = this; + if (me.remoteSort) { + + me.load(); + } else { + me.tree.sort(sorterFn, true); + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + } + me.fireEvent('sort', me, me.sorters.getRange()); + } +}, function() { + var proto = this.prototype; + proto.indexSorter = new Ext.util.Sorter({ + sorterFn: proto.sortByIndex + }); +}); + + + +Ext.define('Ext.tree.Panel', { + extend: Ext.panel.Table , + alias: 'widget.treepanel', + alternateClassName: ['Ext.tree.TreePanel', 'Ext.TreePanel'], + + viewType: 'treeview', + selType: 'treemodel', + + treeCls: Ext.baseCSSPrefix + 'tree-panel', + + + deferRowRender: false, + + + 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: 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 = me.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); + } + + if (Ext.isString(store)) { + store = me.store = Ext.StoreMgr.lookup(store); + } else if (!store || !store.isStore) { + store = Ext.apply({ + type: 'tree', + root: me.root, + fields: me.fields, + model: me.model, + folderSort: me.folderSort + }, store); + store = me.store = Ext.StoreMgr.lookup(store); + } else if (me.root) { + store = me.store = Ext.data.StoreManager.lookup(store); + store.setRootNode(me.root); + if (me.folderSort !== undefined) { + store.folderSort = me.folderSort; + store.sort(); + } + } + + + + me.store.singleExpand = me.singleExpand; + + + + + + + me.viewConfig = Ext.apply({ + rootVisible: me.rootVisible, + animate: me.enableAnimations, + singleExpand: me.singleExpand, + node: store.getRootNode(), + hideHeaders: me.hideHeaders + }, me.viewConfig); + + + if (!me.columns) { + if (me.initialConfig.hideHeaders === undefined) { + me.hideHeaders = true; + } + me.addCls(me.autoWidthCls); + me.columns = [{ + xtype : 'treecolumn', + text : 'Name', + width : Ext.isIE6 ? '100%' : 10000, + dataIndex: me.displayField + }]; + } + + if (me.cls) { + cls.push(me.cls); + } + me.cls = cls.join(' '); + + me.callParent(); + + + + me.selModel.treeStore = me.store; + + view = me.getView(); + + + + me.relayEvents(view, [ + + 'checkchange', + + 'afteritemexpand', + + 'afteritemcollapse' + ]); + + + if (!view.isLockingView) { + + if (!view.rootVisible && !me.getRootNode()) { + me.setRootNode({ + expanded: true + }); + } + } + }, + + + + + + bindStore: function(store, initial) { + var me = this; + + me.store = store; + + + me.storeListeners = me.mon(store, { + destroyable: true, + load: me.onStoreLoad, + rootchange: me.onRootChange, + clear: me.onClear, + scope: me + }); + + + me.storeRelayers = me.relayEvents(store, [ + + 'beforeload', + + + 'load' + ]); + + + me.storeRelayers1 = me.mon(store, { + destroyable: true, + + + append: me.createRelayer('itemappend'), + + + remove: me.createRelayer('itemremove'), + + + move: me.createRelayer('itemmove', [0, 4]), + + + insert: me.createRelayer('iteminsert'), + + + beforeappend: me.createRelayer('beforeitemappend'), + + + beforeremove: me.createRelayer('beforeitemremove'), + + + beforemove: me.createRelayer('beforeitemmove'), + + + beforeinsert: me.createRelayer('beforeiteminsert'), + + + expand: me.createRelayer('itemexpand', [0, 1]), + + + collapse: me.createRelayer('itemcollapse', [0, 1]), + + + beforeexpand: me.createRelayer('beforeitemexpand', [0, 1]), + + + beforecollapse: me.createRelayer('beforeitemcollapse', [0, 1]) + }); + + + store.ownerTree = me; + + if (!initial) { + me.view.setRootNode(me.getRootNode()); + } + }, + + + + unbindStore: function() { + var me = this, + store = me.store; + + if (store) { + Ext.destroy(me.storeListeners, me.storeRelayers, me.storeRelayers1); + delete store.ownerTree; + } + }, + + onClear: function(){ + this.view.onClear(); + }, + + + setRootNode: function() { + return this.store.setRootNode.apply(this.store, arguments); + }, + + + getRootNode: function() { + return this.store.getRootNode(); + }, + + onRootChange: function(root) { + this.view.setRootNode(root); + }, + + + getChecked: function() { + return this.getView().getChecked(); + }, + + isItemChecked: function(rec) { + return rec.get('checked'); + }, + + + expandNode: function(record, deep, callback, scope) { + return this.getView().expand(record, deep, callback, scope || this); + }, + + + collapseNode: function(record, deep, callback, scope) { + return this.getView().collapse(record, deep, callback, scope || this); + }, + + + expandAll : function(callback, scope) { + var me = this, + root = me.getRootNode(), + animate = me.enableAnimations; + if (root) { + if (!animate) { + Ext.suspendLayouts(); + } + root.expand(true, callback, scope || me); + if (!animate) { + Ext.resumeLayouts(true); + } + } + }, + + + collapseAll : function(callback, scope) { + var me = this, + root = me.getRootNode(), + animate = me.enableAnimations, + view = me.getView(); + + if (root) { + if (!animate) { + Ext.suspendLayouts(); + } + scope = scope || me; + if (view.rootVisible) { + root.collapse(true, callback, scope); + } else { + root.collapseChildren(true, callback, scope); + } + if (!animate) { + Ext.resumeLayouts(true); + } + } + }, + + + expandPath: function(path, field, separator, callback, scope) { + var me = this, + current = me.getRootNode(), + index = 1, + view = me.getView(), + keys, + expander; + + field = field || me.getRootNode().idProperty; + separator = separator || '/'; + + if (Ext.isEmpty(path)) { + Ext.callback(callback, scope || me, [false, null]); + return; + } + + keys = path.split(separator); + if (current.get(field) != keys[1]) { + + Ext.callback(callback, scope || me, [false, current]); + return; + } + + expander = function(){ + if (++index === keys.length) { + Ext.callback(callback, scope || me, [true, current]); + return; + } + var node = current.findChild(field, keys[index]); + if (!node) { + Ext.callback(callback, scope || me, [false, current]); + return; + } + current = node; + current.expand(false, expander); + }; + current.expand(false, expander); + }, + + + selectPath: function(path, field, separator, callback, scope) { + var me = this, + root, + keys, + last; + + field = field || me.getRootNode().idProperty; + separator = separator || '/'; + + keys = path.split(separator); + last = keys.pop(); + if (keys.length > 1) { + me.expandPath(keys.join(separator), field, separator, function(success, node){ + var lastNode = node; + if (success && node) { + node = node.findChild(field, last); + if (node) { + me.getSelectionModel().select(node); + Ext.callback(callback, scope || me, [true, node]); + return; + } + } + Ext.callback(callback, scope || me, [false, lastNode]); + }, me); + } else { + root = me.getRootNode(); + if (root.getId() === last) { + me.getSelectionModel().select(root); + Ext.callback(callback, scope || me, [true, root]); + } else { + Ext.callback(callback, scope || me, [false, null]); + } + } + } +}); + + + +Ext.define('Ext.layout.container.Card', { + + + + extend: Ext.layout.container.Fit , + + alternateClassName: 'Ext.layout.CardLayout', + + alias: 'layout.card', + + + + type: 'card', + + hideInactive: true, + + + deferredRender : false, + + getRenderTree: function () { + var me = this, + activeItem = me.getActiveItem(); + + if (activeItem) { + + + if (activeItem.hasListeners.beforeactivate && activeItem.fireEvent('beforeactivate', activeItem) === false) { + + + + + activeItem = me.activeItem = me.owner.activeItem = null; + } + + + 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) { + + me.renderItems([active], me.getRenderTarget()); + } + }, + + isValidParent : function(item, target, position) { + + + var itemEl = item.el ? item.el.dom : Ext.getDom(item); + return (itemEl && itemEl.parentNode === (target.dom || target)) || false; + }, + + + getActiveItem: function() { + var me = this, + + result = me.parseActiveItem(me.activeItem || (me.owner && me.owner.activeItem)); + + + if (result && me.owner.items.indexOf(result) != -1) { + me.activeItem = result; + } else { + me.activeItem = null; + } + + return me.activeItem; + }, + + + parseActiveItem: function(item) { + if (item && item.isComponent) { + return item; + } else if (typeof item == 'number' || item === undefined) { + return this.getLayoutItems()[item || 0]; + } else { + return this.owner.getComponent(item); + } + }, + + + + configureItem: function(item) { + if (item === this.getActiveItem()) { + item.hidden = false; + } else { + item.hidden = true; + } + this.callParent(arguments); + }, + + onRemove: function(component) { + this.callParent(arguments); + if (component === this.activeItem) { + this.activeItem = null; + } + }, + + + getAnimation: function(newCard, owner) { + var newAnim = (newCard || {}).cardSwitchAnimation; + if (newAnim === false) { + return false; + } + return newAnim || owner.cardSwitchAnimation; + }, + + + getNext: function() { + var wrap = arguments[0], + items = this.getLayoutItems(), + index = Ext.Array.indexOf(items, this.activeItem); + + return items[index + 1] || (wrap ? items[0] : false); + }, + + + next: function() { + var anim = arguments[0], + wrap = arguments[1]; + return this.setActiveItem(this.getNext(wrap), anim); + }, + + + 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); + }, + + + prev: function() { + var anim = arguments[0], + wrap = arguments[1]; + return this.setActiveItem(this.getPrev(wrap), anim); + }, + + + setActiveItem: function(newCard) { + var me = this, + owner = me.owner, + oldCard = me.activeItem, + rendered = owner.rendered, + newIndex; + + newCard = me.parseActiveItem(newCard); + newIndex = owner.items.indexOf(newCard); + + + + if (newIndex == -1) { + newIndex = owner.items.items.length; + Ext.suspendLayouts(); + newCard = owner.add(newCard); + Ext.resumeLayouts(); + } + + + if (newCard && oldCard != newCard) { + + if (newCard.fireEvent('beforeactivate', newCard, oldCard) === false) { + return false; + } + if (oldCard && oldCard.fireEvent('beforedeactivate', oldCard, newCard) === false) { + return false; + } + + if (rendered) { + Ext.suspendLayouts(); + + + if (!newCard.rendered) { + me.renderItem(newCard, me.getRenderTarget(), owner.items.length); + } + + if (oldCard) { + if (me.hideInactive) { + oldCard.hide(); + oldCard.hiddenByLayout = true; + } + oldCard.fireEvent('deactivate', oldCard, newCard); + } + + if (newCard.hidden) { + newCard.show(); + } + + + if (!newCard.hidden) { + me.activeItem = newCard; + } + Ext.resumeLayouts(true); + } else { + me.activeItem = newCard; + } + + newCard.fireEvent('activate', newCard, oldCard); + + return me.activeItem; + } + return false; + } +}); + + + +Ext.define('Ext.grid.plugin.BufferedRendererTreeView', { + override: 'Ext.tree.View', + + onRemove: function(store, records, indices, isMove, removeRange) { + var me = this, + bufferedRenderer = me.bufferedRenderer; + + + if (me.rendered && bufferedRenderer) { + + + if (removeRange) { + bufferedRenderer.onReplace(store, indices[0], records, []); + } + + + else { + bufferedRenderer.refreshView(); + } + } else { + me.callParent([store, records, indices]); + } + } +}); + + + +Ext.define('Ext.grid.plugin.BufferedRendererTableView', { + override: 'Ext.view.Table', + + onReplace: function(store, startIndex, oldRecords, newRecords) { + var me = this, + bufferedRenderer = me.bufferedRenderer; + + + if (me.rendered && bufferedRenderer) { + bufferedRenderer.onReplace(store, startIndex, oldRecords, newRecords); + } else { + me.callParent(arguments); + } + }, + + + onAdd: function(store, records, index) { + var me = this, + bufferedRenderer = me.bufferedRenderer; + + if (me.rendered && bufferedRenderer) { + bufferedRenderer.onReplace(store, index, [], records); + } + + else { + me.callParent([store, records, index]); + } + }, + + onRemove: function(store, records, indices, isMove, removeRange) { + var me = this, + bufferedRenderer = me.bufferedRenderer; + + + if (me.rendered && bufferedRenderer) { + + + if (removeRange) { + bufferedRenderer.onReplace(store, indices[0], records, []); + } + + + else { + bufferedRenderer.refreshView(); + } + } else { + me.callParent([store, records, indices]); + } + }, + + + onDataRefresh: function() { + var me = this; + + if (me.bufferedRenderer) { + + me.all.clear(); + me.bufferedRenderer.onStoreClear(); + } + me.callParent(); + } +}); + + +Ext.define('ExtThemeNeptune.panel.Table', { + override: 'Ext.panel.Table', + bodyBorder: true +}); + + + + +Ext.define('Ext.ux.IFrame', { + extend: Ext.Component , + + alias: 'widget.uxiframe', + + loadMask: 'Loading...', + + src: 'about:blank', + + renderTpl: [ + '' + ], + + initComponent: function () { + this.callParent(); + + this.frameName = this.frameName || this.id + '-frame'; + + this.addEvents( + 'beforeload', + 'load' + ); + + Ext.apply(this.renderSelectors, { + iframeEl: 'iframe' + }); + }, + + initEvents : function() { + var me = this; + me.callParent(); + me.iframeEl.on('load', me.onLoad, me); + }, + + initRenderData: function() { + return Ext.apply(this.callParent(), { + src: this.src, + frameName: this.frameName + }); + }, + + getBody: function() { + var doc = this.getDoc(); + return doc.body || doc.documentElement; + }, + + getDoc: function() { + try { + return this.getWin().document; + } catch (ex) { + return null; + } + }, + + getWin: function() { + var me = this, + name = me.frameName, + win = Ext.isIE + ? me.iframeEl.dom.contentWindow + : window.frames[name]; + return win; + }, + + getFrame: function() { + var me = this; + return me.iframeEl.dom; + }, + + beforeDestroy: function () { + this.cleanupListeners(true); + this.callParent(); + }, + + cleanupListeners: function(destroying){ + var doc, prop; + + if (this.rendered) { + try { + doc = this.getDoc(); + if (doc) { + Ext.EventManager.removeAll(doc); + if (destroying) { + for (prop in doc) { + if (doc.hasOwnProperty && doc.hasOwnProperty(prop)) { + delete doc[prop]; + } + } + } + } + } catch(e) { } + } + }, + + onLoad: function() { + var me = this, + doc = me.getDoc(), + fn = me.onRelayedEvent; + + if (doc) { + try { + Ext.EventManager.removeAll(doc); + + + + + + + Ext.EventManager.on(doc, { + mousedown: fn, + mousemove: fn, + mouseup: fn, + click: fn, + dblclick: fn, + scope: me + }); + } catch(e) { + + } + + + Ext.EventManager.on(this.getWin(), 'beforeunload', me.cleanupListeners, me); + + this.el.unmask(); + this.fireEvent('load', this); + + } else if(me.src && me.src != '') { + + this.el.unmask(); + this.fireEvent('error', this); + } + + + }, + + onRelayedEvent: function (event) { + + + var iframeEl = this.iframeEl, + + + iframeXY = Ext.Element.getTrueXY(iframeEl), + originalEventXY = event.getXY(), + + + + + eventXY = Ext.EventManager.getPageXY(event.browserEvent); + + + + event.xy = [iframeXY[0] + eventXY[0], iframeXY[1] + eventXY[1]]; + + event.injectEvent(iframeEl); + + event.xy = originalEventXY; + }, + + load: function (src) { + var me = this, + text = me.loadMask, + frame = me.getFrame(); + + if (me.fireEvent('beforeload', me, src) !== false) { + if (text && me.el) { + me.el.mask(text); + } + + frame.src = me.src = (src || me.src); + } + } +}); + + + +Ext.define('Ext.Img', { + extend: Ext.Component , + alias: ['widget.image', 'widget.imagecomponent'], + + autoEl: 'img', + + baseCls: Ext.baseCSSPrefix + 'img', + + + src: '', + + + alt: '', + + + title: '', + + + imgCls: '', + + + + ariaRole: 'img', + + maskOnDisable: false, + + initComponent: function() { + if (this.glyph) { + this.autoEl = 'div'; + } + this.callParent(); + }, + + getElConfig: function() { + var me = this, + autoEl = me.autoEl, + config = me.callParent(), + glyphFontFamily = Ext._glyphFontFamily, + glyph = me.glyph, + img, glyphParts; + + + + if (autoEl === 'img' || (Ext.isObject(autoEl) && autoEl.tag === 'img')) { + img = config; + } else if (me.glyph) { + if (typeof glyph === 'string') { + glyphParts = glyph.split('@'); + glyph = glyphParts[0]; + glyphFontFamily = glyphParts[1]; + } + config.html = '&#' + glyph + ';'; + if (glyphFontFamily) { + config.style = 'font-family:' + glyphFontFamily; + } + } else { + config.cn = [img = { + tag: 'img', + role: me.ariaRole, + id: me.id + '-img' + }]; + } + + if (img) { + if (me.imgCls) { + img.cls = (img.cls ? img.cls + ' ' : '') + me.imgCls; + } + + img.src = me.src || Ext.BLANK_IMAGE_URL; + } + + if (me.alt) { + (img || config).alt = me.alt; + } + if (me.title) { + (img || config).title = me.title; + } + + return config; + }, + + onRender: function () { + var me = this, + autoEl = me.autoEl, + el; + + me.callParent(arguments); + + el = me.el; + + if (autoEl === 'img' || (Ext.isObject(autoEl) && autoEl.tag === 'img')) { + me.imgEl = el; + } + else { + me.imgEl = el.getById(me.id + '-img'); + } + }, + + onDestroy: function () { + Ext.destroy(this.imgEl); + this.imgEl = null; + this.callParent(); + }, + + + setSrc: function(src) { + var me = this, + imgEl = me.imgEl; + + me.src = src; + + if (imgEl) { + imgEl.dom.src = src || Ext.BLANK_IMAGE_URL; + } + }, + + setGlyph: function(glyph) { + var me = this, + glyphFontFamily = Ext._glyphFontFamily, + glyphParts, dom; + + if (glyph != me.glyph) { + if (typeof glyph === 'string') { + glyphParts = glyph.split('@'); + glyph = glyphParts[0]; + glyphFontFamily = glyphParts[1]; + } + + dom = me.el.dom; + + dom.innerHTML = '&#' + glyph + ';'; + if (glyphFontFamily) { + dom.style = 'font-family:' + glyphFontFamily; + } + } + } +}); + + + +Ext.define('Ext.grid.View', { + extend: Ext.view.Table , + alias: 'widget.gridview', + + + stripeRows: true, + + autoScroll: true +}); + + + +Ext.define('Ext.grid.Panel', { + extend: Ext.panel.Table , + + alias: ['widget.gridpanel', 'widget.grid'], + alternateClassName: ['Ext.list.ListView', 'Ext.ListView', 'Ext.grid.GridPanel'], + viewType: 'gridview', + + lockable: false, + + + rowLines: true + + + + + + + +}); + + + +Ext.define('Ext.toolbar.TextItem', { + extend: Ext.toolbar.Item , + + alias: 'widget.tbtext', + alternateClassName: 'Ext.Toolbar.TextItem', + + + text: '', + + renderTpl: '{text}', + + baseCls: Ext.baseCSSPrefix + 'toolbar-text', + + ariaRole: null, + + beforeRender : function() { + var me = this; + + me.callParent(); + + Ext.apply(me.renderData, { + text: me.text + }); + }, + + + setText : function(text) { + var me = this; + me.text = text; + if (me.rendered) { + me.el.update(text); + me.updateLayout(); + } + } +}); + + + +Ext.define('Ext.form.CheckboxManager', { + extend: Ext.util.MixedCollection , + singleton: true, + + getByName: function(name, formId) { + return this.filterBy(function(item) { + return item.name == name && item.getFormId() == formId; + }); + } +}); + + + +Ext.define('Ext.form.field.Checkbox', { + extend: Ext.form.field.Base , + alias: ['widget.checkboxfield', 'widget.checkbox'], + alternateClassName: 'Ext.form.Checkbox', + + + componentLayout: 'field', + + + stretchInputElFixed: false, + + childEls: [ + + 'boxLabelEl' + ], + + + fieldSubTpl: [ + '', + { + disableFormats: true, + compiled: true + } + ], + + subTplInsertions: [ + + 'beforeBoxLabelTpl', + + + 'afterBoxLabelTpl', + + + 'beforeBoxLabelTextTpl', + + + 'afterBoxLabelTextTpl', + + + 'boxLabelAttrTpl', + + + 'inputAttrTpl' + ], + + + isCheckbox: true, + + + focusCls: 'form-checkbox-focus', + + + + + extraFieldBodyCls: Ext.baseCSSPrefix + 'form-cb-wrap', + + + checked: false, + + + checkedCls: Ext.baseCSSPrefix + 'form-cb-checked', + + + + + boxLabelCls: Ext.baseCSSPrefix + 'form-cb-label', + + + boxLabelAlign: 'after', + + afterLabelCls: Ext.baseCSSPrefix + 'form-cb-after', + + wrapInnerCls: Ext.baseCSSPrefix + 'form-cb-wrap-inner', + + + + + + noBoxLabelCls: Ext.baseCSSPrefix + 'form-cb-wrap-inner-no-box-label', + + + inputValue: 'on', + + + + + + + + + checkChangeEvents: [], + inputType: 'checkbox', + ariaRole: 'checkbox', + + + onRe: /^on$/i, + + + inputCls: Ext.baseCSSPrefix + 'form-cb', + + initComponent: function() { + var me = this, + value = me.value; + + if (value !== undefined) { + me.checked = me.isChecked(value, me.inputValue); + } + + me.callParent(arguments); + me.getManager().add(me); + }, + + initValue: function() { + var me = this, + checked = !!me.checked; + + + me.originalValue = me.lastValue = checked; + + + me.setValue(checked); + }, + + getElConfig: function() { + var me = this; + + + if (me.isChecked(me.rawValue, me.inputValue)) { + me.addCls(me.checkedCls); + } + + return me.callParent(); + }, + + getSubTplData: function() { + var me = this, + boxLabel = me.boxLabel, + boxLabelAlign = me.boxLabelAlign, + labelAlignedBefore = boxLabel && boxLabelAlign === 'before'; + + return Ext.apply(me.callParent(), { + disabled: me.readOnly || me.disabled, + wrapInnerCls: me.wrapInnerCls, + boxLabel: boxLabel, + boxLabelCls: me.boxLabelCls, + boxLabelAlign: boxLabelAlign, + labelAlignedBefore: labelAlignedBefore, + afterLabelCls: labelAlignedBefore ? me.afterLabelCls : '', + noBoxLabelCls: !boxLabel ? me.noBoxLabelCls : '', + role: me.ariaRole + }); + }, + + initEvents: function() { + var me = this; + me.callParent(); + me.mon(me.inputEl, 'click', me.onBoxClick, me); + }, + + + setBoxLabel: function(boxLabel){ + var me = this; + + me.boxLabel = boxLabel; + if (me.rendered) { + me.boxLabelEl.update(boxLabel); + } + }, + + + onBoxClick: function(e) { + var me = this; + if (!me.disabled && !me.readOnly) { + this.setValue(!this.checked); + } + }, + + + getRawValue: function() { + return this.checked; + }, + + + getValue: function() { + return this.checked; + }, + + + getSubmitValue: function() { + var unchecked = this.uncheckedValue, + uncheckedVal = Ext.isDefined(unchecked) ? unchecked : null; + return this.checked ? this.inputValue : uncheckedVal; + }, + + isChecked: function(rawValue, inputValue) { + return (rawValue === true || rawValue === 'true' || rawValue === '1' || rawValue === 1 || + (((Ext.isString(rawValue) || Ext.isNumber(rawValue)) && inputValue) ? rawValue == inputValue : this.onRe.test(rawValue))); + }, + + + setRawValue: function(value) { + var me = this, + inputEl = me.inputEl, + checked = me.isChecked(value, me.inputValue); + + if (inputEl) { + me[checked ? 'addCls' : 'removeCls'](me.checkedCls); + } + + me.checked = me.rawValue = checked; + if (!me.duringSetValue) { + me.lastValue = checked; + } + return checked; + }, + + + setValue: function(checked) { + var me = this, + boxes, i, len, box; + + + + + + if (Ext.isArray(checked)) { + boxes = me.getManager().getByName(me.name, me.getFormId()).items; + len = boxes.length; + + for (i = 0; i < len; ++i) { + box = boxes[i]; + box.setValue(Ext.Array.contains(checked, box.inputValue)); + } + } else { + + + me.duringSetValue = true; + me.callParent(arguments); + delete me.duringSetValue; + } + + return me; + }, + + + valueToRaw: Ext.identityFn, + + + onChange: function(newVal, oldVal) { + var me = this, + handler = me.handler; + if (handler) { + handler.call(me.scope || me, me, newVal); + } + me.callParent(arguments); + }, + + resetOriginalValue: function( fromBoxInGroup){ + var me = this, + boxes, + box, + len, + i; + + + if (!fromBoxInGroup) { + boxes = me.getManager().getByName(me.name, me.getFormId()).items; + len = boxes.length; + + for (i = 0; i < len; ++i) { + box = boxes[i]; + if (box !== me) { + boxes[i].resetOriginalValue(true); + } + } + } + me.callParent(); + }, + + + beforeDestroy: function(){ + this.callParent(); + this.getManager().removeAtKey(this.id); + }, + + + getManager: function() { + return Ext.form.CheckboxManager; + }, + + onEnable: function() { + var me = this, + inputEl = me.inputEl; + me.callParent(); + if (inputEl) { + + inputEl.dom.disabled = me.readOnly; + } + }, + + setReadOnly: function(readOnly) { + var me = this, + inputEl = me.inputEl; + if (inputEl) { + + inputEl.dom.disabled = !!readOnly || me.disabled; + } + me.callParent(arguments); + }, + + getFormId: function(){ + var me = this, + form; + + if (!me.formId) { + form = me.up('form'); + if (form) { + me.formId = form.id; + } + } + return me.formId; + } +}); + + + +Ext.define('Ext.grid.feature.Feature', { + extend: Ext.util.Observable , + alias: 'feature.feature', + + wrapsItem: false, + + + isFeature: true, + + + disabled: false, + + + hasFeatureEvent: true, + + + eventPrefix: null, + + + eventSelector: null, + + + view: null, + + + grid: null, + + constructor: function(config) { + this.initialConfig = config; + this.callParent(arguments); + }, + + clone: function() { + return new this.self(this.initialConfig); + }, + + init: Ext.emptyFn, + + destroy: function(){ + this.clearListeners(); + }, + + + getFireEventArgs: function(eventName, view, featureTarget, e) { + return [eventName, view, featureTarget, e]; + }, + + vetoEvent: Ext.emptyFn, + + + enable: function() { + this.disabled = false; + }, + + + disable: function() { + this.disabled = true; + } + +}); + + + +Ext.define('Ext.grid.feature.RowWrap', { + extend: Ext.grid.feature.Feature , + alias: 'feature.rowwrap', + + rowWrapTd: 'td.' + Ext.baseCSSPrefix + 'grid-rowwrap', + + + hasFeatureEvent: false, + + tableTpl: { + before: function(values, out) { + if (values.view.bufferedRenderer) { + values.view.bufferedRenderer.variableRowHeight = true; + } + }, + priority: 200 + }, + + wrapTpl: [ + '', + '', + '', + '{[values.view.renderRowWrapColumnSizer(out)]}', + '{%', + 'values.itemClasses.length = 0;', + 'this.nextTpl.applyOut(values, out, parent)', + '%}', + '
', + '', + '', { + priority: 200 + } + ], + + getTargetSelector: function () { + return this.itemSelector; + }, + + init: function(grid) { + var me = this, + view = me.view; + + view.addTableTpl(me.tableTpl); + view.addRowTpl(Ext.XTemplate.getTpl(me, 'wrapTpl')); + view.renderRowWrapColumnSizer = me.view.renderColumnSizer; + view.renderColumnSizer = Ext.emptyFn; + + + + view.isRowWrapped = true; + + + view.getTargetSelector = me.getTargetSelector; + }}); + + + +Ext.define('Ext.grid.feature.RowBody', { + extend: Ext.grid.feature.RowWrap , + 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', + + + hasFeatureEvent: true, + + colSpanDecrement: 0, + + tableTpl: { + before: function(values, out) { + + if (!this.rowBody) { + return; + } + + var view = values.view, + rowValues = view.rowValues; + + this.rowBody.setup(values.rows, rowValues); + }, + after: function(values, out) { + + if (!this.rowBody) { + return; + } + + var view = values.view, + rowValues = view.rowValues; + + this.rowBody.cleanup(values.rows, rowValues); + }, + priority: 100 + }, + + extraRowTpl: [ + '{%', + 'values.view.rowBodyFeature.setupRowData(values.record, values.recordIndex, values);', + 'this.nextTpl.applyOut(values, out, parent);', + '%}', + '', + '', + '
{rowBody}
', + '', + '', { + priority: 100, + + syncRowHeights: function(firstRow, secondRow) { + var owner = this.owner, + firstRowBody = Ext.fly(firstRow).down(owner.eventSelector, true), + secondRowBody, + firstHeight, secondHeight; + + + if (firstRowBody && (secondRowBody = Ext.fly(secondRow).down(owner.eventSelector, true))) { + if ((firstHeight = firstRowBody.offsetHeight) > (secondHeight = secondRowBody.offsetHeight)) { + Ext.fly(secondRowBody).setHeight(firstHeight); + } + else if (secondHeight > firstHeight) { + Ext.fly(firstRowBody).setHeight(secondHeight); + } + } + }, + + syncContent: function(destRow, sourceRow) { + var owner = this.owner, + destRowBody = Ext.fly(destRow).down(owner.eventSelector, true), + sourceRowBody; + + + 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(); + + view.rowBodyFeature = me; + + + me.mon(grid.getStore(), 'remove', me.onStoreRemove, me); + + view.headerCt.on({ + columnschanged: me.onColumnsChanged, + scope: me + }); + + view.addTableTpl(me.tableTpl).rowBody = me; + view.addRowTpl(Ext.XTemplate.getTpl(this, 'extraRowTpl')); + + + view.mouseOverOutBuffer = 0; + + me.callParent(arguments); + }, + + onStoreRemove: function(store, model, index){ + var view = this.view, + node; + + if (view.rendered) { + node = view.getNode(index); + if (node) { + node = Ext.fly(node).next(this.eventSelector); + if (node) { + node.remove(); + } + } + } + }, + + getSelectedRow: function(view, rowIndex) { + var selectedRow = view.getNode(rowIndex, false); + if (selectedRow) { + return Ext.fly(selectedRow).down(this.eventSelector); + } + return null; + }, + + + 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].colSpan = colspan; + } + }, + + + 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 = rowValues.view.getGridColumns().length - this.colSpanDecrement; + }, + + cleanup: function(rows, rowValues) { + rowValues.rowBodyCls = rowValues.rowBodyColspan = rowValues.rowBody = null; + } +}); + + + + + + + +Ext.define('Ext.grid.plugin.RowExpander', { + extend: Ext.AbstractPlugin , + lockableScope: 'normal', + + + + alias: 'plugin.rowexpander', + + + columnWidth: 24, + + + rowBodyTpl: null, + + + lockedTpl: null, + + + expandOnEnter: true, + + + expandOnDblClick: true, + + + selectRowOnExpand: false, + + rowBodyTrSelector: '.' + Ext.baseCSSPrefix + 'grid-rowbody-tr', + rowBodyHiddenCls: Ext.baseCSSPrefix + 'grid-row-body-hidden', + rowCollapsedCls: Ext.baseCSSPrefix + 'grid-row-collapsed', + + addCollapsedCls: { + before: function(values, out) { + var me = this.rowExpander; + if (!me.recordsExpanded[values.record.internalId]) { + values.itemClasses.push(me.rowCollapsedCls); + } + }, + priority: 500 + }, + + + + + setCmp: function(grid) { + var me = this, + features = [], + featuresCfg, rowBodyTpl; + + me.callParent(arguments); + + me.recordsExpanded = {}; + if (!me.rowBodyTpl) { + Ext.Error.raise("The 'rowBodyTpl' config is required and is not defined."); + } + + rowBodyTpl = me.rowBodyTpl = Ext.XTemplate.getTpl(me, 'rowBodyTpl'); + + featuresCfg = { + ftype: 'rowbody', + recordsExpanded: me.recordsExpanded, + rowBodyHiddenCls: me.rowBodyHiddenCls, + rowCollapsedCls: me.rowCollapsedCls, + setupRowData: me.getRowBodyFeatureData, + setup: me.setup + }; + + features.push(Ext.apply({ + lockableScope: 'normal', + getRowBodyContents: function (record) { + return rowBodyTpl.applyTemplate(record.getData()); + } + }, featuresCfg)); + + if (me.lockedTpl) { + features.push(Ext.apply({ + lockableScope: 'locked', + getRowBodyContents: function (record) { + return me.lockedTpl.applyTemplate(record.getData()); + } + }, featuresCfg)); + } + + if (grid.features) { + grid.features = Ext.Array.push(features, grid.features); + } else { + grid.features = features; + } + + }, + + init: function(grid) { + var me = this, + reconfigurable = grid, + view, normalView, lockedView; + + if (grid.lockable) { + grid = grid.lockedGrid; + } + + me.callParent(arguments); + me.grid = grid; + view = me.view = grid.getView(); + + + me.addExpander(); + + + + me.bindView(view); + view.addRowTpl(me.addCollapsedCls).rowExpander = me; + + + + if (grid.ownerLockable) { + + reconfigurable = grid.ownerLockable; + reconfigurable.syncRowHeight = false; + lockedView = reconfigurable.lockedGrid.getView(); + + + + me.bindView(lockedView); + lockedView.addRowTpl(me.addCollapsedCls).rowExpander = me; + + + + reconfigurable.mon(reconfigurable, 'columnschanged', me.refreshRowHeights, me); + reconfigurable.mon(reconfigurable.store, 'datachanged', me.refreshRowHeights, me); + } + reconfigurable.on('beforereconfigure', me.beforeReconfigure, me); + }, + + beforeReconfigure: function(grid, store, columns, oldStore, oldColumns) { + var expander = this.getHeaderConfig(); + expander.locked = true; + columns.unshift(expander); + }, + + + addExpander: function() { + var me = this, + expanderGrid = me.grid, + expanderHeader = me.getHeaderConfig(); + + + if (expanderGrid.ownerLockable) { + expanderGrid = expanderGrid.ownerLockable.lockedGrid; + expanderGrid.width += expanderHeader.width; + + + + + + expanderGrid.hidden = false; + } + + expanderGrid.headerCt.insert(0, expanderHeader); + + + + expanderGrid.getSelectionModel().injectCheckbox = 1; + }, + + getRowBodyFeatureData: function(record, idx, rowValues) { + var me = this; + me.self.prototype.setupRowData.apply(me, arguments); + + rowValues.rowBody = me.getRowBodyContents(record); + rowValues.rowBodyCls = me.recordsExpanded[record.internalId] ? '' : me.rowBodyHiddenCls; + }, + + setup: function(rows, rowValues){ + var me = this; + me.self.prototype.setup.apply(me, arguments); + + if (!me.grid.ownerLockable) { + rowValues.rowBodyColspan -= 1; + } + }, + + bindView: function(view) { + if (this.expandOnEnter) { + view.on('itemkeydown', this.onKeyDown, this); + } + if (this.expandOnDblClick) { + view.on('itemdblclick', this.onDblClick, this); + } + }, + + onKeyDown: function(view, record, row, rowIdx, e) { + if (e.getKey() == e.ENTER) { + var ds = view.store, + sels = view.getSelectionModel().getSelection(), + ln = sels.length, + i = 0; + + for (; i < ln; i++) { + rowIdx = ds.indexOf(sels[i]); + this.toggleRow(rowIdx, sels[i]); + } + } + }, + + onDblClick: function(view, record, row, rowIdx, e) { + this.toggleRow(rowIdx, record); + }, + + toggleRow: function(rowIdx, record) { + var me = this, + view = me.view, + rowNode = view.getNode(rowIdx), + normalRow = Ext.fly(rowNode, '_rowExpander'), + lockedRow, + nextBd = normalRow.down(me.rowBodyTrSelector, true), + wasCollapsed = normalRow.hasCls(me.rowCollapsedCls), + addOrRemoveCls = wasCollapsed ? 'removeCls' : 'addCls', + ownerLockable, fireView, + needsRefresh = view.getSizeModel().height.shrinkWrap, + lockedRowHeight, + normalRowHeight; + + + Ext.suspendLayouts(); + normalRow[addOrRemoveCls](me.rowCollapsedCls); + Ext.fly(nextBd)[addOrRemoveCls](me.rowBodyHiddenCls); + me.recordsExpanded[record.internalId] = wasCollapsed; + + + if (needsRefresh) { + view.refreshSize(); + } + + + if (me.grid.ownerLockable) { + normalRow.setHeight(''); + normalRowHeight = normalRow.getHeight(); + ownerLockable = me.grid.ownerLockable; + fireView = ownerLockable.getView(); + view = ownerLockable.lockedGrid.view; + + + + + normalRow.setHeight(wasCollapsed ? normalRowHeight : ''); + + + lockedRow = Ext.fly(view.getNode(rowIdx), '_rowExpander'); + lockedRow[addOrRemoveCls](me.rowCollapsedCls); + + + if (me.lockedTpl) { + nextBd = lockedRow.down(me.rowBodyTrSelector, true); + Ext.fly(nextBd)[addOrRemoveCls](me.rowBodyHiddenCls); + lockedRowHeight = lockedRow.getHeight(); + if (wasCollapsed) { + if (lockedRowHeight > normalRowHeight) { + normalRow.setHeight(lockedRowHeight); + } else { + lockedRow.setHeight(normalRowHeight); + } + } else { + lockedRow.setHeight(''); + } + } else { + lockedRow.setHeight(wasCollapsed ? normalRowHeight : ''); + } + if (needsRefresh) { + view.refreshSize(); + } + } else { + fireView = view; + } + fireView.fireEvent(wasCollapsed ? 'expandbody' : 'collapsebody', normalRow.dom, record, nextBd); + + Ext.resumeLayouts(true); + }, + + + + + + + + refreshRowHeights: function() { + Ext.globalEvents.on({ + idle: this.doRefreshRowHeights, + scope: this, + single: true + }); + }, + + doRefreshRowHeights: function() { + var me = this, + recordsExpanded = me.recordsExpanded, + key, record, + lockedView = me.grid.ownerLockable.lockedGrid.view, + normalView = me.grid.ownerLockable.normalGrid.view, + normalRow, + lockedRow, + lockedHeight, + normalHeight; + + for (key in recordsExpanded) { + if (recordsExpanded.hasOwnProperty(key)) { + record = this.view.store.data.get(key); + lockedRow = lockedView.getNode(record, false); + normalRow = normalView.getNode(record, false); + lockedRow.style.height = normalRow.style.height = ''; + lockedHeight = lockedRow.offsetHeight; + normalHeight = normalRow.offsetHeight; + if (normalHeight > lockedHeight) { + lockedRow.style.height = normalHeight + 'px'; + } else if (lockedHeight > normalHeight) { + normalRow.style.height = lockedHeight + 'px'; + } + } + } + }, + + getHeaderConfig: function() { + var me = this; + + return { + width: me.columnWidth, + lockable: false, + 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(value, metadata) { + + if (!me.grid.ownerLockable) { + metadata.tdAttr += ' rowspan="2"'; + } + return ''; + }, + processEvent: function(type, view, cell, rowIndex, cellIndex, e, record) { + if (e.getTarget('.' + Ext.baseCSSPrefix + 'grid-row-expander')) { + if (type == "click") { + me.toggleRow(rowIndex, record); + return me.selectRowOnExpand; + } + } + } + }; + } +}); + + + +Ext.define('Ext.ShadowPool', { + singleton: true, + + + markup: (function() { + return Ext.String.format( + '', + Ext.baseCSSPrefix, + Ext.isIE && !Ext.supports.CSS3BoxShadow ? 'ie' : 'css' + ); + }()), + + shadows: [], + + pull: function() { + var sh = this.shadows.shift(); + if (!sh) { + sh = Ext.get(Ext.DomHelper.insertHtml("afterBegin", document.body, this.markup)); + sh.autoBoxAdjust = false; + + sh.dom.setAttribute('data-sticky', true); + } + return sh; + }, + + push: function(sh) { + this.shadows.push(sh); + }, + + reset: function() { + var shadows = [].concat(this.shadows), + s, + sLen = shadows.length; + + for (s = 0; s < sLen; s++) { + shadows[s].remove(); + } + + this.shadows = []; + } +}); + + + +Ext.define('Ext.Shadow', { + + + localXYNames: { + get: 'getLocalXY', + set: 'setLocalXY' + }, + + + constructor: function(config) { + var me = this, + adjusts, + offset, + rad; + + Ext.apply(me, config); + if (!Ext.isString(me.mode)) { + me.mode = me.defaultMode; + } + offset = me.offset; + rad = Math.floor(offset / 2); + me.opacity = 50; + switch (me.mode.toLowerCase()) { + + case "drop": + if (Ext.supports.CSS3BoxShadow) { + adjusts = { + t: offset, + l: offset, + h: -offset, + w: -offset + }; + } + else { + adjusts = { + t: -rad, + l: -rad, + h: -rad, + w: -rad + }; + } + break; + case "sides": + if (Ext.supports.CSS3BoxShadow) { + adjusts = { + t: offset, + l: 0, + h: -offset, + w: 0 + }; + } + else { + adjusts = { + t: - (1 + rad), + l: 1 + rad - 2 * offset, + h: -1, + w: rad - 1 + }; + } + break; + case "frame": + if (Ext.supports.CSS3BoxShadow) { + adjusts = { + t: 0, + l: 0, + h: 0, + w: 0 + }; + } + else { + adjusts = { + t: 1 + rad - 2 * offset, + l: 1 + rad - 2 * offset, + h: offset - rad - 1, + w: offset - rad - 1 + }; + } + break; + case "bottom": + if (Ext.supports.CSS3BoxShadow) { + adjusts = { + t: offset, + l: 0, + h: -offset, + w: 0 + }; + } + else { + adjusts = { + t: offset, + l: 0, + h: 0, + w: 0 + }; + } + break; + } + me.adjusts = adjusts; + }, + + + getShadowSize: function() { + var me = this, + offset = me.el ? me.offset : 0, + result = [offset, offset, offset, offset], + mode = me.mode.toLowerCase(); + + + if (me.el && mode !== 'frame') { + result[0] = 0; + if (mode == 'drop') { + result[3] = 0; + } + } + return result; + }, + + + + + offset: 4, + + + defaultMode: "drop", + + + boxShadowProperty: (function() { + var property = 'boxShadow', + style = document.documentElement.style; + + if (!('boxShadow' in style)) { + if ('WebkitBoxShadow' in style) { + + property = 'WebkitBoxShadow'; + } + else if ('MozBoxShadow' in style) { + + property = 'MozBoxShadow'; + } + } + + return property; + }()), + + + show: function(target) { + var me = this, + index, xy; + + target = Ext.get(target); + + + index = (parseInt(target.getStyle("z-index"), 10) - 1) || 0; + xy = target[me.localXYNames.get](); + + + if (!me.el) { + me.el = Ext.ShadowPool.pull(); + + if (me.fixed) { + me.el.dom.style.position = 'fixed'; + } else { + me.el.dom.style.position = ''; + } + if (me.el.dom.nextSibling != target.dom) { + me.el.insertBefore(target); + } + } + me.el.setStyle("z-index", me.zIndex || index); + if (Ext.isIE && !Ext.supports.CSS3BoxShadow) { + me.el.dom.style.filter = "progid:DXImageTransform.Microsoft.alpha(opacity=" + me.opacity + ") progid:DXImageTransform.Microsoft.Blur(pixelradius=" + (me.offset) + ")"; + } + me.realign( + xy[0], + xy[1], + target.dom.offsetWidth, + target.dom.offsetHeight + ); + me.el.dom.style.display = "block"; + }, + + + isVisible: function() { + return this.el ? true: false; + }, + + + realign: function(l, t, targetWidth, targetHeight) { + if (!this.el) { + return; + } + var adjusts = this.adjusts, + el = this.el, + targetStyle = el.dom.style, + shadowWidth, + shadowHeight, + sws, + shs; + + el[this.localXYNames.set](l + adjusts.l, t + adjusts.t); + shadowWidth = Math.max(targetWidth + adjusts.w, 0); + shadowHeight = Math.max(targetHeight + adjusts.h, 0); + sws = shadowWidth + "px"; + shs = shadowHeight + "px"; + if (targetStyle.width != sws || targetStyle.height != shs) { + targetStyle.width = sws; + targetStyle.height = shs; + + if (Ext.supports.CSS3BoxShadow) { + targetStyle[this.boxShadowProperty] = '0 0 ' + (this.offset + 2) + 'px #888'; + } + } + }, + + + hide: function() { + var me = this; + + if (me.el) { + me.el.dom.style.display = "none"; + Ext.ShadowPool.push(me.el); + delete me.el; + } + }, + + + setZIndex: function(z) { + this.zIndex = z; + if (this.el) { + this.el.setStyle("z-index", z); + } + }, + + + setOpacity: function(opacity){ + if (this.el) { + if (Ext.isIE && !Ext.supports.CSS3BoxShadow) { + opacity = Math.floor(opacity * 100 / 2) / 100; + } + this.opacity = opacity; + this.el.setOpacity(opacity); + } + } +}); + + + +Ext.define('Ext.slider.Thumb', { + + + + + + constructor: function(config) { + var me = this; + + + Ext.apply(me, config || {}, { + cls: Ext.baseCSSPrefix + 'slider-thumb', + + + constrain: false + }); + me.callParent([config]); + }, + + + 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' + }; + }, + + + move: function(v, animate) { + var me = this, + el = me.el, + slider = me.slider, + styleProp = slider.vertical ? 'bottom' : slider.horizontalProp, + to, + from; + + v += '%'; + + if (!animate) { + el.dom.style[styleProp] = v; + } else { + to = {}; + to[styleProp] = v; + + if (!Ext.supports.GetPositionPercentage) { + from = {}; + from[styleProp] = el.dom.style[styleProp]; + } + + new Ext.fx.Anim({ + target: el, + duration: 350, + from: from, + to: to + }); + } + }, + + + enable: function() { + var me = this; + + me.disabled = false; + if (me.el) { + me.el.removeCls(me.slider.disabledCls); + } + }, + + + disable: function() { + var me = this; + + me.disabled = true; + if (me.el) { + me.el.addCls(me.slider.disabledCls); + } + }, + + + initEvents: function() { + var me = this, + el = me.el; + + me.tracker = new Ext.dd.DragTracker({ + onBeforeStart: Ext.Function.bind(me.onBeforeDragStart, me), + onStart : Ext.Function.bind(me.onDragStart, me), + onDrag : Ext.Function.bind(me.onDrag, me), + onEnd : Ext.Function.bind(me.onDragEnd, me), + tolerance : 3, + autoStart : 300, + overCls : Ext.baseCSSPrefix + 'slider-thumb-over' + }); + + me.tracker.initEl(el); + }, + + + onBeforeDragStart : function(e) { + var me = this, + el = me.el, + trackerXY = me.tracker.getXY(), + delta = me.pointerOffset = el.getXY(); + + if (me.disabled) { + return false; + } else { + + + + delta[0] += Math.floor(el.getWidth() / 2) - trackerXY[0]; + delta[1] += Math.floor(el.getHeight() / 2) - trackerXY[1]; + me.slider.promoteThumb(me); + return true; + } + }, + + + 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); + }, + + + onDrag: function(e) { + var me = this, + slider = me.slider, + index = me.index, + newValue = me.getValueFromTracker(), + above, + below; + + + 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 (trackPoint !== undefined) { + return slider.reversePixelValue(trackPoint); + } + }, + + + 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() { + Ext.destroy(this.tracker); + } +}); + + + +Ext.define('Ext.slider.Tip', { + extend: Ext.tip.Tip , + minWidth: 10, + alias: 'widget.slidertip', + + + offsets : null, + + + align: null, + + + 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 + }); + }, + + onSlide : function(slider, e, thumb) { + var me = this; + me.show(); + me.update(me.getText(thumb)); + me.el.alignTo(thumb.el, me.align, me.offsets); + }, + + + getText : function(thumb) { + return String(thumb.value); + } +}); + + + +Ext.define('Ext.layout.component.field.Slider', { + + + + alias: ['layout.sliderfield'], + + extend: Ext.layout.component.field.Field , + + + + type: 'sliderfield', + + beginLayout: function(ownerContext) { + this.callParent(arguments); + + ownerContext.endElContext = ownerContext.getEl('endEl'); + ownerContext.innerElContext = ownerContext.getEl('innerEl'); + ownerContext.bodyElContext = ownerContext.getEl('bodyEl'); + }, + + publishInnerHeight: function (ownerContext, height) { + var innerHeight = height - this.measureLabelErrorHeight(ownerContext), + endElPad, + inputPad; + if (this.owner.vertical) { + endElPad = ownerContext.endElContext.getPaddingInfo(); + inputPad = ownerContext.inputContext.getPaddingInfo(); + ownerContext.innerElContext.setHeight(innerHeight - inputPad.height - endElPad.height); + } else { + ownerContext.bodyElContext.setHeight(innerHeight); + } + }, + + publishInnerWidth: function (ownerContext, width) { + if (!this.owner.vertical) { + var endElPad = ownerContext.endElContext.getPaddingInfo(), + inputPad = ownerContext.inputContext.getPaddingInfo(); + + ownerContext.innerElContext.setWidth(width - inputPad.left - endElPad.right - ownerContext.labelContext.getProp('width')); + } + }, + + beginLayoutFixed: function(ownerContext, width, suffix) { + var me = this, + ieInputWidthAdjustment = me.ieInputWidthAdjustment; + + if (ieInputWidthAdjustment) { + + + me.owner.bodyEl.setStyle('padding-right', ieInputWidthAdjustment + 'px'); + } + + me.callParent(arguments); + } +}); + + + +Ext.define('Ext.slider.Multi', { + extend: Ext.form.field.Base , + alias: 'widget.multislider', + alternateClassName: 'Ext.slider.MultiSlider', + + + + + + + + + + + childEls: [ + 'endEl', 'innerEl' + ], + + + fieldSubTpl: [ + '
tabIndex="{tabIdx}"', + ' aria-orientation="vertical" aria-orientation="horizontal"', + '>', + '', + '
', + { + 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', + + + + + + + vertical: false, + + + minValue: 0, + + + maxValue: 100, + + + decimalPrecision: 0, + + + keyIncrement: 1, + + + increment: 0, + + + + + clickRange: [5,15], + + + clickToChange : true, + + + animate: true, + + + dragging: false, + + + constrainThumbs: true, + + componentLayout: 'sliderfield', + + + useTips : true, + + + tipText : null, + + ariaRole: 'slider', + + + initValue: function() { + var me = this, + extValue = Ext.value, + + values = extValue(me.values, [extValue(me.value, extValue(me.minValue, 0))]), + i = 0, + len = values.length; + + + me.originalValue = values; + + + for (; i < len; i++) { + me.addThumb(me.normalizeValue(values[i])); + } + }, + + + initComponent : function() { + var me = this, + tipPlug, + hasTip, + p, pLen, plugins; + + + me.thumbs = []; + + me.keyIncrement = Math.max(me.increment, me.keyIncrement); + + me.addEvents( + + 'beforechange', + + + 'change', + + + 'changecomplete', + + + 'dragstart', + + + 'drag', + + + 'dragend' + ); + + me.callParent(); + + + 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)); + } + } + }, + + + addThumb: function(value) { + var me = this, + thumb = new Ext.slider.Thumb({ + ownerCt : me, + ownerLayout : me.getComponentLayout(), + value : value, + slider : me, + index : me.thumbs.length, + constrain : me.constrainThumbs, + disabled : !!me.readOnly + }); + + me.thumbs.push(thumb); + + + if (me.rendered) { + thumb.render(); + } + + return thumb; + }, + + + promoteThumb: function(topThumb) { + var thumbs = this.thumbStack || (this.thumbStack = Ext.Array.slice(this.thumbs)), + ln = thumbs.length, + zIndex = 10000, i; + + + if (thumbs[0] !== topThumb) { + Ext.Array.remove(thumbs, topThumb); + thumbs.unshift(topThumb); + } + + + for (i = 0; i < ln; i++) { + thumbs[i].el.setStyle('zIndex', zIndex); + zIndex -= 1000; + } + }, + + + getSubTplData : function() { + var me = this; + + return Ext.apply(me.callParent(), { + $comp: me, + isVertical: me.vertical, + vertical: me.vertical ? Ext.baseCSSPrefix + 'slider-vert' : Ext.baseCSSPrefix + 'slider-horz', + minValue: me.minValue, + maxValue: me.maxValue, + value: me.value, + tabIdx: me.tabIndex, + childElCls: '' + }); + }, + + 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(); + } + }, + + + initEvents : function() { + var me = this; + me.mon(me.el, { + scope : me, + mousedown: me.onMouseDown, + keydown : me.onKeyDown + }); + }, + + onDragStart: Ext.emptyFn, + onDragEnd: Ext.emptyFn, + + + 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, + + + onMouseDown : function(e) { + var me = this, + thumbClicked = false, + i = 0, + thumbs = me.thumbs, + len = thumbs.length, + trackPoint; + + if (me.disabled) { + return; + } + + + for (; i < len; i++) { + thumbClicked = thumbClicked || e.target == thumbs[i].el.dom; + } + + if (me.clickToChange && !thumbClicked) { + trackPoint = me.getTrackpoint(e.getXY()); + if (trackPoint !== undefined) { + me.onClickChange(trackPoint); + } + } + me.focus(); + }, + + + onClickChange : function(trackPoint) { + var me = this, + thumb, index; + + + + + + thumb = me.getNearest(trackPoint); + if (!thumb.disabled) { + index = thumb.index; + me.setValue(index, Ext.util.Format.round(me.reversePixelValue(trackPoint), me.decimalPrecision), undefined, true); + } + }, + + + getNearest: function(trackPoint) { + var me = this, + clickValue = me.reversePixelValue(trackPoint), + nearestDistance = me.getRange() + 5, + 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)) { + nearest = thumb; + nearestDistance = dist; + } + } + return nearest; + }, + + + onKeyDown : function(e) { + + var me = this, + k, + val; + + if(me.disabled || me.thumbs.length !== 1) { + e.preventDefault(); + return; + } + k = e.getKey(); + + switch(k) { + case e.UP: + case e.RIGHT: + e.stopEvent(); + val = e.ctrlKey ? me.maxValue : me.getValue(0) + me.keyIncrement; + me.setValue(0, val, undefined, true); + break; + case e.DOWN: + case e.LEFT: + e.stopEvent(); + val = e.ctrlKey ? me.minValue : me.getValue(0) - me.keyIncrement; + me.setValue(0, val, undefined, true); + break; + default: + e.preventDefault(); + } + }, + + + 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; + }, + + + setMinValue : function(val) { + var me = this, + thumbs = me.thumbs, + len = thumbs.length, + thumb, i; + + me.minValue = val; + + for (i = 0; i < len; ++i) { + thumb = thumbs[i]; + if (thumb.value < val) { + me.setValue(i, val, false); + } + } + me.syncThumbs(); + }, + + + setMaxValue : function(val) { + var me = this, + thumbs = me.thumbs, + len = thumbs.length, + thumb, i; + + me.maxValue = val; + + for (i = 0; i < len; ++i) { + thumb = thumbs[i]; + if (thumb.value > val) { + me.setValue(i, val, false); + } + } + me.syncThumbs(); + }, + + + setValue : function(index, value, animate, changeComplete) { + var me = this, + thumbs = me.thumbs, + 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]; + + value = me.normalizeValue(value); + + if (value !== thumb.value && me.fireEvent('beforechange', me, value, thumb.value, thumb) !== false) { + thumb.value = value; + if (me.rendered) { + thumb.move(me.calculateThumbPosition(value), Ext.isDefined(animate) ? animate !== false : me.animate); + + me.fireEvent('change', me, value, thumb); + me.checkDirty(); + if (changeComplete) { + me.fireEvent('changecomplete', me, value, thumb); + } + } + } + return me; + }, + + + calculateThumbPosition : function(v) { + var me = this, + minValue = me.minValue, + pos = (v - minValue) / me.getRange() * 100; + + + if (isNaN(pos)) { + pos = minValue; + } + + return pos; + }, + + + 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; + }, + + + reversePixelValue : function(pos) { + return this.minValue + (pos / this.getRatio()); + }, + + + 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) { + + + 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(); + } + } + }, + + + 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)); + } + } + }, + + + getValue : function(index) { + return Ext.isNumber(index) ? this.thumbs[index].value : this.getValues(); + }, + + + 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 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; + + Ext.destroy(me.innerEl, me.endEl, me.focusEl); + + for (; t < tLen; t++) { + thumb = thumbs[t]; + + Ext.destroy(thumb); + } + + me.callParent(); + } +}); + + + +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'], + + + getValue: function() { + + return this.callParent([0]); + }, + + + setValue: function(value, animate) { + var args = arguments, + len = args.length; + + + + + if (len == 1 || (len <= 3 && typeof args[1] != 'number')) { + args = Ext.toArray(args); + args.unshift(0); + } + return this.callParent(args); + }, + + + getNearest : function(){ + + return this.thumbs[0]; + } +}); + + + +Ext.define('Ext.menu.Item', { + extend: Ext.Component , + alias: 'widget.menuitem', + alternateClassName: 'Ext.menu.TextItem', + + + isMenuItem: true, + + mixins: { + queryable: Ext.Queryable + }, + + + + + + + activeCls: Ext.baseCSSPrefix + 'menu-item-active', + + + ariaRole: 'menuitem', + + + canActivate: true, + + + clickHideDelay: 0, + + + destroyMenu: true, + + + disabledCls: Ext.baseCSSPrefix + 'menu-item-disabled', + + + + + + + hideOnClick: true, + + + + + + + + + + + + + menuAlign: 'tl-tr?', + + + menuExpandDelay: 200, + + + menuHideDelay: 200, + + + + + + + tooltipType: 'qtip', + + 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', + + childEls: [ + 'itemEl', 'iconEl', 'textEl', 'arrowEl' + ], + + renderTpl: [ + '', + '{text}', + '', + ' target="{hrefTarget}"', + ' hidefocus="true"', + + ' unselectable="on"', + '', + ' tabIndex="{tabIndex}"', + '', + '>', + '{text}', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '' + ], + + maskOnDisable: false, + + iconAlign: 'left', + + + + + + activate: function(skipCheck) { + var me = this; + + if (skipCheck || (!me.activated && me.canActivate && me.rendered && !me.isDisabled() && me.isVisible())) { + if (!me.plain) { + me.el.addCls(me.activeCls); + } + + + + me.focus(false, true); + me.activated = true; + if (me.hasListeners.activate) { + me.fireEvent('activate', me); + } + } + }, + + getFocusEl: function() { + return this.itemEl; + }, + + deactivate: function() { + var me = this, + parent; + + if (me.activated) { + parent = me.up(''); + if (!me.plain) { + me.el.removeCls(me.activeCls); + } + + + + if (parent) { + parent.focus(false, true); + } + me.hideMenu(); + me.activated = false; + if (me.hasListeners.deactivate) { + me.fireEvent('deactivate', me); + } + } + }, + + deferHideMenu: function() { + if (this.menu.isVisible()) { + this.menu.hide(); + } + }, + + cancelDeferHide: function(){ + clearTimeout(this.hideMenuTimer); + }, + + deferHideParentMenus: function() { + var ancestor; + Ext.menu.Manager.hideAll(); + + if (!Ext.Element.getActiveElement()) { + + ancestor = this.up(':not([hidden])'); + if (ancestor) { + ancestor.focus(); + } + } + }, + + expandMenu: function(delay) { + var me = this; + + if (me.menu) { + me.cancelDeferHide(); + if (delay === 0) { + me.doExpandMenu(); + } else { + clearTimeout(me.expandMenuTimer); + me.expandMenuTimer = Ext.defer(me.doExpandMenu, Ext.isNumber(delay) ? delay : me.menuExpandDelay, me); + } + } + }, + + doExpandMenu: function() { + var me = this, + menu = me.menu; + + if (me.activated && (!menu.rendered || !menu.isVisible())) { + me.parentMenu.activeChild = menu; + menu.ownerCmp = menu.ownerItem = me; + menu.parentMenu = me.parentMenu; + menu.constrainTo = document.body; + menu.showBy(me, me.menuAlign); + } + }, + + getRefItems: function(deep) { + var menu = this.menu, + items; + + if (menu) { + items = menu.getRefItems(deep); + items.unshift(menu); + } + return items || []; + }, + + hideMenu: function(delay) { + var me = this; + + if (me.menu) { + clearTimeout(me.expandMenuTimer); + me.hideMenuTimer = Ext.defer(me.deferHideMenu, Ext.isNumber(delay) ? delay : me.menuHideDelay, me); + } + }, + + initComponent: function() { + var me = this, + prefix = Ext.baseCSSPrefix, + cls = [prefix + 'menu-item'], + menu; + + me.addEvents( + + 'activate', + + + 'click', + + + 'deactivate', + + + 'textchange', + + + 'iconchange' + ); + + if (me.plain) { + cls.push(prefix + 'menu-item-plain'); + } + + if (me.cls) { + cls.push(me.cls); + } + + me.cls = cls.join(' '); + + if (me.menu) { + menu = me.menu; + delete me.menu; + me.setMenu(menu); + } + + me.callParent(arguments); + }, + + onClick: function (e) { + var me = this, + clickHideDelay = me.clickHideDelay, + browserEvent = e.browserEvent, + preventDefault; + + if (!me.href || me.disabled) { + e.stopEvent(); + } + + if (me.disabled || me.handlingClick) { + return; + } + + if (me.hideOnClick) { + if (!clickHideDelay) { + me.deferHideParentMenus(); + } else { + me.deferHideParentMenusTimer = Ext.defer(me.deferHideParentMenus, clickHideDelay, me); + } + } + + Ext.callback(me.handler, me.scope || me, [me, e]); + me.fireEvent('click', me, e); + + + + + + + + + + if (Ext.isIE9m || Ext.isIEQuirks) { + + preventDefault = (browserEvent.returnValue === false) ? true : false; + } else { + preventDefault = !!browserEvent.defaultPrevented; + } + + if (me.href && !preventDefault) { + me.handlingClick = true; + me.itemEl.dom.click(); + delete me.handlingClick; + } + + if (!me.hideOnClick) { + me.focus(); + } + }, + + onRemoved: function() { + var me = this; + + + if (me.activated && me.parentMenu.activeItem === me) { + me.parentMenu.deactivateActiveItem(); + } + me.callParent(arguments); + me.parentMenu = me.ownerCmp = me.ownerButton = null; + }, + + + beforeDestroy: function() { + var me = this; + if (me.rendered) { + me.clearTip(); + } + me.callParent(); + }, + + onDestroy: function() { + var me = this; + + clearTimeout(me.expandMenuTimer); + 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; + + me.callParent(); + + if (hasIcon) { + if (hasMenu && me.showCheckbox) { + + + 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, { + 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, + groupCls: me.group ? me.groupCls : '', + tabIndex: me.tabIndex + }); + }, + + onRender: function() { + var me = this; + + me.callParent(arguments); + + if (me.tooltip) { + me.setTooltip(me.tooltip, true); + } + }, + + + setMenu: function(menu, destroyMenu) { + var me = this, + oldMenu = me.menu, + arrowEl = me.arrowEl; + + if (oldMenu) { + oldMenu.ownerCmp = oldMenu.ownerItem = oldMenu.parentMenu = null; + + if (destroyMenu === true || (destroyMenu !== false && me.destroyMenu)) { + Ext.destroy(oldMenu); + } + } + if (menu) { + me.menu = Ext.menu.Manager.get(menu); + me.menu.ownerCmp = me.menu.ownerItem = me; + } else { + me.menu = null; + } + + if (me.rendered && !me.destroying && arrowEl) { + arrowEl[me.menu ? 'addCls' : 'removeCls'](me.arrowCls); + } + }, + + + setHandler: function(fn, scope) { + this.handler = fn || null; + this.scope = scope; + }, + + + 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); + }, + + + 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); + }, + + + setText: function(text) { + var me = this, + el = me.textEl || me.el, + oldText = me.text; + + me.text = text; + + if (me.rendered) { + el.update(text || ''); + + me.ownerCt.updateLayout(); + } + me.fireEvent('textchange', me, oldText, text); + }, + + 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(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; + } + +}); + + + +Ext.define('Ext.menu.CheckItem', { + extend: Ext.menu.Item , + alias: 'widget.menucheckitem', + + + + + + + + + + + checkedCls: Ext.baseCSSPrefix + 'menu-item-checked', + + uncheckedCls: Ext.baseCSSPrefix + 'menu-item-unchecked', + + groupCls: Ext.baseCSSPrefix + 'menu-group-icon', + + + hideOnClick: false, + + + checkChangeDisabled: false, + + ariaRole: 'menuitemcheckbox', + + childEls: [ + 'itemEl', 'iconEl', 'textEl', 'checkEl' + ], + + showCheckbox: true, + + isMenuCheckItem: true, + + checkboxCls: Ext.baseCSSPrefix + 'menu-item-checkbox', + + initComponent: function() { + var me = this; + + + me.checked = !!me.checked; + me.addEvents( + + 'beforecheckchange', + + + 'checkchange' + ); + + me.callParent(arguments); + + Ext.menu.Manager.registerCheckable(me); + + if (me.group) { + if (me.initialConfig.hideOnClick !== false) { + me.hideOnClick = true; + } + } + }, + + beforeRender: function() { + var me = this; + + me.callParent(); + Ext.apply(me.renderData, { + checkboxCls: me.checkboxCls, + showCheckbox: me.showCheckbox + }); + }, + + afterRender: function() { + var me = this; + me.callParent(); + me.checked = !me.checked; + me.setChecked(!me.checked, true); + if (me.checkChangeDisabled) { + me.disableCheckChange(); + } + }, + + + disableCheckChange: function() { + var me = this, + checkEl = me.checkEl; + + if (checkEl) { + checkEl.addCls(me.disabledCls); + } + + + if (!(Ext.isIE10p || (Ext.isIE9 && Ext.isStrict)) && me.rendered) { + me.el.repaint(); + } + me.checkChangeDisabled = true; + }, + + + enableCheckChange: function() { + var me = this, + checkEl = me.checkEl; + + if (checkEl) { + checkEl.removeCls(me.disabledCls); + } + me.checkChangeDisabled = false; + }, + + onClick: function(e) { + var me = this; + if(!me.disabled && !me.checkChangeDisabled && !(me.checked && me.group)) { + me.setChecked(!me.checked); + } + this.callParent([e]); + }, + + onDestroy: function() { + Ext.menu.Manager.unregisterCheckable(this); + this.callParent(arguments); + }, + + + setChecked: function(checked, suppressEvents) { + var me = this, + checkedCls = me.checkedCls, + uncheckedCls = me.uncheckedCls, + el = me.el; + + 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); + } + } + me.checked = checked; + Ext.menu.Manager.onCheckChange(me, checked); + if (!suppressEvents) { + Ext.callback(me.checkHandler, me.scope || me, [me, checked]); + me.fireEvent('checkchange', me, checked); + } + } + } +}); + + + +Ext.define('Ext.menu.KeyNav', { + extend: Ext.util.KeyNav , + + constructor: function(config) { + var me = this; + + me.menu = config.target; + me.callParent([Ext.apply({ + down: me.down, + enter: me.enter, + esc: me.escape, + left: me.left, + right: me.right, + space: me.enter, + tab: me.tab, + up: me.up + }, config)]); + }, + + down: function(e) { + var me = this, + fi = me.menu.focusedItem; + + if (fi && e.getKey() == Ext.EventObject.DOWN && me.isWhitelisted(fi)) { + return true; + } + me.focusNextItem(1); + }, + + enter: function(e) { + var menu = this.menu, + focused = menu.focusedItem; + + if (menu.activeItem) { + menu.onClick(e); + } else if (focused && focused.isFormField) { + + return true; + } + }, + + escape: function(e) { + Ext.menu.Manager.hideAll(); + }, + + focusNextItem: function(step) { + var menu = this.menu, + items = menu.items, + focusedItem = menu.focusedItem, + startIdx = focusedItem ? items.indexOf(focusedItem) : -1, + idx = startIdx + step, + len = items.length, + count = 0, + item; + + + while (count < len && idx !== startIdx) { + if (idx < 0) { + idx = len - 1; + } else if (idx >= len) { + idx = 0; + } + + item = items.getAt(idx); + if (menu.canActivateItem(item)) { + menu.setActiveItem(item); + break; + } + idx += step; + ++count; + } + }, + + isWhitelisted: function(item) { + var mgr = Ext['FocusManager']; + + return mgr && mgr.isWhitelisted(item); + }, + + left: function(e) { + var menu = this.menu, + fi = menu.focusedItem; + + if (fi && this.isWhitelisted(fi)) { + return true; + } + + if (menu.parentMenu) { + menu.hide(); + menu.parentMenu.focus(); + } + }, + + right: function(e) { + var menu = this.menu, + fi = menu.focusedItem, + ai = menu.activeItem, + am; + + if (fi && this.isWhitelisted(fi)) { + return true; + } + + if (ai) { + am = menu.activeItem.menu; + if (am) { + ai.expandMenu(0); + am.setActiveItem(am.child(':focusable')); + } + } + }, + + tab: function(e) { + var me = this; + + if (e.shiftKey) { + me.up(e); + } else { + me.down(e); + } + }, + + up: function(e) { + var me = this, + fi = me.menu.focusedItem; + + if (fi && e.getKey() == Ext.EventObject.UP && me.isWhitelisted(fi)) { + return true; + } + me.focusNextItem(-1); + } +}); + + + +Ext.define('Ext.menu.Separator', { + extend: Ext.menu.Item , + alias: 'widget.menuseparator', + + + + + canActivate: false, + + + + + + + + focusable: false, + + + + + + + hideOnClick: false, + + + + + + + + + + + + + + + plain: true, + + + separatorCls: Ext.baseCSSPrefix + 'menu-item-separator', + + + text: ' ', + + ariaRole: 'separator', + + beforeRender: function(ct, pos) { + var me = this; + + me.callParent(); + + me.addCls(me.separatorCls); + } +}); + + + +Ext.define('Ext.menu.Menu', { + extend: Ext.panel.Panel , + alias: 'widget.menu', + + + + + + + + + + + + + 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?', + + + + initComponent: function() { + var me = this, + prefix = Ext.baseCSSPrefix, + cls = [prefix + 'menu'], + bodyCls = me.bodyCls ? [me.bodyCls] : [], + isFloating = me.floating !== false; + + me.addEvents( + + 'click', + + + 'mouseenter', + + + 'mouseleave', + + + 'mouseover' + ); + + Ext.menu.Manager.register(me); + + + if (me.plain) { + cls.push(prefix + 'menu-plain'); + } + me.cls = cls.join(' '); + + + bodyCls.push(prefix + 'menu-body', Ext.dom.Element.unselectableCls); + me.bodyCls = bodyCls.join(' '); + + + + + + if (!me.layout) { + me.layout = { + type: 'vbox', + align: 'stretchmax', + overflowHandler: 'Scroller' + }; + } + + if (isFloating) { + + if (me.minWidth === undefined) { + me.minWidth = me.defaultMinWidth; + } + } else { + + me.hidden = !!me.initialConfig.hidden; + me.constrain = false; + } + + me.callParent(arguments); + + + + Ext.override(me.getLayout(), { + configureItem: me.configureItem + }); + }, + + + + + initFloatConstrain: Ext.emptyFn, + + + + initHierarchyEvents: Ext.emptyFn, + + + + getHierarchyState: function() { + var result = this.callParent(); + result.hidden = this.hidden; + return result; + }, + + beforeRender: function() { + this.callParent(arguments); + + + + if (!this.getSizeModel().width.shrinkWrap) { + this.layout.align = 'stretch'; + } + }, + + onBoxReady: function() { + var me = this; + + me.callParent(arguments); + + + if (me.showSeparator) { + me.iconSepEl = me.layout.getElementTarget().insertFirst({ + role: 'presentation', + cls: Ext.baseCSSPrefix + 'menu-icon-separator', + html: ' ' + }); + } + + me.mon(me.el, { + click: me.onClick, + mouseover: me.onMouseOver, + scope: me + }); + me.mouseMonitor = me.el.monitorMouseLeave(100, me.onMouseLeave, me); + + + if (me.enableKeyNav) { + me.keyNav = new Ext.menu.KeyNav({ + target: me, + keyMap: me.getKeyMap() + }); + } + }, + + + canActivateItem: function(item) { + return item && !item.isDisabled() && item.isVisible() && (item.canActivate || !item.isMenuItem); + }, + + + deactivateActiveItem: function(andBlurFocusedItem) { + var me = this, + activeItem = me.activeItem, + focusedItem = me.focusedItem; + + if (activeItem) { + activeItem.deactivate(); + if (!activeItem.activated) { + delete me.activeItem; + } + } + + + + if (focusedItem && andBlurFocusedItem) { + focusedItem.blur(); + delete me.focusedItem; + } + }, + + + getFocusEl: function() { + return this.focusedItem || this.items.items[0]; + }, + + + hide: function() { + this.deactivateActiveItem(true); + this.callParent(arguments); + }, + + + getItemFromEvent: function(e) { + var me = this, + renderTarget = me.layout.getRenderTarget().dom, + toEl = e.getTarget(); + + + 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); + } + + + + cmp.minWidth = cmp.minWidth || me.minWidth; + + return cmp; + }, + + + 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; + }, + + + lookupItemFromString: function(cmp) { + return (cmp == 'separator' || cmp == '-') ? + new Ext.menu.Separator() + : new Ext.menu.Item({ + canActivate: false, + hideOnClick: false, + plain: true, + text: cmp + }); + }, + + + + + + configureItem: function(cmp) { + var me = this, + owner = me.owner, + prefix = Ext.baseCSSPrefix, + cls; + + if (me.owner.items.getCount() > 1 && !cmp.rendered && !cmp.isMenuItem && !cmp.dock) { + cls = [prefix + 'menu-item-cmp']; + + + + + if (!owner.plain && (cmp.indent !== false || cmp.iconCls === 'no-icon')) { + cls.push(prefix + 'menu-item-indent'); + } + + if (cmp.rendered) { + cmp.el.addCls(cls); + } else { + cmp.cls = (cmp.cls || '') + ' ' + cls.join(' '); + } + } + this.callParent(arguments); + }, + + onClick: function(e) { + var me = this, + item; + + if (me.disabled) { + e.stopEvent(); + return; + } + + item = (e.type === 'click') ? me.getItemFromEvent(e) : me.activeItem; + if (item && item.isMenuItem) { + if (!item.menu || !me.ignoreParentClicks) { + item.onClick(e); + } else { + e.stopEvent(); + } + } + + if (!item || item.disabled) { + item = undefined; + } + me.fireEvent('click', me, item, e); + }, + + onDestroy: function() { + var me = this; + + Ext.menu.Manager.unregister(me); + me.parentMenu = me.ownerCmp = me.ownerButton = null; + if (me.rendered) { + me.el.un(me.mouseMonitor); + Ext.destroy(me.keyNav); + me.keyNav = null; + } + me.callParent(arguments); + }, + + onMouseLeave: function(e) { + var me = this; + + me.deactivateActiveItem(); + + if (me.disabled) { + return; + } + + me.fireEvent('mouseleave', me, 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; + } + + + if (item && !item.activated) { + me.setActiveItem(item); + if (item.activated && item.expandMenu) { + item.expandMenu(); + } + } + if (mouseEnter) { + me.fireEvent('mouseenter', me, e); + } + me.fireEvent('mouseover', me, item, e); + }, + + setActiveItem: function(item) { + var me = this; + + if (item && (item != me.activeItem)) { + me.deactivateActiveItem(); + if (me.canActivateItem(item)) { + if (item.activate) { + + item.activate(true); + if (item.activated) { + me.activeItem = item; + me.focusedItem = item; + } + } else { + item.focus(); + me.focusedItem = item; + } + } + + } + }, + + beforeShow: function() { + var me = this, + viewHeight; + + + if (me.floating) { + 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; + + me.callParent(arguments); + + + if (me.floating) { + me.maxHeight = me.savedMaxHeight; + } + } +}); + + +Ext.define('ExtThemeNeptune.menu.Menu', { + override: 'Ext.menu.Menu', + showSeparator: false +}); + + +Ext.define('ExtThemeNeptune.menu.Separator', { + override: 'Ext.menu.Separator', + border: true +}); + + + + +Ext.define('Ext.layout.component.field.Trigger', { + + + + alias: 'layout.triggerfield', + + extend: Ext.layout.component.field.Field , + + + + type: 'triggerfield', + + + + setWidthInDom: true, + + + borderWidths: {}, + + beginLayout: function(ownerContext) { + var me = this, + owner = me.owner, + flags; + + ownerContext.triggerWrap = ownerContext.getEl('triggerWrap'); + + me.callParent(arguments); + + + flags = owner.getTriggerStateFlags(); + if (flags != owner.lastTriggerStateFlags) { + owner.lastTriggerStateFlags = flags; + me.updateEditState(); + } + }, + + beginLayoutCycle: function(ownerContext){ + this.callParent(arguments); + + + if (ownerContext.widthModel.shrinkWrap && !this.owner.inputWidth) { + ownerContext.inputContext.el.setStyle('width', ''); + } + }, + + beginLayoutFixed: function (ownerContext, width, suffix) { + var me = this, + owner = ownerContext.target, + ieInputWidthAdjustment = me.ieInputWidthAdjustment || 0, + inputWidth = '100%', + triggerWrap = owner.triggerWrap; + + me.callParent(arguments); + + owner.inputCell.setStyle('width', '100%'); + if(ieInputWidthAdjustment) { + me.adjustIEInputPadding(ownerContext); + if(suffix === 'px') { + if (owner.inputWidth) { + inputWidth = owner.inputWidth - me.getExtraWidth(ownerContext); + } else { + inputWidth = width - ieInputWidthAdjustment - me.getExtraWidth(ownerContext); + } + inputWidth += 'px'; + } + } + owner.inputEl.setStyle('width', inputWidth); + inputWidth = owner.inputWidth; + if (inputWidth) { + triggerWrap.setStyle('width', inputWidth + (ieInputWidthAdjustment) + 'px'); + } else { + triggerWrap.setStyle('width', width + suffix); + } + triggerWrap.setStyle('table-layout', 'fixed'); + }, + + adjustIEInputPadding: function(ownerContext) { + + this.owner.inputCell.setStyle('padding-right', this.ieInputWidthAdjustment + 'px'); + }, + + + getExtraWidth: function(ownerContext) { + var me = this, + owner = me.owner, + borderWidths = me.borderWidths, + ui = owner.ui + owner.triggerEl.getCount(); + + if (!(ui in borderWidths)) { + borderWidths[ui] = ownerContext.triggerWrap.getBorderInfo().width + } + return borderWidths[ui] + owner.getTriggerWidth(); + }, + + beginLayoutShrinkWrap: function (ownerContext) { + var owner = ownerContext.target, + emptyString = '', + inputWidth = owner.inputWidth, + triggerWrap = owner.triggerWrap; + + this.callParent(arguments); + + if (inputWidth) { + triggerWrap.setStyle('width', inputWidth + 'px'); + inputWidth = (inputWidth - this.getExtraWidth(ownerContext)) + 'px'; + owner.inputEl.setStyle('width', inputWidth); + owner.inputCell.setStyle('width', inputWidth); + } else { + owner.inputCell.setStyle('width', emptyString); + owner.inputEl.setStyle('width', emptyString); + triggerWrap.setStyle('width', emptyString); + triggerWrap.setStyle('table-layout', 'auto'); + } + }, + + getTextWidth: function () { + var me = this, + owner = me.owner, + inputEl = owner.inputEl, + value; + + + value = (inputEl.dom.value || (owner.hasFocus ? '' : owner.emptyText) || '') + owner.growAppend; + return inputEl.getTextWidth(value); + }, + + publishOwnerWidth: function(ownerContext, width) { + var owner = this.owner; + this.callParent(arguments); + if (!owner.grow && !owner.inputWidth) { + width -= this.getExtraWidth(ownerContext); + if (owner.labelAlign != 'top') { + width -= owner.getLabelWidth(); + } + ownerContext.inputContext.setWidth(width); + } + }, + + publishInnerHeight: function(ownerContext, height) { + ownerContext.inputContext.setHeight(height - this.measureLabelErrorHeight(ownerContext)); + }, + + measureContentWidth: function (ownerContext) { + var me = this, + owner = me.owner, + width = me.callParent(arguments), + inputContext = ownerContext.inputContext, + calcWidth, max, min; + + if (owner.grow && !ownerContext.state.growHandled) { + calcWidth = me.getTextWidth() + ownerContext.inputContext.getFrameInfo().width; + + max = owner.growMax; + min = Math.min(max, width); + max = Math.max(owner.growMin, max, min); + + + calcWidth = Ext.Number.constrain(calcWidth, owner.growMin, max); + inputContext.setWidth(calcWidth); + ownerContext.state.growHandled = true; + + + inputContext.domBlock(me, 'width'); + width = NaN; + } else if (!owner.inputWidth) { + width -= me.getExtraWidth(ownerContext); + } + return width; + }, + + updateEditState: function() { + var me = this, + owner = me.owner, + inputEl = owner.inputEl, + noeditCls = Ext.baseCSSPrefix + 'trigger-noedit', + displayed, + readOnly; + + if (me.owner.readOnly) { + inputEl.addCls(noeditCls); + readOnly = true; + displayed = false; + } else { + if (me.owner.editable) { + inputEl.removeCls(noeditCls); + readOnly = false; + } else { + inputEl.addCls(noeditCls); + readOnly = true; + } + displayed = !me.owner.hideTrigger; + } + + owner.triggerCell.setDisplayed(displayed); + inputEl.dom.readOnly = readOnly; + } +}); + + + +Ext.define('Ext.form.field.Trigger', { + extend: Ext.form.field.Text , + alias: ['widget.triggerfield', 'widget.trigger'], + + alternateClassName: ['Ext.form.TriggerField', 'Ext.form.TwinTriggerField', 'Ext.form.Trigger'], + + childEls: [ + + { name: 'triggerCell', select: '.' + Ext.baseCSSPrefix + 'trigger-cell' }, + { name: 'triggerEl', select: '.' + Ext.baseCSSPrefix + 'form-trigger' }, + + + 'triggerWrap', + + + 'inputCell' + ], + + + + + triggerBaseCls: Ext.baseCSSPrefix + 'form-trigger', + + + triggerWrapCls: Ext.baseCSSPrefix + 'form-trigger-wrap', + + + triggerNoEditCls: Ext.baseCSSPrefix + 'trigger-noedit', + + + hideTrigger: false, + + + editable: true, + + + readOnly: false, + + + + + repeatTriggerClick: false, + + + + autoSize: Ext.emptyFn, + + monitorTab: true, + + mimicing: false, + + triggerIndexRe: /trigger-index-(\d+)/, + + extraTriggerCls: '', + + componentLayout: 'triggerfield', + + initComponent: function() { + this.wrapFocusCls = this.triggerWrapCls + '-focus'; + this.callParent(arguments); + }, + + initEvents: function() { + + + + + + + + + + this.mon(Ext.globalEvents, 'beforefocus', this.onOtherFocus, this); + this.callParent(); + }, + + getSubTplMarkup: function(values) { + var me = this, + childElCls = values.childElCls, + field = me.callParent(arguments); + + return [ + '', + '', + '', + '', + me.getTriggerMarkup(), + '', + '', + '' + ].join(''); + }, + + getSubTplData: function(){ + var me = this, + data = me.callParent(), + readOnly = me.readOnly === true, + editable = me.editable !== false; + + return Ext.apply(data, { + editableCls: (readOnly || !editable) ? ' ' + me.triggerNoEditCls : '', + readOnly: !editable || readOnly + }); + }, + + getLabelableRenderData: function() { + var me = this, + triggerWrapCls = me.triggerWrapCls, + result = me.callParent(arguments); + + return Ext.applyIf(result, { + triggerWrapCls: triggerWrapCls, + triggerMarkup: me.getTriggerMarkup() + }); + }, + + getTriggerMarkup: function() { + var me = this, + i = 0, + hideTrigger = (me.readOnly || me.hideTrigger), + triggerCls, + triggerBaseCls = me.triggerBaseCls, + triggerConfigs = [], + unselectableCls = Ext.dom.Element.unselectableCls, + style = 'width:' + me.triggerWidth + 'px;' + (hideTrigger ? 'display:none;' : ''), + cls = me.extraTriggerCls + ' ' + Ext.baseCSSPrefix + 'trigger-cell ' + unselectableCls; + + + + + + if (!me.trigger1Cls) { + me.trigger1Cls = me.triggerCls; + } + + + for (i = 0; (triggerCls = me['trigger' + (i + 1) + 'Cls']) || i < 1; i++) { + triggerConfigs.push({ + tag: 'td', + role: 'presentation', + valign: 'top', + cls: cls, + style: style, + cn: { + cls: [Ext.baseCSSPrefix + 'trigger-index-' + i, triggerBaseCls, triggerCls].join(' '), + role: 'presentation' + } + }); + } + triggerConfigs[0].cn.cls += ' ' + triggerBaseCls + '-first'; + + return Ext.DomHelper.markup(triggerConfigs); + }, + + disableCheck: function() { + return !this.disabled; + }, + + + beforeRender: function() { + var me = this, + triggerBaseCls = me.triggerBaseCls, + tempEl; + + + if (!me.triggerWidth) { + tempEl = Ext.getBody().createChild({ + role: 'presentation', + style: 'position: absolute;', + cls: Ext.baseCSSPrefix + 'form-trigger' + }); + Ext.form.field.Trigger.prototype.triggerWidth = tempEl.getWidth(); + tempEl.remove(); + } + + me.callParent(); + + if (triggerBaseCls !== Ext.baseCSSPrefix + 'form-trigger') { + + + me.addChildEls({ name: 'triggerEl', select: '.' + triggerBaseCls }); + } + + + me.lastTriggerStateFlags = me.getTriggerStateFlags(); + }, + + onRender: function() { + var me = this; + + me.callParent(arguments); + + me.doc = Ext.getDoc(); + me.initTrigger(); + }, + + + getTriggerWidth: function() { + var me = this, + totalTriggerWidth = 0; + + if (me.triggerWrap && !me.hideTrigger && !me.readOnly) { + totalTriggerWidth = me.triggerEl.getCount() * me.triggerWidth; + } + return totalTriggerWidth; + }, + + setHideTrigger: function(hideTrigger) { + if (hideTrigger != this.hideTrigger) { + this.hideTrigger = hideTrigger; + this.updateLayout(); + } + }, + + + setEditable: function(editable) { + if (editable != this.editable) { + this.editable = editable; + this.updateLayout(); + } + }, + + + setReadOnly: function(readOnly) { + var me = this, + old = me.readOnly; + + me.callParent(arguments); + if (readOnly != old) { + me.updateLayout(); + } + }, + + + initTrigger: function() { + var me = this, + triggerWrap = me.triggerWrap, + triggerEl = me.triggerEl, + disableCheck = me.disableCheck, + els, len, el, i, idx, cls; + + if (me.repeatTriggerClick) { + me.triggerRepeater = new Ext.util.ClickRepeater(triggerWrap, { + preventDefault: true, + handler: me.onTriggerWrapClick, + listeners: { + mouseup: me.onTriggerWrapMouseup, + scope: me + }, + scope: me + }); + } else { + me.mon(triggerWrap, { + click: me.onTriggerWrapClick, + mouseup: me.onTriggerWrapMouseup, + scope: me + }); + } + + triggerEl.setVisibilityMode(Ext.Element.DISPLAY); + triggerEl.addClsOnOver(me.triggerBaseCls + '-over', disableCheck, me); + + els = triggerEl.elements; + len = els.length; + + for (i = 0; i < len; i++) { + el = els[i]; + idx = i + 1; + cls = me['trigger' + (idx) + 'Cls']; + if (cls) { + el.addClsOnOver(cls + '-over', disableCheck, me); + el.addClsOnClick(cls + '-click', disableCheck, me); + } + } + + triggerEl.addClsOnClick(me.triggerBaseCls + '-click', disableCheck, me); + + }, + + + onDestroy: function() { + var me = this; + Ext.destroyMembers(me, 'triggerRepeater', 'triggerWrap', 'triggerEl'); + delete me.doc; + me.callParent(); + }, + + + onFocus: function() { + var me = this; + + + me.otherFocused = false; + + me.callParent(arguments); + if (!me.mimicing) { + me.bodyEl.addCls(me.wrapFocusCls); + me.mimicing = true; + me.mon(me.doc, 'mousedown', me.mimicBlur, me, { + delay: 10 + }); + if (me.monitorTab) { + me.on('specialkey', me.checkTab, me); + } + } + }, + + + checkTab: function(me, e) { + if (!this.ignoreMonitorTab && e.getKey() === e.TAB) { + this.triggerBlur(); + } + }, + + + getTriggerStateFlags: function () { + var me = this, + state = 0; + + if (me.readOnly) { + state += 1; + } + if (me.editable) { + state += 2; + } + if (me.hideTrigger) { + state += 4; + } + return state; + }, + + + + onOtherFocus: function(dom) { + this.otherFocused = (this.hasFocus && !this.bodyEl.contains(dom)); + }, + + + onBlur: function() { + + if (this.blurring || this.otherFocused) { + this.triggerBlur(); + this.otherFocused = false; + } + }, + + + mimicBlur: function(e) { + if (!this.isDestroyed && !this.bodyEl.contains(e.target)) { + this.triggerBlur(e); + } + }, + + + triggerBlur: function(e) { + var me = this; + me.mimicing = false; + me.mun(me.doc, 'mousedown', me.mimicBlur, me); + if (me.monitorTab && me.inputEl) { + me.un('specialkey', me.checkTab, me); + } + Ext.form.field.Trigger.superclass.onBlur.call(me, e); + if (me.bodyEl) { + me.bodyEl.removeCls(me.wrapFocusCls); + } + }, + + + + + onTriggerWrapClick: function() { + var me = this, + targetEl, match, + triggerClickMethod, + event; + + event = arguments[me.triggerRepeater ? 1 : 0]; + if (event && !me.readOnly && !me.disabled) { + targetEl = event.getTarget('.' + me.triggerBaseCls, null); + match = targetEl && targetEl.className.match(me.triggerIndexRe); + + if (match) { + triggerClickMethod = me['onTrigger' + (parseInt(match[1], 10) + 1) + 'Click'] || me.onTriggerClick; + if (triggerClickMethod) { + triggerClickMethod.call(me, event); + } + } + } + }, + + + + + onTriggerWrapMouseup: Ext.emptyFn, + + + onTriggerClick: Ext.emptyFn + + + + +}); + + + +Ext.define('Ext.form.field.Picker', { + extend: Ext.form.field.Trigger , + alias: 'widget.pickerfield', + alternateClassName: 'Ext.form.Picker', + + + + matchFieldWidth: true, + + + pickerAlign: 'tl-bl?', + + + + + openCls: Ext.baseCSSPrefix + 'pickerfield-open', + + + + + editable: true, + + + initComponent: function() { + this.callParent(); + + + this.addEvents( + + 'expand', + + 'collapse', + + 'select' + ); + }, + + + initEvents: function() { + var me = this; + me.callParent(); + + + me.keyNav = new Ext.util.KeyNav(me.inputEl, { + down: me.onDownArrow, + esc: { + handler: me.onEsc, + scope: me, + defaultEventAction: false + }, + scope: me, + forceKeyDown: true + }); + + + if (!me.editable) { + me.mon(me.inputEl, 'click', me.onTriggerClick, me); + } + + + if (Ext.isGecko) { + me.inputEl.dom.setAttribute('autocomplete', 'off'); + } + }, + + + onEsc: function(e) { + if (Ext.isIE) { + + + + + e.preventDefault(); + } + + if (this.isExpanded) { + this.collapse(); + e.stopEvent(); + } + }, + + onDownArrow: function(e) { + if (!this.isExpanded) { + + + this.onTriggerClick(); + } + }, + + + expand: function() { + var me = this, + bodyEl, picker, collapseIf; + + if (me.rendered && !me.isExpanded && !me.isDestroyed) { + me.expanding = true; + bodyEl = me.bodyEl; + picker = me.getPicker(); + collapseIf = me.collapseIf; + + + picker.show(); + me.isExpanded = true; + me.alignPicker(); + bodyEl.addCls(me.openCls); + + + me.mon(Ext.getDoc(), { + mousewheel: collapseIf, + mousedown: collapseIf, + scope: me + }); + Ext.EventManager.onWindowResize(me.alignPicker, me); + me.fireEvent('expand', me); + me.onExpand(); + delete me.expanding; + } + }, + + onExpand: Ext.emptyFn, + + + alignPicker: function() { + var me = this, + picker = me.getPicker(); + + if (me.isExpanded) { + if (me.matchFieldWidth) { + + picker.setWidth(me.bodyEl.getWidth()); + } + if (picker.isFloating()) { + me.doAlign(); + } + } + }, + + + doAlign: function(){ + var me = this, + picker = me.picker, + aboveSfx = '-above', + isAbove; + + + + me.picker.alignTo(me.triggerWrap, me.pickerAlign, me.pickerOffset); + + + isAbove = picker.el.getY() < me.inputEl.getY(); + me.bodyEl[isAbove ? 'addCls' : 'removeCls'](me.openCls + aboveSfx); + picker[isAbove ? 'addCls' : 'removeCls'](picker.baseCls + aboveSfx); + }, + + + collapse: function() { + var me = this; + + if (me.isExpanded && !me.isDestroyed && !me.destroying) { + var openCls = me.openCls, + picker = me.picker, + doc = Ext.getDoc(), + collapseIf = me.collapseIf, + aboveSfx = '-above'; + + + picker.hide(); + me.isExpanded = false; + + + me.bodyEl.removeCls([openCls, openCls + aboveSfx]); + picker.el.removeCls(picker.baseCls + aboveSfx); + + + doc.un('mousewheel', collapseIf, me); + doc.un('mousedown', collapseIf, me); + Ext.EventManager.removeResizeListener(me.alignPicker, me); + me.fireEvent('collapse', me); + me.onCollapse(); + } + }, + + onCollapse: Ext.emptyFn, + + + + collapseIf: function(e) { + var me = this; + + if (!me.isDestroyed && !e.within(me.bodyEl, false, true) && !me.owns(e.target)) { + me.collapse(); + } + }, + + + getPicker: function() { + var me = this, + picker = me.picker; + + if (!picker) { + me.picker = picker = me.createPicker(); + + picker.ownerCmp = me; + } + + return picker; + }, + + + + + + getRefItems: function() { + var result = []; + if (this.picker) { + result[0] = this.picker; + } + return result; + }, + + + createPicker: Ext.emptyFn, + + + onTriggerClick: function() { + var me = this; + if (!me.readOnly && !me.disabled) { + if (me.isExpanded) { + me.collapse(); + } else { + me.expand(); + } + me.inputEl.focus(); + } + }, + + onOtherFocus: function(dom) { + if (this.hasFocus && !this.owns(dom)) { + this.callParent([dom]); + } + }, + + triggerBlur: function() { + var picker = this.picker; + + this.callParent(arguments); + + + this.collapse(); + }, + + mimicBlur: function(e) { + var me = this, + picker = me.picker; + + + if (!picker || !me.owns(e.target)) { + me.callParent(arguments); + } else { + me.inputEl.focus(); + } + }, + + onDestroy : function(){ + var me = this, + picker = me.picker; + + Ext.EventManager.removeResizeListener(me.alignPicker, me); + Ext.destroy(me.keyNav, picker); + if (picker) { + delete me.picker; + delete picker.pickerField; + } + me.callParent(); + } +}); + + + + +Ext.define('Ext.app.EventDomain', { + + + + + statics: { + + instances: {} + }, + + + + isEventDomain: true, + + constructor: function() { + var me = this; + + Ext.app.EventDomain.instances[me.type] = me; + + me.bus = {}; + me.monitoredClasses = []; + }, + + + dispatch: function(target, ev, args) { + var me = this, + bus = me.bus, + selectors = bus[ev.toLowerCase()], + selector, controllers, id, events, event, i, ln; + + if (!selectors) { + return true; + } + + + for (selector in selectors) { + + if (selectors.hasOwnProperty(selector) && me.match(target, selector)) { + + controllers = selectors[selector]; + + for (id in controllers) { + if (controllers.hasOwnProperty(id)) { + + events = controllers[id]; + + for (i = 0, ln = events.length; i < ln; i++) { + event = events[i]; + + + if (event.fire.apply(event, args) === false) { + return false; + } + } + } + } + } + } + + return true; + }, + + + listen: function(selectors, controller) { + var me = this, + bus = me.bus, + idProperty = me.idProperty, + monitoredClasses = me.monitoredClasses, + monitoredClassesCount = monitoredClasses.length, + i, tree, list, selector, options, listener, scope, event, listeners, ev; + + for (selector in selectors) { + if (selectors.hasOwnProperty(selector) && (listeners = selectors[selector])) { + if (idProperty) { + if (!/^[*#]/.test(selector)) { + Ext.Error.raise('Selectors containing id should begin with #'); + } + + selector = selector === '*' ? selector : selector.substring(1); + } + + for (ev in listeners) { + if (listeners.hasOwnProperty(ev)) { + options = null; + listener = listeners[ev]; + scope = controller; + ev = ev.toLowerCase(); + event = new Ext.util.Event(controller, ev); + + + if (Ext.isObject(listener)) { + options = listener; + listener = options.fn; + scope = options.scope || controller; + + delete options.fn; + delete options.scope; + } + + if (typeof listener === 'string') { + listener = scope[listener]; + } + event.addListener(listener, scope, options); + + for (i = monitoredClassesCount; i-- > 0;) { + monitoredClasses[i].hasListeners._incr_(ev); + } + + + tree = bus[ev] || (bus[ev] = {}); + tree = tree[selector] || (tree[selector] = {}); + list = tree[controller.id] || (tree[controller.id] = []); + + + list.push(event); + } + } + } + } + }, + + + match: function(target, selector) { + var idProperty = this.idProperty; + + if (idProperty) { + return selector === '*' || target[idProperty] === selector; + } + + return false; + }, + + + monitor: function(observable) { + var domain = this, + prototype = observable.isInstance ? observable : observable.prototype, + fireEventArgs = prototype.fireEventArgs; + + domain.monitoredClasses.push(observable); + + prototype.fireEventArgs = function(ev, args) { + var ret = fireEventArgs.apply(this, arguments); + + if (ret !== false) { + ret = domain.dispatch(this, ev, args); + } + + return ret; + }; + }, + + + unlisten: function(controllerId) { + var bus = this.bus, + controllers, ev, selector, selectors; + + for (ev in bus) { + ev = ev.toLowerCase(); + if (bus.hasOwnProperty(ev) && (selectors = bus[ev])) { + for (selector in selectors) { + controllers = selectors[selector]; + delete controllers[controllerId]; + } + } + } + } +}); + + + +Ext.define('Ext.app.domain.Component', { + extend: Ext.app.EventDomain , + singleton: true, + + + + + + type: 'component', + + constructor: function() { + var me = this; + + me.callParent(); + me.monitor(Ext.Component); + }, + + match: function(target, selector) { + return target.is(selector); + } +}); + + + +Ext.define('Ext.app.EventBus', { + singleton: true, + + + + + + constructor: function() { + var me = this, + domains = Ext.app.EventDomain.instances; + + me.callParent(); + + me.domains = domains; + me.bus = domains.component.bus; + }, + + + control: function(selectors, controller) { + return this.domains.component.listen(selectors, controller); + }, + + + listen: function(to, controller) { + var domains = this.domains, + domain; + + for (domain in to) { + if (to.hasOwnProperty(domain)) { + domains[domain].listen(to[domain], controller); + } + } + }, + + + unlisten: function(controllerId) { + var domains = Ext.app.EventDomain.instances, + domain; + + for (domain in domains) { + domains[domain].unlisten(controllerId); + } + } +}); + + + +Ext.define('Ext.app.domain.Global', { + extend: Ext.app.EventDomain , + singleton: true, + + type: 'global', + + constructor: function() { + var me = this; + + me.callParent(); + me.monitor(Ext.globalEvents); + }, + + + listen: function(listeners, controller) { + + + this.callParent([{ global: listeners }, controller]); + }, + + match: function() { + return true; + } +}); + + + + +Ext.define('Ext.app.domain.Store', { + extend: Ext.app.EventDomain , + singleton: true, + + + + + + type: 'store', + idProperty: 'storeId', + + constructor: function() { + var me = this; + + me.callParent(); + me.monitor(Ext.data.AbstractStore); + } +}); + + + +Ext.define('Ext.app.Controller', { + + + + + + + + + + + + + + + mixins: { + observable: Ext.util.Observable + }, + + + + statics: { + strings: { + model: { + getter: 'getModel', + upper: 'Model' + }, + + view: { + getter: 'getView', + upper: 'View' + }, + + controller: { + getter: 'getController', + upper: 'Controller' + }, + + store: { + getter: 'getStore', + upper: 'Store' + } + }, + + controllerRegex: /^(.*)\.controller\./, + + createGetter: function(baseGetter, name) { + return function () { + return this[baseGetter](name); + }; + }, + + getGetterName: function(name, kindUpper) { + var fn = 'get', + parts = name.split('.'), + numParts = parts.length, + index; + + + for (index = 0; index < numParts; index++) { + fn += Ext.String.capitalize(parts[index]); + } + + fn += kindUpper; + + return fn; + }, + + + processDependencies: function(cls, requires, namespace, kind, names) { + if (!names || !names.length) { + return; + } + + var me = this, + strings = me.strings[kind], + o, absoluteName, shortName, name, j, subLn, getterName, getter; + + if (!Ext.isArray(names)) { + names = [names]; + } + + for (j = 0, subLn = names.length; j < subLn; j++) { + name = names[j]; + o = me.getFullName(name, kind, namespace); + absoluteName = o.absoluteName; + shortName = o.shortName; + + requires.push(absoluteName); + getterName = me.getGetterName(shortName, strings.upper); + cls[getterName] = getter = me.createGetter(strings.getter, name); + + + if (kind !== 'controller') { + + + + + getter['Ext.app.getter'] = true; + } + } + }, + + getFullName: function(name, kind, namespace) { + var shortName = name, + sep, absoluteName; + + if ((sep = name.indexOf('@')) > 0) { + + + + + shortName = name.substring(0, sep); + absoluteName = name.substring(sep + 1) + '.' + shortName; + } + + + + + + + + + + + + + + + + else if (name.indexOf('.') > 0 && (Ext.ClassManager.isCreated(name) || + Ext.Loader.isAClassNameWithAKnownPrefix(name))) { + absoluteName = name; + } + else { + if (!namespace) { + Ext.log.warn("Cannot find namespace for " + kind + " " + name + ", " + + "assuming it is fully qualified class name"); + } + + if (namespace) { + absoluteName = namespace + '.' + kind + '.' + name; + shortName = name; + } + else { + absoluteName = name; + } + } + + return { + absoluteName: absoluteName, + shortName: shortName + }; + } + }, + + + application: null, + + + + + + + + + + onClassExtended: function(cls, data, hooks) { + var onBeforeClassCreated = hooks.onBeforeCreated; + + hooks.onBeforeCreated = function(cls, data) { + var Controller = Ext.app.Controller, + ctrlRegex = Controller.controllerRegex, + requires = [], + className, namespace, requires, proto, match; + + proto = cls.prototype; + + + className = Ext.getClassName(cls); + namespace = data.$namespace || + Ext.app.getNamespace(className) || + ((match = ctrlRegex.exec(className)) && match[1]); + + if (namespace) { + proto.$namespace = namespace; + } + else { + Ext.log.warn("Missing namespace for " + className + ", please define it "+ + "in namespaces property of your Application class."); + } + + Controller.processDependencies(proto, requires, namespace, 'model', data.models); + Controller.processDependencies(proto, requires, namespace, 'view', data.views); + Controller.processDependencies(proto, requires, namespace, 'store', data.stores); + Controller.processDependencies(proto, requires, namespace, 'controller', data.controllers); + + Ext.require(requires, Ext.Function.pass(onBeforeClassCreated, arguments, this)); + }; + }, + + + constructor: function (config) { + var me = this; + + me.mixins.observable.constructor.call(me, config); + + if (me.refs) { + me.ref(me.refs); + } + + me.eventbus = Ext.app.EventBus; + + me.initAutoGetters(); + }, + + initAutoGetters: function() { + var proto = this.self.prototype, + prop, fn; + + for (prop in proto) { + fn = proto[prop]; + + + + if (fn && fn['Ext.app.getter']) { + fn.call(this); + } + } + }, + + doInit: function(app) { + var me = this; + + if (!me._initialized) { + me.init(app); + me._initialized = true; + } + }, + + finishInit: function(app) { + var me = this, + controllers = me.controllers, + controller, i, l; + + if (me._initialized && controllers && controllers.length) { + for (i = 0, l = controllers.length; i < l; i++) { + controller = me.getController(controllers[i]); + controller.finishInit(app); + } + } + }, + + + init: Ext.emptyFn, + + + onLaunch: Ext.emptyFn, + + ref: function(refs) { + var me = this, + i = 0, + length = refs.length, + info, ref, fn; + + refs = Ext.Array.from(refs); + + me.references = me.references || []; + + for (; i < length; i++) { + info = refs[i]; + ref = info.ref; + fn = 'get' + Ext.String.capitalize(ref); + + if (!me[fn]) { + me[fn] = Ext.Function.pass(me.getRef, [ref, info], me); + } + me.references.push(ref.toLowerCase()); + } + }, + + + addRef: function(refs) { + this.ref(refs); + }, + + getRef: function(ref, info, config) { + var me = this, + refCache = me.refCache || (me.refCache = {}), + cached = refCache[ref]; + + info = info || {}; + config = config || {}; + + Ext.apply(info, config); + + if (info.forceCreate) { + return Ext.ComponentManager.create(info, 'component'); + } + + if (!cached) { + if (info.selector) { + refCache[ref] = cached = Ext.ComponentQuery.query(info.selector)[0]; + } + + if (!cached && info.autoCreate) { + refCache[ref] = cached = Ext.ComponentManager.create(info, 'component'); + } + + if (cached) { + cached.on('beforedestroy', function() { + refCache[ref] = null; + }); + } + } + + return cached; + }, + + + hasRef: function(ref) { + var references = this.references; + return references && Ext.Array.indexOf(references, ref.toLowerCase()) !== -1; + }, + + + control: function(selectors, listeners, controller) { + var me = this, + ctrl = controller, + obj; + + if (Ext.isString(selectors)) { + obj = {}; + obj[selectors] = listeners; + } + else { + obj = selectors; + ctrl = listeners; + } + + me.eventbus.control(obj, ctrl || me); + }, + + + listen: function (to, controller) { + this.eventbus.listen(to, controller || this); + }, + + + getController: function(id) { + var me = this, + app = me.application; + + if (id === me.id) { + return me; + } + + return app && app.getController(id); + }, + + + getStore: function(name) { + var storeId, store; + + storeId = (name.indexOf('@') == -1) ? name : name.split('@')[0]; + store = Ext.StoreManager.get(storeId); + + if (!store) { + name = Ext.app.Controller.getFullName(name, 'store', this.$namespace); + + if (name) { + store = Ext.create(name.absoluteName, { + storeId: storeId + }); + } + } + + return store; + }, + + + getModel: function(model) { + var name = Ext.app.Controller.getFullName(model, 'model', this.$namespace); + + return name && Ext.ModelManager.getModel(name.absoluteName); + }, + + + getView: function(view) { + var name = Ext.app.Controller.getFullName(view, 'view', this.$namespace); + + return name && Ext.ClassManager.get(name.absoluteName); + }, + + + getApplication: function() { + return this.application; + } +}); + + + +Ext.define('Ext.tip.QuickTip', { + extend: Ext.tip.ToolTip , + alias: 'widget.quicktip', + alternateClassName: 'Ext.QuickTip', + + + + + interceptTitles : false, + + + title: ' ', + + + tagConfig : { + namespace : 'data-', + attribute : 'qtip', + width : 'qwidth', + target : 'target', + title : 'qtitle', + hide : 'hide', + cls : 'qclass', + align : 'qalign', + anchor : 'anchor', + showDelay: 'qshowDelay' + }, + + shrinkWrapDock: true, + + + initComponent : function(){ + var me = this; + + me.target = me.target || Ext.getDoc(); + me.targets = me.targets || {}; + me.callParent(); + }, + + + register : function(config){ + var configs = Ext.isArray(config) ? config : arguments, + i = 0, + len = configs.length, + target, j, targetLen; + + for (; i < len; i++) { + config = configs[i]; + target = config.target; + if (target) { + if (Ext.isArray(target)) { + for (j = 0, targetLen = target.length; j < targetLen; j++) { + this.targets[Ext.id(target[j])] = config; + } + } else{ + this.targets[Ext.id(target)] = config; + } + } + } + }, + + + unregister : function(el){ + delete this.targets[Ext.id(el)]; + }, + + + cancelShow: function(el){ + var me = this, + activeTarget = me.activeTarget; + + el = Ext.get(el).dom; + if (me.isVisible()) { + if (activeTarget && activeTarget.el == el) { + me.hide(); + } + } else if (activeTarget && activeTarget.el == el) { + me.clearTimer('show'); + } + }, + + + getTipCfg: function(e) { + var t = e.getTarget(), + titleText = t.title, + cfg; + + if (this.interceptTitles && titleText && Ext.isString(titleText)) { + t.qtip = titleText; + t.removeAttribute("title"); + e.preventDefault(); + return { + text: titleText + }; + } + else { + cfg = this.tagConfig; + t = e.getTarget('[' + cfg.namespace + cfg.attribute + ']'); + if (t) { + return { + target: t, + text: t.getAttribute(cfg.namespace + cfg.attribute) + }; + } + } + }, + + + onTargetOver : function(e){ + var me = this, + target = e.getTarget(me.delegate), + hasShowDelay, + delay, + elTarget, + cfg, + ns, + tipConfig, + autoHide, + targets, targetEl, value, key; + + if (me.disabled) { + return; + } + + + + + me.targetXY = e.getXY(); + + + if(!target || target.nodeType !== 1 || target == document.documentElement || target == document.body){ + return; + } + + if (me.activeTarget && ((target == me.activeTarget.el) || Ext.fly(me.activeTarget.el).contains(target))) { + + + + + if (me.targetTextEmpty()) { + me.onShowVeto(); + delete me.activeTarget; + } else { + me.clearTimer('hide'); + me.show(); + } + return; + } + + if (target) { + targets = me.targets; + + for (key in targets) { + if (targets.hasOwnProperty(key)) { + value = targets[key]; + + targetEl = Ext.fly(value.target); + if (targetEl && (targetEl.dom === target || targetEl.contains(target))) { + elTarget = targetEl.dom; + break; + } + } + } + + if (elTarget) { + me.activeTarget = me.targets[elTarget.id]; + me.activeTarget.el = target; + me.anchor = me.activeTarget.anchor; + if (me.anchor) { + me.anchorTarget = target; + } + hasShowDelay = parseInt(me.activeTarget.showDelay, 10); + if (hasShowDelay) { + delay = me.showDelay; + me.showDelay = hasShowDelay; + } + me.delayShow(); + if (hasShowDelay) { + me.showDelay = delay; + } + return; + } + } + + + elTarget = Ext.fly(target, '_quicktip-target'); + cfg = me.tagConfig; + ns = cfg.namespace; + tipConfig = me.getTipCfg(e); + + if (tipConfig) { + + + + if (tipConfig.target) { + target = tipConfig.target; + elTarget = Ext.fly(target, '_quicktip-target'); + } + autoHide = elTarget.getAttribute(ns + cfg.hide); + + me.activeTarget = { + el: target, + text: tipConfig.text, + width: +elTarget.getAttribute(ns + cfg.width) || null, + autoHide: autoHide != "user" && autoHide !== 'false', + title: elTarget.getAttribute(ns + cfg.title), + cls: elTarget.getAttribute(ns + cfg.cls), + align: elTarget.getAttribute(ns + cfg.align), + showDelay: parseInt(elTarget.getAttribute(ns + cfg.showDelay), 10) + }; + me.anchor = elTarget.getAttribute(ns + cfg.anchor); + if (me.anchor) { + me.anchorTarget = target; + } + hasShowDelay = parseInt(me.activeTarget.showDelay, 10); + if (hasShowDelay) { + delay = me.showDelay; + me.showDelay = hasShowDelay; + } + me.delayShow(); + if (hasShowDelay) { + me.showDelay = delay; + } + } + }, + + + onTargetOut : function(e){ + var me = this, + active = me.activeTarget, + hasHideDelay, + delay; + + + + if (active && e.within(me.activeTarget.el) && !me.getTipCfg(e)) { + return; + } + + me.clearTimer('show'); + delete me.activeTarget; + if (me.autoHide !== false) { + hasHideDelay = active && parseInt(active.hideDelay, 10); + if (hasHideDelay) { + delay = me.hideDelay; + me.hideDelay = hasHideDelay; + } + me.delayHide(); + if (hasHideDelay) { + me.hideDelay = delay; + } + } + }, + + targetTextEmpty: function(){ + var me = this, + target = me.activeTarget, + cfg = me.tagConfig, + el, text; + + if (target) { + el = target.el; + if (el) { + + + text = el.getAttribute(cfg.namespace + cfg.attribute) || me.interceptTitles; + + + + if (!text && !me.targets[Ext.id(target.target)]) { + return true; + } + } + } + return false; + }, + + show: function(){ + var me = this, + fromDelay = me.fromDelayShow; + + + + if (fromDelay && me.targetTextEmpty()) { + me.onShowVeto(); + delete me.activeTarget; + return; + } + me.callParent(arguments); + }, + + + showAt : function(xy){ + var me = this, + target = me.activeTarget, + header = me.header, + dismiss, cls; + + if (target) { + if (!me.rendered) { + me.render(Ext.getBody()); + me.activeTarget = target; + } + me.suspendLayouts(); + if (target.title) { + me.setTitle(target.title); + header.show(); + } else if (header) { + header.hide(); + } + me.update(target.text); + me.autoHide = target.autoHide; + dismiss = target.dismissDelay; + + me.dismissDelay = Ext.isNumber(dismiss) ? dismiss : me.dismissDelay; + if (target.mouseOffset) { + xy[0] += target.mouseOffset[0]; + xy[1] += target.mouseOffset[1]; + } + + cls = me.lastCls; + if (cls) { + me.removeCls(cls); + delete me.lastCls; + } + + cls = target.cls; + if (cls) { + me.addCls(cls); + me.lastCls = cls; + } + + me.setWidth(target.width); + + if (me.anchor) { + me.constrainPosition = false; + } else if (target.align) { + xy = me.getAlignToXY(target.el, target.align); + me.constrainPosition = false; + }else{ + me.constrainPosition = true; + } + me.resumeLayouts(true); + } + me.callParent([xy]); + }, + + + hide: function(){ + delete this.activeTarget; + this.callParent(); + } +}); + + + +Ext.define('Ext.tip.QuickTipManager', { + + singleton: true, + alternateClassName: 'Ext.QuickTips', + disabled: false, + + + init : function (autoRender, config) { + var me = this; + + if (!me.tip) { + if (!Ext.isReady) { + Ext.onReady(function(){ + Ext.tip.QuickTipManager.init(autoRender, config); + }); + return false; + } + + var tipConfig = Ext.apply({ + + sticky: true, + disabled: me.disabled, + id: 'ext-quicktips-tip' + }, config), + className = tipConfig.className, + xtype = tipConfig.xtype; + + if (className) { + delete tipConfig.className; + } else if (xtype) { + className = 'widget.' + xtype; + delete tipConfig.xtype; + } + + if (autoRender !== false) { + tipConfig.renderTo = document.body; + + if (tipConfig.renderTo.tagName.toUpperCase() != 'BODY') { + Ext.Error.raise({ + sourceClass: 'Ext.tip.QuickTipManager', + sourceMethod: 'init', + msg: 'Cannot init QuickTipManager: no document body' + }); + } + } + + me.tip = Ext.create(className || 'Ext.tip.QuickTip', tipConfig); + + + + Ext.quickTipsActive = true; + } + }, + + + destroy: function() { + Ext.destroy(this.tip); + this.tip = undefined; + }, + + + ddDisable : function() { + var me = this, + tip = me.tip; + + + if (tip && !me.disabled) { + tip.disable(); + } + }, + + + ddEnable : function() { + var me = this, + tip = me.tip; + + + if (tip && !me.disabled) { + tip.enable(); + } + }, + + + enable : function(){ + var me = this, + tip = me.tip; + + if (tip) { + tip.enable(); + } + me.disabled = false; + }, + + + disable : function(){ + var me = this, + tip = me.tip; + + if(tip){ + tip.disable(); + } + me.disabled = true; + }, + + + isEnabled : function(){ + var tip = this.tip; + + return tip !== undefined && !tip.disabled; + }, + + + getQuickTip : function(){ + return this.tip; + }, + + + register : function(){ + var tip = this.tip; + + tip.register.apply(tip, arguments); + }, + + + unregister : function(){ + var tip = this.tip; + + tip.unregister.apply(tip, arguments); + }, + + + tips : function(){ + var tip = this.tip; + + tip.register.apply(tip, arguments); + } +}); + + + +Ext.define('Ext.app.Application', { + extend: Ext.app.Controller , + + + + + + + + + + + + + + scope: undefined, + + + enableQuickTips: true, + + + appFolder: 'app', + + + + appProperty: 'app', + + + namespaces: [], + + + autoCreateViewport: false, + + + paths: null, + + onClassExtended: function(cls, data, hooks) { + var Controller = Ext.app.Controller, + proto = cls.prototype, + requires = [], + onBeforeClassCreated, paths, namespace, ns, appFolder; + + + + namespace = data.name || cls.superclass.name; + appFolder = data.appFolder || cls.superclass.appFolder; + + if (namespace) { + data.$namespace = namespace; + Ext.app.addNamespaces(namespace); + } + + if (data.namespaces) { + Ext.app.addNamespaces(data.namespaces); + } + + if (!data['paths processed']) { + if (namespace && appFolder) { + Ext.Loader.setPath(namespace, appFolder); + } + + paths = data.paths; + + if (paths) { + for (ns in paths) { + if (paths.hasOwnProperty(ns)) { + Ext.Loader.setPath(ns, paths[ns]); + } + } + } + } + else { + delete data['paths processed']; + } + + if (data.autoCreateViewport) { + if (!namespace) { + Ext.Error.raise("[Ext.app.Application] Can't resolve namespace for " + + data.$className + ", did you forget to specify 'name' property?"); + } + + Controller.processDependencies(proto, requires, namespace, 'view', ['Viewport']); + } + + + if (requires.length) { + onBeforeClassCreated = hooks.onBeforeCreated; + + hooks.onBeforeCreated = function(cls, data) { + var args = Ext.Array.clone(arguments); + + Ext.require(requires, function () { + return onBeforeClassCreated.apply(this, args); + }); + }; + } + }, + + + constructor: function(config) { + var me = this; + + if (Ext.isEmpty(me.name)) { + Ext.Error.raise("[Ext.app.Application] Name property is required"); + } + + me.callParent(arguments); + + me.doInit(me); + + me.initNamespace(); + me.initControllers(); + me.onBeforeLaunch(); + + me.finishInitControllers(); + }, + + initNamespace: function() { + var me = this, + appProperty = me.appProperty, + ns; + + ns = Ext.namespace(me.name); + + if (ns) { + ns.getApplication = function() { + return me; + }; + + if (appProperty) { + if (!ns[appProperty]) { + ns[appProperty] = me; + } + else if (ns[appProperty] !== me) { + Ext.log.warn('An existing reference is being overwritten for ' + name + '.' + appProperty + + '. See the appProperty config.' + ); + } + } + } + }, + + initControllers: function() { + var me = this, + controllers = Ext.Array.from(me.controllers); + + me.controllers = new Ext.util.MixedCollection(); + + for (var i = 0, ln = controllers.length; i < ln; i++) { + me.getController(controllers[i]); + } + }, + + finishInitControllers: function() { + var me = this, + controllers, i, l; + + controllers = me.controllers.getRange(); + + for (i = 0, l = controllers.length; i < l; i++) { + controllers[i].finishInit(me); + } + }, + + + launch: Ext.emptyFn, + + + onBeforeLaunch: function() { + var me = this, + controllers, c, cLen, controller; + + if (me.enableQuickTips) { + me.initQuickTips(); + } + + if (me.autoCreateViewport) { + me.initViewport(); + } + + me.launch.call(me.scope || me); + me.launched = true; + me.fireEvent('launch', me); + + controllers = me.controllers.items; + cLen = controllers.length; + + for (c = 0; c < cLen; c++) { + controller = controllers[c]; + controller.onLaunch(me); + } + }, + + getModuleClassName: function(name, kind) { + return Ext.app.Controller.getFullName(name, kind, this.name).absoluteName; + }, + + initQuickTips: function() { + Ext.tip.QuickTipManager.init(); + }, + + initViewport: function() { + var viewport = this.getView('Viewport'); + + if (viewport) { + viewport.create(); + } + }, + + getController: function(name) { + var me = this, + controllers = me.controllers, + className, controller; + + controller = controllers.get(name); + + if (!controller) { + className = me.getModuleClassName(name, 'controller'); + + controller = Ext.create(className, { + application: me, + id: name + }); + + controllers.add(controller); + + if (me._initialized) { + controller.doInit(me); + } + } + + return controller; + }, + + getApplication: function() { + return this; + } +}); + + + +Ext.define('Ext.app.domain.Controller', { + extend: Ext.app.EventDomain , + singleton: true, + + + + + + type: 'controller', + idProperty: 'id', + + constructor: function() { + var me = this; + + me.callParent(); + me.monitor(Ext.app.Controller); + } +}); + + + +Ext.define('Ext.direct.Provider', { + alias: 'direct.provider', + + mixins: { + observable: Ext.util.Observable + }, + + isProvider: true, + + + + + + constructor: function(config) { + var me = this; + + Ext.apply(me, config); + + Ext.applyIf(me, { + id: Ext.id(null, 'provider-') + }); + + me.addEvents( + + 'connect', + + + 'disconnect', + + + 'data', + + + 'exception' + ); + + me.mixins.observable.constructor.call(me, config); + }, + + + isConnected: function() { + return false; + }, + + + connect: Ext.emptyFn, + + + disconnect: Ext.emptyFn +}); + + + + +Ext.define('Ext.app.domain.Direct', { + extend: Ext.app.EventDomain , + singleton: true, + + + + + + type: 'direct', + idProperty: 'id', + + constructor: function() { + var me = this; + + me.callParent(); + me.monitor(Ext.direct.Provider); + } +}); + + + +Ext.define('Ext.button.Split', { + + + alias: 'widget.splitbutton', + + extend: Ext.button.Button , + alternateClassName: 'Ext.SplitButton', + + + + + + + arrowCls : 'split', + split : true, + + + initComponent : function(){ + this.callParent(); + + this.addEvents("arrowclick"); + }, + + + setArrowHandler : function(handler, scope){ + this.arrowHandler = handler; + this.scope = scope; + }, + + + onClick : function(e) { + var me = this; + + me.doPreventDefault(e); + if (!me.disabled) { + if (me.overMenuTrigger) { + + + e.preventDefault(); + me.maybeShowMenu(); + me.fireEvent("arrowclick", me, e); + if (me.arrowHandler) { + me.arrowHandler.call(me.scope || me, me, e); + } + } else { + me.doToggle(); + me.fireHandler(e); + } + } + } +}); + + + +Ext.define('Ext.button.Cycle', { + + + + alias: 'widget.cycle', + + extend: Ext.button.Split , + alternateClassName: 'Ext.CycleButton', + + + + + + + + + + + + + + getButtonText: function(item) { + var me = this, + text = ''; + + if (item && me.showText === true) { + if (me.prependText) { + text += me.prependText; + } + text += item.text; + return text; + } + return me.text; + }, + + + setActiveItem: function(item, suppressEvent) { + var me = this; + + if (!Ext.isObject(item)) { + item = me.menu.getComponent(item); + } + if (item) { + if (!me.rendered) { + me.text = me.getButtonText(item); + me.iconCls = item.iconCls; + me.glyph = item.glyph; + } else { + me.setText(me.getButtonText(item)); + me.setIconCls(item.iconCls); + me.setGlyph(item.glyph); + } + me.activeItem = item; + if (!item.checked) { + item.setChecked(true, false); + } + if (me.forceIcon) { + me.setIconCls(me.forceIcon); + } + if (me.forceGlyph) { + me.setGlyph(me.forceGlyph); + } + if (!suppressEvent) { + me.fireEvent('change', me, item); + } + } + }, + + + getActiveItem: function() { + return this.activeItem; + }, + + + initComponent: function() { + var me = this, + checked = 0, + items, + i, iLen, item; + + me.addEvents( + + "change" + ); + + if (me.changeHandler) { + me.on('change', me.changeHandler, me.scope || me); + delete me.changeHandler; + } + + + + items = (me.menu.items || []).concat(me.items || []); + me.menu = Ext.applyIf({ + cls: Ext.baseCSSPrefix + 'cycle-menu', + items: [] + }, me.menu); + + iLen = items.length; + + + for (i = 0; i < iLen; i++) { + item = items[i]; + + item = Ext.applyIf({ + group : me.id, + itemIndex : i, + checkHandler : me.checkHandler, + scope : me, + checked : item.checked || false + }, item); + + me.menu.items.push(item); + + if (item.checked) { + checked = i; + } + } + + me.itemCount = me.menu.items.length; + me.callParent(arguments); + me.on('click', me.toggleSelected, me); + me.setActiveItem(checked, me); + + + if (me.width && me.showText) { + me.addCls(Ext.baseCSSPrefix + 'cycle-fixed-width'); + } + }, + + + checkHandler: function(item, pressed) { + if (pressed) { + this.setActiveItem(item); + } + }, + + + toggleSelected: function() { + var me = this, + m = me.menu, + checkItem; + + checkItem = me.activeItem.next(':not([disabled])') || m.items.getAt(0); + checkItem.setChecked(true); + } +}); + + + +Ext.define('Ext.chart.Callout', { + + + + + + constructor: function(config) { + if (config.callouts) { + config.callouts.styles = Ext.applyIf(config.callouts.styles || {}, { + color: "#000", + font: "11px Helvetica, sans-serif" + }); + this.callouts = Ext.apply(this.callouts || {}, config.callouts); + this.calloutsArray = []; + } + }, + + renderCallouts: function() { + if (!this.callouts) { + return; + } + + var me = this, + items = me.items, + animate = me.chart.animate, + config = me.callouts, + styles = config.styles, + group = me.calloutsArray, + store = me.chart.getChartStore(), + len = store.getCount(), + ratio = items.length / len, + previouslyPlacedCallouts = [], + i, + count, + j, + p, + item, + label, + storeItem, + display; + + for (i = 0, count = 0; i < len; i++) { + for (j = 0; j < ratio; j++) { + item = items[count]; + label = group[count]; + storeItem = store.getAt(i); + + display = (!config.filter || config.filter(storeItem)); + + if (!display && !label) { + count++; + continue; + } + + if (!label) { + group[count] = label = me.onCreateCallout(storeItem, item, i, display, j, count); + } + for (p in label) { + if (label[p] && label[p].setAttributes) { + label[p].setAttributes(styles, true); + } + } + if (!display) { + for (p in label) { + if (label[p]) { + if (label[p].setAttributes) { + label[p].setAttributes({ + hidden: true + }, true); + } else if(label[p].setVisible) { + label[p].setVisible(false); + } + } + } + } + if (config && config.renderer) { + config.renderer(label, storeItem); + } + me.onPlaceCallout(label, storeItem, item, i, display, animate, + j, count, previouslyPlacedCallouts); + previouslyPlacedCallouts.push(label); + count++; + } + } + this.hideCallouts(count); + }, + + onCreateCallout: function(storeItem, item, i, display) { + var me = this, + group = me.calloutsGroup, + config = me.callouts, + styles = (config ? config.styles : undefined), + width = (styles ? styles.width : 0), + height = (styles ? styles.height : 0), + chart = me.chart, + surface = chart.surface, + calloutObj = { + + + lines: false + }; + + calloutObj.lines = surface.add(Ext.apply({}, + { + type: 'path', + path: 'M0,0', + stroke: me.getLegendColor() || '#555' + }, + styles)); + + if (config.items) { + calloutObj.panel = new Ext.Panel({ + style: "position: absolute;", + width: width, + height: height, + items: config.items, + renderTo: chart.el + }); + } + + return calloutObj; + }, + + hideCallouts: function(index) { + var calloutsArray = this.calloutsArray, + len = calloutsArray.length, + co, + p; + while (len-->index) { + co = calloutsArray[len]; + for (p in co) { + if (co[p]) { + co[p].hide(true); + } + } + } + } +}); + + + +Ext.define('Ext.draw.CompositeSprite', { + + + + extend: Ext.util.MixedCollection , + mixins: { + animate: Ext.util.Animate + }, + autoDestroy: false, + + + isCompositeSprite: true, + constructor: function(config) { + var me = this; + + config = config || {}; + Ext.apply(me, config); + + me.addEvents( + + 'mousedown', + + 'mouseup', + + 'mouseover', + + 'mouseout', + + 'click' + ); + me.id = Ext.id(null, 'ext-sprite-group-'); + me.callParent(); + }, + + + onClick: function(e) { + this.fireEvent('click', e); + }, + + + onMouseUp: function(e) { + this.fireEvent('mouseup', e); + }, + + + onMouseDown: function(e) { + this.fireEvent('mousedown', e); + }, + + + onMouseOver: function(e) { + this.fireEvent('mouseover', e); + }, + + + onMouseOut: function(e) { + this.fireEvent('mouseout', e); + }, + + attachEvents: function(o) { + var me = this; + + o.on({ + scope: me, + mousedown: me.onMouseDown, + mouseup: me.onMouseUp, + mouseover: me.onMouseOver, + mouseout: me.onMouseOut, + click: me.onClick + }); + }, + + + add: function(key, o) { + var result = this.callParent(arguments); + this.attachEvents(result); + return result; + }, + + insert: function(index, key, o) { + return this.callParent(arguments); + }, + + + remove: function(o) { + var me = this; + + o.un({ + scope: me, + mousedown: me.onMouseDown, + mouseup: me.onMouseUp, + mouseover: me.onMouseOver, + mouseout: me.onMouseOut, + click: me.onClick + }); + return me.callParent(arguments); + }, + + + getBBox: function() { + var i = 0, + sprite, + bb, + items = this.items, + len = this.length, + infinity = Infinity, + minX = infinity, + maxHeight = -infinity, + minY = infinity, + maxWidth = -infinity, + maxWidthBBox, maxHeightBBox; + + for (; i < len; i++) { + sprite = items[i]; + if (sprite.el && ! sprite.bboxExcluded) { + bb = sprite.getBBox(); + minX = Math.min(minX, bb.x); + minY = Math.min(minY, bb.y); + maxHeight = Math.max(maxHeight, bb.height + bb.y); + maxWidth = Math.max(maxWidth, bb.width + bb.x); + } + } + + return { + x: minX, + y: minY, + height: maxHeight - minY, + width: maxWidth - minX + }; + }, + + + setAttributes: function(attrs, redraw) { + var i = 0, + items = this.items, + len = this.length; + + for (; i < len; i++) { + items[i].setAttributes(attrs, redraw); + } + return this; + }, + + + hide: function(redraw) { + var i = 0, + items = this.items, + len = this.length; + + for (; i < len; i++) { + items[i].hide(redraw); + } + return this; + }, + + + show: function(redraw) { + var i = 0, + items = this.items, + len = this.length; + + for (; i < len; i++) { + items[i].show(redraw); + } + return this; + }, + + + redraw: function() { + var me = this, + i = 0, + items = me.items, + surface = me.getSurface(), + len = me.length; + + if (surface) { + for (; i < len; i++) { + surface.renderItem(items[i]); + } + } + return me; + }, + + + setStyle: function(obj) { + var i = 0, + items = this.items, + len = this.length, + item, el; + + for (; i < len; i++) { + item = items[i]; + el = item.el; + if (el) { + el.setStyle(obj); + } + } + }, + + + addCls: function(obj) { + var i = 0, + items = this.items, + surface = this.getSurface(), + len = this.length; + + if (surface) { + for (; i < len; i++) { + surface.addCls(items[i], obj); + } + } + }, + + + removeCls: function(obj) { + var i = 0, + items = this.items, + surface = this.getSurface(), + len = this.length; + + if (surface) { + for (; i < len; i++) { + surface.removeCls(items[i], obj); + } + } + }, + + + getSurface: function(){ + var first = this.first(); + if (first) { + return first.surface; + } + return null; + }, + + + destroy: function(){ + var me = this, + surface = me.getSurface(), + destroySprites = me.autoDestroy, + item; + + if (surface) { + while (me.getCount() > 0) { + item = me.first(); + me.remove(item); + surface.remove(item, destroySprites); + } + } + me.clearListeners(); + } +}); + + + +Ext.define('Ext.draw.Surface', { + + + + mixins: { + observable: Ext.util.Observable + }, + + + + + separatorRe: /[, ]+/, + + enginePriority: ['Svg', 'Vml'], + + statics: { + + create: function(config, enginePriority) { + enginePriority = enginePriority || this.prototype.enginePriority; + + var i = 0, + len = enginePriority.length; + + for (; i < len; i++) { + if (Ext.supports[enginePriority[i]]) { + return Ext.create('Ext.draw.engine.' + enginePriority[i], config); + } + } + return false; + }, + + + save: function(surface, config) { + config = config || {}; + var exportTypes = { + 'image/png': 'Image', + 'image/jpeg': 'Image', + 'image/svg+xml': 'Svg' + }, + prefix = exportTypes[config.type] || 'Svg', + exporter = Ext.draw.engine[prefix + 'Exporter']; + + return exporter.generate(surface, config); + + } + }, + + + + + availableAttrs: { + blur: 0, + "clip-rect": "0 0 1e9 1e9", + cursor: "default", + cx: 0, + cy: 0, + 'dominant-baseline': 'auto', + fill: "none", + "fill-opacity": 1, + font: '10px "Arial"', + "font-family": '"Arial"', + "font-size": "10", + "font-style": "normal", + "font-weight": 400, + gradient: "", + height: 0, + hidden: false, + href: "http://sencha.com/", + opacity: 1, + path: "M0,0", + radius: 0, + rx: 0, + ry: 0, + scale: "1 1", + src: "", + stroke: "none", + "stroke-dasharray": "", + "stroke-linecap": "butt", + "stroke-linejoin": "butt", + "stroke-miterlimit": 0, + "stroke-opacity": 1, + "stroke-width": 1, + target: "_blank", + text: "", + "text-anchor": "middle", + title: "Ext Draw", + width: 0, + x: 0, + y: 0, + zIndex: 0 + }, + + + + + container: undefined, + height: 352, + width: 512, + x: 0, + y: 0, + + + + + orderSpritesByZIndex: true, + + + + constructor: function(config) { + var me = this; + config = config || {}; + Ext.apply(me, config); + + me.domRef = Ext.getDoc().dom; + + me.customAttributes = {}; + + me.addEvents( + + 'mousedown', + + 'mouseup', + + 'mouseover', + + 'mouseout', + + 'mousemove', + + 'mouseenter', + + 'mouseleave', + + 'click', + + 'dblclick' + ); + + me.mixins.observable.constructor.call(me); + + me.getId(); + me.initGradients(); + me.initItems(); + if (me.renderTo) { + me.render(me.renderTo); + delete me.renderTo; + } + me.initBackground(config.background); + }, + + + + initSurface: Ext.emptyFn, + + + + renderItem: Ext.emptyFn, + + + renderItems: Ext.emptyFn, + + + setViewBox: function (x, y, width, height) { + if (isFinite(x) && isFinite(y) && isFinite(width) && isFinite(height)) { + this.viewBox = {x: x, y: y, width: width, height: height}; + this.applyViewBox(); + } + }, + + + addCls: Ext.emptyFn, + + + removeCls: Ext.emptyFn, + + + setStyle: Ext.emptyFn, + + + initGradients: function() { + if (this.hasOwnProperty('gradients')) { + var gradients = this.gradients, + fn = this.addGradient, + g, gLen; + + if (gradients) { + for (g = 0, gLen = gradients.length; g < gLen; g++) { + if (fn.call(this, gradients[g], g, gLen) === false) { + break; + } + } + } + } + }, + + + initItems: function() { + var items = this.items; + this.items = new Ext.draw.CompositeSprite(); + this.items.autoDestroy = true; + this.groups = new Ext.draw.CompositeSprite(); + if (items) { + this.add(items); + } + }, + + + initBackground: function(config) { + var me = this, + width = me.width, + height = me.height, + gradientId, gradient; + if (Ext.isString(config)) { + config = { + fill : config + }; + } + if (config) { + if (config.gradient) { + gradient = config.gradient; + gradientId = gradient.id; + me.addGradient(gradient); + me.background = me.add({ + type: 'rect', + isBackground: true, + x: 0, + y: 0, + width: width, + height: height, + fill: 'url(#' + gradientId + ')', + zIndex: -1 + }); + } else if (config.fill) { + me.background = me.add({ + type: 'rect', + isBackground: true, + x: 0, + y: 0, + width: width, + height: height, + fill: config.fill, + zIndex: -1 + }); + } else if (config.image) { + me.background = me.add({ + type: 'image', + isBackground: true, + x: 0, + y: 0, + width: width, + height: height, + src: config.image, + zIndex: -1 + }); + } + + me.background.bboxExcluded = true; + } + }, + + + setSize: function(w, h) { + this.applyViewBox(); + }, + + + scrubAttrs: function(sprite) { + var i, + attrs = {}, + exclude = {}, + sattr = sprite.attr; + for (i in sattr) { + + if (this.translateAttrs.hasOwnProperty(i)) { + + attrs[this.translateAttrs[i]] = sattr[i]; + exclude[this.translateAttrs[i]] = true; + } + else if (this.availableAttrs.hasOwnProperty(i) && !exclude[i]) { + + attrs[i] = sattr[i]; + } + } + return attrs; + }, + + + onClick: function(e) { + this.processEvent('click', e); + }, + + + onDblClick: function(e) { + this.processEvent('dblclick', e); + }, + + + onMouseUp: function(e) { + this.processEvent('mouseup', e); + }, + + + onMouseDown: function(e) { + this.processEvent('mousedown', e); + }, + + + onMouseOver: function(e) { + this.processEvent('mouseover', e); + }, + + + onMouseOut: function(e) { + this.processEvent('mouseout', e); + }, + + + onMouseMove: function(e) { + this.fireEvent('mousemove', e); + }, + + + onMouseEnter: Ext.emptyFn, + + + onMouseLeave: Ext.emptyFn, + + + addGradient: Ext.emptyFn, + + + add: function() { + var args = Array.prototype.slice.call(arguments), + sprite, + hasMultipleArgs = args.length > 1, + items, + results, + i, + ln, + item; + + if (hasMultipleArgs || Ext.isArray(args[0])) { + items = hasMultipleArgs ? args : args[0]; + results = []; + + for (i = 0, ln = items.length; i < ln; i++) { + item = items[i]; + item = this.add(item); + results.push(item); + } + + return results; + } + sprite = this.prepareItems(args[0], true)[0]; + this.insertByZIndex(sprite); + this.onAdd(sprite); + return sprite; + }, + + + insertByZIndex: function(sprite) { + var me = this, + sprites = me.items.items, + len = sprites.length, + ceil = Math.ceil, + zIndex = sprite.attr.zIndex, + idx = len, + high = idx - 1, + low = 0, + otherZIndex; + + if (me.orderSpritesByZIndex && len && zIndex < sprites[high].attr.zIndex) { + + while (low <= high) { + idx = ceil((low + high) / 2); + otherZIndex = sprites[idx].attr.zIndex; + if (otherZIndex > zIndex) { + high = idx - 1; + } + else if (otherZIndex < zIndex) { + low = idx + 1; + } + else { + break; + } + } + + while (idx < len && sprites[idx].attr.zIndex <= zIndex) { + idx++; + } + } + + me.items.insert(idx, sprite); + return idx; + }, + + onAdd: function(sprite) { + var group = sprite.group, + draggable = sprite.draggable, + groups, ln, i; + if (group) { + groups = [].concat(group); + ln = groups.length; + for (i = 0; i < ln; i++) { + group = groups[i]; + this.getGroup(group).add(sprite); + } + delete sprite.group; + } + if (draggable) { + sprite.initDraggable(); + } + }, + + + remove: function(sprite, destroySprite) { + if (sprite) { + this.items.remove(sprite); + + var groups = [].concat(this.groups.items), + gLen = groups.length, + g; + + for (g = 0; g < gLen; g++) { + groups[g].remove(sprite); + } + + sprite.onRemove(); + if (destroySprite === true) { + sprite.destroy(); + } + } + }, + + + removeAll: function(destroySprites) { + var items = this.items.items, + ln = items.length, + i; + for (i = ln - 1; i > -1; i--) { + this.remove(items[i], destroySprites); + } + }, + + onRemove: Ext.emptyFn, + + onDestroy: Ext.emptyFn, + + + applyViewBox: function() { + var me = this, + viewBox = me.viewBox, + width = me.width || 1, + height = me.height || 1, + viewBoxX, viewBoxY, viewBoxWidth, viewBoxHeight, + relativeHeight, relativeWidth, size; + + if (viewBox && (width || height)) { + viewBoxX = viewBox.x; + viewBoxY = viewBox.y; + viewBoxWidth = viewBox.width; + viewBoxHeight = viewBox.height; + relativeHeight = height / viewBoxHeight; + relativeWidth = width / viewBoxWidth; + size = Math.min(relativeWidth, relativeHeight); + + if (viewBoxWidth * size < width) { + viewBoxX -= (width - viewBoxWidth * size) / 2 / size; + } + if (viewBoxHeight * size < height) { + viewBoxY -= (height - viewBoxHeight * size) / 2 / size; + } + + me.viewBoxShift = { + dx: -viewBoxX, + dy: -viewBoxY, + scale: size + }; + + if (me.background) { + me.background.setAttributes(Ext.apply({}, { + x: viewBoxX, + y: viewBoxY, + width: width / size, + height: height / size + }, { hidden: false }), true); + } + } else { + if (me.background && width && height) { + me.background.setAttributes(Ext.apply({x: 0, y: 0, width: width, height: height}, { hidden: false }), true); + } + } + }, + + + getBBox: function (sprite, isWithoutTransform) { + var realPath = this["getPath" + sprite.type](sprite); + if (isWithoutTransform) { + sprite.bbox.plain = sprite.bbox.plain || Ext.draw.Draw.pathDimensions(realPath); + return sprite.bbox.plain; + } + if (sprite.dirtyTransform) { + this.applyTransformations(sprite, true); + } + sprite.bbox.transform = sprite.bbox.transform || Ext.draw.Draw.pathDimensions(Ext.draw.Draw.mapPath(realPath, sprite.matrix)); + return sprite.bbox.transform; + }, + + transformToViewBox: function (x, y) { + if (this.viewBoxShift) { + var me = this, shift = me.viewBoxShift; + return [x / shift.scale - shift.dx, y / shift.scale - shift.dy]; + } else { + return [x, y]; + } + }, + + + applyTransformations: function(sprite, onlyMatrix) { + if (sprite.type == 'text') { + + sprite.bbox.transform = 0; + this.transform(sprite, false); + } + + + sprite.dirtyTransform = false; + + var me = this, + attr = sprite.attr; + + if (attr.translation.x != null || attr.translation.y != null) { + me.translate(sprite); + } + if (attr.scaling.x != null || attr.scaling.y != null) { + me.scale(sprite); + } + if (attr.rotation.degrees != null) { + me.rotate(sprite); + } + + sprite.bbox.transform = 0; + this.transform(sprite, onlyMatrix); + sprite.transformations = []; + }, + + + rotate: function (sprite) { + var bbox, + deg = sprite.attr.rotation.degrees, + centerX = sprite.attr.rotation.x, + centerY = sprite.attr.rotation.y; + if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) { + bbox = this.getBBox(sprite, true); + centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX; + centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY; + } + sprite.transformations.push({ + type: "rotate", + degrees: deg, + x: centerX, + y: centerY + }); + }, + + + translate: function(sprite) { + var x = sprite.attr.translation.x || 0, + y = sprite.attr.translation.y || 0; + sprite.transformations.push({ + type: "translate", + x: x, + y: y + }); + }, + + + scale: function(sprite) { + var bbox, + x = sprite.attr.scaling.x || 1, + y = sprite.attr.scaling.y || 1, + centerX = sprite.attr.scaling.centerX, + centerY = sprite.attr.scaling.centerY; + + if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) { + bbox = this.getBBox(sprite, true); + centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX; + centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY; + } + sprite.transformations.push({ + type: "scale", + x: x, + y: y, + centerX: centerX, + centerY: centerY + }); + }, + + + rectPath: function (x, y, w, h, r) { + if (r) { + return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]]; + } + return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]]; + }, + + + ellipsePath: function (x, y, rx, ry) { + if (ry == null) { + ry = rx; + } + return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]]; + }, + + + getPathpath: function (el) { + return el.attr.path; + }, + + + getPathcircle: function (el) { + var a = el.attr; + return this.ellipsePath(a.x, a.y, a.radius, a.radius); + }, + + + getPathellipse: function (el) { + var a = el.attr; + return this.ellipsePath(a.x, a.y, + a.radiusX || (a.width / 2) || 0, + a.radiusY || (a.height / 2) || 0); + }, + + + getPathrect: function (el) { + var a = el.attr; + return this.rectPath(a.x || 0, a.y || 0, a.width || 0, a.height || 0, a.r || 0); + }, + + + getPathimage: function (el) { + var a = el.attr; + return this.rectPath(a.x || 0, a.y || 0, a.width, a.height); + }, + + + getPathtext: function (el) { + var bbox = this.getBBoxText(el); + return this.rectPath(bbox.x, bbox.y, bbox.width, bbox.height); + }, + + createGroup: function(id) { + var group = this.groups.get(id); + if (!group) { + group = new Ext.draw.CompositeSprite({ + surface: this + }); + group.id = id || Ext.id(null, 'ext-surface-group-'); + this.groups.add(group); + } + return group; + }, + + + getGroup: function(id) { + var group; + if (typeof id == "string") { + group = this.groups.get(id); + if (!group) { + group = this.createGroup(id); + } + } else { + group = id; + } + return group; + }, + + + prepareItems: function(items, applyDefaults) { + items = [].concat(items); + + var item, i, ln; + for (i = 0, ln = items.length; i < ln; i++) { + item = items[i]; + if (!(item instanceof Ext.draw.Sprite)) { + + item.surface = this; + items[i] = this.createItem(item); + } else { + item.surface = this; + } + } + return items; + }, + + + setText: Ext.emptyFn, + + + + createItem: Ext.emptyFn, + + + getId: function() { + return this.id || (this.id = Ext.id(null, 'ext-surface-')); + }, + + + destroy: function() { + var me = this; + delete me.domRef; + if (me.background) { + me.background.destroy(); + } + me.removeAll(true); + Ext.destroy(me.groups.items); + } +}); + + + + +Ext.define('Ext.layout.component.Draw', { + + + + alias: 'layout.draw', + + extend: Ext.layout.component.Auto , + + setHeightInDom: true, + + setWidthInDom: true, + + + + type: 'draw', + + measureContentWidth : function (ownerContext) { + var target = ownerContext.target, + paddingInfo = ownerContext.getPaddingInfo(), + bbox = this.getBBox(ownerContext); + + if (!target.viewBox) { + if (target.autoSize) { + return bbox.width + paddingInfo.width; + } else { + return bbox.x + bbox.width + paddingInfo.width; + } + } else { + if (ownerContext.heightModel.shrinkWrap) { + return paddingInfo.width; + } else { + return bbox.width / bbox.height * (ownerContext.getProp('contentHeight') - paddingInfo.height) + paddingInfo.width; + } + } + }, + + measureContentHeight : function (ownerContext) { + var target = ownerContext.target, + paddingInfo = ownerContext.getPaddingInfo(), + bbox = this.getBBox(ownerContext); + + if (!ownerContext.target.viewBox) { + if (target.autoSize) { + return bbox.height + paddingInfo.height; + } else { + return bbox.y + bbox.height + paddingInfo.height; + } + } else { + if (ownerContext.widthModel.shrinkWrap) { + return paddingInfo.height; + } else { + return bbox.height / bbox.width * (ownerContext.getProp('contentWidth') - paddingInfo.width) + paddingInfo.height; + } + } + }, + + getBBox: function(ownerContext) { + var bbox = ownerContext.surfaceBBox; + if (!bbox) { + bbox = ownerContext.target.surface.items.getBBox(); + + if (bbox.width === -Infinity && bbox.height === -Infinity) { + bbox.width = bbox.height = bbox.x = bbox.y = 0; + } + ownerContext.surfaceBBox = bbox; + } + return bbox; + }, + + publishInnerWidth: function (ownerContext, width) { + ownerContext.setContentWidth(width - ownerContext.getFrameInfo().width, true); + }, + + publishInnerHeight: function (ownerContext, height) { + ownerContext.setContentHeight(height - ownerContext.getFrameInfo().height, true); + }, + + finishedLayout: function (ownerContext) { + + var props = ownerContext.props, + paddingInfo = ownerContext.getPaddingInfo(); + + + + this.owner.setSurfaceSize(props.contentWidth - paddingInfo.width, props.contentHeight - paddingInfo.height); + + + this.callParent(arguments); + } +}); + + + +Ext.define('Ext.draw.Component', { + + + + alias: 'widget.draw', + + extend: Ext.Component , + + + + + + + + + + enginePriority: ['Svg', 'Vml'], + + baseCls: Ext.baseCSSPrefix + 'surface', + + componentLayout: 'draw', + + + viewBox: true, + + shrinkWrap: 3, + + + autoSize: false, + + + + + + + + suspendSizing: 0, + + initComponent: function() { + this.callParent(arguments); + + this.addEvents( + + 'mousedown', + + 'mouseup', + + 'mousemove', + + 'mouseenter', + + 'mouseleave', + + 'click', + + 'dblclick' + ); + }, + + + onRender: function() { + this.callParent(arguments); + if (this.createSurface() !== false) { + this.configureSurfaceSize(); + } + }, + + configureSurfaceSize: function(){ + var me = this, + viewBox = me.viewBox, + autoSize = me.autoSize, + bbox; + + if ((viewBox || autoSize) && !me.suspendSizing) { + bbox = me.surface.items.getBBox(); + if (viewBox) { + me.surface.setViewBox(bbox.x, bbox.y, bbox.width, bbox.height); + } else { + me.autoSizeSurface(bbox); + } + } + }, + + + autoSizeSurface: function(bbox) { + bbox = bbox || this.surface.items.getBBox(); + this.setSurfaceSize(bbox.width, bbox.height); + }, + + setSurfaceSize: function (width, height) { + this.surface.setSize(width, height); + if (this.autoSize) { + var bbox = this.surface.items.getBBox(); + this.surface.setViewBox(bbox.x, bbox.y - (+Ext.isOpera), width, height); + } + }, + + + createSurface: function() { + var me = this, + cfg = Ext.applyIf({ + renderTo: me.el, + height: me.height, + width: me.width, + items: me.items + }, me.initialConfig), surface; + + + delete cfg.listeners; + if (!cfg.gradients) { + cfg.gradients = me.gradients; + } + me.initSurfaceCfg(cfg); + surface = Ext.draw.Surface.create(cfg, me.enginePriority); + if (!surface) { + + return false; + } + me.surface = surface; + surface.owner = me; + + + function refire(eventName) { + return function(e) { + me.fireEvent(eventName, e); + }; + } + + surface.on({ + scope: me, + mouseup: refire('mouseup'), + mousedown: refire('mousedown'), + mousemove: refire('mousemove'), + mouseenter: refire('mouseenter'), + mouseleave: refire('mouseleave'), + click: refire('click'), + dblclick: refire('dblclick') + }); + }, + + initSurfaceCfg: Ext.emptyFn, + + + + onDestroy: function() { + Ext.destroy(this.surface); + this.callParent(arguments); + } + +}); + + + +Ext.chart = Ext.chart || {}; + +Ext.define('Ext.chart.theme.Theme', ( + + +function() { + + +(function() { + Ext.chart.theme = function(config, base) { + config = config || {}; + var i = 0, d = Ext.Date.now(), l, colors, color, + seriesThemes, markerThemes, + seriesTheme, markerTheme, + key, gradients = [], + midColor, midL; + + if (config.baseColor) { + midColor = Ext.draw.Color.fromString(config.baseColor); + midL = midColor.getHSL()[2]; + if (midL < 0.15) { + midColor = midColor.getLighter(0.3); + } else if (midL < 0.3) { + midColor = midColor.getLighter(0.15); + } else if (midL > 0.85) { + midColor = midColor.getDarker(0.3); + } else if (midL > 0.7) { + midColor = midColor.getDarker(0.15); + } + config.colors = [ midColor.getDarker(0.3).toString(), + midColor.getDarker(0.15).toString(), + midColor.toString(), + midColor.getLighter(0.15).toString(), + midColor.getLighter(0.3).toString()]; + + delete config.baseColor; + } + if (config.colors) { + colors = config.colors.slice(); + markerThemes = base.markerThemes; + seriesThemes = base.seriesThemes; + l = colors.length; + base.colors = colors; + for (; i < l; i++) { + color = colors[i]; + markerTheme = markerThemes[i] || {}; + seriesTheme = seriesThemes[i] || {}; + markerTheme.fill = seriesTheme.fill = markerTheme.stroke = seriesTheme.stroke = color; + markerThemes[i] = markerTheme; + seriesThemes[i] = seriesTheme; + } + base.markerThemes = markerThemes.slice(0, l); + base.seriesThemes = seriesThemes.slice(0, l); + + } + for (key in base) { + if (key in config) { + if (Ext.isObject(config[key]) && Ext.isObject(base[key])) { + Ext.apply(base[key], config[key]); + } else { + base[key] = config[key]; + } + } + } + if (config.useGradients) { + colors = base.colors || (function () { + var ans = []; + for (i = 0, seriesThemes = base.seriesThemes, l = seriesThemes.length; i < l; i++) { + ans.push(seriesThemes[i].fill || seriesThemes[i].stroke); + } + return ans; + }()); + for (i = 0, l = colors.length; i < l; i++) { + midColor = Ext.draw.Color.fromString(colors[i]); + if (midColor) { + color = midColor.getDarker(0.1).toString(); + midColor = midColor.toString(); + key = 'theme-' + midColor.substr(1) + '-' + color.substr(1) + '-' + d; + gradients.push({ + id: key, + angle: 45, + stops: { + 0: { + color: midColor.toString() + }, + 100: { + color: color.toString() + } + } + }); + colors[i] = 'url(#' + key + ')'; + } + } + base.gradients = gradients; + base.colors = colors; + } + + Ext.apply(this, base); + }; +}()); + +return { + + + + + + + + theme: 'Base', + themeAttrs: false, + + initTheme: function(theme) { + var me = this, + themes = Ext.chart.theme, + key, gradients; + if (theme) { + theme = theme.split(':'); + for (key in themes) { + if (key == theme[0]) { + gradients = theme[1] == 'gradients'; + me.themeAttrs = new themes[key]({ + useGradients: gradients + }); + if (gradients) { + me.gradients = me.themeAttrs.gradients; + } + if (me.themeAttrs.background) { + me.background = me.themeAttrs.background; + } + return; + } + } + Ext.Error.raise('No theme found named "' + theme + '"'); + } + } +}; + +})()); + + + +Ext.define('Ext.chart.MaskLayer', { + extend: Ext.Component , + + constructor: function(config) { + config = Ext.apply(config || {}, { + style: 'position:absolute;background-color:#ff9;cursor:crosshair;opacity:0.5;border:1px solid #00f;' + }); + this.callParent([config]); + }, + + initComponent: function() { + var me = this; + me.callParent(arguments); + me.addEvents( + 'mousedown', + 'mouseup', + 'mousemove', + 'mouseenter', + 'mouseleave' + ); + }, + + initDraggable: function() { + this.callParent(arguments); + this.dd.onStart = function (e) { + var me = this, + comp = me.comp; + + + this.startPosition = comp.getPosition(true); + + + + if (comp.ghost && !comp.liveDrag) { + me.proxy = comp.ghost(); + me.dragTarget = me.proxy.header.el; + } + + + if (me.constrain || me.constrainDelegate) { + me.constrainTo = me.calculateConstrainRegion(); + } + }; + } +}); + + + +Ext.define('Ext.chart.Mask', { + + + + + + + + constructor: function(config) { + var me = this; + + me.addEvents('select'); + + if (config) { + Ext.apply(me, config); + } + if (me.enableMask) { + me.on('afterrender', function() { + + var comp = new Ext.chart.MaskLayer({ + renderTo: me.el, + hidden: true + }); + comp.el.on({ + 'mousemove': function(e) { + me.onMouseMove(e); + }, + 'mouseup': function(e) { + me.onMouseUp(e); + } + }); + comp.initDraggable(); + me.maskType = me.mask; + me.mask = comp; + me.maskSprite = me.surface.add({ + type: 'path', + path: ['M', 0, 0], + zIndex: 1001, + opacity: 0.6, + hidden: true, + stroke: '#00f', + cursor: 'crosshair' + }); + }, me, { single: true }); + } + }, + + onMouseUp: function(e) { + var me = this, + bbox = me.bbox || me.chartBBox, + sel; + me.maskMouseDown = false; + me.mouseDown = false; + if (me.mouseMoved) { + me.handleMouseEvent(e); + me.mouseMoved = false; + sel = me.maskSelection; + me.fireEvent('select', me, { + x: sel.x - bbox.x, + y: sel.y - bbox.y, + width: sel.width, + height: sel.height + }); + } + }, + + onMouseDown: function(e) { + this.handleMouseEvent(e); + }, + + onMouseMove: function(e) { + this.handleMouseEvent(e); + }, + + handleMouseEvent: function(e) { + var me = this, + mask = me.maskType, + bbox = me.bbox || me.chartBBox, + x = bbox.x, + y = bbox.y, + math = Math, + floor = math.floor, + abs = math.abs, + min = math.min, + max = math.max, + height = floor(y + bbox.height), + width = floor(x + bbox.width), + staticX = e.getPageX() - me.el.getX(), + staticY = e.getPageY() - me.el.getY(), + maskMouseDown = me.maskMouseDown, + path; + + staticX = max(staticX, x); + staticY = max(staticY, y); + staticX = min(staticX, width); + staticY = min(staticY, height); + + if (e.type === 'mousedown') { + + me.mouseDown = true; + me.mouseMoved = false; + me.maskMouseDown = { + x: staticX, + y: staticY + }; + } + else { + + + me.mouseMoved = me.mouseDown; + if (maskMouseDown && me.mouseDown) { + if (mask == 'horizontal') { + staticY = y; + maskMouseDown.y = height; + } + else if (mask == 'vertical') { + staticX = x; + maskMouseDown.x = width; + } + width = maskMouseDown.x - staticX; + height = maskMouseDown.y - staticY; + path = ['M', staticX, staticY, 'l', width, 0, 0, height, -width, 0, 'z']; + me.maskSelection = { + x: (width > 0 ? staticX : staticX + width) + me.el.getX(), + y: (height > 0 ? staticY : staticY + height) + me.el.getY(), + width: abs(width), + height: abs(height) + }; + me.mask.updateBox(me.maskSelection); + me.mask.show(); + me.maskSprite.setAttributes({ + hidden: true + }, true); + } + else { + if (mask == 'horizontal') { + path = ['M', staticX, y, 'L', staticX, height]; + } + else if (mask == 'vertical') { + path = ['M', x, staticY, 'L', width, staticY]; + } + else { + path = ['M', staticX, y, 'L', staticX, height, 'M', x, staticY, 'L', width, staticY]; + } + me.maskSprite.setAttributes({ + path: path, + 'stroke-width': mask === true ? 1 : 1, + hidden: false + }, true); + } + } + + }, + + onMouseLeave: function(e) { + var me = this; + me.mouseMoved = false; + me.mouseDown = false; + me.maskMouseDown = false; + me.mask.hide(); + me.maskSprite.hide(true); + } +}); + + + + +Ext.define('Ext.chart.Navigation', { + + + setZoom: function(zoomConfig) { + var me = this, + axesItems = me.axes.items, + i, ln, axis, + bbox = me.chartBBox, + xScale = bbox.width, + yScale = bbox.height, + zoomArea = { + x : zoomConfig.x - me.el.getX(), + y : zoomConfig.y - me.el.getY(), + width : zoomConfig.width, + height : zoomConfig.height + }, + zoomer, ends, from, to, store, count, step, length, horizontal; + + for (i = 0, ln = axesItems.length; i < ln; i++) { + axis = axesItems[i]; + horizontal = (axis.position == 'bottom' || axis.position == 'top'); + if (axis.type == 'Category') { + if (!store) { + store = me.getChartStore(); + count = store.data.items.length; + } + zoomer = zoomArea; + length = axis.length; + step = Math.round(length / count); + if (horizontal) { + from = (zoomer.x ? Math.floor(zoomer.x / step) + 1 : 0); + to = (zoomer.x + zoomer.width) / step; + } else { + from = (zoomer.y ? Math.floor(zoomer.y / step) + 1 : 0); + to = (zoomer.y + zoomer.height) / step; + } + } + else { + zoomer = { + x : zoomArea.x / xScale, + y : zoomArea.y / yScale, + width : zoomArea.width / xScale, + height : zoomArea.height / yScale + }; + ends = axis.calcEnds(); + if (horizontal) { + from = (ends.to - ends.from) * zoomer.x + ends.from; + to = (ends.to - ends.from) * zoomer.width + from; + } else { + to = (ends.to - ends.from) * (1 - zoomer.y) + ends.from; + from = to - (ends.to - ends.from) * zoomer.height; + } + } + axis.minimum = from; + axis.maximum = to; + if (horizontal) { + if (axis.doConstrain && me.maskType != 'vertical') { + axis.doConstrain(); + } + } + else { + if (axis.doConstrain && me.maskType != 'horizontal') { + axis.doConstrain(); + } + } + } + me.redraw(false); + }, + + + restoreZoom: function() { + var me = this, + axesItems = me.axes.items, + i, ln, axis; + + me.setSubStore(null); + for (i = 0, ln = axesItems.length; i < ln; i++) { + axis = axesItems[i]; + delete axis.minimum; + delete axis.maximum; + } + me.redraw(false); + } + +}); + + + +Ext.define('Ext.chart.Shape', { + + + + singleton: true, + + + + circle: function (surface, opts) { + return surface.add(Ext.apply({ + type: 'circle', + x: opts.x, + y: opts.y, + stroke: null, + radius: opts.radius + }, opts)); + }, + line: function (surface, opts) { + return surface.add(Ext.apply({ + type: 'rect', + x: opts.x - opts.radius, + y: opts.y - opts.radius, + height: 2 * opts.radius, + width: 2 * opts.radius / 5 + }, opts)); + }, + square: function (surface, opts) { + return surface.add(Ext.applyIf({ + type: 'rect', + x: opts.x - opts.radius, + y: opts.y - opts.radius, + height: 2 * opts.radius, + width: 2 * opts.radius, + radius: null + }, opts)); + }, + triangle: function (surface, opts) { + opts.radius *= 1.75; + return surface.add(Ext.apply({ + type: 'path', + stroke: null, + path: "M".concat(opts.x, ",", opts.y, "m0-", opts.radius * 0.58, "l", opts.radius * 0.5, ",", opts.radius * 0.87, "-", opts.radius, ",0z") + }, opts)); + }, + diamond: function (surface, opts) { + var r = opts.radius; + r *= 1.5; + return surface.add(Ext.apply({ + type: 'path', + stroke: null, + path: ["M", opts.x, opts.y - r, "l", r, r, -r, r, -r, -r, r, -r, "z"] + }, opts)); + }, + cross: function (surface, opts) { + var r = opts.radius; + r = r / 1.7; + return surface.add(Ext.apply({ + type: 'path', + stroke: null, + path: "M".concat(opts.x - r, ",", opts.y, "l", [-r, -r, r, -r, r, r, r, -r, r, r, -r, r, r, r, -r, r, -r, -r, -r, r, -r, -r, "z"]) + }, opts)); + }, + plus: function (surface, opts) { + var r = opts.radius / 1.3; + return surface.add(Ext.apply({ + type: 'path', + stroke: null, + path: "M".concat(opts.x - r / 2, ",", opts.y - r / 2, "l", [0, -r, r, 0, 0, r, r, 0, 0, r, -r, 0, 0, r, -r, 0, 0, -r, -r, 0, 0, -r, "z"]) + }, opts)); + }, + arrow: function (surface, opts) { + var r = opts.radius; + return surface.add(Ext.apply({ + type: 'path', + path: "M".concat(opts.x - r * 0.7, ",", opts.y - r * 0.4, "l", [r * 0.6, 0, 0, -r * 0.4, r, r * 0.8, -r, r * 0.8, 0, -r * 0.4, -r * 0.6, 0], "z") + }, opts)); + }, + drop: function (surface, x, y, text, size, angle) { + size = size || 30; + angle = angle || 0; + surface.add({ + type: 'path', + path: ['M', x, y, 'l', size, 0, 'A', size * 0.4, size * 0.4, 0, 1, 0, x + size * 0.7, y - size * 0.7, 'z'], + fill: '#000', + stroke: 'none', + rotate: { + degrees: 22.5 - angle, + x: x, + y: y + } + }); + angle = (angle + 90) * Math.PI / 180; + surface.add({ + type: 'text', + x: x + size * Math.sin(angle) - 10, + y: y + size * Math.cos(angle) + 5, + text: text, + 'font-size': size * 12 / 40, + stroke: 'none', + fill: '#fff' + }); + } +}); + + + +Ext.define('Ext.chart.LegendItem', { + + + + extend: Ext.draw.CompositeSprite , + + + + + + + hiddenSeries: false, + + + label: undefined, + + + x: 0, + y: 0, + zIndex: 500, + + + boldRe: /bold\s\d{1,}.*/i, + + constructor: function(config) { + this.callParent(arguments); + this.createLegend(config); + }, + + + createLegend: function(config) { + var me = this, + series = me.series, + index = config.yFieldIndex; + + me.label = me.createLabel(config); + me.createSeriesMarkers(config); + + me.setAttributes({ + hidden: false + }, true); + + me.yFieldIndex = index; + + + me.on('mouseover', me.onMouseOver, me); + me.on('mouseout', me.onMouseOut, me); + me.on('mousedown', me.onMouseDown, me); + + if (!series.visibleInLegend(index)) { + me.hiddenSeries = true; + me.label.setAttributes({ + opacity: 0.5 + }, true); + }; + + + me.updatePosition({ x: 0, y: 0 }); + }, + + + getLabelText: function() { + var me = this, + series = me.series, + idx = me.yFieldIndex; + + function getSeriesProp(name) { + var val = series[name]; + return (Ext.isArray(val) ? val[idx] : val); + } + + return getSeriesProp('title') || getSeriesProp('yField'); + }, + + + createLabel: function(config) { + var me = this, + legend = me.legend; + + return me.add('label', me.surface.add({ + type: 'text', + x: 20, + y: 0, + zIndex: (me.zIndex || 0) + 2, + fill: legend.labelColor, + font: legend.labelFont, + text: me.getLabelText(), + style: { + cursor: 'pointer' + } + })); + }, + + + createSeriesMarkers: function(config) { + var me = this, + index = config.yFieldIndex, + series = me.series, + seriesType = series.type, + surface = me.surface, + z = me.zIndex; + + + if (seriesType === 'line' || seriesType === 'scatter') { + if(seriesType === 'line') { + var seriesStyle = Ext.apply(series.seriesStyle, series.style); + me.drawLine(0.5, 0.5, 16.5, 0.5, z, seriesStyle, index); + }; + + if (series.showMarkers || seriesType === 'scatter') { + var markerConfig = Ext.apply(series.markerStyle, series.markerConfig || {}, { + fill: series.getLegendColor(index) + }); + me.drawMarker(8.5, 0.5, z, markerConfig); + } + } + + else { + me.drawFilledBox(12, 12, z, index); + } + }, + + + drawLine: function(fromX, fromY, toX, toY, z, seriesStyle, index) { + var me = this, + surface = me.surface, + series = me.series; + + return me.add('line', surface.add({ + type: 'path', + path: 'M' + fromX + ',' + fromY + 'L' + toX + ',' + toY, + zIndex: (z || 0) + 2, + "stroke-width": series.lineWidth, + "stroke-linejoin": "round", + "stroke-dasharray": series.dash, + stroke: seriesStyle.stroke || series.getLegendColor(index) || '#000', + style: { + cursor: 'pointer' + } + })); + }, + + + drawMarker: function(x, y, z, markerConfig) { + var me = this, + surface = me.surface, + series = me.series; + + return me.add('marker', Ext.chart.Shape[markerConfig.type](surface, { + fill: markerConfig.fill, + x: x, + y: y, + zIndex: (z || 0) + 2, + radius: markerConfig.radius || markerConfig.size, + style: { + cursor: 'pointer' + } + })); + }, + + + drawFilledBox: function(width, height, z, index) { + var me = this, + surface = me.surface, + series = me.series; + + return me.add('box', surface.add({ + type: 'rect', + zIndex: (z || 0) + 2, + x: 0, + y: 0, + width: width, + height: height, + fill: series.getLegendColor(index), + style: { + cursor: 'pointer' + } + })); + }, + + + onMouseOver: function() { + var me = this; + + me.label.setStyle({ + 'font-weight': 'bold' + }); + me.series._index = me.yFieldIndex; + me.series.highlightItem(); + }, + + + onMouseOut: function() { + var me = this, + legend = me.legend, + boldRe = me.boldRe; + + me.label.setStyle({ + 'font-weight': legend.labelFont && boldRe.test(legend.labelFont) ? 'bold' : 'normal' + }); + me.series._index = me.yFieldIndex; + me.series.unHighlightItem(); + }, + + + onMouseDown: function() { + var me = this, + index = me.yFieldIndex; + + if (!me.hiddenSeries) { + me.series.hideAll(index); + me.label.setAttributes({ + opacity: 0.5 + }, true); + } else { + me.series.showAll(index); + me.label.setAttributes({ + opacity: 1 + }, true); + } + me.hiddenSeries = !me.hiddenSeries; + me.legend.chart.redraw(); + }, + + + updatePosition: function(relativeTo) { + var me = this, + items = me.items, + ln = items.length, + currentX = me.x, + currentY = me.y, + item, i, x, y, translate, o, + relativeX, relativeY; + + if (!relativeTo) { + relativeTo = me.legend; + } + + relativeX = relativeTo.x; + relativeY = relativeTo.y; + for (i = 0; i < ln; i++) { + translate = true; + item = items[i]; + switch (item.type) { + case 'text': + x = 20 + relativeX + currentX; + y = relativeY + currentY; + translate = false; + break; + case 'rect': + x = relativeX + currentX; + y = relativeY + currentY - 6; + break; + default: + x = relativeX + currentX; + y = relativeY + currentY; + } + + o = { + x: x, + y: y + }; + item.setAttributes(translate ? { + translate: o + } : o, true); + } + } +}); + + + +Ext.define('Ext.chart.Legend', { + + + + + + + + + visible: true, + + + update: true, + + + position: 'bottom', + + + x: 0, + + + y: 0, + + + labelColor: '#000', + + + labelFont: '12px Helvetica, sans-serif', + + + boxStroke: '#000', + + + boxStrokeWidth: 1, + + + boxFill: '#FFF', + + + itemSpacing: 10, + + + padding: 5, + + + width: 0, + + height: 0, + + + boxZIndex: 100, + + + constructor: function(config) { + var me = this; + if (config) { + Ext.apply(me, config); + } + me.items = []; + + me.isVertical = ("left|right|float".indexOf(me.position) !== -1); + + + me.origX = me.x; + me.origY = me.y; + }, + + + create: function() { + var me = this, + seriesItems = me.chart.series.items, + i, ln, series; + + me.createBox(); + + if (me.rebuild !== false) { + me.createItems(); + } + + if (!me.created && me.isDisplayed()) { + me.created = true; + + + for (i = 0, ln = seriesItems.length; i < ln; i++) { + series = seriesItems[i]; + series.on('titlechange', me.redraw, me); + } + } + }, + + init: Ext.emptyFn, + + + redraw: function() { + var me = this; + + me.create(); + me.updatePosition(); + }, + + + isDisplayed: function() { + return this.visible && this.chart.series.findIndex('showInLegend', true) !== -1; + }, + + + createItems: function() { + var me = this, + seriesItems = me.chart.series.items, + items = me.items, + fields, i, li, j, lj, series, item; + + + me.removeItems(); + + + for (i = 0, li = seriesItems.length; i < li; i++) { + series = seriesItems[i]; + + if (series.showInLegend) { + fields = [].concat(series.yField); + + for (j = 0, lj = fields.length; j < lj; j++) { + item = me.createLegendItem(series, j); + items.push(item); + } + } + } + + me.alignItems(); + }, + + + removeItems: function() { + var me = this, + items = me.items, + len = items ? items.length : 0, + i; + + if (len) { + for (i = 0; i < len; i++) { + items[i].destroy(); + } + } + + + items.length = []; + }, + + + alignItems: function() { + var me = this, + padding = me.padding, + vertical = me.isVertical, + mfloor = Math.floor, + dim, maxWidth, maxHeight, totalWidth, totalHeight; + + dim = me.updateItemDimensions(); + + maxWidth = dim.maxWidth; + maxHeight = dim.maxHeight; + totalWidth = dim.totalWidth; + totalHeight = dim.totalHeight; + + + me.width = mfloor((vertical ? maxWidth : totalWidth) + padding * 2); + me.height = mfloor((vertical ? totalHeight : maxHeight) + padding * 2); + }, + + updateItemDimensions: function() { + var me = this, + items = me.items, + padding = me.padding, + itemSpacing = me.itemSpacing, + maxWidth = 0, + maxHeight = 0, + totalWidth = 0, + totalHeight = 0, + vertical = me.isVertical, + mfloor = Math.floor, + mmax = Math.max, + spacing = 0, + i, l, item, bbox, width, height; + + + + for (i = 0, l = items.length; i < l; i++) { + item = items[i]; + + bbox = item.getBBox(); + + + width = bbox.width; + height = bbox.height; + + spacing = (i === 0 ? 0 : itemSpacing); + + + item.x = padding + mfloor(vertical ? 0 : totalWidth + spacing); + item.y = padding + mfloor(vertical ? totalHeight + spacing : 0) + height / 2; + + + totalWidth += spacing + width; + totalHeight += spacing + height; + maxWidth = mmax(maxWidth, width); + maxHeight = mmax(maxHeight, height); + } + + return { + totalWidth: totalWidth, + totalHeight: totalHeight, + maxWidth: maxWidth, + maxHeight: maxHeight + }; + }, + + + createLegendItem: function(series, yFieldIndex) { + var me = this; + + return new Ext.chart.LegendItem({ + legend: me, + series: series, + surface: me.chart.surface, + yFieldIndex: yFieldIndex + }); + }, + + + getBBox: function() { + var me = this; + return { + x: Math.round(me.x) - me.boxStrokeWidth / 2, + y: Math.round(me.y) - me.boxStrokeWidth / 2, + width: me.width + me.boxStrokeWidth, + height: me.height + me.boxStrokeWidth + }; + }, + + + createBox: function() { + var me = this, + box, bbox; + + if (me.boxSprite) { + me.boxSprite.destroy(); + } + + bbox = me.getBBox(); + + + + + + if (isNaN(bbox.width) || isNaN(bbox.height)) { + me.boxSprite = false; + return; + } + + box = me.boxSprite = me.chart.surface.add(Ext.apply({ + type: 'rect', + stroke: me.boxStroke, + "stroke-width": me.boxStrokeWidth, + fill: me.boxFill, + zIndex: me.boxZIndex + }, bbox)); + + box.redraw(); + }, + + + calcPosition: function() { + var me = this, + x, y, + legendWidth = me.width, + legendHeight = me.height, + chart = me.chart, + chartBBox = chart.chartBBox, + insets = chart.insetPadding, + chartWidth = chartBBox.width - (insets * 2), + chartHeight = chartBBox.height - (insets * 2), + chartX = chartBBox.x + insets, + chartY = chartBBox.y + insets, + surface = chart.surface, + mfloor = Math.floor; + + + switch(me.position) { + case "left": + x = insets; + y = mfloor(chartY + chartHeight / 2 - legendHeight / 2); + break; + case "right": + x = mfloor(surface.width - legendWidth) - insets; + y = mfloor(chartY + chartHeight / 2 - legendHeight / 2); + break; + case "top": + x = mfloor(chartX + chartWidth / 2 - legendWidth / 2); + y = insets; + break; + case "bottom": + x = mfloor(chartX + chartWidth / 2 - legendWidth / 2); + y = mfloor(surface.height - legendHeight) - insets; + break; + default: + x = mfloor(me.origX) + insets; + y = mfloor(me.origY) + insets; + } + + return { x: x, y: y }; + }, + + + updatePosition: function() { + var me = this, + items = me.items, + pos, i, l, bbox; + + if (me.isDisplayed()) { + + pos = me.calcPosition(); + + me.x = pos.x; + me.y = pos.y; + + + for (i = 0, l = items.length; i < l; i++) { + items[i].updatePosition(); + } + + bbox = me.getBBox(); + + + + + + + if (isNaN(bbox.width) || isNaN(bbox.height)) { + if (me.boxSprite) { + me.boxSprite.hide(true); + } + } + else { + if (!me.boxSprite) { + me.createBox(); + } + + + me.boxSprite.setAttributes(bbox, true); + me.boxSprite.show(true); + } + } + }, + + + toggle: function(show) { + var me = this, + i = 0, + items = me.items, + len = items.length; + + if (me.boxSprite) { + if (show) { + me.boxSprite.show(true); + } else { + me.boxSprite.hide(true); + } + } + + for (; i < len; ++i) { + if (show) { + items[i].show(true); + } else { + items[i].hide(true); + } + } + + me.visible = show; + } +}); + + + +Ext.define('Ext.chart.theme.Base', { + + + + + + + + constructor: function(config) { + var ident = Ext.identityFn; + Ext.chart.theme.call(this, config, { + background: false, + axis: { + stroke: '#444', + 'stroke-width': 1 + }, + axisLabelTop: { + fill: '#444', + font: '12px Arial, Helvetica, sans-serif', + spacing: 2, + padding: 5, + renderer: ident + }, + axisLabelRight: { + fill: '#444', + font: '12px Arial, Helvetica, sans-serif', + spacing: 2, + padding: 5, + renderer: ident + }, + axisLabelBottom: { + fill: '#444', + font: '12px Arial, Helvetica, sans-serif', + spacing: 2, + padding: 5, + renderer: ident + }, + axisLabelLeft: { + fill: '#444', + font: '12px Arial, Helvetica, sans-serif', + spacing: 2, + padding: 5, + renderer: ident + }, + axisTitleTop: { + font: 'bold 18px Arial', + fill: '#444' + }, + axisTitleRight: { + font: 'bold 18px Arial', + fill: '#444', + rotate: { + x:0, y:0, + degrees: 270 + } + }, + axisTitleBottom: { + font: 'bold 18px Arial', + fill: '#444' + }, + axisTitleLeft: { + font: 'bold 18px Arial', + fill: '#444', + rotate: { + x:0, y:0, + degrees: 270 + } + }, + series: { + 'stroke-width': 0 + }, + seriesLabel: { + font: '12px Arial', + fill: '#333' + }, + marker: { + stroke: '#555', + radius: 3, + size: 3 + }, + colors: [ "#94ae0a", "#115fa6","#a61120", "#ff8809", "#ffd13e", "#a61187", "#24ad9a", "#7c7474", "#a66111"], + seriesThemes: [{ + fill: "#94ae0a" + }, { + fill: "#115fa6" + }, { + fill: "#a61120" + }, { + fill: "#ff8809" + }, { + fill: "#ffd13e" + }, { + fill: "#a61187" + }, { + fill: "#24ad9a" + }, { + fill: "#7c7474" + }, { + fill: "#115fa6" + }, { + fill: "#94ae0a" + }, { + fill: "#a61120" + }, { + fill: "#ff8809" + }, { + fill: "#ffd13e" + }, { + fill: "#a61187" + }, { + fill: "#24ad9a" + }, { + fill: "#7c7474" + }, { + fill: "#a66111" + }], + markerThemes: [{ + fill: "#115fa6", + type: 'circle' + }, { + fill: "#94ae0a", + type: 'cross' + }, { + fill: "#115fa6", + type: 'plus' + }, { + fill: "#94ae0a", + type: 'circle' + }, { + fill: "#a61120", + type: 'cross' + }] + }); + } +}, function() { + var palette = ['#b1da5a', '#4ce0e7', '#e84b67', '#da5abd', '#4d7fe6', '#fec935'], + names = ['Green', 'Sky', 'Red', 'Purple', 'Blue', 'Yellow'], + i = 0, j = 0, l = palette.length, themes = Ext.chart.theme, + categories = [['#f0a50a', '#c20024', '#2044ba', '#810065', '#7eae29'], + ['#6d9824', '#87146e', '#2a9196', '#d39006', '#1e40ac'], + ['#fbbc29', '#ce2e4e', '#7e0062', '#158b90', '#57880e'], + ['#ef5773', '#fcbd2a', '#4f770d', '#1d3eaa', '#9b001f'], + ['#7eae29', '#fdbe2a', '#910019', '#27b4bc', '#d74dbc'], + ['#44dce1', '#0b2592', '#996e05', '#7fb325', '#b821a1']], + cats = categories.length; + + + for (; i < l; i++) { + themes[names[i]] = (function(color) { + return Ext.extend(themes.Base, { + constructor: function(config) { + themes.Base.prototype.constructor.call(this, Ext.apply({ + baseColor: color + }, config)); + } + }); + }(palette[i])); + } + + + for (i = 0; i < cats; i++) { + themes['Category' + (i + 1)] = (function(category) { + return Ext.extend(themes.Base, { + constructor: function(config) { + themes.Base.prototype.constructor.call(this, Ext.apply({ + colors: category + }, config)); + } + }); + }(categories[i])); + } +}); + + + +Ext.define('Ext.chart.Chart', { + extend: Ext.draw.Component , + + alias: 'widget.chart', + + mixins: { + themeManager: Ext.chart.theme.Theme , + mask: Ext.chart.Mask , + navigation: Ext.chart.Navigation , + bindable: Ext.util.Bindable , + observable: Ext.util.Observable + }, + + + + + + + + + + + + + + + + + + viewBox: false, + + + + + animate: false, + + + legend: false, + + + insetPadding: 10, + + + background: false, + + + + + + + + + + refreshBuffer: 1, + + constructor: function(config) { + var me = this, + defaultAnim; + + config = Ext.apply({}, config); + me.initTheme(config.theme || me.theme); + if (me.gradients) { + Ext.apply(config, { gradients: me.gradients }); + } + if (me.background) { + Ext.apply(config, { background: me.background }); + } + if (config.animate) { + defaultAnim = { + easing: 'ease', + duration: 500 + }; + if (Ext.isObject(config.animate)) { + config.animate = Ext.applyIf(config.animate, defaultAnim); + } + else { + config.animate = defaultAnim; + } + } + + me.mixins.observable.constructor.call(me, config); + + if (config.mask) { + config = Ext.apply({ enableMask: true }, config); + } + + if (config.enableMask) { + me.mixins.mask.constructor.call(me, config); + } + me.mixins.navigation.constructor.call(me); + me.callParent([config]); + }, + + getChartStore: function(){ + return this.substore || this.store; + }, + + initComponent: function() { + var me = this, + axes, + series; + me.callParent(); + me.addEvents( + 'itemmousedown', + 'itemmouseup', + 'itemmouseover', + 'itemmouseout', + 'itemclick', + 'itemdblclick', + 'itemdragstart', + 'itemdrag', + 'itemdragend', + + 'beforerefresh', + + 'refresh' + ); + Ext.applyIf(me, { + zoom: { + width: 1, + height: 1, + x: 0, + y: 0 + } + }); + me.maxGutters = { left: 0, right: 0, bottom: 0, top: 0 }; + me.store = Ext.data.StoreManager.lookup(me.store); + axes = me.axes; + me.axes = new Ext.util.MixedCollection(false, function(a) { return a.position; }); + if (axes) { + me.axes.addAll(axes); + } + series = me.series; + me.series = new Ext.util.MixedCollection(false, function(a) { return a.seriesId || (a.seriesId = Ext.id(null, 'ext-chart-series-')); }); + if (series) { + me.series.addAll(series); + } + if (me.legend !== false) { + me.legend = new Ext.chart.Legend(Ext.applyIf({ + chart: me + }, me.legend)); + } + + me.on({ + mousemove: me.onMouseMove, + mouseleave: me.onMouseLeave, + mousedown: me.onMouseDown, + mouseup: me.onMouseUp, + click: me.onClick, + dblclick: me.onDblClick, + scope: me + }); + }, + + + afterComponentLayout: function(width, height, oldWidth, oldHeight) { + var me = this; + if (Ext.isNumber(width) && Ext.isNumber(height)) { + if (width !== oldWidth || height !== oldHeight) { + me.curWidth = width; + me.curHeight = height; + me.redraw(true); + me.needsRedraw = false; + } else if (me.needsRedraw) { + me.redraw(); + me.needsRedraw = false; + } + } + this.callParent(arguments); + }, + + + redraw: function(resize) { + var me = this, + seriesItems = me.series.items, + seriesLen = seriesItems.length, + axesItems = me.axes.items, + axesLen = axesItems.length, + themeIndex = 0, + i, item, + chartBBox = me.chartBBox = { + x: 0, + y: 0, + height: me.curHeight, + width: me.curWidth + }, + legend = me.legend, + series; + + me.surface.setSize(chartBBox.width, chartBBox.height); + + for (i = 0; i < seriesLen; i++) { + item = seriesItems[i]; + if (!item.initialized) { + series = me.initializeSeries(item, i, themeIndex); + } else { + series = item; + } + + + series.onRedraw(); + + + if (Ext.isArray(item.yField)) { + themeIndex += item.yField.length; + } else { + ++themeIndex; + } + } + for (i = 0; i < axesLen; i++) { + item = axesItems[i]; + if (!item.initialized) { + me.initializeAxis(item); + } + } + + + for (i = 0; i < axesLen; i++) { + axesItems[i].processView(); + } + for (i = 0; i < axesLen; i++) { + axesItems[i].drawAxis(true); + } + + + if (legend !== false && legend.visible) { + if (legend.update || !legend.created) { + legend.create(); + } + } + + + me.alignAxes(); + + + if (legend !== false && legend.visible) { + legend.updatePosition(); + } + + + me.getMaxGutters(); + + + me.resizing = !!resize; + + for (i = 0; i < axesLen; i++) { + axesItems[i].drawAxis(); + } + for (i = 0; i < seriesLen; i++) { + me.drawCharts(seriesItems[i]); + } + me.resizing = false; + }, + + + afterRender: function() { + var me = this, + legend = me.legend; + + me.callParent(arguments); + + if (me.categoryNames) { + me.setCategoryNames(me.categoryNames); + } + + if (legend) { + legend.init(); + } + + me.bindStore(me.store, true); + me.refresh(); + + if (me.surface.engine === 'Vml') { + me.on('added', me.onAddedVml, me); + me.mon(me.hierarchyEventSource, 'added', me.onContainerAddedVml, me); + } + }, + + + + + + + onAddedVml: function() { + this.needsRedraw = true; + }, + + onContainerAddedVml: function(container) { + if (this.isDescendantOf(container)) { + this.needsRedraw = true; + } + }, + + + getEventXY: function(e) { + var box = this.surface.getRegion(), + pageXY = e.getXY(), + x = pageXY[0] - box.left, + y = pageXY[1] - box.top; + + return [x, y]; + }, + + onClick: function(e) { + this.handleClick('itemclick', e); + }, + + onDblClick: function(e) { + this.handleClick('itemdblclick', e); + }, + + + handleClick: function(name, e) { + var me = this, + position = me.getEventXY(e), + seriesItems = me.series.items, + i, ln, series, + item; + + + + for (i = 0, ln = seriesItems.length; i < ln; i++) { + series = seriesItems[i]; + if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) { + if (series.getItemForPoint) { + item = series.getItemForPoint(position[0], position[1]); + if (item) { + series.fireEvent(name, item); + } + } + } + } + }, + + + onMouseDown: function(e) { + var me = this, + position = me.getEventXY(e), + seriesItems = me.series.items, + i, ln, series, + item; + + if (me.enableMask) { + me.mixins.mask.onMouseDown.call(me, e); + } + + + for (i = 0, ln = seriesItems.length; i < ln; i++) { + series = seriesItems[i]; + if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) { + if (series.getItemForPoint) { + item = series.getItemForPoint(position[0], position[1]); + if (item) { + series.fireEvent('itemmousedown', item); + } + } + } + } + }, + + + onMouseUp: function(e) { + var me = this, + position = me.getEventXY(e), + seriesItems = me.series.items, + i, ln, series, + item; + + if (me.enableMask) { + me.mixins.mask.onMouseUp.call(me, e); + } + + + for (i = 0, ln = seriesItems.length; i < ln; i++) { + series = seriesItems[i]; + if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) { + if (series.getItemForPoint) { + item = series.getItemForPoint(position[0], position[1]); + if (item) { + series.fireEvent('itemmouseup', item); + } + } + } + } + }, + + + onMouseMove: function(e) { + var me = this, + position = me.getEventXY(e), + seriesItems = me.series.items, + i, ln, series, + item, last, storeItem, storeField; + + + if (me.enableMask) { + me.mixins.mask.onMouseMove.call(me, e); + } + + + for (i = 0, ln = seriesItems.length; i < ln; i++) { + series = seriesItems[i]; + if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) { + if (series.getItemForPoint) { + item = series.getItemForPoint(position[0], position[1]); + last = series._lastItemForPoint; + storeItem = series._lastStoreItem; + storeField = series._lastStoreField; + + + if (item !== last || item && (item.storeItem != storeItem || item.storeField != storeField)) { + if (last) { + series.fireEvent('itemmouseout', last); + delete series._lastItemForPoint; + delete series._lastStoreField; + delete series._lastStoreItem; + } + if (item) { + series.fireEvent('itemmouseover', item); + series._lastItemForPoint = item; + series._lastStoreItem = item.storeItem; + series._lastStoreField = item.storeField; + } + } + } + } else { + last = series._lastItemForPoint; + if (last) { + series.fireEvent('itemmouseout', last); + delete series._lastItemForPoint; + delete series._lastStoreField; + delete series._lastStoreItem; + } + } + } + }, + + + onMouseLeave: function(e) { + var me = this, + seriesItems = me.series.items, + i, ln, series; + + if (me.enableMask) { + me.mixins.mask.onMouseLeave.call(me, e); + } + for (i = 0, ln = seriesItems.length; i < ln; i++) { + series = seriesItems[i]; + delete series._lastItemForPoint; + } + }, + + + delayRefresh: function() { + var me = this; + if (!me.refreshTask) { + me.refreshTask = new Ext.util.DelayedTask(me.refresh, me); + } + me.refreshTask.delay(me.refreshBuffer); + }, + + + refresh: function() { + var me = this; + + if (me.rendered && me.curWidth !== undefined && me.curHeight !== undefined) { + if (!me.isVisible(true)) { + if (!me.refreshPending) { + me.setShowListeners('mon'); + me.refreshPending = true; + } + return; + } + if (me.fireEvent('beforerefresh', me) !== false) { + me.redraw(); + me.fireEvent('refresh', me); + } + } + }, + + onShow: function(){ + var me = this; + me.callParent(arguments); + if (me.refreshPending) { + me.delayRefresh(); + me.setShowListeners('mun'); + } + delete me.refreshPending; + }, + + setShowListeners: function(method){ + var me = this; + me[method](me.hierarchyEventSource, { + scope: me, + single: true, + show: me.forceRefresh, + expand: me.forceRefresh + }); + }, + + doRefresh: function(){ + + this.setSubStore(null); + this.refresh(); + }, + + forceRefresh: function(container) { + var me = this; + if (me.isDescendantOf(container) && me.refreshPending) { + + + me.setShowListeners('mun'); + me.delayRefresh(); + } + delete me.refreshPending; + }, + + bindStore: function(store, initial) { + var me = this; + me.mixins.bindable.bindStore.apply(me, arguments); + if (me.store && !initial) { + me.refresh(); + } + }, + + getStoreListeners: function() { + var refresh = this.doRefresh, + delayRefresh = this.delayRefresh; + + return { + refresh: refresh, + add: delayRefresh, + bulkremove: delayRefresh, + update: delayRefresh, + clear: refresh + }; + }, + + setSubStore: function(subStore){ + this.substore = subStore; + }, + + + initializeAxis: function(axis) { + var me = this, + chartBBox = me.chartBBox, + w = chartBBox.width, + h = chartBBox.height, + x = chartBBox.x, + y = chartBBox.y, + themeAttrs = me.themeAttrs, + axes = me.axes, + config = { + chart: me + }; + + if (themeAttrs) { + config.axisStyle = Ext.apply({}, themeAttrs.axis); + config.axisLabelLeftStyle = Ext.apply({}, themeAttrs.axisLabelLeft); + config.axisLabelRightStyle = Ext.apply({}, themeAttrs.axisLabelRight); + config.axisLabelTopStyle = Ext.apply({}, themeAttrs.axisLabelTop); + config.axisLabelBottomStyle = Ext.apply({}, themeAttrs.axisLabelBottom); + config.axisTitleLeftStyle = Ext.apply({}, themeAttrs.axisTitleLeft); + config.axisTitleRightStyle = Ext.apply({}, themeAttrs.axisTitleRight); + config.axisTitleTopStyle = Ext.apply({}, themeAttrs.axisTitleTop); + config.axisTitleBottomStyle = Ext.apply({}, themeAttrs.axisTitleBottom); + me.configureAxisStyles(config); + } + + switch (axis.position) { + case 'top': + Ext.apply(config, { + length: w, + width: h, + x: x, + y: y + }); + break; + case 'bottom': + Ext.apply(config, { + length: w, + width: h, + x: x, + y: h + }); + break; + case 'left': + Ext.apply(config, { + length: h, + width: w, + x: x, + y: h + }); + break; + case 'right': + Ext.apply(config, { + length: h, + width: w, + x: w, + y: h + }); + break; + } + + if (!axis.chart) { + Ext.apply(config, axis); + axis = Ext.createByAlias('axis.' + axis.type.toLowerCase(), config); + axes.replace(axis); + } else { + Ext.apply(axis, config); + } + axis.initialized = true; + }, + + configureAxisStyles: Ext.emptyFn, + + + getInsets: function() { + var me = this, + insetPadding = me.insetPadding; + + return { + top: insetPadding, + right: insetPadding, + bottom: insetPadding, + left: insetPadding + }; + }, + + + calculateInsets: function() { + var me = this, + legend = me.legend, + axes = me.axes, + edges = ['top', 'right', 'bottom', 'left'], + insets, i, l, edge, isVertical, axis, bbox; + + function getAxis(edge) { + var i = axes.findIndex('position', edge); + return (i < 0) ? null : axes.getAt(i); + } + + insets = me.getInsets(); + + + for (i = 0, l = edges.length; i < l; i++) { + edge = edges[i]; + + isVertical = (edge === 'left' || edge === 'right'); + axis = getAxis(edge); + + + if (legend !== false) { + if (legend.position === edge) { + bbox = legend.getBBox(); + insets[edge] += (isVertical ? bbox.width : bbox.height) + me.insetPadding; + } + } + + + + if (axis && axis.bbox) { + bbox = axis.bbox; + insets[edge] += (isVertical ? bbox.width : bbox.height); + } + } + + return insets; + }, + + + alignAxes: function() { + var me = this, + axesItems = me.axes.items, + insets, chartBBox, i, l, axis, pos, isVertical; + + insets = me.calculateInsets(); + + + chartBBox = { + x: insets.left, + y: insets.top, + width: me.curWidth - insets.left - insets.right, + height: me.curHeight - insets.top - insets.bottom + }; + me.chartBBox = chartBBox; + + + + for (i = 0, l = axesItems.length; i < l; i++) { + axis = axesItems[i]; + pos = axis.position; + isVertical = pos === 'left' || pos === 'right'; + + axis.x = (pos === 'right' ? chartBBox.x + chartBBox.width : chartBBox.x); + axis.y = (pos === 'top' ? chartBBox.y : chartBBox.y + chartBBox.height); + axis.width = (isVertical ? chartBBox.width : chartBBox.height); + axis.length = (isVertical ? chartBBox.height : chartBBox.width); + } + }, + + + initializeSeries: function(series, idx, themeIndex) { + var me = this, + themeAttrs = me.themeAttrs, + seriesObj, markerObj, seriesThemes, st, + markerThemes, colorArrayStyle = [], + isInstance = (series instanceof Ext.chart.series.Series). + i = 0, l, config; + + if (!series.initialized) { + config = { + chart: me, + seriesId: series.seriesId + }; + if (themeAttrs) { + seriesThemes = themeAttrs.seriesThemes; + markerThemes = themeAttrs.markerThemes; + seriesObj = Ext.apply({}, themeAttrs.series); + markerObj = Ext.apply({}, themeAttrs.marker); + config.seriesStyle = Ext.apply(seriesObj, seriesThemes[themeIndex % seriesThemes.length]); + config.seriesLabelStyle = Ext.apply({}, themeAttrs.seriesLabel); + config.markerStyle = Ext.apply(markerObj, markerThemes[themeIndex % markerThemes.length]); + if (themeAttrs.colors) { + config.colorArrayStyle = themeAttrs.colors; + } else { + colorArrayStyle = []; + for (l = seriesThemes.length; i < l; i++) { + st = seriesThemes[i]; + if (st.fill || st.stroke) { + colorArrayStyle.push(st.fill || st.stroke); + } + } + if (colorArrayStyle.length) { + config.colorArrayStyle = colorArrayStyle; + } + } + config.seriesIdx = idx; + config.themeIdx = themeIndex; + } + + if (isInstance) { + Ext.applyIf(series, config); + } + else { + Ext.applyIf(config, series); + series = me.series.replace(Ext.createByAlias('series.' + series.type.toLowerCase(), config)); + } + } + + series.initialize(); + series.initialized = true; + return series; + }, + + + getMaxGutters: function() { + var me = this, + seriesItems = me.series.items, + i, ln, series, gutters, + lowerH = 0, upperH = 0, lowerV = 0, upperV = 0; + + for (i = 0, ln = seriesItems.length; i < ln; i++) { + gutters = seriesItems[i].getGutters(); + if (gutters) { + if (gutters.verticalAxis) { + lowerV = Math.max(lowerV, gutters.lower); + upperV = Math.max(upperV, gutters.upper); + } + else { + lowerH = Math.max(lowerH, gutters.lower); + upperH = Math.max(upperH, gutters.upper); + } + } + } + me.maxGutters = { + left: lowerH, + right: upperH, + bottom: lowerV, + top: upperV + }; + }, + + + drawAxis: function(axis) { + axis.drawAxis(); + }, + + + drawCharts: function(series) { + series.triggerafterrender = false; + series.drawSeries(); + if (!this.animate) { + series.fireEvent('afterrender', series); + } + }, + + save: function(config){ + return Ext.draw.Surface.save(this.surface, config); + }, + + destroy: function() { + var me = this, + task = me.refreshTask; + + if (task) { + task.cancel(); + me.refreshTask = null; + } + + Ext.destroy(me.surface); + me.bindStore(null); + me.callParent(arguments); + } +}); + + + +Ext.define('Ext.chart.Highlight', { + + + + + + + + + highlight: false, + + + highlightCfg : { + fill: '#fdd', + "stroke-width": 5, + stroke: '#f55' + }, + + constructor: function(config) { + + if (config.highlight && (typeof config.highlight !== 'boolean')) { + this.highlightCfg = Ext.merge({}, this.highlightCfg, config.highlight); + } + }, + + + highlightItem: function(item) { + if (!item) { + return; + } + + var me = this, + sprite = item.sprite, + opts = Ext.merge({}, me.highlightCfg, me.highlight), + surface = me.chart.surface, + animate = me.chart.animate, + p, from, to, pi; + + if (!me.highlight || !sprite || sprite._highlighted) { + return; + } + if (sprite._anim) { + sprite._anim.paused = true; + } + sprite._highlighted = true; + if (!sprite._defaults) { + sprite._defaults = Ext.apply({}, sprite.attr); + from = {}; + to = {}; + + for (p in opts) { + if (! (p in sprite._defaults)) { + sprite._defaults[p] = surface.availableAttrs[p]; + } + from[p] = sprite._defaults[p]; + to[p] = opts[p]; + if (Ext.isObject(opts[p])) { + from[p] = {}; + to[p] = {}; + Ext.apply(sprite._defaults[p], sprite.attr[p]); + Ext.apply(from[p], sprite._defaults[p]); + for (pi in sprite._defaults[p]) { + if (! (pi in opts[p])) { + to[p][pi] = from[p][pi]; + } else { + to[p][pi] = opts[p][pi]; + } + } + for (pi in opts[p]) { + if (! (pi in to[p])) { + to[p][pi] = opts[p][pi]; + } + } + } + } + sprite._from = from; + sprite._to = to; + sprite._endStyle = to; + } + if (animate) { + sprite._anim = new Ext.fx.Anim({ + target: sprite, + from: sprite._from, + to: sprite._to, + duration: 150 + }); + } else { + sprite.setAttributes(sprite._to, true); + } + }, + + + unHighlightItem: function() { + if (!this.highlight || !this.items) { + return; + } + + var me = this, + items = me.items, + len = items.length, + opts = Ext.merge({}, me.highlightCfg, me.highlight), + animate = me.chart.animate, + i = 0, + obj, p, sprite; + for (; i < len; i++) { + if (!items[i]) { + continue; + } + sprite = items[i].sprite; + if (sprite && sprite._highlighted) { + if (sprite._anim) { + sprite._anim.paused = true; + } + obj = {}; + for (p in opts) { + if (Ext.isObject(sprite._defaults[p])) { + obj[p] = Ext.apply({}, sprite._defaults[p]); + } + else { + obj[p] = sprite._defaults[p]; + } + } + if (animate) { + + sprite._endStyle = obj; + sprite._anim = new Ext.fx.Anim({ + target: sprite, + to: obj, + duration: 150 + }); + } + else { + sprite.setAttributes(obj, true); + } + delete sprite._highlighted; + + } + } + }, + + cleanHighlights: function() { + if (!this.highlight) { + return; + } + + var group = this.group, + markerGroup = this.markerGroup, + i = 0, + l; + for (l = group.getCount(); i < l; i++) { + delete group.getAt(i)._defaults; + } + if (markerGroup) { + for (l = markerGroup.getCount(); i < l; i++) { + delete markerGroup.getAt(i)._defaults; + } + } + } +}); + + + +Ext.define('Ext.chart.Label', { + + + + + + + + + + + + + + + colorStringRe: /url\s*\(\s*#([^\/)]+)\s*\)/, + + + constructor: function(config) { + var me = this; + me.label = Ext.applyIf(me.label || {}, + { + display: "none", + stackedDisplay: "none", + color: "#000", + field: "name", + minMargin: 50, + font: "11px Helvetica, sans-serif", + orientation: "horizontal", + renderer: Ext.identityFn + }); + + if (me.label.display !== 'none') { + me.labelsGroup = me.chart.surface.getGroup(me.seriesId + '-labels'); + } + }, + + + renderLabels: function() { + var me = this, + chart = me.chart, + gradients = chart.gradients, + items = me.items, + animate = chart.animate, + config = me.label, + display = config.display, + stackedDisplay = config.stackedDisplay, + format = config.renderer, + color = config.color, + field = [].concat(config.field), + group = me.labelsGroup, + groupLength = (group || 0) && group.length, + store = me.chart.getChartStore(), + len = store.getCount(), + itemLength = (items || 0) && items.length, + ratio = itemLength / len, + gradientsCount = (gradients || 0) && gradients.length, + Color = Ext.draw.Color, + hides = [], + gradient, i, count, groupIndex, index, j, k, colorStopTotal, colorStopIndex, colorStop, item, label, + storeItem, sprite, spriteColor, spriteBrightness, labelColor, colorString, + total, totalPositive, totalNegative, topText, bottomText; + + if (display == 'none' || !group) { + return; + } + + if(itemLength == 0){ + while(groupLength--) { + hides.push(groupLength); + } + } else { + for (i = 0, count = 0, groupIndex = 0; i < len; i++) { + index = 0; + for (j = 0; j < ratio; j++) { + item = items[count]; + label = group.getAt(groupIndex); + storeItem = store.getAt(i); + + while(this.__excludes && this.__excludes[index]) { + index++; + } + + if (!item && label) { + label.hide(true); + groupIndex++; + } + + if (item && field[j]) { + if (!label) { + label = me.onCreateLabel(storeItem, item, i, display); + if (!label) { + break; + } + } + + + label.setAttributes({ + fill: String(color) + }, true); + + + me.onPlaceLabel(label, storeItem, item, i, display, animate, index); + groupIndex++; + + + if (config.contrast && item.sprite) { + sprite = item.sprite; + + + if (animate && sprite._endStyle) { + colorString = sprite._endStyle.fill; + } else if (animate && sprite._to) { + colorString = sprite._to.fill; + } else { + colorString = sprite.attr.fill; + } + colorString = colorString || sprite.attr.fill; + + spriteColor = Color.fromString(colorString); + + if (colorString && !spriteColor) { + colorString = colorString.match(me.colorStringRe)[1]; + for (k = 0; k < gradientsCount; k++) { + gradient = gradients[k]; + if (gradient.id == colorString) { + + colorStop = 0; colorStopTotal = 0; + for (colorStopIndex in gradient.stops) { + colorStop++; + colorStopTotal += Color.fromString(gradient.stops[colorStopIndex].color).getGrayscale(); + } + spriteBrightness = (colorStopTotal / colorStop) / 255; + break; + } + } + } + else { + spriteBrightness = spriteColor.getGrayscale() / 255; + } + if (label.isOutside) { + spriteBrightness = 1; + } + labelColor = Color.fromString(label.attr.fill || label.attr.color).getHSL(); + labelColor[2] = spriteBrightness > 0.5 ? 0.2 : 0.8; + label.setAttributes({ + fill: String(Color.fromHSL.apply({}, labelColor)) + }, true); + } + + + if (me.stacked && stackedDisplay && (item.totalPositiveValues || item.totalNegativeValues)) { + totalPositive = (item.totalPositiveValues || 0); + totalNegative = (item.totalNegativeValues || 0); + total = totalPositive + totalNegative; + + if (stackedDisplay == 'total') { + topText = format(total); + } else if (stackedDisplay == 'balances') { + if (totalPositive == 0 && totalNegative == 0) { + topText = format(0); + } else { + topText = format(totalPositive); + bottomText = format(totalNegative); + } + } + + if (topText) { + label = group.getAt(groupIndex); + if (!label) { + label = me.onCreateLabel(storeItem, item, i, 'over'); + } + labelColor = Color.fromString(label.attr.color || label.attr.fill).getHSL(); + label.setAttributes({ + text: topText, + style: config.font, + fill: String(Color.fromHSL.apply({}, labelColor)) + }, true); + me.onPlaceLabel(label, storeItem, item, i, 'over', animate, index); + groupIndex ++; + } + + if (bottomText) { + label = group.getAt(groupIndex); + if (!label) { + label = me.onCreateLabel(storeItem, item, i, 'under'); + } + labelColor = Color.fromString(label.attr.color || label.attr.fill).getHSL(); + label.setAttributes({ + text: bottomText, + style: config.font, + fill: String(Color.fromHSL.apply({}, labelColor)) + }, true); + me.onPlaceLabel(label, storeItem, item, i, 'under', animate, index); + groupIndex ++; + } + } + } + count++; + index++; + } + } + groupLength = group.length; + + while(groupLength > groupIndex){ + hides.push(groupIndex); + groupIndex++; + } + } + me.hideLabels(hides); + }, + + hideLabels: function(hides){ + var labelsGroup = this.labelsGroup, + hlen = !!hides && hides.length; + + if (!labelsGroup) { + return; + } + + if (hlen === false) { + hlen = labelsGroup.getCount(); + while (hlen--) { + labelsGroup.getAt(hlen).hide(true); + } + } else { + while(hlen--) { + labelsGroup.getAt(hides[hlen]).hide(true); + } + } + } +}); + + + +Ext.define('Ext.chart.TipSurface', { + + + + extend: Ext.draw.Component , + + + + spriteArray: false, + renderFirst: true, + + constructor: function(config) { + this.callParent([config]); + if (config.sprites) { + this.spriteArray = [].concat(config.sprites); + delete config.sprites; + } + }, + + onRender: function() { + var me = this, + i = 0, + l = 0, + sp, + sprites; + this.callParent(arguments); + sprites = me.spriteArray; + if (me.renderFirst && sprites) { + me.renderFirst = false; + for (l = sprites.length; i < l; i++) { + sp = me.surface.add(sprites[i]); + sp.setAttributes({ + hidden: false + }, + true); + } + } + } +}); + + + +Ext.define('Ext.chart.Tip', { + + + + + + + + constructor: function(config) { + var me = this, + surface, + sprites, + tipSurface; + if (config.tips) { + me.tipTimeout = null; + me.tipConfig = Ext.apply({}, config.tips, { + renderer: Ext.emptyFn, + constrainPosition: true, + autoHide: true, + shrinkWrapDock: true + }); + me.tooltip = new Ext.tip.ToolTip(me.tipConfig); + me.chart.surface.on('mousemove', me.tooltip.onMouseMove, me.tooltip); + me.chart.surface.on('mouseleave', function() { + me.hideTip(); + }); + if (me.tipConfig.surface) { + + surface = me.tipConfig.surface; + sprites = surface.sprites; + tipSurface = new Ext.chart.TipSurface({ + id: 'tipSurfaceComponent', + sprites: sprites + }); + if (surface.width && surface.height) { + tipSurface.setSize(surface.width, surface.height); + } + me.tooltip.add(tipSurface); + me.spriteTip = tipSurface; + } + } + }, + + showTip: function(item) { + var me = this, + tooltip, + spriteTip, + tipConfig, + trackMouse, + sprite, + surface, + surfaceExt, + pos, + x, + y; + if (!me.tooltip) { + return; + } + clearTimeout(me.tipTimeout); + tooltip = me.tooltip; + spriteTip = me.spriteTip; + tipConfig = me.tipConfig; + trackMouse = tooltip.trackMouse; + if (!trackMouse) { + tooltip.trackMouse = true; + sprite = item.sprite; + surface = sprite.surface; + surfaceExt = Ext.get(surface.getId()); + if (surfaceExt) { + pos = surfaceExt.getXY(); + x = pos[0] + (sprite.attr.x || 0) + (sprite.attr.translation && sprite.attr.translation.x || 0); + y = pos[1] + (sprite.attr.y || 0) + (sprite.attr.translation && sprite.attr.translation.y || 0); + tooltip.targetXY = [x, y]; + } + } + if (spriteTip) { + tipConfig.renderer.call(tooltip, item.storeItem, item, spriteTip.surface); + } else { + tipConfig.renderer.call(tooltip, item.storeItem, item); + } + tooltip.delayShow(trackMouse); + tooltip.trackMouse = trackMouse; + }, + + hideTip: function(item) { + var tooltip = this.tooltip; + if (!tooltip) { + return; + } + clearTimeout(this.tipTimeout); + this.tipTimeout = setTimeout(function() { + tooltip.delayHide(); + }, 0); + } +}); + + + +Ext.define('Ext.chart.axis.Abstract', { + + + + + + + + + + + + + constructor: function(config) { + config = config || {}; + + var me = this, + pos = config.position || 'left'; + + pos = pos.charAt(0).toUpperCase() + pos.substring(1); + + config.label = Ext.apply(config['axisLabel' + pos + 'Style'] || {}, config.label || {}); + config.axisTitleStyle = Ext.apply(config['axisTitle' + pos + 'Style'] || {}, config.labelTitle || {}); + Ext.apply(me, config); + me.fields = Ext.Array.from(me.fields); + this.callParent(); + me.labels = []; + me.getId(); + me.labelGroup = me.chart.surface.getGroup(me.axisId + "-labels"); + }, + + alignment: null, + grid: false, + steps: 10, + x: 0, + y: 0, + minValue: 0, + maxValue: 0, + + getId: function() { + return this.axisId || (this.axisId = Ext.id(null, 'ext-axis-')); + }, + + + processView: Ext.emptyFn, + + drawAxis: Ext.emptyFn, + addDisplayAndLabels: Ext.emptyFn +}); + + + +Ext.define('Ext.chart.axis.Axis', { + + + + extend: Ext.chart.axis.Abstract , + + alternateClassName: 'Ext.chart.Axis', + + + + + + + + + + + + + + + hidden: false, + + + forceMinMax: false, + + + dashSize: 3, + + + position: 'bottom', + + + skipFirst: false, + + + length: 0, + + + width: 0, + + + adjustEnd: true, + + majorTickSteps: false, + + nullGutters: { lower: 0, upper: 0, verticalAxis: undefined }, + + + applyData: Ext.emptyFn, + + getRange: function () { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + data = store.data.items, + series = chart.series.items, + position = me.position, + axes, + seriesClasses = Ext.chart.series, + aggregations = [], + min = Infinity, max = -Infinity, + vertical = me.position === 'left' || me.position === 'right' || me.position === 'radial', + i, ln, ln2, j, k, dataLength = data.length, aggregates, + countedFields = {}, + allFields = {}, + excludable = true, + fields, fieldMap, record, field, value; + + fields = me.fields; + for (j = 0, ln = fields.length; j < ln; j++) { + allFields[fields[j]] = true; + } + + for (i = 0, ln = series.length; i < ln; i++) { + if (series[i].seriesIsHidden) { + continue; + } + if (!series[i].getAxesForXAndYFields) { + continue; + } + axes = series[i].getAxesForXAndYFields(); + if (axes.xAxis && axes.xAxis !== position && axes.yAxis && axes.yAxis !== position) { + + continue; + } + + if (seriesClasses.Bar && series[i] instanceof seriesClasses.Bar && !series[i].column) { + + fields = vertical ? Ext.Array.from(series[i].xField) : Ext.Array.from(series[i].yField); + } else { + fields = vertical ? Ext.Array.from(series[i].yField) : Ext.Array.from(series[i].xField); + } + + if (me.fields.length) { + for (j = 0, ln2 = fields.length; j < ln2; j++) { + if (allFields[fields[j]]) { + break; + } + } + if (j == ln2) { + + continue; + } + } + + if (aggregates = series[i].stacked) { + + if (seriesClasses.Bar && series[i] instanceof seriesClasses.Bar) { + if (series[i].column != vertical) { + aggregates = false; + excludable = false; + } + } + + else if (!vertical) { + aggregates = false; + excludable = false; + } + } + + + if (aggregates) { + fieldMap = {}; + for (j = 0; j < fields.length; j++) { + if (excludable && series[i].__excludes && series[i].__excludes[j]) { + continue; + } + if (!allFields[fields[j]]) { + Ext.Logger.warn('Field `' + fields[j] + '` is not included in the ' + position + ' axis config.'); + } + allFields[fields[j]] = fieldMap[fields[j]] = true; + } + aggregations.push({ + fields: fieldMap, + positiveValue: 0, + negativeValue: 0 + }); + } else { + + if (!fields || fields.length == 0) { + fields = me.fields; + } + for (j = 0; j < fields.length; j++) { + if (excludable && series[i].__excludes && series[i].__excludes[j]) { + continue; + } + allFields[fields[j]] = countedFields[fields[j]] = true; + } + } + } + + for (i = 0; i < dataLength; i++) { + record = data[i]; + for (k = 0; k < aggregations.length; k++) { + aggregations[k].positiveValue = 0; + aggregations[k].negativeValue = 0; + } + for (field in allFields) { + value = record.get(field); + if (me.type == 'Time' && typeof value == "string") { + value = Date.parse(value); + } + if (isNaN(value)) { + continue; + } + if (value === undefined) { + value = 0; + } else { + value = Number(value); + } + if (countedFields[field]) { + if (min > value) { + min = value; + } + if (max < value) { + max = value; + } + } + for (k = 0; k < aggregations.length; k++) { + if (aggregations[k].fields[field]) { + + if (value >= 0) { + aggregations[k].positiveValue += value; + if (max < aggregations[k].positiveValue) { + max = aggregations[k].positiveValue; + } + + if (min > 0) { + min = 0; + } + } else { + aggregations[k].negativeValue += value; + if (min > aggregations[k].negativeValue) { + min = aggregations[k].negativeValue; + } + + if (max < 0) { + max = 0; + } + } + } + } + } + } + + if (!isFinite(max)) { + max = me.prevMax || 0; + } + if (!isFinite(min)) { + min = me.prevMin || 0; + } + + if (typeof min === 'number') { + min = Ext.Number.correctFloat(min); + } + + if (typeof max === 'number') { + max = Ext.Number.correctFloat(max); + } + + + if (min != max && (max != Math.floor(max) || min != Math.floor(min))) { + min = Math.floor(min); + max = Math.floor(max) + 1; + } + + if (!isNaN(me.minimum)) { + min = me.minimum; + } + + if (!isNaN(me.maximum)) { + max = me.maximum; + } + + if (min >= max) { + + min = Math.floor(min); + max = min + 1; + } + + return {min: min, max: max}; + }, + + + calcEnds: function () { + var me = this, + range = me.getRange(), + min = range.min, + max = range.max, + steps, prettyNumbers, out, changedRange; + + steps = (Ext.isNumber(me.majorTickSteps) ? me.majorTickSteps + 1 : me.steps); + prettyNumbers = !(Ext.isNumber(me.maximum) && Ext.isNumber(me.minimum) && Ext.isNumber(me.majorTickSteps) && me.majorTickSteps > 0); + + out = Ext.draw.Draw.snapEnds(min, max, steps, prettyNumbers); + + if (Ext.isNumber(me.maximum)) { + out.to = me.maximum; + changedRange = true; + } + if (Ext.isNumber(me.minimum)) { + out.from = me.minimum; + changedRange = true; + } + if (me.adjustMaximumByMajorUnit) { + out.to = Math.ceil(out.to / out.step) * out.step; + changedRange = true; + } + if (me.adjustMinimumByMajorUnit) { + out.from = Math.floor(out.from / out.step) * out.step; + changedRange = true; + } + + if (changedRange) { + out.steps = Math.ceil((out.to - out.from) / out.step); + } + + me.prevMin = (min == max ? 0 : min); + me.prevMax = max; + return out; + }, + + + + drawAxis: function (init) { + var me = this, + i, + x = me.x, + y = me.y, + dashSize = me.dashSize, + length = me.length, + position = me.position, + verticalAxis = (position == 'left' || position == 'right'), + inflections = [], + calcLabels = (me.isNumericAxis), + stepCalcs = me.applyData(), + step = stepCalcs.step, + steps = stepCalcs.steps, + stepsArray = Ext.isArray(steps), + from = stepCalcs.from, + to = stepCalcs.to, + + axisRange = (to - from) || 1, + trueLength, + currentX, + currentY, + path, + subDashesX = me.minorTickSteps || 0, + subDashesY = me.minorTickSteps || 0, + dashesX = Math.max(subDashesX + 1, 0), + dashesY = Math.max(subDashesY + 1, 0), + dashDirection = (position == 'left' || position == 'top' ? -1 : 1), + dashLength = dashSize * dashDirection, + series = me.chart.series.items, + firstSeries = series[0], + gutters = Ext.clone(firstSeries ? firstSeries.nullGutters : me.nullGutters), + seriesGutters, + hasGutters, + sameDirectionGutters, + padding, + subDashes, + subDashValue, + delta = 0, + stepCount = 0, + tick, axes, ln, val, begin, end; + + me.from = from; + me.to = to; + + + if (me.hidden || (from > to)) { + return; + } + + + if ((stepsArray && (steps.length == 0)) || (!stepsArray && isNaN(step))) { + return; + } + + if (stepsArray) { + + + steps = Ext.Array.filter(steps, function(elem, index, array) { + return (+elem > +me.from && +elem < +me.to); + }, this); + + + steps = Ext.Array.union([me.from], steps, [me.to]); + } + else { + + steps = new Array; + for (val = +me.from; val < +me.to; val += step) { + steps.push(val); + } + steps.push(+me.to); + } + stepCount = steps.length; + + + + for (i = 0, ln = series.length; i < ln; i++) { + if (series[i].seriesIsHidden) { + continue; + } + if (!series[i].getAxesForXAndYFields) { + continue; + } + axes = series[i].getAxesForXAndYFields(); + if (!axes.xAxis || !axes.yAxis || (axes.xAxis === position) || (axes.yAxis === position)) { + seriesGutters = Ext.clone(series[i].getGutters()); + hasGutters = (seriesGutters.verticalAxis !== undefined); + sameDirectionGutters = (hasGutters && (seriesGutters.verticalAxis == verticalAxis)); + if (hasGutters) { + if (!sameDirectionGutters) { + + + + + padding = series[i].getPadding(); + if (verticalAxis) { + seriesGutters = { lower: padding.bottom, upper: padding.top, verticalAxis: true }; + } else { + seriesGutters = { lower: padding.left, upper: padding.right, verticalAxis: false }; + } + } + if (gutters.lower < seriesGutters.lower) { + gutters.lower = seriesGutters.lower; + } + if (gutters.upper < seriesGutters.upper) { + gutters.upper = seriesGutters.upper; + } + gutters.verticalAxis = verticalAxis; + } + } + } + + + + if (calcLabels) { + me.labels = []; + } + + if (gutters) { + if (verticalAxis) { + currentX = Math.floor(x); + path = ["M", currentX + 0.5, y, "l", 0, -length]; + trueLength = length - (gutters.lower + gutters.upper); + + for (tick = 0; tick < stepCount; tick++) { + currentY = y - gutters.lower - (steps[tick] - steps[0]) * trueLength / axisRange; + path.push("M", currentX, Math.floor(currentY) + 0.5, "l", dashLength * 2, 0); + + inflections.push([ currentX, Math.floor(currentY) ]); + + if (calcLabels) { + me.labels.push(steps[tick]); + } + } + } else { + currentY = Math.floor(y); + path = ["M", x, currentY + 0.5, "l", length, 0]; + trueLength = length - (gutters.lower + gutters.upper); + + for (tick = 0; tick < stepCount; tick++) { + currentX = x + gutters.lower + (steps[tick] - steps[0]) * trueLength / axisRange; + path.push("M", Math.floor(currentX) + 0.5, currentY, "l", 0, dashLength * 2 + 1); + + inflections.push([ Math.floor(currentX), currentY ]); + + if (calcLabels) { + me.labels.push(steps[tick]); + } + } + } + } + + + + + + + + + subDashes = (verticalAxis ? subDashesY : subDashesX); + if (Ext.isArray(subDashes)) { + if (subDashes.length == 2) { + subDashValue = +Ext.Date.add(new Date(), subDashes[0], subDashes[1]) - Date.now(); + } else { + subDashValue = subDashes[0]; + } + } + else { + if (Ext.isNumber(subDashes) && subDashes > 0) { + subDashValue = step / (subDashes + 1); + } + } + + if (gutters && subDashValue) { + for (tick = 0; tick < stepCount - 1; tick++) { + begin = +steps[tick]; + end = +steps[tick+1]; + if (verticalAxis) { + for (value = begin + subDashValue; value < end; value += subDashValue) { + currentY = y - gutters.lower - (value - steps[0]) * trueLength / axisRange; + path.push("M", currentX, Math.floor(currentY) + 0.5, "l", dashLength, 0); + } + } + else { + for (value = begin + subDashValue; value < end; value += subDashValue) { + currentX = x + gutters.upper + (value - steps[0]) * trueLength / axisRange; + path.push("M", Math.floor(currentX) + 0.5, currentY, "l", 0, dashLength + 1); + } + } + } + } + + + + + if (!me.axis) { + me.axis = me.chart.surface.add(Ext.apply({ + type: 'path', + path: path + }, me.axisStyle)); + } + me.axis.setAttributes({ + path: path + }, true); + me.inflections = inflections; + if (!init && me.grid) { + me.drawGrid(); + } + me.axisBBox = me.axis.getBBox(); + me.drawLabel(); + }, + + + drawGrid: function () { + var me = this, + surface = me.chart.surface, + grid = me.grid, + odd = grid.odd, + even = grid.even, + inflections = me.inflections, + ln = inflections.length - ((odd || even) ? 0 : 1), + position = me.position, + maxGutters = me.chart.maxGutters, + width = me.width - 2, + point, prevPoint, + i = 1, + path = [], styles, lineWidth, dlineWidth, + oddPath = [], evenPath = []; + + if (((maxGutters.bottom !== 0 || maxGutters.top !== 0) && (position == 'left' || position == 'right')) || + ((maxGutters.left !== 0 || maxGutters.right !== 0) && (position == 'top' || position == 'bottom'))) { + i = 0; + ln++; + } + for (; i < ln; i++) { + point = inflections[i]; + prevPoint = inflections[i - 1]; + if (odd || even) { + path = (i % 2) ? oddPath : evenPath; + styles = ((i % 2) ? odd : even) || {}; + lineWidth = (styles.lineWidth || styles['stroke-width'] || 0) / 2; + dlineWidth = 2 * lineWidth; + if (position == 'left') { + path.push("M", prevPoint[0] + 1 + lineWidth, prevPoint[1] + 0.5 - lineWidth, + "L", prevPoint[0] + 1 + width - lineWidth, prevPoint[1] + 0.5 - lineWidth, + "L", point[0] + 1 + width - lineWidth, point[1] + 0.5 + lineWidth, + "L", point[0] + 1 + lineWidth, point[1] + 0.5 + lineWidth, "Z"); + } + else if (position == 'right') { + path.push("M", prevPoint[0] - lineWidth, prevPoint[1] + 0.5 - lineWidth, + "L", prevPoint[0] - width + lineWidth, prevPoint[1] + 0.5 - lineWidth, + "L", point[0] - width + lineWidth, point[1] + 0.5 + lineWidth, + "L", point[0] - lineWidth, point[1] + 0.5 + lineWidth, "Z"); + } + else if (position == 'top') { + path.push("M", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + lineWidth, + "L", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + width - lineWidth, + "L", point[0] + 0.5 - lineWidth, point[1] + 1 + width - lineWidth, + "L", point[0] + 0.5 - lineWidth, point[1] + 1 + lineWidth, "Z"); + } + else { + path.push("M", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - lineWidth, + "L", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - width + lineWidth, + "L", point[0] + 0.5 - lineWidth, point[1] - width + lineWidth, + "L", point[0] + 0.5 - lineWidth, point[1] - lineWidth, "Z"); + } + } else { + if (position == 'left') { + path = path.concat(["M", point[0] + 0.5, point[1] + 0.5, "l", width, 0]); + } + else if (position == 'right') { + path = path.concat(["M", point[0] - 0.5, point[1] + 0.5, "l", -width, 0]); + } + else if (position == 'top') { + path = path.concat(["M", point[0] + 0.5, point[1] + 0.5, "l", 0, width]); + } + else { + path = path.concat(["M", point[0] + 0.5, point[1] - 0.5, "l", 0, -width]); + } + } + } + if (odd || even) { + if (oddPath.length) { + if (!me.gridOdd && oddPath.length) { + me.gridOdd = surface.add({ + type: 'path', + path: oddPath + }); + } + me.gridOdd.setAttributes(Ext.apply({ + path: oddPath, + hidden: false + }, odd || {}), true); + } + if (evenPath.length) { + if (!me.gridEven) { + me.gridEven = surface.add({ + type: 'path', + path: evenPath + }); + } + me.gridEven.setAttributes(Ext.apply({ + path: evenPath, + hidden: false + }, even || {}), true); + } + } + else { + if (path.length) { + if (!me.gridLines) { + me.gridLines = me.chart.surface.add({ + type: 'path', + path: path, + "stroke-width": me.lineWidth || 1, + stroke: me.gridColor || '#ccc' + }); + } + me.gridLines.setAttributes({ + hidden: false, + path: path + }, true); + } + else if (me.gridLines) { + me.gridLines.hide(true); + } + } + }, + + + getOrCreateLabel: function (i, text) { + var me = this, + labelGroup = me.labelGroup, + textLabel = labelGroup.getAt(i), + surface = me.chart.surface; + if (textLabel) { + if (text != textLabel.attr.text) { + textLabel.setAttributes(Ext.apply({ + text: text + }, me.label), true); + textLabel._bbox = textLabel.getBBox(); + } + } + else { + textLabel = surface.add(Ext.apply({ + group: labelGroup, + type: 'text', + x: 0, + y: 0, + text: text + }, me.label)); + surface.renderItem(textLabel); + textLabel._bbox = textLabel.getBBox(); + } + + if (me.label.rotation) { + textLabel.setAttributes({ + rotation: { + degrees: 0 + } + }, true); + textLabel._ubbox = textLabel.getBBox(); + textLabel.setAttributes(me.label, true); + } else { + textLabel._ubbox = textLabel._bbox; + } + return textLabel; + }, + + rect2pointArray: function (sprite) { + var surface = this.chart.surface, + rect = surface.getBBox(sprite, true), + p1 = [rect.x, rect.y], + p1p = p1.slice(), + p2 = [rect.x + rect.width, rect.y], + p2p = p2.slice(), + p3 = [rect.x + rect.width, rect.y + rect.height], + p3p = p3.slice(), + p4 = [rect.x, rect.y + rect.height], + p4p = p4.slice(), + matrix = sprite.matrix; + + p1[0] = matrix.x.apply(matrix, p1p); + p1[1] = matrix.y.apply(matrix, p1p); + + p2[0] = matrix.x.apply(matrix, p2p); + p2[1] = matrix.y.apply(matrix, p2p); + + p3[0] = matrix.x.apply(matrix, p3p); + p3[1] = matrix.y.apply(matrix, p3p); + + p4[0] = matrix.x.apply(matrix, p4p); + p4[1] = matrix.y.apply(matrix, p4p); + return [p1, p2, p3, p4]; + }, + + intersect: function (l1, l2) { + var r1 = this.rect2pointArray(l1), + r2 = this.rect2pointArray(l2); + return !!Ext.draw.Draw.intersect(r1, r2).length; + }, + + drawHorizontalLabels: function () { + var me = this, + labelConf = me.label, + floor = Math.floor, + max = Math.max, + axes = me.chart.axes, + insetPadding = me.chart.insetPadding, + gutters = me.chart.maxGutters, + position = me.position, + inflections = me.inflections, + ln = inflections.length, + labels = me.labels, + maxHeight = 0, + ratio, + bbox, point, prevLabel, prevLabelId, + adjustEnd = me.adjustEnd, + hasLeft = axes.findIndex('position', 'left') != -1, + hasRight = axes.findIndex('position', 'right') != -1, + reverse = me.reverse, + textLabel, text, idx, + last, x, y, i, firstLabel; + + last = ln - 1; + + point = inflections[0]; + firstLabel = me.getOrCreateLabel(0, me.label.renderer(labels[0])); + ratio = Math.floor(Math.abs(Math.sin(labelConf.rotate && (labelConf.rotate.degrees * Math.PI / 180) || 0))); + + for (i = 0; i < ln; i++) { + point = inflections[i]; + idx = i; + if (reverse) { + idx = ln - i - 1; + } + text = me.label.renderer(labels[idx]); + textLabel = me.getOrCreateLabel(i, text); + bbox = textLabel._bbox; + maxHeight = max(maxHeight, bbox.height + me.dashSize + me.label.padding); + x = floor(point[0] - (ratio ? bbox.height : bbox.width) / 2); + if (adjustEnd && gutters.left == 0 && gutters.right == 0) { + if (i == 0 && !hasLeft) { + x = point[0]; + } + else if (i == last && !hasRight) { + x = Math.min(x, point[0] - bbox.width + insetPadding); + } + } + if (position == 'top') { + y = point[1] - (me.dashSize * 2) - me.label.padding - (bbox.height / 2); + } + else { + y = point[1] + (me.dashSize * 2) + me.label.padding + (bbox.height / 2); + } + + textLabel.setAttributes({ + hidden: false, + x: x, + y: y + }, true); + + + if (i != 0 && (me.intersect(textLabel, prevLabel) + || me.intersect(textLabel, firstLabel))) { + if (i === last && prevLabelId !== 0) { + prevLabel.hide(true); + } else { + textLabel.hide(true); + continue; + } + } + + prevLabel = textLabel; + prevLabelId = i; + } + + return maxHeight; + }, + + drawVerticalLabels: function () { + var me = this, + inflections = me.inflections, + position = me.position, + ln = inflections.length, + chart = me.chart, + insetPadding = chart.insetPadding, + labels = me.labels, + maxWidth = 0, + max = Math.max, + floor = Math.floor, + ceil = Math.ceil, + axes = me.chart.axes, + gutters = me.chart.maxGutters, + bbox, point, prevLabel, prevLabelId, + hasTop = axes.findIndex('position', 'top') != -1, + hasBottom = axes.findIndex('position', 'bottom') != -1, + adjustEnd = me.adjustEnd, + textLabel, text, + last = ln - 1, x, y, i; + + for (i = 0; i < ln; i++) { + point = inflections[i]; + text = me.label.renderer(labels[i]); + textLabel = me.getOrCreateLabel(i, text); + bbox = textLabel._bbox; + + maxWidth = max(maxWidth, bbox.width + me.dashSize + me.label.padding); + y = point[1]; + if (adjustEnd && (gutters.bottom + gutters.top) < bbox.height / 2) { + if (i == last && !hasTop) { + y = Math.max(y, me.y - me.length + ceil(bbox.height / 2) - insetPadding); + } + else if (i == 0 && !hasBottom) { + y = me.y + gutters.bottom - floor(bbox.height / 2); + } + } + if (position == 'left') { + x = point[0] - bbox.width - me.dashSize - me.label.padding - 2; + } + else { + x = point[0] + me.dashSize + me.label.padding + 2; + } + textLabel.setAttributes(Ext.apply({ + hidden: false, + x: x, + y: y + }, me.label), true); + + if (i != 0 && me.intersect(textLabel, prevLabel)) { + if (i === last && prevLabelId !== 0) { + prevLabel.hide(true); + } else { + textLabel.hide(true); + continue; + } + } + prevLabel = textLabel; + prevLabelId = i; + } + + return maxWidth; + }, + + + drawLabel: function () { + var me = this, + position = me.position, + labelGroup = me.labelGroup, + inflections = me.inflections, + maxWidth = 0, + maxHeight = 0, + ln, i; + + if (position == 'left' || position == 'right') { + maxWidth = me.drawVerticalLabels(); + } else { + maxHeight = me.drawHorizontalLabels(); + } + + + ln = labelGroup.getCount(); + i = inflections.length; + for (; i < ln; i++) { + labelGroup.getAt(i).hide(true); + } + + me.bbox = {}; + Ext.apply(me.bbox, me.axisBBox); + me.bbox.height = maxHeight; + me.bbox.width = maxWidth; + if (Ext.isString(me.title)) { + me.drawTitle(maxWidth, maxHeight); + } + }, + + + setTitle: function (title) { + this.title = title; + this.drawLabel(); + }, + + + drawTitle: function (maxWidth, maxHeight) { + var me = this, + position = me.position, + titleAlign = me.titleAlign, + surface = me.chart.surface, + displaySprite = me.displaySprite, + title = me.title, + rotate = (position == 'left' || position == 'right'), + x = me.x, + y = me.y, + base, bbox, pad; + + if (displaySprite) { + displaySprite.setAttributes({text: title}, true); + } else { + base = { + type: 'text', + x: 0, + y: 0, + text: title + }; + displaySprite = me.displaySprite = surface.add(Ext.apply(base, me.axisTitleStyle, me.labelTitle)); + surface.renderItem(displaySprite); + } + bbox = displaySprite.getBBox(); + pad = me.dashSize + me.label.padding; + + if (rotate) { + if (titleAlign === 'end') { + y -= me.length - bbox.height; + } + else if (!titleAlign || titleAlign === 'center') { + y -= ((me.length / 2) - (bbox.height / 2)); + } + + if (position == 'left') { + x -= (maxWidth + pad + (bbox.width / 2)); + } + else { + x += (maxWidth + pad + bbox.width - (bbox.width / 2)); + } + me.bbox.width += bbox.width + 10; + } + else { + if (titleAlign === 'end' || (me.reverse && titleAlign === 'start')) { + x += me.length - bbox.width; + } + else if (!titleAlign || titleAlign === 'center') { + x += (me.length / 2) - (bbox.width * 0.5); + } + + if (position == 'top') { + y -= (maxHeight + pad + (bbox.height * 0.3)); + } + else { + y += (maxHeight + pad + (bbox.height * 0.8)); + } + me.bbox.height += bbox.height + 10; + } + displaySprite.setAttributes({ + translate: { + x: x, + y: y + } + }, true); + } +}); + + + +Ext.define('Ext.chart.axis.Category', { + + + + extend: Ext.chart.axis.Axis , + + alternateClassName: 'Ext.chart.CategoryAxis', + + alias: 'axis.category', + + + isCategoryAxis: true, + + + + + doConstrain: function() { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + items = store.data.items, + series = chart.series.items, + seriesLength = series.length, + data = [], i + + for (i = 0; i < seriesLength; i++) { + if (series[i].type === 'bar' && series[i].stacked) { + + return; + } + } + + for (i = me.minimum; i < me.maximum; i++) { + data.push(items[i]); + } + + chart.setSubStore(new Ext.data.Store({ + model: store.model, + data: data + })); + }, + + + setLabels: function() { + var store = this.chart.getChartStore(), + data = store.data.items, + d, dLen, record, + fields = this.fields, + ln = fields.length, + labels, + name, + i; + + labels = this.labels = []; + for (d = 0, dLen = data.length; d < dLen; d++) { + record = data[d]; + for (i = 0; i < ln; i++) { + name = record.get(fields[i]); + if (Ext.Array.indexOf(labels, name) > -1) { + Ext.log.warn('Duplicate category in axis, ' + name); + } + labels.push(name); + } + } + }, + + + applyData: function() { + this.callParent(); + this.setLabels(); + var count = this.chart.getChartStore().getCount(); + return { + from: 0, + to: count - 1, + power: 1, + step: 1, + steps: count - 1 + }; + } +}); + + + +Ext.define('Ext.chart.axis.Gauge', { + + + + extend: Ext.chart.axis.Abstract , + + + + + + + + + + + + + + position: 'gauge', + + alias: 'axis.gauge', + + drawAxis: function(init) { + var chart = this.chart, + surface = chart.surface, + bbox = chart.chartBBox, + centerX = bbox.x + (bbox.width / 2), + centerY = bbox.y + bbox.height, + margin = this.margin || 10, + rho = Math.min(bbox.width, 2 * bbox.height) /2 + margin, + sprites = [], sprite, + steps = this.steps, + i, pi = Math.PI, + cos = Math.cos, + sin = Math.sin; + + if (this.sprites && !chart.resizing) { + this.drawLabel(); + return; + } + + if (this.margin >= 0) { + if (!this.sprites) { + + for (i = 0; i <= steps; i++) { + sprite = surface.add({ + type: 'path', + path: ['M', centerX + (rho - margin) * cos(i / steps * pi - pi), + centerY + (rho - margin) * sin(i / steps * pi - pi), + 'L', centerX + rho * cos(i / steps * pi - pi), + centerY + rho * sin(i / steps * pi - pi), 'Z'], + stroke: '#ccc' + }); + sprite.setAttributes({ + hidden: false + }, true); + sprites.push(sprite); + } + } else { + sprites = this.sprites; + + for (i = 0; i <= steps; i++) { + sprites[i].setAttributes({ + path: ['M', centerX + (rho - margin) * cos(i / steps * pi - pi), + centerY + (rho - margin) * sin(i / steps * pi - pi), + 'L', centerX + rho * cos(i / steps * pi - pi), + centerY + rho * sin(i / steps * pi - pi), 'Z'], + stroke: '#ccc' + }, true); + } + } + } + this.sprites = sprites; + this.drawLabel(); + if (this.title) { + this.drawTitle(); + } + }, + + drawTitle: function() { + var me = this, + chart = me.chart, + surface = chart.surface, + bbox = chart.chartBBox, + labelSprite = me.titleSprite, + labelBBox; + + if (!labelSprite) { + me.titleSprite = labelSprite = surface.add(Ext.apply({ + type: 'text', + zIndex: 2 + }, me.axisTitleStyle, me.labelTitle)); + } + labelSprite.setAttributes(Ext.apply({ + text: me.title + }, me.label || {}), true); + labelBBox = labelSprite.getBBox(); + labelSprite.setAttributes({ + x: bbox.x + (bbox.width / 2) - (labelBBox.width / 2), + y: bbox.y + bbox.height - (labelBBox.height / 2) - 4 + }, true); + }, + + + setTitle: function(title) { + this.title = title; + this.drawTitle(); + }, + + drawLabel: function() { + var me = this, + chart = me.chart, + surface = chart.surface, + bbox = chart.chartBBox, + centerX = bbox.x + (bbox.width / 2), + centerY = bbox.y + bbox.height, + margin = me.margin || 10, + rho = Math.min(bbox.width, 2 * bbox.height) /2 + 2 * margin, + round = Math.round, + labelArray = [], label, + maxValue = me.maximum || 0, + minValue = me.minimum || 0, + steps = me.steps, + pi = Math.PI, + cos = Math.cos, + sin = Math.sin, + labelConf = this.label, + renderer = labelConf.renderer || Ext.identityFn, + reverse = me.reverse, + i, adjY, idx; + + if (!this.labelArray) { + + for (i = 0; i <= steps; i++) { + + adjY = (i === 0 || i === steps) ? 7 : 0; + idx = reverse ? steps - i : i; + label = surface.add({ + type: 'text', + text: renderer(round(minValue + idx / steps * (maxValue - minValue))), + x: centerX + rho * cos(i / steps * pi - pi), + y: centerY + rho * sin(i / steps * pi - pi) - adjY, + 'text-anchor': 'middle', + 'stroke-width': 0.2, + zIndex: 10, + stroke: '#333' + }); + label.setAttributes({ + hidden: false + }, true); + labelArray.push(label); + } + } + else { + labelArray = this.labelArray; + + for (i = 0; i <= steps; i++) { + + adjY = (i === 0 || i === steps) ? 7 : 0; + idx = reverse ? steps - i : i; + labelArray[i].setAttributes({ + text: renderer(round(minValue + idx / steps * (maxValue - minValue))), + x: centerX + rho * cos(i / steps * pi - pi), + y: centerY + rho * sin(i / steps * pi - pi) - adjY + }, true); + } + } + this.labelArray = labelArray; + } +}); + + + +Ext.define('Ext.chart.axis.Numeric', { + + + + extend: Ext.chart.axis.Axis , + + alternateClassName: 'Ext.chart.NumericAxis', + + + + type: 'Numeric', + + + isNumericAxis: true, + + alias: 'axis.numeric', + + + + constructor: function(config) { + var me = this, + hasLabel = !!(config.label && config.label.renderer), + label; + + me.callParent([config]); + label = me.label; + + if (config.constrain == null) { + me.constrain = (config.minimum != null && config.maximum != null); + } + + if (!hasLabel) { + label.renderer = function(v) { + return me.roundToDecimal(v, me.decimals); + }; + } + }, + + roundToDecimal: function(v, dec) { + var val = Math.pow(10, dec || 0); + return Math.round(v * val) / val; + }, + + + minimum: NaN, + + + maximum: NaN, + + + constrain: true, + + + decimals: 2, + + + scale: "linear", + + + doConstrain: function() { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + items = store.data.items, + d, dLen, record, + series = chart.series.items, + fields = me.fields, + ln = fields.length, + range = me.calcEnds(), + min = range.from, max = range.to, i, l, + useAcum = false, + value, data = [], + addRecord; + + for (d = 0, dLen = items.length; d < dLen; d++) { + addRecord = true; + record = items[d]; + for (i = 0; i < ln; i++) { + value = record.get(fields[i]); + if (me.type == 'Time' && typeof value == "string") { + value = Date.parse(value); + } + if (+value < +min) { + addRecord = false; + break; + } + if (+value > +max) { + addRecord = false; + break; + } + } + if (addRecord) { + data.push(record); + } + } + + chart.setSubStore(new Ext.data.Store({ + model: store.model, + data: data + })); + }, + + position: 'left', + + + adjustMaximumByMajorUnit: false, + + + adjustMinimumByMajorUnit: false, + + + processView: function() { + var me = this, + chart = me.chart, + series = chart.series.items, + i, l; + + for (i = 0, l = series.length; i < l; i++) { + if (series[i].stacked) { + + delete me.minimum; + delete me.maximum; + me.constrain = false; + break; + } + } + + if (me.constrain) { + me.doConstrain(); + } + }, + + + applyData: function() { + this.callParent(); + return this.calcEnds(); + } +}); + + + +Ext.define('Ext.chart.axis.Radial', { + + + + extend: Ext.chart.axis.Numeric , + + + + position: 'radial', + + alias: 'axis.radial', + + + + + + + + drawAxis: function(init) { + var chart = this.chart, + surface = chart.surface, + bbox = chart.chartBBox, + store = chart.getChartStore(), + l = store.getCount(), + centerX = bbox.x + (bbox.width / 2), + centerY = bbox.y + (bbox.height / 2), + rho = Math.min(bbox.width, bbox.height) /2, + sprites = [], sprite, + steps = this.steps, + i, j, pi2 = Math.PI * 2, + cos = Math.cos, sin = Math.sin; + + if (this.sprites && !chart.resizing) { + this.drawLabel(); + return; + } + + if (!this.sprites) { + + for (i = 1; i <= steps; i++) { + sprite = surface.add({ + type: 'circle', + x: centerX, + y: centerY, + radius: Math.max(rho * i / steps, 0), + stroke: '#ccc' + }); + sprite.setAttributes({ + hidden: false + }, true); + sprites.push(sprite); + } + + for (i = 0; i < l; i++) { + sprite = surface.add({ + type: 'path', + path: ['M', centerX, centerY, 'L', centerX + rho * cos(i / l * pi2), centerY + rho * sin(i / l * pi2), 'Z'], + stroke: '#ccc' + }); + sprite.setAttributes({ + hidden: false + }, true); + sprites.push(sprite); + } + } else { + sprites = this.sprites; + + for (i = 0; i < steps; i++) { + sprites[i].setAttributes({ + x: centerX, + y: centerY, + radius: Math.max(rho * (i + 1) / steps, 0), + stroke: '#ccc' + }, true); + } + + for (j = 0; j < l; j++) { + sprites[i + j].setAttributes({ + path: ['M', centerX, centerY, 'L', centerX + rho * cos(j / l * pi2), centerY + rho * sin(j / l * pi2), 'Z'], + stroke: '#ccc' + }, true); + } + } + this.sprites = sprites; + + this.drawLabel(); + }, + + drawLabel: function() { + var chart = this.chart, + seriesItems = chart.series.items, + series, + surface = chart.surface, + bbox = chart.chartBBox, + store = chart.getChartStore(), + data = store.data.items, + ln, record, + centerX = bbox.x + (bbox.width / 2), + centerY = bbox.y + (bbox.height / 2), + rho = Math.min(bbox.width, bbox.height) /2, + max = Math.max, round = Math.round, + labelArray = [], label, + fields = [], nfields, + categories = [], xField, + aggregate = !this.maximum, + maxValue = this.maximum || 0, + minValue = this.minimum || 0, + steps = this.steps, i = 0, j, dx, dy, + pi2 = Math.PI * 2, + cos = Math.cos, sin = Math.sin, + display = this.label.display, + draw = display !== 'none', + margin = 10; + + if (!draw) { + return; + } + + + for (i = 0, ln = seriesItems.length; i < ln; i++) { + series = seriesItems[i]; + fields.push(series.yField); + xField = series.xField; + } + + + for (j = 0, ln = data.length; j < ln; j++) { + record = data[j]; + categories.push(record.get(xField)); + + if (aggregate) { + for (i = 0, nfields = fields.length; i < nfields; i++) { + maxValue = max(+record.get(fields[i]), maxValue); + } + } + } + if (!this.labelArray) { + if (display != 'categories') { + + for (i = 1; i <= steps; i++) { + label = surface.add({ + type: 'text', + text: round(i / steps * maxValue), + x: centerX, + y: centerY - rho * i / steps, + 'text-anchor': 'middle', + 'stroke-width': 0.1, + stroke: '#333' + }); + label.setAttributes({ + hidden: false + }, true); + labelArray.push(label); + } + } + if (display != 'scale') { + + for (j = 0, steps = categories.length; j < steps; j++) { + dx = cos(j / steps * pi2) * (rho + margin); + dy = sin(j / steps * pi2) * (rho + margin); + label = surface.add({ + type: 'text', + text: categories[j], + x: centerX + dx, + y: centerY + dy, + 'text-anchor': dx * dx <= 0.001? 'middle' : (dx < 0? 'end' : 'start') + }); + label.setAttributes({ + hidden: false + }, true); + labelArray.push(label); + } + } + } + else { + labelArray = this.labelArray; + if (display != 'categories') { + + for (i = 0; i < steps; i++) { + labelArray[i].setAttributes({ + text: round((i + 1) / steps * (maxValue - minValue) + minValue), + x: centerX, + y: centerY - rho * (i + 1) / steps, + 'text-anchor': 'middle', + 'stroke-width': 0.1, + stroke: '#333' + }, true); + } + } + if (display != 'scale') { + + for (j = 0, steps = categories.length; j < steps; j++) { + dx = cos(j / steps * pi2) * (rho + margin); + dy = sin(j / steps * pi2) * (rho + margin); + if (labelArray[i + j]) { + labelArray[i + j].setAttributes({ + type: 'text', + text: categories[j], + x: centerX + dx, + y: centerY + dy, + 'text-anchor': dx * dx <= 0.001? 'middle' : (dx < 0? 'end' : 'start') + }, true); + } + } + } + } + this.labelArray = labelArray; + }, + + processView: function() { + var me = this, + seriesItems = me.chart.series.items, + i, ln, series, ends, fields = []; + + for (i = 0, ln = seriesItems.length; i < ln; i++) { + series = seriesItems[i]; + fields.push(series.yField); + } + me.fields = fields; + + ends = me.calcEnds(); + me.maximum = ends.to; + me.steps = ends.steps; + } +}); + + + +Ext.define('Ext.chart.axis.Time', { + + + + extend: Ext.chart.axis.Numeric , + + alternateClassName: 'Ext.chart.TimeAxis', + + type: 'Time', + + alias: 'axis.time', + + + + + + + dateFormat: false, + + + fromDate: false, + + + toDate: false, + + + step: [Ext.Date.DAY, 1], + + + constrain: false, + + constructor: function (config) { + var me = this, label, f, df; + me.callParent([config]); + label = me.label || {}; + df = this.dateFormat; + if (df) { + if (label.renderer) { + f = label.renderer; + label.renderer = function(v) { + v = f(v); + return Ext.Date.format(new Date(f(v)), df); + }; + } else { + label.renderer = function(v) { + return Ext.Date.format(new Date(v >> 0), df); + }; + } + } + }, + + + processView: function () { + var me = this; + if (me.fromDate) { + me.minimum = +me.fromDate; + } + if (me.toDate) { + me.maximum = +me.toDate; + } + if(me.constrain){ + me.doConstrain(); + } + }, + + + calcEnds: function() { + var me = this, range, step = me.step; + if (step) { + range = me.getRange(); + range = Ext.draw.Draw.snapEndsByDateAndStep(new Date(range.min), new Date(range.max), Ext.isNumber(step) ? [Date.MILLI, step]: step); + if (me.minimum) { + range.from = me.minimum; + } + if (me.maximum) { + range.to = me.maximum; + } + return range; + } else { + return me.callParent(arguments); + } + } + }); + + + + +Ext.define('Ext.chart.series.Series', { + + + + mixins: { + observable: Ext.util.Observable , + labels: Ext.chart.Label , + highlights: Ext.chart.Highlight , + tips: Ext.chart.Tip , + callouts: Ext.chart.Callout + }, + + + + + + + + + type: null, + + + title: null, + + + showInLegend: true, + + + renderer: function(sprite, record, attributes, index, store) { + return attributes; + }, + + + shadowAttributes: null, + + + animating: false, + + + nullGutters: { lower: 0, upper: 0, verticalAxis: undefined }, + + + nullPadding: { left:0, right:0, width:0, bottom:0, top:0, height:0 }, + + constructor: function(config) { + var me = this; + if (config) { + Ext.apply(me, config); + } + + me.shadowGroups = []; + + me.mixins.labels.constructor.call(me, config); + me.mixins.highlights.constructor.call(me, config); + me.mixins.tips.constructor.call(me, config); + me.mixins.callouts.constructor.call(me, config); + + me.addEvents({ + scope: me, + + + itemclick: true, + + + itemdblclick: true, + + + itemmouseover: true, + + + itemmouseout: true, + + + itemmousedown: true, + + + itemmouseup: true, + + mouseleave: true, + + + afterdraw: true, + + + titlechange: true + }); + + me.mixins.observable.constructor.call(me, config); + + me.on({ + scope: me, + itemmouseover: me.onItemMouseOver, + itemmouseout: me.onItemMouseOut, + mouseleave: me.onMouseLeave + }); + + if (me.style) { + Ext.apply(me.seriesStyle, me.style); + } + }, + + initialize: Ext.emptyFn, + + onRedraw: Ext.emptyFn, + + + eachRecord: function(fn, scope) { + var chart = this.chart; + chart.getChartStore().each(fn, scope); + }, + + + getRecordCount: function() { + var chart = this.chart, + store = chart.getChartStore(); + return store ? store.getCount() : 0; + }, + + + isExcluded: function(index) { + var excludes = this.__excludes; + return !!(excludes && excludes[index]); + }, + + + setBBox: function(noGutter) { + var me = this, + chart = me.chart, + chartBBox = chart.chartBBox, + maxGutters = noGutter ? { left: 0, right: 0, bottom: 0, top: 0 } : chart.maxGutters, + clipBox, bbox; + + clipBox = { + x: chartBBox.x, + y: chartBBox.y, + width: chartBBox.width, + height: chartBBox.height + }; + me.clipBox = clipBox; + + bbox = { + x: (clipBox.x + maxGutters.left) - (chart.zoom.x * chart.zoom.width), + y: (clipBox.y + maxGutters.bottom) - (chart.zoom.y * chart.zoom.height), + width: (clipBox.width - (maxGutters.left + maxGutters.right)) * chart.zoom.width, + height: (clipBox.height - (maxGutters.bottom + maxGutters.top)) * chart.zoom.height + }; + me.bbox = bbox; + }, + + + onAnimate: function(sprite, attr) { + var me = this; + sprite.stopAnimation(); + if (me.animating) { + return sprite.animate(Ext.applyIf(attr, me.chart.animate)); + } else { + me.animating = true; + return sprite.animate(Ext.apply(Ext.applyIf(attr, me.chart.animate), { + + callback: function() { + me.animating = false; + me.fireEvent('afterrender', me); + } + })); + } + }, + + + getGutters: function() { + return this.nullGutters; + }, + + + getPadding: function() { + return this.nullPadding; + }, + + + onItemMouseOver: function(item) { + var me = this; + if (item.series === me) { + if (me.highlight) { + me.highlightItem(item); + } + if (me.tooltip) { + me.showTip(item); + } + } + }, + + + onItemMouseOut: function(item) { + var me = this; + if (item.series === me) { + me.unHighlightItem(); + if (me.tooltip) { + me.hideTip(item); + } + } + }, + + + onMouseLeave: function() { + var me = this; + me.unHighlightItem(); + if (me.tooltip) { + me.hideTip(); + } + }, + + + getItemForPoint: function(x, y) { + + if (!this.items || !this.items.length || this.seriesIsHidden) { + return null; + } + var me = this, + items = me.items, + bbox = me.bbox, + item, i, ln; + + if (!Ext.draw.Draw.withinBox(x, y, bbox)) { + return null; + } + for (i = 0, ln = items.length; i < ln; i++) { + if (items[i] && this.isItemInPoint(x, y, items[i], i)) { + return items[i]; + } + } + + return null; + }, + + isItemInPoint: function(x, y, item, i) { + return false; + }, + + + hideAll: function() { + var me = this, + items = me.items, + item, len, i, j, l, sprite, shadows; + + me.seriesIsHidden = true; + me._prevShowMarkers = me.showMarkers; + + me.showMarkers = false; + + me.hideLabels(0); + + for (i = 0, len = items.length; i < len; i++) { + item = items[i]; + sprite = item.sprite; + if (sprite) { + sprite.setAttributes({ + hidden: true + }, true); + } + + if (sprite && sprite.shadows) { + shadows = sprite.shadows; + for (j = 0, l = shadows.length; j < l; ++j) { + shadows[j].setAttributes({ + hidden: true + }, true); + } + } + } + }, + + + showAll: function() { + var me = this, + prevAnimate = me.chart.animate; + me.chart.animate = false; + me.seriesIsHidden = false; + me.showMarkers = me._prevShowMarkers; + me.drawSeries(); + me.chart.animate = prevAnimate; + }, + + hide: function() { + if (this.items) { + var me = this, + items = me.items, + i, j, lsh, ln, shadows; + + if (items && items.length) { + for (i = 0, ln = items.length; i < ln; ++i) { + if (items[i].sprite) { + items[i].sprite.hide(true); + + shadows = items[i].shadows || items[i].sprite.shadows; + if (shadows) { + for (j = 0, lsh = shadows.length; j < lsh; ++j) { + shadows[j].hide(true); + } + } + } + } + me.hideLabels(); + } + } + }, + + + getLegendColor: function(index) { + var me = this, fill, stroke; + if (me.seriesStyle) { + fill = me.seriesStyle.fill; + stroke = me.seriesStyle.stroke; + if (fill && fill != 'none') { + return fill; + } + if(stroke){ + return stroke; + } + } + return (me.colorArrayStyle)?me.colorArrayStyle[me.themeIdx % me.colorArrayStyle.length]:'#000'; + }, + + + visibleInLegend: function(index){ + var excludes = this.__excludes; + if (excludes) { + return !excludes[index]; + } + return !this.seriesIsHidden; + }, + + + setTitle: function(index, title) { + var me = this, + oldTitle = me.title; + + if (Ext.isString(index)) { + title = index; + index = 0; + } + + if (Ext.isArray(oldTitle)) { + oldTitle[index] = title; + } else { + me.title = title; + } + + me.fireEvent('titlechange', title, index); + } +}); + + + +Ext.define('Ext.chart.series.Cartesian', { + + + + extend: Ext.chart.series.Series , + + alternateClassName: ['Ext.chart.CartesianSeries', 'Ext.chart.CartesianChart'], + + + + + xField: null, + + + yField: null, + + + axis: 'left', + + getLegendLabels: function() { + var me = this, + labels = [], + fields, i, ln, + combinations = me.combinations, + title, + combo, label0, label1; + + fields = [].concat(me.yField); + for (i = 0, ln = fields.length; i < ln; i++) { + title = me.title; + + labels.push((Ext.isArray(title) ? title[i] : title) || fields[i]); + } + + + + if (combinations) { + combinations = Ext.Array.from(combinations); + for (i = 0, ln = combinations.length; i < ln; i++) { + combo = combinations[i]; + label0 = labels[combo[0]]; + label1 = labels[combo[1]]; + labels[combo[1]] = label0 + ' & ' + label1; + labels.splice(combo[0], 1); + } + } + + return labels; + }, + + + eachYValue: function(record, fn, scope) { + var me = this, + yValueAccessors = me.getYValueAccessors(), + i, ln, accessor; + + for (i = 0, ln = yValueAccessors.length; i < ln; i++) { + accessor = yValueAccessors[i]; + fn.call(scope, accessor(record), i); + } + }, + + + getYValueCount: function() { + return this.getYValueAccessors().length; + }, + + combine: function(index1, index2) { + var me = this, + accessors = me.getYValueAccessors(), + accessor1 = accessors[index1], + accessor2 = accessors[index2]; + + + accessors[index2] = function(record) { + return accessor1(record) + accessor2(record); + }; + accessors.splice(index1, 1); + + me.callParent([index1, index2]); + }, + + clearCombinations: function() { + + delete this.yValueAccessors; + this.callParent(); + }, + + + getYValueAccessors: function() { + var me = this, + accessors = me.yValueAccessors, + yFields, i, ln; + + function getFieldAccessor(field) { + return function(record) { + return record.get(field); + } + } + + if (!accessors) { + accessors = me.yValueAccessors = []; + yFields = [].concat(me.yField); + for (i = 0, ln = yFields.length; i < ln; i++) { + accessors.push(getFieldAccessor(yFields[i])); + } + } + return accessors; + }, + + + getMinMaxXValues: function() { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + data = store.data.items, + count = me.getRecordCount(), + i, ln, record, + min, max, + xField = me.xField, + xValue; + + if (count > 0) { + min = Infinity; + max = -min; + + for (i = 0, ln = data.length; i < ln; i++) { + record = data[i]; + xValue = record.get(xField); + if (xValue > max) { + max = xValue; + } + if (xValue < min) { + min = xValue; + } + } + + + if (min == Infinity) { + min = 0; + } + + if (max == -Infinity) { + max = count - 1; + } + } else { + min = max = 0; + } + return [min, max]; + }, + + + getMinMaxYValues: function() { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + data = store.data.items, + count = me.getRecordCount(), + i, ln, record, + stacked = me.stacked, + min, max, + positiveTotal, negativeTotal; + + function eachYValueStacked(yValue, i) { + if (!me.isExcluded(i)) { + if (yValue < 0) { + negativeTotal += yValue; + } else { + positiveTotal += yValue; + } + } + } + + function eachYValue(yValue, i) { + if (!me.isExcluded(i)) { + if (yValue > max) { + max = yValue; + } + if (yValue < min) { + min = yValue; + } + } + } + + if (count > 0) { + min = Infinity; + max = -min; + + for (i = 0, ln = data.length; i < ln; i++) { + record = data[i]; + if (stacked) { + positiveTotal = 0; + negativeTotal = 0; + me.eachYValue(record, eachYValueStacked); + if (positiveTotal > max) { + max = positiveTotal; + } + if (negativeTotal < min) { + min = negativeTotal; + } + } else { + me.eachYValue(record, eachYValue); + } + } + + + if (min == Infinity) { + min = 0; + } + + if (max == -Infinity) { + max = count - 1; + } + } else { + min = max = 0; + } + return [min, max]; + }, + + getAxesForXAndYFields: function() { + var me = this, + axes = me.chart.axes, + reverse = me.reverse, + axis = [].concat(me.axis), + yFields = {}, yFieldList = [].concat(me.yField), + xFields = {}, xFieldList = [].concat(me.xField), + fields, xAxis, yAxis, i, ln, flipXY; + + + flipXY = me.type === 'bar' && me.column === false; + if(flipXY) { + fields = yFieldList; + yFieldList = xFieldList; + xFieldList = fields; + } + if (Ext.Array.indexOf(axis, 'top') > -1) { + xAxis = 'top'; + } else if (Ext.Array.indexOf(axis, 'bottom') > -1) { + xAxis = 'bottom'; + } else { + if (axes.get('top') && axes.get('bottom')) { + for (i = 0, ln = xFieldList.length; i < ln; i++) { + xFields[xFieldList[i]] = true; + } + fields = [].concat(axes.get('bottom').fields); + for (i = 0, ln = fields.length; i < ln; i++) { + if (xFields[fields[i]]) { + xAxis = 'bottom'; + break; + } + } + fields = [].concat(axes.get('top').fields); + for (i = 0, ln = fields.length; i < ln; i++) { + if (xFields[fields[i]]) { + xAxis = 'top'; + break; + } + } + } else if (axes.get('top')) { + xAxis = 'top'; + } else if (axes.get('bottom')) { + xAxis = 'bottom'; + } + } + + if (Ext.Array.indexOf(axis, 'left') > -1) { + yAxis = flipXY ? 'right' : 'left'; + } else if (Ext.Array.indexOf(axis, 'right') > -1) { + yAxis = flipXY ? 'left' : 'right'; + } else { + if (axes.get('left') && axes.get('right')) { + for (i = 0, ln = yFieldList.length; i < ln; i++) { + yFields[yFieldList[i]] = true; + } + fields = [].concat(axes.get('right').fields); + for (i = 0, ln = fields.length; i < ln; i++) { + if (yFields[fields[i]]) { + break; + } + } + fields = [].concat(axes.get('left').fields); + for (i = 0, ln = fields.length; i < ln; i++) { + if (yFields[fields[i]]) { + yAxis = 'left'; + break; + } + } + } else if (axes.get('left')) { + yAxis = 'left'; + } else if (axes.get('right')) { + yAxis = 'right'; + } + } + + return flipXY ? { + xAxis: yAxis, + yAxis: xAxis + }: { + xAxis: xAxis, + yAxis: yAxis + }; + } + + +}); + + + +Ext.define('Ext.chart.series.Area', { + + + + extend: Ext.chart.series.Cartesian , + + alias: 'series.area', + + + + + + type: 'area', + + + stacked: true, + + + style: {}, + + constructor: function(config) { + this.callParent(arguments); + var me = this, + surface = me.chart.surface, + i, l; + config.highlightCfg = Ext.Object.merge({}, { + lineWidth: 3, + stroke: '#55c', + opacity: 0.8, + color: '#f00' + }, config.highlightCfg); + + Ext.apply(me, config, { + __excludes: [] + }); + if (me.highlight) { + me.highlightSprite = surface.add({ + type: 'path', + path: ['M', 0, 0], + zIndex: 1000, + opacity: 0.3, + lineWidth: 5, + hidden: true, + stroke: '#444' + }); + } + me.group = surface.getGroup(me.seriesId); + }, + + + shrink: function(xValues, yValues, size) { + var len = xValues.length, + ratio = Math.floor(len / size), + i, j, + xSum = 0, + yCompLen = this.areas.length, + ySum = [], + xRes = [], + yRes = []; + + for (j = 0; j < yCompLen; ++j) { + ySum[j] = 0; + } + for (i = 0; i < len; ++i) { + xSum += +xValues[i]; + for (j = 0; j < yCompLen; ++j) { + ySum[j] += +yValues[i][j]; + } + if (i % ratio == 0) { + + xRes.push(xSum/ratio); + for (j = 0; j < yCompLen; ++j) { + ySum[j] /= ratio; + } + yRes.push(ySum); + + xSum = 0; + for (j = 0, ySum = []; j < yCompLen; ++j) { + ySum[j] = 0; + } + } + } + return { + x: xRes, + y: yRes + }; + }, + + + getBounds: function() { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + data = store.data.items, + i, l, record, + areas = [].concat(me.yField), + areasLen = areas.length, + xValues = [], + yValues = [], + infinity = Infinity, + minX = infinity, + minY = infinity, + maxX = -infinity, + maxY = -infinity, + math = Math, + mmin = math.min, + mmax = math.max, + boundAxis = me.getAxesForXAndYFields(), + boundXAxis = boundAxis.xAxis, + boundYAxis = boundAxis.yAxis, + ends, allowDate, tmp, + bbox, xScale, yScale, xValue, yValue, areaIndex, acumY, ln, sumValues, clipBox, areaElem, axis, out; + + me.setBBox(); + bbox = me.bbox; + + if (axis = chart.axes.get(boundXAxis)) { + if (axis.type === 'Time') { + allowDate = true; + } + ends = axis.applyData(); + minX = ends.from; + maxX = ends.to; + } + + if (axis = chart.axes.get(boundYAxis)) { + ends = axis.applyData(); + minY = ends.from; + maxY = ends.to; + } + + + if (me.xField && !Ext.isNumber(minX)) { + axis = me.getMinMaxXValues(); + allowDate = true; + minX = axis[0]; + maxX = axis[1]; + } + + if (me.yField && !Ext.isNumber(minY)) { + axis = me.getMinMaxYValues(); + minY = axis[0]; + maxY = axis[1]; + } + + if (!Ext.isNumber(minY)) { + minY = 0; + } + if (!Ext.isNumber(maxY)) { + maxY = 0; + } + + l = data.length; + if (l > 0 && allowDate) { + tmp = data[0].get(me.xField); + if (typeof tmp != 'number') { + tmp = +tmp; + if (isNaN(tmp)) { + allowDate = false; + } + } + } + for (i = 0; i < l; i++) { + record = data[i]; + xValue = record.get(me.xField); + yValue = []; + if (typeof xValue != 'number') { + if (allowDate) { + xValue = +xValue; + } else { + xValue = i; + } + } + xValues.push(xValue); + acumY = 0; + for (areaIndex = 0; areaIndex < areasLen; areaIndex++) { + + if (me.__excludes[areaIndex]) { + continue; + } + areaElem = record.get(areas[areaIndex]); + if (typeof areaElem == 'number') { + yValue.push(areaElem); + } + } + yValues.push(yValue); + } + + xScale = bbox.width / ((maxX - minX) || 1); + yScale = bbox.height / ((maxY - minY) || 1); + + ln = xValues.length; + if ((ln > bbox.width) && me.areas) { + sumValues = me.shrink(xValues, yValues, bbox.width); + xValues = sumValues.x; + yValues = sumValues.y; + } + + return { + bbox: bbox, + minX: minX, + minY: minY, + xValues: xValues, + yValues: yValues, + xScale: xScale, + yScale: yScale, + areasLen: areasLen + }; + }, + + + getPaths: function() { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + first = true, + bounds = me.getBounds(), + bbox = bounds.bbox, + items = me.items = [], + componentPaths = [], + componentPath, + count = 0, + paths = [], + reverse = me.reverse, + i, ln, x, y, xValue, yValue, acumY, areaIndex, + prevAreaIndex, areaElem, path, startX, idx; + + ln = bounds.xValues.length; + + for (i = 0; i < ln; i++) { + xValue = bounds.xValues[i]; + idx = reverse ? ln - i - 1 : i; + yValue = bounds.yValues[idx]; + x = bbox.x + (xValue - bounds.minX) * bounds.xScale; + if (startX === undefined) { + startX = x; + } + acumY = 0; + count = 0; + for (areaIndex = 0; areaIndex < bounds.areasLen; areaIndex++) { + + if (me.__excludes[areaIndex]) { + continue; + } + if (!componentPaths[areaIndex]) { + componentPaths[areaIndex] = []; + } + areaElem = yValue[count]; + acumY += areaElem; + y = bbox.y + bbox.height - (acumY - bounds.minY) * bounds.yScale; + if (!paths[areaIndex]) { + paths[areaIndex] = ['M', x, y]; + componentPaths[areaIndex].push(['L', x, y]); + } else { + paths[areaIndex].push('L', x, y); + componentPaths[areaIndex].push(['L', x, y]); + } + if (!items[areaIndex]) { + items[areaIndex] = { + pointsUp: [], + pointsDown: [], + series: me + }; + } + items[areaIndex].pointsUp.push([x, y]); + count++; + } + } + + + for (areaIndex = 0; areaIndex < bounds.areasLen; areaIndex++) { + + if (me.__excludes[areaIndex]) { + continue; + } + path = paths[areaIndex]; + + + if (areaIndex == 0 || first) { + first = false; + + path.push('L', x, bbox.y + bbox.height, + 'L', startX, bbox.y + bbox.height, + 'Z'); + } + + else { + componentPath = componentPaths[prevAreaIndex]; + componentPath.reverse(); + path.push('L', x, componentPath[0][2]); + for (i = 0; i < ln; i++) { + path.push(componentPath[i][0], + componentPath[i][1], + componentPath[i][2]); + items[areaIndex].pointsDown[ln -i -1] = [componentPath[i][1], componentPath[i][2]]; + } + path.push('L', startX, path[2], 'Z'); + } + prevAreaIndex = areaIndex; + } + return { + paths: paths, + areasLen: bounds.areasLen + }; + }, + + + drawSeries: function() { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + surface = chart.surface, + animate = chart.animate, + group = me.group, + endLineStyle = Ext.apply(me.seriesStyle, me.style), + colorArrayStyle = me.colorArrayStyle, + colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0, + themeIndex = me.themeIdx, + areaIndex, areaElem, paths, path, rendererAttributes, idx; + + me.unHighlightItem(); + me.cleanHighlights(); + + if (!store || !store.getCount() || me.seriesIsHidden) { + me.hide(); + me.items = []; + return; + } + + paths = me.getPaths(); + + if (!me.areas) { + me.areas = []; + } + + for (areaIndex = 0; areaIndex < paths.areasLen; areaIndex++) { + + if (me.__excludes[areaIndex]) { + continue; + } + idx = themeIndex + areaIndex; + if (!me.areas[areaIndex]) { + me.items[areaIndex].sprite = me.areas[areaIndex] = surface.add(Ext.apply({}, { + type: 'path', + group: group, + + path: paths.paths[areaIndex], + stroke: endLineStyle.stroke || colorArrayStyle[idx % colorArrayLength], + fill: colorArrayStyle[idx % colorArrayLength] + }, endLineStyle || {})); + } + areaElem = me.areas[areaIndex]; + path = paths.paths[areaIndex]; + if (animate) { + + rendererAttributes = me.renderer(areaElem, false, { + path: path, + + fill: colorArrayStyle[areaIndex % colorArrayLength], + stroke: endLineStyle.stroke || colorArrayStyle[areaIndex % colorArrayLength] + }, areaIndex, store); + + me.animation = me.onAnimate(areaElem, { + to: rendererAttributes + }); + } else { + rendererAttributes = me.renderer(areaElem, false, { + path: path, + + hidden: false, + fill: colorArrayStyle[idx % colorArrayLength], + stroke: endLineStyle.stroke || colorArrayStyle[idx % colorArrayLength] + }, areaIndex, store); + me.areas[areaIndex].setAttributes(rendererAttributes, true); + } + } + me.renderLabels(); + me.renderCallouts(); + }, + + + onAnimate: function(sprite, attr) { + sprite.show(); + return this.callParent(arguments); + }, + + + onCreateLabel: function(storeItem, item, i, display) { + + + + + + + + + + return null; + + var me = this, + group = me.labelsGroup, + config = me.label, + bbox = me.bbox, + endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {}); + + return me.chart.surface.add(Ext.apply({ + 'type': 'text', + 'text-anchor': 'middle', + 'group': group, + 'x': Number(item.point[0]), + 'y': bbox.y + bbox.height / 2 + }, endLabelStyle || {})); + }, + + + onPlaceLabel: function(label, storeItem, item, i, display, animate, index) { + var me = this, + chart = me.chart, + resizing = chart.resizing, + config = me.label, + format = config.renderer, + field = config.field, + bbox = me.bbox, + x = Number(item.point[i][0]), + y = Number(item.point[i][1]), + labelBox, width, height; + + label.setAttributes({ + text: format(storeItem.get(field[index]), label, storeItem, item, i, display, animate, index), + hidden: true + }, true); + + labelBox = label.getBBox(); + width = labelBox.width / 2; + height = labelBox.height / 2; + + + if (x < bbox.x + width) { + x = bbox.x + width; + } else if (x + width > bbox.x + bbox.width) { + x = bbox.x + bbox.width - width; + } + + y = y - height; + if (y < bbox.y + height) { + y += 2 * height; + } else if (y + height > bbox.y + bbox.height) { + y -= 2 * height; + } + + if (me.chart.animate && !me.chart.resizing) { + label.show(true); + me.onAnimate(label, { + to: { + x: x, + y: y + } + }); + } else { + label.setAttributes({ + x: x, + y: y + }, true); + if (resizing && me.animation) { + me.animation.on('afteranimate', function() { + label.show(true); + }); + } else { + label.show(true); + } + } + }, + + + onPlaceCallout : function(callout, storeItem, item, i, display, animate, index) { + var me = this, + chart = me.chart, + surface = chart.surface, + resizing = chart.resizing, + config = me.callouts, + items = me.items, + prev = (i == 0) ? false : items[i -1].point, + next = (i == items.length -1) ? false : items[i +1].point, + cur = item.point, + dir, norm, normal, a, aprev, anext, + bbox = (callout && callout.label ? callout.label.getBBox() : {width:0,height:0}), + offsetFromViz = 30, + offsetToSide = 10, + offsetBox = 3, + boxx, boxy, boxw, boxh, + p, clipRect = me.clipRect, + x, y; + + if (!bbox.width || !bbox.height) { + return; + } + + + if (!prev) { + prev = cur; + } + if (!next) { + next = cur; + } + a = (next[1] - prev[1]) / (next[0] - prev[0]); + aprev = (cur[1] - prev[1]) / (cur[0] - prev[0]); + anext = (next[1] - cur[1]) / (next[0] - cur[0]); + + norm = Math.sqrt(1 + a * a); + dir = [1 / norm, a / norm]; + normal = [-dir[1], dir[0]]; + + + if (aprev > 0 && anext < 0 && normal[1] < 0 || aprev < 0 && anext > 0 && normal[1] > 0) { + normal[0] *= -1; + normal[1] *= -1; + } else if (Math.abs(aprev) < Math.abs(anext) && normal[0] < 0 || Math.abs(aprev) > Math.abs(anext) && normal[0] > 0) { + normal[0] *= -1; + normal[1] *= -1; + } + + + x = cur[0] + normal[0] * offsetFromViz; + y = cur[1] + normal[1] * offsetFromViz; + + + boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox)); + boxy = y - bbox.height /2 - offsetBox; + boxw = bbox.width + 2 * offsetBox; + boxh = bbox.height + 2 * offsetBox; + + + + if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) { + normal[0] *= -1; + } + if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) { + normal[1] *= -1; + } + + + x = cur[0] + normal[0] * offsetFromViz; + y = cur[1] + normal[1] * offsetFromViz; + + + boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox)); + boxy = y - bbox.height /2 - offsetBox; + boxw = bbox.width + 2 * offsetBox; + boxh = bbox.height + 2 * offsetBox; + + + callout.lines.setAttributes({ + path: ["M", cur[0], cur[1], "L", x, y, "Z"] + }, true); + + callout.box.setAttributes({ + x: boxx, + y: boxy, + width: boxw, + height: boxh + }, true); + + callout.label.setAttributes({ + x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)), + y: y + }, true); + for (p in callout) { + callout[p].show(true); + } + }, + + isItemInPoint: function(x, y, item, i) { + var me = this, + pointsUp = item.pointsUp, + pointsDown = item.pointsDown, + abs = Math.abs, + distChanged = false, + last = false, + reverse = me.reverse, + dist = Infinity, p, pln, point; + + for (p = 0, pln = pointsUp.length; p < pln; p++) { + point = [pointsUp[p][0], pointsUp[p][1]]; + + distChanged = false; + last = p == pln -1; + + if (dist > abs(x - point[0])) { + dist = abs(x - point[0]); + distChanged = true; + if (last) { + ++p; + } + } + + if (!distChanged || (distChanged && last)) { + point = pointsUp[p -1]; + if (y >= point[1] && (!pointsDown.length || y <= (pointsDown[p -1][1]))) { + idx = reverse ? pln - p : p - 1; + item.storeIndex = idx; + item.storeField = me.yField[i]; + item.storeItem = me.chart.getChartStore().getAt(idx); + item._points = pointsDown.length? [point, pointsDown[p - 1]] : [point]; + return true; + } else { + break; + } + } + } + return false; + }, + + + highlightSeries: function() { + var area, to, fillColor; + if (this._index !== undefined) { + area = this.areas[this._index]; + if (area.__highlightAnim) { + area.__highlightAnim.paused = true; + } + area.__highlighted = true; + area.__prevOpacity = area.__prevOpacity || area.attr.opacity || 1; + area.__prevFill = area.__prevFill || area.attr.fill; + area.__prevLineWidth = area.__prevLineWidth || area.attr.lineWidth; + fillColor = Ext.draw.Color.fromString(area.__prevFill); + to = { + lineWidth: (area.__prevLineWidth || 0) + 2 + }; + if (fillColor) { + to.fill = fillColor.getLighter(0.2).toString(); + } + else { + to.opacity = Math.max(area.__prevOpacity - 0.3, 0); + } + if (this.chart.animate) { + area.__highlightAnim = new Ext.fx.Anim(Ext.apply({ + target: area, + to: to + }, this.chart.animate)); + } + else { + area.setAttributes(to, true); + } + } + }, + + + unHighlightSeries: function() { + var area; + if (this._index !== undefined) { + area = this.areas[this._index]; + if (area.__highlightAnim) { + area.__highlightAnim.paused = true; + } + if (area.__highlighted) { + area.__highlighted = false; + area.__highlightAnim = new Ext.fx.Anim({ + target: area, + to: { + fill: area.__prevFill, + opacity: area.__prevOpacity, + lineWidth: area.__prevLineWidth + } + }); + } + } + }, + + + highlightItem: function(item) { + var me = this, + points, path; + if (!item) { + this.highlightSeries(); + return; + } + + points = item._points; + if (points.length === 2) { + path = ['M', points[0][0], points[0][1], 'L', points[1][0], points[1][1]]; + } else { + path = ['M', points[0][0], points[0][1], 'L', points[0][0], me.bbox.y + me.bbox.height]; + } + + me.highlightSprite.setAttributes({ + path: path, + hidden: false + }, true); + }, + + + unHighlightItem: function(item) { + if (!item) { + this.unHighlightSeries(); + } + + if (this.highlightSprite) { + this.highlightSprite.hide(true); + } + }, + + + hideAll: function(index) { + var me = this; + index = (isNaN(me._index) ? index : me._index) || 0; + me.__excludes[index] = true; + me.areas[index].hide(true); + me.redraw(); + }, + + + showAll: function(index) { + var me = this; + index = (isNaN(me._index) ? index : me._index) || 0; + me.__excludes[index] = false; + me.areas[index].show(true); + me.redraw(); + }, + + redraw: function() { + + + + var me = this, + prevLegendConfig; + prevLegendConfig = me.chart.legend.rebuild; + me.chart.legend.rebuild = false; + me.chart.redraw(); + me.chart.legend.rebuild = prevLegendConfig; + }, + + hide: function() { + if (this.areas) { + var me = this, + areas = me.areas, + i, j, l, ln, shadows; + + if (areas && areas.length) { + for (i = 0, ln = areas.length; i < ln; ++i) { + if (areas[i]) { + areas[i].hide(true); + } + } + me.hideLabels(); + } + } + }, + + + getLegendColor: function(index) { + var me = this; + index += me.themeIdx; + return me.colorArrayStyle[index % me.colorArrayStyle.length]; + } +}); + + + +Ext.define('Ext.chart.series.Bar', { + + + + extend: Ext.chart.series.Cartesian , + + alternateClassName: ['Ext.chart.BarSeries', 'Ext.chart.BarChart', 'Ext.chart.StackedBarChart'], + + + + + + type: 'bar', + + alias: 'series.bar', + + column: false, + + + style: {}, + + + gutter: 38.2, + + + groupGutter: 38.2, + + + xPadding: 0, + + + yPadding: 10, + + + + defaultRotate: { + x: 0, + y: 0, + degrees: 0 + }, + + constructor: function(config) { + this.callParent(arguments); + var me = this, + surface = me.chart.surface, + shadow = me.chart.shadow, + i, l; + config.highlightCfg = Ext.Object.merge({ + lineWidth: 3, + stroke: '#55c', + opacity: 0.8, + color: '#f00' + }, config.highlightCfg); + Ext.apply(me, config, { + shadowAttributes: [{ + "stroke-width": 6, + "stroke-opacity": 0.05, + stroke: 'rgb(200, 200, 200)', + translate: { + x: 1.2, + y: 1.2 + } + }, { + "stroke-width": 4, + "stroke-opacity": 0.1, + stroke: 'rgb(150, 150, 150)', + translate: { + x: 0.9, + y: 0.9 + } + }, { + "stroke-width": 2, + "stroke-opacity": 0.15, + stroke: 'rgb(100, 100, 100)', + translate: { + x: 0.6, + y: 0.6 + } + }] + }); + + me.group = surface.getGroup(me.seriesId + '-bars'); + if (shadow) { + for (i = 0, l = me.shadowAttributes.length; i < l; i++) { + me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i)); + } + } + }, + + + getPadding: function() { + var me = this, + xPadding = me.xPadding, + yPadding = me.yPadding, + padding = { }; + + if (Ext.isNumber(xPadding)) { + padding.left = xPadding; + padding.right = xPadding; + } else if (Ext.isObject(xPadding)) { + padding.left = xPadding.left; + padding.right = xPadding.right; + } else { + padding.left = 0; + padding.right = 0; + } + padding.width = padding.left + padding.right; + + if (Ext.isNumber(yPadding)) { + padding.bottom = yPadding; + padding.top = yPadding; + } else if (Ext.isObject(yPadding)) { + padding.bottom = yPadding.bottom; + padding.top = yPadding.top; + } else { + padding.bottom = 0; + padding.top = 0; + } + padding.height = padding.bottom + padding.top; + + return padding; + }, + + + getBarGirth: function() { + var me = this, + store = me.chart.getChartStore(), + column = me.column, + ln = store.getCount(), + gutter = me.gutter / 100, + padding, + property; + + property = (column ? 'width' : 'height'); + + if (me.style && me.style[property]) { + me.configuredColumnGirth = true; + return +me.style[property]; + } + + padding = me.getPadding(); + + return (me.chart.chartBBox[property] - padding[property]) / (ln * (gutter + 1) - gutter); + }, + + + getGutters: function() { + var me = this, + column = me.column, + padding = me.getPadding(), + halfBarGirth = me.getBarGirth() / 2, + lowerGutter = Math.ceil((column ? padding.left : padding.bottom) + halfBarGirth), + upperGutter = Math.ceil((column ? padding.right : padding.top) + halfBarGirth); + + return { + lower: lowerGutter, + upper: upperGutter, + verticalAxis: !column + }; + }, + + + getBounds: function() { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + data = store.data.items, + i, ln, record, + bars = [].concat(me.yField), + barsLoc = [], + barsLen = bars.length, + groupBarsLen = barsLen, + groupGutter = me.groupGutter / 100, + column = me.column, + padding = me.getPadding(), + stacked = me.stacked, + barWidth = me.getBarGirth(), + barWidthProperty = column ? 'width' : 'height', + math = Math, + mmin = math.min, + mmax = math.max, + mabs = math.abs, + boundAxes = me.getAxesForXAndYFields(), + boundYAxis = boundAxes.yAxis, + minX, maxX, colsScale, colsZero, gutters, + ends, shrunkBarWidth, groupBarWidth, bbox, minY, maxY, axis, out, + scale, zero, total, rec, j, plus, minus, inflections, tick, loc; + + me.setBBox(true); + bbox = me.bbox; + + + if (me.__excludes) { + for (j = 0, total = me.__excludes.length; j < total; j++) { + if (me.__excludes[j]) { + groupBarsLen--; + } + } + } + axis = chart.axes.get(boundYAxis); + if (axis) { + ends = axis.applyData(); + minY = ends.from; + maxY = ends.to; + } + + if (me.yField && !Ext.isNumber(minY)) { + out = me.getMinMaxYValues(); + minY = out[0]; + maxY = out[1]; + } + + if (!Ext.isNumber(minY)) { + minY = 0; + } + if (!Ext.isNumber(maxY)) { + maxY = 0; + } + scale = (column ? bbox.height - padding.height : bbox.width - padding.width) / (maxY - minY); + shrunkBarWidth = barWidth; + groupBarWidth = (barWidth / ((stacked ? 1 : groupBarsLen) * (groupGutter + 1) - groupGutter)); + + if (barWidthProperty in me.style) { + groupBarWidth = mmin(groupBarWidth, me.style[barWidthProperty]); + shrunkBarWidth = groupBarWidth * ((stacked ? 1 : groupBarsLen) * (groupGutter + 1) - groupGutter); + } + zero = (column) ? bbox.y + bbox.height - padding.bottom : bbox.x + padding.left; + + if (stacked) { + total = [[], []]; + for (i = 0, ln = data.length; i < ln; i++) { + record = data[i]; + total[0][i] = total[0][i] || 0; + total[1][i] = total[1][i] || 0; + for (j = 0; j < barsLen; j++) { + if (me.__excludes && me.__excludes[j]) { + continue; + } + rec = record.get(bars[j]); + total[+(rec > 0)][i] += mabs(rec); + } + } + total[+(maxY > 0)].push(mabs(maxY)); + total[+(minY > 0)].push(mabs(minY)); + minus = mmax.apply(math, total[0]); + plus = mmax.apply(math, total[1]); + scale = (column ? bbox.height - padding.height : bbox.width - padding.width) / (plus + minus); + zero = zero + minus * scale * (column ? -1 : 1); + } + else if (minY / maxY < 0) { + zero = zero - minY * scale * (column ? -1 : 1); + } + + + if (me.boundColumn) { + axis = chart.axes.get(boundAxes.xAxis); + if (axis) { + ends = axis.applyData(); + minX = ends.from; + maxX = ends.to; + } + if (me.xField && !Ext.isNumber(minX)) { + out = me.getMinMaxYValues(); + minX = out[0]; + maxX = out[1]; + } + if (!Ext.isNumber(minX)) { + minX = 0; + } + if (!Ext.isNumber(maxX)) { + maxX = 0; + } + gutters = me.getGutters(); + colsScale = (bbox.width - (gutters.lower + gutters.upper)) / ((maxX - minX) || 1); + + colsZero = bbox.x + gutters.lower; + + barsLoc = []; + for (i = 0, ln = data.length; i < ln; i++) { + record = data[i]; + rec = record.get(me.xField); + barsLoc[i] = colsZero + (rec - minX) * colsScale - (groupBarWidth / 2); + } + } + + + else if (me.configuredColumnGirth) { + axis = chart.axes.get(boundAxes.xAxis); + if (axis) { + inflections = axis.inflections; + if (axis.isCategoryAxis || inflections.length == data.length) { + barsLoc = []; + for (i = 0, ln = data.length; i < ln; i++) { + tick = inflections[i]; + loc = column ? tick[0] : tick[1]; + barsLoc[i] = loc - (shrunkBarWidth / 2); + } + } + } + } + + return { + bars: bars, + barsLoc: barsLoc, + bbox: bbox, + shrunkBarWidth: shrunkBarWidth, + barsLen: barsLen, + groupBarsLen: groupBarsLen, + barWidth: barWidth, + groupBarWidth: groupBarWidth, + scale: scale, + zero: zero, + padding: padding, + signed: minY / maxY < 0, + minY: minY, + maxY: maxY + }; + }, + + + getPaths: function() { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + data = store.data.items, + i, total, record, + bounds = me.bounds = me.getBounds(), + items = me.items = [], + yFields = Ext.isArray(me.yField) ? me.yField : [me.yField], + gutter = me.gutter / 100, + groupGutter = me.groupGutter / 100, + animate = chart.animate, + column = me.column, + group = me.group, + enableShadows = chart.shadow, + shadowGroups = me.shadowGroups, + shadowAttributes = me.shadowAttributes, + shadowGroupsLn = shadowGroups.length, + bbox = bounds.bbox, + barWidth = bounds.barWidth, + shrunkBarWidth = bounds.shrunkBarWidth, + padding = me.getPadding(), + stacked = me.stacked, + barsLen = bounds.barsLen, + colors = me.colorArrayStyle, + colorLength = colors && colors.length || 0, + themeIndex = me.themeIdx, + reverse = me.reverse, + math = Math, + mmax = math.max, + mmin = math.min, + mabs = math.abs, + j, yValue, height, totalDim, totalNegDim, bottom, top, hasShadow, barAttr, attrs, counter, + totalPositiveValues, totalNegativeValues, + shadowIndex, shadow, sprite, offset, floorY, idx, itemIdx, xPos, yPos, width, usedWidth, barIdx; + + for (i = 0, total = data.length; i < total; i++) { + record = data[i]; + bottom = bounds.zero; + top = bounds.zero; + totalDim = 0; + totalNegDim = 0; + totalPositiveValues = totalNegativeValues = 0; + hasShadow = false; + usedWidth = 0; + + for (j = 0, counter = 0; j < barsLen; j++) { + + if (me.__excludes && me.__excludes[j]) { + continue; + } + yValue = record.get(bounds.bars[j]); + if (yValue >= 0) { + totalPositiveValues += yValue; + } else { + totalNegativeValues += yValue; + } + height = Math.round((yValue - mmax(bounds.minY, 0)) * bounds.scale); + idx = themeIndex + (barsLen > 1 ? j : 0); + barAttr = { + fill: colors[idx % colorLength] + }; + + if (column) { + idx = reverse ? (total - i - 1) : i; + barIdx = reverse ? (barsLen - counter - 1) : counter; + + if (me.boundColumn) { + xPos = bounds.barsLoc[idx]; + } + else if (me.configuredColumnGirth && bounds.barsLoc.length) { + xPos = bounds.barsLoc[idx] + + barIdx * bounds.groupBarWidth * + (1 + groupGutter) * !stacked; + + } + else { + xPos = bbox.x + padding.left + + (barWidth - shrunkBarWidth) * 0.5 + + idx * barWidth * (1 + gutter) + + barIdx * bounds.groupBarWidth * + (1 + groupGutter) * !stacked; + } + + Ext.apply(barAttr, { + height: height, + width: mmax(bounds.groupBarWidth, 0), + x: xPos, + y: bottom - height + }); + } + else { + + offset = (total - 1) - i; + width = height + (bottom == bounds.zero); + xPos = bottom + (bottom != bounds.zero); + + if (reverse) { + + xPos = bounds.zero + bbox.width - width - (usedWidth === 0 ? 1 : 0); + if (stacked) { + xPos -= usedWidth; + usedWidth += width; + } + } + + if (me.configuredColumnGirth && bounds.barsLoc.length) { + yPos = bounds.barsLoc[i] + + counter * bounds.groupBarWidth * (1 + groupGutter) * !stacked; + } + else { + yPos = bbox.y + padding.top + + (barWidth - shrunkBarWidth) * 0.5 + + offset * barWidth * (1 + gutter) + + counter * bounds.groupBarWidth * + (1 + groupGutter) * !stacked + 1; + } + + Ext.apply(barAttr, { + height: mmax(bounds.groupBarWidth, 0), + width: width, + x: xPos, + y: yPos + }); + } + if (height < 0) { + if (column) { + barAttr.y = top; + barAttr.height = mabs(height); + } else { + barAttr.x = top + height; + barAttr.width = mabs(height); + } + } + if (stacked) { + if (height < 0) { + top += height * (column ? -1 : 1); + } else { + bottom += height * (column ? -1 : 1); + } + totalDim += mabs(height); + if (height < 0) { + totalNegDim += mabs(height); + } + } + barAttr.x = Math.floor(barAttr.x) + 1; + floorY = Math.floor(barAttr.y); + if (Ext.isIE8m && barAttr.y > floorY) { + floorY--; + } + barAttr.y = floorY; + barAttr.width = Math.floor(barAttr.width); + barAttr.height = Math.floor(barAttr.height); + items.push({ + series: me, + yField: yFields[j], + storeItem: record, + value: [record.get(me.xField), yValue], + attr: barAttr, + point: column ? [barAttr.x + barAttr.width / 2, yValue >= 0 ? barAttr.y : barAttr.y + barAttr.height] : + [yValue >= 0 ? barAttr.x + barAttr.width : barAttr.x, barAttr.y + barAttr.height / 2] + }); + + if (animate && chart.resizing) { + attrs = column ? { + x: barAttr.x, + y: bounds.zero, + width: barAttr.width, + height: 0 + } : { + x: bounds.zero, + y: barAttr.y, + width: 0, + height: barAttr.height + }; + if (enableShadows && (stacked && !hasShadow || !stacked)) { + hasShadow = true; + + for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) { + shadow = shadowGroups[shadowIndex].getAt(stacked ? i : (i * barsLen + j)); + if (shadow) { + shadow.setAttributes(attrs, true); + } + } + } + + sprite = group.getAt(i * barsLen + j); + if (sprite) { + sprite.setAttributes(attrs, true); + } + } + counter++; + } + if (stacked && items.length) { + items[i * counter].totalDim = totalDim; + items[i * counter].totalNegDim = totalNegDim; + items[i * counter].totalPositiveValues = totalPositiveValues; + items[i * counter].totalNegativeValues = totalNegativeValues; + } + } + if (stacked && counter == 0) { + + for (i = 0, total = data.length; i < total; i++) { + for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) { + shadow = shadowGroups[shadowIndex].getAt(i); + if (shadow) { + shadow.hide(true); + } + } + } + } + }, + + + renderShadows: function(i, barAttr, baseAttrs, bounds) { + var me = this, + chart = me.chart, + surface = chart.surface, + animate = chart.animate, + stacked = me.stacked, + shadowGroups = me.shadowGroups, + shadowAttributes = me.shadowAttributes, + shadowGroupsLn = shadowGroups.length, + store = chart.getChartStore(), + column = me.column, + items = me.items, + shadows = [], + reverse = me.reverse, + zero = bounds.zero, + shadowIndex, shadowBarAttr, shadow, totalDim, totalNegDim, j, rendererAttributes; + + if ((stacked && (i % bounds.groupBarsLen === 0)) || !stacked) { + j = i / bounds.groupBarsLen; + + for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) { + shadowBarAttr = Ext.apply({}, shadowAttributes[shadowIndex]); + shadow = shadowGroups[shadowIndex].getAt(stacked ? j : i); + Ext.copyTo(shadowBarAttr, barAttr, 'x,y,width,height'); + if (!shadow) { + shadow = surface.add(Ext.apply({ + type: 'rect', + isShadow: true, + group: shadowGroups[shadowIndex] + }, Ext.apply({}, baseAttrs, shadowBarAttr))); + } + if (stacked) { + totalDim = items[i].totalDim; + totalNegDim = items[i].totalNegDim; + if (column) { + shadowBarAttr.y = zero + totalNegDim - totalDim - 1; + shadowBarAttr.height = totalDim; + } else { + if (reverse) { + shadowBarAttr.x = zero + bounds.bbox.width - totalDim; + } else { + shadowBarAttr.x = zero - totalNegDim; + } + shadowBarAttr.width = totalDim; + } + } + + rendererAttributes = me.renderer(shadow, store.getAt(j), shadowBarAttr, i, store); + rendererAttributes.hidden = !!barAttr.hidden; + if (animate) { + me.onAnimate(shadow, { + zero: bounds.zero + (reverse ? bounds.bbox.width : 0), + to: rendererAttributes + }); + } + else { + shadow.setAttributes(rendererAttributes, true); + } + shadows.push(shadow); + } + } + return shadows; + }, + + + drawSeries: function() { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + surface = chart.surface, + animate = chart.animate, + stacked = me.stacked, + column = me.column, + chartAxes = chart.axes, + boundAxes = me.getAxesForXAndYFields(), + boundXAxis = boundAxes.xAxis, + boundYAxis = boundAxes.yAxis, + enableShadows = chart.shadow, + shadowGroups = me.shadowGroups, + shadowGroupsLn = shadowGroups.length, + group = me.group, + seriesStyle = me.seriesStyle, + items, ln, i, j, baseAttrs, sprite, rendererAttributes, shadowIndex, shadowGroup, + bounds, endSeriesStyle, barAttr, attrs, anim; + + if (!store || !store.getCount() || me.seriesIsHidden) { + me.hide(); + me.items = []; + return; + } + + + endSeriesStyle = Ext.apply({}, this.style, seriesStyle); + delete endSeriesStyle.fill; + delete endSeriesStyle.x; + delete endSeriesStyle.y; + delete endSeriesStyle.width; + delete endSeriesStyle.height; + + me.unHighlightItem(); + me.cleanHighlights(); + + me.boundColumn = (boundXAxis && Ext.Array.contains(me.axis,boundXAxis) + && chartAxes.get(boundXAxis) + && chartAxes.get(boundXAxis).isNumericAxis); + + me.getPaths(); + bounds = me.bounds; + items = me.items; + + baseAttrs = column ? { + y: bounds.zero, + height: 0 + } : { + x: bounds.zero, + width: 0 + }; + ln = items.length; + + + for (i = 0; i < ln; i++) { + sprite = group.getAt(i); + barAttr = items[i].attr; + + if (enableShadows) { + items[i].shadows = me.renderShadows(i, barAttr, baseAttrs, bounds); + } + + + if (!sprite) { + attrs = Ext.apply({}, baseAttrs, barAttr); + attrs = Ext.apply(attrs, endSeriesStyle || {}); + sprite = surface.add(Ext.apply({}, { + type: 'rect', + group: group + }, attrs)); + } + if (animate) { + rendererAttributes = me.renderer(sprite, store.getAt(i), barAttr, i, store); + sprite._to = rendererAttributes; + anim = me.onAnimate(sprite, { + zero: bounds.zero + (me.reverse ? bounds.bbox.width : 0), + to: Ext.apply(rendererAttributes, endSeriesStyle) + }); + if (enableShadows && stacked && (i % bounds.barsLen === 0)) { + j = i / bounds.barsLen; + for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) { + anim.on('afteranimate', function() { + this.show(true); + }, shadowGroups[shadowIndex].getAt(j)); + } + } + } + else { + rendererAttributes = me.renderer(sprite, store.getAt(i), Ext.apply(barAttr, { hidden: false }), i, store); + sprite.setAttributes(Ext.apply(rendererAttributes, endSeriesStyle), true); + } + items[i].sprite = sprite; + } + + + ln = group.getCount(); + for (j = i; j < ln; j++) { + group.getAt(j).hide(true); + } + + if (me.stacked) { + + i = store.getCount(); + } + + + if (enableShadows) { + for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) { + shadowGroup = shadowGroups[shadowIndex]; + ln = shadowGroup.getCount(); + for (j = i; j < ln; j++) { + shadowGroup.getAt(j).hide(true); + } + } + } + me.renderLabels(); + }, + + + onCreateLabel: function(storeItem, item, i, display) { + var me = this, + surface = me.chart.surface, + group = me.labelsGroup, + config = me.label, + endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {}), + sprite; + + return surface.add(Ext.apply({ + type: 'text', + group: group + }, endLabelStyle || {})); + }, + + + onPlaceLabel: function(label, storeItem, item, i, display, animate, index) { + + + var me = this, + opt = me.bounds, + groupBarWidth = opt.groupBarWidth, + column = me.column, + chart = me.chart, + chartBBox = chart.chartBBox, + resizing = chart.resizing, + xValue = item.value[0], + yValue = item.value[1], + attr = item.attr, + config = me.label, + stacked = me.stacked, + stackedDisplay = config.stackedDisplay, + rotate = (config.orientation == 'vertical'), + field = [].concat(config.field), + format = config.renderer, + text, size, width, height, + zero = opt.zero, + insideStart = 'insideStart', + insideEnd = 'insideEnd', + outside = 'outside', + over = 'over', + under = 'under', + labelMarginX = 4, + labelMarginY = 2, + signed = opt.signed, + reverse = me.reverse, + x, y, finalAttr; + + if (display == insideStart || display == insideEnd || display == outside) { + if (stacked && (display == outside)) { + + + label.hide(true); + return; + } + label.setAttributes({ + + + style: undefined + }); + text = (Ext.isNumber(index) ? format(storeItem.get(field[index]), label, storeItem, item, i, display, animate, index) : ''); + label.setAttributes({ + + text: text + }); + size = me.getLabelSize(text, label.attr.style); + width = size.width; + height = size.height; + if (column) { + + + + + + if (!width || !height || (stacked && (attr.height < height))) { + label.hide(true); + return; + } + + + x = attr.x + (rotate ? groupBarWidth/2 : (groupBarWidth - width)/2); + + + if (display == outside) { + var free = (yValue >= 0 ? (attr.y - chartBBox.y) : (chartBBox.y + chartBBox.height - attr.y - attr.height)); + if (free < height + labelMarginY) { + display = insideEnd; + } + } + + + + if (!stacked && (display != outside)) { + if (height + labelMarginY > attr.height) { + display = outside; + } + } + + + + if (!y) { + y = attr.y; + if (yValue >= 0) { + switch (display) { + case insideStart: y += attr.height + (rotate ? -labelMarginY : -height/2); break; + case insideEnd: y += (rotate ? height + labelMarginX : height/2); break; + case outside: y += (rotate ? -labelMarginY : -height/2); break; + } + } else { + switch (display) { + case insideStart: y += (rotate ? height + labelMarginY : height/2); break; + case insideEnd: y += (rotate ? attr.height - labelMarginY : attr.height - height/2); break; + case outside: y += (rotate ? attr.height + height + labelMarginY : attr.height + height/2); break; + } + } + } + } + else { + + + + + if (!width || !height || (stacked && !attr.width)) { + label.hide(true); + return; + } + + + y = attr.y + (rotate ? (groupBarWidth + height)/2 : groupBarWidth/2); + + + if (display == outside) { + var free = (yValue >= 0 ? (chartBBox.x + chartBBox.width - attr.x - attr.width) : (attr.x - chartBBox.x)); + if (free < width + labelMarginX) { + display = insideEnd; + } + } + + + + + if ((display != outside) && !rotate) { + + if (width + labelMarginX * 2 >= attr.width) { + if (stacked) { + if (height > attr.width) { + label.hide(true); + return; + } + x = attr.x + attr.width/2; + rotate = true; + } else { + display = outside; + } + } + } + + + + if (!x) { + x = attr.x; + if (yValue >= 0) { + switch (display) { + case insideStart: + if (reverse) { + x += attr.width + (rotate ? -width/2 : -width - labelMarginX); + } else { + x += (rotate ? width/2 : labelMarginX); + } + break; + case insideEnd: + if (reverse) { + x -= rotate ? -width/2 : -width - labelMarginX; + } else { + x += attr.width + (rotate ? -width/2 : -width - labelMarginX); + } + break; + case outside: + if (reverse) { + x -= width + (rotate ? width/2 : labelMarginX); + } else { + x += attr.width + (rotate ? width/2 : labelMarginX); + } + break; + } + } else { + switch (display) { + case insideStart: + if (reverse) { + x -= rotate ? -width/2 : -width - labelMarginX; + } else { + x += attr.width + (rotate ? -width/2 : -width - labelMarginX); + } + break; + case insideEnd: + if (reverse) { + x += attr.width + (rotate ? -width/2 : -width - labelMarginX); + } else { + x += (rotate ? width/2 : labelMarginX); + } + break; + case outside: + if (reverse) { + x -= width + (rotate ? width/2 : labelMarginX); + } else { + x += (rotate ? -width/2 : -width - labelMarginX); + } + break; + } + } + } + } + } else if (display == over || display == under) { + if (stacked && stackedDisplay) { + + + + text = label.attr.text; + label.setAttributes({ + + + style: Ext.applyIf((label.attr && label.attr.style) || {}, + { + 'font-weight':'bold', + 'font-size':'14px' + } + ) + }); + + size = me.getLabelSize(text, label.attr.style); + width = size.width; + height = size.height; + + switch (display) { + case over: + if (column) { + x = attr.x + (rotate ? groupBarWidth/2 : (groupBarWidth - width)/2); + y = zero - (item.totalDim - item.totalNegDim) - height/2 - labelMarginY; + } else { + x = zero + (item.totalDim - item.totalNegDim) + labelMarginX; + y = attr.y + (rotate ? (groupBarWidth + height)/2 : groupBarWidth/2); + } + break; + case under: + if (column) { + x = attr.x + (rotate ? groupBarWidth/2 : (groupBarWidth - width)/2); + y = zero + item.totalNegDim + height/2; + } else { + x = zero - item.totalNegDim - width - labelMarginX; + y = attr.y + (rotate ? (groupBarWidth + height)/2 : groupBarWidth/2); + } + break; + } + } + } + + if (x == undefined || y == undefined) { + + label.hide(true); + return; + } + + label.isOutside = (display == outside); + label.setAttributes({ + text: text + }); + + + finalAttr = { + x: x, + y: y + }; + + + + finalAttr.rotate = rotate ? { + x: x, + y: y, + degrees: 270 + } : me.defaultRotate; + + + if (animate && resizing) { + if (column) { + x = attr.x + attr.width / 2; + y = zero; + } else { + x = zero; + y = attr.y + attr.height / 2; + } + label.setAttributes({ + x: x, + y: y + }, true); + if (rotate) { + label.setAttributes({ + rotate: { + x: x, + y: y, + degrees: 270 + } + }, true); + } + } + + + if (animate) { + me.onAnimate(label, { + zero: item.point[0], + to: finalAttr + }); + } + else { + label.setAttributes(Ext.apply(finalAttr, { + hidden: false + }), true); + } + }, + + + getLabelSize: function(value, labelStyle) { + var tester = this.testerLabel, + config = this.label, + endLabelStyle = Ext.apply({}, config, labelStyle, this.seriesLabelStyle || {}), + rotated = config.orientation === 'vertical', + bbox, w, h, + undef; + if (!tester) { + tester = this.testerLabel = this.chart.surface.add(Ext.apply({ + type: 'text', + opacity: 0 + }, endLabelStyle)); + } + tester.setAttributes({ + style: labelStyle, + text: value + }, true); + + + bbox = tester.getBBox(); + w = bbox.width; + h = bbox.height; + return { + width: rotated ? h : w, + height: rotated ? w : h + }; + }, + + + onAnimate: function(sprite, attr) { + var me = this, + to = attr.to, + stacked = me.stacked, + reverse = me.reverse, + width = 0, + isText, bbox, x, from; + + sprite.show(); + + if (!me.column) { + if (reverse) { + bbox = sprite.getBBox(); + isText = sprite.type == 'text'; + + + if (!me.inHighlight) { + if (!stacked) { + if (isText) { + + x = bbox.x >= 5 ? x : attr.zero; + } else { + if (bbox.width) { + width = bbox.width; + } + x = bbox.width ? bbox.x : to.x + to.width; + } + } else { + x = attr.zero; + } + } + attr.from = { + x: x, + width: width + }; + } + + if (stacked) { + from = attr.from; + if (!from) { + from = attr.from = {}; + } + from.y = to.y; + if (!reverse) { + from.x = attr.zero; + if (sprite.isShadow) { + from.width = 0; + } + } + } + } + + return this.callParent(arguments); + }, + + isItemInPoint: function(x, y, item) { + var bbox = item.sprite.getBBox(); + return bbox.x <= x && bbox.y <= y + && (bbox.x + bbox.width) >= x + && (bbox.y + bbox.height) >= y; + }, + + + hideAll: function(index) { + var axes = this.chart.axes, + axesItems = axes.items, + ln = axesItems.length, + i = 0; + + index = (isNaN(this._index) ? index : this._index) || 0; + + if (!this.__excludes) { + this.__excludes = []; + } + + this.__excludes[index] = true; + this.drawSeries(); + + for (i; i < ln; i++) { + axesItems[i].drawAxis(); + } + }, + + + showAll: function(index) { + var axes = this.chart.axes, + axesItems = axes.items, + ln = axesItems.length, + i = 0; + + index = (isNaN(this._index) ? index : this._index) || 0; + + if (!this.__excludes) { + this.__excludes = []; + } + + this.__excludes[index] = false; + this.drawSeries(); + + for (i; i < ln; i++) { + axesItems[i].drawAxis(); + } + }, + + + getLegendColor: function(index) { + var me = this, + colors = me.colorArrayStyle, + colorLength = colors && colors.length; + + if (me.style && me.style.fill) { + return me.style.fill; + } else { + return (colors ? colors[(me.themeIdx + index) % colorLength] : '#000'); + } + }, + + highlightItem: function(item) { + this.callParent(arguments); + this.inHighlight = true; + this.renderLabels(); + delete this.inHighlight; + }, + + unHighlightItem: function() { + this.callParent(arguments); + this.inHighlight = true; + this.renderLabels(); + delete this.inHighlight; + }, + + cleanHighlights: function() { + this.callParent(arguments); + this.inHighlight = true; + this.renderLabels(); + delete this.inHighlight; + } +}); + + + +Ext.define('Ext.chart.series.Column', { + + + + alternateClassName: ['Ext.chart.ColumnSeries', 'Ext.chart.ColumnChart', 'Ext.chart.StackedColumnChart'], + + extend: Ext.chart.series.Bar , + + + + type: 'column', + alias: 'series.column', + + column: true, + + + boundColumn: false, + + + + + xPadding: 10, + + + yPadding: 0 +}); + + + +Ext.define('Ext.chart.series.Gauge', { + + + + extend: Ext.chart.series.Series , + + + + type: "gauge", + alias: 'series.gauge', + + rad: Math.PI / 180, + + + + + highlightDuration: 150, + + + angleField: false, + + + needle: false, + + + donut: false, + + + showInLegend: false, + + + style: {}, + + constructor: function(config) { + this.callParent(arguments); + var me = this, + chart = me.chart, + surface = chart.surface, + store = chart.store, + shadow = chart.shadow, i, l, cfg; + Ext.apply(me, config, { + shadowAttributes: [{ + "stroke-width": 6, + "stroke-opacity": 1, + stroke: 'rgb(200, 200, 200)', + translate: { + x: 1.2, + y: 2 + } + }, + { + "stroke-width": 4, + "stroke-opacity": 1, + stroke: 'rgb(150, 150, 150)', + translate: { + x: 0.9, + y: 1.5 + } + }, + { + "stroke-width": 2, + "stroke-opacity": 1, + stroke: 'rgb(100, 100, 100)', + translate: { + x: 0.6, + y: 1 + } + }] + }); + me.group = surface.getGroup(me.seriesId); + if (shadow) { + for (i = 0, l = me.shadowAttributes.length; i < l; i++) { + me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i)); + } + } + surface.customAttributes.segment = function(opt) { + return me.getSegment(opt); + }; + }, + + + initialize: function() { + var me = this, + store = me.chart.getChartStore(), + data = store.data.items, + label = me.label, + ln = data.length; + + me.yField = []; + if (label && label.field && ln > 0) { + me.yField.push(data[0].get(label.field)); + } + }, + + + getSegment: function(opt) { + var me = this, + rad = me.rad, + cos = Math.cos, + sin = Math.sin, + abs = Math.abs, + x = me.centerX, + y = me.centerY, + x1 = 0, x2 = 0, x3 = 0, x4 = 0, + y1 = 0, y2 = 0, y3 = 0, y4 = 0, + delta = 1e-2, + r = opt.endRho - opt.startRho, + startAngle = opt.startAngle, + endAngle = opt.endAngle, + midAngle = (startAngle + endAngle) / 2 * rad, + margin = opt.margin || 0, + flag = abs(endAngle - startAngle) > 180, + a1 = Math.min(startAngle, endAngle) * rad, + a2 = Math.max(startAngle, endAngle) * rad, + singleSlice = false; + + x += margin * cos(midAngle); + y += margin * sin(midAngle); + + x1 = x + opt.startRho * cos(a1); + y1 = y + opt.startRho * sin(a1); + + x2 = x + opt.endRho * cos(a1); + y2 = y + opt.endRho * sin(a1); + + x3 = x + opt.startRho * cos(a2); + y3 = y + opt.startRho * sin(a2); + + x4 = x + opt.endRho * cos(a2); + y4 = y + opt.endRho * sin(a2); + + if (abs(x1 - x3) <= delta && abs(y1 - y3) <= delta) { + singleSlice = true; + } + + if (singleSlice) { + return { + path: [ + ["M", x1, y1], + ["L", x2, y2], + ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4], + ["Z"]] + }; + } else { + return { + path: [ + ["M", x1, y1], + ["L", x2, y2], + ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4], + ["L", x3, y3], + ["A", opt.startRho, opt.startRho, 0, +flag, 0, x1, y1], + ["Z"]] + }; + } + }, + + + calcMiddle: function(item) { + var me = this, + rad = me.rad, + slice = item.slice, + x = me.centerX, + y = me.centerY, + startAngle = slice.startAngle, + endAngle = slice.endAngle, + radius = Math.max(('rho' in slice) ? slice.rho: me.radius, me.label.minMargin), + donut = +me.donut, + a1 = Math.min(startAngle, endAngle) * rad, + a2 = Math.max(startAngle, endAngle) * rad, + midAngle = -(a1 + (a2 - a1) / 2), + xm = x + (item.endRho + item.startRho) / 2 * Math.cos(midAngle), + ym = y - (item.endRho + item.startRho) / 2 * Math.sin(midAngle); + + item.middle = { + x: xm, + y: ym + }; + }, + + + drawSeries: function() { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + group = me.group, + animate = me.chart.animate, + axis = me.chart.axes.get(0), + minimum = axis && axis.minimum || me.minimum || 0, + maximum = axis && axis.maximum || me.maximum || 0, + field = me.angleField || me.field || me.xField, + surface = chart.surface, + chartBBox = chart.chartBBox, + rad = me.rad, + donut = +me.donut, + values = {}, + items = [], + seriesStyle = me.seriesStyle, + seriesLabelStyle = me.seriesLabelStyle, + colorArrayStyle = me.colorArrayStyle, + colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0, + cos = Math.cos, + sin = Math.sin, + defaultStart = -180, + reverse = me.reverse, + rendererAttributes, centerX, centerY, slice, slices, sprite, value, + item, ln, record, i, j, startAngle, endAngle, middleAngle, sliceLength, path, + p, spriteOptions, bbox, splitAngle, sliceA, sliceB; + + Ext.apply(seriesStyle, me.style || {}); + + me.setBBox(); + bbox = me.bbox; + + + if (me.colorSet) { + colorArrayStyle = me.colorSet; + colorArrayLength = colorArrayStyle.length; + } + + + if (!store || !store.getCount() || me.seriesIsHidden) { + me.hide(); + me.items = []; + return; + } + + centerX = me.centerX = chartBBox.x + (chartBBox.width / 2); + centerY = me.centerY = chartBBox.y + chartBBox.height; + me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y); + me.slices = slices = []; + me.items = items = []; + + if (!me.value) { + record = store.getAt(0); + me.value = record.get(field); + } + + value = reverse ? maximum - me.value : me.value; + if (me.needle) { + sliceA = { + series: me, + value: value, + startAngle: defaultStart, + endAngle: 0, + rho: me.radius + }; + splitAngle = defaultStart * (1 - (value - minimum) / (maximum - minimum)); + slices.push(sliceA); + } else { + splitAngle = defaultStart * (1 - (value - minimum) / (maximum - minimum)); + sliceA = { + series: me, + value: value, + startAngle: defaultStart, + endAngle: splitAngle, + rho: me.radius + }; + sliceB = { + series: me, + value: maximum - value, + startAngle: splitAngle, + endAngle: 0, + rho: me.radius + }; + + if (reverse) { + slices.push(sliceB, sliceA); + } else { + slices.push(sliceA, sliceB); + } + } + + + for (i = 0, ln = slices.length; i < ln; i++) { + slice = slices[i]; + sprite = group.getAt(i); + + rendererAttributes = Ext.apply({ + segment: { + startAngle: slice.startAngle, + endAngle: slice.endAngle, + margin: 0, + rho: slice.rho, + startRho: slice.rho * +donut / 100, + endRho: slice.rho + } + }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {})); + + item = Ext.apply({}, + rendererAttributes.segment, { + slice: slice, + series: me, + storeItem: record, + index: i + }); + items[i] = item; + + if (!sprite) { + spriteOptions = Ext.apply({ + type: "path", + group: group + }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {})); + sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes)); + } + slice.sprite = slice.sprite || []; + item.sprite = sprite; + slice.sprite.push(sprite); + if (animate) { + rendererAttributes = me.renderer(sprite, record, rendererAttributes, i, store); + sprite._to = rendererAttributes; + me.onAnimate(sprite, { + to: rendererAttributes + }); + } else { + rendererAttributes = me.renderer(sprite, record, Ext.apply(rendererAttributes, { + hidden: false + }), i, store); + sprite.setAttributes(rendererAttributes, true); + } + } + + if (me.needle) { + splitAngle = splitAngle * Math.PI / 180; + + if (!me.needleSprite) { + me.needleSprite = me.chart.surface.add({ + type: 'path', + path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle), + centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)), + 'L', centerX + me.radius * cos(splitAngle), + centerY + -Math.abs(me.radius * sin(splitAngle))], + 'stroke-width': 4, + 'stroke': '#222' + }); + } else { + if (animate) { + me.onAnimate(me.needleSprite, { + to: { + path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle), + centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)), + 'L', centerX + me.radius * cos(splitAngle), + centerY + -Math.abs(me.radius * sin(splitAngle))] + } + }); + } else { + me.needleSprite.setAttributes({ + type: 'path', + path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle), + centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)), + 'L', centerX + me.radius * cos(splitAngle), + centerY + -Math.abs(me.radius * sin(splitAngle))] + }); + } + } + me.needleSprite.setAttributes({ + hidden: false + }, true); + } + + delete me.value; + }, + + + setValue: function (value) { + this.value = value; + this.drawSeries(); + }, + + + onCreateLabel: function(storeItem, item, i, display) {}, + + + onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {}, + + + onPlaceCallout: function() {}, + + + onAnimate: function(sprite, attr) { + sprite.show(); + return this.callParent(arguments); + }, + + isItemInPoint: function(x, y, item, i) { + var me = this, + cx = me.centerX, + cy = me.centerY, + abs = Math.abs, + dx = abs(x - cx), + dy = abs(y - cy), + startAngle = item.startAngle, + endAngle = item.endAngle, + rho = Math.sqrt(dx * dx + dy * dy), + angle = Math.atan2(y - cy, x - cx) / me.rad; + + + return (i === 0) && (angle >= startAngle && angle < endAngle && + rho >= item.startRho && rho <= item.endRho); + }, + + + getLegendColor: function(index) { + var colors = this.colorSet || this.colorArrayStyle; + return colors[index % colors.length]; + } +}); + + + + +Ext.define('Ext.chart.series.Line', { + + + + extend: Ext.chart.series.Cartesian , + + alternateClassName: ['Ext.chart.LineSeries', 'Ext.chart.LineChart'], + + + + + + type: 'line', + + alias: 'series.line', + + + selectionTolerance: 20, + + + showMarkers: true, + + + markerConfig: {}, + + + style: {}, + + + smooth: false, + + + defaultSmoothness: 3, + + + fill: false, + + constructor: function(config) { + this.callParent(arguments); + var me = this, + surface = me.chart.surface, + shadow = me.chart.shadow, + i, l; + config.highlightCfg = Ext.Object.merge({ 'stroke-width': 3 }, config.highlightCfg); + Ext.apply(me, config, { + shadowAttributes: [{ + "stroke-width": 6, + "stroke-opacity": 0.05, + stroke: 'rgb(0, 0, 0)', + translate: { + x: 1, + y: 1 + } + }, { + "stroke-width": 4, + "stroke-opacity": 0.1, + stroke: 'rgb(0, 0, 0)', + translate: { + x: 1, + y: 1 + } + }, { + "stroke-width": 2, + "stroke-opacity": 0.15, + stroke: 'rgb(0, 0, 0)', + translate: { + x: 1, + y: 1 + } + }] + }); + me.group = surface.getGroup(me.seriesId); + if (me.showMarkers) { + me.markerGroup = surface.getGroup(me.seriesId + '-markers'); + } + if (shadow) { + for (i = 0, l = me.shadowAttributes.length; i < l; i++) { + me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i)); + } + } + }, + + + shrink: function(xValues, yValues, size) { + + var len = xValues.length, + ratio = Math.floor(len / size), + i = 1, + xSum = 0, + ySum = 0, + xRes = [+xValues[0]], + yRes = [+yValues[0]]; + + for (; i < len; ++i) { + xSum += +xValues[i] || 0; + ySum += +yValues[i] || 0; + if (i % ratio == 0) { + xRes.push(xSum/ratio); + yRes.push(ySum/ratio); + xSum = 0; + ySum = 0; + } + } + return { + x: xRes, + y: yRes + }; + }, + + + drawSeries: function() { + var me = this, + chart = me.chart, + chartAxes = chart.axes, + store = chart.getChartStore(), + data = store.data.items, + record, + storeCount = store.getCount(), + surface = me.chart.surface, + bbox = {}, + group = me.group, + showMarkers = me.showMarkers, + markerGroup = me.markerGroup, + enableShadows = chart.shadow, + shadowGroups = me.shadowGroups, + shadowAttributes = me.shadowAttributes, + smooth = me.smooth, + lnsh = shadowGroups.length, + dummyPath = ["M"], + path = ["M"], + renderPath = ["M"], + smoothPath = ["M"], + markerIndex = chart.markerIndex, + axes = [].concat(me.axis), + shadowBarAttr, + xValues = [], + yValues = [], + onbreak = false, + reverse = me.reverse, + storeIndices = [], + markerStyle = Ext.apply({}, me.markerStyle), + seriesStyle = me.seriesStyle, + colorArrayStyle = me.colorArrayStyle, + colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0, + isNumber = Ext.isNumber, + seriesIdx = me.seriesIdx, + boundAxes = me.getAxesForXAndYFields(), + boundXAxis = boundAxes.xAxis, + boundYAxis = boundAxes.yAxis, + xAxis = chartAxes && chartAxes.get(boundXAxis), + yAxis = chartAxes && chartAxes.get(boundYAxis), + xAxisType = boundXAxis ? xAxis && xAxis.type : '', + yAxisType = boundYAxis ? yAxis && yAxis.type : '', + shadows, shadow, shindex, fromPath, fill, fillPath, rendererAttributes, + x, y, prevX, prevY, firstX, firstY, markerCount, i, j, ln, axis, ends, marker, markerAux, item, xValue, + yValue, coords, xScale, yScale, minX, maxX, minY, maxY, line, animation, endMarkerStyle, + endLineStyle, type, count, opacity, lineOpacity, fillOpacity, fillDefaultValue; + + if (me.fireEvent('beforedraw', me) === false) { + return; + } + + + if (!storeCount || me.seriesIsHidden) { + me.hide(); + me.items = []; + if (me.line) { + me.line.hide(true); + if (me.line.shadows) { + shadows = me.line.shadows; + for (j = 0, lnsh = shadows.length; j < lnsh; j++) { + shadow = shadows[j]; + shadow.hide(true); + } + } + if (me.fillPath) { + me.fillPath.hide(true); + } + } + me.line = null; + me.fillPath = null; + return; + } + + + endMarkerStyle = Ext.apply(markerStyle || {}, me.markerConfig, { + fill: me.seriesStyle.fill || colorArrayStyle[me.themeIdx % colorArrayStyle.length] + }); + type = endMarkerStyle.type; + delete endMarkerStyle.type; + endLineStyle = seriesStyle; + + + if (!endLineStyle['stroke-width']) { + endLineStyle['stroke-width'] = 0.5; + } + + + opacity = 'opacity' in endLineStyle ? endLineStyle.opacity : 1; + fillDefaultValue = 'opacity' in endLineStyle ? endLineStyle.opacity : 0.3; + lineOpacity = 'lineOpacity' in endLineStyle ? endLineStyle.lineOpacity : opacity; + fillOpacity = 'fillOpacity' in endLineStyle ? endLineStyle.fillOpacity : fillDefaultValue; + + + + if (markerIndex && markerGroup && markerGroup.getCount()) { + for (i = 0; i < markerIndex; i++) { + marker = markerGroup.getAt(i); + markerGroup.remove(marker); + markerGroup.add(marker); + markerAux = markerGroup.getAt(markerGroup.getCount() - 2); + marker.setAttributes({ + x: 0, + y: 0, + translate: { + x: markerAux.attr.translation.x, + y: markerAux.attr.translation.y + } + }, true); + } + } + + me.unHighlightItem(); + me.cleanHighlights(); + + me.setBBox(); + bbox = me.bbox; + me.clipRect = [bbox.x, bbox.y, bbox.width, bbox.height]; + + if (xAxis) { + ends = xAxis.applyData(); + minX = ends.from; + maxX = ends.to; + } + + if (yAxis) { + ends = yAxis.applyData(); + minY = ends.from; + maxY = ends.to; + } + + + if (me.xField && !Ext.isNumber(minX)) { + axis = me.getMinMaxXValues(); + minX = axis[0]; + maxX = axis[1]; + } + + if (me.yField && !Ext.isNumber(minY)) { + axis = me.getMinMaxYValues(); + minY = axis[0]; + maxY = axis[1]; + } + + if (isNaN(minX)) { + minX = 0; + xScale = bbox.width / ((storeCount - 1) || 1); + } + else { + xScale = bbox.width / ((maxX - minX) || (storeCount -1) || 1); + } + + if (isNaN(minY)) { + minY = 0; + yScale = bbox.height / ((storeCount - 1) || 1); + } + else { + yScale = bbox.height / ((maxY - minY) || (storeCount - 1) || 1); + } + + + for (i = 0, ln = data.length; i < ln; i++) { + record = data[i]; + xValue = record.get(me.xField); + if (xAxisType === 'Time' && typeof xValue === "string") { + xValue = Date.parse(xValue); + } + + if (typeof xValue === 'string' || typeof xValue === 'object' && !Ext.isDate(xValue) + + || xAxisType === 'Category') { + xValue = i; + } + + + yValue = record.get(me.yField); + + if (yAxisType === 'Time' && typeof yValue === "string") { + yValue = Date.parse(yValue); + } + + + if (typeof yValue === 'undefined' || (typeof yValue === 'string' && !yValue)) { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn("[Ext.chart.series.Line] Skipping a store element with an undefined value at ", record, xValue, yValue); + } + continue; + } + + + if (typeof yValue === 'string' || typeof yValue === 'object' && !Ext.isDate(yValue) + + || yAxisType === 'Category') { + yValue = i; + } + storeIndices.push(i); + xValues.push(xValue); + yValues.push(yValue); + } + + ln = xValues.length; + if (ln > bbox.width) { + coords = me.shrink(xValues, yValues, bbox.width); + xValues = coords.x; + yValues = coords.y; + } + + me.items = []; + + count = 0; + ln = xValues.length; + for (i = 0; i < ln; i++) { + xValue = xValues[i]; + yValue = yValues[i]; + if (yValue === false) { + if (path.length == 1) { + path = []; + } + onbreak = true; + me.items.push(false); + continue; + } else { + if (reverse) { + x = bbox.x + bbox.width - ((xValue - minX) * xScale); + } else { + x = (bbox.x + (xValue - minX) * xScale); + } + x = Ext.Number.toFixed(x, 2); + y = Ext.Number.toFixed((bbox.y + bbox.height) - (yValue - minY) * yScale, 2); + if (onbreak) { + onbreak = false; + path.push('M'); + } + path = path.concat([x, y]); + } + if ((typeof firstY == 'undefined') && (typeof y != 'undefined')) { + firstY = y; + firstX = x; + } + + if (!me.line || chart.resizing) { + dummyPath = dummyPath.concat([x, bbox.y + bbox.height / 2]); + } + + + if (chart.animate && chart.resizing && me.line) { + me.line.setAttributes({ + path: dummyPath, + opacity: lineOpacity + }, true); + if (me.fillPath) { + me.fillPath.setAttributes({ + path: dummyPath, + opacity: fillOpacity + }, true); + } + if (me.line.shadows) { + shadows = me.line.shadows; + for (j = 0, lnsh = shadows.length; j < lnsh; j++) { + shadow = shadows[j]; + shadow.setAttributes({ + path: dummyPath + }, true); + } + } + } + if (showMarkers) { + marker = markerGroup.getAt(count++); + if (!marker) { + marker = Ext.chart.Shape[type](surface, Ext.apply({ + group: [group, markerGroup], + x: 0, y: 0, + translate: { + x: +(prevX || x), + y: prevY || (bbox.y + bbox.height / 2) + }, + value: '"' + xValue + ', ' + yValue + '"', + zIndex: 4000 + }, endMarkerStyle)); + marker._to = { + translate: { + x: +x, + y: +y + } + }; + } else { + marker.setAttributes({ + value: '"' + xValue + ', ' + yValue + '"', + x: 0, y: 0, + hidden: false + }, true); + marker._to = { + translate: { + x: +x, + y: +y + } + }; + } + } + + me.items.push({ + series: me, + value: [xValue, yValue], + point: [x, y], + sprite: marker, + storeItem: store.getAt(storeIndices[i]) + }); + prevX = x; + prevY = y; + } + + if (path.length <= 1) { + + return; + } + + if (me.smooth) { + smoothPath = Ext.draw.Draw.smooth(path, isNumber(smooth) ? smooth : me.defaultSmoothness); + } + + renderPath = smooth ? smoothPath : path; + + + if (chart.markerIndex && me.previousPath) { + fromPath = me.previousPath; + if (!smooth) { + Ext.Array.erase(fromPath, 1, 2); + } + } else { + fromPath = path; + } + + + if (!me.line) { + me.line = surface.add(Ext.apply({ + type: 'path', + group: group, + path: dummyPath, + stroke: endLineStyle.stroke || endLineStyle.fill + }, endLineStyle || {})); + me + + + me.line.setAttributes({ + opacity: lineOpacity + }, true); + + if (enableShadows) { + me.line.setAttributes(Ext.apply({}, me.shadowOptions), true); + } + + + me.line.setAttributes({ + fill: 'none', + zIndex: 3000 + }); + if (!endLineStyle.stroke && colorArrayLength) { + me.line.setAttributes({ + stroke: colorArrayStyle[me.themeIdx % colorArrayLength] + }, true); + } + if (enableShadows) { + + shadows = me.line.shadows = []; + for (shindex = 0; shindex < lnsh; shindex++) { + shadowBarAttr = shadowAttributes[shindex]; + shadowBarAttr = Ext.apply({}, shadowBarAttr, { path: dummyPath }); + shadow = surface.add(Ext.apply({}, { + type: 'path', + group: shadowGroups[shindex] + }, shadowBarAttr)); + shadows.push(shadow); + } + } + } + if (me.fill) { + fillPath = renderPath.concat([ + ["L", x, bbox.y + bbox.height], + ["L", firstX, bbox.y + bbox.height], + ["L", firstX, firstY] + ]); + if (!me.fillPath) { + me.fillPath = surface.add({ + group: group, + type: 'path', + fill: endLineStyle.fill || colorArrayStyle[me.themeIdx % colorArrayLength], + path: dummyPath + }); + } + } + markerCount = showMarkers && markerGroup.getCount(); + if (chart.animate) { + fill = me.fill; + line = me.line; + + rendererAttributes = me.renderer(line, false, { path: renderPath }, i, store); + Ext.apply(rendererAttributes, endLineStyle || {}, { + stroke: endLineStyle.stroke || endLineStyle.fill + }); + + delete rendererAttributes.fill; + line.show(true); + if (chart.markerIndex && me.previousPath) { + me.animation = animation = me.onAnimate(line, { + to: rendererAttributes, + from: { + path: fromPath + } + }); + } else { + me.animation = animation = me.onAnimate(line, { + to: rendererAttributes + }); + } + + if (enableShadows) { + shadows = line.shadows; + for(j = 0; j < lnsh; j++) { + shadows[j].show(true); + if (chart.markerIndex && me.previousPath) { + me.onAnimate(shadows[j], { + to: { path: renderPath }, + from: { path: fromPath } + }); + } else { + me.onAnimate(shadows[j], { + to: { path: renderPath } + }); + } + } + } + + if (fill) { + me.fillPath.show(true); + me.onAnimate(me.fillPath, { + to: Ext.apply({}, { + path: fillPath, + fill: endLineStyle.fill || colorArrayStyle[me.themeIdx % colorArrayLength], + 'stroke-width': 0, + opacity: fillOpacity + }, endLineStyle || {}) + }); + } + + if (showMarkers) { + count = 0; + for(i = 0; i < ln; i++) { + if (me.items[i]) { + item = markerGroup.getAt(count++); + if (item) { + rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store); + me.onAnimate(item, { + to: Ext.applyIf(rendererAttributes, endMarkerStyle || {}) + }); + item.show(true); + } + } + } + for(; count < markerCount; count++) { + item = markerGroup.getAt(count); + item.hide(true); + } + + + + + } + } else { + rendererAttributes = me.renderer(me.line, false, { path: renderPath, hidden: false }, i, store); + Ext.apply(rendererAttributes, endLineStyle || {}, { + stroke: endLineStyle.stroke || endLineStyle.fill + }); + + delete rendererAttributes.fill; + me.line.setAttributes(rendererAttributes, true); + me.line.setAttributes({ + opacity: lineOpacity + }, true); + + if (enableShadows) { + shadows = me.line.shadows; + for(j = 0; j < lnsh; j++) { + shadows[j].setAttributes({ + path: renderPath, + hidden: false + }, true); + } + } + if (me.fill) { + me.fillPath.setAttributes({ + path: fillPath, + hidden: false, + opacity: fillOpacity + }, true); + } + if (showMarkers) { + count = 0; + for(i = 0; i < ln; i++) { + if (me.items[i]) { + item = markerGroup.getAt(count++); + if (item) { + rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store); + item.setAttributes(Ext.apply(endMarkerStyle || {}, rendererAttributes || {}), true); + if (!item.attr.hidden) { + item.show(true); + } + } + } + } + for(; count < markerCount; count++) { + item = markerGroup.getAt(count); + item.hide(true); + } + } + } + + if (chart.markerIndex) { + if (me.smooth) { + Ext.Array.erase(path, 1, 2); + } else { + Ext.Array.splice(path, 1, 0, path[1], path[2]); + } + me.previousPath = path; + } + me.renderLabels(); + me.renderCallouts(); + + me.fireEvent('draw', me); + }, + + + onCreateLabel: function(storeItem, item, i, display) { + var me = this, + group = me.labelsGroup, + config = me.label, + bbox = me.bbox, + endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {}); + + return me.chart.surface.add(Ext.apply({ + 'type': 'text', + 'text-anchor': 'middle', + 'group': group, + 'x': Number(item.point[0]), + 'y': bbox.y + bbox.height / 2 + }, endLabelStyle || {})); + }, + + + onPlaceLabel: function(label, storeItem, item, i, display, animate, index) { + var me = this, + chart = me.chart, + resizing = chart.resizing, + config = me.label, + format = config.renderer, + field = config.field, + bbox = me.bbox, + x = Number(item.point[0]), + y = Number(item.point[1]), + radius = item.sprite.attr.radius, + labelBox, markerBox, width, height, xOffset, yOffset; + + label.setAttributes({ + text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index), + hidden: true + }, true); + + + markerBox = item.sprite.getBBox(); + markerBox.width = markerBox.width || (radius * 2); + markerBox.height = markerBox.height || (radius * 2); + + labelBox = label.getBBox(); + width = labelBox.width/2; + height = labelBox.height/2; + + if (display == 'rotate') { + + xOffset = markerBox.width/2 + width + height/2; + if (x + xOffset + width > bbox.x + bbox.width) { + x -= xOffset; + } else { + x += xOffset; + } + label.setAttributes({ + 'rotation': { + x: x, + y: y, + degrees: -45 + } + }, true); + } else if (display == 'under' || display == 'over') { + label.setAttributes({ + 'rotation': { + degrees: 0 + } + }, true); + + + if (x < bbox.x + width) { + x = bbox.x + width; + } else if (x + width > bbox.x + bbox.width) { + x = bbox.x + bbox.width - width; + } + + yOffset = markerBox.height/2 + height; + y = y + (display == 'over' ? -yOffset : yOffset); + if (y < bbox.y + height) { + y += 2 * yOffset; + } else if (y + height > bbox.y + bbox.height) { + y -= 2 * yOffset; + } + } + + if (me.chart.animate && !me.chart.resizing) { + label.show(true); + me.onAnimate(label, { + to: { + x: x, + y: y + } + }); + } else { + label.setAttributes({ + x: x, + y: y + }, true); + if (resizing && chart.animate) { + me.on({ + single: true, + afterrender: function() { + label.show(true); + } + }); + } else { + label.show(true); + } + } + }, + + + highlightItem: function() { + var me = this, + line = me.line; + + me.callParent(arguments); + if (line && !me.highlighted) { + if (!('__strokeWidth' in line)) { + line.__strokeWidth = parseFloat(line.attr['stroke-width']) || 0; + } + if (line.__anim) { + line.__anim.paused = true; + } + + line.__anim = new Ext.fx.Anim({ + target: line, + to: { + 'stroke-width': line.__strokeWidth + 3 + } + }); + me.highlighted = true; + } + }, + + + unHighlightItem: function() { + var me = this, + line = me.line, + width; + + me.callParent(arguments); + if (line && me.highlighted) { + width = line.__strokeWidth || parseFloat(line.attr['stroke-width']) || 0; + line.__anim = new Ext.fx.Anim({ + target: line, + to: { + 'stroke-width': width + } + }); + me.highlighted = false; + } + }, + + + onPlaceCallout : function(callout, storeItem, item, i, display, animate, index) { + if (!display) { + return; + } + + var me = this, + chart = me.chart, + surface = chart.surface, + resizing = chart.resizing, + config = me.callouts, + items = me.items, + prev = i == 0? false : items[i -1].point, + next = (i == items.length -1)? false : items[i +1].point, + cur = [+item.point[0], +item.point[1]], + dir, norm, normal, a, aprev, anext, + offsetFromViz = config.offsetFromViz || 30, + offsetToSide = config.offsetToSide || 10, + offsetBox = config.offsetBox || 3, + boxx, boxy, boxw, boxh, + p, clipRect = me.clipRect, + bbox = { + width: config.styles.width || 10, + height: config.styles.height || 10 + }, + x, y; + + + if (!prev) { + prev = cur; + } + if (!next) { + next = cur; + } + a = (next[1] - prev[1]) / (next[0] - prev[0]); + aprev = (cur[1] - prev[1]) / (cur[0] - prev[0]); + anext = (next[1] - cur[1]) / (next[0] - cur[0]); + + norm = Math.sqrt(1 + a * a); + dir = [1 / norm, a / norm]; + normal = [-dir[1], dir[0]]; + + + if (aprev > 0 && anext < 0 && normal[1] < 0 + || aprev < 0 && anext > 0 && normal[1] > 0) { + normal[0] *= -1; + normal[1] *= -1; + } else if (Math.abs(aprev) < Math.abs(anext) && normal[0] < 0 + || Math.abs(aprev) > Math.abs(anext) && normal[0] > 0) { + normal[0] *= -1; + normal[1] *= -1; + } + + x = cur[0] + normal[0] * offsetFromViz; + y = cur[1] + normal[1] * offsetFromViz; + + + boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox)); + boxy = y - bbox.height /2 - offsetBox; + boxw = bbox.width + 2 * offsetBox; + boxh = bbox.height + 2 * offsetBox; + + + + if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) { + normal[0] *= -1; + } + if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) { + normal[1] *= -1; + } + + + x = cur[0] + normal[0] * offsetFromViz; + y = cur[1] + normal[1] * offsetFromViz; + + + boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox)); + boxy = y - bbox.height /2 - offsetBox; + boxw = bbox.width + 2 * offsetBox; + boxh = bbox.height + 2 * offsetBox; + + if (chart.animate) { + + me.onAnimate(callout.lines, { + to: { + path: ["M", cur[0], cur[1], "L", x, y, "Z"] + } + }); + + if (callout.panel) { + callout.panel.setPosition(boxx, boxy, true); + } + } + else { + + callout.lines.setAttributes({ + path: ["M", cur[0], cur[1], "L", x, y, "Z"] + }, true); + + if (callout.panel) { + callout.panel.setPosition(boxx, boxy); + } + } + for (p in callout) { + callout[p].show(true); + } + }, + + isItemInPoint: function(x, y, item, i) { + var me = this, + items = me.items, + tolerance = me.selectionTolerance, + result = null, + prevItem, + nextItem, + prevPoint, + nextPoint, + ln, + x1, + y1, + x2, + y2, + xIntersect, + yIntersect, + dist1, dist2, dist, midx, midy, + sqrt = Math.sqrt, abs = Math.abs; + + nextItem = items[i]; + prevItem = i && items[i - 1]; + + if (i >= ln) { + prevItem = items[ln - 1]; + } + prevPoint = prevItem && prevItem.point; + nextPoint = nextItem && nextItem.point; + x1 = prevItem ? prevPoint[0] : nextPoint[0] - tolerance; + y1 = prevItem ? prevPoint[1] : nextPoint[1]; + x2 = nextItem ? nextPoint[0] : prevPoint[0] + tolerance; + y2 = nextItem ? nextPoint[1] : prevPoint[1]; + dist1 = sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1)); + dist2 = sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2)); + dist = Math.min(dist1, dist2); + + if (dist <= tolerance) { + return dist == dist1? prevItem : nextItem; + } + return false; + }, + + + toggleAll: function(show) { + var me = this, + i, ln, shadow, shadows; + if (!show) { + Ext.chart.series.Cartesian.prototype.hideAll.call(me); + } + else { + Ext.chart.series.Cartesian.prototype.showAll.call(me); + } + if (me.line) { + me.line.setAttributes({ + hidden: !show + }, true); + + if (me.line.shadows) { + for (i = 0, shadows = me.line.shadows, ln = shadows.length; i < ln; i++) { + shadow = shadows[i]; + shadow.setAttributes({ + hidden: !show + }, true); + } + } + } + if (me.fillPath) { + me.fillPath.setAttributes({ + hidden: !show + }, true); + } + }, + + + hideAll: function() { + this.toggleAll(false); + }, + + + showAll: function() { + this.toggleAll(true); + } +}); + + + +Ext.define('Ext.chart.series.Pie', { + + + + alternateClassName: ['Ext.chart.PieSeries', 'Ext.chart.PieChart'], + + extend: Ext.chart.series.Series , + + + + type: "pie", + + alias: 'series.pie', + + accuracy: 100000, + + rad: Math.PI * 2 / 100000, + + + highlightDuration: 150, + + + angleField: false, + + + + + + + lengthField: false, + + + donut: false, + + + showInLegend: false, + + + + + style: {}, + + + clockwise: false, + + + rotation: undefined, + + + constructor: function(config) { + this.callParent(arguments); + var me = this, + chart = me.chart, + surface = chart.surface, + store = chart.store, + shadow = chart.shadow, + highlight = config.highlight, + i, l, cfg; + + if (highlight) { + config.highlightCfg = Ext.merge({ + segment: { + margin: 20 + } + }, highlight, config.highlightCfg); + } + + Ext.apply(me, config, { + shadowAttributes: [{ + "stroke-width": 6, + "stroke-opacity": 1, + stroke: 'rgb(200, 200, 200)', + translate: { + x: 1.2, + y: 2 + } + }, + { + "stroke-width": 4, + "stroke-opacity": 1, + stroke: 'rgb(150, 150, 150)', + translate: { + x: 0.9, + y: 1.5 + } + }, + { + "stroke-width": 2, + "stroke-opacity": 1, + stroke: 'rgb(100, 100, 100)', + translate: { + x: 0.6, + y: 1 + } + }] + }); + me.group = surface.getGroup(me.seriesId); + if (shadow) { + for (i = 0, l = me.shadowAttributes.length; i < l; i++) { + me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i)); + } + } + surface.customAttributes.segment = function(opt) { + + + + var ans = me.getSegment(opt); + if (!ans.path || ans.path.length === 0) { + ans.path = ['M', 0, 0]; + } + return ans; + }; + me.__excludes = me.__excludes || []; + }, + + onRedraw: function(){ + this.initialize(); + }, + + + initialize: function() { + var me = this, + store = me.chart.getChartStore(), + data = store.data.items, + i, ln, rec; + + me.callParent(); + + me.yField = []; + if (me.label.field) { + for (i = 0, ln = data.length; i < ln; i++) { + rec = data[i]; + me.yField.push(rec.get(me.label.field)); + } + } + }, + + + getSegment: function(opt) { + var me = this, + rad = me.rad, + cos = Math.cos, + sin = Math.sin, + x = me.centerX, + y = me.centerY, + x1 = 0, x2 = 0, x3 = 0, x4 = 0, + y1 = 0, y2 = 0, y3 = 0, y4 = 0, + x5 = 0, y5 = 0, x6 = 0, y6 = 0, + delta = 1e-2, + startAngle = opt.startAngle, + endAngle = opt.endAngle, + midAngle = (startAngle + endAngle) / 2 * rad, + margin = opt.margin || 0, + a1 = Math.min(startAngle, endAngle) * rad, + a2 = Math.max(startAngle, endAngle) * rad, + c1 = cos(a1), s1 = sin(a1), + c2 = cos(a2), s2 = sin(a2), + cm = cos(midAngle), sm = sin(midAngle), + flag = 0, hsqr2 = 0.7071067811865476; + + if (a2 - a1 < delta) { + return {path: ""}; + } + + if (margin !== 0) { + x += margin * cm; + y += margin * sm; + } + + x2 = x + opt.endRho * c1; + y2 = y + opt.endRho * s1; + + x4 = x + opt.endRho * c2; + y4 = y + opt.endRho * s2; + + x6 = x + opt.endRho * cm; + y6 = y + opt.endRho * sm; + + if (opt.startRho !== 0) { + x1 = x + opt.startRho * c1; + y1 = y + opt.startRho * s1; + + x3 = x + opt.startRho * c2; + y3 = y + opt.startRho * s2; + + x5 = x + opt.startRho * cm; + y5 = y + opt.startRho * sm; + + return { + path: [ + ["M", x2, y2], + ["A", opt.endRho, opt.endRho, 0, 0, 1, x6, y6], ["L", x6, y6], + ["A", opt.endRho, opt.endRho, 0, flag, 1, x4, y4], ["L", x4, y4], + ["L", x3, y3], + ["A", opt.startRho, opt.startRho, 0, flag, 0, x5, y5], ["L", x5, y5], + ["A", opt.startRho, opt.startRho, 0, 0, 0, x1, y1], ["L", x1, y1], + ["Z"] + ] + }; + } else { + return { + path: [ + ["M", x, y], + ["L", x2, y2], + ["A", opt.endRho, opt.endRho, 0, 0, 1, x6, y6], ["L", x6, y6], + ["A", opt.endRho, opt.endRho, 0, flag, 1, x4, y4], ["L", x4, y4], + ["L", x, y], + ["Z"] + ] + }; + } + }, + + + calcMiddle: function(item) { + var me = this, + rad = me.rad, + slice = item.slice, + x = me.centerX, + y = me.centerY, + startAngle = slice.startAngle, + endAngle = slice.endAngle, + donut = +me.donut, + midAngle = -(startAngle + endAngle) * rad / 2, + r = (item.endRho + item.startRho) / 2, + xm = x + r * Math.cos(midAngle), + ym = y - r * Math.sin(midAngle); + + item.middle = { + x: xm, + y: ym + }; + }, + + + drawSeries: function() { + var me = this, + store = me.chart.getChartStore(), + data = store.data.items, + record, + group = me.group, + animate = me.chart.animate, + field = me.angleField || me.field || me.xField, + lenField = [].concat(me.lengthField), + totalLenField = 0, + chart = me.chart, + surface = chart.surface, + chartBBox = chart.chartBBox, + enableShadows = chart.shadow, + shadowGroups = me.shadowGroups, + shadowAttributes = me.shadowAttributes, + lnsh = shadowGroups.length, + layers = lenField.length, + rhoAcum = 0, + donut = +me.donut, + layerTotals = [], + items = [], + totalField = 0, + maxLenField = 0, + angle = 0, + rotation = me.rotation, + seriesStyle = me.seriesStyle, + colorArrayStyle = me.colorArrayStyle, + colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0, + rendererAttributes, + shadowAttr, + shadows, + shadow, + shindex, + centerX, + centerY, + deltaRho, + first = 0, + slice, + slices, + sprite, + value, + item, + lenValue, + ln, + i, + j, + endAngle, + path, + p, + spriteOptions, bbox; + + Ext.apply(seriesStyle, me.style || {}); + + me.setBBox(); + bbox = me.bbox; + + + if (me.colorSet) { + colorArrayStyle = me.colorSet; + colorArrayLength = colorArrayStyle.length; + } + + + if (!store || !store.getCount() || me.seriesIsHidden) { + me.hide(); + me.items = []; + return; + } + + me.unHighlightItem(); + me.cleanHighlights(); + + centerX = me.centerX = chartBBox.x + (chartBBox.width / 2); + centerY = me.centerY = chartBBox.y + (chartBBox.height / 2); + me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y); + me.slices = slices = []; + me.items = items = []; + + for (i = 0, ln = data.length; i < ln; i++) { + record = data[i]; + if (this.__excludes && this.__excludes[i]) { + + continue; + } + totalField += +record.get(field); + if (lenField[0]) { + for (j = 0, totalLenField = 0; j < layers; j++) { + totalLenField += +record.get(lenField[j]); + } + layerTotals[i] = totalLenField; + maxLenField = Math.max(maxLenField, totalLenField); + } + } + + totalField = totalField || 1; + for (i = 0, ln = data.length; i < ln; i++) { + record = data[i]; + if (this.__excludes && this.__excludes[i]) { + value = 0; + } else { + value = record.get(field); + if (first === 0) { + first = 1; + } + } + + + if (first == 1) { + first = 2; + + if (Ext.isEmpty(rotation)) { + me.firstAngle = angle = (me.clockwise ? -1 : 1) * (me.accuracy * value / totalField / 2); + } else { + if (!Ext.isEmpty(rotation.degrees)) { + rotation = Ext.draw.Draw.rad(rotation.degrees); + } else if (!Ext.isEmpty(rotation.radians)) { + rotation = rotation.radians; + } + me.firstAngle = angle = me.accuracy * rotation / (2 * Math.PI); + } + + for (j = 0; j < i; j++) { + slices[j].startAngle = slices[j].endAngle = me.firstAngle; + } + } + + endAngle = angle + (me.clockwise ? 1 : -1) * (me.accuracy * value / totalField); + slice = { + series: me, + value: value, + startAngle: (me.clockwise ? endAngle : angle), + endAngle: (me.clockwise ? angle : endAngle), + storeItem: record + }; + if (lenField[0] && !(this.__excludes && this.__excludes[i])) { + lenValue = +layerTotals[i]; + + slice.rho = Math.floor(me.radius / maxLenField * lenValue); + } else { + slice.rho = me.radius; + } + slices[i] = slice; + + (function () { + angle = endAngle; + })(); + } + + + if (enableShadows) { + for (i = 0, ln = slices.length; i < ln; i++) { + slice = slices[i]; + slice.shadowAttrs = []; + record = store.getAt(i); + + for (j = 0, rhoAcum = 0, shadows = []; j < layers; j++) { + sprite = group.getAt(i * layers + j); + + if (lenField[j] && !(this.__excludes && this.__excludes[i])) { + deltaRho = record.get(lenField[j]) / layerTotals[i] * slice.rho; + } + else { + deltaRho = slice.rho; + } + + + rendererAttributes = { + segment: { + startAngle: slice.startAngle, + endAngle: slice.endAngle, + margin: 0, + rho: slice.rho, + startRho: rhoAcum + (deltaRho * donut / 100), + endRho: rhoAcum + deltaRho + }, + hidden: !slice.value && (slice.startAngle % me.accuracy) == (slice.endAngle % me.accuracy) + }; + + for (shindex = 0, shadows = []; shindex < lnsh; shindex++) { + shadowAttr = shadowAttributes[shindex]; + shadow = shadowGroups[shindex].getAt(i); + if (!shadow) { + shadow = chart.surface.add(Ext.apply({}, { + type: 'path', + group: shadowGroups[shindex], + strokeLinejoin: "round" + }, rendererAttributes, shadowAttr)); + } + shadowAttr = me.renderer(shadow, record, Ext.apply({}, rendererAttributes, shadowAttr), i, store); + if (animate) { + me.onAnimate(shadow, { + to: shadowAttr + }); + } else { + shadow.setAttributes(shadowAttr, true); + } + shadows.push(shadow); + } + slice.shadowAttrs[j] = shadows; + } + } + } + + for (i = 0, ln = slices.length; i < ln; i++) { + slice = slices[i]; + record = store.getAt(i); + + for (j = 0, rhoAcum = 0; j < layers; j++) { + sprite = group.getAt(i * layers + j); + + if (lenField[j] && !(this.__excludes && this.__excludes[i])) { + deltaRho = record.get(lenField[j]) / layerTotals[i] * slice.rho; + } + else { + deltaRho = slice.rho; + } + + + rendererAttributes = Ext.apply({ + segment: { + startAngle: slice.startAngle, + endAngle: slice.endAngle, + margin: 0, + rho: slice.rho, + startRho: rhoAcum + (deltaRho * donut / 100), + endRho: rhoAcum + deltaRho + }, + hidden: (!slice.value && (slice.startAngle % me.accuracy) == (slice.endAngle % me.accuracy)) + }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[(layers > 1? j : i) % colorArrayLength] } || {})); + item = Ext.apply({}, + rendererAttributes.segment, { + slice: slice, + series: me, + storeItem: slice.storeItem, + index: i + }); + me.calcMiddle(item); + if (enableShadows) { + item.shadows = slice.shadowAttrs[j]; + } + items[i] = item; + + if (!sprite) { + spriteOptions = Ext.apply({ + type: "path", + group: group, + middle: item.middle + }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[(layers > 1? j : i) % colorArrayLength] } || {})); + sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes)); + } + slice.sprite = slice.sprite || []; + item.sprite = sprite; + slice.sprite.push(sprite); + slice.point = [item.middle.x, item.middle.y]; + if (animate) { + rendererAttributes = me.renderer(sprite, record, rendererAttributes, i, store); + sprite._to = rendererAttributes; + sprite._animating = true; + me.onAnimate(sprite, { + to: rendererAttributes, + listeners: { + afteranimate: { + fn: function() { + this._animating = false; + }, + scope: sprite + } + } + }); + } else { + rendererAttributes = me.renderer(sprite, record, Ext.apply(rendererAttributes, { + hidden: false + }), i, store); + sprite.setAttributes(rendererAttributes, true); + } + rhoAcum += deltaRho; + } + } + + + ln = group.getCount(); + for (i = 0; i < ln; i++) { + if (!slices[(i / layers) >> 0] && group.getAt(i)) { + group.getAt(i).hide(true); + } + } + if (enableShadows) { + lnsh = shadowGroups.length; + for (shindex = 0; shindex < ln; shindex++) { + if (!slices[(shindex / layers) >> 0]) { + for (j = 0; j < lnsh; j++) { + if (shadowGroups[j].getAt(shindex)) { + shadowGroups[j].getAt(shindex).hide(true); + } + } + } + } + } + me.renderLabels(); + me.renderCallouts(); + }, + + setSpriteAttributes: function(sprite, attrs, animate) { + var me = this; + if (animate) { + sprite.stopAnimation(); + sprite.animate({ + to: attrs, + duration: me.highlightDuration + }); + } + else { + sprite.setAttributes(attrs, true); + } + }, + + createLabelLine: function(i, hidden) { + var me = this; + calloutLine = me.label.calloutLine, + line = me.chart.surface.add({ + type: 'path', + stroke: (i === undefined ? '#555' : ((calloutLine && calloutLine.color) || me.getLegendColor(i))), + lineWidth: (calloutLine && calloutLine.width) || 2, + path: 'M0,0Z', + hidden: hidden + }); + return line; + }, + + drawLabelLine: function(label, from, to, animate) { + var me = this, + line = label.lineSprite, + path = 'M' + from.x + ' ' + from.y + 'L' + to.x + ' ' + to.y + 'Z'; + + me.setSpriteAttributes(line, { + 'path': path + }, animate); + }, + + + onCreateLabel: function(storeItem, item, i, display) { + var me = this, + group = me.labelsGroup, + config = me.label, + centerX = me.centerX, + centerY = me.centerY, + middle = item.middle, + endLabelStyle = Ext.apply(me.seriesLabelStyle || {}, config || {}); + + return me.chart.surface.add(Ext.apply({ + 'type': 'text', + 'text-anchor': 'middle', + 'group': group, + 'x': middle.x, + 'y': middle.y + }, endLabelStyle)); + }, + + + onPlaceLabel: function(label, storeItem, item, i, display, animate, index) { + var me = this, + rad = me.rad, + chart = me.chart, + resizing = chart.resizing, + config = me.label, + format = config.renderer, + field = config.field, + centerX = me.centerX, + centerY = me.centerY, + startAngle = item.startAngle, + endAngle = item.endAngle, + middle = item.middle, + opt = { + x: middle.x, + y: middle.y + }, + x = middle.x - centerX, + y = middle.y - centerY, + from = {}, + rho = 1, + theta = Math.atan2(y, x || 1), + dg = Ext.draw.Draw.degrees(theta), + prevDg, labelBox, width, height, + isOutside = (display === 'outside'), + calloutLine = label.attr.calloutLine, + lineWidth = (calloutLine && calloutLine.width) || 2, + labelPadding = (label.attr.padding || 20) + (isOutside ? lineWidth/2 + 4 : 0), + labelPaddingX = 0, labelPaddingY = 0, + a1, a2, seg; + + opt.hidden = false; + + if (this.__excludes && this.__excludes[i]) { + opt.hidden = true; + } + + if (config.hideLessThan) { + a1 = Math.min(startAngle, endAngle) * rad; + a2 = Math.max(startAngle, endAngle) * rad; + seg = (a2 - a1) * item.rho; + + if (seg < config.hideLessThan) { + opt.hidden = label.showOnHighlight = true; + } + } + + label.setAttributes({ + opacity: (opt.hidden ? 0 : 1), + text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index) + }, true); + + if (label.lineSprite) { + var attrs = { opacity: (opt.hidden ? 0 : 1) }; + if (opt.hidden) { + attrs.translate = {x:0, y:0}; + } + me.setSpriteAttributes(label.lineSprite, attrs, false); + } + + switch (display) { + case 'outside': + label.isOutside = true; + + + rho = item.endRho; + + + labelPaddingX = (Math.abs(dg) <= 90 ? labelPadding : -labelPadding); + labelPaddingY = (dg >= 0 ? labelPadding : -labelPadding); + + + label.setAttributes({rotation:{degrees: 0}}, true); + labelBox = label.getBBox(); + width = labelBox.width/2 * Math.cos(theta); + height = labelBox.height/2 * Math.sin(theta); + width += labelPaddingX; + height += labelPaddingY; + + rho += Math.sqrt(width*width + height*height); + + + opt.x = rho * Math.cos(theta) + centerX; + opt.y = rho * Math.sin(theta) + centerY; + break; + + case 'rotate': + dg = Ext.draw.Draw.normalizeDegrees(dg); + dg = (dg > 90 && dg < 270) ? dg + 180: dg; + + prevDg = label.attr.rotation.degrees; + if (prevDg != null && Math.abs(prevDg - dg) > 180 * 0.5) { + if (dg > prevDg) { + dg -= 360; + } else { + dg += 360; + } + dg = dg % 360; + } else { + dg = Ext.draw.Draw.normalizeDegrees(dg); + } + + opt.rotate = { + degrees: dg, + x: opt.x, + y: opt.y + }; + break; + + default: + break; + } + + + opt.translate = { + x: 0, y: 0 + }; + if (animate && !resizing && (display != 'rotate' || prevDg != null)) { + me.onAnimate(label, { + to: opt + }); + } else { + label.setAttributes(opt, true); + } + label._from = from; + + + if (label.isOutside && calloutLine) { + var line = label.lineSprite, + animateLine = animate, + fromPoint = { + + x: (item.endRho - lineWidth/2) * Math.cos(theta) + centerX, + y: (item.endRho - lineWidth/2) * Math.sin(theta) + centerY + }, + labelCenter = { + + x: opt.x, + y: opt.y + }, + toPoint = {}; + + function sign(x) { + return x ? x < 0 ? -1 : 1 : 0; + } + + if (calloutLine && calloutLine.length) { + toPoint = { + x: (item.endRho + calloutLine.length) * Math.cos(theta) + centerX, + y: (item.endRho + calloutLine.length) * Math.sin(theta) + centerY + } + } else { + + + + + + + var normalTheta = Ext.draw.Draw.normalizeRadians(-theta), + cos = Math.cos(normalTheta), + sin = Math.sin(normalTheta), + labelWidth = (labelBox.width + lineWidth + 4)/2, + labelHeight = (labelBox.height + lineWidth + 4)/2; + + if (Math.abs(cos) * labelHeight > Math.abs(sin) * labelWidth) { + + toPoint.x = labelCenter.x - labelWidth * sign(cos); + toPoint.y = labelCenter.y + labelWidth * sin/cos * sign(cos); + } else { + + toPoint.x = labelCenter.x - labelHeight * cos/sin * sign(sin); + toPoint.y = labelCenter.y + labelHeight * sign(sin); + } + } + + if (!line) { + line = label.lineSprite = me.createLabelLine(i, opt.hidden); + animateLine = false; + } + me.drawLabelLine(label, fromPoint, toPoint, animateLine); + } else { + delete label.lineSprite; + } + }, + + + onPlaceCallout: function(callout, storeItem, item, i, display, animate, index) { + var me = this, + chart = me.chart, + centerX = me.centerX, + centerY = me.centerY, + middle = item.middle, + opt = { + x: middle.x, + y: middle.y + }, + x = middle.x - centerX, + y = middle.y - centerY, + rho = 1, + rhoCenter, + theta = Math.atan2(y, x || 1), + bbox = (callout && callout.label ? callout.label.getBBox() : {width:0,height:0}), + offsetFromViz = 20, + offsetToSide = 10, + offsetBox = 10, + p; + + if (!bbox.width || !bbox.height) { + return; + } + + + rho = item.endRho + offsetFromViz; + rhoCenter = (item.endRho + item.startRho) / 2 + (item.endRho - item.startRho) / 3; + + opt.x = rho * Math.cos(theta) + centerX; + opt.y = rho * Math.sin(theta) + centerY; + + x = rhoCenter * Math.cos(theta); + y = rhoCenter * Math.sin(theta); + + if (chart.animate) { + + me.onAnimate(callout.lines, { + to: { + path: ["M", x + centerX, y + centerY, "L", opt.x, opt.y, "Z", "M", opt.x, opt.y, "l", x > 0 ? offsetToSide: -offsetToSide, 0, "z"] + } + }); + + me.onAnimate(callout.box, { + to: { + x: opt.x + (x > 0 ? offsetToSide: -(offsetToSide + bbox.width + 2 * offsetBox)), + y: opt.y + (y > 0 ? ( - bbox.height - offsetBox / 2) : ( - bbox.height - offsetBox / 2)), + width: bbox.width + 2 * offsetBox, + height: bbox.height + 2 * offsetBox + } + }); + + me.onAnimate(callout.label, { + to: { + x: opt.x + (x > 0 ? (offsetToSide + offsetBox) : -(offsetToSide + bbox.width + offsetBox)), + y: opt.y + (y > 0 ? -bbox.height / 4: -bbox.height / 4) + } + }); + } else { + + callout.lines.setAttributes({ + path: ["M", x + centerX, y + centerY, "L", opt.x, opt.y, "Z", "M", opt.x, opt.y, "l", x > 0 ? offsetToSide: -offsetToSide, 0, "z"] + }, + true); + + callout.box.setAttributes({ + x: opt.x + (x > 0 ? offsetToSide: -(offsetToSide + bbox.width + 2 * offsetBox)), + y: opt.y + (y > 0 ? ( - bbox.height - offsetBox / 2) : ( - bbox.height - offsetBox / 2)), + width: bbox.width + 2 * offsetBox, + height: bbox.height + 2 * offsetBox + }, + true); + + callout.label.setAttributes({ + x: opt.x + (x > 0 ? (offsetToSide + offsetBox) : -(offsetToSide + bbox.width + offsetBox)), + y: opt.y + (y > 0 ? -bbox.height / 4: -bbox.height / 4) + }, + true); + } + for (p in callout) { + callout[p].show(true); + } + }, + + + onAnimate: function(sprite, attr) { + sprite.show(); + return this.callParent(arguments); + }, + + isItemInPoint: function(x, y, item, i) { + var me = this, + cx = me.centerX, + cy = me.centerY, + abs = Math.abs, + dx = abs(x - cx), + dy = abs(y - cy), + startAngle = item.startAngle, + endAngle = item.endAngle, + rho = Math.sqrt(dx * dx + dy * dy), + angle = Math.atan2(y - cy, x - cx) / me.rad; + + + if (me.clockwise) { + if (angle < me.firstAngle) { + angle += me.accuracy; + } + } else { + if (angle > me.firstAngle) { + angle -= me.accuracy; + } + } + return (angle <= startAngle && angle > endAngle + && rho >= item.startRho && rho <= item.endRho); + }, + + + hideAll: function(index) { + var i, l, shadow, shadows, sh, lsh, sprite; + index = (isNaN(this._index) ? index : this._index) || 0; + this.__excludes = this.__excludes || []; + this.__excludes[index] = true; + sprite = this.slices[index].sprite; + for (sh = 0, lsh = sprite.length; sh < lsh; sh++) { + sprite[sh].setAttributes({ + hidden: true + }, true); + var line = sprite[sh].lineSprite; + if (line) { + line.setAttributes({ + hidden: true + }, true); + } + } + if (this.slices[index].shadowAttrs) { + for (i = 0, shadows = this.slices[index].shadowAttrs, l = shadows.length; i < l; i++) { + shadow = shadows[i]; + for (sh = 0, lsh = shadow.length; sh < lsh; sh++) { + shadow[sh].setAttributes({ + hidden: true + }, true); + } + } + } + this.drawSeries(); + }, + + + showAll: function(index) { + index = (isNaN(this._index) ? index : this._index) || 0; + this.__excludes[index] = false; + this.drawSeries(); + }, + + + highlightItem: function(item) { + var me = this, + rad = me.rad, + highlightSegment, + animate, + attrs, + i, + shadows, + shadow, + ln, + to, + itemHighlightSegment, + prop, + group, + display, + label, + middle, + r, + x, + y, + line; + item = item || this.items[this._index]; + + + + + + this.unHighlightItem(); + + if (!item || me.animating || (item.sprite && item.sprite._animating)) { + return; + } + + me.callParent([item]); + + if (!me.highlight) { + return; + } + + if ('segment' in me.highlightCfg) { + highlightSegment = me.highlightCfg.segment; + animate = me.chart.animate; + + if (me.labelsGroup) { + group = me.labelsGroup; + display = me.label.display; + label = group.getAt(item.index); + middle = (item.startAngle + item.endAngle) / 2 * rad; + r = highlightSegment.margin || 0; + x = r * Math.cos(middle); + y = r * Math.sin(middle); + + + + + + + if (Math.abs(x) < 1e-10) { + x = 0; + } + if (Math.abs(y) < 1e-10) { + y = 0; + } + + attrs = { + translate: { + x: x, + y: y + } + }; + + if (label.showOnHighlight) { + attrs.opacity = 1; + attrs.hidden = false; + } + + me.setSpriteAttributes(label, attrs, animate); + + line = label.lineSprite; + if (line) { + me.setSpriteAttributes(line, attrs, animate); + } + } + + if (me.chart.shadow && item.shadows) { + i = 0; + shadows = item.shadows; + ln = shadows.length; + for (; i < ln; i++) { + shadow = shadows[i]; + to = {}; + itemHighlightSegment = item.sprite._from.segment; + for (prop in itemHighlightSegment) { + if (! (prop in highlightSegment)) { + to[prop] = itemHighlightSegment[prop]; + } + } + attrs = { + segment: Ext.applyIf(to, me.highlightCfg.segment) + }; + me.setSpriteAttributes(shadow, attrs, animate); + } + } + } + }, + + + unHighlightItem: function() { + var me = this, + items, + animate, + shadowsEnabled, + group, + len, + i, + j, + display, + shadowLen, + p, + to, + ihs, + hs, + sprite, + shadows, + shadow, + item, + label, + attrs; + if (!me.highlight) { + return; + } + + if (('segment' in me.highlightCfg) && me.items) { + items = me.items; + animate = me.chart.animate; + shadowsEnabled = !!me.chart.shadow; + group = me.labelsGroup; + len = items.length; + i = 0; + j = 0; + display = me.label.display; + + for (; i < len; i++) { + item = items[i]; + if (!item) { + continue; + } + sprite = item.sprite; + if (sprite && sprite._highlighted) { + + if (group) { + label = group.getAt(item.index); + attrs = Ext.apply({ + translate: { + x: 0, + y: 0 + } + }, + display == 'rotate' ? { + rotate: { + x: label.attr.x, + y: label.attr.y, + degrees: label.attr.rotation.degrees + } + }: {}); + + if (label.showOnHighlight) { + attrs.opacity = 0; + attrs.hidden = true; + } + + me.setSpriteAttributes(label, attrs, animate); + + var line = label.lineSprite; + if (line) { + me.setSpriteAttributes(line, attrs, animate); + } + } + if (shadowsEnabled) { + shadows = item.shadows; + shadowLen = shadows.length; + for (; j < shadowLen; j++) { + to = {}; + ihs = item.sprite._to.segment; + hs = item.sprite._from.segment; + Ext.apply(to, hs); + for (p in ihs) { + if (! (p in hs)) { + to[p] = ihs[p]; + } + } + shadow = shadows[j]; + me.setSpriteAttributes(shadow, { segment: to }, animate); + } + } + } + } + } + me.callParent(arguments); + }, + + + getLegendColor: function(index) { + var me = this; + return (me.colorSet && me.colorSet[index % me.colorSet.length]) || me.colorArrayStyle[index % me.colorArrayStyle.length]; + } +}); + + + + +Ext.define('Ext.chart.series.Radar', { + + + + extend: Ext.chart.series.Series , + + + + + + type: "radar", + alias: 'series.radar', + + + rad: Math.PI / 180, + + showInLegend: false, + + + style: {}, + + + + + + + + + + constructor: function(config) { + this.callParent(arguments); + var me = this, + surface = me.chart.surface; + me.group = surface.getGroup(me.seriesId); + if (me.showMarkers) { + me.markerGroup = surface.getGroup(me.seriesId + '-markers'); + } + }, + + + drawSeries: function() { + var me = this, + store = me.chart.getChartStore(), + data = store.data.items, + d, record, + group = me.group, + chart = me.chart, + seriesItems = chart.series.items, + s, sLen, series, + field = me.field || me.yField, + surface = chart.surface, + chartBBox = chart.chartBBox, + colorArrayStyle = me.colorArrayStyle, + centerX, centerY, + items, + radius, + maxValue = 0, + minValue = 0, + fields = [], + max = Math.max, + cos = Math.cos, + sin = Math.sin, + pi2 = Math.PI * 2, + l = store.getCount(), + startPath, path, x, y, rho, + i, nfields, + seriesStyle = me.seriesStyle, + axis = chart.axes && chart.axes.get(0), + aggregate = !(axis && axis.maximum); + + me.setBBox(); + + maxValue = aggregate? 0 : (axis.maximum || 0); + minValue = axis.minimum || 0; + + Ext.apply(seriesStyle, me.style || {}); + + + if (!store || !store.getCount() || me.seriesIsHidden) { + me.hide(); + me.items = []; + if (me.radar) { + me.radar.hide(true); + } + me.radar = null; + return; + } + + if(!seriesStyle['stroke']){ + seriesStyle['stroke'] = colorArrayStyle[me.themeIdx % colorArrayStyle.length]; + } + + me.unHighlightItem(); + me.cleanHighlights(); + + centerX = me.centerX = chartBBox.x + (chartBBox.width / 2); + centerY = me.centerY = chartBBox.y + (chartBBox.height / 2); + me.radius = radius = Math.min(chartBBox.width, chartBBox.height) /2; + me.items = items = []; + + if (aggregate) { + + for (s = 0, sLen = seriesItems.length; s < sLen; s++) { + series = seriesItems[s]; + fields.push(series.yField); + } + + for (d = 0; d < l; d++) { + record = data[d]; + for (i = 0, nfields = fields.length; i < nfields; i++) { + maxValue = max(+record.get(fields[i]), maxValue); + } + } + } + + maxValue = maxValue || 1; + if (minValue >= maxValue) { + minValue = maxValue - 1; + } + + startPath = []; path = []; + for (i = 0; i < l; i++) { + record = data[i]; + rho = radius * (record.get(field) - minValue) / (maxValue - minValue); + if (rho < 0) { + rho = 0; + } + x = rho * cos(i / l * pi2); + y = rho * sin(i / l * pi2); + if (i == 0) { + path.push('M', x + centerX, y + centerY); + startPath.push('M', 0.01 * x + centerX, 0.01 * y + centerY); + } else { + path.push('L', x + centerX, y + centerY); + startPath.push('L', 0.01 * x + centerX, 0.01 * y + centerY); + } + items.push({ + sprite: false, + point: [centerX + x, centerY + y], + storeItem: record, + series: me + }); + } + path.push('Z'); + + if (!me.radar) { + me.radar = surface.add(Ext.apply({ + type: 'path', + group: group, + path: startPath + }, seriesStyle || {})); + } + + if (chart.resizing) { + me.radar.setAttributes({ + path: startPath + }, true); + } + + if (chart.animate) { + me.onAnimate(me.radar, { + to: Ext.apply({ + path: path + }, seriesStyle || {}) + }); + } else { + me.radar.setAttributes(Ext.apply({ + path: path + }, seriesStyle || {}), true); + } + + if (me.showMarkers) { + me.drawMarkers(); + } + me.renderLabels(); + me.renderCallouts(); + }, + + + drawMarkers: function() { + var me = this, + chart = me.chart, + surface = chart.surface, + store = chart.getChartStore(), + markerStyle = Ext.apply({}, me.markerStyle || {}), + endMarkerStyle = Ext.apply(markerStyle, me.markerConfig, { + fill: me.colorArrayStyle[me.themeIdx % me.colorArrayStyle.length] + }), + items = me.items, + type = endMarkerStyle.type, + markerGroup = me.markerGroup, + centerX = me.centerX, + centerY = me.centerY, + item, i, l, marker, rendererAttributes; + + delete endMarkerStyle.type; + + for (i = 0, l = items.length; i < l; i++) { + item = items[i]; + marker = markerGroup.getAt(i); + if (!marker) { + marker = Ext.chart.Shape[type](surface, Ext.apply({ + group: markerGroup, + x: 0, + y: 0, + translate: { + x: centerX, + y: centerY + } + }, endMarkerStyle)); + } + else { + marker.show(); + } + + item.sprite = marker; + + if (chart.resizing) { + marker.setAttributes({ + x: 0, + y: 0, + translate: { + x: centerX, + y: centerY + } + }, true); + } + marker._to = { + translate: { + x: item.point[0], + y: item.point[1] + } + }; + + rendererAttributes = me.renderer(marker, store.getAt(i), marker._to, i, store); + rendererAttributes = Ext.applyIf(rendererAttributes || {}, endMarkerStyle || {}); + if (chart.animate) { + me.onAnimate(marker, { + to: rendererAttributes + }); + } + else { + marker.setAttributes(rendererAttributes, true); + } + } + }, + + isItemInPoint: function(x, y, item) { + var point, + tolerance = 10, + abs = Math.abs; + point = item.point; + return (abs(point[0] - x) <= tolerance && + abs(point[1] - y) <= tolerance); + }, + + + onCreateLabel: function(storeItem, item, i, display) { + var me = this, + group = me.labelsGroup, + config = me.label, + centerX = me.centerX, + centerY = me.centerY, + endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {}); + + return me.chart.surface.add(Ext.apply({ + 'type': 'text', + 'text-anchor': 'middle', + 'group': group, + 'x': centerX, + 'y': centerY + }, endLabelStyle || {})); + }, + + + onPlaceLabel: function(label, storeItem, item, i, display, animate, index) { + var me = this, + chart = me.chart, + resizing = chart.resizing, + config = me.label, + format = config.renderer, + field = config.field, + centerX = me.centerX, + centerY = me.centerY, + opt = { + x: Number(item.point[0]), + y: Number(item.point[1]) + }, + x = opt.x - centerX, + y = opt.y - centerY, + theta = Math.atan2(y, x || 1), + deg = theta * 180 / Math.PI, + labelBox, direction; + + function fixAngle(a) { + if (a < 0) { + a += 360; + } + return a % 360; + } + + label.setAttributes({ + text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index), + hidden: true + }, + true); + + + + labelBox = label.getBBox(); + deg = fixAngle(deg); + if ((deg > 45 && deg < 135) || (deg > 225 && deg < 315)) { + direction = (deg > 45 && deg < 135 ? 1 : -1); + opt.y += direction * labelBox.height/2; + } else { + direction = (deg >= 135 && deg <= 225 ? -1 : 1); + opt.x += direction * labelBox.width/2; + } + + if (resizing) { + label.setAttributes({ + x: centerX, + y: centerY + }, true); + } + + if (animate) { + label.show(true); + me.onAnimate(label, { + to: opt + }); + } else { + label.setAttributes(opt, true); + label.show(true); + } + }, + + + toggleAll: function(show) { + var me = this, + i, ln, shadow, shadows; + if (!show) { + Ext.chart.series.Radar.superclass.hideAll.call(me); + } + else { + Ext.chart.series.Radar.superclass.showAll.call(me); + } + if (me.radar) { + me.radar.setAttributes({ + hidden: !show + }, true); + + if (me.radar.shadows) { + for (i = 0, shadows = me.radar.shadows, ln = shadows.length; i < ln; i++) { + shadow = shadows[i]; + shadow.setAttributes({ + hidden: !show + }, true); + } + } + } + }, + + + hideAll: function() { + this.toggleAll(false); + this.hideMarkers(0); + }, + + + showAll: function() { + this.toggleAll(true); + }, + + + hideMarkers: function(index) { + var me = this, + count = me.markerGroup && me.markerGroup.getCount() || 0, + i = index || 0; + for (; i < count; i++) { + me.markerGroup.getAt(i).hide(true); + } + }, + + + + getAxesForXAndYFields: function() { + var me = this, + chart = me.chart, + axes = chart.axes, + axis = [].concat(axes && axes.get(0)); + + return { + yAxis: axis + }; + } +}); + + + + +Ext.define('Ext.chart.series.Scatter', { + + + + extend: Ext.chart.series.Cartesian , + + + + + + type: 'scatter', + alias: 'series.scatter', + + + + + + constructor: function(config) { + this.callParent(arguments); + var me = this, + shadow = me.chart.shadow, + surface = me.chart.surface, i, l; + Ext.apply(me, config, { + style: {}, + markerConfig: {}, + shadowAttributes: [{ + "stroke-width": 6, + "stroke-opacity": 0.05, + stroke: 'rgb(0, 0, 0)' + }, { + "stroke-width": 4, + "stroke-opacity": 0.1, + stroke: 'rgb(0, 0, 0)' + }, { + "stroke-width": 2, + "stroke-opacity": 0.15, + stroke: 'rgb(0, 0, 0)' + }] + }); + me.group = surface.getGroup(me.seriesId); + if (shadow) { + for (i = 0, l = me.shadowAttributes.length; i < l; i++) { + me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i)); + } + } + }, + + + getBounds: function() { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + chartAxes = chart.axes, + boundAxes = me.getAxesForXAndYFields(), + boundXAxis = boundAxes.xAxis, + boundYAxis = boundAxes.yAxis, + bbox, xScale, yScale, ln, minX, minY, maxX, maxY, i, axis, ends; + + me.setBBox(); + bbox = me.bbox; + + if (axis = chartAxes.get(boundXAxis)) { + ends = axis.applyData(); + minX = ends.from; + maxX = ends.to; + } + + if (axis = chartAxes.get(boundYAxis)) { + ends = axis.applyData(); + minY = ends.from; + maxY = ends.to; + } + + + if (me.xField && !Ext.isNumber(minX)) { + axis = me.getMinMaxXValues(); + minX = axis[0]; + maxX = axis[1]; + } + + if (me.yField && !Ext.isNumber(minY)) { + axis = me.getMinMaxYValues(); + minY = axis[0]; + maxY = axis[1]; + } + + if (isNaN(minX)) { + minX = 0; + maxX = store.getCount() - 1; + xScale = bbox.width / (store.getCount() - 1); + } + else { + xScale = bbox.width / (maxX - minX); + } + + if (isNaN(minY)) { + minY = 0; + maxY = store.getCount() - 1; + yScale = bbox.height / (store.getCount() - 1); + } + else { + yScale = bbox.height / (maxY - minY); + } + + return { + bbox: bbox, + minX: minX, + minY: minY, + xScale: xScale, + yScale: yScale + }; + }, + + + getPaths: function() { + var me = this, + chart = me.chart, + enableShadows = chart.shadow, + store = chart.getChartStore(), + data = store.data.items, + i, ln, record, + group = me.group, + bounds = me.bounds = me.getBounds(), + bbox = me.bbox, + xScale = bounds.xScale, + yScale = bounds.yScale, + minX = bounds.minX, + minY = bounds.minY, + boxX = bbox.x, + boxY = bbox.y, + boxHeight = bbox.height, + items = me.items = [], + attrs = [], + reverse = me.reverse, + x, y, xValue, yValue, sprite; + + for (i = 0, ln = data.length; i < ln; i++) { + record = data[i]; + xValue = record.get(me.xField); + yValue = record.get(me.yField); + + + if (typeof yValue == 'undefined' || (typeof yValue == 'string' && !yValue) + || xValue == null || yValue == null) { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn("[Ext.chart.series.Scatter] Skipping a store element with a value which is either undefined or null at ", record, xValue, yValue); + } + continue; + } + + if (typeof xValue == 'string' || typeof xValue == 'object' && !Ext.isDate(xValue)) { + xValue = i; + } + if (typeof yValue == 'string' || typeof yValue == 'object' && !Ext.isDate(yValue)) { + yValue = i; + } + if (reverse) { + x = boxX + bbox.width - ((xValue - minX) * xScale); + } else { + x = boxX + (xValue - minX) * xScale; + } + + y = boxY + boxHeight - (yValue - minY) * yScale; + + attrs.push({ + x: x, + y: y + }); + + me.items.push({ + series: me, + value: [xValue, yValue], + point: [x, y], + storeItem: record + }); + + + if (chart.animate && chart.resizing) { + sprite = group.getAt(i); + if (sprite) { + me.resetPoint(sprite); + if (enableShadows) { + me.resetShadow(sprite); + } + } + } + } + return attrs; + }, + + + resetPoint: function(sprite) { + var bbox = this.bbox; + sprite.setAttributes({ + translate: { + x: (bbox.x + bbox.width) / 2, + y: (bbox.y + bbox.height) / 2 + } + }, true); + }, + + + resetShadow: function(sprite) { + var me = this, + shadows = sprite.shadows, + shadowAttributes = me.shadowAttributes, + ln = me.shadowGroups.length, + bbox = me.bbox, + i, attr; + for (i = 0; i < ln; i++) { + attr = Ext.apply({}, shadowAttributes[i]); + + if (attr.translate) { + attr.translate.x += (bbox.x + bbox.width) / 2; + attr.translate.y += (bbox.y + bbox.height) / 2; + } + else { + attr.translate = { + x: (bbox.x + bbox.width) / 2, + y: (bbox.y + bbox.height) / 2 + }; + } + shadows[i].setAttributes(attr, true); + } + }, + + + createPoint: function(attr, type) { + var me = this, + chart = me.chart, + group = me.group, + bbox = me.bbox; + + return Ext.chart.Shape[type](chart.surface, Ext.apply({}, { + x: 0, + y: 0, + group: group, + translate: { + x: (bbox.x + bbox.width) / 2, + y: (bbox.y + bbox.height) / 2 + } + }, attr)); + }, + + + createShadow: function(sprite, endMarkerStyle, type) { + var me = this, + chart = me.chart, + shadowGroups = me.shadowGroups, + shadowAttributes = me.shadowAttributes, + lnsh = shadowGroups.length, + bbox = me.bbox, + i, shadow, shadows, attr; + + sprite.shadows = shadows = []; + + for (i = 0; i < lnsh; i++) { + attr = Ext.apply({}, shadowAttributes[i]); + if (attr.translate) { + attr.translate.x += (bbox.x + bbox.width) / 2; + attr.translate.y += (bbox.y + bbox.height) / 2; + } + else { + Ext.apply(attr, { + translate: { + x: (bbox.x + bbox.width) / 2, + y: (bbox.y + bbox.height) / 2 + } + }); + } + Ext.apply(attr, endMarkerStyle); + shadow = Ext.chart.Shape[type](chart.surface, Ext.apply({}, { + x: 0, + y: 0, + group: shadowGroups[i] + }, attr)); + shadows.push(shadow); + } + }, + + + drawSeries: function() { + var me = this, + chart = me.chart, + store = chart.getChartStore(), + group = me.group, + enableShadows = chart.shadow, + shadowGroups = me.shadowGroups, + shadowAttributes = me.shadowAttributes, + lnsh = shadowGroups.length, + sprite, attrs, attr, ln, i, endMarkerStyle, shindex, type, shadows, + rendererAttributes, shadowAttribute; + + if (!store || !store.getCount() || me.seriesIsHidden) { + me.hide(); + me.items = []; + return; + } + + endMarkerStyle = Ext.apply({}, me.markerStyle, me.markerConfig); + type = endMarkerStyle.type || 'circle'; + delete endMarkerStyle.type; + + + if (!store || !store.getCount()) { + me.hide(); + me.items = []; + return; + } + + + me.unHighlightItem(); + me.cleanHighlights(); + + attrs = me.getPaths(); + ln = attrs.length; + for (i = 0; i < ln; i++) { + attr = attrs[i]; + sprite = group.getAt(i); + Ext.apply(attr, endMarkerStyle); + + + if (!sprite) { + sprite = me.createPoint(attr, type); + if (enableShadows) { + me.createShadow(sprite, endMarkerStyle, type); + } + } + + shadows = sprite.shadows; + if (chart.animate) { + rendererAttributes = me.renderer(sprite, store.getAt(i), { translate: attr }, i, store); + sprite._to = rendererAttributes; + me.onAnimate(sprite, { + to: rendererAttributes + }); + + for (shindex = 0; shindex < lnsh; shindex++) { + shadowAttribute = Ext.apply({}, shadowAttributes[shindex]); + rendererAttributes = me.renderer(shadows[shindex], store.getAt(i), Ext.apply({}, { + hidden: false, + translate: { + x: attr.x + (shadowAttribute.translate? shadowAttribute.translate.x : 0), + y: attr.y + (shadowAttribute.translate? shadowAttribute.translate.y : 0) + } + }, shadowAttribute), i, store); + me.onAnimate(shadows[shindex], { to: rendererAttributes }); + } + } + else { + rendererAttributes = me.renderer(sprite, store.getAt(i), { translate: attr }, i, store); + sprite._to = rendererAttributes; + sprite.setAttributes(rendererAttributes, true); + + for (shindex = 0; shindex < lnsh; shindex++) { + shadowAttribute = Ext.apply({}, shadowAttributes[shindex]); + rendererAttributes = me.renderer(shadows[shindex], store.getAt(i), Ext.apply({}, { + hidden: false, + translate: { + x: attr.x + (shadowAttribute.translate? shadowAttribute.translate.x : 0), + y: attr.y + (shadowAttribute.translate? shadowAttribute.translate.y : 0) + } + }, shadowAttribute), i, store); + shadows[shindex].setAttributes(rendererAttributes, true); + } + } + me.items[i].sprite = sprite; + } + + + ln = group.getCount(); + for (i = attrs.length; i < ln; i++) { + group.getAt(i).hide(true); + } + me.renderLabels(); + me.renderCallouts(); + }, + + + onCreateLabel: function(storeItem, item, i, display) { + var me = this, + group = me.labelsGroup, + config = me.label, + endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle), + bbox = me.bbox; + + return me.chart.surface.add(Ext.apply({ + type: 'text', + 'text-anchor': 'middle', + group: group, + x: Number(item.point[0]), + y: bbox.y + bbox.height / 2 + }, endLabelStyle)); + }, + + + onPlaceLabel: function(label, storeItem, item, i, display, animate, index) { + var me = this, + chart = me.chart, + resizing = chart.resizing, + config = me.label, + format = config.renderer, + field = config.field, + bbox = me.bbox, + x = Number(item.point[0]), + y = Number(item.point[1]), + radius = item.sprite.attr.radius, + labelBox, markerBox, width, height, xOffset, yOffset, + anim; + + label.setAttributes({ + text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index), + hidden: true + }, true); + + + + markerBox = item.sprite.getBBox(); + markerBox.width = markerBox.width || (radius * 2); + markerBox.height = markerBox.height || (radius * 2); + + labelBox = label.getBBox(); + width = labelBox.width/2; + height = labelBox.height/2; + + if (display == 'rotate') { + + xOffset = markerBox.width/2 + width + height/2; + if (x + xOffset + width > bbox.x + bbox.width) { + x -= xOffset; + } else { + x += xOffset; + } + label.setAttributes({ + 'rotation': { + x: x, + y: y, + degrees: -45 + } + }, true); + } else if (display == 'under' || display == 'over') { + label.setAttributes({ + 'rotation': { + degrees: 0 + } + }, true); + + + if (x < bbox.x + width) { + x = bbox.x + width; + } else if (x + width > bbox.x + bbox.width) { + x = bbox.x + bbox.width - width; + } + + yOffset = markerBox.height/2 + height; + y = y + (display == 'over' ? -yOffset : yOffset); + if (y < bbox.y + height) { + y += 2 * yOffset; + } else if (y + height > bbox.y + bbox.height) { + y -= 2 * yOffset; + } + } + + if (!chart.animate) { + label.setAttributes({ + x: x, + y: y + }, true); + label.show(true); + } + else { + if (resizing) { + anim = item.sprite.getActiveAnimation(); + if (anim) { + anim.on('afteranimate', function() { + label.setAttributes({ + x: x, + y: y + }, true); + label.show(true); + }); + } + else { + label.show(true); + } + } + else { + me.onAnimate(label, { + to: { + x: x, + y: y + } + }); + } + } + }, + + + onPlaceCallout: function(callout, storeItem, item, i, display, animate, index) { + var me = this, + chart = me.chart, + surface = chart.surface, + resizing = chart.resizing, + config = me.callouts, + items = me.items, + cur = item.point, + normal, + bbox = callout.label.getBBox(), + offsetFromViz = 30, + offsetToSide = 10, + offsetBox = 3, + boxx, boxy, boxw, boxh, + p, clipRect = me.bbox, + x, y; + + + normal = [Math.cos(Math.PI /4), -Math.sin(Math.PI /4)]; + x = cur[0] + normal[0] * offsetFromViz; + y = cur[1] + normal[1] * offsetFromViz; + + + boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox)); + boxy = y - bbox.height /2 - offsetBox; + boxw = bbox.width + 2 * offsetBox; + boxh = bbox.height + 2 * offsetBox; + + + + if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) { + normal[0] *= -1; + } + if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) { + normal[1] *= -1; + } + + + x = cur[0] + normal[0] * offsetFromViz; + y = cur[1] + normal[1] * offsetFromViz; + + + boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox)); + boxy = y - bbox.height /2 - offsetBox; + boxw = bbox.width + 2 * offsetBox; + boxh = bbox.height + 2 * offsetBox; + + if (chart.animate) { + + me.onAnimate(callout.lines, { + to: { + path: ["M", cur[0], cur[1], "L", x, y, "Z"] + } + }, true); + + me.onAnimate(callout.box, { + to: { + x: boxx, + y: boxy, + width: boxw, + height: boxh + } + }, true); + + me.onAnimate(callout.label, { + to: { + x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)), + y: y + } + }, true); + } else { + + callout.lines.setAttributes({ + path: ["M", cur[0], cur[1], "L", x, y, "Z"] + }, true); + + callout.box.setAttributes({ + x: boxx, + y: boxy, + width: boxw, + height: boxh + }, true); + + callout.label.setAttributes({ + x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)), + y: y + }, true); + } + for (p in callout) { + callout[p].show(true); + } + }, + + + onAnimate: function(sprite, attr) { + sprite.show(); + return this.callParent(arguments); + }, + + isItemInPoint: function(x, y, item) { + var point, + tolerance = 10, + abs = Math.abs; + + function dist(point) { + var dx = abs(point[0] - x), + dy = abs(point[1] - y); + return Math.sqrt(dx * dx + dy * dy); + } + point = item.point; + return (point[0] - tolerance <= x && point[0] + tolerance >= x && + point[1] - tolerance <= y && point[1] + tolerance >= y); + } +}); + + + + +Ext.define('Ext.layout.container.Table', { + + + + alias: ['layout.table'], + extend: Ext.layout.container.Container , + alternateClassName: 'Ext.layout.TableLayout', + + + + + + type: 'table', + + createsInnerCt: true, + + targetCls: Ext.baseCSSPrefix + 'table-layout-ct', + tableCls: Ext.baseCSSPrefix + 'table-layout', + cellCls: Ext.baseCSSPrefix + 'table-layout-cell', + + + tableAttrs: null, + + + + + + getItemSizePolicy: function (item) { + return this.autoSizePolicy; + }, + + initHierarchyState: function (hierarchyStateInner) { + hierarchyStateInner.inShrinkWrapTable = true; + }, + + getLayoutItems: function() { + var me = this, + result = [], + items = me.callParent(), + item, + len = items.length, i; + + for (i = 0; i < len; i++) { + item = items[i]; + if (!item.hidden) { + result.push(item); + } + } + return result; + }, + + getHiddenItems: function(){ + var result = [], + items = this.owner.items.items, + len = items.length, + i = 0, item; + + for (; i < len; ++i) { + item = items[i]; + if (item.rendered && item.hidden) { + result.push(item); + } + } + return result; + }, + + + renderChildren: function() { + var me = this, + items = me.getLayoutItems(), + tbody = me.owner.getTargetEl().child('table', true).tBodies[0], + rows = tbody.rows, + i = 0, + len = items.length, + hiddenItems = me.getHiddenItems(), + cells, curCell, rowIdx, cellIdx, item, trEl, tdEl, itemCt, el; + + + cells = me.calculateCells(items); + + + + + for (; i < len; i++) { + curCell = cells[i]; + rowIdx = curCell.rowIdx; + cellIdx = curCell.cellIdx; + item = items[i]; + + + trEl = rows[rowIdx]; + if (!trEl) { + trEl = tbody.insertRow(rowIdx); + if (me.trAttrs) { + trEl.set(me.trAttrs); + } + } + + + itemCt = tdEl = Ext.get(trEl.cells[cellIdx] || trEl.insertCell(cellIdx)); + if (me.needsDivWrap()) { + itemCt = tdEl.first() || tdEl.createChild({ tag: 'div', role: 'presentation' }); + itemCt.setWidth(null); + } + + + if (!item.rendered) { + me.renderItem(item, itemCt, 0); + } else if (!me.isValidParent(item, itemCt, rowIdx, cellIdx, tbody)) { + me.moveItem(item, itemCt, 0); + } + + + if (me.tdAttrs) { + tdEl.set(me.tdAttrs); + } + if (item.tdAttrs) { + tdEl.set(item.tdAttrs); + } + tdEl.set({ + colSpan: item.colspan || 1, + rowSpan: item.rowspan || 1, + id: item.cellId || '', + cls: me.cellCls + ' ' + (item.cellCls || '') + }); + + + if (!cells[i + 1] || cells[i + 1].rowIdx !== rowIdx) { + cellIdx++; + while (trEl.cells[cellIdx]) { + trEl.deleteCell(cellIdx); + } + } + } + + + rowIdx++; + while (tbody.rows[rowIdx]) { + tbody.deleteRow(rowIdx); + } + + + + for (i = 0, len = hiddenItems.length; i < len; ++i) { + me.ensureInDocument(hiddenItems[i].getEl()); + } + }, + + ensureInDocument: function(el){ + var dom = el.dom.parentNode; + while (dom) { + if (dom.tagName.toUpperCase() == 'BODY') { + return; + } + dom = dom.parentNode; + } + + Ext.getDetachedBody().appendChild(el); + }, + + calculate: function (ownerContext) { + if (!ownerContext.hasDomProp('containerChildrenSizeDone')) { + this.done = false; + } else { + var targetContext = ownerContext.targetContext, + widthShrinkWrap = ownerContext.widthModel.shrinkWrap, + heightShrinkWrap = ownerContext.heightModel.shrinkWrap, + shrinkWrap = heightShrinkWrap || widthShrinkWrap, + table = shrinkWrap && targetContext.el.child('table', true), + targetPadding = shrinkWrap && targetContext.getPaddingInfo(); + + if (widthShrinkWrap) { + ownerContext.setContentWidth(table.offsetWidth + targetPadding.width, true); + } + + if (heightShrinkWrap) { + ownerContext.setContentHeight(table.offsetHeight + targetPadding.height, true); + } + } + }, + + finalizeLayout: function() { + if (this.needsDivWrap()) { + + var items = this.getLayoutItems(), + i, + iLen = items.length, + item; + + for (i = 0; i < iLen; i++) { + item = items[i]; + + Ext.fly(item.el.dom.parentNode).setWidth(item.getWidth()); + } + } + if (Ext.isIE6 || Ext.isIEQuirks) { + + this.owner.getTargetEl().child('table').repaint(); + } + }, + + + calculateCells: function(items) { + var cells = [], + rowIdx = 0, + colIdx = 0, + cellIdx = 0, + totalCols = this.columns || Infinity, + rowspans = [], + i = 0, j, + len = items.length, + item; + + for (; i < len; i++) { + item = items[i]; + + + while (colIdx >= totalCols || rowspans[colIdx] > 0) { + if (colIdx >= totalCols) { + + colIdx = 0; + cellIdx = 0; + rowIdx++; + + + for (j = 0; j < totalCols; j++) { + if (rowspans[j] > 0) { + rowspans[j]--; + } + } + } else { + colIdx++; + } + } + + + cells.push({ + rowIdx: rowIdx, + cellIdx: cellIdx + }); + + + for (j = item.colspan || 1; j; --j) { + rowspans[colIdx] = item.rowspan || 1; + ++colIdx; + } + ++cellIdx; + } + + return cells; + }, + + getRenderTree: function() { + var me = this, + items = me.getLayoutItems(), + cells, + rows = [], + result = Ext.apply({ + tag: 'table', + role: 'presentation', + cls: me.tableCls, + cellspacing: 0, + cellpadding: 0, + cn: { + tag: 'tbody', + role: 'presentation', + cn: rows + } + }, me.tableAttrs), + tdAttrs = me.tdAttrs, + needsDivWrap = me.needsDivWrap(), + i, len = items.length, item, curCell, tr, rowIdx, cellIdx, cell; + + + cells = me.calculateCells(items); + + for (i = 0; i < len; i++) { + item = items[i]; + + curCell = cells[i]; + rowIdx = curCell.rowIdx; + cellIdx = curCell.cellIdx; + + + tr = rows[rowIdx]; + if (!tr) { + tr = rows[rowIdx] = { + tag: 'tr', + role: 'presentation', + cn: [] + }; + if (me.trAttrs) { + Ext.apply(tr, me.trAttrs); + } + } + + + cell = tr.cn[cellIdx] = { + tag: 'td', + role: 'presentation' + }; + if (tdAttrs) { + Ext.apply(cell, tdAttrs); + } + Ext.apply(cell, { + colSpan: item.colspan || 1, + rowSpan: item.rowspan || 1, + id: item.cellId || '', + cls: me.cellCls + ' ' + (item.cellCls || '') + }); + + if (needsDivWrap) { + cell = cell.cn = { + tag: 'div', + role: 'presentation' + }; + } + + me.configureItem(item); + + cell.cn = item.getRenderTree(); + } + return result; + }, + + isValidParent: function(item, target, rowIdx, cellIdx) { + var tbody, + correctCell, + table; + + + if (arguments.length === 3) { + table = item.el.up('table'); + return table && table.dom.parentNode === target.dom; + } + tbody = this.owner.getTargetEl().child('table', true).tBodies[0]; + correctCell = tbody.rows[rowIdx].cells[cellIdx]; + return item.el.dom.parentNode === correctCell; + }, + + + needsDivWrap: function() { + return Ext.isOpera10_5; + } +}); + + + +Ext.define('Ext.container.ButtonGroup', { + extend: Ext.panel.Panel , + alias: 'widget.buttongroup', + alternateClassName: 'Ext.ButtonGroup', + + + + + + + baseCls: Ext.baseCSSPrefix + 'btn-group', + + + layout: { + type: 'table' + }, + + defaultType: 'button', + + + frame: true, + + + + frameHeader: false, + + titleAlign: 'center', + + noTitleCls: 'notitle', + + ariaRole: 'group', + + initComponent : function() { + + var me = this, + cols = me.columns; + + if (cols) { + me.layout = Ext.apply({}, {columns: cols}, me.layout); + } + + if (!me.title) { + me.addClsWithUI(me.noTitleCls); + } + me.callParent(arguments); + }, + + + onBeforeAdd: function(component) { + if (component.isButton) { + if (this.defaultButtonUI && component.ui === 'default' && + !component.hasOwnProperty('ui')) { + component.ui = this.defaultButtonUI; + } else { + component.ui = component.ui + '-toolbar'; + } + } + this.callParent(arguments); + }, + + + applyDefaults: function(c) { + if (!Ext.isString(c)) { + c = this.callParent(arguments); + } + return c; + } + + + + + + +}); + + +Ext.define('ExtThemeNeptune.container.ButtonGroup', { + override: 'Ext.container.ButtonGroup', + usePlainButtons: false +}); + + + +Ext.define('Ext.container.Monitor', { + target: null, + selector: '', + + scope: null, + addHandler: null, + removeHandler: null, + invalidateHandler: null, + + disabled: 0, + + constructor: function(config){ + Ext.apply(this, config); + }, + + bind: function(target){ + var me = this; + + me.target = target; + target.on('beforedestroy', me.disable, me); + me.onContainerAdd(target); + }, + + unbind: function() { + var me = this, + target = me.target; + + if (target) { + target.un('beforedestroy', me.disable, me); + } + me.items = null; + }, + + disable: function(){ + ++this.disabled; + }, + + enable: function(){ + if (this.disabled > 0) { + --this.disabled; + } + }, + + handleAdd: function(ct, comp) { + if (!this.disabled) { + if (comp.is(this.selector)) { + this.onItemAdd(comp.ownerCt, comp); + } + + if (comp.isQueryable) { + this.onContainerAdd(comp); + } + } + }, + + onItemAdd: function(ct, comp){ + var me = this, + items = me.items, + handler = me.addHandler; + + if (!me.disabled) { + if (handler) { + handler.call(me.scope || comp, comp); + } + if (items) { + items.add(comp); + } + } + }, + + onItemRemove: function(ct, comp){ + var me = this, + items = me.items, + handler = me.removeHandler; + + if (!me.disabled) { + if (handler) { + handler.call(me.scope || comp, comp); + } + if (items) { + items.remove(comp); + } + } + }, + + onContainerAdd: function(ct, preventChildren) { + var me = this, + items, len, + handleAdd = me.handleAdd, + handleRemove = me.handleRemove, + i, comp; + + if (ct.isContainer) { + ct.on('add', handleAdd, me); + ct.on('dockedadd', handleAdd, me); + ct.on('remove', handleRemove, me); + ct.on('dockedremove', handleRemove, me); + } + + + + if (preventChildren !== true) { + items = ct.query(me.selector); + for (i = 0, len = items.length; i < len; ++i) { + comp = items[i]; + me.onItemAdd(comp.ownerCt, comp); + } + } + + items = ct.query('>container'); + for (i = 0, len = items.length; i < len; ++i) { + me.onContainerAdd(items[i], true); + } + + }, + + handleRemove: function(ct, comp) { + var me = this; + + + + if (!me.disabled) { + if (comp.is(me.selector)) { + me.onItemRemove(ct, comp); + } + + if (comp.isQueryable) { + me.onContainerRemove(ct, comp); + } + } + }, + + onContainerRemove: function(ct, comp){ + var me = this, + items, i, len, item; + + + + if (!comp.isDestroyed && !comp.destroying && comp.isContainer) { + me.removeCtListeners(comp); + + items = comp.query(me.selector); + for (i = 0, len = items.length; i < len; ++i) { + item = items[i]; + me.onItemRemove(item.ownerCt, item); + } + + items = comp.query('container'); + for (i = 0, len = items.length; i < len; ++i) { + me.removeCtListeners(items[i]); + } + } else { + + me.invalidateItems(true); + } + }, + + removeCtListeners: function(comp){ + var me = this; + comp.un('add', me.handleAdd, me); + comp.un('dockedadd', me.handleAdd, me); + comp.un('remove', me.handleRemove, me); + comp.un('dockedremove', me.handleRemove, me); + }, + + getItems: function(){ + var me = this, + items = me.items; + + if (!items) { + items = me.items = new Ext.util.MixedCollection(); + items.addAll(me.target.query(me.selector)); + } + return items; + }, + + invalidateItems: function(triggerHandler) { + var me = this, + handler = me.invalidateHandler; + + if (triggerHandler && handler) { + handler.call(me.scope || me, me); + } + me.items = null; + } +}); + + + +Ext.define('Ext.container.Viewport', { + extend: Ext.container.Container , + alias: 'widget.viewport', + + alternateClassName: 'Ext.Viewport', + + + + + + + + + + + + + + + isViewport: true, + + ariaRole: 'application', + + preserveElOnDestroy: true, + + viewportCls: Ext.baseCSSPrefix + 'viewport', + + initComponent : function() { + var me = this, + html = document.body.parentNode, + el = me.el = Ext.getBody(); + + + Ext.getScrollbarSize(); + + + me.width = me.height = undefined; + + me.callParent(arguments); + Ext.fly(html).addCls(me.viewportCls); + if (me.autoScroll) { + Ext.fly(html).setStyle(me.getOverflowStyle()); + delete me.autoScroll; + } + el.setHeight = el.setWidth = Ext.emptyFn; + el.dom.scroll = 'no'; + me.allowDomMove = false; + me.renderTo = me.el; + }, + + + applyTargetCls: function(targetCls) { + this.el.addCls(targetCls); + }, + + onRender: function() { + var me = this; + + me.callParent(arguments); + + + + me.width = Ext.Element.getViewportWidth(); + me.height = Ext.Element.getViewportHeight(); + }, + + afterFirstLayout: function() { + var me = this; + + me.callParent(arguments); + setTimeout(function() { + Ext.EventManager.onWindowResize(me.fireResize, me); + }, 1); + }, + + fireResize : function(width, height){ + + + if (width != this.width || height != this.height) { + this.setSize(width, height); + } + }, + + initHierarchyState: function(hierarchyState) { + this.callParent([this.hierarchyState = Ext.rootHierarchyState]); + }, + + beforeDestroy: function(){ + var me = this; + + me.removeUIFromElement(); + me.el.removeCls(me.baseCls); + Ext.fly(document.body.parentNode).removeCls(me.viewportCls); + me.callParent(); + } +}); + + + +Ext.define('Ext.data.reader.Array', { + extend: Ext.data.reader.Json , + alternateClassName: 'Ext.data.ArrayReader', + alias : 'reader.array', + + + totalProperty: undefined, + successProperty: undefined, + + + createFieldAccessExpression: function(field, fieldVarName, dataName) { + + + var index = (field.mapping == null) ? field.originalIndex : field.mapping, + result; + + if (typeof index === 'function') { + result = fieldVarName + '.mapping(' + dataName + ', this)'; + } else { + if (isNaN(index)) { + index = '"' + index + '"'; + } + result = dataName + "[" + index + "]"; + } + return result; + } +}); + + + +Ext.define('Ext.data.ArrayStore', { + extend: Ext.data.Store , + alias: 'store.array', + + + + + + constructor: function(config) { + config = Ext.apply({ + proxy: { + type: 'memory', + reader: 'array' + } + }, config); + this.callParent([config]); + }, + + loadData: function(data, append) { + if (this.expandData === true) { + var r = [], + i = 0, + ln = data.length; + + for (; i < ln; i++) { + r[r.length] = [data[i]]; + } + + data = r; + } + + this.callParent([data, append]); + } +}, function() { + + Ext.data.SimpleStore = Ext.data.ArrayStore; + +}); + + + +Ext.define('Ext.data.Batch', { + mixins: { + observable: Ext.util.Observable + }, + + + autoStart: false, + + + pauseOnException: false, + + + current: -1, + + + total: 0, + + + isRunning: false, + + + isComplete: false, + + + hasException: false, + + + constructor: function(config) { + var me = this; + + + + + + + + me.mixins.observable.constructor.call(me, config); + + + me.operations = []; + + + me.exceptions = []; + }, + + + add: function(operation) { + this.total++; + + operation.setBatch(this); + + this.operations.push(operation); + + return this; + }, + + + start: function( index) { + var me = this; + + if (me.isRunning) { + return me; + } + + me.exceptions.length = 0; + me.hasException = false; + me.isRunning = true; + + return me.runOperation(Ext.isDefined(index) ? index : me.current + 1); + }, + + + retry: function() { + return this.start(this.current); + }, + + + runNextOperation: function() { + return this.runOperation(this.current + 1); + }, + + + pause: function() { + this.isRunning = false; + return this; + }, + + + runOperation: function(index) { + var me = this, + operations = me.operations, + operation = operations[index], + onProxyReturn; + + if (operation === undefined) { + me.isRunning = false; + me.isComplete = true; + me.fireEvent('complete', me, operations[operations.length - 1]); + } else { + me.current = index; + + onProxyReturn = function(operation) { + var hasException = operation.hasException(); + + if (hasException) { + me.hasException = true; + me.exceptions.push(operation); + me.fireEvent('exception', me, operation); + } + + if (hasException && me.pauseOnException) { + me.pause(); + } else { + operation.setCompleted(); + me.fireEvent('operationcomplete', me, operation); + me.runNextOperation(); + } + }; + + operation.setStarted(); + + me.proxy[operation.action](operation, onProxyReturn, me); + } + + return me; + } +}); + + + +Ext.define('Ext.data.BufferStore', { + extend: Ext.data.Store , + alias: 'store.buffer', + sortOnLoad: false, + filterOnLoad: false, + + constructor: function() { + Ext.Error.raise('The BufferStore class has been deprecated. Instead, specify the buffered config option on Ext.data.Store'); + } +}); + + + + +Ext.define('Ext.direct.Manager', { + singleton: true, + + + + + + + mixins: { + observable: Ext.util.Observable + }, + + + exceptions: { + TRANSPORT: 'xhr', + PARSE: 'parse', + DATA: 'data', + LOGIN: 'login', + SERVER: 'exception' + }, + + constructor: function() { + var me = this; + + me.addEvents( + + 'event', + + + 'exception' + ); + + me.transactions = new Ext.util.MixedCollection(); + me.providers = new Ext.util.MixedCollection(); + + me.mixins.observable.constructor.call(me); + }, + + + addProvider: function(provider) { + var me = this, + args = arguments, + relayers = me.relayers || (me.relayers = {}), + i, len; + + if (args.length > 1) { + for (i = 0, len = args.length; i < len; ++i) { + me.addProvider(args[i]); + } + + return; + } + + + if (!provider.isProvider) { + provider = Ext.create('direct.' + provider.type + 'provider', provider); + } + + me.providers.add(provider); + provider.on('data', me.onProviderData, me); + + if (provider.relayedEvents) { + relayers[provider.id] = me.relayEvents(provider, provider.relayedEvents); + } + + if (!provider.isConnected()) { + provider.connect(); + } + + return provider; + }, + + + getProvider: function(id) { + return id.isProvider ? id : this.providers.get(id); + }, + + + removeProvider: function(provider) { + var me = this, + providers = me.providers, + relayers = me.relayers, + id; + + provider = provider.isProvider ? provider : providers.get(provider); + + if (provider) { + provider.un('data', me.onProviderData, me); + + id = provider.id; + + if (relayers[id]) { + relayers[id].destroy(); + delete relayers[id]; + } + + providers.remove(provider); + + return provider; + } + + return null; + }, + + + addTransaction: function(transaction) { + this.transactions.add(transaction); + + return transaction; + }, + + + removeTransaction: function(transaction) { + var me = this; + + transaction = me.getTransaction(transaction); + me.transactions.remove(transaction); + + return transaction; + }, + + + getTransaction: function(transaction) { + return typeof transaction === 'object' ? transaction : this.transactions.get(transaction); + }, + + onProviderData: function(provider, event) { + var me = this, + i, len; + + if (Ext.isArray(event)) { + for (i = 0, len = event.length; i < len; ++i) { + me.onProviderData(provider, event[i]); + } + + return; + } + + if (event.name && event.name != 'event' && event.name != 'exception') { + me.fireEvent(event.name, event); + } + else if (event.status === false) { + me.fireEvent('exception', event); + } + + me.fireEvent('event', event, provider); + }, + + + parseMethod: function(fn) { + if (Ext.isString(fn)) { + var parts = fn.split('.'), + i = 0, + len = parts.length, + current = Ext.global; + + while (current && i < len) { + current = current[parts[i]]; + ++i; + } + + fn = Ext.isFunction(current) ? current : null; + } + + return fn || null; + } + +}, function() { + + Ext.Direct = Ext.direct.Manager; +}); + + + +Ext.define('Ext.data.proxy.Direct', { + + + extend: Ext.data.proxy.Server , + alternateClassName: 'Ext.data.DirectProxy', + + alias: 'proxy.direct', + + + + + + + + + paramOrder: undefined, + + + paramsAsHash: true, + + + directFn : undefined, + + + + + + + paramOrderRe: /[\s,|]/, + + constructor: function(config){ + var me = this, + paramOrder; + + me.callParent(arguments); + + paramOrder = me.paramOrder; + if (Ext.isString(paramOrder)) { + me.paramOrder = paramOrder.split(me.paramOrderRe); + } + }, + + resolveMethods: function() { + var me = this, + fn = me.directFn, + api = me.api, + Manager = Ext.direct.Manager, + method; + + if (fn) { + method = me.directFn = Manager.parseMethod(fn); + + if (!Ext.isFunction(method)) { + Ext.Error.raise('Cannot resolve directFn ' + fn); + } + } + else if (api) { + for (fn in api) { + if (api.hasOwnProperty(fn)) { + method = api[fn]; + api[fn] = Manager.parseMethod(method); + + if (!Ext.isFunction(api[fn])) { + Ext.Error.raise('Cannot resolve Direct api ' + fn + ' method ' + method); + } + } + } + } + + me.methodsResolved = true; + }, + + doRequest: function(operation, callback, scope) { + var me = this, + writer = me.getWriter(), + request = me.buildRequest(operation), + params = request.params, + args = [], + fn, method; + + if (!me.methodsResolved) { + me.resolveMethods(); + } + + fn = me.api[request.action] || me.directFn; + + if (!fn) { + Ext.Error.raise('No direct function specified for this proxy'); + } + + if (operation.allowWrite()) { + request = writer.write(request); + } + + if (operation.action == 'read') { + + method = fn.directCfg.method; + args = method.getArgs(params, me.paramOrder, me.paramsAsHash); + } else { + args.push(request.jsonData); + } + + Ext.apply(request, { + args: args, + directFn: fn + }); + args.push(me.createRequestCallback(request, operation, callback, scope), me); + fn.apply(window, args); + + + + + return request; + }, + + + applyEncoding: Ext.identityFn, + + createRequestCallback: function(request, operation, callback, scope){ + var me = this; + + return function(data, event){ + me.processResponse(event.status, operation, request, event, callback, scope); + }; + }, + + + extractResponseData: function(response){ + return Ext.isDefined(response.result) ? response.result : response.data; + }, + + + setException: function(operation, response) { + operation.setException(response.message); + }, + + + buildUrl: function(){ + return ''; + } +}); + + + +Ext.define('Ext.data.DirectStore', { + + + extend: Ext.data.Store , + + alias: 'store.direct', + + + + + + constructor : function(config){ + config = Ext.apply({}, config); + if (!config.proxy) { + var proxy = { + type: 'direct', + reader: { + type: 'json' + } + }; + Ext.copyTo(proxy, config, 'paramOrder,paramsAsHash,directFn,api,simpleSortMode'); + Ext.copyTo(proxy.reader, config, 'totalProperty,root,idProperty'); + config.proxy = proxy; + } + this.callParent([config]); + } +}); + + + +Ext.define('Ext.data.JsonP', { + + + + singleton: true, + + + + + requestCount: 0, + + + requests: {}, + + + timeout: 30000, + + + disableCaching: true, + + + disableCachingParam: '_dc', + + + callbackKey: 'callback', + + + request: function(options) { + options = Ext.apply({}, options); + + if (!options.url) { + Ext.Error.raise('A url must be specified for a JSONP request.'); + } + + var me = this, + disableCaching = Ext.isDefined(options.disableCaching) ? options.disableCaching : me.disableCaching, + cacheParam = options.disableCachingParam || me.disableCachingParam, + id = ++me.requestCount, + callbackName = options.callbackName || 'callback' + id, + callbackKey = options.callbackKey || me.callbackKey, + timeout = Ext.isDefined(options.timeout) ? options.timeout : me.timeout, + params = Ext.apply({}, options.params), + url = options.url, + name = Ext.name, + request, + script; + + + + if (disableCaching && !params[cacheParam]) { + params[cacheParam] = Ext.Date.now(); + } + options.params = params; + + params[callbackKey] = name + '.data.JsonP.' + callbackName; + script = me.createScript(url, params, options); + + me.requests[id] = request = { + url: url, + params: params, + script: script, + id: id, + scope: options.scope, + success: options.success, + failure: options.failure, + callback: options.callback, + callbackKey: callbackKey, + callbackName: callbackName + }; + + if (timeout > 0) { + request.timeout = setTimeout(Ext.bind(me.handleTimeout, me, [request]), timeout); + } + + me.setupErrorHandling(request); + me[callbackName] = Ext.bind(me.handleResponse, me, [request], true); + me.loadScript(request); + return request; + }, + + + abort: function(request){ + var me = this, + requests = me.requests, + key; + + if (request) { + if (!request.id) { + request = requests[request]; + } + me.handleAbort(request); + } else { + for (key in requests) { + if (requests.hasOwnProperty(key)) { + me.abort(requests[key]); + } + } + } + }, + + + setupErrorHandling: function(request){ + request.script.onerror = Ext.bind(this.handleError, this, [request]); + }, + + + handleAbort: function(request){ + request.errorType = 'abort'; + this.handleResponse(null, request); + }, + + + handleError: function(request){ + request.errorType = 'error'; + this.handleResponse(null, request); + }, + + + cleanupErrorHandling: function(request){ + request.script.onerror = null; + }, + + + handleTimeout: function(request){ + request.errorType = 'timeout'; + this.handleResponse(null, request); + }, + + + handleResponse: function(result, request){ + + var success = true; + + if (request.timeout) { + clearTimeout(request.timeout); + } + delete this[request.callbackName]; + delete this.requests[request.id]; + this.cleanupErrorHandling(request); + Ext.fly(request.script).remove(); + + if (request.errorType) { + success = false; + Ext.callback(request.failure, request.scope, [request.errorType]); + } else { + Ext.callback(request.success, request.scope, [result]); + } + Ext.callback(request.callback, request.scope, [success, result, request.errorType]); + Ext.EventManager.idleEvent.fire(); + }, + + + createScript: function(url, params, options) { + var script = document.createElement('script'); + script.setAttribute("src", Ext.urlAppend(url, Ext.Object.toQueryString(params))); + script.setAttribute("async", true); + script.setAttribute("type", "text/javascript"); + return script; + }, + + + loadScript: function (request) { + Ext.getHead().appendChild(request.script); + } +}); + + + +Ext.define('Ext.data.proxy.JsonP', { + extend: Ext.data.proxy.Server , + alternateClassName: 'Ext.data.ScriptTagProxy', + alias: ['proxy.jsonp', 'proxy.scripttag'], + + + defaultWriterType: 'base', + + + callbackKey : 'callback', + + + recordParam: 'records', + + + autoAppendParams: true, + + constructor: function() { + this.addEvents( + + 'exception' + ); + this.callParent(arguments); + }, + + + doRequest: function(operation, callback, scope) { + + var me = this, + request = me.buildRequest(operation), + params = request.params; + + + Ext.apply(request, { + callbackKey: me.callbackKey, + timeout: me.timeout, + scope: me, + disableCaching: false, + callback: me.createRequestCallback(request, operation, callback, scope) + }); + + + + if (me.autoAppendParams) { + request.params = {}; + } + + request.jsonp = Ext.data.JsonP.request(request); + + request.params = params; + operation.setStarted(); + me.lastRequest = request; + + return request; + }, + + + createRequestCallback: function(request, operation, callback, scope) { + var me = this; + + return function(success, response, errorType) { + delete me.lastRequest; + me.processResponse(success, operation, request, response, callback, scope); + }; + }, + + + setException: function(operation, response) { + operation.setException(operation.request.jsonp.errorType); + }, + + + + buildUrl: function(request) { + var me = this, + url = me.callParent(arguments), + records = request.records, + writer = me.getWriter(), + params, + filters, + filter, i; + + + + if (writer && request.operation.allowWrite()) { + request = writer.write(request); + } + + + params = request.params; + filters = params.filters, + delete params.filters; + if (filters && filters.length) { + for (i = 0; i < filters.length; i++) { + filter = filters[i]; + + if (filter.value) { + params[filter.property] = filter.value; + } + } + } + + + if ((!writer || !writer.encode) && Ext.isArray(records) && records.length > 0) { + params[me.recordParam] = me.encodeRecords(records); + } + + + + if (me.autoAppendParams) { + url = Ext.urlAppend(url, Ext.Object.toQueryString(params)); + } + + return url; + }, + + + abort: function() { + var lastRequest = this.lastRequest; + if (lastRequest) { + Ext.data.JsonP.abort(lastRequest.jsonp); + } + }, + + + encodeRecords: function(records) { + var encoded = [], + i = 0, + len = records.length; + + for (; i < len; i++) { + encoded.push(Ext.encode(records[i].getData())); + } + + return encoded; + } +}); + + + +Ext.define('Ext.data.JsonPStore', { + extend: Ext.data.Store , + alias : 'store.jsonp', + + + + + + constructor: function(config) { + config = Ext.apply({ + proxy: { + type: 'jsonp', + reader: 'json' + } + }, config); + this.callParent([config]); + } +}); + + + +Ext.define('Ext.data.JsonStore', { + extend: Ext.data.Store , + alias: 'store.json', + + + + + + + constructor: function(config) { + config = Ext.apply({ + proxy: { + type : 'ajax', + reader: 'json', + writer: 'json' + } + }, config); + this.callParent([config]); + } +}); + + +Ext.define('Ext.ux.data.PagingMemoryProxy', { + extend: Ext.data.proxy.Memory , + alias: 'proxy.pagingmemory', + alternateClassName: 'Ext.data.PagingMemoryProxy', + + constructor: function() { + Ext.log.warn('Ext.ux.data.PagingMemoryProxy functionality has been merged into Ext.data.proxy.Memory by using the enablePaging flag.'); + this.callParent(arguments); + }, + + read : function(operation, callback, scope){ + var reader = this.getReader(), + result = reader.read(this.data), + sorters, filters, sorterFn, records; + + scope = scope || this; + + filters = operation.filters; + if (filters.length > 0) { + + + records = []; + + Ext.each(result.records, function(record) { + var isMatch = true, + length = filters.length, + i; + + for (i = 0; i < length; i++) { + var filter = filters[i], + fn = filter.filterFn, + scope = filter.scope; + + isMatch = isMatch && fn.call(scope, record); + } + if (isMatch) { + records.push(record); + } + }, this); + + result.records = records; + result.totalRecords = result.total = records.length; + } + + + sorters = operation.sorters; + if (sorters.length > 0) { + + sorterFn = function(r1, r2) { + var result = sorters[0].sort(r1, r2), + length = sorters.length, + i; + + + for (i = 1; i < length; i++) { + result = result || sorters[i].sort.call(this, r1, r2); + } + + return result; + }; + + result.records.sort(sorterFn); + } + + + if (operation.start !== undefined && operation.limit !== undefined) { + result.records = result.records.slice(operation.start, operation.start + operation.limit); + result.count = result.records.length; + } + + Ext.apply(operation, { + resultSet: result + }); + + operation.setCompleted(); + operation.setSuccessful(); + + Ext.Function.defer(function () { + Ext.callback(callback, scope, [operation]); + }, 10); + } +}); + + + +Ext.define('Ext.data.Request', { + + action: undefined, + + + params: undefined, + + + method: 'GET', + + + url: undefined, + + + constructor: function(config) { + Ext.apply(this, config); + } +}); + + + +Ext.define('Ext.data.SequentialIdGenerator', { + extend: Ext.data.IdGenerator , + alias: 'idgen.sequential', + + constructor: function() { + var me = this; + + me.callParent(arguments); + + me.parts = [ me.prefix, '']; + }, + + + prefix: '', + + + seed: 1, + + + generate: function () { + var me = this, + parts = me.parts; + + parts[1] = me.seed++; + return parts.join(''); + } +}); + + + +Ext.define('Ext.data.UuidGenerator', (function () { + var twoPow14 = Math.pow(2, 14), + twoPow16 = Math.pow(2, 16), + twoPow28 = Math.pow(2, 28), + twoPow32 = Math.pow(2, 32); + + function toHex (value, length) { + var ret = value.toString(16); + if (ret.length > length) { + ret = ret.substring(ret.length - length); + } else if (ret.length < length) { + ret = Ext.String.leftPad(ret, length, '0'); + } + return ret; + } + + function rand (lo, hi) { + var v = Math.random() * (hi - lo + 1); + return Math.floor(v) + lo; + } + + function split (bignum) { + if (typeof(bignum) == 'number') { + var hi = Math.floor(bignum / twoPow32); + return { + lo: Math.floor(bignum - hi * twoPow32), + hi: hi + }; + } + return bignum; + } + + return { + extend: Ext.data.IdGenerator , + + alias: 'idgen.uuid', + + id: 'uuid', + + + + + + + version: 4, + + constructor: function() { + var me = this; + + me.callParent(arguments); + + me.parts = []; + me.init(); + }, + + generate: function () { + var me = this, + parts = me.parts, + ts = me.timestamp; + + + parts[0] = toHex(ts.lo, 8); + parts[1] = toHex(ts.hi & 0xFFFF, 4); + parts[2] = toHex(((ts.hi >>> 16) & 0xFFF) | (me.version << 12), 4); + parts[3] = toHex(0x80 | ((me.clockSeq >>> 8) & 0x3F), 2) + + toHex(me.clockSeq & 0xFF, 2); + parts[4] = toHex(me.salt.hi, 4) + toHex(me.salt.lo, 8); + + if (me.version == 4) { + me.init(); + } else { + + ++ts.lo; + if (ts.lo >= twoPow32) { + ts.lo = 0; + ++ts.hi; + } + } + + return parts.join('-').toLowerCase(); + }, + + getRecId: function (rec) { + return rec.getId(); + }, + + + init: function () { + var me = this, + salt, time; + + if (me.version == 4) { + + + + + me.clockSeq = rand(0, twoPow14-1); + + + salt = me.salt || (me.salt = {}); + time = me.timestamp || (me.timestamp = {}); + + + salt.lo = rand(0, twoPow32-1); + salt.hi = rand(0, twoPow16-1); + time.lo = rand(0, twoPow32-1); + time.hi = rand(0, twoPow28-1); + } else { + + me.salt = split(me.salt); + me.timestamp = split(me.timestamp); + + + + me.salt.hi |= 0x100; + } + }, + + + reconfigure: function (config) { + Ext.apply(this, config); + this.init(); + } + }; +}())); + + + +Ext.define('Ext.data.reader.Xml', { + extend: Ext.data.reader.Reader , + alternateClassName: 'Ext.data.XmlReader', + alias : 'reader.xml', + + + + + + + createAccessor: function(expr) { + var me = this; + + if (Ext.isEmpty(expr)) { + return Ext.emptyFn; + } + + if (Ext.isFunction(expr)) { + return expr; + } + + return function(root) { + return me.getNodeValue(Ext.DomQuery.selectNode(expr, root)); + }; + }, + + getNodeValue: function(node) { + if (node) { + + + + if (typeof node.normalize === 'function') { + node.normalize(); + } + node = node.firstChild; + if (node) { + return node.nodeValue; + } + } + return undefined; + }, + + + getResponseData: function(response) { + var xml = response.responseXML, + error, + msg; + + if (!xml) { + msg = 'XML data not found in the response'; + + error = new Ext.data.ResultSet({ + total : 0, + count : 0, + records: [], + success: false, + message: msg + }); + + this.fireEvent('exception', this, response, error); + + Ext.Logger.warn(msg); + + return error; + } + + return this.readRecords(xml); + }, + + + getData: function(data) { + return data.documentElement || data; + }, + + + getRoot: function(data) { + var nodeName = data.nodeName, + root = this.root; + + if (!root || (nodeName && nodeName == root)) { + return data; + } else if (Ext.DomQuery.isXml(data)) { + + + + return Ext.DomQuery.selectNode(root, data); + } + }, + + + extractData: function(root) { + var recordName = this.record; + + if (!recordName) { + Ext.Error.raise('Record is a required parameter'); + } + + if (recordName != root.nodeName) { + root = Ext.DomQuery.select(recordName, root); + } else { + root = [root]; + } + return this.callParent([root]); + }, + + + getAssociatedDataRoot: function(data, associationName) { + return Ext.DomQuery.select(associationName, data)[0]; + }, + + + readRecords: function(doc) { + + + if (Ext.isArray(doc)) { + doc = doc[0]; + } + + + this.xmlData = doc; + return this.callParent([doc]); + }, + + + createFieldAccessExpression: function(field, fieldVarName, dataName) { + var namespace = this.namespace, + selector, result; + + selector = field.mapping || ((namespace ? namespace + '|' : '') + field.name); + + if (typeof selector === 'function') { + result = fieldVarName + '.mapping(' + dataName + ', this)'; + } else { + result = 'me.getNodeValue(Ext.DomQuery.selectNode("' + selector + '", ' + dataName + '))'; + } + return result; + } +}); + + + +Ext.define('Ext.data.writer.Xml', { + + + + extend: Ext.data.writer.Writer , + alternateClassName: 'Ext.data.XmlWriter', + + alias: 'writer.xml', + + + + + documentRoot: 'xmlData', + + + defaultDocumentRoot: 'xmlData', + + + header: '', + + + record: 'record', + + + writeRecords: function(request, data) { + var me = this, + xml = [], + i = 0, + len = data.length, + root = me.documentRoot, + record = me.record, + needsRoot = data.length !== 1, + item, + key; + + + xml.push(me.header || ''); + + if (!root && needsRoot) { + root = me.defaultDocumentRoot; + } + + if (root) { + xml.push('<', root, '>'); + } + + for (; i < len; ++i) { + item = data[i]; + xml.push('<', record, '>'); + for (key in item) { + if (item.hasOwnProperty(key)) { + xml.push('<', key, '>', item[key], ''); + } + } + xml.push(''); + } + + if (root) { + xml.push(''); + } + + request.xmlData = xml.join(''); + return request; + } +}); + + + +Ext.define('Ext.data.XmlStore', { + extend: Ext.data.Store , + alias: 'store.xml', + + + + + + + + constructor: function(config){ + config = Ext.apply({ + proxy: { + type: 'ajax', + reader: 'xml', + writer: 'xml' + } + }, config); + + this.callParent([config]); + } +}); + + + +Ext.define('Ext.data.association.BelongsTo', { + extend: Ext.data.association.Association , + alternateClassName: 'Ext.data.BelongsToAssociation', + alias: 'association.belongsto', + + + + + + + + + constructor: function(config) { + this.callParent(arguments); + + var me = this, + ownerProto = me.ownerModel.prototype, + associatedName = me.associatedName, + getterName = me.getterName || 'get' + associatedName, + setterName = me.setterName || 'set' + associatedName; + + Ext.applyIf(me, { + name : associatedName, + foreignKey : associatedName.toLowerCase() + "_id", + instanceName: associatedName + 'BelongsToInstance', + associationKey: associatedName.toLowerCase() + }); + + ownerProto[getterName] = me.createGetter(); + ownerProto[setterName] = me.createSetter(); + }, + + + createSetter: function() { + var me = this, + foreignKey = me.foreignKey, + instanceName = me.instanceName; + + + return function(value, options, scope) { + + var setByRecord = value && value.isModel, + valueToSet = setByRecord ? value.getId() : value; + + + if (setByRecord) { + this[instanceName] = value; + } + + + else if (this[instanceName] instanceof Ext.data.Model && !this.isEqual(this.get(foreignKey), valueToSet)) { + delete this[instanceName]; + } + + + this.set(foreignKey, valueToSet); + + if (Ext.isFunction(options)) { + options = { + callback: options, + scope: scope || this + }; + } + + if (Ext.isObject(options)) { + return this.save(options); + } + }; + }, + + + createGetter: function() { + var me = this, + associatedName = me.associatedName, + associatedModel = me.associatedModel, + foreignKey = me.foreignKey, + primaryKey = me.primaryKey, + instanceName = me.instanceName; + + + return function(options, scope) { + options = options || {}; + + var model = this, + foreignKeyId = model.get(foreignKey), + success, + instance, + args; + + if (options.reload === true || model[instanceName] === undefined) { + + if (Ext.isEmpty(foreignKeyId)) { + return null; + } + instance = Ext.ModelManager.create({}, associatedName); + instance.set(primaryKey, foreignKeyId); + + if (typeof options == 'function') { + options = { + callback: options, + scope: scope || model + }; + } + + + success = options.success; + options.success = function(rec){ + model[instanceName] = rec; + if (success) { + success.apply(this, arguments); + } + }; + + associatedModel.load(foreignKeyId, options); + + model[instanceName] = instance; + return instance; + } else { + instance = model[instanceName]; + args = [instance]; + scope = scope || options.scope || model; + + + + + if (options) { + Ext.callback(options, scope, args); + Ext.callback(options.success, scope, args); + Ext.callback(options.callback, scope, args); + } + + return instance; + } + }; + }, + + + read: function(record, reader, associationData){ + record[this.instanceName] = reader.read([associationData]).records[0]; + } +}); + + + +Ext.define('Ext.util.Inflector', { + + + + singleton: true, + + + + + plurals: [ + [(/(quiz)$/i), "$1zes" ], + [(/^(ox)$/i), "$1en" ], + [(/([m|l])ouse$/i), "$1ice" ], + [(/(matr|vert|ind)ix|ex$/i), "$1ices" ], + [(/(x|ch|ss|sh)$/i), "$1es" ], + [(/([^aeiouy]|qu)y$/i), "$1ies" ], + [(/(hive)$/i), "$1s" ], + [(/(?:([^f])fe|([lr])f)$/i), "$1$2ves"], + [(/sis$/i), "ses" ], + [(/([ti])um$/i), "$1a" ], + [(/(buffal|tomat|potat)o$/i), "$1oes" ], + [(/(bu)s$/i), "$1ses" ], + [(/(alias|status|sex)$/i), "$1es" ], + [(/(octop|vir)us$/i), "$1i" ], + [(/(ax|test)is$/i), "$1es" ], + [(/^person$/), "people" ], + [(/^man$/), "men" ], + [(/^(child)$/), "$1ren" ], + [(/s$/i), "s" ], + [(/$/), "s" ] + ], + + + singulars: [ + [(/(quiz)zes$/i), "$1" ], + [(/(matr)ices$/i), "$1ix" ], + [(/(vert|ind)ices$/i), "$1ex" ], + [(/^(ox)en/i), "$1" ], + [(/(alias|status)es$/i), "$1" ], + [(/(octop|vir)i$/i), "$1us" ], + [(/(cris|ax|test)es$/i), "$1is" ], + [(/(shoe)s$/i), "$1" ], + [(/(o)es$/i), "$1" ], + [(/(bus)es$/i), "$1" ], + [(/([m|l])ice$/i), "$1ouse" ], + [(/(x|ch|ss|sh)es$/i), "$1" ], + [(/(m)ovies$/i), "$1ovie" ], + [(/(s)eries$/i), "$1eries"], + [(/([^aeiouy]|qu)ies$/i), "$1y" ], + [(/([lr])ves$/i), "$1f" ], + [(/(tive)s$/i), "$1" ], + [(/(hive)s$/i), "$1" ], + [(/([^f])ves$/i), "$1fe" ], + [(/(^analy)ses$/i), "$1sis" ], + [(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i), "$1$2sis"], + [(/([ti])a$/i), "$1um" ], + [(/(n)ews$/i), "$1ews" ], + [(/people$/i), "person" ], + [(/s$/i), "" ] + ], + + + uncountable: [ + "sheep", + "fish", + "series", + "species", + "money", + "rice", + "information", + "equipment", + "grass", + "mud", + "offspring", + "deer", + "means" + ], + + + singular: function(matcher, replacer) { + this.singulars.unshift([matcher, replacer]); + }, + + + plural: function(matcher, replacer) { + this.plurals.unshift([matcher, replacer]); + }, + + + clearSingulars: function() { + this.singulars = []; + }, + + + clearPlurals: function() { + this.plurals = []; + }, + + + isTransnumeral: function(word) { + return Ext.Array.indexOf(this.uncountable, word) != -1; + }, + + + pluralize: function(word) { + if (this.isTransnumeral(word)) { + return word; + } + + var plurals = this.plurals, + length = plurals.length, + tuple, regex, i; + + for (i = 0; i < length; i++) { + tuple = plurals[i]; + regex = tuple[0]; + + if (regex == word || (regex.test && regex.test(word))) { + return word.replace(regex, tuple[1]); + } + } + + return word; + }, + + + singularize: function(word) { + if (this.isTransnumeral(word)) { + return word; + } + + var singulars = this.singulars, + length = singulars.length, + tuple, regex, i; + + for (i = 0; i < length; i++) { + tuple = singulars[i]; + regex = tuple[0]; + + if (regex == word || (regex.test && regex.test(word))) { + return word.replace(regex, tuple[1]); + } + } + + return word; + }, + + + classify: function(word) { + return Ext.String.capitalize(this.singularize(word)); + }, + + + ordinalize: function(number) { + var parsed = parseInt(number, 10), + mod10 = parsed % 10, + mod100 = parsed % 100; + + + if (11 <= mod100 && mod100 <= 13) { + return number + "th"; + } else { + switch(mod10) { + case 1 : return number + "st"; + case 2 : return number + "nd"; + case 3 : return number + "rd"; + default: return number + "th"; + } + } + } +}, function() { + + var irregulars = { + alumnus: 'alumni', + cactus : 'cacti', + focus : 'foci', + nucleus: 'nuclei', + radius: 'radii', + stimulus: 'stimuli', + ellipsis: 'ellipses', + paralysis: 'paralyses', + oasis: 'oases', + appendix: 'appendices', + index: 'indexes', + beau: 'beaux', + bureau: 'bureaux', + tableau: 'tableaux', + woman: 'women', + child: 'children', + man: 'men', + corpus: 'corpora', + criterion: 'criteria', + curriculum: 'curricula', + genus: 'genera', + memorandum: 'memoranda', + phenomenon: 'phenomena', + foot: 'feet', + goose: 'geese', + tooth: 'teeth', + antenna: 'antennae', + formula: 'formulae', + nebula: 'nebulae', + vertebra: 'vertebrae', + vita: 'vitae' + }, + singular; + + for (singular in irregulars) { + this.plural(singular, irregulars[singular]); + this.singular(irregulars[singular], singular); + } +}); + + + +Ext.define('Ext.data.association.HasMany', { + extend: Ext.data.association.Association , + alternateClassName: 'Ext.data.HasManyAssociation', + + + alias: 'association.hasmany', + + + + + + + + + + + + + + + + constructor: function(config) { + var me = this, + ownerProto, + name; + + me.callParent(arguments); + + me.name = me.name || Ext.util.Inflector.pluralize(me.associatedName.toLowerCase()); + + ownerProto = me.ownerModel.prototype; + name = me.name; + + Ext.applyIf(me, { + storeName : name + "Store", + foreignKey: me.ownerName.toLowerCase() + "_id" + }); + + ownerProto[name] = me.createStore(); + }, + + + createStore: function() { + var that = this, + associatedModel = that.associatedModel, + storeName = that.storeName, + foreignKey = that.foreignKey, + primaryKey = that.primaryKey, + filterProperty = that.filterProperty, + autoLoad = that.autoLoad, + remoteFilter = that.remoteFilter, + storeConfig = that.storeConfig || {}; + + return function() { + var me = this, + config, filter, + modelDefaults = {}, + id; + + if (me[storeName] === undefined) { + id = me.get(primaryKey); + if (filterProperty) { + filter = { + property : filterProperty, + value : me.get(filterProperty), + exactMatch: true + }; + } else if (me.hasId(id)) { + + + filter = { + property : foreignKey, + value : id, + exactMatch: true + }; + } + + modelDefaults[foreignKey] = me.get(primaryKey); + + config = Ext.apply({}, storeConfig, { + model : associatedModel, + filters : filter ? [filter] : undefined, + remoteFilter : remoteFilter === true, + modelDefaults: modelDefaults, + disableMetaChangeEvent: true + }); + + me[storeName] = Ext.data.AbstractStore.create(config); + if (autoLoad) { + me[storeName].load(); + } + } + + return me[storeName]; + }; + }, + + + read: function(record, reader, associationData){ + var store = record[this.name](), + inverse, + items, iLen, i; + + store.add(reader.read(associationData).records); + + + + inverse = this.associatedModel.prototype.associations.findBy(function(assoc){ + return assoc.type === 'belongsTo' && assoc.associatedName === record.$className; + }); + + + if (inverse) { + items = store.data.items; + iLen = items.length; + + for (i = 0; i < iLen; i++) { + items[i][inverse.instanceName] = record; + } + } + } +}); + + + +Ext.define('Ext.data.association.HasOne', { + extend: Ext.data.association.Association , + alternateClassName: 'Ext.data.HasOneAssociation', + + alias: 'association.hasone', + + + + + + + + + + constructor: function(config) { + this.callParent(arguments); + + var me = this, + ownerProto = me.ownerModel.prototype, + associatedName = me.associatedName, + getterName = me.getterName || 'get' + associatedName, + setterName = me.setterName || 'set' + associatedName; + + Ext.applyIf(me, { + name : associatedName, + foreignKey : associatedName.toLowerCase() + "_id", + instanceName: associatedName + 'HasOneInstance', + associationKey: associatedName.toLowerCase() + }); + + ownerProto[getterName] = me.createGetter(); + ownerProto[setterName] = me.createSetter(); + }, + + + createSetter: function() { + var me = this, + foreignKey = me.foreignKey, + instanceName = me.instanceName; + + + return function(value, options, scope) { + + var setByRecord = value && value.isModel, + valueToSet = setByRecord ? value.getId() : value; + + + if (setByRecord) { + this[instanceName] = value; + } + + + else if (this[instanceName] instanceof Ext.data.Model && !this.isEqual(this.get(foreignKey), valueToSet)) { + delete this[instanceName]; + } + + + this.set(foreignKey, valueToSet); + + if (Ext.isFunction(options)) { + options = { + callback: options, + scope: scope || this + }; + } + + if (Ext.isObject(options)) { + return this.save(options); + } + }; + }, + + + createGetter: function() { + var me = this, + ownerModel = me.ownerModel, + associatedName = me.associatedName, + associatedModel = me.associatedModel, + foreignKey = me.foreignKey, + primaryKey = me.primaryKey, + instanceName = me.instanceName; + + + return function(options, scope) { + options = options || {}; + + var model = this, + foreignKeyId = model.get(foreignKey), + success, + instance, + args; + + if (options.reload === true || model[instanceName] === undefined) { + + if (Ext.isEmpty(foreignKeyId)) { + return null; + } + instance = Ext.ModelManager.create({}, associatedName); + instance.set(primaryKey, foreignKeyId); + + if (typeof options == 'function') { + options = { + callback: options, + scope: scope || model + }; + } + + + success = options.success; + options.success = function(rec){ + model[instanceName] = rec; + if (success) { + success.apply(this, arguments); + } + }; + + associatedModel.load(foreignKeyId, options); + + model[instanceName] = instance; + return instance; + } else { + instance = model[instanceName]; + args = [instance]; + scope = scope || options.scope || model; + + + + + if (options) { + Ext.callback(options, scope, args); + Ext.callback(options.success, scope, args); + Ext.callback(options.callback, scope, args); + } + + return instance; + } + }; + }, + + + read: function(record, reader, associationData){ + var inverse = this.associatedModel.prototype.associations.findBy(function(assoc){ + return assoc.type === 'belongsTo' && assoc.associatedName === record.$className; + }), newRecord = reader.read([associationData]).records[0]; + + record[this.instanceName] = newRecord; + + + if (inverse) { + newRecord[inverse.instanceName] = record; + } + } +}); + + + +Ext.define('Ext.data.proxy.WebStorage', { + extend: Ext.data.proxy.Client , + alternateClassName: 'Ext.data.WebStorageProxy', + + + + + + id: undefined, + + + + + + + constructor: function(config) { + this.callParent(arguments); + + + this.cache = {}; + + if (this.getStorageObject() === undefined) { + Ext.Error.raise("Local Storage is not supported in this browser, please use another type of data proxy"); + } + + + this.id = this.id || (this.store ? this.store.storeId : undefined); + + if (this.id === undefined) { + Ext.Error.raise("No unique id was provided to the local storage proxy. See Ext.data.proxy.LocalStorage documentation for details"); + } + + this.initialize(); + }, + + + create: function(operation, callback, scope) { + var me = this, + records = operation.records, + length = records.length, + ids = me.getIds(), + id, record, i; + + operation.setStarted(); + + if(me.isHierarchical === undefined) { + + + me.isHierarchical = !!records[0].isNode; + if(me.isHierarchical) { + me.getStorageObject().setItem(me.getTreeKey(), true); + } + } + + for (i = 0; i < length; i++) { + record = records[i]; + + if (record.phantom) { + record.phantom = false; + id = me.getNextId(); + } else { + id = record.getId(); + } + + me.setRecord(record, id); + record.commit(); + ids.push(id); + } + + me.setIds(ids); + + operation.setCompleted(); + operation.setSuccessful(); + + if (typeof callback == 'function') { + callback.call(scope || me, operation); + } + }, + + + read: function(operation, callback, scope) { + + + var me = this, + records = [], + i = 0, + success = true, + Model = me.model, + ids, length, record, data, id; + + operation.setStarted(); + + if(me.isHierarchical) { + records = me.getTreeData(); + } else { + ids = me.getIds(); + length = ids.length; + id = operation.id; + + if (id) { + data = me.getRecord(id); + if (data !== null) { + record = new Model(data, id, data); + } + + if (record) { + records.push(record); + } else { + success = false; + } + } else { + for (; i < length; i++) { + id = ids[i]; + data = me.getRecord(id); + records.push(new Model(data, id, data)); + } + } + + } + + if(success) { + operation.setSuccessful(); + } + operation.setCompleted(); + + operation.resultSet = Ext.create('Ext.data.ResultSet', { + records: records, + total : records.length, + loaded : true + }); + + if (typeof callback == 'function') { + callback.call(scope || me, operation); + } + }, + + + update: function(operation, callback, scope) { + var records = operation.records, + length = records.length, + ids = this.getIds(), + record, id, i; + + operation.setStarted(); + + for (i = 0; i < length; i++) { + record = records[i]; + this.setRecord(record); + record.commit(); + + + + id = record.getId(); + if (id !== undefined && Ext.Array.indexOf(ids, id) == -1) { + ids.push(id); + } + } + this.setIds(ids); + + operation.setCompleted(); + operation.setSuccessful(); + + if (typeof callback == 'function') { + callback.call(scope || this, operation); + } + }, + + + destroy: function(operation, callback, scope) { + var me = this, + records = operation.records, + ids = me.getIds(), + idLength = ids.length, + newIds = [], + removedHash = {}, + i = records.length, + id; + + operation.setStarted(); + + for (; i--;) { + Ext.apply(removedHash, me.removeRecord(records[i])); + } + + for(i = 0; i < idLength; i++) { + id = ids[i]; + if(!removedHash[id]) { + newIds.push(id); + } + } + + me.setIds(newIds); + + operation.setCompleted(); + operation.setSuccessful(); + + if (typeof callback == 'function') { + callback.call(scope || me, operation); + } + }, + + + getRecord: function(id) { + var me = this, + cache = me.cache, + data = !cache[id] ? Ext.decode(me.getStorageObject().getItem(me.getRecordKey(id))) : cache[id]; + + if(!data) { + return null; + } + + cache[id] = data; + data[me.model.prototype.idProperty] = id; + + return data; + }, + + + setRecord: function(record, id) { + if (id) { + record.setId(id); + } else { + id = record.getId(); + } + + var me = this, + rawData = record.data, + data = {}, + model = me.model, + fields = model.prototype.fields.items, + length = fields.length, + i = 0, + field, name, obj, key; + + for (; i < length; i++) { + field = fields[i]; + name = field.name; + + if(field.persist) { + data[name] = rawData[name]; + } + } + + + delete data[me.model.prototype.idProperty]; + + + if(record.isNode && record.get('depth') === 1) { + delete data.parentId; + } + + obj = me.getStorageObject(); + key = me.getRecordKey(id); + + + me.cache[id] = data; + + + obj.removeItem(key); + obj.setItem(key, Ext.encode(data)); + }, + + + removeRecord: function(record) { + var me = this, + id = record.getId(), + records = {}, + i, childNodes; + + records[id] = record; + me.getStorageObject().removeItem(me.getRecordKey(id)); + delete me.cache[id]; + + if(record.childNodes) { + childNodes = record.childNodes; + for(i = childNodes.length; i--;) { + Ext.apply(records, me.removeRecord(childNodes[i])); + } + } + + return records; + }, + + + getRecordKey: function(id) { + if (id.isModel) { + id = id.getId(); + } + + return Ext.String.format("{0}-{1}", this.id, id); + }, + + + getRecordCounterKey: function() { + return Ext.String.format("{0}-counter", this.id); + }, + + + getTreeKey: function() { + return Ext.String.format("{0}-tree", this.id); + }, + + + getIds: function() { + var me = this, + ids = (me.getStorageObject().getItem(me.id) || "").split(","), + model = me.model, + length = ids.length, + isString = model.prototype.fields.get(model.prototype.idProperty).type.type === 'string', + i; + + if (length == 1 && ids[0] === "") { + ids = []; + } else { + for (i = 0; i < length; i++) { + ids[i] = isString ? ids[i] : +ids[i]; + } + } + + return ids; + }, + + + setIds: function(ids) { + var obj = this.getStorageObject(), + str = ids.join(","); + + obj.removeItem(this.id); + + if (!Ext.isEmpty(str)) { + obj.setItem(this.id, str); + } + }, + + + getNextId: function() { + var me = this, + obj = me.getStorageObject(), + key = me.getRecordCounterKey(), + model = me.model, + isString = model.prototype.fields.get(model.prototype.idProperty).type.type === 'string', + id; + + id = me.idGenerator.generate(); + + obj.setItem(key, id); + + if(!isString) { + id = +id; + } + + return id; + }, + + + getTreeData: function() { + var me = this, + ids = me.getIds(), + length = ids.length, + records = [], + recordHash = {}, + root = [], + i = 0, + Model = me.model, + idProperty = Model.prototype.idProperty, + rootLength, record, parent, parentId, children, id; + + for(; i < length; i++) { + id = ids[i]; + + record = me.getRecord(id); + + records.push(record); + + recordHash[id] = record; + if(!record.parentId) { + + root.push(record); + } + } + + rootLength = root.length; + + + Ext.Array.sort(records, me.sortByParentId); + + + for(i = rootLength; i < length; i++) { + record = records[i]; + parentId = record.parentId; + if(!parent || parent[idProperty] !== parentId) { + + parent = recordHash[parentId]; + parent.children = children = []; + } + + + children.push(record); + } + + for(i = length; i--;) { + record = records[i]; + if(!record.children && !record.leaf) { + + record.loaded = true; + } + } + + + for(i = rootLength; i--;) { + record = root[i]; + root[i] = new Model(record, record[idProperty], record); + } + + return root; + }, + + + sortByParentId: function(node1, node2) { + return (node1.parentId || 0) - (node2.parentId || 0); + }, + + + initialize: function() { + var me = this, + storageObject = me.getStorageObject(), + lastId = +storageObject.getItem(me.getRecordCounterKey()); + + storageObject.setItem(me.id, storageObject.getItem(me.id) || ""); + if(storageObject.getItem(me.getTreeKey())) { + me.isHierarchical = true; + } + + me.idGenerator = new Ext.data.SequentialIdGenerator({ + seed: lastId ? lastId + 1 : 1 + }); + }, + + + clear: function() { + var me = this, + obj = me.getStorageObject(), + ids = me.getIds(), + len = ids.length, + i; + + + for (i = 0; i < len; i++) { + obj.removeItem(me.getRecordKey(ids[i])); + } + + + obj.removeItem(me.getRecordCounterKey()); + obj.removeItem(me.getTreeKey()); + obj.removeItem(me.id); + + + me.cache = {}; + }, + + + getStorageObject: function() { + Ext.Error.raise("The getStorageObject function has not been defined in your Ext.data.proxy.WebStorage subclass"); + } +}); + + + +Ext.define('Ext.data.proxy.LocalStorage', { + extend: Ext.data.proxy.WebStorage , + alias: 'proxy.localstorage', + alternateClassName: 'Ext.data.LocalStorageProxy', + + + getStorageObject: function() { + return window.localStorage; + } +}); + + + +Ext.define('Ext.data.proxy.Rest', { + extend: Ext.data.proxy.Ajax , + alternateClassName: 'Ext.data.RestProxy', + alias : 'proxy.rest', + + + actionMethods: { + create : 'POST', + read : 'GET', + update : 'PUT', + destroy: 'DELETE' + }, + + defaultActionMethods: { + create : 'POST', + read : 'GET', + update : 'PUT', + destroy: 'DELETE' + }, + + + appendId: true, + + + + + batchActions: false, + + + buildUrl: function(request) { + var me = this, + operation = request.operation, + records = operation.records || [], + record = records[0], + format = me.format, + url = me.getUrl(request), + id = record ? record.getId() : operation.id; + + if (me.appendId && me.isValidId(id)) { + if (!url.match(/\/$/)) { + url += '/'; + } + + url += id; + } + + if (format) { + if (!url.match(/\.$/)) { + url += '.'; + } + + url += format; + } + + request.url = url; + + return me.callParent(arguments); + }, + + isValidId: function(id) { + return id || id === 0; + } +}); + + + +Ext.define('Ext.data.proxy.SessionStorage', { + extend: Ext.data.proxy.WebStorage , + alias: 'proxy.sessionstorage', + alternateClassName: 'Ext.data.SessionStorageProxy', + + + getStorageObject: function() { + return window.sessionStorage; + } +}); + + + +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; + }, + + + 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); + }, + + + getCount : function(){ + return this.length; + }, + + getKey : function(obj){ + return obj.id; + }, + + + 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; + } +}); + + + +Ext.define('Ext.layout.ClassList', (function () { + + var splitWords = Ext.String.splitWords, + toMap = Ext.Array.toMap; + + return { + dirty: false, + + constructor: function (owner) { + this.owner = owner; + this.map = toMap(this.classes = splitWords(owner.el.className)); + }, + + + add: function (cls) { + var me = this; + + if (!me.map[cls]) { + me.map[cls] = true; + me.classes.push(cls); + if (!me.dirty) { + me.dirty = true; + me.owner.markDirty(); + } + } + }, + + + addMany: function (classes) { + Ext.each(splitWords(classes), this.add, this); + }, + + contains: function (cls) { + return this.map[cls]; + }, + + flush: function () { + this.owner.el.className = this.classes.join(' '); + this.dirty = false; + }, + + + remove: function (cls) { + var me = this; + + if (me.map[cls]) { + delete me.map[cls]; + me.classes = Ext.Array.filter(me.classes, function (c) { + return c != cls; + }); + if (!me.dirty) { + me.dirty = true; + me.owner.markDirty(); + } + } + }, + + + removeMany: function (classes) { + var me = this, + remove = toMap(splitWords(classes)); + + me.classes = Ext.Array.filter(me.classes, function (c) { + if (!remove[c]) { + return true; + } + + delete me.map[c]; + if (!me.dirty) { + me.dirty = true; + me.owner.markDirty(); + } + return false; + }); + } + }; +}())); + + + +Ext.define('Ext.layout.ContextItem', { + + + + heightModel: null, + widthModel: null, + sizeModel: null, + + + optOut: false, + + ownerSizePolicy: null, + + boxChildren: null, + + boxParent: null, + isBorderBoxValue: null, + + children: [], + + dirty: null, + + + dirtyCount: 0, + + hasRawContent: true, + + isContextItem: true, + + isTopLevel: false, + + consumersContentHeight: 0, + consumersContentWidth: 0, + consumersContainerHeight: 0, + consumersContainerWidth: 0, + consumersHeight: 0, + consumersWidth: 0, + + ownerCtContext: null, + + remainingChildDimensions: 0, + + + props: null, + + + state: null, + + + 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); + + el = me.el; + me.id = el.id; + + + + + + + + + + + + + + + + + + + + + + me.flushedProps = {}; + me.props = props = {}; + + + me.styles = {}; + + target = me.target; + + 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; + + + + ownerCt = target.ownerCt; + if (ownerCt && (ownerCtContext = ownerCt.el && me.context.items[ownerCt.el.id])) { + me.ownerCtContext = ownerCtContext; + } + + + + me.sizeModel = sizeModel = target.getSizeModel(ownerCtContext && + ownerCtContext.widthModel.pairsByHeightOrdinal[ownerCtContext.heightModel.ordinal]); + + + + + + + + me.widthModel = widthModel = sizeModel.width; + me.heightModel = heightModel = sizeModel.height; + + + + + 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) { + + me.optOut = true; + props.width = lastWidth; + props.height = lastHeight; + } + } + } + + me.lastBox = lastBox; + }, + + + 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 = {}; + + + me.remainingChildDimensions = 0; + + if (me.boxChildren) { + me.boxChildren.length = 0; + } + + if (!firstTime) { + me.clearAllBlocks('blocks'); + me.clearAllBlocks('domBlocks'); + } + + + if (!me.wrapsComponent) { + return ret; + } + + + target = me.target; + me.state = {}; + + if (firstTime) { + + + if (target.beforeLayout && target.beforeLayout !== Ext.emptyFn) { + target.beforeLayout(); + } + + + + + + + + if (!ownerCtContext && (ownerCt = target.ownerCt)) { + ownerCtContext = me.context.items[ownerCt.el.id]; + } + + if (ownerCtContext) { + me.ownerCtContext = ownerCtContext; + me.isBoxParent = target.ownerLayout.isItemBoxParent(me); + } else { + me.isTopLevel = true; + } + + me.frameBodyContext = me.getEl('frameBody'); + } else { + ownerCtContext = me.ownerCtContext; + + + me.isTopLevel = !ownerCtContext; + + + + children = me.children; + for (i = 0, n = children.length; i < n; ++i) { + children[i].init(true); + } + } + + + + + me.hasRawContent = !(target.isContainer && target.items.items.length > 0); + + if (full) { + + + 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 (ownerCtContext && !me.isComponentChild) { + ownerCtContext.remainingChildDimensions += 2; + } + } else if (oldProps) { + + + me.recoverProp('x', oldProps, oldDirty); + me.recoverProp('y', oldProps, oldDirty); + + + 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 (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); + } + + + + if (options) { + + + + + + + + + + + + + + + + + + + + + + newHeightModel = options.heightModel; + newWidthModel = options.widthModel; + if (newWidthModel && newHeightModel && oldWidthModel && oldHeightModel) { + if (oldWidthModel.shrinkWrap && oldHeightModel.shrinkWrap) { + if (newWidthModel.constrainedMax && newHeightModel.constrainedMin) { + newHeightModel = null; + } + } + } + + + if (newWidthModel) { + me.widthModel = newWidthModel; + } + if (newHeightModel) { + me.heightModel = newHeightModel; + } + + if (options.state) { + Ext.apply(me.state, options.state); + } + } + + return ret; + }, + + + initContinue: function (full) { + var me = this, + ownerCtContext = me.ownerCtContext, + comp = me.target, + widthModel = me.widthModel, + hierarchyState = comp.getHierarchyState(), + boxParent; + + if (widthModel.fixed) { + hierarchyState.inShrinkWrapTable = false; + } else { + delete hierarchyState.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; + }, + + + initDone: function(containerLayoutDone) { + var me = this, + props = me.props, + state = me.state; + + + if (me.remainingChildDimensions === 0) { + props.containerChildrenSizeDone = true; + } + if (containerLayoutDone) { + props.containerLayoutDone = true; + } + + if (me.boxChildren && me.boxChildren.length && me.widthModel.shrinkWrap) { + + + me.el.setWidth(10000); + + + state.blocks = (state.blocks || 0) + 1; + } + }, + + + initAnimation: function() { + var me = this, + target = me.target, + ownerCtContext = me.ownerCtContext; + + if (ownerCtContext && ownerCtContext.isTopLevel) { + + + me.animatePolicy = target.ownerLayout.getAnimatePolicy(me); + } else if (!ownerCtContext && target.isCollapsingOrExpanding && target.animCollapse) { + + + + me.animatePolicy = target.componentLayout.getAnimatePolicy(me); + } + + if (me.animatePolicy) { + me.context.queueAnimation(me); + } + }, + + + addCls: function(newCls) { + this.getClassList().addMany(newCls); + }, + + + removeCls: function(removeCls) { + this.getClassList().removeMany(removeCls); + }, + + + 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; + + + + + + + + boxChildItem.measuresBox = widthModel.shrinkWrap ? boxChildItem.hasRawContent : widthModel.natural; + + if (boxChildItem.measuresBox) { + children = me.boxChildren; + + if (children) { + children.push(boxChildItem); + } else { + me.boxChildren = [ boxChildItem ]; + } + } + }, + + + 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; + }, + + + 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) { + + + 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) { + + 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); + } + } + }, + + + 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); + } + } + } + }, + + + block: function (layout, propName) { + this.addBlock('blocks', layout, propName); + }, + + + domBlock: function (layout, propName) { + this.addBlock('domBlocks', layout, propName); + }, + + + 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); + } + } + } + }, + + + flush: function () { + var me = this, + dirty = me.dirty, + state = me.state, + targetEl = me.el; + + me.dirtyCount = 0; + + + if (me.classList && me.classList.dirty) { + me.classList.flush(); + } + + + if ('attributes' in me) { + targetEl.set(me.attributes); + delete me.attributes; + } + + + 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); + } + }, + + + flushAnimations: function() { + var me = this, + animateFrom = me.previousSize, + target, targetAnim, duration, animateProps, anim, + changeCount, j, propsLen, propName, oldValue, newValue; + + + if (animateFrom) { + target = me.target; + targetAnim = target.getAnimationProps(); + duration = targetAnim.duration; + animateProps = Ext.Object.getKeys(me.animatePolicy); + + + + 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 (changeCount) { + + if (me.isCollapsingOrExpanding === 1) { + target.componentLayout.undoLayout(me); + } + + + else { + me.writeProps(anim.from); + } + me.el.animate(anim); + + Ext.fx.Manager.getFxQueue(me.el.id)[0].on({ + afteranimate: function() { + if (me.isCollapsingOrExpanding === 1) { + target.componentLayout.redoLayout(me); + target.afterCollapse(true); + } else if (me.isCollapsingOrExpanding === 2) { + target.afterExpand(true); + } + } + }); + } + } + }, + + + getBorderInfo: function () { + var me = this, + info = me.borderInfo; + + if (!info) { + me.borderInfo = info = me.checkCache('borderInfo'); + } + + return info; + }, + + + getClassList: function () { + return this.classList || (this.classList = new Ext.layout.ClassList(this)); + }, + + + 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') { + el = el.call(src); + if (el === me.el) { + return this; + } + } + } + + if (el) { + elContext = me.context.getEl(me, el); + } + } + + return elContext || null; + }, + + + 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; + }, + + + getMarginInfo: function () { + var me = this, + info = me.marginInfo, + comp, manageMargins, margins, 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; + + + + + + + + + + + + + + + + + info = comp.margin$; + if (info && info.ownerId !== ownerLayoutId) { + + info = null; + + + + + } + + if (!info) { + + info = me.parseMargins(comp, comp.margin) || me.checkCache('marginInfo'); + + + if (manageMargins) { + margins = me.parseMargins(comp, comp.margins, ownerLayout.defaultMargins); + + if (margins) { + + info = { + top: info.top + margins.top, + right: info.right + margins.right, + bottom: info.bottom + margins.bottom, + left: info.left + margins.left + }; + } + + me.setProp('margin-top', 0); + me.setProp('margin-right', 0); + me.setProp('margin-bottom', 0); + me.setProp('margin-left', 0); + } + + + info.ownerId = ownerLayoutId; + comp.margin$ = info; + } + + info.width = info.left + info.right; + info.height = info.top + info.bottom; + } + + me.marginInfo = info; + } + + return info; + }, + + + clearMarginCache: function() { + delete this.marginInfo; + delete this.target.margin$; + }, + + + getPaddingInfo: function () { + var me = this, + info = me.paddingInfo; + + if (!info) { + me.paddingInfo = info = me.checkCache('paddingInfo'); + } + + return info; + }, + + + getProp: function (propName) { + var me = this, + result = me.props[propName]; + + me.addTrigger(propName); + return result; + }, + + + getDomProp: function (propName) { + var me = this, + result = (me.dirty && (propName in me.dirty)) ? undefined : me.props[propName]; + + me.addTrigger(propName, true); + return result; + }, + + + 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; + }, + + + 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; + + + + for (i = 0; i < n; ++i) { + name = styleNames[i]; + + if (name in styleCache) { + values[altNames[i]] = styleCache[name]; + ++hits; + + if (i && hits==1) { + 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; + }, + + + hasProp: function (propName) { + return this.getProp(propName) != null; + }, + + + hasDomProp: function (propName) { + return this.getDomProp(propName) != null; + }, + + + invalidate: function (options) { + this.context.queueInvalidate(this, options); + }, + + markDirty: function () { + if (++this.dirtyCount == 1) { + + this.context.queueFlush(this); + } + }, + + onBoxMeasured: function () { + var boxParent = this.boxParent, + state = this.state; + + if (boxParent && boxParent.widthModel.shrinkWrap && !state.boxMeasured && this.measuresBox) { + + + state.boxMeasured = 1; + boxParent.boxChildMeasured(); + } + }, + + parseMargins: function (comp, margins, defaultMargins) { + if (margins === true) { + margins = 5; + } + + var type = typeof margins, + ret; + + if (type == 'string' || type == 'number') { + ret = comp.parseBox(margins); + } else if (margins || defaultMargins) { + ret = { top: 0, right: 0, bottom: 0, left: 0 }; + + if (defaultMargins) { + Ext.apply(ret, this.parseMargins(comp, defaultMargins)); + } + + if (margins) { + margins = Ext.apply(ret, comp.parseBox(margins)); + } + } + + return ret; + }, + + peek: function (propName) { + return this.props[propName]; + }, + + + 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) { + + if (me.childItems) { + for (i = 0, items = me.childItems, len = items.length; i < len; i++) { + items[i].redo(deep); + } + } + + + for (i = 0, items = me.children, len = items.length; i < len; i++) { + items[i].redo(); + } + } + }, + + + 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') { + el = el.call(src); + if (el === me.el) { + return this; + } + } + } + + if (el) { + me.context.removeEl(me, el); + } + } + }, + + revertProps: function (props) { + var name, + flushed = this.flushedProps, + reverted = {}; + + for (name in props) { + if (flushed.hasOwnProperty(name)) { + reverted[name] = props[name]; + } + } + + this.writeProps(reverted); + }, + + + 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); + } + + + + + me.setSize(box.width, box.height); + }, + + + setContentHeight: function (height, measured) { + if (!measured && this.hasRawContent) { + return 1; + } + + return this.setProp('contentHeight', height); + }, + + + setContentWidth: function (width, measured) { + if (!measured && this.hasRawContent) { + return 1; + } + + return this.setProp('contentWidth', width); + }, + + + setContentSize: function (width, height, measured) { + return this.setContentWidth(width, measured) + + this.setContentHeight(height, measured) == 2; + }, + + + setProp: function (propName, value, dirty) { + var me = this, + valueType = typeof value, + borderBox, 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) { + + + me.fireTriggers('domTriggers', propName); + me.clearBlocks('domBlocks', propName); + } else { + info = me.styleInfo[propName]; + if (info) { + if (!me.dirty) { + me.dirty = {}; + } + + if (propName == 'width' || propName == 'height') { + borderBox = me.isBorderBoxValue; + if (borderBox === null) { + me.isBorderBoxValue = borderBox = !!me.el.isBorderBox(); + } + + if (!borderBox) { + me.borderInfo || me.getBorderInfo(); + me.paddingInfo || me.getPaddingInfo(); + } + } + me.dirty[propName] = value; + me.markDirty(); + } + } + + + me.fireTriggers('triggers', propName); + me.clearBlocks('blocks', propName); + return 1; + }, + + + setHeight: function (height, dirty ) { + 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 (ownerCtContext && !me.isComponentChild && isNaN(oldHeight)) { + rem = --ownerCtContext.remainingChildDimensions; + if (!rem) { + + + + ownerCtContext.setProp('containerChildrenSizeDone', true); + } + } + + frameBody = me.frameBodyContext; + if (frameBody){ + frameInfo = me.getFrameInfo(); + frameBody.setHeight(height - frameInfo.height, dirty); + } + } + + return height; + }, + + + setWidth: function (width, dirty ) { + 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 (ownerCtContext && !me.isComponentChild && isNaN(oldWidth)) { + rem = --ownerCtContext.remainingChildDimensions; + if (!rem) { + + + + ownerCtContext.setProp('containerChildrenSizeDone', true); + } + } + + + frameBody = me.frameBodyContext; + if (frameBody) { + frameInfo = me.getFrameInfo(); + frameBody.setWidth(width - frameInfo.width, dirty); + } + + + } + + 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) { + + if (me.childItems) { + for (i = 0, items = me.childItems, len = items.length; i < len; i++) { + items[i].undo(deep); + } + } + + + 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, + styleInfo = me.styleInfo, + + info, + propName, + numericValue, + width = dirtyProps.width, + height = dirtyProps.height, + isBorderBox = me.isBorderBoxValue, + target = me.target, + max = Math.max, + paddingWidth = 0, + paddingHeight = 0, + hasWidth, hasHeight, isAbsolute, scrollbarSize, style, targetEl; + + + if ('displayed' in dirtyProps) { + el.setDisplayed(dirtyProps.displayed); + } + + + 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) { + + if (info.suffix && (numericValue = parseInt(dirtyProps[propName], 10))) { + styles[propName] = numericValue + info.suffix; + } + + else { + styles[propName] = dirtyProps[propName]; + } + ++styleCount; + } + } + + + if ('x' in dirtyProps || 'y' in dirtyProps) { + if (target.isComponent) { + target.setPosition(dirtyProps.x, dirtyProps.y); + } else { + + styleCount += me.addPositionStyles(styles, dirtyProps); + } + } + + + if (!isBorderBox && (width > 0 || height > 0)) { + + + if (!(me.borderInfo && me.paddingInfo)) { + throw Error("Needed to have gotten the borderInfo and paddingInfo when the width or height was setProp'd"); + } + if(!me.frameBodyContext) { + + paddingWidth = me.paddingInfo.width; + paddingHeight = me.paddingInfo.height; + } + if (width) { + width = max(parseInt(width, 10) - (me.borderInfo.width + paddingWidth), 0); + styles.width = width + 'px'; + ++styleCount; + } + if (height) { + height = max(parseInt(height, 10) - (me.borderInfo.height + paddingHeight), 0); + styles.height = height + 'px'; + ++styleCount; + } + } + + + + + + + + if (me.wrapsComponent && Ext.isIE9 && Ext.isStrict) { + + + if ((hasWidth = width !== undefined && me.hasOverflowY) || + (hasHeight = height !== undefined && me.hasOverflowX)) { + + isAbsolute = me.isAbsolute; + if (isAbsolute === undefined) { + isAbsolute = false; + targetEl = me.target.getTargetEl(); + style = targetEl.getStyle('position'); + + if (style == 'absolute') { + style = targetEl.getStyle('box-sizing'); + isAbsolute = (style == 'border-box'); + } + + me.isAbsolute = isAbsolute; + } + + 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; + } + } + } + } + + + if (styleCount) { + el.setStyle(styles); + } + } +}, function () { + + var px = { dom: true, parseInt: true, suffix: 'px' }, + isDom = { dom: true }, + faux = { dom: false }; + + + + + + + + + + this.prototype.styleInfo = { + containerChildrenSizeDone: faux, + containerLayoutDone: faux, + displayed: faux, + done: faux, + x: faux, + y: faux, + + + columnWidthsDone: 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 + }; +}); + + + +Ext.define('Ext.layout.Context', { + + + + + + + + + remainingLayouts: 0, + + + state: 0, + + constructor: function (config) { + var me = this; + + Ext.apply(me, config); + + + me.items = {}; + + + me.layouts = {}; + + + me.blockCount = 0; + + me.cycleCount = 0; + + me.flushCount = 0; + + 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 = {}; + + + me.layoutQueue = me.newQueue(); + + + + me.invalidQueue = []; + + me.triggers = { + data: { + + }, + 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 && comp.ownerCt) { + ownerCtContext = this.items[comp.ownerCt.el.id]; + if (ownerCtContext) { + Ext.Array.remove(ownerCtContext.childItems, me.getCmp(comp)); + } + } + + 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.isDescendant(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]; + } + }, + + + 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++) { + + + + if (items[i].target.animate !== false) { + items[i].flushAnimations(); + } + } + + + + 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; + + + + + 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; + } + }, + + + getCmp: function (cmp) { + return this.getItem(cmp, cmp.el); + }, + + + getEl: function (parent, el) { + var item = this.getItem(el, el); + + if (!item.parent) { + item.parent = parent; + + + + + if (parent.children.length) { + parent.children.push(item); + } else { + parent.children = [ item ]; + } + } + + 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 () { + + + + 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.repoDevMode && !this.pageAnalyzerMode) { + Ext.Error.raise('Layout run failed'); + } else { + Ext.log.error('Layout run failed'); + } + }, + + + invalidate: function (components, full) { + var me = this, + isArray = !components.isComponent, + containerLayoutDone, + firstTime, i, comp, item, items, length, componentLayout, layout, + invalidateOptions, token; + + for (i = 0, length = isArray ? components.length : 1; i < length; ++i) { + comp = isArray ? components[i] : components; + + if (comp.rendered && !comp.hidden) { + item = me.getCmp(comp); + + + + + + + + componentLayout = comp.componentLayout; + firstTime = !componentLayout.ownerContext; + layout = (comp.isContainer && !comp.collapsed) ? comp.layout : null; + + + invalidateOptions = me.invalidateData[item.id]; + delete me.invalidateData[item.id]; + + + + + + + + token = item.init(full, invalidateOptions); + + if (invalidateOptions) { + me.processInvalidate(invalidateOptions, item, 'before'); + } + + + + + if (componentLayout.beforeLayoutCycle) { + componentLayout.beforeLayoutCycle(item); + } + + if (layout && layout.beforeLayoutCycle) { + + + + + layout.beforeLayoutCycle(item); + } + + + + token = item.initContinue(token); + + + + containerLayoutDone = true; + + + + + + if (componentLayout.getLayoutItems) { + componentLayout.renderChildren(); + + items = componentLayout.getLayoutItems(); + if (items.length) { + me.invalidate(items, true); + } + } + + if (layout) { + containerLayoutDone = false; + layout.renderChildren(); + + items = layout.getVisibleItems(); + if (items.length) { + me.invalidate(items, true); + } + } + + + + item.initDone(containerLayoutDone); + + + + me.resetLayout(componentLayout, item, firstTime); + if (layout) { + me.resetLayout(layout, item, firstTime); + } + + + + + item.initAnimation(); + + if (invalidateOptions) { + me.processInvalidate(invalidateOptions, item, 'after'); + } + } + } + + me.currentLayout = null; + }, + + layoutDone: function (layout) { + var ownerContext = layout.ownerContext; + + layout.running = false; + + + if (layout.isComponentLayout) { + if (ownerContext.measuresBox) { + ownerContext.onBoxMeasured(); + } + + ownerContext.setProp('done', true); + } else { + ownerContext.setProp('containerLayoutDone', true); + } + + --this.remainingLayouts; + ++this.progressCount; + }, + + newQueue: function () { + return new Ext.util.Queue(); + }, + + processInvalidate: function (options, item, name) { + + + if (options[name]) { + var me = this, + currentLayout = me.currentLayout; + + me.currentLayout = options.layout || null; + + options[name](item, options); + + me.currentLayout = currentLayout; + } + }, + + + queueAnimation: function (item) { + this.animateQueue.add(item); + }, + + + queueCompletion: function (layout) { + this.completionQueue.add(layout); + }, + + + queueFinalize: function (layout) { + this.finalizeQueue.add(layout); + }, + + + 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]; + + + + 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; + }; + }, + + + queueInvalidate: function (item, options) { + var me = this, + newQueue = [], + oldQueue = me.invalidQueue, + index = oldQueue.length, + comp, old, oldComp, oldOptions, oldState; + + if (item.isComponent) { + item = me.getCmp(comp = item); + } else { + comp = item.target; + } + + item.invalid = true; + + + + + while (index--) { + old = oldQueue[index]; + oldComp = old.item.target; + + if (comp.isDescendant(oldComp)) { + return; + } + + if (oldComp == comp) { + + 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'); + } + } + + + return; + } + + if (!oldComp.isDescendant(comp)) { + newQueue.push(old); + } + + } + + + + + 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) { + this.queueLayout(layout); + } + }, + + + queueLayout: function (layout) { + this.layoutQueue.add(layout); + layout.pending = true; + }, + + + removeEl: function (parent, el) { + var id = el.id, + children = parent.children, + items = this.items; + + if(children) { + Ext.Array.remove(children, items[id]); + } + delete items[id]; + }, + + + 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; + layout.running = true; + + if (layout.finishedLayout) { + me.finishQueue.add(layout); + } + + + + ++me.remainingLayouts; + ++layout.layoutCount; + + layout.ownerContext = ownerContext; + layout.beginCount = 0; + layout.blockCount = 0; + layout.calcCount = 0; + layout.triggerCount = 0; + + if (!layout.initialized) { + layout.initLayout(); + } + + layout.beginLayout(ownerContext); + } else { + ++layout.beginCount; + + if (!layout.running) { + + ++me.remainingLayouts; + layout.running = true; + + if (layout.isComponentLayout) { + + + + ownerContext.unsetProp('done'); + } + + + me.completionQueue.remove(layout); + me.finalizeQueue.remove(layout); + } + } + + layout.beginLayoutCycle(ownerContext, firstTime); + }, + + + run: function () { + var me = this, + flushed = false, + watchDog = 100; + + me.flushInvalidates(); + + me.state = 1; + me.totalCount = me.layoutQueue.getCount(); + + + + + + + + me.flush(); + + + while ((me.remainingLayouts || me.invalidQueue.length) && watchDog--) { + if (me.invalidQueue.length) { + me.flushInvalidates(); + } + + + if (me.runCycle()) { + flushed = false; + + } else if (!flushed) { + + + me.flush(); + flushed = true; + + me.flushLayouts('completionQueue', 'completeLayout'); + } else if (!me.invalidQueue.length) { + + 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(); + + + me.flushLayouts('finishQueue', 'finishedLayout', true); + + + me.flushLayouts('finishQueue', 'notifyOwner'); + + me.flush(); + + me.flushAnimations(); + + return true; + }, + + + runCycle: function () { + var me = this, + layouts = me.layoutQueue.clear(), + length = layouts.length, + i; + + ++me.cycleCount; + + + + me.progressCount = 0; + + for (i = 0; i < length; ++i) { + me.runLayout(me.currentLayout = layouts[i]); + } + + me.currentLayout = null; + + return me.progressCount > 0; + }, + + + runLayout: function (layout) { + var me = this, + ownerContext = me.getCmp(layout.owner); + + layout.pending = false; + + if (ownerContext.state.blocks) { + return; + } + + + + + + 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)) { + + + me.queueLayout(layout); + } + }, + + + setItemSize: function(item, width, height) { + var items = item, + len = 1, + contextItem, i; + + + + + + + + if (item.isComposite) { + items = item.elements; + len = items.length; + item = items[0]; + } else if (!item.dom && !item.el) { + len = items.length; + item = items[0]; + } + + + for (i = 0; i < len; ) { + contextItem = this.get(item); + contextItem.setSize(width, height); + + item = items[++i]; + } + } +}); + + + +Ext.define('Ext.direct.Event', { + alias: 'direct.event', + + status: true, + + + constructor: function(config) { + Ext.apply(this, config); + }, + + + getName: function() { + return this.name; + }, + + + getData: function() { + return this.data; + } +}); + + + +Ext.define('Ext.direct.RemotingEvent', { + extend: Ext.direct.Event , + alias: 'direct.rpc', + + + getTransaction: function() { + var me = this; + + return me.transaction || Ext.direct.Manager.getTransaction(me.tid); + } +}); + + + +Ext.define('Ext.direct.ExceptionEvent', { + extend: Ext.direct.RemotingEvent , + alias: 'direct.exception', + + status: false +}); + + + + +Ext.define('Ext.direct.JsonProvider', { + extend: Ext.direct.Provider , + alias: 'direct.jsonprovider', + + + + + + + + parseResponse: function(response) { + if (!Ext.isEmpty(response.responseText)) { + if (Ext.isObject(response.responseText)) { + return response.responseText; + } + + return Ext.decode(response.responseText); + } + + return null; + }, + + + createEvents: function(response) { + var me = this, + data = null, + events = [], + event, i, len; + + try { + data = me.parseResponse(response); + } + catch (e) { + event = new Ext.direct.ExceptionEvent({ + data: e, + xhr: response, + code: Ext.direct.Manager.exceptions.PARSE, + message: 'Error parsing json response: \n\n ' + e + }); + + return [event]; + } + + if (Ext.isArray(data)) { + for (i = 0, len = data.length; i < len; ++i) { + events.push(me.createEvent(data[i])); + } + } + else if (Ext.isObject(data)) { + events.push(me.createEvent(data)); + } + + return events; + }, + + + createEvent: function(response) { + if (typeof response !== 'object'|| !('type' in response)) { + return new Ext.direct.ExceptionEvent({ + data: response, + code: Ext.direct.Manager.exceptions.DATA, + message: 'Invalid data: event type is not specified' + }); + } + + return Ext.create('direct.' + response.type, response); + } +}); + + + +Ext.define('Ext.direct.PollingProvider', { + extend: Ext.direct.JsonProvider , + alias: 'direct.pollingprovider', + + + + + + + + + + + + + interval: 3000, + + + + + + constructor: function(config) { + var me = this; + + me.callParent(arguments); + + me.addEvents( + + 'beforepoll', + + + 'poll' + ); + }, + + + isConnected: function() { + return !!this.pollTask; + }, + + + connect: function() { + var me = this, + url = me.url; + + if (url && !me.pollTask) { + me.pollTask = Ext.TaskManager.start({ + run: me.runPoll, + interval: me.interval, + scope: me + }); + + me.fireEvent('connect', me); + } + else if (!url) { + Ext.Error.raise('Error initializing PollingProvider, no url configured.'); + } + }, + + + disconnect: function() { + var me = this; + + if (me.pollTask) { + Ext.TaskManager.stop(me.pollTask); + delete me.pollTask; + me.fireEvent('disconnect', me); + } + }, + + + runPoll: function() { + var me = this, + url = me.url; + + if (me.fireEvent('beforepoll', me) !== false) { + if (Ext.isFunction(url)) { + url(me.baseParams); + } + else { + Ext.Ajax.request({ + url: url, + callback: me.onData, + scope: me, + params: me.baseParams + }); + } + + me.fireEvent('poll', me); + } + }, + + + onData: function(opt, success, response) { + var me = this, + i, len, events; + + if (success) { + events = me.createEvents(response); + + for (i = 0, len = events.length; i < len; ++i) { + me.fireEvent('data', me, events[i]); + } + } + else { + events = new Ext.direct.ExceptionEvent({ + data: null, + code: Ext.direct.Manager.exceptions.TRANSPORT, + message: 'Unable to connect to the server.', + xhr: response + }); + + me.fireEvent('data', me, events); + } + } +}); + + + +Ext.define('Ext.direct.RemotingMethod', { + + constructor: function(config) { + var me = this, + params = Ext.isDefined(config.params) ? config.params : config.len, + name, pLen, p, param; + + me.name = config.name; + me.formHandler = config.formHandler; + + if (Ext.isNumeric(params)) { + + me.len = params; + me.ordered = true; + } + else { + + me.strict = config.strict !== undefined ? config.strict : true; + me.params = {}; + pLen = params.length; + + for (p = 0; p < pLen; p++) { + param = params[p]; + name = Ext.isObject(param) ? param.name : param; + me.params[name] = true; + } + } + }, + + getArgs: function(params, paramOrder, paramsAsHash) { + var me = this, + args = [], + i, len; + + if (me.ordered) { + if (me.len > 0) { + + if (paramOrder) { + for (i = 0, len = paramOrder.length; i < len; i++) { + args.push(params[paramOrder[i]]); + } + } + else if (paramsAsHash) { + + args.push(params); + } + } + } + else { + args.push(params); + } + + return args; + }, + + + getCallData: function(args) { + var me = this, + data = null, + len = me.len, + params = me.params, + strict = me.strict, + callback, scope, name, options; + + if (me.ordered) { + callback = args[len]; + scope = args[len + 1]; + options = args[len + 2]; + + if (len !== 0) { + data = args.slice(0, len); + } + } + else { + data = Ext.apply({}, args[0]); + callback = args[1]; + scope = args[2]; + options = args[3]; + + + if (strict) { + for (name in data) { + if (data.hasOwnProperty(name) && !params[name]) { + delete data[name]; + } + } + } + } + + return { + data: data, + callback: callback, + scope: scope, + options: options + }; + } +}); + + + +Ext.define('Ext.direct.Transaction', { + alias: 'direct.transaction', + alternateClassName: 'Ext.Direct.Transaction', + + statics: { + TRANSACTION_ID: 0 + }, + + + + + constructor: function(config) { + var me = this; + + Ext.apply(me, config); + + me.id = me.tid = ++me.self.TRANSACTION_ID; + me.retryCount = 0; + }, + + send: function() { + var me = this; + + me.provider.queueTransaction(me); + }, + + retry: function() { + var me = this; + + me.retryCount++; + me.send(); + }, + + getProvider: function() { + return this.provider; + } +}); + + + +Ext.define('Ext.direct.RemotingProvider', { + extend: Ext.direct.JsonProvider , + alias: 'direct.remotingprovider', + + + + + + + + + + + + + + + + + + + + enableBuffer: 10, + + + maxRetries: 1, + + + + constructor: function(config) { + var me = this; + + me.callParent(arguments); + + me.addEvents( + + 'beforecall', + + + 'call', + + + 'beforecallback' + ); + + me.namespace = (Ext.isString(me.namespace)) ? Ext.ns(me.namespace) : me.namespace || Ext.global; + me.transactions = new Ext.util.MixedCollection(); + me.callBuffer = []; + }, + + + getNamespace: function(root, action) { + var parts, ns, i, l; + + root = root || Ext.global; + parts = action.toString().split('.'); + + for (i = 0, l = parts.length; i < l; i++) { + ns = parts[i]; + root = root[ns]; + + if (typeof root === 'undefined') { + return root; + } + } + + return root; + }, + + + createNamespaces: function(root, action) { + var parts, ns; + + root = root || Ext.global; + parts = action.toString().split('.'); + + for ( var i = 0, l = parts.length; i < l; i++ ) { + ns = parts[i]; + + root[ns] = root[ns] || {}; + root = root[ns]; + }; + + return root; + }, + + + initAPI: function() { + var me = this, + actions = me.actions, + namespace = me.namespace, + action, cls, methods, i, len, method; + + for (action in actions) { + if (actions.hasOwnProperty(action)) { + if (me.disableNestedActions) { + cls = namespace[action]; + + if (!cls) { + cls = namespace[action] = {}; + } + } + else { + cls = me.getNamespace(namespace, action); + + if (!cls) { + cls = me.createNamespaces(namespace, action); + } + } + + methods = actions[action]; + + for (i = 0, len = methods.length; i < len; ++i) { + method = new Ext.direct.RemotingMethod(methods[i]); + cls[method.name] = me.createHandler(action, method); + } + } + } + }, + + + createHandler: function(action, method) { + var me = this, + slice = Array.prototype.slice, + handler; + + if (!method.formHandler) { + handler = function() { + me.configureRequest(action, method, slice.call(arguments, 0)); + }; + } + else { + handler = function() { + me.configureFormRequest(action, method, slice.call(arguments, 0)); + }; + } + + handler.directCfg = { + action: action, + method: method + }; + + return handler; + }, + + + isConnected: function() { + return !!this.connected; + }, + + + connect: function() { + var me = this; + + if (me.url) { + me.initAPI(); + me.connected = true; + me.fireEvent('connect', me); + } + else if (!me.url) { + Ext.Error.raise('Error initializing RemotingProvider "' + me.id + + '", no url configured.'); + } + }, + + + disconnect: function() { + var me = this; + + if (me.connected) { + me.connected = false; + me.fireEvent('disconnect', me); + } + }, + + + runCallback: function(transaction, event) { + var success = !!event.status, + funcName = success ? 'success' : 'failure', + callback, options, result; + + if (transaction && transaction.callback) { + callback = transaction.callback; + options = transaction.callbackOptions; + result = typeof event.result !== 'undefined' ? event.result : event.data; + + if (Ext.isFunction(callback)) { + callback(result, event, success, options); + } + else { + Ext.callback(callback[funcName], callback.scope, [result, event, success, options]); + Ext.callback(callback.callback, callback.scope, [result, event, success, options]); + } + } + }, + + + onData: function(options, success, response) { + var me = this, + i, len, events, event, transaction, transactions; + + if (success) { + events = me.createEvents(response); + + for (i = 0, len = events.length; i < len; ++i) { + event = events[i]; + transaction = me.getTransaction(event); + me.fireEvent('data', me, event); + + if (transaction && me.fireEvent('beforecallback', me, event, transaction) !== false) { + me.runCallback(transaction, event, true); + Ext.direct.Manager.removeTransaction(transaction); + } + } + } + else { + transactions = [].concat(options.transaction); + + for (i = 0, len = transactions.length; i < len; ++i) { + transaction = me.getTransaction(transactions[i]); + + if (transaction && transaction.retryCount < me.maxRetries) { + transaction.retry(); + } + else { + event = new Ext.direct.ExceptionEvent({ + data: null, + transaction: transaction, + code: Ext.direct.Manager.exceptions.TRANSPORT, + message: 'Unable to connect to the server.', + xhr: response + }); + + me.fireEvent('data', me, event); + + if (transaction && me.fireEvent('beforecallback', me, transaction) !== false) { + me.runCallback(transaction, event, false); + Ext.direct.Manager.removeTransaction(transaction); + } + } + } + } + }, + + + getTransaction: function(options) { + return options && options.tid ? Ext.direct.Manager.getTransaction(options.tid) : null; + }, + + + configureRequest: function(action, method, args) { + var me = this, + callData, data, callback, scope, opts, transaction, params; + + callData = method.getCallData(args); + data = callData.data; + callback = callData.callback; + scope = callData.scope; + opts = callData.options || {}; + + params = Ext.apply({}, { + provider: me, + args: args, + action: action, + method: method.name, + data: data, + callbackOptions: opts, + callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback + }); + + if (opts.timeout) { + Ext.applyIf(params, { + timeout: opts.timeout + }); + }; + + transaction = new Ext.direct.Transaction(params); + + if (me.fireEvent('beforecall', me, transaction, method) !== false) { + Ext.direct.Manager.addTransaction(transaction); + me.queueTransaction(transaction); + me.fireEvent('call', me, transaction, method); + } + }, + + + getCallData: function(transaction) { + return { + action: transaction.action, + method: transaction.method, + data: transaction.data, + type: 'rpc', + tid: transaction.id + }; + }, + + + sendRequest: function(data) { + var me = this, + request, callData, params, + enableUrlEncode = me.enableUrlEncode, + i, len; + + request = { + url: me.url, + callback: me.onData, + scope: me, + transaction: data, + timeout: me.timeout + }; + + + if (data.timeout) { + request.timeout = data.timeout; + } + + if (Ext.isArray(data)) { + callData = []; + + for (i = 0, len = data.length; i < len; ++i) { + callData.push(me.getCallData(data[i])); + } + } + else { + callData = me.getCallData(data); + } + + if (enableUrlEncode) { + params = {}; + params[Ext.isString(enableUrlEncode) ? enableUrlEncode : 'data'] = Ext.encode(callData); + request.params = params; + } + else { + request.jsonData = callData; + } + + Ext.Ajax.request(request); + }, + + + queueTransaction: function(transaction) { + var me = this, + enableBuffer = me.enableBuffer; + + if (transaction.form) { + me.sendFormRequest(transaction); + return; + } + + if (enableBuffer === false || typeof transaction.timeout !== 'undefined') { + me.sendRequest(transaction); + return; + } + + me.callBuffer.push(transaction); + + if (enableBuffer) { + if (!me.callTask) { + me.callTask = new Ext.util.DelayedTask(me.combineAndSend, me); + } + + me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10); + } + else { + me.combineAndSend(); + } + }, + + + combineAndSend : function() { + var me = this, + buffer = me.callBuffer, + len = buffer.length; + + if (len > 0) { + me.sendRequest(len == 1 ? buffer[0] : buffer); + me.callBuffer = []; + } + }, + + + configureFormRequest: function(action, method, args) { + var me = this, + form = args[0], + callback = args[1], + scope = args[2], + transaction, isUpload, params; + + transaction = new Ext.direct.Transaction({ + provider: me, + action: action, + method: method.name, + args: [form, callback, scope], + callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback, + isForm: true + }); + + if (me.fireEvent('beforecall', me, transaction, method) !== false) { + Ext.direct.Manager.addTransaction(transaction); + isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data'; + + params = { + extTID: transaction.id, + extAction: action, + extMethod: method.name, + extType: 'rpc', + extUpload: String(isUpload) + }; + + + + Ext.apply(transaction, { + form: Ext.getDom(form), + isUpload: isUpload, + params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params + }); + + me.fireEvent('call', me, transaction, method); + me.sendFormRequest(transaction); + } + }, + + + sendFormRequest: function(transaction) { + var me = this; + + Ext.Ajax.request({ + url: me.url, + params: transaction.params, + callback: me.onData, + scope: me, + form: transaction.form, + isUpload: transaction.isUpload, + transaction: transaction + }); + } +}); + + + +Ext.define('Ext.dom.Layer', { + extend: Ext.Element , + + alternateClassName: 'Ext.Layer', + + + + + + + + + + + + + + + + + + + + + + + statics: { + shims: [] + }, + + isLayer: true, + + localXYNames: { + get: 'getLocalXY', + set: 'setLocalXY' + }, + + + constructor: function(config, existingEl) { + config = config || {}; + var me = this, + dh = Ext.DomHelper, + cp = config.parentEl, + pel = cp ? Ext.getDom(cp) : document.body, + hm = config.hideMode, + cls = Ext.baseCSSPrefix + (config.fixed && !(Ext.isIE6 || Ext.isIEQuirks) ? 'fixed-layer' : 'layer'); + + + + + me.el = me; + + if (existingEl) { + me.dom = Ext.getDom(existingEl); + } + if (!me.dom) { + me.dom = dh.append(pel, config.dh || { + tag: 'div', + cls: cls + }); + } else { + me.addCls(cls); + if (!me.dom.parentNode) { + pel.appendChild(me.dom); + } + } + + if (config.preventSync) { + me.preventSync = true; + } + + if (config.id) { + me.id = me.dom.id = config.id; + } else { + me.id = Ext.id(me.dom); + } + + Ext.Element.addToCache(me); + + if (config.cls) { + me.addCls(config.cls); + } + me.constrain = config.constrain !== false; + + + + + if (hm) { + me.setVisibilityMode(Ext.Element[hm.toUpperCase()]); + if (me.visibilityMode == Ext.Element.ASCLASS) { + me.visibilityCls = config.visibilityCls; + } + } else if (config.useDisplay) { + me.setVisibilityMode(Ext.Element.DISPLAY); + } else { + me.setVisibilityMode(Ext.Element.VISIBILITY); + } + + if (config.shadow) { + me.shadowOffset = config.shadowOffset || 4; + me.shadow = new Ext.Shadow({ + offset: me.shadowOffset, + mode: config.shadow, + fixed: config.fixed + }); + me.disableShadow(); + } else { + me.shadowOffset = 0; + } + me.useShim = config.shim !== false && Ext.useShims; + + + + + if (config.hidden === true) { + me.hide(); + } else if (config.hidden === false) { + me.show(); + } + }, + + getZIndex: function() { + return parseInt((this.getShim() || this).getStyle('z-index'), 10); + }, + + getShim: function() { + var me = this, + shim, pn; + + if (!me.useShim) { + return null; + } + if (!me.shim) { + shim = me.self.shims.shift(); + if (!shim) { + shim = me.createShim(); + + shim.dom.setAttribute('data-sticky', true); + shim.enableDisplayMode('block'); + shim.hide(); + } + pn = me.dom.parentNode; + if (shim.dom.parentNode != pn) { + pn.insertBefore(shim.dom, me.dom); + } + me.shim = shim; + } + return me.shim; + }, + + hideShim: function() { + var me = this; + + if (me.shim) { + me.shim.setDisplayed(false); + me.self.shims.push(me.shim); + delete me.shim; + } + }, + + disableShadow: function() { + var me = this; + + if (me.shadow && !me.shadowDisabled) { + me.shadowDisabled = true; + me.shadow.hide(); + me.lastShadowOffset = me.shadowOffset; + me.shadowOffset = 0; + } + }, + + enableShadow: function(show) { + var me = this; + + if (me.shadow && me.shadowDisabled) { + me.shadowDisabled = false; + me.shadowOffset = me.lastShadowOffset; + delete me.lastShadowOffset; + if (show) { + me.sync(true); + } + } + }, + + + sync: function(doShow) { + var me = this, + shadow = me.shadow, + shadowPos, shimStyle, shadowSize, + shim, xy, x, y, w, h, shimIndex; + + if (me.preventSync) { + return; + } + + if (!me.updating && me.isVisible() && (shadow || me.useShim)) { + shim = me.getShim(); + xy = me[me.localXYNames.get](); + x = xy[0]; + y = xy[1]; + w = me.dom.offsetWidth; + h = me.dom.offsetHeight; + + if (shadow && !me.shadowDisabled) { + if (doShow && !shadow.isVisible()) { + shadow.show(me); + } else { + shadow.realign(x, y, w, h); + } + if (shim) { + + shimIndex = shim.getStyle('z-index'); + if (shimIndex > me.zindex) { + me.shim.setStyle('z-index', me.zindex - 2); + } + shim.show(); + + if (shadow.isVisible()) { + shadowPos = shadow.el.getXY(); + shimStyle = shim.dom.style; + shadowSize = shadow.el.getSize(); + if (Ext.supports.CSS3BoxShadow) { + shadowSize.height += 6; + shadowSize.width += 4; + shadowPos[0] -= 2; + shadowPos[1] -= 4; + } + shimStyle.left = (shadowPos[0]) + 'px'; + shimStyle.top = (shadowPos[1]) + 'px'; + shimStyle.width = (shadowSize.width) + 'px'; + shimStyle.height = (shadowSize.height) + 'px'; + } else { + shim.setSize(w, h); + shim[me.localXYNames.set](x, y); + } + } + } else if (shim) { + + shimIndex = shim.getStyle('z-index'); + if (shimIndex > me.zindex) { + me.shim.setStyle('z-index', me.zindex - 2); + } + shim.show(); + shim.setSize(w, h); + shim[me.localXYNames.set](x, y); + } + } + return me; + }, + + remove: function() { + this.hideUnders(); + this.callParent(); + }, + + + beginUpdate: function() { + this.updating = true; + }, + + + endUpdate: function() { + this.updating = false; + this.sync(true); + }, + + + hideUnders: function() { + if (this.shadow) { + this.shadow.hide(); + } + this.hideShim(); + }, + + + constrainXY: function() { + if (this.constrain) { + var vw = Ext.Element.getViewWidth(), + vh = Ext.Element.getViewHeight(), + s = Ext.getDoc().getScroll(), + xy = this.getXY(), + x = xy[0], + y = xy[1], + so = this.shadowOffset, + w = this.dom.offsetWidth + so, + h = this.dom.offsetHeight + so, + moved = false; + + if ((x + w) > vw + s.left) { + x = vw - w - so; + moved = true; + } + if ((y + h) > vh + s.top) { + y = vh - h - so; + moved = true; + } + + if (x < s.left) { + x = s.left; + moved = true; + } + if (y < s.top) { + y = s.top; + moved = true; + } + if (moved) { + Ext.Layer.superclass.setXY.call(this, [x, y]); + this.sync(); + } + } + return this; + }, + + getConstrainOffset: function() { + return this.shadowOffset; + }, + + + setVisible: function(visible, animate, duration, callback, easing) { + var me = this, + cb; + + + cb = function() { + if (visible) { + me.sync(true); + } + if (callback) { + callback(); + } + }; + + + if (!visible) { + me.hideUnders(true); + } + me.callParent([visible, animate, duration, callback, easing]); + if (!animate) { + cb(); + } + return me; + }, + + + beforeFx: function() { + this.beforeAction(); + return this.callParent(arguments); + }, + + + afterFx: function() { + this.callParent(arguments); + this.sync(this.isVisible()); + }, + + + beforeAction: function() { + if (!this.updating && this.shadow) { + this.shadow.hide(); + } + }, + + + setLeft: function(left) { + this.callParent(arguments); + return this.sync(); + }, + + setTop: function(top) { + this.callParent(arguments); + return this.sync(); + }, + + setLeftTop: function(left, top) { + this.callParent(arguments); + return this.sync(); + }, + + setLocalX: function() { + this.callParent(arguments); + return this.sync(); + }, + + setLocalXY: function() { + this.callParent(arguments); + return this.sync(); + }, + + setLocalY: function() { + this.callParent(arguments); + return this.sync(); + }, + + setXY: function(xy, animate, duration, callback, easing) { + var me = this; + + + callback = me.createCB(callback); + + me.fixDisplay(); + me.beforeAction(); + me.callParent([xy, animate, duration, callback, easing]); + if (!animate) { + callback(); + } + return me; + }, + + + createCB: function(callback) { + var me = this, + showShadow = me.shadow && me.shadow.isVisible(); + + return function() { + me.constrainXY(); + me.sync(showShadow); + if (callback) { + callback(); + } + }; + }, + + + setX: function(x, animate, duration, callback, easing) { + this.setXY([x, this.getY()], animate, duration, callback, easing); + return this; + }, + + + setY: function(y, animate, duration, callback, easing) { + this.setXY([this.getX(), y], animate, duration, callback, easing); + return this; + }, + + + setSize: function(w, h, animate, duration, callback, easing) { + var me = this; + + + callback = me.createCB(callback); + + me.beforeAction(); + me.callParent([w, h, animate, duration, callback, easing]); + if (!animate) { + callback(); + } + return me; + }, + + + setWidth: function(w, animate, duration, callback, easing) { + var me = this; + + + callback = me.createCB(callback); + + me.beforeAction(); + me.callParent([w, animate, duration, callback, easing]); + if (!animate) { + callback(); + } + return me; + }, + + + setHeight: function(h, animate, duration, callback, easing) { + var me = this; + + + callback = me.createCB(callback); + + me.beforeAction(); + me.callParent([h, animate, duration, callback, easing]); + if (!animate) { + callback(); + } + return me; + }, + + + setBounds: function(x, y, width, height, animate, duration, callback, easing) { + var me = this; + + + callback = me.createCB(callback); + + me.beforeAction(); + if (!animate) { + Ext.Layer.superclass.setXY.call(me, [x, y]); + Ext.Layer.superclass.setSize.call(me, width, height); + callback(); + } else { + me.callParent([x, y, width, height, animate, duration, callback, easing]); + } + return me; + }, + + + setZIndex: function(zindex) { + var me = this; + + me.zindex = zindex; + if (me.getShim()) { + me.shim.setStyle('z-index', zindex++); + } + if (me.shadow) { + me.shadow.setZIndex(zindex++); + } + return me.setStyle('z-index', zindex); + }, + + onOpacitySet: function(opacity){ + var shadow = this.shadow; + if (shadow) { + shadow.setOpacity(opacity); + } + } +}); + + + +Ext.define('Ext.draw.Matrix', { + + + + + + + + constructor: function(a, b, c, d, e, f) { + if (a != null) { + this.matrix = [[a, c, e], [b, d, f], [0, 0, 1]]; + } + else { + this.matrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]; + } + }, + + add: function(a, b, c, d, e, f) { + var me = this, + out = [[], [], []], + matrix = [[a, c, e], [b, d, f], [0, 0, 1]], + x, + y, + z, + res; + + for (x = 0; x < 3; x++) { + for (y = 0; y < 3; y++) { + res = 0; + for (z = 0; z < 3; z++) { + res += me.matrix[x][z] * matrix[z][y]; + } + out[x][y] = res; + } + } + me.matrix = out; + }, + + prepend: function(a, b, c, d, e, f) { + var me = this, + out = [[], [], []], + matrix = [[a, c, e], [b, d, f], [0, 0, 1]], + x, + y, + z, + res; + + for (x = 0; x < 3; x++) { + for (y = 0; y < 3; y++) { + res = 0; + for (z = 0; z < 3; z++) { + res += matrix[x][z] * me.matrix[z][y]; + } + out[x][y] = res; + } + } + me.matrix = out; + }, + + invert: function() { + var matrix = this.matrix, + a = matrix[0][0], + b = matrix[1][0], + c = matrix[0][1], + d = matrix[1][1], + e = matrix[0][2], + f = matrix[1][2], + x = a * d - b * c; + return new Ext.draw.Matrix(d / x, -b / x, -c / x, a / x, (c * f - d * e) / x, (b * e - a * f) / x); + }, + + clone: function() { + var matrix = this.matrix, + a = matrix[0][0], + b = matrix[1][0], + c = matrix[0][1], + d = matrix[1][1], + e = matrix[0][2], + f = matrix[1][2]; + return new Ext.draw.Matrix(a, b, c, d, e, f); + }, + + translate: function(x, y) { + this.prepend(1, 0, 0, 1, x, y); + }, + + scale: function(x, y, cx, cy) { + var me = this; + if (y == null) { + y = x; + } + me.add(x, 0, 0, y, cx * (1 - x), cy * (1 - y)); + }, + + rotate: function(a, x, y) { + a = Ext.draw.Draw.rad(a); + var me = this, + cos = +Math.cos(a).toFixed(9), + sin = +Math.sin(a).toFixed(9); + me.add(cos, sin, -sin, cos, x - cos * x + sin * y, -(sin * x) + y - cos * y); + }, + + x: function(x, y) { + var matrix = this.matrix; + return x * matrix[0][0] + y * matrix[0][1] + matrix[0][2]; + }, + + y: function(x, y) { + var matrix = this.matrix; + return x * matrix[1][0] + y * matrix[1][1] + matrix[1][2]; + }, + + get: function(i, j) { + return + this.matrix[i][j].toFixed(4); + }, + + toString: function() { + var me = this; + return [me.get(0, 0), me.get(0, 1), me.get(1, 0), me.get(1, 1), 0, 0].join(); + }, + + toSvg: function() { + var me = this; + return "matrix(" + [me.get(0, 0), me.get(1, 0), me.get(0, 1), me.get(1, 1), me.get(0, 2), me.get(1, 2)].join() + ")"; + }, + + toFilter: function(dx, dy) { + var me = this; + dx = dx || 0; + dy = dy || 0; + return "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', filterType='bilinear', M11=" + me.get(0, 0) + + ", M12=" + me.get(0, 1) + ", M21=" + me.get(1, 0) + ", M22=" + me.get(1, 1) + + ", Dx=" + (me.get(0, 2) + dx) + ", Dy=" + (me.get(1, 2) + dy) + ")"; + }, + + offset: function() { + var matrix = this.matrix; + return [(matrix[0][2] || 0).toFixed(4), (matrix[1][2] || 0).toFixed(4)]; + }, + + + split: function () { + function norm(a) { + return a[0] * a[0] + a[1] * a[1]; + } + function normalize(a) { + var mag = Math.sqrt(norm(a)); + a[0] /= mag; + a[1] /= mag; + } + var matrix = this.matrix, + out = { + translateX: matrix[0][2], + translateY: matrix[1][2] + }, + row; + + + row = [[matrix[0][0], matrix[0][1]], [matrix[1][1], matrix[1][1]]]; + out.scaleX = Math.sqrt(norm(row[0])); + normalize(row[0]); + + out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1]; + row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear]; + + out.scaleY = Math.sqrt(norm(row[1])); + normalize(row[1]); + out.shear /= out.scaleY; + + + out.rotate = Math.asin(-row[0][1]); + + out.isSimple = !+out.shear.toFixed(9) && (out.scaleX.toFixed(9) == out.scaleY.toFixed(9) || !out.rotate); + + return out; + } +}); + + + +Ext.define('Ext.draw.SpriteDD', { + extend: Ext.dd.DragSource , + + constructor : function(sprite, cfg){ + var me = this, + el = sprite.el; + me.sprite = sprite; + me.el = el; + me.dragData = {el: el, sprite: sprite}; + me.callParent([el, cfg]); + me.sprite.setStyle('cursor', 'move'); + }, + + showFrame: Ext.emptyFn, + createFrame : Ext.emptyFn, + + getDragEl : function(e){ + return this.el; + }, + + getRegion: function() { + var me = this, + el = me.el, + pos, x1, x2, y1, y2, t, r, b, l, bbox, sprite; + + sprite = me.sprite; + bbox = sprite.getBBox(); + + try { + pos = Ext.Element.getXY(el); + } catch (e) { } + + if (!pos) { + return null; + } + + x1 = pos[0]; + x2 = x1 + bbox.width; + y1 = pos[1]; + y2 = y1 + bbox.height; + + return new Ext.util.Region(y1, x2, y2, x1); + }, + + + + startDrag: function(x, y) { + var me = this, + attr = me.sprite.attr; + me.prev = me.sprite.surface.transformToViewBox(x, y); + }, + + onDrag: function(e) { + var xy = e.getXY(), + me = this, + sprite = me.sprite, + attr = sprite.attr, dx, dy; + xy = me.sprite.surface.transformToViewBox(xy[0], xy[1]); + dx = xy[0] - me.prev[0]; + dy = xy[1] - me.prev[1]; + sprite.setAttributes({ + translate: { + x: attr.translation.x + dx, + y: attr.translation.y + dy + } + }, true); + me.prev = xy; + }, + + setDragElPos: function () { + + return false; + } +}); + + + +Ext.define('Ext.draw.Sprite', { + + + + mixins: { + observable: Ext.util.Observable , + animate: Ext.util.Animate + }, + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + dirty: false, + dirtyHidden: false, + dirtyTransform: false, + dirtyPath: true, + dirtyFont: true, + zIndexDirty: true, + + + isSprite: true, + zIndex: 0, + fontProperties: [ + 'font', + 'font-size', + 'font-weight', + 'font-style', + 'font-family', + 'text-anchor', + 'text' + ], + pathProperties: [ + 'x', + 'y', + 'd', + 'path', + 'height', + 'width', + 'radius', + 'r', + 'rx', + 'ry', + 'cx', + 'cy' + ], + constructor: function(config) { + var me = this; + config = Ext.merge({}, config || {}); + me.id = Ext.id(null, 'ext-sprite-'); + me.transformations = []; + Ext.copyTo(this, config, 'surface,group,type,draggable'); + + me.bbox = {}; + me.attr = { + zIndex: 0, + translation: { + x: null, + y: null + }, + rotation: { + degrees: null, + x: null, + y: null + }, + scaling: { + x: null, + y: null, + cx: null, + cy: null + } + }; + + delete config.surface; + delete config.group; + delete config.type; + delete config.draggable; + me.setAttributes(config); + me.addEvents( + + 'beforedestroy', + + 'destroy', + + 'render', + + 'mousedown', + + 'mouseup', + + 'mouseover', + + 'mouseout', + + 'mousemove', + + 'click' + ); + me.mixins.observable.constructor.apply(this, arguments); + }, + + + + initDraggable: function() { + var me = this; + + if (!me.el) { + me.surface.createSpriteElement(me); + } + me.dd = new Ext.draw.SpriteDD(me, Ext.isBoolean(me.draggable) ? null : me.draggable); + me.on('beforedestroy', me.dd.destroy, me.dd); + }, + + + setAttributes: function(attrs, redraw) { + var me = this, + fontProps = me.fontProperties, + fontPropsLength = fontProps.length, + pathProps = me.pathProperties, + pathPropsLength = pathProps.length, + hasSurface = !!me.surface, + custom = hasSurface && me.surface.customAttributes || {}, + spriteAttrs = me.attr, + dirtyBBox = false, + attr, i, newTranslation, translation, newRotate, rotation, newScaling, scaling; + + attrs = Ext.apply({}, attrs); + for (attr in custom) { + if (attrs.hasOwnProperty(attr) && typeof custom[attr] == "function") { + Ext.apply(attrs, custom[attr].apply(me, [].concat(attrs[attr]))); + } + } + + + if (!!attrs.hidden !== !!spriteAttrs.hidden) { + me.dirtyHidden = true; + } + + + for (i = 0; i < pathPropsLength; i++) { + attr = pathProps[i]; + if (attr in attrs && attrs[attr] !== spriteAttrs[attr]) { + me.dirtyPath = true; + dirtyBBox = true; + break; + } + } + + + if ('zIndex' in attrs) { + me.zIndexDirty = true; + } + + + if ('text' in attrs) { + me.dirtyFont = true; + dirtyBBox = true; + attrs.text = me.transformText(attrs.text); + } + + for (i = 0; i < fontPropsLength; i++) { + attr = fontProps[i]; + if (attr in attrs && attrs[attr] !== spriteAttrs[attr]) { + me.dirtyFont = true; + dirtyBBox = true; + break; + } + } + + newTranslation = attrs.translation || attrs.translate; + delete attrs.translate; + delete attrs.translation; + translation = spriteAttrs.translation; + if (newTranslation) { + if (('x' in newTranslation && newTranslation.x !== translation.x) || + ('y' in newTranslation && newTranslation.y !== translation.y)) { + me.dirtyTransform = true; + translation.x = newTranslation.x; + translation.y = newTranslation.y; + } + } + + newRotate = attrs.rotation || attrs.rotate; + rotation = spriteAttrs.rotation; + delete attrs.rotate; + delete attrs.rotation; + if (newRotate) { + if (('x' in newRotate && newRotate.x !== rotation.x) || + ('y' in newRotate && newRotate.y !== rotation.y) || + ('degrees' in newRotate && newRotate.degrees !== rotation.degrees)) { + me.dirtyTransform = true; + rotation.x = newRotate.x; + rotation.y = newRotate.y; + rotation.degrees = newRotate.degrees; + } + } + + newScaling = attrs.scaling || attrs.scale; + scaling = spriteAttrs.scaling; + delete attrs.scale; + delete attrs.scaling; + if (newScaling) { + if (('x' in newScaling && newScaling.x !== scaling.x) || + ('y' in newScaling && newScaling.y !== scaling.y) || + ('cx' in newScaling && newScaling.cx !== scaling.cx) || + ('cy' in newScaling && newScaling.cy !== scaling.cy)) { + me.dirtyTransform = true; + scaling.x = newScaling.x; + scaling.y = newScaling.y; + scaling.cx = newScaling.cx; + scaling.cy = newScaling.cy; + } + } + + + if (!me.dirtyTransform && dirtyBBox) { + if (spriteAttrs.scaling.x === null || + spriteAttrs.scaling.y === null || + spriteAttrs.rotation.y === null || + spriteAttrs.rotation.y === null) { + me.dirtyTransform = true; + } + } + + Ext.apply(spriteAttrs, attrs); + me.dirty = true; + + if (redraw === true && hasSurface) { + me.redraw(); + } + return this; + }, + + transformText: Ext.identityFn, + + + getBBox: function() { + return this.surface.getBBox(this); + }, + + + setText: function(text) { + this.attr.text = text; + this.surface.applyAttrs(this); + return this; + }, + + + hide: function(redraw) { + this.setAttributes({ + hidden: true + }, redraw); + return this; + }, + + + show: function(redraw) { + this.setAttributes({ + hidden: false + }, redraw); + return this; + }, + + + remove: function() { + if (this.surface) { + this.surface.remove(this); + return true; + } + return false; + }, + + onRemove: function() { + this.surface.onRemove(this); + }, + + + destroy: function() { + var me = this; + if (me.fireEvent('beforedestroy', me) !== false) { + me.remove(); + me.surface.onDestroy(me); + me.clearListeners(); + me.fireEvent('destroy'); + } + }, + + + redraw: function() { + var me = this, + changed = !me.el || me.dirty, + surface = me.surface, + owner; + + surface.renderItem(me); + + + + if (changed) { + owner = surface.owner; + if (!me.isBackground && owner && (owner.viewBox || owner.autoSize)) { + owner.configureSurfaceSize(); + } + } + return this; + }, + + + setStyle: function() { + this.el.setStyle.apply(this.el, arguments); + return this; + }, + + + addCls: function(obj) { + this.surface.addCls(this, obj); + return this; + }, + + + removeCls: function(obj) { + this.surface.removeCls(this, obj); + return this; + } +}); + + + +Ext.define('Ext.draw.Text', { + extend: Ext.draw.Component , + + alias: 'widget.text', + + + text: '', + + + + + + focusable: false, + viewBox: false, + autoSize: true, + baseCls: Ext.baseCSSPrefix + 'surface ' + Ext.baseCSSPrefix + 'draw-text', + + initComponent: function() { + var me = this; + + me.textConfig = Ext.apply({ + type: 'text', + text: me.text, + rotate: { + degrees: me.degrees || 0 + } + }, me.textStyle); + Ext.apply(me.textConfig, me.getStyles(me.styleSelectors || me.styleSelector)); + + + + me.initialConfig.items = [me.textConfig]; + me.callParent(arguments); + }, + + + getStyles: function(selectors) { + selectors = Ext.Array.from(selectors); + var i = 0, + len = selectors.length, + rule, + style, + prop, + result = {}; + + for (; i < len; i++) { + + rule = Ext.util.CSS.getRule(selectors[i]); + if (rule) { + style = rule.style; + if (style) { + Ext.apply(result, { + 'font-family': style.fontFamily, + 'font-weight': style.fontWeight, + 'line-height': style.lineHeight, + 'font-size': style.fontSize, + fill: style.color + }); + } + } + } + return result; + }, + + + setAngle: function(degrees) { + var me = this, + surface, + sprite; + + if (me.rendered) { + surface = me.surface; + sprite = surface.items.items[0]; + + me.degrees = degrees; + sprite.setAttributes({ + rotate: { + degrees: degrees + } + }, true); + if (me.autoSize || me.viewBox) { + me.updateLayout(); + } + } else { + me.degrees = degrees; + } + }, + + + setText: function(text) { + var me = this, + surface, + sprite; + + if (me.rendered) { + surface = me.surface; + sprite = surface.items.items[0]; + + me.text = text || ''; + surface.remove(sprite); + me.textConfig.type = 'text'; + me.textConfig.text = me.text; + sprite = surface.add(me.textConfig); + sprite.setAttributes({ + rotate: { + degrees: me.degrees + } + }, true); + if (me.autoSize || me.viewBox) { + me.updateLayout(); + } + } else { + me.on({ + render: function() { + me.setText(text); + }, + single: true + }); + } + } +}); + + + +Ext.define('Ext.draw.engine.ImageExporter', { + singleton: true, + + + defaultUrl: 'http://svg.sencha.io', + + + supportedTypes: ['image/png', 'image/jpeg'], + + + widthParam: 'width', + + + heightParam: 'height', + + + typeParam: 'type', + + + svgParam: 'svg', + + formCls: Ext.baseCSSPrefix + 'hide-display', + + + generate: function(surface, config) { + config = config || {}; + var me = this, + type = config.type, + form; + + if (Ext.Array.indexOf(me.supportedTypes, type) === -1) { + return false; + } + + form = Ext.getBody().createChild({ + tag: 'form', + method: 'POST', + action: config.url || me.defaultUrl, + cls: me.formCls, + children: [{ + tag: 'input', + type: 'hidden', + name: config.widthParam || me.widthParam, + value: config.width || surface.width + }, { + tag: 'input', + type: 'hidden', + name: config.heightParam || me.heightParam, + value: config.height || surface.height + }, { + tag: 'input', + type: 'hidden', + name: config.typeParam || me.typeParam, + value: type + }, { + tag: 'input', + type: 'hidden', + name: config.svgParam || me.svgParam + }] + }); + + + form.last(null, true).value = Ext.draw.engine.SvgExporter.generate(surface); + + form.dom.submit(); + form.remove(); + return true; + } + +}); + + + +Ext.define('Ext.draw.engine.Svg', { + + + + extend: Ext.draw.Surface , + + + + + + engine: 'Svg', + + trimRe: /^\s+|\s+$/g, + spacesRe: /\s+/, + xlink: "http:/" + "/www.w3.org/1999/xlink", + + translateAttrs: { + radius: "r", + radiusX: "rx", + radiusY: "ry", + path: "d", + lineWidth: "stroke-width", + fillOpacity: "fill-opacity", + strokeOpacity: "stroke-opacity", + strokeLinejoin: "stroke-linejoin" + }, + + parsers: {}, + + minDefaults: { + circle: { + cx: 0, + cy: 0, + r: 0, + fill: "none", + stroke: null, + "stroke-width": null, + opacity: null, + "fill-opacity": null, + "stroke-opacity": null + }, + ellipse: { + cx: 0, + cy: 0, + rx: 0, + ry: 0, + fill: "none", + stroke: null, + "stroke-width": null, + opacity: null, + "fill-opacity": null, + "stroke-opacity": null + }, + rect: { + x: 0, + y: 0, + width: 0, + height: 0, + rx: 0, + ry: 0, + fill: "none", + stroke: null, + "stroke-width": null, + opacity: null, + "fill-opacity": null, + "stroke-opacity": null + }, + text: { + x: 0, + y: 0, + "text-anchor": "start", + "font-family": null, + "font-size": null, + "font-weight": null, + "font-style": null, + fill: "#000", + stroke: null, + "stroke-width": null, + opacity: null, + "fill-opacity": null, + "stroke-opacity": null + }, + path: { + d: "M0,0", + fill: "none", + stroke: null, + "stroke-width": null, + opacity: null, + "fill-opacity": null, + "stroke-opacity": null + }, + image: { + x: 0, + y: 0, + width: 0, + height: 0, + preserveAspectRatio: "none", + opacity: null + } + }, + + createSvgElement: function(type, attrs) { + var el = this.domRef.createElementNS("http:/" + "/www.w3.org/2000/svg", type), + key; + if (attrs) { + for (key in attrs) { + el.setAttribute(key, String(attrs[key])); + } + } + return el; + }, + + createSpriteElement: function(sprite) { + + var el = this.createSvgElement(sprite.type); + el.id = sprite.id; + if (el.style) { + el.style.webkitTapHighlightColor = "rgba(0,0,0,0)"; + } + sprite.el = Ext.get(el); + this.applyZIndex(sprite); + sprite.matrix = new Ext.draw.Matrix(); + sprite.bbox = { + plain: 0, + transform: 0 + }; + this.applyAttrs(sprite); + this.applyTransformations(sprite); + sprite.fireEvent("render", sprite); + return el; + }, + + getBBoxText: function (sprite) { + var bbox = {}, + bb, height, width, i, ln, el; + + if (sprite && sprite.el) { + el = sprite.el.dom; + try { + bbox = el.getBBox(); + return bbox; + } catch(e) { + + } + bbox = {x: bbox.x, y: Infinity, width: 0, height: 0}; + ln = el.getNumberOfChars(); + for (i = 0; i < ln; i++) { + bb = el.getExtentOfChar(i); + bbox.y = Math.min(bb.y, bbox.y); + height = bb.y + bb.height - bbox.y; + bbox.height = Math.max(bbox.height, height); + width = bb.x + bb.width - bbox.x; + bbox.width = Math.max(bbox.width, width); + } + return bbox; + } + }, + + hide: function() { + Ext.get(this.el).hide(); + }, + + show: function() { + Ext.get(this.el).show(); + }, + + hidePrim: function(sprite) { + this.addCls(sprite, Ext.baseCSSPrefix + 'hide-visibility'); + }, + + showPrim: function(sprite) { + this.removeCls(sprite, Ext.baseCSSPrefix + 'hide-visibility'); + }, + + getDefs: function() { + return this._defs || (this._defs = this.createSvgElement("defs")); + }, + + transform: function(sprite, matrixOnly) { + var me = this, + matrix = new Ext.draw.Matrix(), + transforms = sprite.transformations, + transformsLength = transforms.length, + i = 0, + transform, type; + + for (; i < transformsLength; i++) { + transform = transforms[i]; + type = transform.type; + if (type == "translate") { + matrix.translate(transform.x, transform.y); + } + else if (type == "rotate") { + matrix.rotate(transform.degrees, transform.x, transform.y); + } + else if (type == "scale") { + matrix.scale(transform.x, transform.y, transform.centerX, transform.centerY); + } + } + sprite.matrix = matrix; + if (!matrixOnly) { + sprite.el.set({transform: matrix.toSvg()}); + } + }, + + setSize: function(width, height) { + var me = this, + el = me.el; + + width = +width || me.width; + height = +height || me.height; + me.width = width; + me.height = height; + + el.setSize(width, height); + el.set({ + width: width, + height: height + }); + me.callParent([width, height]); + }, + + + getRegion: function() { + + + var svgXY = this.el.getXY(), + rectXY = this.bgRect.getXY(), + max = Math.max, + x = max(svgXY[0], rectXY[0]), + y = max(svgXY[1], rectXY[1]); + return { + left: x, + top: y, + right: x + this.width, + bottom: y + this.height + }; + }, + + onRemove: function(sprite) { + if (sprite.el) { + sprite.el.destroy(); + delete sprite.el; + } + this.callParent(arguments); + }, + + setViewBox: function(x, y, width, height) { + if (isFinite(x) && isFinite(y) && isFinite(width) && isFinite(height)) { + this.callParent(arguments); + this.el.dom.setAttribute("viewBox", [x, y, width, height].join(" ")); + } + }, + + render: function (container) { + var me = this, + cfg, el, defs, bgRect, webkitRect; + + if (!me.el) { + cfg = { + xmlns: "http:/" + "/www.w3.org/2000/svg", + version: 1.1, + width: me.width || 0, + height: me.height || 0 + }; + + if (me.forceLtr) { + cfg.direction = 'ltr'; + } + + el = me.createSvgElement('svg', cfg); + defs = me.getDefs(); + + + + + + bgRect = me.createSvgElement("rect", { + width: "100%", + height: "100%", + fill: "#000", + stroke: "none", + opacity: 0 + }); + + if (Ext.isSafari3) { + + webkitRect = me.createSvgElement("rect", { + x: -10, + y: -10, + width: "110%", + height: "110%", + fill: "none", + stroke: "#000" + }); + } + el.appendChild(defs); + if (Ext.isSafari3) { + el.appendChild(webkitRect); + } + el.appendChild(bgRect); + container.appendChild(el); + me.el = Ext.get(el); + me.bgRect = Ext.get(bgRect); + if (Ext.isSafari3) { + me.webkitRect = Ext.get(webkitRect); + me.webkitRect.hide(); + } + me.el.on({ + scope: me, + mouseup: me.onMouseUp, + mousedown: me.onMouseDown, + mouseover: me.onMouseOver, + mouseout: me.onMouseOut, + mousemove: me.onMouseMove, + mouseenter: me.onMouseEnter, + mouseleave: me.onMouseLeave, + click: me.onClick, + dblclick: me.onDblClick + }); + } + me.renderAll(); + }, + + + onMouseEnter: function(e) { + if (this.el.parent().getRegion().contains(e.getPoint())) { + this.fireEvent('mouseenter', e); + } + }, + + + onMouseLeave: function(e) { + if (!this.el.parent().getRegion().contains(e.getPoint())) { + this.fireEvent('mouseleave', e); + } + }, + + processEvent: function(name, e) { + var target = e.getTarget(), + surface = this.surface, + sprite; + + this.fireEvent(name, e); + + if (target.nodeName == "tspan" && target.parentNode) { + target = target.parentNode; + } + sprite = this.items.get(target.id); + if (sprite) { + sprite.fireEvent(name, sprite, e); + } + }, + + + tuneText: function (sprite, attrs) { + var el = sprite.el.dom, + tspans = [], + height, tspan, text, i, ln, texts, factor, x; + + if (attrs.hasOwnProperty("text")) { + + + + + + text = sprite.tspans && Ext.Array.map(sprite.tspans, function(t) { return t.textContent; }).join(''); + + if (!sprite.tspans || attrs.text != text) { + tspans = this.setText(sprite, attrs.text); + sprite.tspans = tspans; + + } else { + tspans = sprite.tspans || []; + } + } + + if (tspans.length) { + height = this.getBBoxText(sprite).height; + x = sprite.el.dom.getAttribute("x"); + for (i = 0, ln = tspans.length; i < ln; i++) { + + + factor = (Ext.isFF3_0 || Ext.isFF3_5) ? 2 : 4; + tspans[i].setAttribute("x", x); + tspans[i].setAttribute("dy", i ? height * 1.2 : height / factor); + } + sprite.dirty = true; + } + }, + + setText: function(sprite, textString) { + var me = this, + el = sprite.el.dom, + tspans = [], + height, tspan, text, i, ln, texts; + + while (el.firstChild) { + el.removeChild(el.firstChild); + } + + texts = String(textString).split("\n"); + for (i = 0, ln = texts.length; i < ln; i++) { + text = texts[i]; + if (text) { + tspan = me.createSvgElement("tspan"); + tspan.appendChild(document.createTextNode(Ext.htmlDecode(text))); + el.appendChild(tspan); + tspans[i] = tspan; + } + } + return tspans; + }, + + renderAll: function() { + this.items.each(this.renderItem, this); + }, + + renderItem: function (sprite) { + if (!this.el) { + return; + } + if (!sprite.el) { + this.createSpriteElement(sprite); + } + if (sprite.zIndexDirty) { + this.applyZIndex(sprite); + } + if (sprite.dirty) { + this.applyAttrs(sprite); + if (sprite.dirtyTransform) { + this.applyTransformations(sprite); + } + } + }, + + redraw: function(sprite) { + sprite.dirty = sprite.zIndexDirty = true; + this.renderItem(sprite); + }, + + applyAttrs: function (sprite) { + var me = this, + el = sprite.el, + group = sprite.group, + sattr = sprite.attr, + parsers = me.parsers, + + + + gradientsMap = me.gradientsMap || {}, + safariFix = Ext.isSafari && !Ext.isStrict, + groups, i, ln, attrs, font, key, style, name, rect; + + if (group) { + groups = [].concat(group); + ln = groups.length; + for (i = 0; i < ln; i++) { + group = groups[i]; + me.getGroup(group).add(sprite); + } + delete sprite.group; + } + attrs = me.scrubAttrs(sprite) || {}; + + + sprite.bbox.plain = 0; + sprite.bbox.transform = 0; + if (sprite.type == "circle" || sprite.type == "ellipse") { + attrs.cx = attrs.cx || attrs.x; + attrs.cy = attrs.cy || attrs.y; + } + else if (sprite.type == "rect") { + attrs.rx = attrs.ry = attrs.r; + } + else if (sprite.type == "path" && attrs.d) { + attrs.d = Ext.draw.Draw.pathToString(Ext.draw.Draw.pathToAbsolute(attrs.d)); + } + sprite.dirtyPath = false; + + + + + + if (attrs['clip-rect']) { + me.setClip(sprite, attrs); + delete attrs['clip-rect']; + } + if (sprite.type == 'text' && attrs.font && sprite.dirtyFont) { + el.set({ style: "font: " + attrs.font}); + } + if (sprite.type == "image") { + el.dom.setAttributeNS(me.xlink, "href", attrs.src); + } + Ext.applyIf(attrs, me.minDefaults[sprite.type]); + + if (sprite.dirtyHidden) { + (sattr.hidden) ? me.hidePrim(sprite) : me.showPrim(sprite); + sprite.dirtyHidden = false; + } + for (key in attrs) { + if (attrs.hasOwnProperty(key) && attrs[key] != null) { + + + + + + if (safariFix && ('color|stroke|fill'.indexOf(key) > -1) && (attrs[key] in gradientsMap)) { + attrs[key] = gradientsMap[attrs[key]]; + } + + if (key == 'hidden' && sprite.type == 'text') { + continue; + } + if (key in parsers) { + el.dom.setAttribute(key, parsers[key](attrs[key], sprite, me)); + } else { + el.dom.setAttribute(key, attrs[key]); + } + } + } + + if (sprite.type == 'text') { + me.tuneText(sprite, attrs); + } + sprite.dirtyFont = false; + + + style = sattr.style; + if (style) { + el.setStyle(style); + } + + sprite.dirty = false; + + if (Ext.isSafari3) { + + me.webkitRect.show(); + setTimeout(function () { + me.webkitRect.hide(); + }); + } + }, + + setClip: function(sprite, params) { + var me = this, + rect = params["clip-rect"], + clipEl, clipPath; + if (rect) { + if (sprite.clip) { + sprite.clip.parentNode.parentNode.removeChild(sprite.clip.parentNode); + } + clipEl = me.createSvgElement('clipPath'); + clipPath = me.createSvgElement('rect'); + clipEl.id = Ext.id(null, 'ext-clip-'); + clipPath.setAttribute("x", rect.x); + clipPath.setAttribute("y", rect.y); + clipPath.setAttribute("width", rect.width); + clipPath.setAttribute("height", rect.height); + clipEl.appendChild(clipPath); + me.getDefs().appendChild(clipEl); + sprite.el.dom.setAttribute("clip-path", "url(#" + clipEl.id + ")"); + sprite.clip = clipPath; + } + + + + + + + }, + + + applyZIndex: function(sprite) { + var me = this, + items = me.items, + idx = items.indexOf(sprite), + el = sprite.el, + prevEl; + if (me.el.dom.childNodes[idx + 2] !== el.dom) { + if (idx > 0) { + + do { + prevEl = items.getAt(--idx).el; + } while (!prevEl && idx > 0); + } + el.insertAfter(prevEl || me.bgRect); + } + sprite.zIndexDirty = false; + }, + + createItem: function (config) { + var sprite = new Ext.draw.Sprite(config); + sprite.surface = this; + return sprite; + }, + + addGradient: function(gradient) { + gradient = Ext.draw.Draw.parseGradient(gradient); + var me = this, + ln = gradient.stops.length, + vector = gradient.vector, + + + + usePlain = Ext.isSafari && !Ext.isStrict, + gradientEl, stop, stopEl, i, gradientsMap; + + gradientsMap = me.gradientsMap || {}; + + if (!usePlain) { + if (gradient.type == "linear") { + gradientEl = me.createSvgElement("linearGradient"); + gradientEl.setAttribute("x1", vector[0]); + gradientEl.setAttribute("y1", vector[1]); + gradientEl.setAttribute("x2", vector[2]); + gradientEl.setAttribute("y2", vector[3]); + } + else { + gradientEl = me.createSvgElement("radialGradient"); + gradientEl.setAttribute("cx", gradient.centerX); + gradientEl.setAttribute("cy", gradient.centerY); + gradientEl.setAttribute("r", gradient.radius); + if (Ext.isNumber(gradient.focalX) && Ext.isNumber(gradient.focalY)) { + gradientEl.setAttribute("fx", gradient.focalX); + gradientEl.setAttribute("fy", gradient.focalY); + } + } + gradientEl.id = gradient.id; + me.getDefs().appendChild(gradientEl); + for (i = 0; i < ln; i++) { + stop = gradient.stops[i]; + stopEl = me.createSvgElement("stop"); + stopEl.setAttribute("offset", stop.offset + "%"); + stopEl.setAttribute("stop-color", stop.color); + stopEl.setAttribute("stop-opacity",stop.opacity); + gradientEl.appendChild(stopEl); + } + } else { + gradientsMap['url(#' + gradient.id + ')'] = gradient.stops[0].color; + } + me.gradientsMap = gradientsMap; + }, + + + hasCls: function(sprite, className) { + return className && (' ' + (sprite.el.dom.getAttribute('class') || '') + ' ').indexOf(' ' + className + ' ') != -1; + }, + + addCls: function(sprite, className) { + var el = sprite.el, + i, + len, + v, + cls = [], + curCls = el.getAttribute('class') || ''; + + if (!Ext.isArray(className)) { + if (typeof className == 'string' && !this.hasCls(sprite, className)) { + el.set({ 'class': curCls + ' ' + className }); + } + } + else { + for (i = 0, len = className.length; i < len; i++) { + v = className[i]; + if (typeof v == 'string' && (' ' + curCls + ' ').indexOf(' ' + v + ' ') == -1) { + cls.push(v); + } + } + if (cls.length) { + el.set({ 'class': ' ' + cls.join(' ') }); + } + } + }, + + removeCls: function(sprite, className) { + var me = this, + el = sprite.el, + curCls = el.getAttribute('class') || '', + i, idx, len, cls, elClasses; + if (!Ext.isArray(className)){ + className = [className]; + } + if (curCls) { + elClasses = curCls.replace(me.trimRe, ' ').split(me.spacesRe); + for (i = 0, len = className.length; i < len; i++) { + cls = className[i]; + if (typeof cls == 'string') { + cls = cls.replace(me.trimRe, ''); + idx = Ext.Array.indexOf(elClasses, cls); + if (idx != -1) { + Ext.Array.erase(elClasses, idx, 1); + } + } + } + el.set({ 'class': elClasses.join(' ') }); + } + }, + + destroy: function() { + var me = this; + + me.callParent(); + if (me.el) { + me.el.remove(); + } + if (me._defs) { + Ext.get(me._defs).destroy(); + } + if (me.bgRect) { + Ext.get(me.bgRect).destroy(); + } + if (me.webkitRect) { + Ext.get(me.webkitRect).destroy(); + } + delete me.el; + } +}); + + + +Ext.define('Ext.draw.engine.SvgExporter', function(){ + var commaRe = /,/g, + fontRegex = /(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)\s('*.*'*)/, + rgbColorRe = /rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + rgbaColorRe = /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,([\d\.]+)\)/g, + surface, len, width, height, + + init = function(s){ + surface = s; + len = surface.length; + width = surface.width; + height = surface.height; + }, + spriteProcessor = { + path: function(sprite){ + + var attr = sprite.attr, + path = attr.path, + pathString = '', + props, p, pLen; + + if (Ext.isArray(path[0])) { + pLen = path.length; + for (p = 0; p < pLen; p++) { + pathString += path[p].join(' '); + } + } else if (Ext.isArray(path)) { + pathString = path.join(' '); + } else { + pathString = path.replace(commaRe,' '); + } + + props = toPropertyString({ + d: pathString, + fill: attr.fill || 'none', + stroke: attr.stroke, + 'fill-opacity': attr.opacity, + 'stroke-width': attr['stroke-width'], + 'stroke-opacity': attr['stroke-opacity'], + "z-index": attr.zIndex, + transform: sprite.matrix.toSvg() + }); + + return ''; + }, + text: function(sprite){ + + + + + var attr = sprite.attr, + match = fontRegex.exec(attr.font), + size = (match && match[1]) || "12", + + family = (match && match[3]) || 'Arial', + text = attr.text, + factor = (Ext.isFF3_0 || Ext.isFF3_5) ? 2 : 4, + tspanString = '', + props; + + sprite.getBBox(); + tspanString += ''; + tspanString += Ext.htmlEncode(text) + ''; + + + props = toPropertyString({ + x: attr.x, + y: attr.y, + 'font-size': size, + 'font-family': family, + 'font-weight': attr['font-weight'], + 'text-anchor': attr['text-anchor'], + + fill: attr.fill || '#000', + 'fill-opacity': attr.opacity, + transform: sprite.matrix.toSvg() + }); + + + + return '' + tspanString + ''; + }, + rect: function(sprite){ + + var attr = sprite.attr, + props = toPropertyString({ + x: attr.x, + y: attr.y, + rx: attr.rx, + ry: attr.ry, + width: attr.width, + height: attr.height, + fill: attr.fill || 'none', + 'fill-opacity': attr.opacity, + stroke: attr.stroke, + 'stroke-opacity': attr['stroke-opacity'], + 'stroke-width':attr['stroke-width'], + transform: sprite.matrix && sprite.matrix.toSvg() + }); + + return ''; + }, + circle: function(sprite){ + + var attr = sprite.attr, + props = toPropertyString({ + cx: attr.x, + cy: attr.y, + r: attr.radius, + fill: attr.translation.fill || attr.fill || 'none', + 'fill-opacity': attr.opacity, + stroke: attr.stroke, + 'stroke-opacity': attr['stroke-opacity'], + 'stroke-width':attr['stroke-width'], + transform: sprite.matrix.toSvg() + }); + + return ''; + }, + image: function(sprite){ + + var attr = sprite.attr, + props = toPropertyString({ + x: attr.x - (attr.width/2 >> 0), + y: attr.y - (attr.height/2 >> 0), + width: attr.width, + height: attr.height, + 'xlink:href': attr.src, + transform: sprite.matrix.toSvg() + }); + + return ''; + } + }, + svgHeader = function(){ + var svg = ''; + svg += ''; + return svg; + }, + svgContent = function(){ + var svg = '', + defs = '', item, itemsLen, items, gradient, + getSvgString, colorstops, stop, + coll, keys, colls, k, kLen, key, collI, i, j, stopsLen, sortedItems, za, zb; + + items = surface.items.items; + itemsLen = items.length; + + + getSvgString = function(node){ + + var childs = node.childNodes, + childLength = childs.length, + i = 0, + attrLength, + j, + svgString = '', child, attr, tagName, attrItem; + + for(; i < childLength; i++){ + child = childs[i]; + attr = child.attributes; + tagName = child.tagName; + + svgString += '<' +tagName; + + for(j = 0, attrLength = attr.length; j < attrLength; j++){ + attrItem = attr.item(j); + svgString += ' '+attrItem.name+'="'+attrItem.value+'"'; + } + + svgString += '>'; + + if(child.childNodes.length > 0){ + svgString += getSvgString(child); + } + + svgString += ''; + + } + return svgString; + }; + + + if(surface.getDefs){ + defs = getSvgString(surface.getDefs()); + }else{ + + coll = surface.gradientsColl; + if (coll) { + keys = coll.keys; + colls = coll.items; + k = 0; + kLen = keys.length; + } + + for (; k < kLen; k++) { + key = keys[k]; + collI = colls[k]; + + gradient = surface.gradientsColl.getByKey(key); + defs += ''; + + var color = gradient.colors.replace(rgbColorRe, 'rgb($1|$2|$3)'); + color = color.replace(rgbaColorRe, 'rgba($1|$2|$3|$4)') + colorstops = color.split(','); + for(i=0, stopsLen = colorstops.length; i < stopsLen; i++){ + stop = colorstops[i].split(' '); + color = Ext.draw.Color.fromString(stop[1].replace(/\|/g,',')); + defs += ''; + } + defs += ''; + } + } + + svg += '' + defs + ''; + + + svg += spriteProcessor.rect({ + attr: { + width: '100%', + height: '100%', + fill: '#fff', + stroke: 'none', + opacity: '0' + } + }); + + + sortedItems = new Array(itemsLen); + for(i = 0; i < itemsLen; i++){ + sortedItems[i] = i; + } + sortedItems.sort(function (a, b) { + za = items[a].attr.zIndex || 0; + zb = items[b].attr.zIndex || 0; + if (za == zb) { + return a - b; + } + return za - zb; + }); + + for(i = 0; i < itemsLen; i++){ + item = items[sortedItems[i]]; + if(!item.attr.hidden){ + svg += spriteProcessor[item.type](item); + } + } + + svg += ''; + + return svg; + }, + toPropertyString = function(obj){ + var propString = '', + key; + + for(key in obj){ + + if(obj.hasOwnProperty(key) && obj[key] != null){ + propString += key +'="'+ obj[key]+'" '; + } + + } + + return propString; + }; + + return { + singleton: true, + + + generate: function(surface, config){ + config = config || {}; + init(surface); + return svgHeader() + svgContent(); + } + }; +}); + + + +Ext.define('Ext.draw.engine.Vml', { + + + + extend: Ext.draw.Surface , + + + + + + engine: 'Vml', + + map: {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"}, + bitesRe: /([clmz]),?([^clmz]*)/gi, + valRe: /-?[^,\s\-]+/g, + fillUrlRe: /^url\(\s*['"]?([^\)]+?)['"]?\s*\)$/i, + pathlike: /^(path|rect)$/, + NonVmlPathRe: /[ahqstv]/ig, // Non-VML Pathing ops + partialPathRe: /[clmz]/g, + fontFamilyRe: /^['"]+|['"]+$/g, + baseVmlCls: Ext.baseCSSPrefix + 'vml-base', + vmlGroupCls: Ext.baseCSSPrefix + 'vml-group', + spriteCls: Ext.baseCSSPrefix + 'vml-sprite', + measureSpanCls: Ext.baseCSSPrefix + 'vml-measure-span', + zoom: 21600, + coordsize: 1000, + coordorigin: '0 0', + zIndexShift: 0, + // VML uses CSS z-index and therefore doesn't need sprites to be kept in zIndex order + orderSpritesByZIndex: false, + + + + path2vml: function (path) { + var me = this, + nonVML = me.NonVmlPathRe, + map = me.map, + val = me.valRe, + zoom = me.zoom, + bites = me.bitesRe, + command = Ext.Function.bind(Ext.draw.Draw.pathToAbsolute, Ext.draw.Draw), + res, pa, p, r, i, ii, j, jj; + if (String(path).match(nonVML)) { + command = Ext.Function.bind(Ext.draw.Draw.path2curve, Ext.draw.Draw); + } else if (!String(path).match(me.partialPathRe)) { + res = String(path).replace(bites, function (all, command, args) { + var vals = [], + isMove = command.toLowerCase() == "m", + res = map[command]; + args.replace(val, function (value) { + if (isMove && vals.length === 2) { + res += vals + map[command == "m" ? "l" : "L"]; + vals = []; + } + vals.push(Math.round(value * zoom)); + }); + return res + vals; + }); + return res; + } + pa = command(path); + res = []; + for (i = 0, ii = pa.length; i < ii; i++) { + p = pa[i]; + r = pa[i][0].toLowerCase(); + if (r == "z") { + r = "x"; + } + for (j = 1, jj = p.length; j < jj; j++) { + r += Math.round(p[j] * me.zoom) + (j != jj - 1 ? "," : ""); + } + res.push(r); + } + return res.join(" "); + }, + + + translateAttrs: { + radius: "r", + radiusX: "rx", + radiusY: "ry", + lineWidth: "stroke-width", + fillOpacity: "fill-opacity", + strokeOpacity: "stroke-opacity", + strokeLinejoin: "stroke-linejoin" + }, + + + minDefaults: { + circle: { + fill: "none", + stroke: null, + "stroke-width": null, + opacity: null, + "fill-opacity": null, + "stroke-opacity": null + }, + ellipse: { + cx: 0, + cy: 0, + rx: 0, + ry: 0, + fill: "none", + stroke: null, + "stroke-width": null, + opacity: null, + "fill-opacity": null, + "stroke-opacity": null + }, + rect: { + x: 0, + y: 0, + width: 0, + height: 0, + rx: 0, + ry: 0, + fill: "none", + stroke: null, + "stroke-width": null, + opacity: null, + "fill-opacity": null, + "stroke-opacity": null + }, + text: { + x: 0, + y: 0, + "text-anchor": "start", + font: '10px "Arial"', + fill: "#000", + stroke: null, + "stroke-width": null, + opacity: null, + "fill-opacity": null, + "stroke-opacity": null + }, + path: { + d: "M0,0", + fill: "none", + stroke: null, + "stroke-width": null, + opacity: null, + "fill-opacity": null, + "stroke-opacity": null + }, + image: { + x: 0, + y: 0, + width: 0, + height: 0, + preserveAspectRatio: "none", + opacity: null + } + }, + + + onMouseEnter: function (e) { + this.fireEvent("mouseenter", e); + }, + + + onMouseLeave: function (e) { + this.fireEvent("mouseleave", e); + }, + + + processEvent: function (name, e) { + var target = e.getTarget(), + surface = this.surface, + sprite; + this.fireEvent(name, e); + sprite = this.items.get(target.id); + if (sprite) { + sprite.fireEvent(name, sprite, e); + } + }, + + + createSpriteElement: function (sprite) { + var me = this, + attr = sprite.attr, + type = sprite.type, + zoom = me.zoom, + vml = sprite.vml || (sprite.vml = {}), + round = Math.round, + el = (type === 'image') ? me.createNode('image') : me.createNode('shape'), + path, skew, textPath; + + el.coordsize = zoom + ' ' + zoom; + el.coordorigin = attr.coordorigin || "0 0"; + Ext.get(el).addCls(me.spriteCls); + if (type == "text") { + vml.path = path = me.createNode("path"); + path.textpathok = true; + vml.textpath = textPath = me.createNode("textpath"); + textPath.on = true; + el.appendChild(textPath); + el.appendChild(path); + } + el.id = sprite.id; + sprite.el = Ext.get(el); + sprite.el.setStyle('zIndex', -me.zIndexShift); + me.el.appendChild(el); + if (type !== 'image') { + skew = me.createNode("skew"); + skew.on = true; + el.appendChild(skew); + sprite.skew = skew; + } + sprite.matrix = new Ext.draw.Matrix(); + sprite.bbox = { + plain: null, + transform: null + }; + + this.applyAttrs(sprite); + this.applyTransformations(sprite); + sprite.fireEvent("render", sprite); + return sprite.el; + }, + + getBBoxText: function (sprite) { + var vml = sprite.vml; + return { + x: vml.X + (vml.bbx || 0) - vml.W / 2, + y: vml.Y - vml.H / 2, + width: vml.W, + height: vml.H + }; + }, + + applyAttrs: function (sprite) { + var me = this, + vml = sprite.vml, + group = sprite.group, + spriteAttr = sprite.attr, + el = sprite.el, + dom = el.dom, + style, name, groups, i, ln, scrubbedAttrs, font, key, + cx, cy, rx, ry; + + if (group) { + groups = [].concat(group); + ln = groups.length; + for (i = 0; i < ln; i++) { + group = groups[i]; + me.getGroup(group).add(sprite); + } + delete sprite.group; + } + scrubbedAttrs = me.scrubAttrs(sprite) || {}; + + if (sprite.zIndexDirty) { + me.setZIndex(sprite); + } + + + Ext.applyIf(scrubbedAttrs, me.minDefaults[sprite.type]); + + if (sprite.type == 'image') { + Ext.apply(sprite.attr, { + x: scrubbedAttrs.x, + y: scrubbedAttrs.y, + width: scrubbedAttrs.width, + height: scrubbedAttrs.height + }); + el.setStyle({ + width: scrubbedAttrs.width + 'px', + height: scrubbedAttrs.height + 'px' + }); + dom.src = scrubbedAttrs.src; + } + + if (dom.href) { + dom.href = scrubbedAttrs.href; + } + if (dom.title) { + dom.title = scrubbedAttrs.title; + } + if (dom.target) { + dom.target = scrubbedAttrs.target; + } + if (dom.cursor) { + dom.cursor = scrubbedAttrs.cursor; + } + + + if (sprite.dirtyHidden) { + (scrubbedAttrs.hidden) ? me.hidePrim(sprite) : me.showPrim(sprite); + sprite.dirtyHidden = false; + } + + + if (sprite.dirtyPath) { + if (sprite.type == "circle" || sprite.type == "ellipse") { + cx = scrubbedAttrs.x; + cy = scrubbedAttrs.y; + rx = scrubbedAttrs.rx || scrubbedAttrs.r || 0; + ry = scrubbedAttrs.ry || scrubbedAttrs.r || 0; + dom.path = Ext.String.format("ar{0},{1},{2},{3},{4},{1},{4},{1}", + Math.round((cx - rx) * me.zoom), + Math.round((cy - ry) * me.zoom), + Math.round((cx + rx) * me.zoom), + Math.round((cy + ry) * me.zoom), + Math.round(cx * me.zoom)); + sprite.dirtyPath = false; + } else { + sprite.attr.path = scrubbedAttrs.path = me.setPaths(sprite, scrubbedAttrs) || scrubbedAttrs.path; + dom.path = me.path2vml(scrubbedAttrs.path); + sprite.dirtyPath = false; + } + } + + + if ("clip-rect" in scrubbedAttrs) { + me.setClip(sprite, scrubbedAttrs); + } + + + if (sprite.type == "text") { + me.setTextAttributes(sprite, scrubbedAttrs); + } + + + if (scrubbedAttrs.opacity || scrubbedAttrs['stroke-opacity'] || scrubbedAttrs.fill) { + me.setFill(sprite, scrubbedAttrs); + } + + + if (scrubbedAttrs.stroke || scrubbedAttrs['stroke-opacity'] || scrubbedAttrs.fill) { + me.setStroke(sprite, scrubbedAttrs); + } + + + style = spriteAttr.style; + if (style) { + el.setStyle(style); + } + + sprite.dirty = false; + }, + + setZIndex: function (sprite) { + var me = this, + zIndex = sprite.attr.zIndex, + shift = me.zIndexShift, + items, iLen, item, i; + + if (zIndex < shift) { + + + items = me.items.items; + iLen = items.length; + + for (i = 0; i < iLen; i++) { + if ((zIndex = items[i].attr.zIndex) && zIndex < shift) { + shift = zIndex; + } + } + + me.zIndexShift = shift; + for (i = 0; i < iLen; i++) { + item = items[i]; + if (item.el) { + item.el.setStyle('zIndex', item.attr.zIndex - shift); + } + item.zIndexDirty = false; + } + } else if (sprite.el) { + sprite.el.setStyle('zIndex', zIndex - shift); + sprite.zIndexDirty = false; + } + }, + + + setPaths: function (sprite, params) { + var spriteAttr = sprite.attr, thickness = sprite.attr['stroke-width'] || 1; + + sprite.bbox.plain = null; + sprite.bbox.transform = null; + if (sprite.type == 'circle') { + spriteAttr.rx = spriteAttr.ry = params.r; + return Ext.draw.Draw.ellipsePath(sprite); + } + else if (sprite.type == 'ellipse') { + spriteAttr.rx = params.rx; + spriteAttr.ry = params.ry; + return Ext.draw.Draw.ellipsePath(sprite); + } + else if (sprite.type == 'rect') { + spriteAttr.rx = spriteAttr.ry = params.r; + return Ext.draw.Draw.rectPath(sprite); + } + else if (sprite.type == 'path' && spriteAttr.path) { + return Ext.draw.Draw.pathToAbsolute(spriteAttr.path); + } + return false; + }, + + setFill: function (sprite, params) { + var me = this, + el = sprite.el.dom, + fillEl = el.fill, + newfill = false, + opacity, gradient, fillUrl, rotation, angle; + + if (!fillEl) { + + fillEl = el.fill = me.createNode("fill"); + newfill = true; + } + if (Ext.isArray(params.fill)) { + params.fill = params.fill[0]; + } + if (params.fill == "none") { + fillEl.on = false; + } + else { + if (typeof params.opacity == "number") { + fillEl.opacity = params.opacity; + } + if (typeof params["fill-opacity"] == "number") { + fillEl.opacity = params["fill-opacity"]; + } + fillEl.on = true; + if (typeof params.fill == "string") { + fillUrl = params.fill.match(me.fillUrlRe); + if (fillUrl) { + fillUrl = fillUrl[1]; + + if (fillUrl.charAt(0) == "#") { + gradient = me.gradientsColl.getByKey(fillUrl.substring(1)); + } + if (gradient) { + + rotation = params.rotation; + angle = -(gradient.angle + 270 + (rotation ? rotation.degrees : 0)) % 360; + + if (angle === 0) { + angle = 180; + } + fillEl.angle = angle; + fillEl.type = "gradient"; + fillEl.method = "sigma"; + if (fillEl.colors) { + fillEl.colors.value = gradient.colors; + } else { + fillEl.colors = gradient.colors; + } + } + + else { + fillEl.src = fillUrl; + fillEl.type = "tile"; + } + } + else { + fillEl.color = Ext.draw.Color.toHex(params.fill); + fillEl.src = ""; + fillEl.type = "solid"; + } + } + } + if (newfill) { + el.appendChild(fillEl); + } + }, + + setStroke: function (sprite, params) { + var me = this, + el = sprite.el.dom, + strokeEl = sprite.strokeEl, + newStroke = false, + width, opacity; + + if (!strokeEl) { + strokeEl = sprite.strokeEl = me.createNode("stroke"); + newStroke = true; + } + if (Ext.isArray(params.stroke)) { + params.stroke = params.stroke[0]; + } + if (!params.stroke || params.stroke == "none" || params.stroke == 0 || params["stroke-width"] == 0) { + strokeEl.on = false; + } + else { + strokeEl.on = true; + if (params.stroke && !params.stroke.match(me.fillUrlRe)) { + + strokeEl.color = Ext.draw.Color.toHex(params.stroke); + } + strokeEl.dashstyle = params["stroke-dasharray"] ? "dash" : "solid"; + strokeEl.joinstyle = params["stroke-linejoin"]; + strokeEl.endcap = params["stroke-linecap"] || "round"; + strokeEl.miterlimit = params["stroke-miterlimit"] || 8; + width = parseFloat(params["stroke-width"] || 1) * 0.75; + opacity = params["stroke-opacity"] || 1; + + if (Ext.isNumber(width) && width < 1) { + strokeEl.weight = 1; + strokeEl.opacity = opacity * width; + } + else { + strokeEl.weight = width; + strokeEl.opacity = opacity; + } + } + if (newStroke) { + el.appendChild(strokeEl); + } + }, + + setClip: function (sprite, params) { + var me = this, + el = sprite.el, + clipEl = sprite.clipEl, + rect = String(params["clip-rect"]).split(me.separatorRe); + if (!clipEl) { + clipEl = sprite.clipEl = me.el.insertFirst(Ext.getDoc().dom.createElement("div")); + clipEl.addCls(Ext.baseCSSPrefix + 'vml-sprite'); + } + if (rect.length == 4) { + rect[2] = +rect[2] + (+rect[0]); + rect[3] = +rect[3] + (+rect[1]); + clipEl.setStyle("clip", Ext.String.format("rect({1}px {2}px {3}px {0}px)", rect[0], rect[1], rect[2], rect[3])); + clipEl.setSize(me.el.width, me.el.height); + } + else { + clipEl.setStyle("clip", ""); + } + }, + + setTextAttributes: function (sprite, params) { + var me = this, + vml = sprite.vml, + textStyle = vml.textpath.style, + spanCacheStyle = me.span.style, + zoom = me.zoom, + round = Math.round, + fontObj = { + fontSize: "font-size", + fontWeight: "font-weight", + fontStyle: "font-style" + }, + fontProp, + paramProp; + if (sprite.dirtyFont) { + if (params.font) { + textStyle.font = spanCacheStyle.font = params.font; + } + if (params["font-family"]) { + textStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(me.fontFamilyRe, "") + '"'; + spanCacheStyle.fontFamily = params["font-family"]; + } + + for (fontProp in fontObj) { + paramProp = params[fontObj[fontProp]]; + if (paramProp) { + textStyle[fontProp] = spanCacheStyle[fontProp] = paramProp; + } + } + + me.setText(sprite, params.text); + + if (vml.textpath.string) { + me.span.innerHTML = String(vml.textpath.string).replace(/"); + } + vml.W = me.span.offsetWidth; + vml.H = me.span.offsetHeight + 2; + + + if (params["text-anchor"] == "middle") { + textStyle["v-text-align"] = "center"; + } + else if (params["text-anchor"] == "end") { + textStyle["v-text-align"] = "right"; + vml.bbx = -Math.round(vml.W / 2); + } + else { + textStyle["v-text-align"] = "left"; + vml.bbx = Math.round(vml.W / 2); + } + } + vml.X = params.x; + vml.Y = params.y; + vml.path.v = Ext.String.format("m{0},{1}l{2},{1}", Math.round(vml.X * zoom), Math.round(vml.Y * zoom), Math.round(vml.X * zoom) + 1); + + sprite.bbox.plain = null; + sprite.bbox.transform = null; + sprite.dirtyFont = false; + }, + + setText: function (sprite, text) { + sprite.vml.textpath.string = Ext.htmlDecode(text); + }, + + hide: function () { + this.el.hide(); + }, + + show: function () { + this.el.show(); + }, + + hidePrim: function (sprite) { + sprite.el.addCls(Ext.baseCSSPrefix + 'hide-visibility'); + }, + + showPrim: function (sprite) { + sprite.el.removeCls(Ext.baseCSSPrefix + 'hide-visibility'); + }, + + setSize: function (width, height) { + var me = this; + width = width || me.width; + height = height || me.height; + me.width = width; + me.height = height; + + if (me.el) { + + if (width != undefined) { + me.el.setWidth(width); + } + if (height != undefined) { + me.el.setHeight(height); + } + } + + me.callParent(arguments); + }, + + + applyViewBox: function () { + var me = this, + viewBox = me.viewBox, + width = me.width, + height = me.height, + items, + iLen, + i; + + me.callParent(); + + if (viewBox && (width || height)) { + items = me.items.items; + iLen = items.length; + + for (i = 0; i < iLen; i++) { + me.applyTransformations(items[i]); + } + } + }, + + onAdd: function (item) { + this.callParent(arguments); + if (this.el) { + this.renderItem(item); + } + }, + + onRemove: function (sprite) { + if (sprite.el) { + sprite.el.remove(); + delete sprite.el; + } + this.callParent(arguments); + }, + + render: function (container) { + var me = this, + doc = Ext.getDoc().dom, + el; + + if (!me.createNode) { + try { + if (!doc.namespaces.rvml) { + doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml"); + } + me.createNode = function (tagName) { + return doc.createElement("'); + }; + } catch (e) { + me.createNode = function (tagName) { + return doc.createElement("<" + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">'); + }; + } + } + + if (!me.el) { + el = doc.createElement("div"); + me.el = Ext.get(el); + me.el.addCls(me.baseVmlCls); + + + me.span = doc.createElement("span"); + Ext.get(me.span).addCls(me.measureSpanCls); + el.appendChild(me.span); + me.el.setSize(me.width || 0, me.height || 0); + container.appendChild(el); + me.el.on({ + scope: me, + mouseup: me.onMouseUp, + mousedown: me.onMouseDown, + mouseover: me.onMouseOver, + mouseout: me.onMouseOut, + mousemove: me.onMouseMove, + mouseenter: me.onMouseEnter, + mouseleave: me.onMouseLeave, + click: me.onClick, + dblclick: me.onDblClick + }); + } + me.renderAll(); + }, + + renderAll: function () { + this.items.each(this.renderItem, this); + }, + + redraw: function (sprite) { + sprite.dirty = true; + this.renderItem(sprite); + }, + + renderItem: function (sprite) { + + if (!this.el) { + return; + } + + + if (!sprite.el) { + this.createSpriteElement(sprite); + } + + if (sprite.dirty) { + this.applyAttrs(sprite); + if (sprite.dirtyTransform) { + this.applyTransformations(sprite); + } + } + }, + + rotationCompensation: function (deg, dx, dy) { + var matrix = new Ext.draw.Matrix(); + matrix.rotate(-deg, 0.5, 0.5); + return { + x: matrix.x(dx, dy), + y: matrix.y(dx, dy) + }; + }, + + transform: function (sprite, matrixOnly) { + var me = this, + bbox = me.getBBox(sprite, true), + cx = bbox.x + bbox.width * 0.5, + cy = bbox.y + bbox.height * 0.5, + matrix = new Ext.draw.Matrix(), + transforms = sprite.transformations, + transformsLength = transforms.length, + i = 0, + deltaDegrees = 0, + deltaScaleX = 1, + deltaScaleY = 1, + flip = "", + el = sprite.el, + dom = el.dom, + domStyle = dom.style, + zoom = me.zoom, + skew = sprite.skew, + shift = me.viewBoxShift, + deltaX, deltaY, transform, type, compensate, y, fill, newAngle, zoomScaleX, zoomScaleY, newOrigin, offset; + + + for (; i < transformsLength; i++) { + transform = transforms[i]; + type = transform.type; + if (type == "translate") { + matrix.translate(transform.x, transform.y); + } + else if (type == "rotate") { + matrix.rotate(transform.degrees, transform.x, transform.y); + deltaDegrees += transform.degrees; + } + else if (type == "scale") { + matrix.scale(transform.x, transform.y, transform.centerX, transform.centerY); + deltaScaleX *= transform.x; + deltaScaleY *= transform.y; + } + } + + sprite.matrix = matrix.clone(); + + if (matrixOnly) { + return; + } + + if (shift) { + matrix.prepend(shift.scale, 0, 0, shift.scale, shift.dx * shift.scale, shift.dy * shift.scale); + } + + + if (sprite.type != "image" && skew) { + skew.origin = "0,0"; + + skew.matrix = matrix.toString(); + + + + offset = matrix.offset(); + if (offset[0] > 32767) { + offset[0] = 32767; + } else if (offset[0] < -32768) { + offset[0] = -32768; + } + if (offset[1] > 32767) { + offset[1] = 32767; + } else if (offset[1] < -32768) { + offset[1] = -32768; + } + skew.offset = offset; + } + else { + domStyle.filter = matrix.toFilter(); + domStyle.left = Math.min( + matrix.x(bbox.x, bbox.y), + matrix.x(bbox.x + bbox.width, bbox.y), + matrix.x(bbox.x, bbox.y + bbox.height), + matrix.x(bbox.x + bbox.width, bbox.y + bbox.height)) + 'px'; + domStyle.top = Math.min( + matrix.y(bbox.x, bbox.y), + matrix.y(bbox.x + bbox.width, bbox.y), + matrix.y(bbox.x, bbox.y + bbox.height), + matrix.y(bbox.x + bbox.width, bbox.y + bbox.height)) + 'px'; + } + }, + + createItem: function (config) { + return Ext.create('Ext.draw.Sprite', config); + }, + + getRegion: function () { + return this.el.getRegion(); + }, + + addCls: function (sprite, className) { + if (sprite && sprite.el) { + sprite.el.addCls(className); + } + }, + + removeCls: function (sprite, className) { + if (sprite && sprite.el) { + sprite.el.removeCls(className); + } + }, + + + addGradient: function (gradient) { + var gradients = this.gradientsColl || (this.gradientsColl = Ext.create('Ext.util.MixedCollection')), + colors = [], + stops = Ext.create('Ext.util.MixedCollection'), + keys, + items, + iLen, + key, + item, + i; + + + stops.addAll(gradient.stops); + stops.sortByKey("ASC", function (a, b) { + a = parseInt(a, 10); + b = parseInt(b, 10); + return a > b ? 1 : (a < b ? -1 : 0); + }); + + keys = stops.keys; + items = stops.items; + iLen = keys.length; + + for (i = 0; i < iLen; i++) { + key = keys[i]; + item = items[i]; + colors.push(key + '% ' + item.color); + } + + gradients.add(gradient.id, { + colors: colors.join(","), + angle: gradient.angle + }); + }, + + destroy: function () { + var me = this; + + me.callParent(arguments); + if (me.el) { + me.el.remove(); + } + delete me.el; + } +}); + + + +Ext.define('Ext.form.action.Action', { + alternateClassName: 'Ext.form.Action', + + + + + + + + + + + + + + + + + + + + + + + + + + + submitEmptyText : true, + + + + + + + + + + + constructor: function(config) { + if (config) { + Ext.apply(this, config); + } + + + var params = config.params; + if (Ext.isString(params)) { + this.params = Ext.Object.fromQueryString(params); + } + }, + + + run: Ext.emptyFn, + + + + + + + onFailure : function(response){ + this.response = response; + this.failureType = Ext.form.action.Action.CONNECT_FAILURE; + this.form.afterAction(this, false); + }, + + + processResponse : function(response){ + this.response = response; + if (!response.responseText && !response.responseXML) { + return true; + } + return (this.result = this.handleResponse(response)); + }, + + + 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 me = this, + undef, + form = me.form; + return { + success: me.onSuccess, + failure: me.onFailure, + scope: me, + timeout: (this.timeout * 1000) || (form.timeout * 1000), + upload: form.fileUpload ? me.onSuccess : undef + }; + }, + + statics: { + + CLIENT_INVALID: 'client', + + + SERVER_INVALID: 'server', + + + CONNECT_FAILURE: 'connect', + + + LOAD_FAILURE: 'load' + + + } +}); + + + +Ext.define('Ext.form.action.Load', { + extend: Ext.form.action.Action , + + alternateClassName: 'Ext.form.Action.Load', + alias: 'formaction.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(response){ + var result = this.processResponse(response), + form = this.form; + if (result === true || !result.success || !result.data) { + this.failureType = Ext.form.action.Action.LOAD_FAILURE; + form.afterAction(this, false); + return; + } + form.clearInvalid(); + form.setValues(result.data); + form.afterAction(this, true); + }, + + + handleResponse: function(response) { + var reader = this.form.reader, + rs, data; + if (reader) { + rs = reader.read(response); + data = rs.records && rs.records[0] ? rs.records[0].data : null; + return { + success : rs.success, + data : data + }; + } + return Ext.decode(response.responseText); + } +}); + + + + +Ext.define('Ext.form.action.Submit', { + extend: Ext.form.action.Action , + alternateClassName: 'Ext.form.Action.Submit', + alias: 'formaction.submit', + + type: 'submit', + + + + + run : function(){ + var me = this, + form = me.form; + + if (me.clientValidation === false || form.isValid()) { + me.doSubmit(); + } else { + + me.failureType = Ext.form.action.Action.CLIENT_INVALID; + form.afterAction(me, false); + } + }, + + + doSubmit: function() { + var me = this, + ajaxOptions = Ext.apply(me.createCallback(), { + url: me.getUrl(), + method: me.getMethod(), + headers: me.headers + }), + form = me.form, + jsonSubmit = me.jsonSubmit || form.jsonSubmit, + paramsProp = jsonSubmit ? 'jsonData' : 'params', + formInfo; + + + + if (form.hasUpload()) { + formInfo = me.buildForm(); + ajaxOptions.form = formInfo.formEl; + ajaxOptions.isUpload = true; + } else { + ajaxOptions[paramsProp] = me.getParams(jsonSubmit); + } + + Ext.Ajax.request(ajaxOptions); + if (formInfo) { + me.cleanup(formInfo); + } + }, + + cleanup: function(formInfo) { + var formEl = formInfo.formEl, + uploadEls = formInfo.uploadEls, + uploadFields = formInfo.uploadFields, + len = uploadFields.length, + i, field; + + for (i = 0; i < len; ++i) { + field = uploadFields[i]; + if (!field.clearOnSubmit) { + field.restoreInput(uploadEls[i]); + } + } + + if (formEl) { + Ext.removeNode(formEl); + } + }, + + + getParams: function(useModelValues) { + var falseVal = false, + configParams = this.callParent(), + fieldParams = this.form.getValues(falseVal, falseVal, this.submitEmptyText !== falseVal, useModelValues, true); + return Ext.apply({}, fieldParams, configParams); + }, + + + buildForm: function() { + var me = this, + fieldsSpec = [], + formSpec, + formEl, + basicForm = me.form, + params = me.getParams(), + uploadFields = [], + uploadEls = [], + fields = basicForm.getFields().items, + i, + len = fields.length, + field, key, value, v, vLen, + el; + + for (i = 0; i < len; ++i) { + field = fields[i]; + + if (field.isFileUpload()) { + uploadFields.push(field); + } + } + + for (key in params) { + if (params.hasOwnProperty(key)) { + value = params[key]; + + if (Ext.isArray(value)) { + vLen = value.length; + for (v = 0; v < vLen; v++) { + fieldsSpec.push(me.getFieldConfig(key, value[v])); + } + } else { + fieldsSpec.push(me.getFieldConfig(key, value)); + } + } + } + + formSpec = { + tag: 'form', + role: 'presentation', + action: me.getUrl(), + method: me.getMethod(), + target: me.target || '_self', + style: 'display:none', + cn: fieldsSpec + }; + + + if (uploadFields.length) { + formSpec.encoding = formSpec.enctype = 'multipart/form-data'; + } + + + formEl = Ext.DomHelper.append(Ext.getBody(), formSpec); + + + + + len = uploadFields.length; + + for (i = 0; i < len; ++i) { + el = uploadFields[i].extractFileInput(); + formEl.appendChild(el); + uploadEls.push(el); + } + + return { + formEl: formEl, + uploadFields: uploadFields, + uploadEls: uploadEls + }; + }, + + getFieldConfig: function(name, value) { + return { + tag: 'input', + type: 'hidden', + name: name, + value: Ext.String.htmlEncode(value) + }; + }, + + + onSuccess: function(response) { + var form = this.form, + success = true, + result = this.processResponse(response); + if (result !== true && !result.success) { + if (result.errors) { + form.markInvalid(result.errors); + } + this.failureType = Ext.form.action.Action.SERVER_INVALID; + success = false; + } + form.afterAction(this, success); + }, + + + handleResponse: function(response) { + var form = this.form, + errorReader = form.errorReader, + rs, errors, i, len, records, result; + + if (errorReader) { + rs = errorReader.read(response); + records = rs.records; + errors = []; + if (records) { + for(i = 0, len = records.length; i < len; i++) { + errors[i] = records[i].data; + } + } + if (errors.length < 1) { + errors = null; + } + result = { + success : rs.success, + errors : errors + }; + } else { + try { + result = Ext.decode(response.responseText); + } catch (e) { + result = { + success: false, + errors: [] + }; + } + + } + return result; + } +}); + + + +Ext.define('Ext.form.Basic', { + extend: Ext.util.Observable , + alternateClassName: 'Ext.form.BasicForm', + + + + + + taskDelay: 10, + + + constructor: function(owner, config) { + var me = this, + reader; + + + me.owner = owner; + + me.checkValidityTask = new Ext.util.DelayedTask(me.checkValidity, me); + me.checkDirtyTask = new Ext.util.DelayedTask(me.checkDirty, me); + + + + + me.monitor = new Ext.container.Monitor({ + selector: '[isFormField]:not([excludeForm])', + scope: me, + addHandler: me.onFieldAdd, + removeHandler: me.onFieldRemove, + invalidateHandler: me.onMonitorInvalidate + }); + me.monitor.bind(owner); + + Ext.apply(me, config); + + + if (Ext.isString(me.paramOrder)) { + me.paramOrder = me.paramOrder.split(/[\s,|]/); + } + + reader = me.reader; + if (reader && !reader.isReader) { + if (typeof reader === 'string') { + reader = { + type: reader + }; + } + me.reader = Ext.createByAlias('reader.' + reader.type, reader); + } + + reader = me.errorReader; + if (reader && !reader.isReader) { + if (typeof reader === 'string') { + reader = { + type: reader + }; + } + me.errorReader = Ext.createByAlias('reader.' + reader.type, reader); + } + + me.addEvents( + + 'beforeaction', + + 'actionfailed', + + 'actioncomplete', + + 'validitychange', + + 'dirtychange' + ); + me.callParent(); + }, + + + initialize : function() { + this.initialized = true; + this.onValidityChange(!this.hasInvalidField()); + }, + + + + + + + + + + + + + + timeout: 30, + + + + + + + paramsAsHash: false, + + + + waitTitle: 'Please Wait...', + + + + trackResetOnLoad: false, + + + + + + + + + + wasDirty: false, + + + + destroy: function() { + var me = this, + mon = me.monitor; + + if (mon) { + mon.unbind(); + me.monitor = null; + } + me.clearListeners(); + me.checkValidityTask.cancel(); + me.checkDirtyTask.cancel(); + me.isDestroyed = true; + }, + + onFieldAdd: function(field){ + var me = this; + + me.mon(field, 'validitychange', me.checkValidityDelay, me); + me.mon(field, 'dirtychange', me.checkDirtyDelay, me); + me.onMonitorInvalidate(); + }, + + onFieldRemove: function(field){ + var me = this; + + me.mun(field, 'validitychange', me.checkValidityDelay, me); + me.mun(field, 'dirtychange', me.checkDirtyDelay, me); + me.onMonitorInvalidate(); + }, + + onMonitorInvalidate: function() { + if (this.initialized) { + this.checkValidityDelay(); + } + }, + + + getFields: function() { + return this.monitor.getItems(); + }, + + + getBoundItems: function() { + var boundItems = this._boundItems; + + if (!boundItems || boundItems.getCount() === 0) { + boundItems = this._boundItems = new Ext.util.MixedCollection(); + boundItems.addAll(this.owner.query('[formBind]')); + } + + return boundItems; + }, + + + hasInvalidField: function() { + return !!this.getFields().findBy(function(field) { + var preventMark = field.preventMark, + isValid; + field.preventMark = true; + isValid = field.isValid(); + field.preventMark = preventMark; + return !isValid; + }); + }, + + + isValid: function() { + var me = this, + invalid; + Ext.suspendLayouts(); + invalid = me.getFields().filterBy(function(field) { + return !field.validate(); + }); + Ext.resumeLayouts(true); + return invalid.length < 1; + }, + + + checkValidity: function() { + var me = this, + valid; + + if (me.isDestroyed) { + return; + } + + valid = !me.hasInvalidField(); + if (valid !== me.wasValid) { + me.onValidityChange(valid); + me.fireEvent('validitychange', me, valid); + me.wasValid = valid; + } + }, + + checkValidityDelay: function(){ + var timer = this.taskDelay; + if (timer) { + this.checkValidityTask.delay(timer); + } else { + this.checkValidity(); + } + }, + + + onValidityChange: function(valid) { + var boundItems = this.getBoundItems(), + items, i, iLen, cmp; + + if (boundItems) { + items = boundItems.items; + iLen = items.length; + + for (i = 0; i < iLen; i++) { + cmp = items[i]; + + if (cmp.disabled === valid) { + cmp.setDisabled(!valid); + } + } + } + }, + + + isDirty: function() { + return !!this.getFields().findBy(function(f) { + return f.isDirty(); + }); + }, + + checkDirtyDelay: function(){ + var timer = this.taskDelay; + if (timer) { + this.checkDirtyTask.delay(timer); + } else { + this.checkDirty(); + } + }, + + + checkDirty: function() { + var me = this, + dirty; + + if (me.isDestroyed) { + return; + } + + dirty = this.isDirty(); + if (dirty !== this.wasDirty) { + this.fireEvent('dirtychange', this, dirty); + this.wasDirty = dirty; + } + }, + + + hasUpload: function() { + return !!this.getFields().findBy(function(f) { + return f.isFileUpload(); + }); + }, + + + doAction: function(action, options) { + if (Ext.isString(action)) { + action = Ext.ClassManager.instantiateByAlias('formaction.' + action, Ext.apply({}, options, {form: this})); + } + if (this.fireEvent('beforeaction', this, action) !== false) { + this.beforeAction(action); + Ext.defer(action.run, 100, action); + } + return this; + }, + + + submit: function(options) { + options = options || {}; + var me = this, + action; + + if (options.standardSubmit || me.standardSubmit) { + action = 'standardsubmit'; + } else { + action = me.api ? 'directsubmit' : 'submit'; + } + + return me.doAction(action, options); + }, + + + load: function(options) { + return this.doAction(this.api ? 'directload' : 'load', options); + }, + + + updateRecord: function(record) { + record = record || this._record; + if (!record) { + Ext.Error.raise("A record is required."); + return this; + } + + var fields = record.fields.items, + values = this.getFieldValues(), + obj = {}, + i = 0, + len = fields.length, + name; + + for (; i < len; ++i) { + name = fields[i].name; + + if (values.hasOwnProperty(name)) { + obj[name] = values[name]; + } + } + + record.beginEdit(); + record.set(obj); + record.endEdit(); + + return this; + }, + + + loadRecord: function(record) { + this._record = record; + return this.setValues(record.getData()); + }, + + + getRecord: function() { + return this._record; + }, + + + beforeAction: function(action) { + var me = this, + waitMsg = action.waitMsg, + maskCls = Ext.baseCSSPrefix + 'mask-loading', + fields = me.getFields().items, + f, + fLen = fields.length, + field, waitMsgTarget; + + + for (f = 0; f < fLen; f++) { + field = fields[f]; + + if (field.isFormField && field.syncValue) { + field.syncValue(); + } + } + + if (waitMsg) { + waitMsgTarget = me.waitMsgTarget; + if (waitMsgTarget === true) { + me.owner.el.mask(waitMsg, maskCls); + } else if (waitMsgTarget) { + waitMsgTarget = me.waitMsgTarget = Ext.get(waitMsgTarget); + waitMsgTarget.mask(waitMsg, maskCls); + } else { + me.floatingAncestor = me.owner.up('[floating]'); + + + + + + + + + if (me.floatingAncestor) { + me.savePreventFocusOnActivate = me.floatingAncestor.preventFocusOnActivate; + me.floatingAncestor.preventFocusOnActivate = true; + } + Ext.MessageBox.wait(waitMsg, action.waitTitle || me.waitTitle); + } + } + }, + + + afterAction: function(action, success) { + var me = this; + if (action.waitMsg) { + var messageBox = Ext.MessageBox, + waitMsgTarget = me.waitMsgTarget; + if (waitMsgTarget === true) { + me.owner.el.unmask(); + } else if (waitMsgTarget) { + waitMsgTarget.unmask(); + } else { + messageBox.hide(); + } + } + + if (me.floatingAncestor) { + me.floatingAncestor.preventFocusOnActivate = me.savePreventFocusOnActivate; + } + if (success) { + if (action.reset) { + me.reset(); + } + Ext.callback(action.success, action.scope || action, [me, action]); + me.fireEvent('actioncomplete', me, action); + } else { + Ext.callback(action.failure, action.scope || action, [me, action]); + me.fireEvent('actionfailed', me, action); + } + }, + + + + findField: function (id) { + return this.getFields().findBy(function (f) { + return f.id === id || f.name === id || f.dataIndex === id; + }); + }, + + + + markInvalid: function(errors) { + var me = this, + e, eLen, error, value, + key; + + function mark(fieldId, msg) { + var field = me.findField(fieldId); + if (field) { + field.markInvalid(msg); + } + } + + if (Ext.isArray(errors)) { + eLen = errors.length; + + for (e = 0; e < eLen; e++) { + error = errors[e]; + mark(error.id, error.msg); + } + } else if (errors instanceof Ext.data.Errors) { + eLen = errors.items.length; + for (e = 0; e < eLen; e++) { + error = errors.items[e]; + + mark(error.field, error.message); + } + } else { + for (key in errors) { + if (errors.hasOwnProperty(key)) { + value = errors[key]; + mark(key, value, errors); + } + } + } + return this; + }, + + + setValues: function(values) { + var me = this, + v, vLen, val, field; + + function setVal(fieldId, val) { + var field = me.findField(fieldId); + if (field) { + field.setValue(val); + if (me.trackResetOnLoad) { + field.resetOriginalValue(); + } + } + } + + + + Ext.suspendLayouts(); + if (Ext.isArray(values)) { + + vLen = values.length; + + for (v = 0; v < vLen; v++) { + val = values[v]; + + setVal(val.id, val.value); + } + } else { + + Ext.iterate(values, setVal); + } + Ext.resumeLayouts(true); + return this; + }, + + + getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues, isSubmitting) { + var values = {}, + fields = this.getFields().items, + fLen = fields.length, + isArray = Ext.isArray, + field, data, val, bucket, name, f; + + for (f = 0; f < fLen; f++) { + field = fields[f]; + if (!dirtyOnly || field.isDirty()) { + data = field[useDataValues ? 'getModelData' : 'getSubmitData'](includeEmptyText, isSubmitting); + + if (Ext.isObject(data)) { + for (name in data) { + if (data.hasOwnProperty(name)) { + val = data[name]; + + if (includeEmptyText && val === '') { + val = field.emptyText || ''; + } + + if (!field.isRadio) { + if (values.hasOwnProperty(name)) { + bucket = values[name]; + + if (!isArray(bucket)) { + bucket = values[name] = [bucket]; + } + + if (isArray(val)) { + values[name] = bucket.concat(val); + } else { + bucket.push(val); + } + } else { + values[name] = val; + } + } else { + values[name] = values[name] || val; + } + } + } + } + } + } + + if (asString) { + values = Ext.Object.toQueryString(values); + } + return values; + }, + + + getFieldValues: function(dirtyOnly) { + return this.getValues(false, dirtyOnly, false, true); + }, + + + clearInvalid: function() { + Ext.suspendLayouts(); + + var me = this, + fields = me.getFields().items, + f, + fLen = fields.length; + + for (f = 0; f < fLen; f++) { + fields[f].clearInvalid(); + } + + Ext.resumeLayouts(true); + return me; + }, + + + reset: function(resetRecord) { + Ext.suspendLayouts(); + + var me = this, + fields = me.getFields().items, + f, + fLen = fields.length; + + for (f = 0; f < fLen; f++) { + fields[f].reset(); + } + + Ext.resumeLayouts(true); + + if (resetRecord === true) { + delete me._record; + } + return me; + }, + + + applyToFields: function(obj) { + var fields = this.getFields().items, + f, + fLen = fields.length; + + for (f = 0; f < fLen; f++) { + Ext.apply(fields[f], obj); + } + + return this; + }, + + + applyIfToFields: function(obj) { + var fields = this.getFields().items, + f, + fLen = fields.length; + + for (f = 0; f < fLen; f++) { + Ext.applyIf(fields[f], obj); + } + + return this; + } +}); + + + +Ext.define('Ext.form.FieldAncestor', { + + + + + + + + + xhooks: { + initHierarchyState: function(hierarchyState) { + if (this.fieldDefaults) { + if (hierarchyState.fieldDefaults) { + hierarchyState.fieldDefaults = Ext.apply(Ext.Object.chain(hierarchyState.fieldDefaults), this.fieldDefaults); + } else { + hierarchyState.fieldDefaults = this.fieldDefaults; + } + } + } + }, + + + initFieldAncestor: function() { + var me = this; + + me.addEvents( + + 'fieldvaliditychange', + + + 'fielderrorchange' + ); + + + + + me.monitor = new Ext.container.Monitor({ + scope: me, + selector: '[isFormField]:not([excludeForm])', + addHandler: me.onChildFieldAdd, + removeHandler: me.onChildFieldRemove + }); + me.initFieldDefaults(); + }, + + initMonitor: function() { + this.monitor.bind(this); + }, + + onChildFieldAdd: function(field) { + var me = this; + me.mon(field, 'errorchange', me.handleFieldErrorChange, me); + me.mon(field, 'validitychange', me.handleFieldValidityChange, me); + }, + + onChildFieldRemove: function(field) { + var me = this; + me.mun(field, 'errorchange', me.handleFieldErrorChange, me); + me.mun(field, 'validitychange', me.handleFieldValidityChange, me); + }, + + + initFieldDefaults: function() { + if (!this.fieldDefaults) { + this.fieldDefaults = {}; + } + }, + + + handleFieldValidityChange: function(field, isValid) { + var me = this; + if (field !== me) { + me.fireEvent('fieldvaliditychange', me, field, isValid); + me.onFieldValidityChange(field, isValid); + } + }, + + + handleFieldErrorChange: function(labelable, activeError) { + var me = this; + if (labelable !== me) { + me.fireEvent('fielderrorchange', me, labelable, activeError); + me.onFieldErrorChange(labelable, activeError); + } + }, + + + onFieldValidityChange: Ext.emptyFn, + + + onFieldErrorChange: Ext.emptyFn, + + beforeDestroy: function(){ + this.monitor.unbind(); + this.callParent(); + } + +}); + + + +Ext.define('Ext.layout.component.field.FieldContainer', { + + + + extend: Ext.layout.component.field.Field , + + alias: 'layout.fieldcontainer', + + + + type: 'fieldcontainer', + + waitForOuterHeightInDom: true, + waitForOuterWidthInDom: true, + + beginLayout: function(ownerContext) { + var owner = this.owner; + this.callParent(arguments); + + + ownerContext.hasRawContent = true; + owner.bodyEl.setStyle('height', ''); + owner.containerEl.setStyle('height', ''); + ownerContext.containerElContext = ownerContext.getEl('containerEl'); + }, + + measureContentHeight: function (ownerContext) { + + + + return ownerContext.hasDomProp('containerLayoutDone') ? this.callParent(arguments) : NaN; + }, + + measureContentWidth: function (ownerContext) { + + return ownerContext.hasDomProp('containerLayoutDone') ? this.callParent(arguments) : NaN; + }, + + publishInnerWidth: function (ownerContext, width) { + var bodyContext = ownerContext.bodyCellContext, + innerWidth = bodyContext.el.getWidth() - bodyContext.getPaddingInfo().width; + + bodyContext.setWidth(innerWidth, false); + ownerContext.containerElContext.setWidth(innerWidth, false); + }, + + publishInnerHeight: function (ownerContext, height) { + var bodyContext = ownerContext.bodyCellContext, + containerElContext = ownerContext.containerElContext; + + height -= this.measureLabelErrorHeight(ownerContext) + bodyContext.getPaddingInfo().height; + bodyContext.setHeight(height); + containerElContext.setHeight(height); + } +}); + + + +Ext.define('Ext.form.FieldContainer', { + extend: Ext.container.Container , + mixins: { + labelable: Ext.form.Labelable , + fieldAncestor: Ext.form.FieldAncestor + }, + + + alias: 'widget.fieldcontainer', + + componentLayout: 'fieldcontainer', + + componentCls: Ext.baseCSSPrefix + 'form-fieldcontainer', + + + + customOverflowEl: 'containerEl', + + childEls: [ + 'containerEl' + ], + + + + + combineLabels: false, + + + + labelConnector: ', ', + + + + combineErrors: false, + + maskOnDisable: false, + + + invalidCls: '', + + fieldSubTpl: [ + '' + ], + + initComponent: function() { + var me = this; + + + me.initLabelable(); + me.initFieldAncestor(); + + me.callParent(); + me.initMonitor(); + }, + + getOverflowEl: function(){ + return this.containerEl; + }, + + + onAdd: function(item) { + var me = this; + + + + + if (item.isLabelable && Ext.isGecko && me.layout.type === 'absolute' && !me.hideLabel && me.labelAlign !== 'top') { + item.x += (me.labelWidth + me.labelPad); + } + me.callParent(arguments); + if (item.isLabelable && me.combineLabels) { + item.oldHideLabel = item.hideLabel; + item.hideLabel = true; + } + me.updateLabel(); + }, + + + onRemove: function(item, isDestroying) { + var me = this; + me.callParent(arguments); + if (!isDestroying) { + if (item.isLabelable && me.combineLabels) { + item.hideLabel = item.oldHideLabel; + } + me.updateLabel(); + } + }, + + initRenderTpl: function() { + var me = this; + if (!me.hasOwnProperty('renderTpl')) { + me.renderTpl = me.getTpl('labelableRenderTpl'); + } + return me.callParent(); + }, + + initRenderData: function() { + var me = this, + data = me.callParent(); + + data.containerElCls = me.containerElCls; + return Ext.applyIf(data, me.getLabelableRenderData()); + }, + + + getFieldLabel: function() { + var label = this.fieldLabel || ''; + if (!label && this.combineLabels) { + label = Ext.Array.map(this.query('[isFieldLabelable]'), function(field) { + return field.getFieldLabel(); + }).join(this.labelConnector); + } + return label; + }, + + getSubTplData: function() { + var ret = this.initRenderData(); + + Ext.apply(ret, this.subTplData); + return ret; + }, + + getSubTplMarkup: function() { + var me = this, + tpl = me.getTpl('fieldSubTpl'), + html; + + if (!tpl.renderContent) { + me.setupRenderTpl(tpl); + } + + html = tpl.apply(me.getSubTplData()); + return html; + }, + + + updateLabel: function() { + var me = this, + label = me.labelEl; + + if (label) { + me.setFieldLabel(me.getFieldLabel()); + } + }, + + + + onFieldErrorChange: function(field, activeError) { + if (this.combineErrors) { + var me = this, + oldError = me.getActiveError(), + invalidFields = Ext.Array.filter(me.query('[isFormField]'), function(field) { + return field.hasActiveError(); + }), + newErrors = me.getCombinedErrors(invalidFields); + + if (newErrors) { + me.setActiveErrors(newErrors); + } else { + me.unsetActiveError(); + } + + if (oldError !== me.getActiveError()) { + me.doComponentLayout(); + } + } + }, + + + getCombinedErrors: function(invalidFields) { + var errors = [], + f, + fLen = invalidFields.length, + field, + activeErrors, a, aLen, + error, label; + + for (f = 0; f < fLen; f++) { + field = invalidFields[f]; + activeErrors = field.getActiveErrors(); + aLen = activeErrors.length; + + for (a = 0; a < aLen; a++) { + error = activeErrors[a]; + label = field.getFieldLabel(); + + errors.push((label ? label + ': ' : '') + error); + } + } + + return errors; + }, + + getTargetEl: function() { + return this.containerEl; + }, + + applyTargetCls: function(targetCls) { + var containerElCls = this.containerElCls; + + this.containerElCls = containerElCls ? containerElCls + ' ' + targetCls : targetCls; + } +}); + + + +Ext.define('Ext.layout.container.CheckboxGroup', { + extend: Ext.layout.container.Container , + alias: ['layout.checkboxgroup'], + + + autoFlex: true, + + type: 'checkboxgroup', + + createsInnerCt: true, + + childEls: [ + 'innerCt' + ], + + renderTpl: [ + '', + '', + '', + '', + '', + '
' + ], + + lastOwnerItemsGeneration : null, + + beginLayout: function(ownerContext) { + var me = this, + columns, + numCols, + i, width, cwidth, + totalFlex = 0, flexedCols = 0, + autoFlex = me.autoFlex, + innerCtStyle = me.innerCt.dom.style; + + me.callParent(arguments); + + columns = me.columnNodes; + ownerContext.innerCtContext = ownerContext.getEl('innerCt', me); + + + if (!ownerContext.widthModel.shrinkWrap) { + numCols = columns.length; + + + if (me.columnsArray) { + + + for (i = 0; i < numCols; i++) { + width = me.owner.columns[i]; + if (width < 1) { + totalFlex += width; + flexedCols++; + } + } + + + for (i = 0; i < numCols; i++) { + width = me.owner.columns[i]; + if (width < 1) { + cwidth = ((width / totalFlex) * 100) + '%'; + } else { + cwidth = width + 'px'; + } + columns[i].style.width = cwidth; + } + } + + + else { + for (i = 0; i < numCols; i++) { + + + + cwidth = autoFlex + ? (1 / numCols * 100) + '%' + : ''; + columns[i].style.width = cwidth; + flexedCols++; + } + } + + + if (!flexedCols) { + innerCtStyle.tableLayout = 'fixed'; + innerCtStyle.width = ''; + + } else if (flexedCols < numCols) { + innerCtStyle.tableLayout = 'fixed'; + innerCtStyle.width = '100%'; + + } else { + innerCtStyle.tableLayout = 'auto'; + + if (autoFlex) { + innerCtStyle.width = '100%'; + } else { + innerCtStyle.width = ''; + } + } + + } else { + innerCtStyle.tableLayout = 'auto'; + innerCtStyle.width = ''; + } + }, + + cacheElements: function () { + var me = this; + + + me.callParent(); + + me.rowEl = me.innerCt.down('tr'); + + + me.columnNodes = me.rowEl.dom.childNodes; + }, + + + calculate: function(ownerContext) { + var me = this, + targetContext, widthShrinkWrap, heightShrinkWrap, shrinkWrap, table, targetPadding; + + + + if (!ownerContext.getDomProp('containerChildrenSizeDone')) { + me.done = false; + } else { + targetContext = ownerContext.innerCtContext; + widthShrinkWrap = ownerContext.widthModel.shrinkWrap; + heightShrinkWrap = ownerContext.heightModel.shrinkWrap; + shrinkWrap = heightShrinkWrap || widthShrinkWrap; + table = targetContext.el.dom; + targetPadding = shrinkWrap && targetContext.getPaddingInfo(); + + if (widthShrinkWrap) { + ownerContext.setContentWidth(table.offsetWidth + targetPadding.width, true); + } + + if (heightShrinkWrap) { + ownerContext.setContentHeight(table.offsetHeight + targetPadding.height, true); + } + } + }, + + doRenderColumn: function (out, renderData, columnIndex) { + + + + var me = renderData.$layout, + owner = me.owner, + columnCount = renderData.columnCount, + items = owner.items.items, + itemCount = items.length, + item, itemIndex, rowCount, increment, tree; + + + + + + if (owner.vertical) { + + + + + + + + + + + + rowCount = Math.ceil(itemCount / columnCount); + itemIndex = columnIndex * rowCount; + itemCount = Math.min(itemCount, itemIndex + rowCount); + increment = 1; + } else { + + + + + + + + + + + + itemIndex = columnIndex; + increment = columnCount; + } + + for ( ; itemIndex < itemCount; itemIndex += increment) { + item = items[itemIndex]; + me.configureItem(item); + tree = item.getRenderTree(); + Ext.DomHelper.generateMarkup(tree, out); + } + }, + + + getColumnCount: function() { + var me = this, + owner = me.owner, + ownerColumns = owner.columns; + + + + if (me.columnsArray) { + return ownerColumns.length; + } + + if (Ext.isNumber(ownerColumns)) { + return ownerColumns; + } + return owner.items.length; + }, + + getItemSizePolicy: function (item) { + return this.autoSizePolicy; + }, + + getRenderData: function () { + var me = this, + data = me.callParent(), + owner = me.owner, + i, columns = me.getColumnCount(), + width, column, cwidth, + autoFlex = me.autoFlex, + totalFlex = 0, flexedCols = 0; + + + if (me.columnsArray) { + for (i=0; i < columns; i++) { + width = me.owner.columns[i]; + if (width < 1) { + totalFlex += width; + flexedCols++; + } + } + } + + data.colCls = owner.groupCls; + data.columnCount = columns; + + data.columns = []; + for (i = 0; i < columns; i++) { + column = (data.columns[i] = {}); + + if (me.columnsArray) { + width = me.owner.columns[i]; + if (width < 1) { + cwidth = ((width / totalFlex) * 100) + '%'; + } else { + cwidth = width + 'px'; + } + column.style = 'width:' + cwidth; + } else { + column.style = 'width:' + (1 / columns * 100) + '%'; + flexedCols++; + } + } + + + data.tableStyle = + !flexedCols ? 'table-layout:fixed;' : + (flexedCols < columns) ? 'table-layout:fixed;width:100%' : + (autoFlex) ? 'table-layout:auto;width:100%' : 'table-layout:auto;'; + + return data; + }, + + initLayout: function () { + var me = this, + owner = me.owner; + + me.columnsArray = Ext.isArray(owner.columns); + me.autoColumns = !owner.columns || owner.columns === 'auto'; + me.vertical = owner.vertical; + + me.callParent(); + }, + + + isValidParent: function() { + return true; + }, + + setupRenderTpl: function (renderTpl) { + this.callParent(arguments); + + renderTpl.renderColumn = this.doRenderColumn; + }, + + renderChildren: function () { + var me = this, + generation = me.owner.items.generation; + + if (me.lastOwnerItemsGeneration !== generation) { + me.lastOwnerItemsGeneration = generation; + me.renderItems(me.getLayoutItems()); + } + }, + + + renderItems : function(items) { + var me = this, + itemCount = items.length, + i, + item, + rowCount, + columnCount, + rowIndex, + columnIndex; + + if (itemCount) { + Ext.suspendLayouts(); + + if (me.autoColumns) { + me.addMissingColumns(itemCount); + } + + columnCount = me.columnNodes.length; + rowCount = Math.ceil(itemCount / columnCount); + + for (i = 0; i < itemCount; i++) { + item = items[i]; + rowIndex = me.getRenderRowIndex(i, rowCount, columnCount); + columnIndex = me.getRenderColumnIndex(i, rowCount, columnCount); + + if (!item.rendered) { + me.renderItem(item, rowIndex, columnIndex); + } else if (!me.isItemAtPosition(item, rowIndex, columnIndex)) { + me.moveItem(item, rowIndex, columnIndex); + } + } + + if (me.autoColumns) { + me.removeExceedingColumns(itemCount); + } + + Ext.resumeLayouts(true); + } + }, + + isItemAtPosition : function(item, rowIndex, columnIndex) { + return item.el.dom === this.getNodeAt(rowIndex, columnIndex); + }, + + getRenderColumnIndex : function(itemIndex, rowCount, columnCount) { + if (this.vertical) { + return Math.floor(itemIndex / rowCount); + } else { + return itemIndex % columnCount; + } + }, + + getRenderRowIndex : function(itemIndex, rowCount, columnCount) { + var me = this; + if (me.vertical) { + return itemIndex % rowCount; + } else { + return Math.floor(itemIndex / columnCount); + } + }, + + getNodeAt : function(rowIndex, columnIndex) { + return this.columnNodes[columnIndex].childNodes[rowIndex]; + }, + + addMissingColumns : function(itemsCount) { + var me = this, + existingColumnsCount = me.columnNodes.length, + missingColumnsCount, + row, + cls, + i; + if (existingColumnsCount < itemsCount) { + missingColumnsCount = itemsCount - existingColumnsCount; + row = me.rowEl; + cls = me.owner.groupCls; + for (i = 0; i < missingColumnsCount; i++) { + row.createChild({ + cls: cls, + tag: 'td', + vAlign: 'top', + role: 'presentation' + }); + } + } + }, + + removeExceedingColumns : function(itemsCount) { + var me = this, + existingColumnsCount = me.columnNodes.length, + exceedingColumnsCount, + row, + i; + if (existingColumnsCount > itemsCount) { + exceedingColumnsCount = existingColumnsCount - itemsCount; + row = me.rowEl; + for (i = 0; i < exceedingColumnsCount; i++) { + row.last().remove(); + } + } + }, + + + renderItem : function(item, rowIndex, columnIndex) { + var me = this; + + me.configureItem(item); + item.render(Ext.get(me.columnNodes[columnIndex]), rowIndex); + me.afterRenderItem(item); + }, + + + moveItem : function(item, rowIndex, columnIndex) { + var me = this, + column = me.columnNodes[columnIndex], + targetNode = column.childNodes[rowIndex]; + column.insertBefore(item.el.dom, targetNode || null); + } + +}); + + + +Ext.define('Ext.form.CheckboxGroup', { + extend: Ext.form.FieldContainer , + mixins: { + field: Ext.form.field.Field + }, + alias: 'widget.checkboxgroup', + + + + + + + + + + + + columns : 'auto', + + + vertical : false, + + + allowBlank : true, + + + + blankText : "You must select at least one item in this group", + + + + defaultType : 'checkboxfield', + + + groupCls : Ext.baseCSSPrefix + 'form-check-group', + + + extraFieldBodyCls: Ext.baseCSSPrefix + 'form-checkboxgroup-body', + + + layout: 'checkboxgroup', + + componentCls: Ext.baseCSSPrefix + 'form-checkboxgroup', + + ariaRole: 'group', + + initComponent: function() { + var me = this; + me.callParent(); + me.initField(); + }, + + + initValue: function() { + var me = this, + valueCfg = me.value; + me.originalValue = me.lastValue = valueCfg || me.getValue(); + if (valueCfg) { + me.setValue(valueCfg); + } + }, + + + onAdd: function(item) { + var me = this, + items, + len, i; + + if (item.isCheckbox) { + me.mon(item, 'change', me.checkChange, me); + } else if (item.isContainer) { + items = item.items.items; + for (i = 0, len = items.length; i < len; i++) { + me.onAdd(items[i]); + } + } + me.callParent(arguments); + }, + + onRemove: function(item) { + var me = this, + items, + len, i; + + if (item.isCheckbox) { + me.mun(item, 'change', me.checkChange, me); + } else if (item.isContainer) { + items = item.items.items; + for (i = 0, len = items.length; i < len; i++) { + me.onRemove(items[i]); + } + } + me.callParent(arguments); + }, + + + isEqual: function(value1, value2) { + var toQueryString = Ext.Object.toQueryString; + return toQueryString(value1) === toQueryString(value2); + }, + + + getErrors: function() { + var errors = []; + if (!this.allowBlank && Ext.isEmpty(this.getChecked())) { + errors.push(this.blankText); + } + return errors; + }, + + + getBoxes: function(query) { + return this.query('[isCheckbox]' + (query||'')); + }, + + + eachBox: function(fn, scope) { + Ext.Array.forEach(this.getBoxes(), fn, scope || this); + }, + + + getChecked: function() { + return this.getBoxes('[checked]'); + }, + + + isDirty: function(){ + var boxes = this.getBoxes(), + b , + bLen = boxes.length; + + for (b = 0; b < bLen; b++) { + if (boxes[b].isDirty()) { + return true; + } + } + }, + + + setReadOnly: function(readOnly) { + var boxes = this.getBoxes(), + b, + bLen = boxes.length; + + for (b = 0; b < bLen; b++) { + boxes[b].setReadOnly(readOnly); + } + + this.readOnly = readOnly; + }, + + + reset: function() { + var me = this, + hadError = me.hasActiveError(), + preventMark = me.preventMark; + me.preventMark = true; + me.batchChanges(function() { + var boxes = me.getBoxes(), + b, + bLen = boxes.length; + + for (b = 0; b < bLen; b++) { + boxes[b].reset(); + } + }); + me.preventMark = preventMark; + me.unsetActiveError(); + if (hadError) { + me.updateLayout(); + } + }, + + resetOriginalValue: function(){ + var me = this, + boxes = me.getBoxes(), + b, + bLen = boxes.length; + + for (b = 0; b < bLen; b++) { + boxes[b].resetOriginalValue(); + } + + me.originalValue = me.getValue(); + me.checkDirty(); + }, + + + + setValue: function(value) { + var me = this, + boxes = me.getBoxes(), + b, + bLen = boxes.length, + box, name, + cbValue; + + me.batchChanges(function() { + for (b = 0; b < bLen; b++) { + box = boxes[b]; + name = box.getName(); + cbValue = false; + + if (value && value.hasOwnProperty(name)) { + if (Ext.isArray(value[name])) { + cbValue = Ext.Array.contains(value[name], box.inputValue); + } else { + + cbValue = value[name]; + } + } + + box.setValue(cbValue); + } + }); + return me; + }, + + + + getValue: function() { + var values = {}, + boxes = this.getBoxes(), + b, + bLen = boxes.length, + box, name, inputValue, bucket; + + for (b = 0; b < bLen; b++) { + box = boxes[b]; + name = box.getName(); + inputValue = box.inputValue; + + if (box.getValue()) { + if (values.hasOwnProperty(name)) { + bucket = values[name]; + if (!Ext.isArray(bucket)) { + bucket = values[name] = [bucket]; + } + bucket.push(inputValue); + } else { + values[name] = inputValue; + } + } + } + + return values; + }, + + + getSubmitData: function() { + return null; + }, + + + getModelData: function() { + return null; + }, + + validate: function() { + var me = this, + errors, + isValid, + wasValid; + + if (me.disabled) { + isValid = true; + } else { + errors = me.getErrors(); + isValid = Ext.isEmpty(errors); + wasValid = me.wasValid; + if (isValid) { + me.unsetActiveError(); + } else { + me.setActiveError(errors); + } + } + if (isValid !== wasValid) { + me.wasValid = isValid; + me.fireEvent('validitychange', me, isValid); + me.updateLayout(); + } + + return isValid; + } + +}, function() { + + this.borrow(Ext.form.field.Base, ['markInvalid', 'clearInvalid', 'setError']); + +}); + + + + +Ext.define('Ext.form.FieldSet', { + extend: Ext.container.Container , + mixins: { + fieldAncestor: Ext.form.FieldAncestor + }, + alias: 'widget.fieldset', + + + + + + + + + + + + collapsed: false, + + + toggleOnTitleClick : true, + + + + + baseCls: Ext.baseCSSPrefix + 'fieldset', + + + layout: 'anchor', + + componentLayout: 'fieldset', + + ariaRole: null, + + autoEl: 'fieldset', + + childEls: [ + 'body' + ], + + renderTpl: [ + '{%this.renderLegend(out,values);%}', + '' + ], + + stateEvents : [ 'collapse', 'expand' ], + + maskOnDisable: false, + + beforeDestroy: function(){ + var me = this, + legend = me.legend; + + if (legend) { + + delete legend.ownerCt; + legend.destroy(); + me.legend = null; + } + me.callParent(); + }, + + initComponent: function() { + var me = this, + baseCls = me.baseCls; + + me.initFieldAncestor(); + + me.callParent(); + + + + + + + + + + + + me.layout.managePadding = me.layout.manageOverflow = false; + + me.addEvents( + + + "beforeexpand", + + + "beforecollapse", + + + "expand", + + + "collapse" + ); + + if (me.collapsed) { + me.addCls(baseCls + '-collapsed'); + me.collapse(); + } + if (me.title || me.checkboxToggle || me.collapsible) { + me.addTitleClasses(); + me.legend = Ext.widget(me.createLegendCt()); + } + me.initMonitor(); + }, + + initPadding: function(targetEl) { + var me = this, + body = me.getProtoBody(), + padding = me.padding, + bodyPadding; + + if (padding !== undefined) { + if (Ext.isIEQuirks || Ext.isIE8m) { + + + padding = me.parseBox(padding); + bodyPadding = Ext.Element.parseBox(0); + bodyPadding.top = padding.top; + padding.top = 0; + body.setStyle('padding', me.unitizeBox(bodyPadding)); + } + + targetEl.setStyle('padding', me.unitizeBox(padding)); + } + }, + + getProtoBody: function () { + var me = this, + body = me.protoBody; + + if (!body) { + me.protoBody = body = new Ext.util.ProtoElement({ + styleProp: 'bodyStyle', + styleIsText: true + }); + } + + return body; + }, + + + initRenderData: function() { + var me = this, + data = me.callParent(); + + data.bodyTargetCls = me.bodyTargetCls; + me.protoBody.writeTo(data); + delete me.protoBody; + + return data; + }, + + getState: function () { + var state = this.callParent(); + + state = this.addPropertyToState(state, 'collapsed'); + + return state; + }, + + afterCollapse: Ext.emptyFn, + afterExpand: Ext.emptyFn, + + collapsedHorizontal: function () { + return true; + }, + + collapsedVertical: function () { + return true; + }, + + createLegendCt: function () { + var me = this, + items = [], + legend = { + xtype: 'container', + baseCls: me.baseCls + '-header', + id: me.id + '-legend', + autoEl: 'legend', + ariaRole: null, + ariaLabelledBy: '.' + me.baseCls + '-header-text', + items: items, + ownerCt: me, + shrinkWrap: true, + ownerLayout: me.componentLayout + }; + + + if (me.checkboxToggle) { + items.push(me.createCheckboxCmp()); + } else if (me.collapsible) { + + items.push(me.createToggleCmp()); + } + + + items.push(me.createTitleCmp()); + + return legend; + }, + + + createTitleCmp: function() { + var me = this, + cfg = { + xtype : 'component', + html : me.title, + cls : me.baseCls + '-header-text', + id : me.id + '-legendTitle' + }; + + if (me.collapsible && me.toggleOnTitleClick) { + cfg.listeners = { + click : { + element: 'el', + scope : me, + fn : me.toggle + } + }; + cfg.cls += ' ' + me.baseCls + '-header-text-collapsible'; + } + + return (me.titleCmp = Ext.widget(cfg)); + }, + + + + + createCheckboxCmp: function() { + var me = this, + suffix = '-checkbox'; + + me.checkboxCmp = Ext.widget({ + xtype: 'checkbox', + hideEmptyLabel: true, + name: me.checkboxName || me.id + suffix, + cls: me.baseCls + '-header' + suffix, + id: me.id + '-legendChk', + checked: !me.collapsed, + msgTarget: 'none', + listeners: { + change: me.onCheckChange, + scope: me + } + }); + return me.checkboxCmp; + }, + + + + + createToggleCmp: function() { + var me = this; + me.toggleCmp = Ext.widget({ + xtype: 'tool', + height: 15, + width: 15, + type: 'toggle', + handler: me.toggle, + id: me.id + '-legendToggle', + scope: me + }); + return me.toggleCmp; + }, + + doRenderLegend: function (out, renderData) { + + + + var me = renderData.$comp, + legend = me.legend, + tree; + + + if (legend) { + legend.ownerLayout.configureItem(legend); + tree = legend.getRenderTree(); + Ext.DomHelper.generateMarkup(tree, out); + } + }, + + finishRender: function () { + var legend = this.legend; + + this.callParent(); + + if (legend) { + legend.finishRender(); + } + }, + + getCollapsed: function () { + return this.collapsed ? 'top' : false; + }, + + getCollapsedDockedItems: function () { + var legend = this.legend; + + return legend ? [ legend ] : []; + }, + + + setTitle: function(title) { + var me = this, + legend = me.legend, + baseCls = me.baseCls; + + me.title = title; + if (me.rendered) { + if (!legend) { + me.legend = legend = Ext.widget(me.createLegendCt()); + me.addTitleClasses(); + legend.ownerLayout.configureItem(legend); + legend.render(me.el, 0); + } + me.titleCmp.update(title); + } else if (legend) { + me.titleCmp.update(title); + } else { + me.addTitleClasses(); + me.legend = Ext.widget(me.createLegendCt()); + } + return me; + }, + + addTitleClasses: function(){ + var me = this, + title = me.title, + baseCls = me.baseCls; + + if (title) { + me.addCls(baseCls + '-with-title'); + } + + if (title || me.checkboxToggle || me.collapsible) { + me.addCls(baseCls + '-with-legend'); + } + }, + + applyTargetCls: function(targetCls) { + this.bodyTargetCls = targetCls; + }, + + getTargetEl : function() { + return this.body || this.frameBody || this.el; + }, + + getDefaultContentTarget: function() { + return this.body; + }, + + + expand : function(){ + return this.setExpanded(true); + }, + + + collapse : function() { + return this.setExpanded(false); + }, + + + setExpanded: function(expanded) { + var me = this, + checkboxCmp = me.checkboxCmp, + operation = expanded ? 'expand' : 'collapse'; + + if (!me.rendered || me.fireEvent('before' + operation, me) !== false) { + expanded = !!expanded; + + if (checkboxCmp) { + checkboxCmp.setValue(expanded); + } + + if (expanded) { + me.removeCls(me.baseCls + '-collapsed'); + } else { + me.addCls(me.baseCls + '-collapsed'); + } + me.collapsed = !expanded; + if (expanded) { + delete me.getHierarchyState().collapsed; + } else { + me.getHierarchyState().collapsed = true; + } + if (me.rendered) { + + + + me.updateLayout({ isRoot: false }); + me.fireEvent(operation, me); + } + } + return me; + }, + + getRefItems: function(deep) { + var refItems = this.callParent(arguments), + legend = this.legend; + + + if (legend) { + refItems.unshift(legend); + if (deep) { + refItems.unshift.apply(refItems, legend.getRefItems(true)); + } + } + return refItems; + }, + + + toggle: function() { + this.setExpanded(!!this.collapsed); + }, + + + onCheckChange: function(cmp, checked) { + this.setExpanded(checked); + }, + + setupRenderTpl: function (renderTpl) { + this.callParent(arguments); + + renderTpl.renderLegend = this.doRenderLegend; + } +}); + + + +Ext.define('Ext.layout.component.BoundList', { + extend: Ext.layout.component.Auto , + alias: 'layout.boundlist', + + type: 'component', + + beginLayout: function(ownerContext) { + var me = this, + owner = me.owner, + toolbar = owner.pagingToolbar; + + me.callParent(arguments); + + if (owner.floating) { + ownerContext.savedXY = owner.getXY(); + + + owner.setXY([0, -9999]); + } + + if (toolbar) { + ownerContext.toolbarContext = ownerContext.context.getCmp(toolbar); + } + ownerContext.listContext = ownerContext.getEl('listEl'); + }, + + beginLayoutCycle: function(ownerContext){ + var owner = this.owner; + + this.callParent(arguments); + if (ownerContext.heightModel.auto) { + + + + owner.el.setHeight('auto'); + owner.listEl.setHeight('auto'); + } + }, + + getLayoutItems: function() { + var toolbar = this.owner.pagingToolbar; + return toolbar ? [toolbar] : []; + }, + + isValidParent: function() { + + + return true; + }, + + finishedLayout: function(ownerContext) { + var xy = ownerContext.savedXY; + + this.callParent(arguments); + if (xy) { + this.owner.setXY(xy); + } + }, + + measureContentWidth: function(ownerContext) { + return this.owner.listEl.getWidth(); + }, + + measureContentHeight: function(ownerContext) { + return this.owner.listEl.getHeight(); + }, + + publishInnerHeight: function(ownerContext, height) { + var toolbar = ownerContext.toolbarContext, + toolbarHeight = 0; + + if (toolbar) { + toolbarHeight = toolbar.getProp('height'); + } + + if (toolbarHeight === undefined) { + this.done = false; + } else { + ownerContext.listContext.setHeight(height - ownerContext.getFrameInfo().height - toolbarHeight); + } + }, + + calculateOwnerHeightFromContentHeight: function(ownerContext){ + var height = this.callParent(arguments), + toolbar = ownerContext.toolbarContext; + + if (toolbar) { + height += toolbar.getProp('height'); + } + return height; + } +}); + + + +Ext.define('Ext.form.field.Spinner', { + extend: Ext.form.field.Trigger , + alias: 'widget.spinnerfield', + alternateClassName: 'Ext.form.Spinner', + + + trigger1Cls: Ext.baseCSSPrefix + 'form-spinner-up', + trigger2Cls: Ext.baseCSSPrefix + 'form-spinner-down', + + + spinUpEnabled: true, + + + spinDownEnabled: true, + + + keyNavEnabled: true, + + + mouseWheelEnabled: true, + + + repeatTriggerClick: true, + + + onSpinUp: Ext.emptyFn, + + + onSpinDown: Ext.emptyFn, + + ariaRole: 'spinbutton', + + triggerTpl: '' + + '' + + '' + + '' + + '', + + initComponent: function() { + this.callParent(); + + this.addEvents( + + 'spin', + + + 'spinup', + + + 'spindown' + ); + }, + + + onRender: function() { + var me = this, + triggers; + + me.callParent(arguments); + triggers = me.triggerEl; + + + me.spinUpEl = triggers.item(0); + + me.spinDownEl = triggers.item(1); + + me.triggerCell = me.spinUpEl.parent(); + + + if (me.keyNavEnabled) { + me.spinnerKeyNav = new Ext.util.KeyNav(me.inputEl, { + scope: me, + up: me.spinUp, + down: me.spinDown + }); + } + + + if (me.mouseWheelEnabled) { + me.mon(me.bodyEl, 'mousewheel', me.onMouseWheel, me); + } + }, + + getSubTplMarkup: function(values) { + var me = this, + childElCls = values.childElCls, + field = Ext.form.field.Base.prototype.getSubTplMarkup.apply(me, arguments); + + return '' + + '' + + '' + + me.getTriggerMarkup() + + ''; + }, + + getTriggerMarkup: function() { + return this.getTpl('triggerTpl').apply(this.getTriggerData()); + }, + + getTriggerData: function(){ + var me = this, + hideTrigger = (me.readOnly || me.hideTrigger); + + return { + triggerCls: Ext.baseCSSPrefix + 'trigger-cell', + triggerStyle: hideTrigger ? 'display:none' : '', + spinnerUpCls: !me.spinUpEnabled ? me.trigger1Cls + '-disabled': '', + spinnerDownCls: !me.spinDownEnabled ? me.trigger2Cls + '-disabled': '' + }; + }, + + + getTriggerWidth: function() { + var me = this, + totalTriggerWidth = 0; + + if (me.triggerWrap && !me.hideTrigger && !me.readOnly) { + totalTriggerWidth = me.triggerWidth; + } + return totalTriggerWidth; + }, + + + onTrigger1Click: function() { + this.spinUp(); + }, + + + onTrigger2Click: function() { + this.spinDown(); + }, + + + + onTriggerWrapMouseup: function() { + this.inputEl.focus(); + }, + + + spinUp: function() { + var me = this; + if (me.spinUpEnabled && !me.disabled) { + me.fireEvent('spin', me, 'up'); + me.fireEvent('spinup', me); + me.onSpinUp(); + } + }, + + + spinDown: function() { + var me = this; + if (me.spinDownEnabled && !me.disabled) { + me.fireEvent('spin', me, 'down'); + me.fireEvent('spindown', me); + me.onSpinDown(); + } + }, + + + setSpinUpEnabled: function(enabled) { + var me = this, + wasEnabled = me.spinUpEnabled; + me.spinUpEnabled = enabled; + if (wasEnabled !== enabled && me.rendered) { + me.spinUpEl[enabled ? 'removeCls' : 'addCls'](me.trigger1Cls + '-disabled'); + } + }, + + + setSpinDownEnabled: function(enabled) { + var me = this, + wasEnabled = me.spinDownEnabled; + me.spinDownEnabled = enabled; + if (wasEnabled !== enabled && me.rendered) { + me.spinDownEl[enabled ? 'removeCls' : 'addCls'](me.trigger2Cls + '-disabled'); + } + }, + + + onMouseWheel: function(e) { + var me = this, + delta; + if (me.hasFocus) { + delta = e.getWheelDelta(); + if (delta > 0) { + me.spinUp(); + } else if (delta < 0) { + me.spinDown(); + } + e.stopEvent(); + } + }, + + onDestroy: function() { + Ext.destroyMembers(this, 'spinnerKeyNav', 'spinUpEl', 'spinDownEl'); + this.callParent(); + } + +}); + + + +Ext.define('Ext.form.field.Number', { + extend: Ext.form.field.Spinner , + alias: 'widget.numberfield', + 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 me = this; + if (me.decimalSeparator === null) { + me.decimalSeparator = Ext.util.Format.decimalSeparator; + } + me.callParent(); + + me.setMinValue(me.minValue); + me.setMaxValue(me.maxValue); + }, + + + getErrors: function(value) { + var me = this, + errors = me.callParent(arguments), + format = Ext.String.format, + num; + + value = Ext.isDefined(value) ? value : this.processRawValue(this.getRawValue()); + + if (value.length < 1) { + return errors; + } + + value = String(value).replace(me.decimalSeparator, '.'); + + if(isNaN(value)){ + errors.push(format(me.nanText, value)); + } + + num = me.parseValue(value); + + if (me.minValue === 0 && num < 0) { + errors.push(this.negativeText); + } + else if (num < me.minValue) { + errors.push(format(me.minText, me.minValue)); + } + + if (num > me.maxValue) { + errors.push(format(me.maxText, me.maxValue)); + } + + + return errors; + }, + + rawToValue: function(rawValue) { + var value = this.fixPrecision(this.parseValue(rawValue)); + if (value === null) { + value = rawValue || null; + } + return value; + }, + + valueToRaw: function(value) { + var me = this, + decimalSeparator = me.decimalSeparator; + value = me.parseValue(value); + value = me.fixPrecision(value); + value = Ext.isNumber(value) ? value : parseFloat(String(value).replace(decimalSeparator, '.')); + value = isNaN(value) ? '' : String(value).replace('.', decimalSeparator); + return value; + }, + + getSubmitValue: function() { + var me = this, + value = me.callParent(); + + if (!me.submitLocaleSeparator) { + value = value.replace(me.decimalSeparator, '.'); + } + return value; + }, + + onChange: function() { + this.toggleSpinners(); + this.callParent(arguments); + }, + + toggleSpinners: function(){ + var me = this, + value = me.getValue(), + valueIsNull = value === null, + enabled; + + + + if (me.spinUpEnabled || me.spinUpDisabledByToggle) { + enabled = valueIsNull || value < me.maxValue; + me.setSpinUpEnabled(enabled, true); + } + + + if (me.spinDownEnabled || me.spinDownDisabledByToggle) { + enabled = valueIsNull || value > me.minValue; + me.setSpinDownEnabled(enabled, true); + } + }, + + + setMinValue : function(value) { + var me = this, + allowed; + + me.minValue = Ext.Number.from(value, Number.NEGATIVE_INFINITY); + me.toggleSpinners(); + + + if (me.disableKeyFilter !== true) { + allowed = me.baseChars + ''; + + if (me.allowExponential) { + allowed += me.decimalSeparator + 'e+-'; + } + else { + if (me.allowDecimals) { + allowed += me.decimalSeparator; + } + if (me.minValue < 0) { + allowed += '-'; + } + } + + allowed = Ext.String.escapeRegex(allowed); + me.maskRe = new RegExp('[' + allowed + ']'); + if (me.autoStripChars) { + me.stripCharsRe = new RegExp('[^' + allowed + ']', 'gi'); + } + } + }, + + + setMaxValue: function(value) { + this.maxValue = Ext.Number.from(value, Number.MAX_VALUE); + this.toggleSpinners(); + }, + + + parseValue : function(value) { + value = parseFloat(String(value).replace(this.decimalSeparator, '.')); + return isNaN(value) ? null : value; + }, + + + fixPrecision : function(value) { + var me = this, + nan = isNaN(value), + precision = me.decimalPrecision; + + if (nan || !value) { + return nan ? '' : value; + } else if (!me.allowDecimals || precision <= 0) { + precision = 0; + } + + return parseFloat(Ext.Number.toFixed(parseFloat(value), precision)); + }, + + beforeBlur : function() { + var me = this, + v = me.rawToValue(me.getRawValue()); + + if (!Ext.isEmpty(v)) { + me.setValue(v); + } + }, + + setSpinUpEnabled: function(enabled, internal){ + this.callParent(arguments); + if (!internal) { + delete this.spinUpDisabledByToggle; + } else { + this.spinUpDisabledByToggle = !enabled; + } + }, + + onSpinUp: function() { + var me = this; + + if (!me.readOnly) { + me.setSpinValue(Ext.Number.constrain(me.getValue() + me.step, me.minValue, me.maxValue)); + } + }, + + setSpinDownEnabled: function(enabled, internal){ + this.callParent(arguments); + if (!internal) { + delete this.spinDownDisabledByToggle; + } else { + this.spinDownDisabledByToggle = !enabled; + } + }, + + onSpinDown: function() { + var me = this; + + if (!me.readOnly) { + me.setSpinValue(Ext.Number.constrain(me.getValue() - me.step, me.minValue, me.maxValue)); + } + }, + + setSpinValue: function(value) { + var me = this, + len; + + if (me.enforceMaxLength) { + + + if (me.fixPrecision(value).toString().length > me.maxLength) { + return; + } + } + me.setValue(value); + } +}); + + + +Ext.define('Ext.toolbar.Paging', { + extend: Ext.toolbar.Toolbar , + alias: 'widget.pagingtoolbar', + alternateClassName: 'Ext.PagingToolbar', + + mixins: { + bindable: Ext.util.Bindable + }, + + + + 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, + + + getPagingItems: function() { + var me = this, + inputListeners = { + scope: me, + blur: me.onPagingBlur + }; + + inputListeners[Ext.EventManager.getKeyEvent()] = me.onPagingKeyDown; + + return [{ + itemId: 'first', + tooltip: me.firstText, + overflowText: me.firstText, + iconCls: Ext.baseCSSPrefix + 'tbar-page-first', + disabled: true, + handler: me.moveFirst, + scope: me + },{ + itemId: 'prev', + tooltip: me.prevText, + overflowText: me.prevText, + iconCls: Ext.baseCSSPrefix + 'tbar-page-prev', + disabled: true, + handler: me.movePrevious, + scope: me + }, + '-', + me.beforePageText, + { + xtype: 'numberfield', + itemId: 'inputItem', + name: 'inputItem', + cls: Ext.baseCSSPrefix + 'tbar-page-number', + allowDecimals: false, + minValue: 1, + hideTrigger: true, + enableKeyEvents: true, + keyNavEnabled: false, + selectOnFocus: true, + submitValue: false, + + isFormField: false, + width: me.inputItemWidth, + margins: '-1 2 3 2', + listeners: inputListeners + },{ + xtype: 'tbtext', + itemId: 'afterTextItem', + text: Ext.String.format(me.afterPageText, 1) + }, + '-', + { + itemId: 'next', + tooltip: me.nextText, + overflowText: me.nextText, + iconCls: Ext.baseCSSPrefix + 'tbar-page-next', + disabled: true, + handler: me.moveNext, + scope: me + },{ + itemId: 'last', + tooltip: me.lastText, + overflowText: me.lastText, + iconCls: Ext.baseCSSPrefix + 'tbar-page-last', + disabled: true, + handler: me.moveLast, + scope: me + }, + '-', + { + itemId: 'refresh', + tooltip: me.refreshText, + overflowText: me.refreshText, + iconCls: Ext.baseCSSPrefix + 'tbar-loading', + disabled: me.store.isLoading(), + handler: me.doRefresh, + scope: me + }]; + }, + + initComponent : function(){ + var me = this, + userItems = me.items || me.buttons || [], + pagingItems; + + me.bindStore(me.store || 'ext-empty-store', true); + pagingItems = me.getPagingItems(); + if (me.prependButtons) { + me.items = userItems.concat(pagingItems); + } else { + me.items = pagingItems.concat(userItems); + } + delete me.buttons; + + if (me.displayInfo) { + me.items.push('->'); + me.items.push({xtype: 'tbtext', itemId: 'displayItem'}); + } + + me.callParent(); + + me.addEvents( + + 'change', + + + 'beforechange' + ); + }, + + beforeRender: function() { + var me = this; + + me.callParent(arguments); + if (!me.store.isLoading()) { + me.calledFromRender = true; + me.onLoad(); + delete me.calledFromRender; + } + }, + + + updateInfo : function(){ + var me = this, + displayItem = me.child('#displayItem'), + store = me.store, + pageData = me.getPageData(), + count, msg; + + if (displayItem) { + count = store.getCount(); + if (count === 0) { + msg = me.emptyMsg; + } else { + msg = Ext.String.format( + me.displayMsg, + pageData.fromRecord, + pageData.toRecord, + pageData.total + ); + } + displayItem.setText(msg); + } + }, + + + onLoad : function(){ + var me = this, + pageData, + currPage, + pageCount, + afterText, + count, + isEmpty, + item; + + count = me.store.getCount(); + isEmpty = count === 0; + if (!isEmpty) { + pageData = me.getPageData(); + currPage = pageData.currentPage; + pageCount = pageData.pageCount; + + + if (currPage > pageCount) { + me.store.loadPage(pageCount); + return; + } + + afterText = Ext.String.format(me.afterPageText, isNaN(pageCount) ? 1 : pageCount); + } else { + currPage = 0; + pageCount = 0; + afterText = Ext.String.format(me.afterPageText, 0); + } + + Ext.suspendLayouts(); + item = me.child('#afterTextItem'); + if (item) { + item.setText(afterText); + } + item = me.getInputItem(); + if (item) { + item.setDisabled(isEmpty).setValue(currPage); + } + me.setChildDisabled('#first', currPage === 1 || isEmpty); + me.setChildDisabled('#prev', currPage === 1 || isEmpty); + me.setChildDisabled('#next', currPage === pageCount || isEmpty); + me.setChildDisabled('#last', currPage === pageCount || isEmpty); + me.setChildDisabled('#refresh', false); + me.updateInfo(); + Ext.resumeLayouts(true); + + if (!me.calledFromRender) { + me.fireEvent('change', me, pageData); + } + }, + + setChildDisabled: function(selector, disabled){ + var item = this.child(selector); + if (item) { + item.setDisabled(disabled); + } + }, + + + getPageData : function(){ + var store = this.store, + totalCount = store.getTotalCount(); + + return { + total : totalCount, + currentPage : store.currentPage, + pageCount: Math.ceil(totalCount / store.pageSize), + fromRecord: ((store.currentPage - 1) * store.pageSize) + 1, + toRecord: Math.min(store.currentPage * store.pageSize, totalCount) + + }; + }, + + + onLoadError : function(){ + this.setChildDisabled('#refresh', false); + }, + + getInputItem: function(){ + return this.child('#inputItem'); + }, + + + readPageFromInput : function(pageData){ + var inputItem = this.getInputItem(), + pageNum = false, + v; + + if (inputItem) { + v = inputItem.getValue(); + pageNum = parseInt(v, 10); + if (!v || isNaN(pageNum)) { + inputItem.setValue(pageData.currentPage); + return false; + } + } + return pageNum; + }, + + + onPagingBlur : function(e){ + var inputItem = this.getInputItem(), + curPage; + + if (inputItem) { + curPage = this.getPageData().currentPage; + inputItem.setValue(curPage); + } + }, + + + onPagingKeyDown : function(field, e){ + this.processKeyEvent(field, e); + }, + + processKeyEvent: function(field, e) { + var me = this, + k = e.getKey(), + pageData = me.getPageData(), + increment = e.shiftKey ? 10 : 1, + pageNum; + + if (k == e.RETURN) { + e.stopEvent(); + pageNum = me.readPageFromInput(pageData); + if (pageNum !== false) { + pageNum = Math.min(Math.max(1, pageNum), pageData.pageCount); + if (pageNum !== pageData.currentPage && me.fireEvent('beforechange', me, pageNum) !== false) { + me.store.loadPage(pageNum); + } + } + } else if (k == e.HOME || k == e.END) { + e.stopEvent(); + pageNum = k == e.HOME ? 1 : pageData.pageCount; + field.setValue(pageNum); + } else if (k == e.UP || k == e.PAGE_UP || k == e.DOWN || k == e.PAGE_DOWN) { + e.stopEvent(); + pageNum = me.readPageFromInput(pageData); + if (pageNum) { + if (k == e.DOWN || k == e.PAGE_DOWN) { + increment *= -1; + } + pageNum += increment; + if (pageNum >= 1 && pageNum <= pageData.pageCount) { + field.setValue(pageNum); + } + } + } + }, + + + 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 me = this, + store = me.store, + prev = store.currentPage - 1; + + if (prev > 0) { + if (me.fireEvent('beforechange', me, prev) !== false) { + store.previousPage(); + return true; + } + } + return false; + }, + + + moveNext : function(){ + var me = this, + store = me.store, + total = me.getPageData().pageCount, + next = store.currentPage + 1; + + if (next <= total) { + if (me.fireEvent('beforechange', me, next) !== false) { + store.nextPage(); + return true; + } + } + return false; + }, + + + moveLast : function(){ + var me = this, + last = me.getPageData().pageCount; + + if (me.fireEvent('beforechange', me, last) !== false) { + me.store.loadPage(last); + return true; + } + return false; + }, + + + doRefresh : function(){ + var me = this, + store = me.store, + current = store.currentPage; + + if (me.fireEvent('beforechange', me, current) !== false) { + store.loadPage(current); + return true; + } + return false; + }, + + getStoreListeners: function() { + return { + beforeload: this.beforeLoad, + load: this.onLoad, + exception: this.onLoadError + }; + }, + + + unbind : function(store){ + this.bindStore(null); + }, + + + bind : function(store){ + this.bindStore(store); + }, + + + onDestroy : function(){ + this.unbind(); + this.callParent(); + } +}); + + + +Ext.define('Ext.view.BoundList', { + extend: Ext.view.View , + alias: 'widget.boundlist', + alternateClassName: 'Ext.BoundList', + + + mixins: { + queryable: Ext.Queryable + }, + + + pageSize: 0, + + + + + + + baseCls: Ext.baseCSSPrefix + 'boundlist', + itemCls: Ext.baseCSSPrefix + 'boundlist-item', + listItemCls: '', + shadow: false, + trackOver: true, + refreshed: 0, + + preserveScrollOnRefresh: true, + + + deferInitialRefresh: false, + + componentLayout: 'boundlist', + + childEls: [ + 'listEl' + ], + + renderTpl: [ + '', + '{%', + 'var me=values.$comp, pagingToolbar=me.pagingToolbar;', + 'if (pagingToolbar) {', + 'pagingToolbar.ownerLayout = me.componentLayout;', + 'Ext.DomHelper.generateMarkup(pagingToolbar.getRenderTree(), out);', + '}', + '%}', + { + disableFormats: true + } + ], + + + + initComponent: function() { + var me = this, + baseCls = me.baseCls, + itemCls = me.itemCls; + + me.selectedItemCls = baseCls + '-selected'; + if (me.trackOver) { + me.overItemCls = baseCls + '-item-over'; + } + me.itemSelector = "." + itemCls; + + if (me.floating) { + me.addCls(baseCls + '-floating'); + } + + if (!me.tpl) { + + + me.tpl = new Ext.XTemplate( + '
    ', + '
  • ' + me.getInnerTpl(me.displayField) + '
  • ', + '
' + ); + } else if (!me.tpl.isTemplate) { + me.tpl = new Ext.XTemplate(me.tpl); + } + + if (me.pageSize) { + me.pagingToolbar = me.createPagingToolbar(); + } + + me.callParent(); + }, + + getRefOwner: function() { + return this.pickerField || this.callParent(); + }, + + getRefItems: function() { + var me = this, + result = []; + + if (me.pagingToolbar) { + result.push(me.pagingToolbar); + } + if (me.loadMask) { + result.push(me.loadMask); + } + return result; + }, + + createPagingToolbar: function() { + return Ext.widget('pagingtoolbar', { + id: this.id + '-paging-toolbar', + pageSize: this.pageSize, + store: this.dataSource, + border: false, + ownerCt: this, + ownerLayout: this.getComponentLayout() + }); + }, + + + + finishRenderChildren: function () { + var toolbar = this.pagingToolbar; + + this.callParent(arguments); + + if (toolbar) { + toolbar.finishRender(); + } + }, + + refresh: function(){ + var me = this, + tpl = me.tpl, + toolbar = me.pagingToolbar, + rendered = me.rendered; + + + tpl.field = me.pickerField; + tpl.store = me.store; + me.callParent(); + tpl.field = tpl.store = null; + + + + if (rendered && toolbar && toolbar.rendered && !me.preserveScrollOnRefresh) { + me.el.appendChild(toolbar.el); + } + + + + if (rendered && Ext.isIE6 && Ext.isStrict) { + me.listEl.repaint(); + } + }, + + bindStore: function(store, initial) { + var toolbar = this.pagingToolbar; + + this.callParent(arguments); + if (toolbar) { + toolbar.bindStore(store, initial); + } + }, + + getTargetEl: function() { + return this.listEl || this.el; + }, + + + getNodeContainer: function() { + return Ext.get(this.listEl.dom.firstChild); + }, + + + getInnerTpl: function(displayField) { + return '{' + displayField + '}'; + }, + + onDestroy: function() { + Ext.destroyMembers(this, 'pagingToolbar', 'listEl'); + this.callParent(); + } +}); + + +Ext.define('Ext.ux.form.MultiSelect', { + + extend: Ext.form.FieldContainer , + + mixins: { + bindable: Ext.util.Bindable , + field: Ext.form.field.Field + }, + + alternateClassName: 'Ext.ux.Multiselect', + alias: ['widget.multiselectfield', 'widget.multiselect'], + + + + + + layout: 'anchor', + + + + + + + + + ddReorder: false, + + + + + appendOnly: false, + + + displayField: 'text', + + + + + allowBlank: true, + + + minSelections: 0, + + + maxSelections: Number.MAX_VALUE, + + + blankText: 'This field is required', + + + minSelectionsText: 'Minimum {0} item(s) required', + + + maxSelectionsText: 'Maximum {0} item(s) required', + + + delimiter: ',', + + + dragText: '{0} Item{1}', + + + + ignoreSelectChange: 0, + + + + initComponent: function(){ + var me = this; + + me.bindStore(me.store, true); + if (me.store.autoCreated) { + me.valueField = me.displayField = 'field1'; + if (!me.store.expanded) { + me.displayField = 'field2'; + } + } + + if (!Ext.isDefined(me.valueField)) { + me.valueField = me.displayField; + } + me.items = me.setupItems(); + + + me.callParent(); + me.initField(); + me.addEvents('drop'); + }, + + setupItems: function() { + var me = this; + + me.boundList = Ext.create('Ext.view.BoundList', Ext.apply({ + anchor: 'none 100%', + deferInitialRefresh: false, + border: 1, + multiSelect: true, + store: me.store, + displayField: me.displayField, + disabled: me.disabled + }, me.listConfig)); + me.boundList.getSelectionModel().on('selectionchange', me.onSelectChange, me); + + + if (!me.title) { + return me.boundList; + } + + + me.boundList.border = false; + return { + border: true, + anchor: 'none 100%', + layout: 'anchor', + title: me.title, + tbar: me.tbar, + items: me.boundList + }; + }, + + onSelectChange: function(selModel, selections){ + if (!this.ignoreSelectChange) { + this.setValue(selections); + } + }, + + getSelected: function(){ + return this.boundList.getSelectionModel().getSelection(); + }, + + + isEqual: function(v1, v2) { + var fromArray = Ext.Array.from, + i = 0, + len; + + v1 = fromArray(v1); + v2 = fromArray(v2); + len = v1.length; + + if (len !== v2.length) { + return false; + } + + for(; i < len; i++) { + if (v2[i] !== v1[i]) { + return false; + } + } + + return true; + }, + + afterRender: function(){ + var me = this, + records; + + me.callParent(); + if (me.selectOnRender) { + records = me.getRecordsForValue(me.value); + if (records.length) { + ++me.ignoreSelectChange; + me.boundList.getSelectionModel().select(records); + --me.ignoreSelectChange; + } + delete me.toSelect; + } + + if (me.ddReorder && !me.dragGroup && !me.dropGroup){ + me.dragGroup = me.dropGroup = 'MultiselectDD-' + Ext.id(); + } + + if (me.draggable || me.dragGroup){ + me.dragZone = Ext.create('Ext.view.DragZone', { + view: me.boundList, + ddGroup: me.dragGroup, + dragText: me.dragText + }); + } + if (me.droppable || me.dropGroup){ + me.dropZone = Ext.create('Ext.view.DropZone', { + view: me.boundList, + ddGroup: me.dropGroup, + handleNodeDrop: function(data, dropRecord, position) { + var view = this.view, + store = view.getStore(), + records = data.records, + index; + + + data.view.store.remove(records); + + index = store.indexOf(dropRecord); + if (position === 'after') { + index++; + } + store.insert(index, records); + view.getSelectionModel().select(records); + me.fireEvent('drop', me, records); + } + }); + } + }, + + isValid : function() { + var me = this, + disabled = me.disabled, + validate = me.forceValidation || !disabled; + + + return validate ? me.validateValue(me.value) : disabled; + }, + + validateValue: function(value) { + var me = this, + errors = me.getErrors(value), + isValid = Ext.isEmpty(errors); + + if (!me.preventMark) { + if (isValid) { + me.clearInvalid(); + } else { + me.markInvalid(errors); + } + } + + return isValid; + }, + + markInvalid : function(errors) { + + var me = this, + oldMsg = me.getActiveError(); + me.setActiveErrors(Ext.Array.from(errors)); + if (oldMsg !== me.getActiveError()) { + me.updateLayout(); + } + }, + + + clearInvalid : function() { + + var me = this, + hadError = me.hasActiveError(); + me.unsetActiveError(); + if (hadError) { + me.updateLayout(); + } + }, + + getSubmitData: function() { + var me = this, + data = null, + val; + if (!me.disabled && me.submitValue && !me.isFileUpload()) { + val = me.getSubmitValue(); + if (val !== null) { + data = {}; + data[me.getName()] = val; + } + } + return data; + }, + + + getSubmitValue: function() { + var me = this, + delimiter = me.delimiter, + val = me.getValue(); + + return Ext.isString(delimiter) ? val.join(delimiter) : val; + }, + + getValue: function(){ + return this.value || []; + }, + + getRecordsForValue: function(value){ + var me = this, + records = [], + all = me.store.getRange(), + valueField = me.valueField, + i = 0, + allLen = all.length, + rec, + j, + valueLen; + + for (valueLen = value.length; i < valueLen; ++i) { + for (j = 0; j < allLen; ++j) { + rec = all[j]; + if (rec.get(valueField) == value[i]) { + records.push(rec); + } + } + } + + return records; + }, + + setupValue: function(value){ + var delimiter = this.delimiter, + valueField = this.valueField, + i = 0, + out, + len, + item; + + if (Ext.isDefined(value)) { + if (delimiter && Ext.isString(value)) { + value = value.split(delimiter); + } else if (!Ext.isArray(value)) { + value = [value]; + } + + for (len = value.length; i < len; ++i) { + item = value[i]; + if (item && item.isModel) { + value[i] = item.get(valueField); + } + } + out = Ext.Array.unique(value); + } else { + out = []; + } + return out; + }, + + setValue: function(value){ + var me = this, + selModel = me.boundList.getSelectionModel(), + store = me.store; + + + if (!store.getCount()) { + store.on({ + load: Ext.Function.bind(me.setValue, me, [value]), + single: true + }); + return; + } + + value = me.setupValue(value); + me.mixins.field.setValue.call(me, value); + + if (me.rendered) { + ++me.ignoreSelectChange; + selModel.deselectAll(); + if (value.length) { + selModel.select(me.getRecordsForValue(value)); + } + --me.ignoreSelectChange; + } else { + me.selectOnRender = true; + } + }, + + clearValue: function(){ + this.setValue([]); + }, + + onEnable: function(){ + var list = this.boundList; + this.callParent(); + if (list) { + list.enable(); + } + }, + + onDisable: function(){ + var list = this.boundList; + this.callParent(); + if (list) { + list.disable(); + } + }, + + getErrors : function(value) { + var me = this, + format = Ext.String.format, + errors = [], + numSelected; + + value = Ext.Array.from(value || me.getValue()); + numSelected = value.length; + + if (!me.allowBlank && numSelected < 1) { + errors.push(me.blankText); + } + if (numSelected < me.minSelections) { + errors.push(format(me.minSelectionsText, me.minSelections)); + } + if (numSelected > me.maxSelections) { + errors.push(format(me.maxSelectionsText, me.maxSelections)); + } + return errors; + }, + + onDestroy: function(){ + var me = this; + + me.bindStore(null); + Ext.destroy(me.dragZone, me.dropZone); + me.callParent(); + }, + + onBindStore: function(store){ + var boundList = this.boundList; + + if (boundList) { + boundList.bindStore(store); + } + } + +}); + + + + +Ext.define('Ext.ux.form.ItemSelector', { + extend: Ext.ux.form.MultiSelect , + alias: ['widget.itemselectorfield', 'widget.itemselector'], + alternateClassName: ['Ext.ux.ItemSelector'], + + + + + + + hideNavIcons:false, + + + buttons: ['top', 'up', 'add', 'remove', 'down', 'bottom'], + + + buttonsText: { + top: "Move to Top", + up: "Move Up", + add: "Add to Selected", + remove: "Remove from Selected", + down: "Move Down", + bottom: "Move to Bottom" + }, + + layout: { + type: 'hbox', + align: 'stretch' + }, + + initComponent: function() { + var me = this; + + me.ddGroup = me.id + '-dd'; + me.callParent(); + + + + me.bindStore(me.store); + }, + + createList: function(title){ + var me = this; + + return Ext.create('Ext.ux.form.MultiSelect', { + + + + submitValue: false, + getSubmitData: function(){ + return null; + }, + getModelData: function(){ + return null; + }, + flex: 1, + dragGroup: me.ddGroup, + dropGroup: me.ddGroup, + title: title, + store: { + model: me.store.model, + data: [] + }, + displayField: me.displayField, + valueField: me.valueField, + disabled: me.disabled, + listeners: { + boundList: { + scope: me, + itemdblclick: me.onItemDblClick, + drop: me.syncValue + } + } + }); + }, + + setupItems: function() { + var me = this; + + me.fromField = me.createList(me.fromTitle); + me.toField = me.createList(me.toTitle); + + return [ + me.fromField, + { + xtype: 'container', + margins: '0 4', + layout: { + type: 'vbox', + pack: 'center' + }, + items: me.createButtons() + }, + me.toField + ]; + }, + + createButtons: function() { + var me = this, + buttons = []; + + if (!me.hideNavIcons) { + Ext.Array.forEach(me.buttons, function(name) { + buttons.push({ + xtype: 'button', + tooltip: me.buttonsText[name], + handler: me['on' + Ext.String.capitalize(name) + 'BtnClick'], + cls: Ext.baseCSSPrefix + 'form-itemselector-btn', + iconCls: Ext.baseCSSPrefix + 'form-itemselector-' + name, + navBtn: true, + scope: me, + margin: '4 0 0 0' + }); + }); + } + return buttons; + }, + + + getSelections: function(list) { + var store = list.getStore(); + + return Ext.Array.sort(list.getSelectionModel().getSelection(), function(a, b) { + a = store.indexOf(a); + b = store.indexOf(b); + + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } + return 0; + }); + }, + + onTopBtnClick : function() { + var list = this.toField.boundList, + store = list.getStore(), + selected = this.getSelections(list); + + store.suspendEvents(); + store.remove(selected, true); + store.insert(0, selected); + store.resumeEvents(); + list.refresh(); + this.syncValue(); + list.getSelectionModel().select(selected); + }, + + onBottomBtnClick : function() { + var list = this.toField.boundList, + store = list.getStore(), + selected = this.getSelections(list); + + store.suspendEvents(); + store.remove(selected, true); + store.add(selected); + store.resumeEvents(); + list.refresh(); + this.syncValue(); + list.getSelectionModel().select(selected); + }, + + onUpBtnClick : function() { + var list = this.toField.boundList, + store = list.getStore(), + selected = this.getSelections(list), + rec, + i = 0, + len = selected.length, + index = 0; + + + store.suspendEvents(); + for (; i < len; ++i, index++) { + rec = selected[i]; + index = Math.max(index, store.indexOf(rec) - 1); + store.remove(rec, true); + store.insert(index, rec); + } + store.resumeEvents(); + list.refresh(); + this.syncValue(); + list.getSelectionModel().select(selected); + }, + + onDownBtnClick : function() { + var list = this.toField.boundList, + store = list.getStore(), + selected = this.getSelections(list), + rec, + i = selected.length - 1, + index = store.getCount() - 1; + + + store.suspendEvents(); + for (; i > -1; --i, index--) { + rec = selected[i]; + index = Math.min(index, store.indexOf(rec) + 1); + store.remove(rec, true); + store.insert(index, rec); + } + store.resumeEvents(); + list.refresh(); + this.syncValue(); + list.getSelectionModel().select(selected); + }, + + onAddBtnClick : function() { + var me = this, + selected = me.getSelections(me.fromField.boundList); + + me.moveRec(true, selected); + me.toField.boundList.getSelectionModel().select(selected); + }, + + onRemoveBtnClick : function() { + var me = this, + selected = me.getSelections(me.toField.boundList); + + me.moveRec(false, selected); + me.fromField.boundList.getSelectionModel().select(selected); + }, + + moveRec: function(add, recs) { + var me = this, + fromField = me.fromField, + toField = me.toField, + fromStore = add ? fromField.store : toField.store, + toStore = add ? toField.store : fromField.store; + + fromStore.suspendEvents(); + toStore.suspendEvents(); + fromStore.remove(recs); + toStore.add(recs); + fromStore.resumeEvents(); + toStore.resumeEvents(); + + fromField.boundList.refresh(); + toField.boundList.refresh(); + + me.syncValue(); + }, + + + syncValue: function() { + var me = this; + me.mixins.field.setValue.call(me, me.setupValue(me.toField.store.getRange())); + }, + + onItemDblClick: function(view, rec) { + this.moveRec(view === this.fromField.boundList, rec); + }, + + setValue: function(value) { + var me = this, + fromField = me.fromField, + toField = me.toField, + fromStore = fromField.store, + toStore = toField.store, + selected; + + + if (!me.fromStorePopulated) { + me.fromField.store.on({ + load: Ext.Function.bind(me.setValue, me, [value]), + single: true + }); + return; + } + + value = me.setupValue(value); + me.mixins.field.setValue.call(me, value); + + selected = me.getRecordsForValue(value); + + + + fromStore.suspendEvents(); + toStore.suspendEvents(); + fromStore.removeAll(); + toStore.removeAll(); + + + me.populateFromStore(me.store); + + + Ext.Array.forEach(selected, function(rec){ + + if (fromStore.indexOf(rec) > -1) { + fromStore.remove(rec); + } + toStore.add(rec); + }); + + + fromStore.resumeEvents(); + toStore.resumeEvents(); + + + Ext.suspendLayouts(); + fromField.boundList.refresh(); + toField.boundList.refresh(); + Ext.resumeLayouts(true); + }, + + onBindStore: function(store, initial) { + var me = this; + + if (me.fromField) { + me.fromField.store.removeAll() + me.toField.store.removeAll(); + + + if (store.getCount()) { + me.populateFromStore(store); + } else { + me.store.on('load', me.populateFromStore, me); + } + } + }, + + populateFromStore: function(store) { + var fromStore = this.fromField.store; + + + this.fromStorePopulated = true; + + fromStore.add(store.getRange()); + + + fromStore.fireEvent('load', fromStore); + }, + + onEnable: function(){ + var me = this; + + me.callParent(); + me.fromField.enable(); + me.toField.enable(); + + Ext.Array.forEach(me.query('[navBtn]'), function(btn){ + btn.enable(); + }); + }, + + onDisable: function(){ + var me = this; + + me.callParent(); + me.fromField.disable(); + me.toField.disable(); + + Ext.Array.forEach(me.query('[navBtn]'), function(btn){ + btn.disable(); + }); + }, + + onDestroy: function(){ + this.bindStore(null); + this.callParent(); + } +}); + + +Ext.define('ExtThemeNeptune.toolbar.Paging', { + override: 'Ext.toolbar.Paging', + defaultButtonUI: 'plain-toolbar', + + inputItemWidth: 40 +}); + + + +Ext.define('Ext.form.Label', { + extend: Ext.Component , + alias: 'widget.label', + + + autoEl: 'label', + + + + + + maskOnDisable: false, + + getElConfig: function(){ + var me = this; + + me.html = me.text ? Ext.util.Format.htmlEncode(me.text) : (me.html || ''); + return Ext.apply(me.callParent(), { + htmlFor: me.forId || '' + }); + }, + + + setText : function(text, encode){ + var me = this; + + encode = encode !== false; + if(encode) { + me.text = text; + delete me.html; + } else { + me.html = text; + delete me.text; + } + + if(me.rendered){ + me.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(text) : text; + me.updateLayout(); + } + return me; + } +}); + + + + +Ext.define('Ext.form.Panel', { + extend: Ext.panel.Panel , + mixins: { + fieldAncestor: Ext.form.FieldAncestor + }, + alias: 'widget.form', + alternateClassName: ['Ext.FormPanel', 'Ext.form.FormPanel'], + + + + + + + + layout: 'anchor', + + ariaRole: 'form', + + basicFormConfigs: [ + + 'api', + + 'baseParams', + + 'errorReader', + + 'jsonSubmit', + + 'method', + + 'paramOrder', + + 'paramsAsHash', + + 'reader', + + 'standardSubmit', + + 'timeout', + + 'trackResetOnLoad', + + 'url', + + 'waitMsgTarget', + + 'waitTitle' + ], + + initComponent: function() { + var me = this; + + if (me.frame) { + me.border = false; + } + + me.initFieldAncestor(); + me.callParent(); + + me.relayEvents(me.form, [ + + 'beforeaction', + + 'actionfailed', + + 'actioncomplete', + + 'validitychange', + + 'dirtychange' + ]); + + + if (me.pollForChanges) { + me.startPolling(me.pollInterval || 500); + } + }, + + initItems: function() { + + this.callParent(); + this.initMonitor(); + this.form = this.createForm(); + }, + + + afterFirstLayout: function() { + this.callParent(arguments); + this.form.initialize(); + }, + + + createForm: function() { + var cfg = {}, + props = this.basicFormConfigs, + len = props.length, + i = 0, + prop; + + for (; i < len; ++i) { + prop = props[i]; + cfg[prop] = this[prop]; + } + return new Ext.form.Basic(this, cfg); + }, + + + getForm: function() { + return this.form; + }, + + + loadRecord: function(record) { + return this.getForm().loadRecord(record); + }, + + + getRecord: function() { + return this.getForm().getRecord(); + }, + + + updateRecord: function(record) { + return this.getForm().updateRecord(record); + }, + + + getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues) { + return this.getForm().getValues(asString, dirtyOnly, includeEmptyText, useDataValues); + }, + + + isDirty: function () { + return this.form.isDirty(); + }, + + + isValid: function () { + return this.form.isValid(); + }, + + + hasInvalidField: function () { + return this.form.hasInvalidField(); + }, + + beforeDestroy: function() { + this.stopPolling(); + this.form.destroy(); + this.callParent(); + }, + + + load: function(options) { + this.form.load(options); + }, + + + submit: function(options) { + this.form.submit(options); + }, + + + startPolling: function(interval) { + this.stopPolling(); + var task = new Ext.util.TaskRunner(interval); + task.start({ + interval: 0, + run: this.checkChange, + scope: this + }); + this.pollTask = task; + }, + + + stopPolling: function() { + var task = this.pollTask; + if (task) { + task.stopAll(); + delete this.pollTask; + } + }, + + + checkChange: function() { + var fields = this.form.getFields().items, + f, + fLen = fields.length; + + for (f = 0; f < fLen; f++) { + fields[f].checkChange(); + } + } +}); + + + +Ext.define('Ext.form.RadioManager', { + extend: Ext.util.MixedCollection , + singleton: true, + + getByName: function(name, formId) { + return this.filterBy(function(item) { + return item.name == name && item.getFormId() == formId; + }); + }, + + getWithValue: function(name, value, formId) { + return this.filterBy(function(item) { + return item.name == name && item.inputValue == value && item.getFormId() == formId; + }); + }, + + getChecked: function(name, formId) { + return this.findBy(function(item) { + return item.name == name && item.checked && item.getFormId() == formId; + }); + } +}); + + + +Ext.define('Ext.form.field.Radio', { + extend: Ext.form.field.Checkbox , + alias: ['widget.radiofield', 'widget.radio'], + alternateClassName: 'Ext.form.Radio', + + + + isRadio: true, + + + focusCls: 'form-radio-focus', + + + + + inputType: 'radio', + ariaRole: 'radio', + + formId: null, + + + getGroupValue: function() { + var selected = this.getManager().getChecked(this.name, this.getFormId()); + return selected ? selected.inputValue : null; + }, + + + onBoxClick: function(e) { + var me = this; + if (!me.disabled && !me.readOnly) { + this.setValue(true); + } + }, + + onRemoved: function(){ + this.callParent(arguments); + this.formId = null; + }, + + + setValue: function(v) { + var me = this, + active; + + if (Ext.isBoolean(v)) { + me.callParent(arguments); + } else { + active = me.getManager().getWithValue(me.name, v, me.getFormId()).getAt(0); + if (active) { + active.setValue(true); + } + } + return me; + }, + + + getSubmitValue: function() { + return this.checked ? this.inputValue : null; + }, + + getModelData: function() { + var o = this.callParent(arguments); + if (o) { + o[this.getName()] = this.getSubmitValue(); + } + return o; + }, + + + onChange: function(newVal, oldVal) { + var me = this, + r, rLen, radio, radios; + + me.callParent(arguments); + + if (newVal) { + radios = me.getManager().getByName(me.name, me.getFormId()).items; + rLen = radios.length; + + for (r = 0; r < rLen; r++) { + radio = radios[r]; + + if (radio !== me) { + radio.setValue(false); + } + } + } + }, + + + getManager: function() { + return Ext.form.RadioManager; + } +}); + + + +Ext.define('Ext.form.RadioGroup', { + extend: Ext.form.CheckboxGroup , + alias: 'widget.radiogroup', + + + + + + + + allowBlank : true, + + + blankText : 'You must select one item in this group', + + + + defaultType : 'radiofield', + + + groupCls : Ext.baseCSSPrefix + 'form-radio-group', + + ariaRole: 'radiogroup', + + getBoxes: function(query) { + return this.query('[isRadio]' + (query||'')); + }, + + checkChange: function() { + var value = this.getValue(), + key = Ext.Object.getKeys(value)[0]; + + + + if (Ext.isArray(value[key])) { + return; + } + this.callParent(arguments); + }, + + + setValue: function(value) { + var cbValue, first, formId, radios, + i, len, name; + + if (Ext.isObject(value)) { + for (name in value) { + if (value.hasOwnProperty(name)) { + cbValue = value[name]; + first = this.items.first(); + formId = first ? first.getFormId() : null; + radios = Ext.form.RadioManager.getWithValue(name, cbValue, formId).items; + len = radios.length; + + for (i = 0; i < len; ++i) { + radios[i].setValue(true); + } + } + } + } + return this; + } +}); + + + +Ext.define('Ext.form.action.DirectLoad', { + extend: Ext.form.action.Load , + + alternateClassName: 'Ext.form.Action.DirectLoad', + alias: 'formaction.directload', + + type: 'directload', + + run: function() { + var me = this, + form = me.form, + api = form.api, + fn = api.load, + method, args; + + if (typeof fn !== 'function') { + var fnName = fn; + + api.load = fn = Ext.direct.Manager.parseMethod(fn); + + if (!Ext.isFunction(fn)) { + Ext.Error.raise('Cannot resolve Ext.Direct API method ' + fnName); + } + } + + method = fn.directCfg.method; + args = method.getArgs(me.getParams(), form.paramOrder, form.paramsAsHash); + + args.push(me.onComplete, me); + fn.apply(window, args); + }, + + + + + processResponse: function(result) { + return (this.result = result); + }, + + onComplete: function(data, response) { + if (data) { + this.onSuccess(data); + } else { + this.onFailure(null); + } + } +}); + + + + + +Ext.define('Ext.form.action.DirectSubmit', { + extend: Ext.form.action.Submit , + + alternateClassName: 'Ext.form.Action.DirectSubmit', + alias: 'formaction.directsubmit', + + type: 'directsubmit', + + doSubmit: function() { + var me = this, + form = me.form, + api = form.api, + fn = api.submit, + callback, formInfo, options; + options; + + if (typeof fn !== 'function') { + var fnName = fn; + + api.submit = fn = Ext.direct.Manager.parseMethod(fn); + + if (!Ext.isFunction(fn)) { + Ext.Error.raise('Cannot resolve Ext.Direct API method ' + fnName); + } + } + + if (me.timeout || form.timeout) { + options = { + timeout: me.timeout * 1000 || form.timeout * 1000 + }; + } + + formInfo = me.buildForm(); + + fn.call(window, formInfo.formEl, me.onComplete, me, options); + me.cleanup(formInfo); + }, + + + + + processResponse: function(result) { + return (this.result = result); + }, + + onComplete: function(data, response){ + if (data) { + this.onSuccess(data); + } else { + this.onFailure(null); + } + } +}); + + + +Ext.define('Ext.form.action.StandardSubmit', { + extend: Ext.form.action.Submit , + alias: 'formaction.standardsubmit', + + + + + doSubmit: function() { + var formInfo = this.buildForm(); + formInfo.formEl.submit(); + this.cleanup(formInfo); + } + +}); + + + +Ext.define('Ext.view.BoundListKeyNav', { + extend: Ext.util.KeyNav , + + + + + constructor: function(el, config) { + var me = this; + me.boundList = config.boundList; + me.callParent([el, Ext.apply({}, config, me.defaultHandlers)]); + }, + + defaultHandlers: { + up: function() { + var me = this, + boundList = me.boundList, + allItems = boundList.all, + oldItem = boundList.highlightedItem, + oldItemIdx = oldItem ? boundList.indexOf(oldItem) : -1, + newItemIdx = oldItemIdx > 0 ? oldItemIdx - 1 : allItems.getCount() - 1; + me.highlightAt(newItemIdx); + }, + + down: function() { + var me = this, + boundList = me.boundList, + allItems = boundList.all, + oldItem = boundList.highlightedItem, + oldItemIdx = oldItem ? boundList.indexOf(oldItem) : -1, + newItemIdx = oldItemIdx < allItems.getCount() - 1 ? oldItemIdx + 1 : 0; + me.highlightAt(newItemIdx); + }, + + pageup: function() { + + }, + + pagedown: function() { + + }, + + home: function() { + this.highlightAt(0); + }, + + end: function() { + var me = this; + me.highlightAt(me.boundList.all.getCount() - 1); + }, + + enter: function(e) { + this.selectHighlighted(e); + } + }, + + + highlightAt: function(index) { + var boundList = this.boundList, + item = boundList.all.item(index); + if (item) { + item = item.dom; + boundList.highlightItem(item); + boundList.getTargetEl().scrollChildIntoView(item, false); + } + }, + + + selectHighlighted: function(e) { + var boundList = this.boundList, + selModel = boundList.getSelectionModel(), + highlighted, highlightedRec; + + highlighted = boundList.highlightedItem; + if (highlighted) { + highlightedRec = boundList.getRecord(highlighted); + + + + if (e.getKey() === e.ENTER || !selModel.isSelected(highlightedRec)) { + selModel.selectWithEvent(highlightedRec, e); + } + } + } + +}); + + + +Ext.define('Ext.layout.component.field.ComboBox', { + extend: Ext.layout.component.field.Trigger , + alias: 'layout.combobox', + + + type: 'combobox', + + startingWidth: null, + + getTextWidth: function () { + var me = this, + owner = me.owner, + store = owner.store, + field = owner.displayField, + storeLn = store.data.length, + value = '', + i = 0, n = 0, ln, item, width; + + for (; i < storeLn; i++) { + item = store.getAt(i).data[field]; + ln = item.length; + + if (ln > n) { + n = ln; + value = item; + } + } + + width = Math.max(me.callParent(arguments), owner.inputEl.getTextWidth(value + owner.growAppend)); + + + + if (!me.startingWidth || owner.removingRecords) { + me.startingWidth = width; + + + + if (width < owner.growMin) { + owner.defaultListConfig.minWidth = owner.growMin; + } + + owner.removingRecords = false; + } + + + return (width < me.startingWidth) ? me.startingWidth : width; + } +}); + + + +Ext.define('Ext.form.field.ComboBox', { + extend: Ext.form.field.Picker , + + alternateClassName: 'Ext.form.ComboBox', + alias: ['widget.combobox', 'widget.combo'], + mixins: { + bindable: Ext.util.Bindable + }, + + componentLayout: 'combobox', + + + + + triggerCls: Ext.baseCSSPrefix + 'form-arrow-trigger', + + + hiddenName: '', + + + + + hiddenDataCls: Ext.baseCSSPrefix + 'hide-display ' + Ext.baseCSSPrefix + 'form-data-hidden', + + ariaRole: 'combobox', + + + fieldSubTpl: [ + '', + ' value="{[Ext.util.Format.htmlEncode(values.value)]}"
', + ' name="{name}"', + ' placeholder="{placeholder}"', + ' size="{size}"', + ' maxlength="{maxLength}"', + ' readonly="readonly"', + ' disabled="disabled"', + ' tabIndex="{tabIdx}"', + ' style="{fieldStyle}"', + '/>', + { + compiled: true, + disableFormats: true + } + ], + + getSubTplData: function(){ + var me = this; + Ext.applyIf(me.subTplData, { + hiddenDataCls: me.hiddenDataCls + }); + return me.callParent(arguments); + }, + + afterRender: function(){ + var me = this; + me.callParent(arguments); + me.setHiddenValue(me.value); + }, + + + + + multiSelect: false, + + + + delimiter: ', ', + + + + displayField: 'text', + + + + + triggerAction: 'all', + + + allQuery: '', + + + queryParam: 'query', + + + queryMode: 'remote', + + + queryCaching: true, + + + 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, + + + + + ignoreSelection: 0, + + + removingRecords: null, + + + resizeComboToGrow: function () { + var me = this; + return me.grow && me.growToLongestValue; + }, + + initComponent: function() { + var me = this, + isDefined = Ext.isDefined, + store = me.store, + transform = me.transform, + displayTpl = me.displayTpl, + transformSelect, isLocalMode; + + Ext.applyIf(me.renderSelectors, { + hiddenDataEl: '.' + me.hiddenDataCls.split(' ').join('.') + }); + + if (me.typeAhead && me.multiSelect) { + Ext.Error.raise('typeAhead and multiSelect are mutually exclusive options -- please remove one of them.'); + } + if (me.typeAhead && !me.editable) { + Ext.Error.raise('If typeAhead is enabled the combo must be editable: true -- please change one of those settings.'); + } + if (me.selectOnFocus && !me.editable) { + Ext.Error.raise('If selectOnFocus is enabled the combo must be editable: true -- please change one of those settings.'); + } + + me.addEvents( + + 'beforequery', + + + 'select', + + + 'beforeselect', + + + 'beforedeselect' + ); + + + if (transform) { + transformSelect = Ext.getDom(transform); + if (transformSelect) { + if (!me.store) { + store = Ext.Array.map(Ext.Array.from(transformSelect.options), function(option){ + return [option.value, option.text]; + }); + } + if (!me.name) { + me.name = transformSelect.name; + } + if (!('value' in me)) { + me.value = transformSelect.value; + } + } + } + + me.bindStore(store || 'ext-empty-store', true, true); + store = me.store; + if (store.autoCreated) { + me.queryMode = 'local'; + me.valueField = me.displayField = 'field1'; + if (!store.expanded) { + me.displayField = 'field2'; + } + } + + if (!isDefined(me.valueField)) { + me.valueField = me.displayField; + } + + isLocalMode = me.queryMode === 'local'; + if (!isDefined(me.queryDelay)) { + me.queryDelay = isLocalMode ? 10 : 500; + } + if (!isDefined(me.minChars)) { + me.minChars = isLocalMode ? 0 : 4; + } + + if (!displayTpl) { + me.displayTpl = new Ext.XTemplate( + '' + + '{[typeof values === "string" ? values : values["' + me.displayField + '"]]}' + + '' + me.delimiter + '' + + '' + ); + } else if (!displayTpl.isTemplate) { + me.displayTpl = new Ext.XTemplate(displayTpl); + } + + me.callParent(); + + me.doQueryTask = new Ext.util.DelayedTask(me.doRawQuery, me); + + + if (me.store.getCount() > 0) { + me.setValue(me.value); + } + + + if (transformSelect) { + if (me.transformInPlace) { + me.render(transformSelect.parentNode, transformSelect); + delete me.renderTo; + } + Ext.removeNode(transformSelect); + } + }, + + + getStore : function(){ + return this.store; + }, + + beforeBlur: function() { + var me = this, + filter = me.queryFilter; + + me.doQueryTask.cancel(); + me.assertValue(); + + if (filter && !filter.disabled && me.queryMode === 'local' && me.clearFilterOnBlur) { + filter.disabled = true; + me.store.filter(); + } + }, + + onFocus: function() { + var me = this, + filter = me.queryFilter; + + me.callParent(arguments); + if (!me.duringTriggerClick && me.triggerAction !== 'all' && filter && filter.disabled && me.queryMode === 'local' && me.clearFilterOnBlur) { + delete me.lastQuery; + me.doRawQuery(); + } + }, + + + assertValue: function() { + var me = this, + value = me.getRawValue(), + rec, currentValue; + + if (me.forceSelection) { + if (me.multiSelect) { + + + if (value !== me.getDisplayValue()) { + me.setValue(me.lastSelection); + } + } else { + + + rec = me.findRecordByDisplay(value); + if (rec) { + currentValue = me.value; + + + if (!me.findRecordByValue(currentValue)) { + me.select(rec, true); + } + } else { + me.setValue(me.lastSelection); + } + } + } + me.collapse(); + }, + + onTypeAhead: function() { + var me = this, + displayField = me.displayField, + record = me.store.findRecord(displayField, me.getRawValue()), + boundList = me.getPicker(), + newValue, len, selStart; + + if (record) { + newValue = record.get(displayField); + len = newValue.length; + selStart = me.getRawValue().length; + + boundList.highlightItem(boundList.getNode(record)); + + if (selStart !== 0 && selStart !== len) { + me.setRawValue(newValue); + me.selectText(selStart, newValue.length); + } + } + }, + + + + resetToDefault: Ext.emptyFn, + + beforeReset: function() { + var filter = this.queryFilter; + + this.callParent(); + + + if (filter && !filter.disabled) { + filter.disabled = true; + this.store.filter(); + } + }, + + onUnbindStore: function(store) { + var me = this, + picker = me.picker, + filter = me.queryFilter; + + + if (filter) { + me.store.removeFilter(filter); + } + if (picker) { + picker.bindStore(null); + } + }, + + onBindStore: function(store, initial) { + var picker = this.picker; + + if (!initial) { + this.resetToDefault(); + } + + if (picker) { + picker.bindStore(store); + } + }, + + + bindStore: function(store, preventFilter, initial) { + var me = this, + filter = me.queryFilter; + + me.mixins.bindable.bindStore.call(me, store, initial); + store = me.getStore(); + if (store && filter) { + filter.disabled = !!preventFilter; + store.addFilter(filter); + } + }, + + getStoreListeners: function() { + var me = this; + + return { + beforeload: me.onBeforeLoad, + clear: me.onClear, + datachanged: me.onDataChanged, + load: me.onLoad, + exception: me.onException, + remove: me.onRemove + }; + }, + + onBeforeLoad: function(){ + + + + ++this.ignoreSelection; + }, + + onDataChanged: function() { + var me = this; + + if (me.resizeComboToGrow()) { + me.updateLayout(); + } + }, + + onClear: function() { + var me = this; + + if (me.resizeComboToGrow()) { + me.removingRecords = true; + me.onDataChanged(); + } + }, + + onRemove: function() { + var me = this; + + if (me.resizeComboToGrow()) { + me.removingRecords = true; + } + }, + + onException: function(){ + if (this.ignoreSelection > 0) { + --this.ignoreSelection; + } + this.collapse(); + }, + + onLoad: function(store, records, success) { + var me = this; + + if (me.ignoreSelection > 0) { + --me.ignoreSelection; + } + + + if (success && !store.lastOptions.rawQuery) { + + + + if (me.value == null) { + + if (me.store.getCount()) { + me.doAutoSelect(); + } else { + + me.setValue(me.value); + } + } else { + me.setValue(me.value); + } + } + }, + + + doRawQuery: function() { + this.doQuery(this.getRawValue(), false, true); + }, + + + doQuery: function(queryString, forceAll, rawQuery) { + var me = this, + + + queryPlan = me.beforeQuery({ + query: queryString || '', + rawQuery: rawQuery, + forceAll: forceAll, + combo: me, + cancel: false + }); + + + if (queryPlan === false || queryPlan.cancel) { + return false; + } + + + if (me.queryCaching && queryPlan.query === me.lastQuery) { + me.expand(); + if (me.queryMode === 'local') { + me.doAutoSelect(); + } + } + + + else { + me.lastQuery = queryPlan.query; + + if (me.queryMode === 'local') { + me.doLocalQuery(queryPlan); + + } else { + me.doRemoteQuery(queryPlan); + } + } + + return true; + }, + + + beforeQuery: function(queryPlan) { + var me = this; + + + if (me.fireEvent('beforequery', queryPlan) === false) { + queryPlan.cancel = true; + } + + + else if (!queryPlan.cancel) { + + + if (queryPlan.query.length < me.minChars && !queryPlan.forceAll) { + queryPlan.cancel = true; + } + } + return queryPlan; + }, + + doLocalQuery: function(queryPlan) { + var me = this, + queryString = queryPlan.query; + + + if (!me.queryFilter) { + + me.queryFilter = new Ext.util.Filter({ + id: me.id + '-query-filter', + anyMatch: me.anyMatch, + caseSensitive: me.caseSensitive, + root: 'data', + property: me.displayField + }); + me.store.addFilter(me.queryFilter, false); + } + + + if (queryString || !queryPlan.forceAll) { + me.queryFilter.disabled = false; + me.queryFilter.setValue(me.enableRegEx ? new RegExp(queryString) : queryString); + } + + + else { + me.queryFilter.disabled = true; + } + + + me.store.filter(); + + + if (me.store.getCount() || me.getPicker().emptyText) { + me.expand(); + } else { + me.collapse(); + } + + me.afterQuery(queryPlan); + }, + + doRemoteQuery: function(queryPlan) { + var me = this, + loadCallback = function() { + me.afterQuery(queryPlan); + }; + + + me.expand(); + + + + if (me.pageSize) { + + me.loadPage(1, { + rawQuery: queryPlan.rawQuery, + callback: loadCallback + }); + } else { + me.store.load({ + params: me.getParams(queryPlan.query), + rawQuery: queryPlan.rawQuery, + callback: loadCallback + }); + } + }, + + + afterQuery: function(queryPlan) { + var me = this; + + if (me.store.getCount()) { + if (me.typeAhead) { + me.doTypeAhead(); + } + + + if (me.getRawValue() !== me.getDisplayValue()) { + me.ignoreSelection++; + me.picker.getSelectionModel().deselectAll(); + me.ignoreSelection--; + } + + if (queryPlan.rawQuery) { + me.syncSelection(); + if (me.picker && !me.picker.getSelectionModel().hasSelection()) { + me.doAutoSelect(); + } + } else { + me.doAutoSelect(); + } + } + }, + + loadPage: function(pageNum, options) { + this.store.loadPage(pageNum, Ext.apply({ + params: this.getParams(this.lastQuery) + }, options)); + }, + + onPageChange: function(toolbar, newPage){ + + this.loadPage(newPage); + return false; + }, + + + getParams: function(queryString) { + var params = {}, + param = this.queryParam; + + if (param) { + params[param] = queryString; + } + return params; + }, + + + doAutoSelect: function() { + var me = this, + picker = me.picker, + lastSelected, itemNode; + if (picker && me.autoSelect && me.store.getCount() > 0) { + + lastSelected = picker.getSelectionModel().lastSelected; + itemNode = picker.getNode(lastSelected || 0); + if (itemNode) { + picker.highlightItem(itemNode); + picker.listEl.scrollChildIntoView(itemNode, false); + } + } + }, + + doTypeAhead: function() { + var me = this; + if (!me.typeAheadTask) { + me.typeAheadTask = new Ext.util.DelayedTask(me.onTypeAhead, me); + } + if (me.lastKey != Ext.EventObject.BACKSPACE && me.lastKey != Ext.EventObject.DELETE) { + me.typeAheadTask.delay(me.typeAheadDelay); + } + }, + + onTriggerClick: function() { + var me = this; + + me.duringTriggerClick = true; + if (!me.readOnly && !me.disabled) { + if (me.isExpanded) { + me.collapse(); + } else { + me.onFocus({}); + if (me.triggerAction === 'all') { + me.doQuery(me.allQuery, true); + } else if (me.triggerAction === 'last') { + me.doQuery(me.lastQuery, true); + } else { + me.doQuery(me.getRawValue(), false, true); + } + } + me.inputEl.focus(); + } + delete me.duringTriggerClick; + }, + + onPaste: function(){ + var me = this; + + if (!me.readOnly && !me.disabled && me.editable) { + me.doQueryTask.delay(me.queryDelay); + } + }, + + + onKeyUp: function(e, t) { + var me = this, + key = e.getKey(); + + if (!me.readOnly && !me.disabled && me.editable) { + me.lastKey = key; + + + + + if (!e.isSpecialKey() || key == e.BACKSPACE || key == e.DELETE) { + me.doQueryTask.delay(me.queryDelay); + } + } + + if (me.enableKeyEvents) { + me.callParent(arguments); + } + }, + + initEvents: function() { + var me = this; + me.callParent(); + + + if (!me.enableKeyEvents) { + me.mon(me.inputEl, 'keyup', me.onKeyUp, me); + } + me.mon(me.inputEl, 'paste', me.onPaste, me); + }, + + onDestroy: function() { + var me = this + + if (me.typeAheadTask) { + me.typeAheadTask.cancel(); + me.typeAheadTask = null; + } + + Ext.destroy(me.listKeyNav); + me.bindStore(null); + me.callParent(); + }, + + + + onAdded: function() { + var me = this; + me.callParent(arguments); + if (me.picker) { + me.picker.ownerCt = me.up('[floating]'); + me.picker.registerWithOwnerCt(); + } + }, + + createPicker: function() { + var me = this, + picker, + pickerCfg = Ext.apply({ + xtype: 'boundlist', + pickerField: me, + selModel: { + mode: me.multiSelect ? 'SIMPLE' : 'SINGLE' + }, + floating: true, + hidden: true, + store: me.store, + displayField: me.displayField, + focusOnToFront: false, + pageSize: me.pageSize, + tpl: me.tpl + }, me.listConfig, me.defaultListConfig); + + picker = me.picker = Ext.widget(pickerCfg); + if (me.pageSize) { + picker.pagingToolbar.on('beforechange', me.onPageChange, me); + } + + me.mon(picker, { + itemclick: me.onItemClick, + refresh: me.onListRefresh, + scope: me + }); + + me.mon(picker.getSelectionModel(), { + beforeselect: me.onBeforeSelect, + beforedeselect: me.onBeforeDeselect, + selectionchange: me.onListSelectionChange, + scope: me + }); + + return picker; + }, + + alignPicker: function(){ + var me = this, + picker = me.getPicker(), + heightAbove = me.getPosition()[1] - Ext.getBody().getScroll().top, + heightBelow = Ext.Element.getViewHeight() - heightAbove - me.getHeight(), + space = Math.max(heightAbove, heightBelow); + + + if (picker.height) { + delete picker.height; + picker.updateLayout(); + } + + if (picker.getHeight() > space - 5) { + picker.setHeight(space - 5); + } + me.callParent(); + }, + + onListRefresh: function() { + + if (!this.expanding) { + this.alignPicker(); + } + this.syncSelection(); + }, + + onItemClick: function(picker, record){ + + var me = this, + selection = me.picker.getSelectionModel().getSelection(), + valueField = me.valueField; + + if (!me.multiSelect && selection.length) { + if (record.get(valueField) === selection[0].get(valueField)) { + + me.displayTplData = [record.data]; + me.setRawValue(me.getDisplayValue()); + me.collapse(); + } + } + }, + + onBeforeSelect: function(list, record) { + return this.fireEvent('beforeselect', this, record, record.index); + }, + + onBeforeDeselect: function(list, record) { + return this.fireEvent('beforedeselect', this, record, record.index); + }, + + onListSelectionChange: function(list, selectedRecords) { + var me = this, + isMulti = me.multiSelect, + hasRecords = selectedRecords.length > 0; + + + if (!me.ignoreSelection && me.isExpanded) { + if (!isMulti) { + Ext.defer(me.collapse, 1, me); + } + + if (isMulti || hasRecords) { + me.setValue(selectedRecords, false); + } + if (hasRecords) { + me.fireEvent('select', me, selectedRecords); + } + me.inputEl.focus(); + } + }, + + + onExpand: function() { + var me = this, + keyNav = me.listKeyNav, + selectOnTab = me.selectOnTab, + picker = me.getPicker(); + + + if (keyNav) { + keyNav.enable(); + } else { + keyNav = me.listKeyNav = new Ext.view.BoundListKeyNav(me.inputEl, { + boundList: picker, + forceKeyDown: true, + tab: function(e) { + if (selectOnTab) { + this.selectHighlighted(e); + me.triggerBlur(); + } + + return true; + }, + enter: function(e){ + var selModel = picker.getSelectionModel(), + count = selModel.getCount(); + + this.selectHighlighted(e); + + + + if (!me.multiSelect && count === selModel.getCount()) { + me.collapse(); + } + } + }); + } + + + if (selectOnTab) { + me.ignoreMonitorTab = true; + } + + Ext.defer(keyNav.enable, 1, keyNav); + me.inputEl.focus(); + }, + + + onCollapse: function() { + var me = this, + keyNav = me.listKeyNav; + if (keyNav) { + keyNav.disable(); + me.ignoreMonitorTab = false; + } + }, + + + select: function(r, assert) { + var me = this, + picker = me.picker, + fireSelect; + + if (r && r.isModel && assert === true && picker) { + fireSelect = !picker.getSelectionModel().isSelected(r); + } + + me.setValue(r, true); + + + if (fireSelect) { + me.fireEvent('select', me, r); + } + }, + + + findRecord: function(field, value) { + var ds = this.store, + idx = ds.findExact(field, value); + return idx !== -1 ? ds.getAt(idx) : false; + }, + + + findRecordByValue: function(value) { + return this.findRecord(this.valueField, value); + }, + + + findRecordByDisplay: function(value) { + return this.findRecord(this.displayField, value); + }, + + + setValue: function(value, doSelect) { + var me = this, + valueNotFoundText = me.valueNotFoundText, + inputEl = me.inputEl, + i, len, record, + dataObj, + matchedRecords = [], + displayTplData = [], + processedValue = []; + + if (me.store.loading) { + + me.value = value; + me.setHiddenValue(me.value); + return me; + } + + + value = Ext.Array.from(value); + + + for (i = 0, len = value.length; i < len; i++) { + record = value[i]; + if (!record || !record.isModel) { + record = me.findRecordByValue(record); + } + + if (record) { + matchedRecords.push(record); + displayTplData.push(record.data); + processedValue.push(record.get(me.valueField)); + } + + + else { + + + if (!me.forceSelection) { + processedValue.push(value[i]); + dataObj = {}; + dataObj[me.displayField] = value[i]; + displayTplData.push(dataObj); + + } + + else if (Ext.isDefined(valueNotFoundText)) { + displayTplData.push(valueNotFoundText); + } + } + } + + + me.setHiddenValue(processedValue); + me.value = me.multiSelect ? processedValue : processedValue[0]; + if (!Ext.isDefined(me.value)) { + me.value = null; + } + me.displayTplData = displayTplData; + me.lastSelection = me.valueModels = matchedRecords; + + if (inputEl && me.emptyText && !Ext.isEmpty(value)) { + inputEl.removeCls(me.emptyCls); + } + + + me.setRawValue(me.getDisplayValue()); + me.checkChange(); + + if (doSelect !== false) { + me.syncSelection(); + } + me.applyEmptyText(); + + return me; + }, + + + setHiddenValue: function(values){ + var me = this, + name = me.hiddenName, + i, + dom, childNodes, input, valueCount, childrenCount; + + if (!me.hiddenDataEl || !name) { + return; + } + values = Ext.Array.from(values); + dom = me.hiddenDataEl.dom; + childNodes = dom.childNodes; + input = childNodes[0]; + valueCount = values.length; + childrenCount = childNodes.length; + + if (!input && valueCount > 0) { + me.hiddenDataEl.update(Ext.DomHelper.markup({ + tag: 'input', + type: 'hidden', + name: name + })); + childrenCount = 1; + input = dom.firstChild; + } + while (childrenCount > valueCount) { + dom.removeChild(childNodes[0]); + -- childrenCount; + } + while (childrenCount < valueCount) { + dom.appendChild(input.cloneNode(true)); + ++ childrenCount; + } + for (i = 0; i < valueCount; i++) { + childNodes[i].value = values[i]; + } + }, + + + getDisplayValue: function() { + return this.displayTpl.apply(this.displayTplData); + }, + + getValue: function() { + + + + var me = this, + picker = me.picker, + rawValue = me.getRawValue(), + value = me.value; + + if (me.getDisplayValue() !== rawValue) { + value = rawValue; + me.value = me.displayTplData = me.valueModels = null; + if (picker) { + me.ignoreSelection++; + picker.getSelectionModel().deselectAll(); + me.ignoreSelection--; + } + } + + return value; + }, + + getSubmitValue: function() { + var value = this.getValue(); + + + if (Ext.isEmpty(value)) { + value = ''; + } + return value; + }, + + isEqual: function(v1, v2) { + var fromArray = Ext.Array.from, + 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] !== v1[i]) { + return false; + } + } + + return true; + }, + + + clearValue: function() { + this.setValue([]); + }, + + + syncSelection: function() { + var me = this, + picker = me.picker, + selection, selModel, + values = me.valueModels || [], + vLen = values.length, v, value; + + if (picker) { + + selection = []; + for (v = 0; v < vLen; v++) { + value = values[v]; + + if (value && value.isModel && me.store.indexOf(value) >= 0) { + selection.push(value); + } + } + + + me.ignoreSelection++; + selModel = picker.getSelectionModel(); + selModel.deselectAll(); + if (selection.length) { + selModel.select(selection, undefined, true); + } + me.ignoreSelection--; + } + }, + + onEditorTab: function(e){ + var keyNav = this.listKeyNav; + + if (this.selectOnTab && keyNav) { + keyNav.selectHighlighted(e); + } + } +}); + + + +Ext.define('Ext.picker.Month', { + extend: Ext.Component , + + + + + + + alias: 'widget.monthpicker', + alternateClassName: 'Ext.MonthPicker', + + childEls: [ + 'bodyEl', 'prevEl', 'nextEl', 'monthEl', 'yearEl' + ], + + renderTpl: [ + '
', + '
', + '', + '
', + + '{.}', + '
', + '
', + '
', + '
', + '
', + '
', + + '', + '
', + '
', + + '', + '
', + '
', + '', + '
', + + '{.}', + '
', + '
', + '
', + '
', + '', + '
{%', + '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: Ext.baseCSSPrefix + 'monthpicker', + + + showButtons: true, + + + + + + measureWidth: 35, + measureMaxHeight: 20, + + + smallCls: Ext.baseCSSPrefix + 'monthpicker-small', + + + totalYears: 10, + yearOffset: 5, + monthOffset: 6, + + + + initComponent: function(){ + var me = this; + + me.selectedCls = me.baseCls + '-selected'; + me.addEvents( + + 'cancelclick', + + + 'monthclick', + + + 'monthdblclick', + + + 'okclick', + + + 'select', + + + 'yearclick', + + + 'yeardblclick' + ); + if (me.small) { + me.addCls(me.smallCls); + } + me.setValue(me.value); + me.activeYear = me.getYear(new Date().getFullYear() - 4, -4); + + if (me.showButtons) { + me.okBtn = new Ext.button.Button({ + text: me.okText, + handler: me.onOkClick, + scope: me + }); + me.cancelBtn = new Ext.button.Button({ + text: me.cancelText, + handler: me.onCancelClick, + scope: me + }); + } + + this.callParent(); + }, + + + + beforeRender: function(){ + var me = this, + i = 0, + months = [], + shortName = Ext.Date.getShortMonthName, + monthLen = me.monthOffset, + margin = me.monthMargin, + style = ''; + + if (me.padding && !me.width) { + me.cacheWidth(); + } + + me.callParent(); + + for (; i < monthLen; ++i) { + months.push(shortName(i), shortName(i + monthLen)); + } + + if (Ext.isDefined(margin)) { + style = 'margin: 0 ' + margin + 'px;'; + } + + Ext.apply(me.renderData, { + months: months, + years: me.getYears(), + showButtons: me.showButtons, + monthStyle: style + }); + }, + + cacheWidth: function() { + var me = this, + padding = me.parseBox(me.padding), + widthEl = Ext.getBody().createChild({ + cls: me.baseCls + ' ' + me.borderBoxCls, + style: 'position:absolute;top:-1000px;left:-1000px;', + html: ' ' + }); + + me.self.prototype.width = widthEl.getWidth() + padding.left + padding.right; + widthEl.remove(); + }, + + + + afterRender: function(){ + var me = this, + body = me.bodyEl; + + me.callParent(); + + me.mon(body, 'click', me.onBodyClick, me); + me.mon(body, 'dblclick', me.onBodyClick, me); + + + me.years = body.select('.' + me.baseCls + '-year a'); + me.months = body.select('.' + me.baseCls + '-month a'); + + me.backRepeater = new Ext.util.ClickRepeater(me.prevEl, { + handler: Ext.Function.bind(me.adjustYear, me, [-me.totalYears]) + }); + + me.prevEl.addClsOnOver(me.baseCls + '-yearnav-prev-over'); + me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl, { + handler: Ext.Function.bind(me.adjustYear, me, [me.totalYears]) + }); + me.nextEl.addClsOnOver(me.baseCls + '-yearnav-next-over'); + me.updateBody(); + + if (!Ext.isDefined(me.monthMargin)) { + Ext.picker.Month.prototype.monthMargin = me.calculateMonthMargin(); + } + }, + + calculateMonthMargin: function(){ + + + + + var me = this, + monthEl = me.monthEl, + months = me.months, + first = months.first(), + itemMargin = first.getMargin('l'); + + while (itemMargin && me.getLargest() > me.measureMaxHeight) { + --itemMargin; + months.setStyle('margin', '0 ' + itemMargin + 'px'); + } + return itemMargin; + }, + + getLargest: function(months){ + var largest = 0; + this.months.each(function(item){ + var h = item.getHeight(); + if (h > largest) { + largest = h; + } + }); + return largest; + + }, + + + setValue: function(value){ + var me = this, + active = me.activeYear, + offset = me.monthOffset, + year, + index; + + if (!value) { + me.value = [null, null]; + } else if (Ext.isDate(value)) { + me.value = [value.getMonth(), value.getFullYear()]; + } else { + me.value = [value[0], value[1]]; + } + + if (me.rendered) { + year = me.value[1]; + if (year !== null) { + if ((year < active || year > active + me.yearOffset)) { + me.activeYear = year - me.yearOffset + 1; + } + } + me.updateBody(); + } + + return me; + }, + + + getValue: function(){ + return this.value; + }, + + + hasSelection: function(){ + var value = this.value; + return value[0] !== null && value[1] !== null; + }, + + + getYears: function(){ + var me = this, + offset = me.yearOffset, + start = me.activeYear, + end = start + offset, + i = start, + years = []; + + for (; i < end; ++i) { + years.push(i, i + offset); + } + + return years; + }, + + + updateBody: function(){ + var me = this, + years = me.years, + months = me.months, + yearNumbers = me.getYears(), + cls = me.selectedCls, + value = me.getYear(null), + month = me.value[0], + monthOffset = me.monthOffset, + year, + yearItems, y, yLen, el; + + if (me.rendered) { + years.removeCls(cls); + months.removeCls(cls); + + yearItems = years.elements; + yLen = yearItems.length; + + for (y = 0; y < yLen; y++) { + el = Ext.fly(yearItems[y]); + + year = yearNumbers[y]; + el.dom.innerHTML = year; + if (year == value) { + el.addCls(cls); + } + } + if (month !== null) { + if (month < monthOffset) { + month = month * 2; + } else { + month = (month - monthOffset) * 2 + 1; + } + months.item(month).addCls(cls); + } + } + }, + + + getYear: function(defaultValue, offset) { + var year = this.value[1]; + offset = offset || 0; + return year === null ? defaultValue : year + offset; + }, + + + onBodyClick: function(e, t) { + var me = this, + isDouble = e.type == 'dblclick'; + + if (e.getTarget('.' + me.baseCls + '-month')) { + e.stopEvent(); + me.onMonthClick(t, isDouble); + } else if (e.getTarget('.' + me.baseCls + '-year')) { + e.stopEvent(); + me.onYearClick(t, isDouble); + } + }, + + + adjustYear: function(offset){ + if (typeof offset != 'number') { + offset = this.totalYears; + } + this.activeYear += offset; + this.updateBody(); + }, + + + onOkClick: function(){ + this.fireEvent('okclick', this, this.value); + }, + + + onCancelClick: function(){ + this.fireEvent('cancelclick', this); + }, + + + onMonthClick: function(target, isDouble){ + var me = this; + me.value[0] = me.resolveOffset(me.months.indexOf(target), me.monthOffset); + me.updateBody(); + me.fireEvent('month' + (isDouble ? 'dbl' : '') + 'click', me, me.value); + me.fireEvent('select', me, me.value); + }, + + + onYearClick: function(target, isDouble){ + var me = this; + me.value[1] = me.activeYear + me.resolveOffset(me.years.indexOf(target), me.yearOffset); + me.updateBody(); + me.fireEvent('year' + (isDouble ? 'dbl' : '') + 'click', me, me.value); + me.fireEvent('select', me, me.value); + + }, + + + resolveOffset: function(index, offset){ + if (index % 2 === 0) { + return (index / 2); + } else { + return offset + Math.floor(index / 2); + } + }, + + + + beforeDestroy: function(){ + var me = this; + me.years = me.months = null; + Ext.destroyMembers(me, 'backRepeater', 'nextRepeater', 'okBtn', 'cancelBtn'); + me.callParent(); + }, + + + + finishRenderChildren: function () { + var me = this; + + this.callParent(arguments); + + if (this.showButtons) { + me.okBtn.finishRender(); + me.cancelBtn.finishRender(); + } + }, + + onDestroy: function() { + Ext.destroyMembers(this, 'okBtn', 'cancelBtn'); + this.callParent(); + } + +}); + + + +Ext.define('Ext.picker.Date', { + extend: Ext.Component , + + + + + + + + + + + alias: 'widget.datepicker', + alternateClassName: 'Ext.DatePicker', + + childEls: [ + 'innerEl', 'eventEl', 'prevEl', 'nextEl', 'middleBtnEl', 'footerEl' + ], + + border: true, + + renderTpl: [ + '
', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '{#:this.isEndOfWeek}', + '', + '', + '', + '
', + '
{.:this.firstInitial}
', + '
', + + '', + '
', + '', + '', + '', + '
', + { + firstInitial: function(value) { + return Ext.picker.Date.prototype.getDayInitial(value); + }, + isEndOfWeek: function(value) { + + + value--; + var end = value % 7 === 0 && value !== 0; + return end ? '' : ''; + }, + renderTodayBtn: function(values, out) { + Ext.DomHelper.generateMarkup(values.$comp.todayBtn.getRenderTree(), out); + }, + renderMonthBtn: function(values, out) { + Ext.DomHelper.generateMarkup(values.$comp.monthBtn.getRenderTree(), out); + } + } + ], + + + + todayText : 'Today', + + + + + ariaTitle: 'Date Picker: {0}', + + + + + ariaTitleDateFormat: 'F d, Y', + + + + + + + + + todayTip : '{0} (Spacebar)', + + + + + minText : 'This date is before the minimum date', + + + + + maxText : 'This date is after the maximum date', + + + + + + + disabledDaysText : 'Disabled', + + + + + disabledDatesText : 'Disabled', + + + + + + + + + nextText : 'Next Month (Control+Right)', + + + + + prevText : 'Previous Month (Control+Left)', + + + + + monthYearText : 'Choose a month (Control+Up/Down to move years)', + + + + + monthYearFormat: 'F Y', + + + + + startDay : 0, + + + + + showToday : true, + + + + + + + + + + + + + + disableAnim: false, + + + baseCls: Ext.baseCSSPrefix + 'datepicker', + + + + + + + + longDayFormat: 'F d, Y', + + + + + + focusOnShow: false, + + + + focusOnSelect: true, + + + + initHour: 12, + + numDays: 42, + + + initComponent : function() { + var me = this, + clearTime = Ext.Date.clearTime; + + me.selectedCls = me.baseCls + '-selected'; + me.disabledCellCls = me.baseCls + '-disabled'; + me.prevCls = me.baseCls + '-prevday'; + me.activeCls = me.baseCls + '-active'; + me.cellCls = me.baseCls + '-cell'; + me.nextCls = me.baseCls + '-prevday'; + me.todayCls = me.baseCls + '-today'; + + + if (!me.format) { + me.format = Ext.Date.defaultFormat; + } + if (!me.dayNames) { + me.dayNames = Ext.Date.dayNames; + } + me.dayNames = me.dayNames.slice(me.startDay).concat(me.dayNames.slice(0, me.startDay)); + + me.callParent(); + + me.value = me.value ? + clearTime(me.value, true) : clearTime(new Date()); + + me.addEvents( + + 'select' + ); + + me.initDisabledDays(); + }, + + + getRefOwner: function() { + return this.pickerField || this.callParent(); + }, + + getRefItems: function() { + var results = [], + monthBtn = this.monthBtn, + todayBtn = this.todayBtn; + + if (monthBtn) { + results.push(monthBtn); + } + + if (todayBtn) { + results.push(todayBtn); + } + return results; + }, + + beforeRender: function () { + + var me = this, + days = new Array(me.numDays), + today = Ext.Date.format(new Date(), me.format); + + if (me.padding && !me.width) { + me.cacheWidth(); + } + + me.monthBtn = new Ext.button.Split({ + ownerCt: me, + ownerLayout: me.getComponentLayout(), + text: '', + tooltip: me.monthYearText, + listeners: { + click: me.doShowMonthPicker, + arrowclick: me.doShowMonthPicker, + scope: me + } + }); + + if (me.showToday) { + me.todayBtn = new Ext.button.Button({ + ownerCt: me, + ownerLayout: me.getComponentLayout(), + text: Ext.String.format(me.todayText, today), + tooltip: Ext.String.format(me.todayTip, today), + tooltipType: 'title', + handler: me.selectToday, + scope: me + }); + } + + me.callParent(); + + Ext.applyIf(me, { + renderData: {} + }); + + Ext.apply(me.renderData, { + dayNames: me.dayNames, + showToday: me.showToday, + prevText: me.prevText, + nextText: me.nextText, + days: days + }); + + me.protoEl.unselectable(); + }, + + cacheWidth: function() { + var me = this, + padding = me.parseBox(me.padding), + widthEl = Ext.getBody().createChild({ + cls: me.baseCls + ' ' + me.borderBoxCls, + style: 'position:absolute;top:-1000px;left:-1000px;' + }); + + me.self.prototype.width = widthEl.getWidth() + padding.left + padding.right; + widthEl.remove(); + }, + + + + finishRenderChildren: function () { + var me = this; + + me.callParent(); + me.monthBtn.finishRender(); + if (me.showToday) { + me.todayBtn.finishRender(); + } + }, + + + + onRender : function(container, position){ + var me = this; + + me.callParent(arguments); + + me.cells = me.eventEl.select('tbody td'); + me.textNodes = me.eventEl.query('tbody td a'); + + me.mon(me.eventEl, { + scope: me, + mousewheel: me.handleMouseWheel, + click: { + fn: me.handleDateClick, + delegate: 'a.' + me.baseCls + '-date' + } + }); + + }, + + + + initEvents: function(){ + var me = this, + eDate = Ext.Date, + day = eDate.DAY; + + me.callParent(); + + me.prevRepeater = new Ext.util.ClickRepeater(me.prevEl, { + handler: me.showPrevMonth, + scope: me, + preventDefault: true, + stopDefault: true + }); + + me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl, { + handler: me.showNextMonth, + scope: me, + preventDefault:true, + stopDefault:true + }); + + me.keyNav = new Ext.util.KeyNav(me.eventEl, Ext.apply({ + scope: me, + left : function(e){ + if(e.ctrlKey){ + me.showPrevMonth(); + }else{ + me.update(eDate.add(me.activeDate, day, -1)); + } + }, + + right : function(e){ + if(e.ctrlKey){ + me.showNextMonth(); + }else{ + me.update(eDate.add(me.activeDate, day, 1)); + } + }, + + up : function(e){ + if(e.ctrlKey){ + me.showNextYear(); + }else{ + me.update(eDate.add(me.activeDate, day, -7)); + } + }, + + down : function(e){ + if(e.ctrlKey){ + me.showPrevYear(); + }else{ + me.update(eDate.add(me.activeDate, day, 7)); + } + }, + + pageUp:function (e) { + if (e.altKey) { + me.showPrevYear(); + } else { + me.showPrevMonth(); + } + }, + + pageDown:function (e) { + if (e.altKey) { + me.showNextYear(); + } else { + me.showNextMonth(); + } + }, + + tab:function (e) { + me.doCancelFieldFocus = true; + me.handleTabClick(e); + delete me.doCancelFieldFocus; + return true; + }, + + enter : function(e){ + e.stopPropagation(); + return true; + }, + + + + home:function (e) { + me.update(eDate.getFirstDateOfMonth(me.activeDate)); + }, + + end:function (e) { + me.update(eDate.getLastDateOfMonth(me.activeDate)); + } + }, me.keyNavConfig)); + + if (me.showToday) { + me.todayKeyListener = me.eventEl.addKeyListener(Ext.EventObject.SPACE, me.selectToday, me); + } + me.update(me.value); + }, + + handleTabClick:function (e) { + var me = this, + t = me.getSelectedDate(me.activeDate), + handler = me.handler; + + + if (!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)) { + + + + + if (me.pickerField) { + e.stopEvent(); + } + + + me.doCancelFocus = true; + me.setValue(new Date(t.dateValue)); + delete me.doCancelFocus; + me.fireEvent('select', me, me.value); + if (handler) { + handler.call(me.scope || me, me, me.value); + } + me.onSelect(); + } + }, + + getSelectedDate:function (date) { + var me = this, + t = date.getTime(), + cells = me.cells, + cls = me.selectedCls, + cellItems = cells.elements, + c, + cLen = cellItems.length, + cell; + + cells.removeCls(cls); + + for (c = 0; c < cLen; c++) { + cell = Ext.fly(cellItems[c]); + + if (cell.dom.firstChild.dateValue == t) { + return cell.dom.firstChild; + } + } + return null; + }, + + + initDisabledDays : function(){ + var me = this, + dd = me.disabledDates, + re = '(?:', + len, + d, dLen, dI; + + if(!me.disabledDatesRE && dd){ + len = dd.length - 1; + + dLen = dd.length; + + for (d = 0; d < dLen; d++) { + dI = dd[d]; + + re += Ext.isDate(dI) ? '^' + Ext.String.escapeRegex(Ext.Date.dateFormat(dI, me.format)) + '$' : dI; + if (d != len) { + re += '|'; + } + } + + me.disabledDatesRE = new RegExp(re + ')'); + } + }, + + + setDisabledDates : function(dd){ + var me = this; + + if(Ext.isArray(dd)){ + me.disabledDates = dd; + me.disabledDatesRE = null; + }else{ + me.disabledDatesRE = dd; + } + me.initDisabledDays(); + me.update(me.value, true); + return me; + }, + + + setDisabledDays : function(dd){ + this.disabledDays = dd; + return this.update(this.value, true); + }, + + + setMinDate : function(dt){ + this.minDate = dt; + return this.update(this.value, true); + }, + + + setMaxDate : function(dt){ + this.maxDate = dt; + return this.update(this.value, true); + }, + + + setValue : function(value){ + this.value = Ext.Date.clearTime(value, true); + return this.update(this.value); + }, + + + getValue : function(){ + return this.value; + }, + + + + getDayInitial: function(value){ + return value.substr(0,1); + }, + + + + focus : function(){ + this.update(this.activeDate); + }, + + + + onEnable: function(){ + this.callParent(); + this.setDisabledStatus(false); + this.update(this.activeDate); + + }, + + + + onDisable : function(){ + this.callParent(); + this.setDisabledStatus(true); + }, + + + setDisabledStatus : function(disabled){ + var me = this; + + me.keyNav.setDisabled(disabled); + me.prevRepeater.setDisabled(disabled); + me.nextRepeater.setDisabled(disabled); + if (me.showToday) { + me.todayKeyListener.setDisabled(disabled); + me.todayBtn.setDisabled(disabled); + } + }, + + + getActive: function(){ + return this.activeDate || this.value; + }, + + + runAnimation: function(isHide){ + var picker = this.monthPicker, + options = { + duration: 200, + callback: function() { + picker.setVisible(!isHide); + } + }, visible = picker.isVisible(); + + if (isHide) { + picker.el.slideOut('t', options); + } else { + picker.el.slideIn('t', options); + } + }, + + + hideMonthPicker : function(animate){ + var me = this, + picker = me.monthPicker; + + if (picker && picker.isVisible()) { + if (me.shouldAnimate(animate)) { + me.runAnimation(true); + } else { + picker.hide(); + } + } + return me; + }, + + doShowMonthPicker: function(){ + + + this.showMonthPicker(); + }, + + doHideMonthPicker: function() { + + + this.hideMonthPicker(); + }, + + + showMonthPicker : function(animate){ + var me = this, + el = me.el, + picker; + + if (me.rendered && !me.disabled) { + picker = me.createMonthPicker(); + if (!picker.isVisible()) { + picker.setValue(me.getActive()); + picker.setSize(el.getSize()); + picker.setPosition(-el.getBorderWidth('l'), -el.getBorderWidth('t')); + if (me.shouldAnimate(animate)) { + me.runAnimation(false); + } else { + picker.show(); + } + } + } + return me; + }, + + + shouldAnimate: function(animate){ + return Ext.isDefined(animate) ? animate : !this.disableAnim; + }, + + + createMonthPicker: function(){ + var me = this, + picker = me.monthPicker; + + if (!picker) { + me.monthPicker = picker = new Ext.picker.Month({ + renderTo: me.el, + floating: true, + padding: me.padding, + shadow: false, + small: me.showToday === false, + listeners: { + scope: me, + cancelclick: me.onCancelClick, + okclick: me.onOkClick, + yeardblclick: me.onOkClick, + monthdblclick: me.onOkClick + } + }); + if (!me.disableAnim) { + + picker.el.setStyle('display', 'none'); + } + picker.hide(); + me.on('beforehide', me.doHideMonthPicker, me); + } + return picker; + }, + + + onOkClick: function(picker, value){ + var me = this, + month = value[0], + year = value[1], + date = new Date(year, month, me.getActive().getDate()); + + if (date.getMonth() !== month) { + + date = Ext.Date.getLastDateOfMonth(new Date(year, month, 1)); + } + me.setValue(date); + me.hideMonthPicker(); + }, + + + onCancelClick: function(){ + + this.selectedUpdate(this.activeDate); + this.hideMonthPicker(); + }, + + + showPrevMonth : function(e){ + return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH, -1)); + }, + + + showNextMonth : function(e){ + return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH, 1)); + }, + + + showPrevYear : function(){ + return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR, -1)); + }, + + + showNextYear : function(){ + return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR, 1)); + }, + + + handleMouseWheel : function(e){ + e.stopEvent(); + if(!this.disabled){ + var delta = e.getWheelDelta(); + if(delta > 0){ + this.showPrevMonth(); + } else if(delta < 0){ + this.showNextMonth(); + } + } + }, + + + handleDateClick : function(e, t){ + var me = this, + handler = me.handler; + + e.stopEvent(); + if(!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)){ + me.doCancelFocus = me.focusOnSelect === false; + me.setValue(new Date(t.dateValue)); + delete me.doCancelFocus; + me.fireEvent('select', me, me.value); + if (handler) { + handler.call(me.scope || me, me, me.value); + } + + + + + me.onSelect(); + } + }, + + + onSelect: function() { + if (this.hideOnSelect) { + this.hide(); + } + }, + + + selectToday : function(){ + var me = this, + btn = me.todayBtn, + handler = me.handler; + + if(btn && !btn.disabled){ + me.setValue(Ext.Date.clearTime(new Date())); + me.fireEvent('select', me, me.value); + if (handler) { + handler.call(me.scope || me, me, me.value); + } + me.onSelect(); + } + return me; + }, + + + selectedUpdate: function(date){ + var me = this, + t = date.getTime(), + cells = me.cells, + cls = me.selectedCls, + cellItems = cells.elements, + c, + cLen = cellItems.length, + cell; + + cells.removeCls(cls); + + for (c = 0; c < cLen; c++) { + cell = Ext.fly(cellItems[c]); + + if (cell.dom.firstChild.dateValue == t) { + me.fireEvent('highlightitem', me, cell); + cell.addCls(cls); + + if(me.isVisible() && !me.doCancelFocus){ + Ext.fly(cell.dom.firstChild).focus(50); + } + + break; + } + } + }, + + + fullUpdate: function(date){ + var me = this, + cells = me.cells.elements, + textNodes = me.textNodes, + disabledCls = me.disabledCellCls, + eDate = Ext.Date, + i = 0, + extraDays = 0, + visible = me.isVisible(), + newDate = +eDate.clearTime(date, true), + today = +eDate.clearTime(new Date()), + min = me.minDate ? eDate.clearTime(me.minDate, true) : Number.NEGATIVE_INFINITY, + max = me.maxDate ? eDate.clearTime(me.maxDate, true) : Number.POSITIVE_INFINITY, + ddMatch = me.disabledDatesRE, + ddText = me.disabledDatesText, + ddays = me.disabledDays ? me.disabledDays.join('') : false, + ddaysText = me.disabledDaysText, + format = me.format, + days = eDate.getDaysInMonth(date), + firstOfMonth = eDate.getFirstDateOfMonth(date), + startingPos = firstOfMonth.getDay() - me.startDay, + previousMonth = eDate.add(date, eDate.MONTH, -1), + longDayFormat = me.longDayFormat, + prevStart, + current, + disableToday, + tempDate, + setCellClass, + html, + cls, + formatValue, + value; + + if (startingPos < 0) { + startingPos += 7; + } + + days += startingPos; + prevStart = eDate.getDaysInMonth(previousMonth) - startingPos; + current = new Date(previousMonth.getFullYear(), previousMonth.getMonth(), prevStart, me.initHour); + + if (me.showToday) { + tempDate = eDate.clearTime(new Date()); + disableToday = (tempDate < min || tempDate > max || + (ddMatch && format && ddMatch.test(eDate.dateFormat(tempDate, format))) || + (ddays && ddays.indexOf(tempDate.getDay()) != -1)); + + if (!me.disabled) { + me.todayBtn.setDisabled(disableToday); + me.todayKeyListener.setDisabled(disableToday); + } + } + + setCellClass = function(cell, cls){ + value = +eDate.clearTime(current, true); + cell.title = eDate.format(current, longDayFormat); + + cell.firstChild.dateValue = value; + if(value == today){ + cls += ' ' + me.todayCls; + cell.title = me.todayText; + + + me.todayElSpan = Ext.DomHelper.append(cell.firstChild, { + tag:'span', + cls: Ext.baseCSSPrefix + 'hide-clip', + html:me.todayText + }, true); + } + if(value == newDate) { + cls += ' ' + me.selectedCls; + me.fireEvent('highlightitem', me, cell); + if (visible && me.floating) { + Ext.fly(cell.firstChild).focus(50); + } + } + + if (value < min) { + cls += ' ' + disabledCls; + cell.title = me.minText; + } + else if (value > max) { + cls += ' ' + disabledCls; + cell.title = me.maxText; + } + else if (ddays && ddays.indexOf(current.getDay()) !== -1){ + cell.title = ddaysText; + cls += ' ' + disabledCls; + } + else if (ddMatch && format){ + formatValue = eDate.dateFormat(current, format); + if(ddMatch.test(formatValue)){ + cell.title = ddText.replace('%0', formatValue); + cls += ' ' + disabledCls; + } + } + cell.className = cls + ' ' + me.cellCls; + }; + + for(; i < me.numDays; ++i) { + if (i < startingPos) { + html = (++prevStart); + cls = me.prevCls; + } else if (i >= days) { + html = (++extraDays); + cls = me.nextCls; + } else { + html = i - startingPos + 1; + cls = me.activeCls; + } + textNodes[i].innerHTML = html; + current.setDate(current.getDate() + 1); + setCellClass(cells[i], cls); + } + + me.monthBtn.setText(Ext.Date.format(date, me.monthYearFormat)); + }, + + + update : function(date, forceRefresh){ + var me = this, + active = me.activeDate; + + if (me.rendered) { + me.activeDate = date; + if(!forceRefresh && active && me.el && active.getMonth() == date.getMonth() && active.getFullYear() == date.getFullYear()){ + me.selectedUpdate(date, active); + } else { + me.fullUpdate(date, active); + } + } + return me; + }, + + + + beforeDestroy : function() { + var me = this; + + if (me.rendered) { + Ext.destroy( + me.todayKeyListener, + me.keyNav, + me.monthPicker, + me.monthBtn, + me.nextRepeater, + me.prevRepeater, + me.todayBtn + ); + delete me.textNodes; + delete me.cells.elements; + } + me.callParent(); + }, + + + + onShow: function() { + this.callParent(arguments); + if (this.focusOnShow) { + this.focus(); + } + } +}); + + + +Ext.define('Ext.form.field.Date', { + extend: Ext.form.field.Picker , + alias: 'widget.datefield', + + alternateClassName: ['Ext.form.DateField', 'Ext.form.Date'], + + + + format : "m/d/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", + + + + disabledDatesText : "Disabled", + + + + minText : "The date in this field must be equal to or after {0}", + + + + maxText : "The date in this field must be equal to or before {0}", + + + + invalidText : "{0} is not a valid date - it must be in the format {1}", + + + triggerCls : Ext.baseCSSPrefix + 'form-date-trigger', + + showToday : true, + + + + + + + + + useStrict: undefined, + + + + initTime: '12', + + initTimeFormat: 'H', + + matchFieldWidth: false, + + + startDay: 0, + + + initComponent : function(){ + var me = this, + isString = Ext.isString, + min, max; + + min = me.minValue; + max = me.maxValue; + if(isString(min)){ + me.minValue = me.parseDate(min); + } + if(isString(max)){ + me.maxValue = me.parseDate(max); + } + me.disabledDatesRE = null; + me.initDisabledDays(); + + me.callParent(); + }, + + initValue: function() { + var me = this, + value = me.value; + + + if (Ext.isString(value)) { + me.value = me.rawToValue(value); + } + + me.callParent(); + }, + + + initDisabledDays : function(){ + if(this.disabledDates){ + var dd = this.disabledDates, + len = dd.length - 1, + re = "(?:", + d, + dLen = dd.length, + date; + + for (d = 0; d < dLen; d++) { + date = dd[d]; + + re += Ext.isDate(date) ? '^' + Ext.String.escapeRegex(date.dateFormat(this.format)) + '$' : date; + if (d !== len) { + re += '|'; + } + } + + this.disabledDatesRE = new RegExp(re + ')'); + } + }, + + + setDisabledDates : function(dd){ + var me = this, + picker = me.picker; + + me.disabledDates = dd; + me.initDisabledDays(); + if (picker) { + picker.setDisabledDates(me.disabledDatesRE); + } + }, + + + setDisabledDays : function(dd){ + var picker = this.picker; + + this.disabledDays = dd; + if (picker) { + picker.setDisabledDays(dd); + } + }, + + + setMinValue : function(dt){ + var me = this, + picker = me.picker, + minValue = (Ext.isString(dt) ? me.parseDate(dt) : dt); + + me.minValue = minValue; + if (picker) { + picker.minText = Ext.String.format(me.minText, me.formatDate(me.minValue)); + picker.setMinDate(minValue); + } + }, + + + setMaxValue : function(dt){ + var me = this, + picker = me.picker, + maxValue = (Ext.isString(dt) ? me.parseDate(dt) : dt); + + me.maxValue = maxValue; + if (picker) { + picker.maxText = Ext.String.format(me.maxText, me.formatDate(me.maxValue)); + picker.setMaxDate(maxValue); + } + }, + + + getErrors: function(value) { + var me = this, + format = Ext.String.format, + clearTime = Ext.Date.clearTime, + errors = me.callParent(arguments), + disabledDays = me.disabledDays, + disabledDatesRE = me.disabledDatesRE, + minValue = me.minValue, + maxValue = me.maxValue, + len = disabledDays ? disabledDays.length : 0, + i = 0, + svalue, + fvalue, + day, + time; + + value = me.formatDate(value || me.processRawValue(me.getRawValue())); + + if (value === null || value.length < 1) { + return errors; + } + + svalue = value; + value = me.parseDate(value); + if (!value) { + errors.push(format(me.invalidText, svalue, Ext.Date.unescapeFormat(me.format))); + return errors; + } + + time = value.getTime(); + if (minValue && time < clearTime(minValue).getTime()) { + errors.push(format(me.minText, me.formatDate(minValue))); + } + + if (maxValue && time > clearTime(maxValue).getTime()) { + errors.push(format(me.maxText, me.formatDate(maxValue))); + } + + if (disabledDays) { + day = value.getDay(); + + for(; i < len; i++) { + if (day === disabledDays[i]) { + errors.push(me.disabledDaysText); + break; + } + } + } + + fvalue = me.formatDate(value); + if (disabledDatesRE && disabledDatesRE.test(fvalue)) { + errors.push(format(me.disabledDatesText, fvalue)); + } + + return errors; + }, + + rawToValue: function(rawValue) { + return this.parseDate(rawValue) || rawValue || null; + }, + + valueToRaw: function(value) { + return this.formatDate(this.parseDate(value)); + }, + + + + + safeParse : function(value, format) { + var me = this, + utilDate = Ext.Date, + result = null, + strict = me.useStrict, + parsedDate; + + if (utilDate.formatContainsHourInfo(format)) { + + result = utilDate.parse(value, format, strict); + } else { + + parsedDate = utilDate.parse(value + ' ' + me.initTime, format + ' ' + me.initTimeFormat, strict); + if (parsedDate) { + result = utilDate.clearTime(parsedDate); + } + } + return result; + }, + + + getSubmitValue: function() { + var format = this.submitFormat || this.format, + value = this.getValue(); + + return value ? Ext.Date.format(value, format) : ''; + }, + + + parseDate : function(value) { + if(!value || Ext.isDate(value)){ + return value; + } + + var me = this, + val = me.safeParse(value, me.format), + altFormats = me.altFormats, + altFormatsArray = me.altFormatsArray, + i = 0, + len; + + if (!val && altFormats) { + altFormatsArray = altFormatsArray || altFormats.split('|'); + len = altFormatsArray.length; + for (; i < len && !val; ++i) { + val = me.safeParse(value, altFormatsArray[i]); + } + } + return val; + }, + + + formatDate: function(date){ + return Ext.isDate(date) ? Ext.Date.dateFormat(date, this.format) : date; + }, + + createPicker: function() { + var me = this, + format = Ext.String.format; + + + + + return new Ext.picker.Date({ + pickerField: me, + renderTo: document.body, + floating: true, + hidden: true, + focusOnShow: true, + minDate: me.minValue, + maxDate: me.maxValue, + disabledDatesRE: me.disabledDatesRE, + disabledDatesText: me.disabledDatesText, + disabledDays: me.disabledDays, + disabledDaysText: me.disabledDaysText, + format: me.format, + showToday: me.showToday, + startDay: me.startDay, + minText: format(me.minText, me.formatDate(me.minValue)), + maxText: format(me.maxText, me.formatDate(me.maxValue)), + listeners: { + scope: me, + select: me.onSelect + }, + keyNavConfig: { + esc: function() { + me.collapse(); + me.focus(); + } + } + }); + }, + + onDownArrow: function(e) { + this.callParent(arguments); + if (this.isExpanded) { + this.getPicker().focus(); + } + }, + + onSelect: function(m, d) { + var me = this; + + me.setValue(d); + me.fireEvent('select', me, d); + me.collapse(); + me.focus(false, 50); + }, + + + onExpand: function() { + var value = this.getValue(); + this.picker.setValue(Ext.isDate(value) ? value : new Date()); + }, + + + beforeBlur: function(){ + var me = this, + v = me.rawToValue(me.getRawValue()); + + if (Ext.isDate(v)) { + me.setValue(v); + } + } + + + + + +}); + + +Ext.define('ExtThemeNeptune.picker.Month', { + override: 'Ext.picker.Month', + + + + + + + + + + + measureMaxHeight: 36 +}); + + + +Ext.define('Ext.form.field.FileButton', { + extend: Ext.button.Button , + alias: 'widget.filebutton', + + childEls: [ + 'btnEl', 'btnWrap', 'btnInnerEl', 'btnIconEl', 'fileInputEl' + ], + + inputCls: Ext.baseCSSPrefix + 'form-file-input', + + cls: Ext.baseCSSPrefix + 'form-file-btn', + + preventDefault: false, + + autoEl: { + tag: 'div', + unselectable: 'on' + }, + + renderTpl: [ + ' {splitCls}
', + '{childElCls}" unselectable="on">', + '', + '', + '{text}', + '', + 'background-image:url({iconUrl});
', + 'font-family:{glyphFontFamily};">', + '&#{glyph}; ', + '', + '', + '', + '' + ], + + getTemplateArgs: function(){ + var args = this.callParent(); + args.inputCls = this.inputCls; + args.inputName = this.inputName; + args.tabIndex = this.ownerCt.tabIndex; + return args; + }, + + afterRender: function(){ + var me = this; + me.callParent(arguments); + me.fileInputEl.on('change', me.fireChange, me); + }, + + fireChange: function(e){ + this.fireEvent('change', this, e, this.fileInputEl.dom.value); + }, + + + createFileInput : function(isTemporary) { + var me = this; + me.fileInputEl = me.el.createChild({ + name: me.inputName, + id: !isTemporary ? me.id + '-fileInputEl' : undefined, + cls: me.inputCls, + tag: 'input', + type: 'file', + size: 1, + role: 'button' + }); + me.fileInputEl.on('change', me.fireChange, me); + }, + + reset: function(remove){ + if (remove) { + this.fileInputEl.remove(); + } + this.createFileInput(!remove); + }, + + restoreInput: function(el){ + this.fileInputEl.remove(); + el = Ext.get(el); + this.el.appendChild(el); + this.fileInputEl = el; + }, + + onDisable: function(){ + this.callParent(); + this.fileInputEl.dom.disabled = true; + }, + + onEnable : function() { + this.callParent(); + this.fileInputEl.dom.disabled = false; + } +}); + + + +Ext.define('Ext.form.field.File', { + extend: Ext.form.field.Trigger , + alias: ['widget.filefield', 'widget.fileuploadfield'], + alternateClassName: ['Ext.form.FileUploadField', 'Ext.ux.form.FileUploadField', 'Ext.form.File'], + + + + + + + buttonText: 'Browse...', + + + + buttonOnly: false, + + + buttonMargin: 3, + + + clearOnSubmit: true, + + + + + + + + + + + + extraFieldBodyCls: Ext.baseCSSPrefix + 'form-file-wrap', + + + readOnly: true, + + submitValue: false, + + + triggerNoEditCls: '', + + + componentLayout: 'triggerfield', + + + childEls: ['browseButtonWrap'], + + + onRender: function() { + var me = this, + id = me.id, + inputEl; + + me.callParent(arguments); + + inputEl = me.inputEl; + inputEl.dom.name = ''; + + + + + me.button = new Ext.form.field.FileButton(Ext.apply({ + renderTo: id + '-browseButtonWrap', + ownerCt: me, + ownerLayout: me.componentLayout, + id: id + '-button', + ui: me.ui, + disabled: me.disabled, + text: me.buttonText, + style: me.buttonOnly ? '' : me.getButtonMarginProp() + me.buttonMargin + 'px', + inputName: me.getName(), + listeners: { + scope: me, + change: me.onFileChange + } + }, me.buttonConfig)); + me.fileInputEl = me.button.fileInputEl; + + if (me.buttonOnly) { + me.inputCell.setDisplayed(false); + me.shrinkWrap = 3; + } + + + me.browseButtonWrap.dom.style.width = (me.browseButtonWrap.dom.lastChild.offsetWidth + me.button.getEl().getMargin('lr')) + 'px'; + if (Ext.isIE) { + me.button.getEl().repaint(); + } + }, + + + getTriggerMarkup: function() { + return ''; + }, + + + onFileChange: function(button, e, value) { + this.duringFileSelect = true; + Ext.form.field.File.superclass.setValue.call(this, value); + delete this.duringFileSelect; + }, + + didValueChange: function(){ + + + + + return !!this.duringFileSelect; + }, + + + setValue: Ext.emptyFn, + + reset : function(){ + var me = this, + clear = me.clearOnSubmit; + if (me.rendered) { + me.button.reset(clear); + me.fileInputEl = me.button.fileInputEl; + if (clear) { + me.inputEl.dom.value = ''; + + Ext.form.field.File.superclass.setValue.call(this, null); + } + } + me.callParent(); + }, + + onShow: function(){ + this.callParent(); + + + this.button.updateLayout(); + }, + + onDisable: function(){ + this.callParent(); + this.button.disable(); + }, + + onEnable: function(){ + this.callParent(); + this.button.enable(); + }, + + isFileUpload: function() { + return true; + }, + + extractFileInput: function() { + var me = this, + fileInput; + + if (me.rendered) { + fileInput = me.button.fileInputEl.dom; + me.reset(); + } else { + + + fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.className = Ext.baseCSSPrefix + 'hide-display'; + fileInput.name = me.getName(); + } + return fileInput; + }, + + restoreInput: function(el) { + + + if (this.rendered) { + var button = this.button; + button.restoreInput(el); + this.fileInputEl = button.fileInputEl; + } + }, + + onDestroy: function(){ + Ext.destroyMembers(this, 'button'); + delete this.fileInputEl; + this.callParent(); + }, + + getButtonMarginProp: function() { + return 'margin-left:'; + } +}); + + + +Ext.define('Ext.form.field.Hidden', { + extend: Ext.form.field.Base , + alias: ['widget.hiddenfield', 'widget.hidden'], + alternateClassName: 'Ext.form.Hidden', + + + inputType : 'hidden', + hideLabel: true, + hidden: true, + + ariaRole: 'presentation', + + initComponent: function() { + this.formItemCls += '-hidden'; + this.callParent(); + }, + + + isEqual: function(value1, value2) { + return this.isEqualAsString(value1, value2); + }, + + + initEvents: Ext.emptyFn, + setSize : Ext.emptyFn, + setWidth : Ext.emptyFn, + setHeight : Ext.emptyFn, + setPosition : Ext.emptyFn, + setPagePosition : Ext.emptyFn, + markInvalid : Ext.emptyFn, + clearInvalid : Ext.emptyFn +}); + + + +Ext.define('Ext.picker.Color', { + extend: Ext.Component , + + alias: 'widget.colorpicker', + alternateClassName: 'Ext.ColorPalette', + + + componentCls : Ext.baseCSSPrefix + 'color-picker', + + + selectedCls: Ext.baseCSSPrefix + 'color-picker-selected', + + + itemCls: Ext.baseCSSPrefix + 'color-picker-item', + + + value : null, + + + clickEvent :'click', + + + allowReselect : false, + + + colors : [ + '000000', '993300', '333300', '003300', '003366', '000080', '333399', '333333', + '800000', 'FF6600', '808000', '008000', '008080', '0000FF', '666699', '808080', + 'FF0000', 'FF9900', '99CC00', '339966', '33CCCC', '3366FF', '800080', '969696', + 'FF00FF', 'FFCC00', 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0', + 'FF99CC', 'FFCC99', 'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF' + ], + + + + + + colorRe: /(?:^|\s)color-(.{6})(?:\s|$)/, + + renderTpl: [ + '', + '', + ' ', + '', + '' + ], + + + initComponent : function(){ + var me = this; + + me.callParent(arguments); + me.addEvents( + + 'select' + ); + + if (me.handler) { + me.on('select', me.handler, me.scope, true); + } + }, + + + + initRenderData : function(){ + var me = this; + return Ext.apply(me.callParent(), { + itemCls: me.itemCls, + colors: me.colors + }); + }, + + onRender : function(){ + var me = this, + clickEvent = me.clickEvent; + + me.callParent(arguments); + + me.mon(me.el, clickEvent, me.handleClick, me, {delegate: 'a'}); + + if(clickEvent != 'click'){ + me.mon(me.el, 'click', Ext.emptyFn, me, {delegate: 'a', stopEvent: true}); + } + }, + + + afterRender : function(){ + var me = this, + value; + + me.callParent(arguments); + if (me.value) { + value = me.value; + me.value = null; + me.select(value, true); + } + }, + + + handleClick : function(event, target){ + var me = this, + color; + + event.stopEvent(); + if (!me.disabled) { + color = target.className.match(me.colorRe)[1]; + me.select(color.toUpperCase()); + } + }, + + + select : function(color, suppressEvent){ + + var me = this, + selectedCls = me.selectedCls, + value = me.value, + el; + + color = color.replace('#', ''); + if (!me.rendered) { + me.value = color; + return; + } + + + if (color != value || me.allowReselect) { + el = me.el; + + if (me.value) { + el.down('a.color-' + value).removeCls(selectedCls); + } + el.down('a.color-' + color).addCls(selectedCls); + me.value = color; + if (suppressEvent !== true) { + me.fireEvent('select', me, color); + } + } + }, + + + clear: function(){ + var me = this, + value = me.value, + el; + + if (value && me.rendered) { + el = me.el.down('a.color-' + value); + el.removeCls(me.selectedCls); + } + me.value = null; + }, + + + getValue: function(){ + return this.value || null; + } +}); + + + +Ext.define('Ext.layout.component.field.HtmlEditor', { + extend: Ext.layout.component.field.FieldContainer , + alias: ['layout.htmleditor'], + + type: 'htmleditor', + + naturalHeight: 150, + naturalWidth: 300, + + beginLayout: function(ownerContext) { + var owner = this.owner, + dom; + + + + + if (Ext.isGecko) { + dom = owner.textareaEl.dom; + this.lastValue = dom.value; + dom.value = ''; + } + this.callParent(arguments); + + ownerContext.toolbarContext = ownerContext.context.getCmp(owner.toolbar); + ownerContext.inputCmpContext = ownerContext.context.getCmp(owner.inputCmp); + ownerContext.textAreaContext = ownerContext.getEl('textareaEl'); + ownerContext.iframeContext = ownerContext.getEl('iframeEl'); + }, + + beginLayoutCycle: function(ownerContext) { + var me = this, + widthModel = ownerContext.widthModel, + heightModel = ownerContext.heightModel, + owner = me.owner, + iframeEl = owner.iframeEl, + textareaEl = owner.textareaEl; + + me.callParent(arguments); + if (widthModel.shrinkWrap) { + iframeEl.setStyle('width', ''); + textareaEl.setStyle('width', ''); + } else if (widthModel.natural) { + ownerContext.bodyCellContext.setWidth(me.naturalWidth); + } + + if (heightModel.natural || heightModel.shrinkWrap) { + iframeEl.setHeight(me.naturalHeight); + textareaEl.setHeight(me.naturalHeight); + } + }, + + finishedLayout: function(){ + var owner = this.owner; + + this.callParent(arguments); + + + if (Ext.isIE9m && Ext.isIEQuirks) { + owner.el.repaint(); + } + if (Ext.isGecko) { + owner.textareaEl.dom.value = this.lastValue; + } + }, + + publishOwnerWidth: function(ownerContext, width){ + this.callParent(arguments); + width -= ownerContext.inputCmpContext.getBorderInfo().width; + ownerContext.textAreaContext.setWidth(width); + ownerContext.iframeContext.setWidth(width); + }, + + + + + + + + publishInnerWidth: function(ownerContext, width){ + var border = ownerContext.inputCmpContext.getBorderInfo().width, + ieBug = Ext.isStrict && Ext.isIE8m, + natural = ownerContext.widthModel.natural; + + this.callParent(arguments); + width = ownerContext.bodyCellContext.props.width - border; + if (natural) { + if (ieBug) { + width -= 2; + } + ownerContext.textAreaContext.setWidth(width); + ownerContext.iframeContext.setWidth(width); + } else if (ieBug) { + ownerContext.textAreaContext.setWidth(width); + } + }, + + publishInnerHeight: function (ownerContext, height) { + var toolbarHeight = ownerContext.toolbarContext.getProp('height'), + sourceEdit = this.owner.sourceEditMode; + + this.callParent(arguments); + height = ownerContext.bodyCellContext.props.height; + + if (toolbarHeight !== undefined) { + height -= toolbarHeight + ownerContext.inputCmpContext.getFrameInfo().height; + if (Ext.isIE8 && Ext.isStrict) { + height -= 2; + } else if (Ext.isIEQuirks && (Ext.isIE8 || Ext.isIE9)) { + height -= 4; + } + ownerContext.iframeContext.setHeight(height); + ownerContext.textAreaContext.setHeight(height); + } else { + this.done = false; + } + } + +}); + + + +Ext.define('Ext.form.field.HtmlEditor', { + extend: Ext.form.FieldContainer , + mixins: { + field: Ext.form.field.Field + }, + alias: 'widget.htmleditor', + alternateClassName: 'Ext.form.HtmlEditor', + + + + + + + + + + + componentLayout: 'htmleditor', + + componentTpl: [ + '{beforeTextAreaTpl}', + '', + '{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 || Ext.isIE6) ? ' ' : '​', + + + extraFieldBodyCls: Ext.baseCSSPrefix + 'html-editor-wrap', + + + + + initialized: false, + + activated: false, + + sourceEditMode: false, + + iframePad:3, + + hideMode:'offsets', + + maskOnDisable: true, + + containerElCls: Ext.baseCSSPrefix + 'html-editor-container', + + + reStripQuotes: /^['"]*|['"]*$/g, + + // @private + initComponent: function(){ + var me = this; + + me.addEvents( + /** + * @event initialize + * Fires when the editor is fully initialized (including the iframe) + * @param {Ext.form.field.HtmlEditor} this + */ + 'initialize', + /** + * @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 + */ + 'activate', + /** + * @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 + */ + 'beforesync', + /** + * @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 + */ + 'beforepush', + /** + * @event sync + * Fires when the textarea is updated with content from the editor iframe. + * @param {Ext.form.field.HtmlEditor} this + * @param {String} html + */ + 'sync', + /** + * @event push + * Fires when the iframe editor is updated with content from the textarea. + * @param {Ext.form.field.HtmlEditor} this + * @param {String} html + */ + 'push', + /** + * @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. + */ + 'editmodechange' + ); + + me.items = [me.createToolbar(), me.createInputCmp()]; + + me.layout = { + type: 'vbox', + align: 'stretch' + }; + + 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 : 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(btn){ + 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 AbstractComponent.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, + oldIE = Ext.isIE8m; + + // - 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( + (oldIE ? '' : '') + + '' + , 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 used internally + 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, + inputCmp = me.inputCmp; + + if (value === null || value === undefined) { + 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(/\sclass="(?:Apple-style-span|Apple-tab-span|khtml-block-placeholder)"/gi, ''); + } + + /* + * 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(/\D/g, ''), 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(/text-align:(.*?);/i); + 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); + } + } + }, + + // @private + deferFocus: function(){ + this.focus(false, true); + }, + + getFocusEl: function() { + var me = this, + win = me.win; + return win && !me.sourceEditMode ? win : me.textareaEl; + }, + + 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(){ + //Destroying the component during/before initEditor can cause issues. + try { + var me = this, + dbody = me.getEditorBody(), + ss = me.textareaEl.getStyles('font-size', 'font-family', 'background-image', 'background-repeat', 'background-color', 'color'), + doc, + fn; + + ss['background-attachment'] = 'fixed'; // w3c + dbody.bgProperties = 'fixed'; // ie + + Ext.DomHelper.applyStyles(dbody, ss); + + doc = me.getDoc(); + + if (doc) { + try { + Ext.EventManager.removeAll(doc); + } 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 = Ext.Function.bind(me.onEditorEvent, me); + Ext.EventManager.on(doc, { + mousedown: fn, + dblclick: fn, + click: fn, + keyup: fn, + 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; + Ext.EventManager.on(doc, { + 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 + scope: me + }); + + if (Ext.isGecko) { + Ext.EventManager.on(doc, 'keypress', me.applyCommand, me); + } + + if (me.fixKeys) { + Ext.EventManager.on(doc, 'keydown', me.fixKeys, me); + } + if (me.fixKeysAfter) { + Ext.EventManager.on(doc, 'keyup', me.fixKeysAfter, me); + } + + if (Ext.isIE9 && Ext.isStrict) { + Ext.EventManager.on(doc.documentElement, '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.isIE8m || (Ext.isIE9 && !Ext.isStrict)) { + Ext.EventManager.on(doc, 'focusout', function() { + me.savedSelection = doc.selection.type !== 'None' ? doc.selection.createRange() : null; + }, me); + + Ext.EventManager.on(doc, '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.EventManager.onWindowUnload(me.beforeDestroy, me); + doc.editorInitialized = true; + + me.initialized = true; + me.pushValue(); + me.setReadOnly(me.readOnly); + me.fireEvent('initialize', me); + } catch(ex) { + // ignore (why?) + } + }, + + // @private + beforeDestroy: function(){ + var me = this, + monitorTask = me.monitorTask, + doc, prop; + + if (monitorTask) { + Ext.TaskManager.stop(monitorTask); + } + if (me.rendered) { + Ext.EventManager.removeUnloadListener(me.beforeDestroy, me); + try { + 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 using Ext.fly, + // 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.EventManager.removeAll(Ext.fly(doc)); + for (prop in doc) { + if (doc.hasOwnProperty && doc.hasOwnProperty(prop)) { + delete doc[prop]; + } + } + } + } catch(e) { + // ignore (why?) + } + me.iframeEl.remove(); + 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, + + // Get the left-based iframe position + iframeXY = Ext.Element.getTrueXY(iframeEl), + originalEventXY = event.getXY(), + + // Get the left-based XY position. + // This is because the consumer of the injected event (Ext.EventManager) will + // perform its own RTL normalization. + eventXY = Ext.EventManager.getPageXY(event.browserEvent); + + // 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(e) { + 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.isDestroyed) { + 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){ + var me = this, + range; + + if (me.activated) { + me.win.focus(); + if (Ext.isIE) { + range = me.getDoc().selection.createRange(); + if (range) { + range.pasteHTML(text); + me.syncValue(); + me.deferFocus(); + } + }else{ + me.execCmd('InsertHTML', text); + me.deferFocus(); + } + } + }, + + // @private + fixKeys: (function () { // load time branching for fastest keydown performance + var tag; + + if (Ext.isIE) { + return function(e){ + var me = this, + k = e.getKey(), + doc = me.getDoc(), + readOnly = me.readOnly, + range, target; + + if (k === e.TAB) { + e.stopEvent(); + if (!readOnly) { + range = doc.selection.createRange(); + if (range){ + if (range.collapse) { + range.collapse(true); + range.pasteHTML('    '); + } + + me.deferFocus(); + } + } + } else if (k === e.ENTER) { + if (!readOnly) { + if (Ext.isIE10m) { + range = doc.selection.createRange(); + if (range) { + target = range.parentElement(); + if (!target || target.tagName.toLowerCase() !== 'li') { + e.stopEvent(); + range.pasteHTML('
'); + range.collapse(false); + range.select(); + } + } + } else { + // IE 11 + range = doc.getSelection().getRangeAt(0); + if (range && range.commonAncestorContainer.parentNode.tagName.toLowerCase() !== 'li') { + // Use divs so it doesn't double-space. + e.stopEvent(); + tag = doc.createElement('div'); + range.insertNode(tag); + } + } + } + } + }; + } + + 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(); + } + } + }; + } + + return null; // not needed, so 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 + */ +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +Ext.define('ExtThemeNeptune.form.field.HtmlEditor', { + override: 'Ext.form.field.HtmlEditor', + defaultButtonUI: 'plain-toolbar' +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * 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', + + + /** + * @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); + + me.store = me.createStore(); + + // Add our min/max range filter, but do not apply it. + // The owning TimeField will filter it. + me.store.addFilter(me.rangeFilter = new Ext.util.Filter({ + id: 'time-picker-filter' + }), false); + + // Updates the range filter's filterFn according to our configured min and max + me.updateList(); + + me.callParent(); + }, + + /** + * 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); + + me.rangeFilter.setFilterFn(function(record) { + var date = record.get('date'); + return date >= min && date <= max; + }); + me.store.filter(); + }, + + /** + * @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() { + var me = this, + utilDate = Ext.Date, + times = [], + min = me.absMin, + max = me.absMax; + + while(min <= max){ + times.push({ + disp: utilDate.dateFormat(min, me.format), + date: min + }); + min = utilDate.add(min, 'mi', me.increment); + } + + return new Ext.data.Store({ + model: me.modelType, + autoDestroy: true, + data: times + }); + }, + + focusNode: function (rec) { + // We don't want the view being focused when interacting with the inputEl (see Ext.form.field.ComboBox:onKeyUp) + // so this is here to prevent focus of the boundlist view. See EXTJSIV-7319. + return false; + } +}, function() { + this.prototype.modelType = Ext.define(null, { + extend: 'Ext.data.Model', + fields: ['disp', 'date'] + }); +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * 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', + + 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 #triggerBaseCls} + * 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 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", + // + + // + /** + * @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", + // + + /** + * @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, + + /** + * @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', + + ignoreSelection: 0, + + 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); + } + // Forcibly create the picker, since we need the store it creates + me.store = me.getPicker().store; + + me.displayTpl = new Ext.XTemplate( + '' + + '{[typeof values === "string" ? values : this.formatDate(values["' + me.displayField + '"])]}' + + '' + me.delimiter + '' + + '', { + formatDate: Ext.Function.bind(me.formatDate, me) + }); + me.callParent(); + }, + + /** + * @private + */ + transformOriginalValue: function (value) { + if (Ext.isDefined(value)) { + return this.rawToValue(value) || value || null; + } + return value; + }, + + /** + * @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 (!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()); + } + // Invalid min/maxValue config should result in a null so that defaulting takes over + else { + 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) { + var me = this, + format = Ext.String.format, + errors = me.callParent(arguments), + 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, + picker; + + me.listConfig = Ext.apply({ + xtype: 'timepicker', + selModel: { + mode: me.multiSelect ? 'SIMPLE' : 'SINGLE' + }, + cls: undefined, + minValue: me.minValue, + maxValue: me.maxValue, + increment: me.increment, + format: me.format, + maxHeight: me.pickerMaxHeight + }, me.listConfig); + picker = me.callParent(); + return picker; + }, + + onItemClick: function(picker, record){ + // The selection change events won't fire when clicking on the selected element. Detect it here. + var me = this, + selected = picker.getSelectionModel().getSelection(); + + if (!me.multiSelect && selected.length) { + if (selected.length > 0) { + selected = selected[0]; + if (selected && Ext.Date.isEqual(record.get('date'), selected.get('date'))) { + me.collapse(); + } + } + } + }, + + /** + * @private + * Synchronizes the selection in the picker to match the current value + */ + syncSelection: function() { + var me = this, + picker = me.picker, + isEqual = Ext.Date.isEqual, + toSelect = [], + selModel, + value, values, i, len, item, + data, d, dLen, rec; + + if (picker) { + picker.clearHighlight(); + value = me.getValue(); + selModel = picker.getSelectionModel(); + // Update the selection to match + me.ignoreSelection++; + if (value === null) { + selModel.deselectAll(); + } else { + values = Ext.Array.from(value); + data = picker.store.data.items; + dLen = data.length; + + for (i = 0, len = values.length; i < len; i++) { + item = values[i]; + if (Ext.isDate(item)) { + // find value, select it + for (d = 0; d < dLen; d++) { + rec = data[d]; + + if (isEqual(rec.get('date'), item)) { + toSelect.push(rec); + if (!me.multiSelect) { + break; + } + } + } + + if (toSelect.length) { + selModel.select(toSelect); + } + } + } + } + me.ignoreSelection--; + } + }, + + postBlur: 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) { + // Store MUST be created for parent setValue to function. + this.getPicker(); + + if (Ext.isDate(v)) { + v = this.getInitDate(v.getHours(), v.getMinutes(), v.getSeconds()); + } + + return this.callParent([v]); + }, + + getValue: function () { + return this.rawToValue(this.callParent(arguments)); + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * Internal utility class that provides default configuration for cell editing. + * @private + */ +Ext.define('Ext.grid.CellEditor', { + extend: Ext.Editor , + + // Editor must appear at the top so that it does not contribute to scrollbars + y: 0, + + constructor: function(config) { + config = Ext.apply({}, config); + + if (config.field) { + config.field.monitorTab = false; + } + this.callParent([config]); + }, + + // Set the grid that owns this editor. + // Usually this will only *change* once, and the renderTo will cause + // rendering into the owning grid. + // However in a Lockable assembly the editor has to swap sides if the column is moved across. + // Called by CellEditing#getEditor + setGrid: function(grid) { + var me = this, + oldGrid = me.grid, + view, + viewListeners; + + if (grid != oldGrid) { + viewListeners = { + beforerefresh: me.beforeViewRefresh, + refresh: me.onViewRefresh, + scope: me + }; + // Remove previous refresh listener + if (oldGrid) { + oldGrid.getView().un(viewListeners); + } + + // Set the renderTo target to reflect new grid view ownership + // TODO: In Sencha-5.0.x use view.getScrollerEl() if Ext.touchScroll + view = grid.getView(), + me.renderTo = view.getTargetEl().dom; + me.grid = me.ownerCt = grid; + + // On view refresh, we need to copy our DOM into the detached body to prevent it from being garbage collected. + view.on(viewListeners); + } + }, + + // @private + // @override + // Final position is decided upon by the Editor's realign() call which syncs position over the edited element. + adjustPosition: function() { + return {x:0,y:0}; + }, + + beforeViewRefresh: function() { + var me = this, + dom = me.el && me.el.dom; + + if (dom && dom.parentNode) { + if (me.editing && !me.field.column.sorting) { + // Set the Editor.allowBlur setting so that it does not process the upcoming field blur event and terminate the edit + me.wasAllowBlur = me.allowBlur; + me.allowBlur = false; + } + + // Remove the editor from the view to protect it from anihilation: https://sencha.jira.com/browse/EXTJSIV-11713 + dom.parentNode.removeChild(dom); + } + }, + + onViewRefresh: function() { + var me = this, + dom = me.el && me.el.dom, + sorting; + + if (dom) { + sorting = me.field.column.sorting; + + // If the view was refreshed while we were editing, replace it. + if (me.editing && !sorting) { + me.allowBlur = me.wasAllowBlur; + me.renderTo.appendChild(dom); + me.field.focus(); + } else if (!sorting) { + Ext.getDetachedBody().dom.appendChild(dom); + } + + // If the column was sorted while editing, we must detect that and complete the edit + // because the view will be refreshed and the editor will be removed from the dom. + if (me.editing && sorting) { + me.completeEdit(); + } + } + }, + + /** + * @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.first(); + + // If we have had our owning grid changed (by a column switching sides in a Lockable assembly) + // or, if a view refresh has removed us from the DOM + // append this component into its renderTo target. + if (me.el.dom.parentNode !== me.renderTo) { + me.renderTo.appendChild(me.el.dom); + } + + if (innerCell) { + if (me.isForTree) { + innerCell = innerCell.child(me.treeNodeSelector); + } + innerCell.hide(); + } + + me.callParent(arguments); + }, + + /** + * @private + * Shows the grid cell inner element when a cell editor is hidden + */ + onHide: function() { + var me = this, + innerCell = me.boundEl.first(); + + if (innerCell) { + if (me.isForTree) { + innerCell = innerCell.child(me.treeNodeSelector); + } + innerCell.show(); + } + + me.callParent(arguments); + }, + + /** + * @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.first(), + 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(innerCell, me.alignment, offsets); + if (isEmpty) { + innerCell.dom.firstChild.data = v; + } + }, + + // private + getTreeNodeOffset: function(innerCell) { + return innerCell.child(this.treeNodeSelector).getOffsetsTo(innerCell)[0]; + }, + + onEditorTab: function(e){ + var field = this.field; + if (field.onEditorTab) { + field.onEditorTab(e); + } + }, + + onFieldBlur : function() { + this.callParent(arguments); + // Reset the flag that may have been set by CellEditing#startEdit to prevent + // Ext.Editor#onFieldBlur from canceling editing. + this.selectSameEditor = false; + }, + + 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 +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * @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.Error.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.Error.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; + }, + + /** + * 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 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 = 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; + } +}, function() { + this.createAlias('indexOf', 'getHeaderIndex'); +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * @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 + }, { + cls: cssPrefix + 'row-editor-cancel-button', + itemId: 'cancel', + handler: plugin.cancelEdit, + text: rowEditor.cancelBtnText + }] + }, config); + + me.callParent([config]); + + me.addClsWithUI(me.position); + }, + + 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 + }); + }, + + 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; + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +// 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', + + + + + + + // + saveBtnText : 'Update', + // + // + cancelBtnText: 'Cancel', + // + // + errorsText: 'Errors', + // + // + dirtyText: 'You need to commit or cancel your changes', + // + + lastScrollLeft: 0, + lastScrollTop: 0, + + border: false, + + 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', + + initComponent: function() { + var me = this, + grid = me.editingPlugin.grid, + Container = Ext.container.Container, + form; + + 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 + me.lockedColumnContainer = new Container({ + id: grid.id + '-locked-editor-cells', + 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 + me.normalColumnContainer = new Container({ + flex: 1, + id: grid.id + '-normal-editor-cells', + layout: { + type: 'hbox', + align: 'middle' + } + }) + ]; + } else { + me.lockedColumnContainer = me.normalColumnContainer = me; + } + + me.callParent(arguments); + + if (me.fields) { + me.addFieldsForColumn(me.fields, true); + me.insertColumnEditor(me.fields); + delete me.fields; + } + + me.mon(me.hierarchyEventSource, { + scope: me, + show: me.repositionIfVisible + }); + + form = me.getForm(); + form.trackResetOnLoad = true; + form.on('validitychange', me.onValidityChange, 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.setLocalX(gridBody.getOffsetsTo(grid)[0] + gridBody.getBorderWidth('l') - grid.el.getBorderWidth('l')); + + me.setWidth(clientWidth); + btns.setLocalX((clientWidth - btns.getWidth()) / 2); + }, + + syncAllFieldWidths: function() { + var me = this; + // 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 + Ext.Array.each(me.query('[isEditorComponent]'), function(editorComponent) { + if (editorComponent.column.isVisible()) { + me.onColumnShow(editorComponent.column); + } + }, me); + }, + + 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) { + var me = this; + + if (me.errorSummary && me.isVisible()) { + me[valid ? 'hideToolTip' : 'showToolTip'](); + } + me.updateButton(valid); + me.isValid = valid; + }, + + 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.mon(me.scrollingViewEl, '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, + scope: me + }); + + me.el.swallowEvent([ + 'keypress', + 'keydown' + ]); + + me.fieldScroller = me.normalColumnContainer.layout.innerCt; + me.fieldScroller.dom.style.overflow = 'hidden'; + me.fieldScroller.on({ + scroll: me.onFieldContainerScroll, + scope: me + }); + + 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, { + enter: plugin.onEnter, + 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; + + // Recover our row node after a view refresh + if (context && (row = view.getNode(context.record, true))) { + context.row = row; + me.reposition(); + if (me.tooltip && me.tooltip.isVisible()) { + me.tooltip.setTarget(context.row); + } + } else { + me.editingPlugin.cancelEdit(); + } + }, + + onViewItemRemove: function(record, index) { + var context = this.context; + if (context && record === context.record) { + // if the record being edited was removed, cancel editing + this.editingPlugin.cancelEdit(); + } + }, + + onViewScroll: function() { + var me = this, + viewEl = me.editingPlugin.view.el, + scrollingViewEl = me.scrollingViewEl, + scrollTop = scrollingViewEl.dom.scrollTop, + scrollLeft = scrollingViewEl.getScrollLeft(), + scrollLeftChanged = scrollLeft !== me.lastScrollLeft, + scrollTopChanged = scrollTop !== me.lastScrollTop, + row; + + me.lastScrollTop = scrollTop; + me.lastScrollLeft = scrollLeft; + if (me.isVisible()) { + row = Ext.getDom(me.context.row.id); + + // Only reposition if the row is in the DOM (buffered rendering may mean the context row is not there) + if (row && viewEl.contains(row)) { + 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(); + } + } + // If row is NOT in the DOM, ensure the editor is out of sight + else { + me.setLocalY(-400); + } + } + + // Keep fields' left/right scroll position synced with view's left/right scroll + if (me.rendered && scrollLeftChanged) { + me.syncFieldsHorizontalScroll(); + } + }, + + // Synchronize the horizontal scroll position of the fields with the state of the grid view + syncFieldsHorizontalScroll: function() { + // Set overflow style here because it is an embedded element and the "style" Component config does not target it. + this.fieldScroller.setScrollLeft(this.lastScrollLeft); + }, + + // Synchronize the horizontal scroll position of the grid view with the fields. + onFieldContainerScroll: function() { + this.scrollingViewEl.setScrollLeft(this.fieldScroller.getScrollLeft()); + }, + + 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(), + grid = me.editingPlugin.grid, + mgr = locked ? grid.lockedGrid.getColumnManager() : grid.getColumnManager(), + 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 = mgr.getHeaderIndex(column); + if (after) { + ++offset; + } + this.setColumnEditor(column, toIdx + offset, fieldContainer); + } + Ext.resumeLayouts(true); + } else { + toIdx = mgr.getHeaderIndex(column); + this.setColumnEditor(column, toIdx, 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, + plugin = me.editingPlugin, + grid = plugin.grid, + lockable = grid.lockable, + 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(grid.getColumnManager().getHeaderIndex(column), column.getEditor()); + me.needsSyncFieldWidths = true; + }, + + destroyColumnEditor: function(column) { + var me = this, + field, + len, i; + + if (Ext.isArray(column)) { + for (i = 0, len = column.length; i < len; i++) { + me.removeColumnEditor(column[i]); + } + return; + } + + 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({ + 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(); + } + }, + + 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 && Ext.get(context.row), + yOffset = 0, + 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.dom)) { + + deltaY = me.syncButtonPosition(me.getScrollDelta()); + + if (!me.editingPlugin.grid.rowLines) { + // When the grid does not have rowLines we add a bottom border to the previous + // row when the row is focused, but subtract the border width from the + // top padding to keep the row from changing size. This adjusts the top offset + // of the cell edtor to account for the added border. + yOffset = -parseInt(row.first().getStyle('border-bottom-width'), 10); + } + rowTop = me.calculateLocalRowTop(row); + localY = me.calculateEditorTop(rowTop) + yOffset; + + // 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.focusContextCell(); + }; + } + + 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) { + me.animate(Ext.applyIf({ + to: { + top: localY + }, + duration: animateConfig.duration || 125, + callback: afterPosition + }, animateConfig)); + } else { + me.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] - body.getBorderPadding().beforeY; + if (deltaY > 0) { + deltaY = Math.max(deltaY + me.getHeight() + me.floatingButtons.getHeight() - + scrollingViewDom.clientHeight - body.getBorderWidth('b'), 0); + } + } + 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 - 1; + } + 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; + + // temporarily suspend events on form fields before loading record to prevent the fields' change events from firing + for (i = 0; i < length; i++) { + items[i].suspendEvents(); + } + + 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; + + // 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( + column.scope || headerCt.ownerCt, + value, + metaData, + record, + rowIdx, + colIdx, + store, + view + ); + } + + field.setRawValue(value); + field.resetOriginalValue(); + }, + + 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; + + if (!me.rendered) { + me.width = me.getClientWidth(); + me.render(grid.el, grid.el.dom.firstChild); + me.getFloatingButtons().render(me.el); + // On first show we need to ensure that we have the scroll positions cached + me.onViewScroll(); + } else { + me.syncFieldsHorizontalScroll(); + } + + // Select the record before showing the editor, since + // selecting will steal focus + context.grid.getSelectionModel().select(record); + + if (me.isVisible()) { + me.reposition(true); + } else { + me.show(); + } + + // Make sure the container el is correctly sized. + me.onGridResize(); + + // Reload the record data + me.loadRecord(record); + }, + + // 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(), + scrollingViewElDom = me.scrollingViewEl.dom, + overflow = this.getScrollDelta() - (scrollingViewElDom.scrollHeight - + scrollingViewElDom.scrollTop - scrollingViewElDom.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; + } + + 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 + syncEditorClip: function() { + var me = this, + overflow = me.getScrollDelta(), + btnHeight; + + if (overflow) { + // The editor is overflowing outside of the view area, either above or below + me.isOverflowing = true; + btnHeight = me.floatingButtons.getHeight(); + + if (overflow > 0) { + // editor is overflowing the bottom of the view + me.clipBottom(Math.max(me.getHeight() - overflow + btnHeight, -btnHeight)); + } else if (overflow < 0) { + // editor is overflowing the top of the view + overflow = Math.abs(overflow); + me.clipTop(Math.max(overflow, 0)); + } + } else if (me.isOverflowing) { + me.clearClip(); + me.isOverflowing = false; + } + }, + + // Focus the cell on start edit based upon the current context + focusContextCell: function() { + var column = this.context.column, + field; + + if (!column.isDestroyed) { + field = this.getEditor(column); + if (field && field.focus) { + field.focus(); + } + } + }, + + cancelEdit: function() { + var me = this, + form = me.getForm(), + fields = form.getFields(), + items = fields.items, + length = items.length, + i; + + 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(); + } + }, + + completeEdit: function() { + var me = this, + form = me.getForm(); + + if (!form.isValid()) { + return false; + } + + form.updateRecord(me.context.record); + me.hide(); + return true; + }, + + onShow: function() { + var me = this; + + me.callParent(arguments); + if (me.needsSyncFieldWidths) { + me.suspendLayouts(); + me.syncAllFieldWidths(); + me.resumeLayouts(true); + } + delete me.needsSyncFieldWidths; + + me.reposition(); + }, + + onHide: function() { + var me = this; + + me.callParent(arguments); + if (me.tooltip) { + me.hideToolTip(); + } + if (me.context) { + me.context.view.focusRow(me.context.record); + me.context = null; + } + }, + + isDirty: function() { + var me = this, + form = me.getForm(); + return form.isDirty(); + }, + + getToolTip: function() { + return this.tooltip || (this.tooltip = new Ext.tip.ToolTip({ + cls: Ext.baseCSSPrefix + 'grid-row-editor-errors', + title: this.errorsText, + autoHide: false, + closable: true, + closeAction: 'disable', + anchor: 'left', + anchorToTarget: false + })); + }, + + 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.showAt([0, 0]); + tip.update(me.getErrors()); + me.repositionTip(); + tip.enable(); + }, + + repositionTip: function() { + var me = this, + tip = me.getToolTip(), + context = me.context, + row = Ext.get(context.row), + viewEl = me.scrollingViewEl, + viewHeight = viewEl.dom.clientHeight, + viewTop = me.lastScrollTop, + viewBottom = viewTop + viewHeight, + rowHeight = row.getHeight(), + rowTop = row.getOffsetsTo(me.context.view.body)[1], + rowBottom = rowTop + rowHeight; + + if (rowBottom > viewTop && rowTop < viewBottom) { + tip.showAt(tip.getAlignToXY(viewEl, 'tl-tr', [15, row.getOffsetsTo(viewEl)[1]])); + me.hiddenTip = false; + } else { + tip.hide(); + me.hiddenTip = true; + } + }, + + getErrors: function() { + var me = this, + errors = [], + fields = me.query('>[isFormField]'), + length = fields.length, + i; + + for (i = 0; i < length; i++) { + errors = errors.concat( + Ext.Array.map(fields[i].getErrors(), me.createErrorListItem) + ); + } + + // 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) { + return '
  • ' + e + '
  • '; + }, + + beforeDestroy: function(){ + Ext.destroy(this.floatingButtons, this.tooltip); + this.callParent(); + }, + + clipBottom: function(value) { + this.el.setStyle('clip', 'rect(-1000px auto ' + value + 'px auto)'); + }, + + clipTop: function(value) { + this.el.setStyle('clip', 'rect(' + value + 'px auto 1000px auto)'); + }, + + clearClip: function(el) { + this.el.setStyle( + 'clip', + Ext.isIE8m || Ext.isIEQuirks ? 'rect(-1000px auto 1000px auto)' : 'auto' + ); + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +Ext.define('ExtThemeNeptune.grid.RowEditor', { + override: 'Ext.grid.RowEditor', + buttonUI: 'default-toolbar' +}); + + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +Ext.define('Ext.grid.Scroller', { + constructor: Ext.deprecated() +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * @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.getPageY(); + 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 + * @returns {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, true), + 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(); + } + 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); + } + + // No records, position the indicator at the top + else { + 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(){ + Ext.destroy(this.indicator); + delete this.indicator; + this.callParent(); + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * @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); + } + // No position specified - append. + else { + store.add(data.records); + } + + view.getSelectionModel().select(data.records); + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * 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 + * 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/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/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 + * The URL of an image to display as the clickable element in the column. + * + * There are no default icons that come with Ext JS. + * + * Defaults to `{@link Ext#BLANK_IMAGE_URL}`. + */ + /** + * @cfg {String} iconCls + * A CSS class to apply to the icon image. To determine the class dynamically, configure the Column with + * a `{@link #getClass}` function. + * + * There are no default icon classes that come with Ext JS. + */ + /** + * @cfg {Function} 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. + */ + /** + * @cfg {Object} scope + * The scope (`this` reference) in which the `{@link #handler}`, `{@link #getClass}`, `{@link #cfg-isDisabled}` and `{@link #getTip}` fuctions 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. + * @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. + * @cfg {Object} getTip.v The value of the column's configured field (if any). + * @cfg {Object} getTip.metadata An object in which you may set the following attributes: + * @cfg {String} getTip.metadata.css A CSS class name to add to the cell's TD element. + * @cfg {String} 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} getTip.r The Record providing the data. + * @cfg {Number} getTip.rowIndex The row index. + * @cfg {Number} getTip.colIndex The column index. + * @cfg {Ext.data.Store} getTip.store The Store which is providing the data Model. + * + */ + /** + * @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 image. To determine the class dynamically, + * configure the item with a `getClass` function. + * + * @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', + + 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; + } + }, + + // Renderer closure iterates through items creating an element for each and tagging with an identifying + // class name x-action-col-{n} + defaultRenderer: function(v, meta, record, rowIdx, colIdx, store, view){ + var me = this, + prefix = Ext.baseCSSPrefix, + scope = me.origScope || me, + items = me.items, + len = items.length, + i = 0, + item, ret, disabled, tooltip; + + // 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) || '' : ''; + + meta.tdCls += ' ' + Ext.baseCSSPrefix + 'action-col-cell'; + for (; i < len; i++) { + item = items[i]; + + 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)); + + // 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 += '' + (item.altText || me.altText) + ''; + } + return ret; + }, + + /** + * 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); + } + }, + + destroy: function() { + delete this.items; + delete this.renderer; + return this.callParent(arguments); + }, + + /** + * @private + * Process and refire events routed from the GridView'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(), + match, + item, fn, + key = (type === 'keydown' && e.getKey()), + disabled; + + // Don't process mousedown events anymore! + if (type === 'mousedown') { + return false; + } + + // If the target was not within a cell (ie it's a keydown event from the View), then + // rely on the selection data injected by View.processUIEvent to grab the + // first action icon from the selected cell. + if (key && !Ext.fly(target).findParent(view.getCellSelector())) { + target = Ext.fly(cell).down('.' + Ext.baseCSSPrefix + 'action-col-icon', true); + } + + // 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) { + if (type == 'click' || (key == e.ENTER || key == e.SPACE)) { + fn = item.handler || me.handler; + if (fn) { + fn.call(item.scope || me.origScope || me, view, recordIndex, cellIndex, item, e, record, row); + } + + // The default is to stop the event from propagating (thus preventing the selection model from + // selecting and focusing the grid row). See EXTJSIV-11177. + if (item.stopSelection !== false) { + 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 []; + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * 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 + * Ext.create('Ext.data.Store', { + * storeId:'sampleStore', + * fields:[ + * {name: 'framework', type: 'string'}, + * {name: 'rocks', type: 'boolean'} + * ], + * data:{'items':[ + * { 'framework': "Ext JS 4", 'rocks': true }, + * { 'framework': "Sencha Touch", 'rocks': true }, + * { 'framework': "Ext GWT", 'rocks': true }, + * { 'framework': "Other Guys", 'rocks': false } + * ]}, + * proxy: { + * type: 'memory', + * reader: { + * type: 'json', + * root: 'items' + * } + * } + * }); + * + * Ext.create('Ext.grid.Panel', { + * title: 'Boolean Column Demo', + * store: Ext.data.StoreManager.lookup('sampleStore'), + * 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: ' ', + + /** + * @cfg {Object} renderer + * @hide + */ + + /** + * @cfg {Object} scope + * @hide + */ + + defaultRenderer: function(value){ + if (value === undefined) { + return this.undefinedText; + } + + if (!value || value === 'false') { + return this.falseText; + } + return this.trueText; + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * 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 : { + * items : [ + * { 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 } + * ] + * }, + * proxy : { + * type : 'memory', + * reader : { + * type : 'json', + * root : 'items' + * } + * } + * }); + * + * 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', + + /** + * @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', + + constructor: function() { + this.addEvents( + /** + * @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 + */ + 'beforecheckchange', + /** + * @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 + */ + 'checkchange' + ); + 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'; + + if (!me.disabled && (mousedown || (key == e.ENTER || key == e.SPACE))) { + var dataIndex = me.dataIndex, + checked = !record.get(dataIndex); + + // Allow apps to hook beforecheckchange + if (me.fireEvent('beforecheckchange', me, recordIndex, checked) !== false) { + record.set(dataIndex, checked); + me.fireEvent('checkchange', me, recordIndex, checked); + + // Mousedown on the now nonexistent cell causes the view to blur, so stop it continuing. + if (mousedown) { + e.stopEvent(); + } + + // Selection will not proceed after this because of the DOM update caused by the record modification + // Invoke the SelectionModel unless configured not to do so + if (!me.stopSelection) { + view.selModel.selectByPosition({ + row: recordIndex, + column: cellIndex + }); + } + + // Prevent the view from propagating the event to the selection model - we have done that job. + return false; + } else { + // Prevent the view from propagating the event to the selection model if configured to do so. + return !me.stopSelection; + } + } else { + return me.callParent(arguments); + } + }, + + /** + * 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. + renderer : function(value, meta) { + var cssPrefix = Ext.baseCSSPrefix, + cls = cssPrefix + 'grid-checkcolumn'; + + if (this.disabled) { + meta.tdCls += ' ' + this.disabledCls; + } + if (value) { + cls += ' ' + cssPrefix + 'grid-checkcolumn-checked'; + } + return ''; + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * 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'], + + alternateClassName: 'Ext.grid.DateColumn', + + /** + * @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 + */ + + initComponent: function(){ + if (!this.format) { + this.format = Ext.Date.defaultFormat; + } + + this.callParent(arguments); + }, + + defaultRenderer: function(value){ + return Ext.util.Format.date(value, this.format); + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * 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'], + + alternateClassName: 'Ext.grid.NumberColumn', + + // + /** + * @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 + */ + + defaultRenderer: function(value){ + return Ext.util.Format.number(value, this.format); + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * 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', + + /** + * @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', + + 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 AbstractContainer.applyDefaults use of Ext.applyIf + me.width = me.width; + + me.callParent(arguments); + me.scope = me; + }, + + beforeRender: function () { + var rowBody = this.up('tablepanel').view.findFeature('rowbody'); + + this.callParent(arguments); + + // If there is a RowBody Feature, and this coliumn is index 1 (immediately after the expander)... + // the RowBody cell must not span this column, and this column must span into the expander row. + if (rowBody && this.ownerCt.items.indexOf(this) === 1) { + rowBody.colSpanDecrement = rowBody.colSpanDecrement + 1; + this.rowspan = 2; + } + }, + + // private + 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, + + // private + renderer: function(value, metaData, record, rowIdx, colIdx, dataSource, view) { + var rowspan = this.rowspan, + page = dataSource.currentPage, + result = view.store.indexOf(record); + + if (rowspan) { + metaData.tdAttr = 'rowspan="' + rowspan + '"'; + } + + if (page > 1) { + result += (page - 1) * dataSource.pageSize; + } + return result + 1; + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +Ext.define('ExtThemeNeptune.grid.column.RowNumberer', { + override: 'Ext.grid.column.RowNumberer', + width: 25 +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * A Column definition class which renders a value by processing a {@link Ext.data.Model Model}'s + * {@link Ext.data.Model#persistenceProperty 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'], + + 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 {@link Ext.data.Model#persistenceProperty data} to produce a + * column'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); + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * 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', + summaryTableCls: Ext.plainTableCls + ' ' + Ext.baseCSSPrefix + 'grid-table', + summaryRowSelector: '.' + Ext.baseCSSPrefix + 'grid-row-summary', + + // 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: { + before: function(values, out) { + // If a summary record comes through the rendering pipeline, render it simply, and return false from the + // before method which aborts the tpl chain + if (values.record.isSummary && this.summaryFeature.showSummaryRow) { + this.summaryFeature.outputSummaryRecord(values.record, values, out); + return false; + } + }, + 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 = {}; + }, + + /** + * Toggle whether or not to show the summary row. + * @param {Boolean} visible True to show the summary row + */ + toggleSummaryRow: function(visible) { + this.showSummaryRow = !!visible; + }, + + createRenderer: function (column, record) { + var me = this, + ownerGroup = record.ownerGroup, + summaryData = ownerGroup ? me.summaryData[ownerGroup] : me.summaryData, + // Use the column.id for columns without a dataIndex. The populateRecord method does the same. + dataIndex = column.dataIndex || column.id; + + return function () { + return column.summaryRenderer ? + column.summaryRenderer(record.get(dataIndex), summaryData, dataIndex) : + // For no summaryRenderer, return the field value in the Feature record. + record.get(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); + 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) { + // Note `group` will either be an instance of Ext.data.Group or a list of records. + var records = group.records; + + if (type) { + if (Ext.isFunction(type)) { + return store.getAggregate(type, null, records, [field]); + } + + switch (type) { + case 'count': + return records.length; + case 'min': + return store.getMin(records, field); + case 'max': + return store.getMax(records, field); + case 'sum': + return store.getSum(records, field); + case 'average': + return store.getAverage(records, field); + default: + return ''; + + } + } + }, + + /** + * 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.view.store, + groups = store.groups.items, + reader = store.proxy.reader, + len = groups.length, + groupField = me.getGroupField(), + data = {}, + lockingPartner = me.lockingPartner, + i, group, record, + root, summaryRows, hasRemote, + convertedSummaryRow, 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 && reader.rawData) { + hasRemote = true; + remoteData = {}; + // reset reader root and rebuild extractors to extract summaries data + root = reader.root; + reader.root = me.remoteRoot; + reader.buildExtractors(true); + summaryRows = reader.getRoot(reader.rawData)||[]; + len = summaryRows.length; + + // Ensure the Reader has a data conversion function to convert a raw data row into a Record data hash + if (!reader.convertRecordData) { + reader.buildExtractors(); + } + + for (i = 0; i < len; ++i) { + convertedSummaryRow = {}; + + // Convert a raw data row into a Record's hash object using the Reader + reader.convertRecordData(convertedSummaryRow, summaryRows[i]); + remoteData[convertedSummaryRow[groupField]] = convertedSummaryRow; + } + + // restore initial reader configuration + reader.root = root; + reader.buildExtractors(true); + } + + for (i = 0; i < len; ++i) { + group = groups[i]; + // Something has changed or it doesn't exist, populate it + if (hasRemote || group.isDirty() || !group.hasAggregate()) { + record = me.populateRecord(group, 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 || (me.view.ownerCt === me.view.ownerCt.ownerLockable.normalGrid)) { + group.commit(); + } + } else { + record = group.getAggregateRecord(); + } + + data[group.key] = record; + } + + return data; + }, + + setSummaryData: function (record, colId, summaryValue, groupName) { + if (groupName) { + if (!this.summaryData[groupName]) { + this.summaryData[groupName] = {}; + } + this.summaryData[groupName][colId] = summaryValue; + } else { + this.summaryData[colId] = summaryValue; + } + }, + + populateRecord: function (group, remoteData) { + var me = this, + view = me.grid.ownerLockable ? me.grid.ownerLockable.view : me.view, + store = me.view.store, + record = group.getAggregateRecord(), + // Use the full column set, regardless of locking + columns = view.headerCt.getGridColumns(), + len = columns.length, + groupName = group.key, + 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.id; + + // 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(fieldName); + } + + // Capture the columnId:value for the summaryRenderer in the summaryData object. + me.setSummaryData(record, column.id, summaryValue, groupName); + } + + // Poke on the owner group for easy lookup in this.createRenderer(). + record.ownerGroup = groupName; + + record.endEdit(true); + record.commit(); + + return record; + } +}); + +/* +This file is part of Ext JS 4.2 + +Copyright (c) 2011-2014 Sencha Inc + +Contact: http://www.sencha.com/contact + +Commercial Usage +Licensees holding valid commercial licenses may use this file in accordance with the Commercial +Software License Agreement provided with the Software or, alternatively, in accordance with the +terms contained in a written agreement between you and Sencha. + +If you are unsure which license is appropriate for your use, please contact the sales department +at http://www.sencha.com/contact. + +Build date: 2014-09-02 11:12:40 (ef1fa70924f51a26dacbe29644ca3f31501a5fce) +*/ +/** + * 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, + + constructor: function(groupingFeature, store) { + var me = this; + + me.callParent(); + me.groupingFeature = groupingFeature; + me.bindStore(store); + }, + + bindStore: function(store) { + var me = this; + + if (me.store) { + Ext.destroy(me.storeListeners); + me.store = null; + } + if (store) { + me.storeListeners = store.on({ + bulkremove: me.onBulkRemove, + add: me.onAdd, + update: me.onUpdate, + refresh: me.onRefresh, + clear: me.onClear, + scope: me, + destroyable: true + }); + me.store = store; + me.processStore(store); + } + }, + + processStore: function(store) { + var me = this, + groups = store.getGroups(), + groupCount = groups.length, + i, + group, + groupPlaceholder, + data = me.data, + oldGroupCache = me.groupingFeature.groupCache, + groupCache = me.groupingFeature.clearGroupCache(), + collapseAll = me.groupingFeature.startCollapsed; + + if (data) { + data.clear(); + } else { + data = me.data = new Ext.util.MixedCollection(false, Ext.data.Store.recordIdFn); + } + + if (store.getCount()) { + + // Upon first process of a loaded store, clear the "always" collapse" flag + me.groupingFeature.startCollapsed = false; + + for (i = 0; i < groupCount; i++) { + + + + group = groups[i]; + + + groupCache[group.name] = group; + group.isCollapsed = collapseAll || (oldGroupCache[group.name] && oldGroupCache[group.name].isCollapsed); + + + + if (group.isCollapsed) { + group.placeholder = groupPlaceholder = new store.model(null, 'group-' + group.name + '-placeholder'); + groupPlaceholder.set(store.getGroupField(), group.name); + groupPlaceholder.rows = groupPlaceholder.children = group.children; + groupPlaceholder.isCollapsedPlaceholder = true; + data.add(groupPlaceholder); + } + + + else { + data.insert(me.data.length, group.children); + } + } + } + }, + + isCollapsed: function(name) { + return this.groupingFeature.groupCache[name].isCollapsed; + }, + + isInCollapsedGroup: function(record) { + var store = this.store, + groupData; + + if (store.isGrouped() && (groupData = this.groupingFeature.groupCache[record.get(store.getGroupField())])) { + return groupData.isCollapsed || false; + } + return false; + }, + + getCount: function() { + return this.data.getCount(); + }, + + getTotalCount: function() { + return this.data.getCount(); + }, + + + rangeCached: function(start, end) { + return end < this.getCount(); + }, + + getRange: function(start, end, options) { + var result = this.data.getRange(start, end); + + if (options && options.callback) { + options.callback.call(options.scope || this, result, start, end, options); + } + return result; + }, + + getAt: function(index) { + return this.getRange(index, index)[0]; + }, + + getById: function(id) { + return this.store.getById(id); + }, + + getByInternalId: function(internalId) { + + + return (this.store.snapshot || this.data).get(internalId) || null; + }, + + expandGroup: function(group) { + var me = this, + startIdx; + + if (typeof group === 'string') { + group = me.groupingFeature.groupCache[group]; + } + + if (group && group.children.length && (startIdx = me.data.indexOf(group.placeholder)) !== -1) { + + + group.isCollapsed = false; + me.isExpandingOrCollapsing = 1; + + + me.data.removeAt(startIdx); + + + me.data.insert(startIdx, group.children); + + + me.fireEvent('replace', me, startIdx, [group.placeholder], group.children); + + me.fireEvent('groupexpand', me, group); + me.isExpandingOrCollapsing = 0; + } + }, + + collapseGroup: function(group) { + var me = this, + startIdx, + placeholder, + len; + + if (typeof group === 'string') { + group = me.groupingFeature.groupCache[group]; + } + + if (group && (len = group.children.length) && (startIdx = me.data.indexOf(group.children[0])) !== -1) { + + + group.isCollapsed = true; + me.isExpandingOrCollapsing = 2; + + + me.data.removeRange(startIdx, len); + + + me.data.insert(startIdx, placeholder = me.getGroupPlaceholder(group)); + + + me.fireEvent('replace', me, startIdx, group.children, [placeholder]); + + me.fireEvent('groupcollapse', me, group); + me.isExpandingOrCollapsing = 0; + } + }, + + getGroupPlaceholder: function(group) { + if (!group.placeholder) { + var groupPlaceholder = group.placeholder = new this.store.model(null, 'group-' + group.name + '-placeholder'); + groupPlaceholder.set(this.store.getGroupField(), group.name); + groupPlaceholder.rows = groupPlaceholder.children = group.children; + groupPlaceholder.isCollapsedPlaceholder = true; + } + return group.placeholder; + }, + + + + indexOf: function(record) { + if (!record.isCollapsedPlaceholder) { + return this.data.indexOf(record); + } + return -1; + }, + + + indexOfTotal: function(record) { + return this.store.indexOf(record); + }, + + onRefresh: function(store) { + this.processStore(this.store); + this.fireEvent('refresh', this); + }, + + onBulkRemove: function(store, records, indices, isMove, removeRange) { + this.processStore(this.store); + + + + + if (removeRange) { + this.fireEvent('replace', this, this.indexOf(records[0]), records, []); + } else { + this.fireEvent('refresh', this); + } + }, + + onClear: function(store, records, startIndex) { + this.processStore(this.store); + this.fireEvent('clear', this); + }, + + onAdd: function(store, records, startIndex) { + this.processStore(this.store); + + + + this.fireEvent('replace', this, this.indexOf(records[0]), [], records); + }, + + onUpdate: function(store, record, operation, modifiedFieldNames) { + var me = this, + groupInfo, + firstRec, lastRec; + + + + + if (store.isGrouped()) { + + groupInfo = record.group = me.groupingFeature.getRecordGroup(record); + + if (modifiedFieldNames && Ext.Array.contains(modifiedFieldNames, me.groupingFeature.getGroupField())) { + return me.onRefresh(me.store); + } + + + if (groupInfo.isCollapsed) { + me.fireEvent('update', me, groupInfo.placeholder); + } + + + + else { + Ext.suspendLayouts(); + + + me.fireEvent('update', me, record, operation, modifiedFieldNames); + + + + firstRec = groupInfo.children[0]; + lastRec = groupInfo.children[groupInfo.children.length - 1]; + + + if (firstRec !== record) { + firstRec.group = groupInfo; + me.fireEvent('update', me, firstRec, 'edit'); + delete firstRec.group; + } + if (lastRec !== record && lastRec !== firstRec && me.groupingFeature.showSummaryRow) { + lastRec.group = groupInfo; + me.fireEvent('update', me, lastRec, 'edit'); + delete lastRec.group; + } + Ext.resumeLayouts(true); + } + + delete record.group; + } else { + + me.fireEvent('update', me, record, operation, modifiedFieldNames); + } + } +}); + + + +Ext.define('Ext.grid.feature.Grouping', { + extend: Ext.grid.feature.Feature , + mixins: { + summary: Ext.grid.feature.AbstractSummary + }, + + + alias: 'feature.grouping', + + eventPrefix: 'group', + groupCls: Ext.baseCSSPrefix + 'grid-group-hd', + eventSelector: '.' + Ext.baseCSSPrefix + 'grid-group-hd', + + refreshData: {}, + groupInfo: {}, + wrapsItem: true, + + + + + + + + + + + + + groupHeaderTpl: '{columnName}: {name}', + + + 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', + + + + groupByText : 'Group by this field', + + + + showGroupsText : 'Show in groups', + + + + hideGroupedHeader : false, + + + startCollapsed : false, + + + enableGroupingMenu : true, + + + enableNoGroups : true, + + + collapsible: true, + + + expandTip: 'Click to expand. CTRL key collapses all others', + + + + collapseTip: 'Click to collapse. CTRL/click collapses all others', + + + showSummaryRow: false, + + tableTpl: { + before: function(values) { + + if (this.groupingFeature.disabled || values.rows.length === 1 && values.rows[0].isSummary) { + return; + } + this.groupingFeature.setup(values.rows, values.view.rowValues); + }, + after: function(values) { + + if (this.groupingFeature.disabled || values.rows.length === 1 && values.rows[0].isSummary) { + return; + } + this.groupingFeature.cleanup(values.rows, values.view.rowValues); + }, + priority: 200 + }, + + groupTpl: [ + '{%', + 'var me = this.groupingFeature;', + + 'if (me.disabled) {', + 'values.needsWrap = false;', + '} else {', + + 'me.setupRowData(values.record, values.rowIndex, values);', + '}', + '%}', + '', + ' ', Ext.baseCSSPrefix, 'grid-group-row" {ariaRowAttr}>', + '', + '', + '{%', + + + '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.groupInfo, parent) || " "]}', + '
    ', + '
    ', + '
    ', + + + '', + ' ', Ext.baseCSSPrefix, 'grid-table-summary"', + 'border="0" cellspacing="0" cellpadding="0" style="width:100%" {ariaSummaryTableAttr}>', + '{[values.view.renderColumnSizer(out)]}', + + '', + '{%', + 'values.itemClasses.length = 0;', + 'this.nextTpl.applyOut(values, out, parent);', + '%}', + '', + '', + '{%me.outputSummaryRecord(values.summaryRecord, values, out);%}', + '', + '
    ', + '
    ', + '', + '', + '', + '{%this.nextTpl.applyOut(values, out, parent);%}', + '', { + priority: 200, + + syncRowHeights: function(firstRow, secondRow) { + firstRow = Ext.fly(firstRow, 'syncDest'); + secondRow = Ext.fly(secondRow, 'sycSrc'); + var owner = this.owner, + firstHd = firstRow.down(owner.eventSelector, true), + secondHd, + firstSummaryRow = firstRow.down(owner.summaryRowSelector, true), + secondSummaryRow, + firstHeight, secondHeight; + + + if (firstHd && (secondHd = secondRow.down(owner.eventSelector, true))) { + firstHd.style.height = secondHd.style.height = ''; + if ((firstHeight = firstHd.offsetHeight) > (secondHeight = secondHd.offsetHeight)) { + Ext.fly(secondHd).setHeight(firstHeight); + } + else if (secondHeight > firstHeight) { + Ext.fly(firstHd).setHeight(secondHeight); + } + } + + + if (firstSummaryRow && (secondSummaryRow = secondRow.down(owner.summaryRowSelector, true))) { + firstSummaryRow.style.height = secondSummaryRow.style.height = ''; + if ((firstHeight = firstSummaryRow.offsetHeight) > (secondHeight = secondSummaryRow.offsetHeight)) { + Ext.fly(secondSummaryRow).setHeight(firstHeight); + } + else if (secondHeight > firstHeight) { + Ext.fly(firstSummaryRow).setHeight(secondHeight); + } + } + }, + + syncContent: function(destRow, sourceRow) { + destRow = Ext.fly(destRow, 'syncDest'); + sourceRow = Ext.fly(sourceRow, 'sycSrc'); + var owner = this.owner, + destHd = destRow.down(owner.eventSelector, true), + sourceHd = sourceRow.down(owner.eventSelector, true), + destSummaryRow = destRow.down(owner.summaryRowSelector, true), + sourceSummaryRow = sourceRow.down(owner.summaryRowSelector, true); + + + if (destHd && sourceHd) { + Ext.fly(destHd).syncContent(sourceHd); + } + + + if (destSummaryRow && sourceSummaryRow) { + Ext.fly(destSummaryRow).syncContent(sourceSummaryRow); + } + } + } + ], + + constructor: function() { + this.groupCache = {}; + this.callParent(arguments); + }, + + init: function(grid) { + var me = this, + view = me.view, + store = view.store; + + if (store.isGrouped()) { + view.isGrouping = true; + } + + + if (me.lockingPartner && me.lockingPartner.groupCache) { + me.groupCache = me.lockingPartner.groupCache; + } + + me.mixins.summary.init.call(me); + + me.callParent(arguments); + view.headerCt.on({ + columnhide: me.onColumnHideShow, + columnshow: me.onColumnHideShow, + columnmove: me.onColumnMove, + scope: me + }); + + + view.addTableTpl(me.tableTpl).groupingFeature = me; + + + view.addRowTpl(Ext.XTemplate.getTpl(me, 'groupTpl')).groupingFeature = me; + + view.preserveScrollOnRefresh = true; + + + if (store.buffered) { + me.collapsible = false; + } + + else { + + + if (this.lockingPartner && this.lockingPartner.dataSource) { + me.dataSource = view.dataSource = this.lockingPartner.dataSource; + } else { + me.dataSource = view.dataSource = new Ext.grid.feature.GroupStore(me, store); + } + } + + me.grid.on({ + reconfigure: me.onReconfigure + }); + view.on({ + afterrender: me.afterViewRender, + scope: me, + single: true + }); + }, + + indexOf: function(record) { + return this.dataSource.indexOf(record); + }, + + isInCollapsedGroup: function(record) { + var groupData, + store = this.view.store; + + if (store.isGrouped() && (groupData = this.getGroup(record))) { + return groupData.isCollapsed || false; + } + return false; + }, + + clearGroupCache: function() { + var me = this, + groupCache = me.groupCache = {}; + + if (me.lockingPartner) { + me.lockingPartner.groupCache = groupCache; + } + return groupCache; + }, + + vetoEvent: function(record, row, rowIndex, e) { + + 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 = view.store, + groupToggleMenuItem; + + view.isGrouping = true; + if (me.lastGroupers) { + me.block(); + store.group(me.lastGroupers); + me.lastGroupers = null; + me.unblock(); + } + me.callParent(); + groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem'); + if (groupToggleMenuItem) { + groupToggleMenuItem.setChecked(true, true); + } + me.refreshIf(); + }, + + disable: function() { + var me = this, + view = me.view, + store = view.store, + groupToggleMenuItem, + lastGroupers = store.groupers.getRange(); + + view.isGrouping = false; + if (lastGroupers.length) { + me.lastGroupers = lastGroupers; + me.block(); + store.clearGrouping(); + me.unblock(); + } + + me.callParent(); + groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem'); + if (groupToggleMenuItem) { + groupToggleMenuItem.setChecked(false, true); + } + me.refreshIf(); + }, + + refreshIf: function() { + var ownerCt = this.grid.ownerCt, + view = this.view; + + if (!view.store.remoteGroup && !this.blockRefresh) { + + + if (ownerCt && ownerCt.lockable) { + ownerCt.view.refresh(); + } else { + view.refresh(); + } + } + }, + + + afterViewRender: function() { + var me = this, + view = me.view; + + view.on({ + scope: me, + groupclick: me.onGroupClick + }); + + if (me.enableGroupingMenu) { + me.injectGroupingMenu(); + } + + me.pruneGroupedHeader(); + + me.lastGroupers = me.view.store.groupers.getRange(); + me.block(); + me.onGroupChange(); + me.unblock(); + + + + 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 view = this.view, + headerCt = view.headerCt, + menu = headerCt.getMenu(), + activeHeader = menu.activeHeader, + groupMenuItem = menu.down('#groupMenuItem'), + groupMenuMeth, + colCount = this.grid.getVisibleColumnManager().getColumns().length, + items, + len, + i; + + + if (activeHeader && groupMenuItem) { + groupMenuMeth = activeHeader.groupable === false || activeHeader.dataIndex == null || this.view.headerCt.getVisibleGridColumns().length < 2 ? 'disable' : 'enable'; + groupMenuItem[groupMenuMeth](); + } + + + if (view.rendered) { + items = view.el.query('.' + this.ctCls); + for (i = 0, len = items.length; i < len; ++i) { + items[i].colSpan = colCount; + } + } + }, + + + + onColumnMove: function() { + var me = this, + store = me.view.store, + groups, + groupName, + group, firstRec, lastRec; + + if (store.isGrouped()) { + groups = me.groupCache; + Ext.suspendLayouts(); + for (groupName in groups) { + if (groups.hasOwnProperty(groupName)) { + group = groups[groupName]; + firstRec = group.children[0]; + lastRec = group.children[group.children.length - 1]; + + + + store.fireEvent('update', store, firstRec, 'edit', null); + if (lastRec !== firstRec && me.showSummaryRow) { + store.fireEvent('update', store, lastRec, 'edit', null); + } + } + } + Ext.resumeLayouts(true); + } + }, + + showMenuBy: function(t, header) { + var menu = this.getMenu(), + groupMenuItem = menu.down('#groupMenuItem'), + groupMenuMeth = header.groupable === false || header.dataIndex == null || this.view.headerCt.getVisibleGridColumns().length < 2 ? 'disable' : 'enable', + groupToggleMenuItem = menu.down('#groupToggleMenuItem'), + isGrouped = this.view.store.isGrouped(); + + groupMenuItem[groupMenuMeth](); + if (groupToggleMenuItem) { + groupToggleMenuItem.setChecked(isGrouped, true); + groupToggleMenuItem[isGrouped ? 'enable' : 'disable'](); + } + Ext.grid.header.Container.prototype.showMenuBy.apply(this, 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; + + + return function() { + + + + 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; + }; + }, + + + onGroupMenuItemClick: function(menuItem, e) { + var me = this, + menu = menuItem.parentMenu, + hdr = menu.activeHeader, + view = me.view, + store = view.store; + + me.lastGroupers = null; + me.block(); + me.enable(); + store.group(hdr.dataIndex); + me.pruneGroupedHeader(); + me.unblock(); + me.refreshIf(); + }, + + block: function(fromPartner) { + this.blockRefresh = this.view.blockRefresh = true; + if (this.lockingPartner && !fromPartner) { + this.lockingPartner.block(true); + } + }, + + unblock: function(fromPartner) { + this.blockRefresh = this.view.blockRefresh = false; + if (this.lockingPartner && !fromPartner) { + this.lockingPartner.unblock(true); + } + }, + + + onGroupToggleMenuItemClick: function(menuItem, checked) { + this[checked ? 'enable' : 'disable'](); + }, + + + 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; + header.hide(); + Ext.resumeLayouts(true); + } + }, + + getHeaderNode: function(groupName) { + return Ext.get(this.createGroupId(groupName)); + }, + + getGroup: function(name) { + if (name.isModel) { + name = name.get(this.view.store.getGroupField()); + } + var cache = this.groupCache, + item = cache[name]; + + if (!item) { + item = cache[name] = { + isCollapsed: false + }; + } + return item; + }, + + + isExpanded: function(groupName) { + return !this.getGroup(groupName).isCollapsed; + }, + + + expand: function(groupName, focus) { + this.doCollapseExpand(false, groupName, focus); + }, + + + expandAll: function() { + var me = this, + groupCache = me.groupCache, + groupName, + lockingPartner = me.lockingPartner; + + + + for (groupName in groupCache) { + if (groupCache.hasOwnProperty(groupName)) { + groupCache[groupName].isCollapsed = false; + } + } + Ext.suspendLayouts(); + me.dataSource.onRefresh(); + Ext.resumeLayouts(true); + + + for (groupName in groupCache) { + if (groupCache.hasOwnProperty(groupName)) { + me.afterCollapseExpand(false, groupName); + if (lockingPartner) { + lockingPartner.afterCollapseExpand(false, groupName); + } + } + } + }, + + + collapse: function(groupName, focus) { + this.doCollapseExpand(true, groupName, focus); + }, + + + + isAllCollapsed: function() { + var me = this, + groupCache = me.groupCache, + groupName; + + + + for (groupName in groupCache) { + if (groupCache.hasOwnProperty(groupName)) { + if (!groupCache[groupName].isCollapsed) { + return false; + } + } + } + return true; + }, + + + + isAllExpanded: function() { + var me = this, + groupCache = me.groupCache, + groupName; + + + + for (groupName in groupCache) { + if (groupCache.hasOwnProperty(groupName)) { + if (groupCache[groupName].isCollapsed) { + return false; + } + } + } + return true; + }, + + + collapseAll: function() { + var me = this, + groupCache = me.groupCache, + groupName, + lockingPartner = me.lockingPartner; + + + + for (groupName in groupCache) { + if (groupCache.hasOwnProperty(groupName)) { + groupCache[groupName].isCollapsed = true; + } + } + Ext.suspendLayouts(); + me.dataSource.onRefresh(); + + if (lockingPartner && !lockingPartner.isAllCollapsed()) { + lockingPartner.collapseAll(); + } + Ext.resumeLayouts(true); + + + for (groupName in groupCache) { + if (groupCache.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.groupCache[groupName]; + + + if (group.isCollapsed !== collapsed) { + + + + Ext.suspendLayouts(); + if (collapsed) { + me.dataSource.collapseGroup(group); + } else { + me.dataSource.expandGroup(group); + } + Ext.resumeLayouts(true); + + + me.afterCollapseExpand(collapsed, groupName, focus); + + + + if (lockingPartner) { + lockingPartner.afterCollapseExpand(collapsed, groupName, false); + } + } + }, + + afterCollapseExpand: function(collapsed, groupName, focus) { + var me = this, + view = me.view, + header; + + header = Ext.get(this.getHeaderNode(groupName)); + view.fireEvent(collapsed ? 'groupcollapse' : 'groupexpand', view, header, groupName); + if (focus) { + header.up(view.getItemSelector()).scrollIntoView(view.el, null, true); + } + }, + + onGroupChange: function() { + var me = this, + field = me.getGroupField(), + menuItem, + visibleGridColumns, + groupingByLastVisibleColumn, + newChecked; + + if (me.hideGroupedHeader) { + if (me.lastGroupers && me.lastGroupers.length) { + menuItem = me.getMenuItem(me.lastGroupers[0].property); + if (menuItem) { + newChecked = true; + } + } + if (field) { + visibleGridColumns = me.view.headerCt.getVisibleGridColumns(); + + + + groupingByLastVisibleColumn = ((visibleGridColumns.length === 1) && (visibleGridColumns[0].dataIndex === field)); + menuItem = me.getMenuItem(field); + if (menuItem && !groupingByLastVisibleColumn) { + newChecked = false; + } + } + + + if (menuItem && menuItem.checked !== newChecked) { + menuItem.setChecked(newChecked); + } + } + me.refreshIf(); + me.lastGroupers = me.view.store.groupers.getRange(); + }, + + + 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); + } + }, + + + onGroupClick: function(view, rowElement, groupName, e) { + var me = this, + groupCache = me.groupCache, + groupIsCollapsed = !me.isExpanded(groupName), + g; + + if (me.collapsible) { + + + if (e.ctrlKey) { + Ext.suspendLayouts(); + for (g in groupCache) { + if (g === groupName) { + if (groupIsCollapsed) { + me.expand(groupName); + } + } else if (!groupCache[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, + groupInfo = me.groupInfo, + header = data.header, + groupField = data.groupField, + store = me.view.store, + dataSource = me.view.dataSource, + grouper, groupName, prev, next; + + rowValues.isCollapsedGroup = false; + rowValues.summaryRecord = null; + + if (data.doGrouping) { + grouper = me.view.store.groupers.first(); + + + + if (record.children) { + groupName = grouper.getGroupString(record.children[0]); + + rowValues.isFirstRow = rowValues.isLastRow = true; + rowValues.itemClasses.push(me.hdCollapsedCls); + rowValues.isCollapsedGroup = rowValues.needsWrap = true; + rowValues.groupInfo = groupInfo; + groupInfo.groupField = groupField; + groupInfo.name = groupName; + groupInfo.groupValue = record.children[0].get(groupField); + groupInfo.columnName = header ? header.text : groupField; + rowValues.collapsibleCls = me.collapsible ? me.collapsibleCls : me.hdNotCollapsibleCls; + rowValues.groupId = me.createGroupId(groupName); + groupInfo.rows = groupInfo.children = record.children; + if (me.showSummaryRow) { + rowValues.summaryRecord = data.summaryData[groupName]; + } + return; + } + + groupName = grouper.getGroupString(record); + + + if (record.group) { + rowValues.isFirstRow = record === record.group.children[0]; + rowValues.isLastRow = record === record.group.children[record.group.children.length - 1]; + } + + else { + + rowValues.isFirstRow = recordIndex === 0; + if (!rowValues.isFirstRow) { + prev = store.getAt(recordIndex - 1); + + if (prev) { + + rowValues.isFirstRow = !prev.isEqual(grouper.getGroupString(prev), groupName); + } + } + + + rowValues.isLastRow = recordIndex == (store.buffered ? store.getTotalCount() : store.getCount()) - 1; + if (!rowValues.isLastRow) { + next = store.getAt(recordIndex + 1); + if (next) { + + rowValues.isLastRow = !next.isEqual(grouper.getGroupString(next), groupName); + } + } + } + + if (rowValues.isFirstRow) { + groupInfo.groupField = groupField; + groupInfo.name = groupName; + groupInfo.groupValue = record.get(groupField); + groupInfo.columnName = header ? header.text : groupField; + rowValues.collapsibleCls = me.collapsible ? me.collapsibleCls : me.hdNotCollapsibleCls; + rowValues.groupId = me.createGroupId(groupName); + + if (!me.isExpanded(groupName)) { + rowValues.itemClasses.push(me.hdCollapsedCls); + rowValues.isCollapsedGroup = true; + } + + + if (dataSource.buffered) { + groupInfo.rows = groupInfo.children = []; + } else { + groupInfo.rows = groupInfo.children = me.getRecordGroup(record).children; + } + rowValues.groupInfo = groupInfo; + } + + if (rowValues.isLastRow) { + + if (me.showSummaryRow) { + rowValues.summaryRecord = data.summaryData[groupName]; + } + } + rowValues.needsWrap = (rowValues.isFirstRow || rowValues.summaryRecord); + } + }, + + setup: function(rows, rowValues) { + var me = this, + data = me.refreshData, + isGrouping = !me.disabled && me.view.isGrouping; + + me.skippedRows = 0; + if (rowValues.view.bufferedRenderer) { + rowValues.view.bufferedRenderer.variableRowHeight = true; + } + 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.groupInfo = rowValues.groupHeaderTpl = rowValues.isFirstRow = null; + data.groupField = data.header = null; + }, + + getGroupName: function(element) { + var me = this, + view = me.view, + eventSelector = me.eventSelector, + parts, + targetEl, + row; + + + targetEl = Ext.fly(element).findParent(eventSelector); + + if (!targetEl) { + + row = Ext.fly(element).findParent(view.itemSelector); + if (row) { + targetEl = row.down(eventSelector, true); + } + } + + if (targetEl) { + parts = targetEl.id.split(view.id + '-hd-'); + if (parts.length === 2) { + return Ext.htmlDecode(parts[1]); + } + } + }, + + + getRecordGroup: function(record) { + var grouper = this.view.store.groupers.first(); + if (grouper) { + return this.groupCache[grouper.getGroupString(record)]; + } + }, + + createGroupId: function(group) { + return this.view.id + '-hd-' + Ext.htmlEncode(group); + }, + + createGroupCls: function(group) { + return this.view.id + '-' + Ext.htmlEncode(group) + '-item'; + }, + + getGroupField: function(){ + return this.view.store.getGroupField(); + }, + + getGroupedHeader: function(groupField) { + var me = this, + headerCt = me.view.headerCt, + partner = me.lockingPartner, + selector, header; + + groupField = groupField || this.getGroupField(); + + if (groupField) { + selector = '[dataIndex=' + groupField + ']'; + header = headerCt.down(selector); + + 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.view = me.prunedHeader = me.grid = me.groupCache = me.dataSource = null; + me.callParent(); + if (dataSource) { + dataSource.bindStore(null); + } + }, + + onReconfigure: function(grid, store, columns, oldStore, oldColumns) { + var bufferedStore; + + if (store && store !== oldStore) { + bufferedStore = store.buffered; + + + if (bufferedStore !== oldStore.buffered) { + Ext.Error.raise('Cannot reconfigure grouping switching between buffered and non-buffered stores'); + } + + if (bufferedStore || grid.findPlugin('bufferedrenderer')) { + + grid.view.dataSource.bindStore(store); + } + } + } +}); + + + +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) { + if (e.getTarget(this.summaryRowSelector)) { + result = false; + } + } + return result; + } +}); + + + +Ext.define('Ext.grid.feature.Summary', { + + + + extend: Ext.grid.feature.AbstractSummary , + + alias: 'feature.summary', + + + dock: undefined, + + dockedSummaryCls: Ext.baseCSSPrefix + 'docked-summary', + + panelBodyCls: Ext.baseCSSPrefix + 'summary-', + + scrollPadProperty: 'padding-right', + + + hasFeatureEvent: false, + + init: function(grid) { + var me = this, + view = me.view; + + me.callParent(arguments); + + if (me.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'], + renderTpl: [ + '' + ], + style: 'overflow:hidden', + itemId: 'summaryBar', + cls: [ me.dockedSummaryCls, me.dockedSummaryCls + '-' + me.dock ], + xtype: 'component', + dock: me.dock, + weight: 10000000 + })[0]; + }, + afterrender: function() { + grid.body.addCls(me.panelBodyCls + me.dock); + view.mon(view.el, { + scroll: me.onViewScroll, + scope: me + }); + me.onStoreUpdate(); + }, + single: true + }); + + + grid.headerCt.afterComponentLayout = Ext.Function.createSequence(grid.headerCt.afterComponentLayout, function() { + var width = this.getTableWidth(), + innerCt = me.summaryBar.innerCt, + scrollWidth; + + if (view.hasVerticalScroll()) { + scrollWidth = Ext.getScrollbarSize().width; + width -= scrollWidth; + innerCt.down('table').setStyle(me.scrollPadProperty, scrollWidth + 'px'); + } + innerCt.setWidth(width); + }); + } else { + me.view.addFooterFn(me.renderTFoot); + } + + grid.on({ + columnmove: me.onStoreUpdate, + scope: me + }); + + + view.mon(view.store, { + update: me.onStoreUpdate, + datachanged: me.onStoreUpdate, + scope: me + }); + }, + + renderTFoot: function(values, out) { + var view = values.view, + me = view.findFeature('summary'); + + if (me.showSummaryRow) { + out.push(''); + me.outputSummaryRecord(me.createSummaryRecord(view), values, out); + out.push(''); + } + }, + + toggleSummaryRow: function(visible) { + var me = this, + bar = me.summaryBar; + + me.callParent(arguments); + if (bar) { + bar.setVisible(me.showSummaryRow); + me.onViewScroll(); + } + }, + + vetoEvent: function(record, row, rowIndex, e) { + return !e.getTarget(this.summaryRowSelector); + }, + + onViewScroll: function() { + this.summaryBar.el.dom.scrollLeft = this.view.el.dom.scrollLeft; + }, + + createSummaryRecord: function (view) { + var columns = view.headerCt.getVisibleGridColumns(), + info = { + records: view.store.getRange() + }, + colCount = columns.length, i, column, + summaryRecord = this.summaryRecord || (this.summaryRecord = new view.store.model(null, view.id + '-summary-record')), + dataIndex, summaryValue; + + + summaryRecord.beginEdit(); + for (i = 0; i < colCount; i++) { + column = columns[i]; + + + + dataIndex = column.dataIndex || column.id; + + + + summaryValue = this.getSummary(view.store, column.summaryType, dataIndex, info); + summaryRecord.set(dataIndex, summaryValue); + + + this.setSummaryData(summaryRecord, column.id, summaryValue); + } + + summaryRecord.endEdit(true); + + summaryRecord.commit(true); + summaryRecord.isSummary = true; + + return summaryRecord; + }, + + onStoreUpdate: function() { + var me = this, + view = me.view, + record = me.createSummaryRecord(view), + newRowDom = view.createRowElement(record, -1), + oldRowDom, + p; + + if (!view.rendered) { + return; + } + + + if (me.dock) { + oldRowDom = me.summaryBar.el.down('.' + me.summaryRowCls, true); + } + + + else { + oldRowDom = me.view.getNode(record); + } + + if (oldRowDom) { + p = oldRowDom.parentNode; + p.insertBefore(newRowDom, oldRowDom); + p.removeChild(oldRowDom); + } + + if (me.dock) { + me.onColumnHeaderLayout(); + } + }, + + + 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)); + if (el) { + if (column.hidden) { + el.setDisplayed(false); + } else { + el.setDisplayed(true); + el.setWidth(column.width || (column.lastBox ? column.lastBox.width : 100)); + } + } + } + } +}); + + + +Ext.define('Ext.grid.locking.HeaderContainer', { + extend: Ext.grid.header.Container , + + + + + constructor: function(lockable) { + var me = this, + events, + event, + eventNames = [], + lockedGrid = lockable.lockedGrid, + normalGrid = lockable.normalGrid; + + me.lockable = lockable; + me.callParent(); + + + 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); + + + events = lockedGrid.headerCt.events; + for (event in events) { + if (events.hasOwnProperty(event)) { + eventNames.push(event); + } + } + me.relayEvents(lockedGrid.headerCt, eventNames); + me.relayEvents(normalGrid.headerCt, eventNames); + }, + + getRefItems: function() { + return this.lockable.lockedGrid.headerCt.getRefItems().concat(this.lockable.normalGrid.headerCt.getRefItems()); + }, + + + + getGridColumns: function() { + return this.lockable.lockedGrid.headerCt.getGridColumns().concat(this.lockable.normalGrid.headerCt.getGridColumns()); + }, + + + getColumnsState: function () { + var me = this, + locked = me.lockable.lockedGrid.headerCt.getColumnsState(), + normal = me.lockable.normalGrid.headerCt.getColumnsState(); + + return locked.concat(normal); + }, + + + applyColumnsState: function (columns) { + var me = this, + lockedGrid = me.lockable.lockedGrid, + lockedHeaderCt = lockedGrid.headerCt, + normalHeaderCt = me.lockable.normalGrid.headerCt, + lockedCols = Ext.Array.toValueMap(lockedHeaderCt.items.items, 'headerId'), + normalCols = Ext.Array.toValueMap(normalHeaderCt.items.items, 'headerId'), + 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); + } + 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); + } + } + } + + + 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); + } + } +}); + + + +Ext.define('Ext.grid.locking.View', { + alternateClassName: 'Ext.grid.LockingView', + + + + + mixins: { + observable: Ext.util.Observable , + bindable: Ext.util.Bindable + }, + + + isLockingView: true, + + loadMask: true, + + eventRelayRe: /^(beforeitem|beforecontainer|item|container|cell|refresh)/, + + constructor: function(config){ + var me = this, + eventNames = [], + eventRe = me.eventRelayRe, + lockedView, + normalView, + events, + event; + + me.panel = config.panel; + + + + + + config.locked.viewConfig.bindStore = config.normal.viewConfig.bindStore = Ext.emptyFn; + + + + + config.locked.viewConfig.beforeLayout = config.normal.viewConfig.beforeLayout = me.beforeLayout; + + me.lockedGrid = me.panel.lockedGrid = Ext.ComponentManager.create(config.locked); + me.lockedView = lockedView = me.lockedGrid.getView(); + + if (me.lockedGrid.isTree) { + + me.lockedView.animate = false; + + + config.normal.store = lockedView.store; + + + config.normal.viewConfig.stripeRows = me.lockedView.stripeRows; + config.normal.rowLines = me.lockedGrid.rowLines; + } + + + + + + me.normalGrid = me.panel.normalGrid = Ext.ComponentManager.create(config.normal); + lockedView.lockingPartner = normalView = me.normalView = me.normalGrid.getView(); + normalView.lockingPartner = lockedView; + + + + + me.deferInitialRefresh = normalView.deferInitialRefresh && lockedView.deferInitialRefresh; + + me.loadMask = (config.loadMask !== undefined) ? config.loadMask : me.loadMask; + + me.mixins.observable.constructor.call(me); + + + events = lockedView.events; + for (event in events) { + if (events.hasOwnProperty(event) && eventRe.test(event)) { + eventNames.push(event); + } + } + me.relayEvents(lockedView, eventNames); + me.relayEvents(normalView, eventNames); + + normalView.on({ + scope: me, + itemmouseleave: me.onItemMouseLeave, + itemmouseenter: me.onItemMouseEnter + }); + + lockedView.on({ + scope: me, + itemmouseleave: me.onItemMouseLeave, + itemmouseenter: me.onItemMouseEnter + }); + + me.panel.on({ + render: me.onPanelRender, + scope: me + }); + + me.loadingText = normalView.loadingText; + me.loadingCls = normalView.loadingCls; + me.loadingUseMsg = normalView.loadingUseMsg; + + + + + me.bindStore(normalView.dataSource, true, 'dataSource'); + }, + + + beforeLayout: function() { + + var me = this.ownerCt.ownerLockable.view, + lockedView = me.lockedGrid.view, + normalView = me.normalGrid.view; + + + + 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.panel, + msg: me.loadingText, + msgCls: me.loadingCls, + useMsg: me.loadingUseMsg, + store: me.panel.store + }; + + + + me.el = me.panel.body; + me.fireEvent('render', me); + + if (mask) { + + if (Ext.isObject(mask)) { + cfg = Ext.apply(cfg, mask); + } + + + + + me.loadMask = new Ext.LoadMask(cfg); + } + }, + + getRefOwner: function() { + return this.panel; + }, + + getGridColumns: function() { + var cols = this.lockedGrid.headerCt.getVisibleGridColumns(); + return cols.concat(this.normalGrid.headerCt.getVisibleGridColumns()); + }, + + getEl: function(column){ + return this.getViewForColumn(column).getEl(); + }, + + 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, false); + 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 view = this.lockedView; + view[name].apply(view, args); + view = this.normalView; + view[name].apply(view, args); + }, + + getSelectionModel: function(){ + return this.panel.getSelectionModel(); + }, + + getStore: function(){ + return this.panel.store; + }, + + + bindStore : function(store, initial, propName) { + var me = this; + me.mixins.bindable.bindStore.apply(me, arguments); + + + + if (!initial) { + me.getSelectionModel().bindStore(store); + } + + + + + if (me.componentLayoutCounter) { + Ext.suspendLayouts(); + me.normalView.doFirstRefresh(store); + me.lockedView.doFirstRefresh(store); + Ext.resumeLayouts(true); + } + }, + + getStoreListeners: function() { + var me = this; + return { + idchanged: me.onIdChanged, + refresh: me.onDataRefresh, + replace: me.onReplace, + add: me.onAdd, + bulkremove: me.onRemove, + update: me.onUpdate, + clear: me.refresh + }; + }, + + onIdChanged: function(eventName, fn, scope){ + this.relayFn('onIdChanged', arguments); + }, + + onDataRefresh: function(eventName, fn, scope){ + this.relayFn('onDataRefresh', arguments); + }, + + onReplace: function(eventName, fn, scope){ + this.relayFn('onReplace', arguments); + }, + + onAdd: function(eventName, fn, scope){ + this.relayFn('onAdd', arguments); + }, + + onRemove: function(eventName, fn, scope){ + this.relayFn('onRemove', arguments); + }, + + onUpdate: function(eventName, fn, scope){ + this.relayFn('onUpdate', arguments); + }, + + refresh: function(eventName, fn, scope){ + this.relayFn('refresh', arguments); + }, + + getNode: function(nodeInfo, dataRow) { + + return this.normalView.getNode(nodeInfo, dataRow); + }, + + getCell: function(record, column) { + var view = this.getViewForColumn(column), + row = view.getNode(record, true); + + 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() { + var p = this.getSelectionModel().getCurrentPosition(), + v = p ? p.view : this.normalView; + + v.focus(); + }, + + focusRow: function(row) { + this.normalView.focusRow(row); + }, + + focusCell: function(position) { + position.view.focusCell(position); + }, + + isVisible: function(deep) { + return this.panel.isVisible(deep); + }, + + getCellByPosition: function(pos, returnDom) { + var col = pos.column, + lockedSize = this.lockedGrid.getColumnManager().getColumns().length; + + + if (col >= lockedSize) { + + pos = Ext.apply({}, pos); + pos.column -= lockedSize; + return this.normalView.getCellByPosition(pos, returnDom); + } else { + return this.lockedView.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); + }, + + addElListener: function(eventName, fn, scope){ + 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, + mask = me.loadMask; + + + this.isDestroyed = true; + + + + me.clearListeners(); + if (mask && mask.bindStore) { + mask.bindStore(null); + } + } + +}, function() { + this.borrow(Ext.AbstractComponent, ['up']); + this.borrow(Ext.view.AbstractView, ['doFirstRefresh', 'applyFirstRefresh']); +}); + + + +Ext.define('Ext.grid.locking.Lockable', { + alternateClassName: 'Ext.grid.Lockable', + + + + + + + + + supportsOverflowX: 'overflow-x' in document.documentElement.style, + + + syncRowHeight: true, + + + + + + + + headerCounter: 0, + + + scrollDelta: 40, + + + + + + + + + + lockedGridCls: Ext.baseCSSPrefix + 'grid-inner-locked', + normalGridCls: Ext.baseCSSPrefix + 'grid-inner-normal', + + + + unlockText: 'Unlock', + + + lockText: 'Lock', + + + + + bothCfgCopy: [ + 'invalidateScrollerOnRefresh', + 'hideHeaders', + 'enableColumnHide', + 'enableColumnMove', + 'enableColumnResize', + 'sortableColumns', + 'multiColumnSort', + 'columnLines', + 'rowLines', + 'deferRowRender' + ], + normalCfgCopy: [ + 'verticalScroller', + 'verticalScrollDock', + 'verticalScrollerType', + 'scroll' + ], + lockedCfgCopy: [], + + determineXTypeToCreate: function(lockedSide) { + var me = this, + typeToCreate, + xtypes, xtypesLn, xtype, superxtype; + + if (me.subGridXType) { + typeToCreate = me.subGridXType; + } else { + + + 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: function() { + + this.lockable = true; + + + this.hasView = true; + + var me = this, + scrollbarHeight = Ext.getScrollbarSize().height, + store = me.store = Ext.StoreManager.lookup(me.store), + + + selModel = me.getSelectionModel(), + + + allFeatures, + + + allPlugins, + + lockedGrid, + normalGrid, + i, + columns, + lockedHeaderCt, + normalHeaderCt, + lockedView, + normalView, + listeners, + viewConfig = me.viewConfig, + + loadMaskCfg = viewConfig && viewConfig.loadMask, + loadMask = (loadMaskCfg !== undefined) ? loadMaskCfg : me.loadMask, + bufferedRenderer = me.findPlugin('bufferedrenderer'); + + allFeatures = me.constructLockableFeatures(); + + + if (me.features) { + me.features = null; + } + + + allPlugins = me.constructLockablePlugins(); + me.plugins = allPlugins.topPlugins; + + lockedGrid = Ext.apply({ + id: me.id + '-locked', + isLocked: true, + ownerLockable: me, + xtype: me.determineXTypeToCreate(true), + store: store, + scrollerOwner: false, + + + animate: false, + + scroll: scrollbarHeight ? false : 'vertical', + selModel: selModel, + border: false, + cls: me.lockedGridCls, + + + + + + isLayoutRoot: function() { + return this.floatedFromCollapse || me.normalGrid.floatedFromCollapse; + }, + features: allFeatures.lockedFeatures, + plugins: allPlugins.lockedPlugins + }, me.lockedGridConfig); + + normalGrid = Ext.apply({ + id: me.id + '-normal', + isLocked: false, + ownerLockable: me, + xtype: me.determineXTypeToCreate(), + store: store, + scrollerOwner: false, + selModel: selModel, + border: false, + cls: me.normalGridCls, + + + isLayoutRoot: function() { + return this.floatedFromCollapse || me.lockedGrid.floatedFromCollapse; + }, + features: allFeatures.normalFeatures, + plugins: allPlugins.normalPlugins + }, me.normalGridConfig); + + me.addCls(Ext.baseCSSPrefix + 'grid-locked'); + + + + + Ext.copyTo(normalGrid, me, me.bothCfgCopy, true); + Ext.copyTo(lockedGrid, me, me.bothCfgCopy, true); + Ext.copyTo(normalGrid, me, me.normalCfgCopy, true); + Ext.copyTo(lockedGrid, me, me.lockedCfgCopy, true); + 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.addEvents( + + 'processcolumns', + + + 'lockcolumn', + + + 'unlockcolumn' + ); + + me.addStateEvents(['lockcolumn', 'unlockcolumn']); + + columns = me.processColumns(me.columns, lockedGrid); + + lockedGrid.columns = columns.locked; + + + if (!lockedGrid.columns.items.length) { + lockedGrid.hidden = true; + } + + normalGrid.columns = columns.normal; + + + normalGrid.flex = 1; + lockedGrid.viewConfig = me.lockedViewConfig || {}; + normalGrid.viewConfig = me.normalViewConfig || {}; + lockedGrid.viewConfig.loadingUseMsg = false; + lockedGrid.viewConfig.loadMask = false; + + + + + + + if (scrollbarHeight && !me.supportsOverflowX) { + lockedGrid.viewConfig.style = 'border-bottom:' + scrollbarHeight + + 'px solid #f6f6f6;' + (lockedGrid.viewConfig.style || ''); + normalGrid.viewConfig.style = 'border-bottom:' + scrollbarHeight + + 'px solid #f6f6f6;' + (lockedGrid.viewConfig.style || ''); + } + + normalGrid.viewConfig.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(lockedGrid.viewConfig, viewConfig); + Ext.applyIf(normalGrid.viewConfig, viewConfig); + + + + if (!me.initialConfig.layout) { + me.layout = { + type: 'hbox', + align: 'stretch' + }; + } + me.getLayout(); + + + + if (me.layout.type === 'border') { + if (me.split) { + lockedGrid.split = true; + } + if (!normalGrid.title) { + lockedGrid.header = false; + } + 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; + } + + + + me.view = new Ext.grid.locking.View({ + loadMask: loadMask, + locked: lockedGrid, + normal: normalGrid, + panel: me + }); + + + + + + lockedView = me.lockedGrid.getView(); + normalView = me.normalGrid.getView(); + + + listeners = bufferedRenderer ? {} : { + scroll: { + fn: me.onLockedViewScroll, + element: 'el', + scope: me + } + }; + + + + + + if (scrollbarHeight) { + me.lockedGrid.on({ + afterlayout: me.afterLockedViewLayout, + scope: me + }); + + + lockedView.getOverflowStyle(); + + + if (lockedView.scrollFlags.y) { + me.lockedGrid.headerCt.forceFit = true; + } + + else { + listeners.mousewheel = { + fn: me.onLockedViewMouseWheel, + element: 'el', + scope: me + }; + } + } + lockedView.on(listeners); + + + listeners = bufferedRenderer ? {} : { + scroll: { + fn: me.onNormalViewScroll, + element: 'el', + scope: me + }, + scope: me + }; + normalView.on(listeners); + + lockedHeaderCt = me.lockedGrid.headerCt; + normalHeaderCt = me.normalGrid.headerCt; + + + + me.headerCt = me.view.headerCt = new Ext.grid.locking.HeaderContainer(me); + + lockedHeaderCt.lockedCt = true; + lockedHeaderCt.lockableInjected = true; + normalHeaderCt.lockableInjected = true; + + lockedHeaderCt.on({ + + add: { + buffer: 1, + scope: me, + fn: me.onLockedHeaderAdd + }, + columnshow: me.onLockedHeaderShow, + columnhide: me.onLockedHeaderHide, + sortchange: me.onLockedHeaderSortChange, + columnresize: me.onLockedHeaderResize, + scope: me + }); + + normalHeaderCt.on({ + sortchange: me.onNormalHeaderSortChange, + scope: me + }); + + me.modifyHeaderCt(); + me.items = [me.lockedGrid]; + if (me.split) { + me.addCls(Ext.baseCSSPrefix + 'grid-locked-split'); + me.items[1] = { + xtype: 'splitter' + }; + } + me.items.push(me.normalGrid); + + me.relayHeaderCtEvents(lockedHeaderCt); + me.relayHeaderCtEvents(normalHeaderCt); + + + + me.storeRelayers = me.relayEvents(store, [ + + 'filterchange', + + 'groupchange' + ]); + + + + me.gridRelayers = me.relayEvents(me.normalGrid, [ + + 'viewready' + ]); + }, + + getLockingViewConfig: function(){ + return { + xclass: 'Ext.grid.locking.View', + locked: this.lockedGrid, + normal: this.normalGrid, + panel: this + }; + }, + + processColumns: function (columns, lockedGrid) { + + var me = this, + i, + len, + column, + cp = new Ext.grid.header.Container(), + lockedHeaders = [], + normalHeaders = [], + lockedHeaderCt = { + itemId: 'lockedHeaderCt', + stretchMaxPartner: '^^>>#normalHeaderCt', + items: lockedHeaders + }, + normalHeaderCt = { + itemId: 'normalHeaderCt', + stretchMaxPartner: '^^>>#lockedHeaderCt', + items: normalHeaders + }, + result = { + lockedWidth: lockedGrid.width || 0, + locked: lockedHeaderCt, + normal: normalHeaderCt + }, + shrinkWrapLocked = me.shrinkWrapLocked = !(lockedGrid.width || lockedGrid.flex), + copy; + + + 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; + } + + for (i = 0, len = columns.length; i < len; ++i) { + column = columns[i]; + + + + + if (!column.isComponent) { + column = cp.lookupComponent(cp.applyDefaults(column)); + } + + + + column.processed = true; + if (column.locked || column.autoLock) { + + + if (shrinkWrapLocked && !column.hidden) { + result.lockedWidth += me.getColumnWidth(column) || cp.defaultWidth; + } + lockedHeaders.push(column); + } else { + normalHeaders.push(column); + } + if (!column.headerId) { + column.headerId = (column.initialConfig || column).id || ('h' + (++me.headerCounter)); + } + } + me.fireEvent('processcolumns', me, lockedHeaders, normalHeaders); + cp.destroy(); + + + + + + if (shrinkWrapLocked) { + lockedGrid.width = (result.lockedWidth += Ext.num(me.getSelectionModel().headerWidth, 0) + (lockedHeaders.length ? 1 : 0)); + } + return result; + }, + + + + getColumnWidth: function(column) { + var result = column.width || 0, + subcols, len, i; + + if (column.flex) { + Ext.Error.raise("Locked columns in an unsized locked side do NOT support a flex width. You must set a width on the " + column.text + "column."); + } + if (!result && column.isGroupHeader) { + subcols = column.items.items; + len = subcols.length; + for (i = 0; i < len; i++) { + result += this.getColumnWidth(subcols[i]); + } + } + return result; + }, + + + + + + afterLockedViewLayout: function() { + var me = this, + lockedView = me.lockedGrid.getView(), + normalView = me.normalGrid.getView(), + lockedViewEl = lockedView.el, + normalViewEl = normalView.el, + spacerHeight = Ext.getScrollbarSize().height, + lockedViewHorizScrollBar = (lockedView.scrollFlags.x && me.lockedGrid.headerCt.tooNarrow ? spacerHeight : 0), + normalViewHorizScrollBar = (normalView.scrollFlags.x && me.normalGrid.headerCt.tooNarrow ? spacerHeight : 0); + + if (lockedViewHorizScrollBar !== normalViewHorizScrollBar) { + if (lockedViewHorizScrollBar) { + if (me.supportsOverflowX) { + normalViewEl.dom.style.overflowX = 'scroll'; + lockedView.getOverflowEl().setStyle(lockedView.getOverflowStyle()); + } else { + normalViewEl.dom.style.borderBottomWidth = lockedViewHorizScrollBar + 'px'; + lockedViewEl.dom.style.borderBottomWidth = '0px'; + } + } else { + if (me.supportsOverflowX) { + lockedViewEl.dom.style.overflowX = 'scroll'; + normalView.getOverflowEl().setStyle(normalView.getOverflowStyle()); + } else { + lockedViewEl.dom.style.borderBottomWidth = normalViewHorizScrollBar + 'px'; + normalViewEl.dom.style.borderBottomWidth = '0px'; + } + } + } else { + if (me.supportsOverflowX) { + lockedView.getOverflowEl().dom.style.overflowX = normalViewHorizScrollBar ? 'scroll' : ''; + normalView.getOverflowEl().setStyle(normalView.getOverflowStyle()); + } else { + normalViewEl.dom.style.borderBottomWidth = lockedViewEl.dom.style.borderBottomWidth = '0px'; + } + } + + + + if (!Ext.isBorderBox && !me.supportsOverflowX) { + lockedViewEl.setHeight(lockedView.lastBox.height); + normalViewEl.setHeight(normalView.lastBox.height); + } + }, + + + onLockedViewMouseWheel: function(e) { + var me = this, + deltaY = -me.scrollDelta * e.getWheelDeltas().y, + vertScrollerEl = me.lockedGrid.getView().el.dom, + verticalCanScrollDown, verticalCanScrollUp; + + if (!me.ignoreMousewheel) { + if (vertScrollerEl) { + verticalCanScrollDown = vertScrollerEl.scrollTop !== vertScrollerEl.scrollHeight - vertScrollerEl.clientHeight; + verticalCanScrollUp = vertScrollerEl.scrollTop !== 0; + } + + if ((deltaY < 0 && verticalCanScrollUp) || (deltaY > 0 && verticalCanScrollDown)) { + e.stopEvent(); + + + + + vertScrollerEl.scrollTop += deltaY; + me.normalGrid.getView().el.dom.scrollTop = vertScrollerEl.scrollTop; + + + me.onNormalViewScroll(); + } + } + }, + + onLockedViewScroll: function() { + var me = this, + normalDom = me.normalGrid.getView().el.dom, + lockedDom = me.lockedGrid.getView().el.dom; + + if (normalDom.scrollTop !== lockedDom.scrollTop) { + normalDom.scrollTop = lockedDom.scrollTop; + } + }, + + onNormalViewScroll: function() { + var me = this, + normalDom = me.normalGrid.getView().el.dom, + lockedDom = me.lockedGrid.getView().el.dom; + + if (normalDom.scrollTop !== lockedDom.scrollTop) { + lockedDom.scrollTop = normalDom.scrollTop; + } + }, + + + syncRowHeights: function() { + var me = this, + i, + lockedView = me.lockedGrid.getView(), + normalView = me.normalGrid.getView(), + lockedRowEls = lockedView.all.slice(), + normalRowEls = normalView.all.slice(), + ln = lockedRowEls.length, + scrollTop; + + + if (normalRowEls.length === ln) { + + + for (i = 0; i < ln; i++) { + normalView.syncRowHeights(lockedRowEls[i], normalRowEls[i]); + } + + + scrollTop = normalView.el.dom.scrollTop; + normalView.el.dom.scrollTop = scrollTop; + lockedView.el.dom.scrollTop = scrollTop; + } + }, + + + + 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(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 = Ext.Function.bind(me.onUnlockMenuClick, me), + lockHandler = Ext.Function.bind(me.onLockMenuClick, me); + + + return function() { + + + + 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; + }; + }, + + + syncLockedWidth: function() { + var me = this, + locked = me.lockedGrid, + lockedView = locked.view, + lockedViewEl = lockedView.el.dom, + normal = me.normalGrid, + lockedColCount = locked.headerCt.getVisibleGridColumns().length, + normalColCount = normal.headerCt.getVisibleGridColumns().length; + + Ext.suspendLayouts(); + + + + if (normalColCount) { + normal.show(); + if (lockedColCount) { + + + + if (me.shrinkWrapLocked && !locked.headerCt.forceFit) { + delete locked.flex; + + locked.setWidth(locked.headerCt.getTableWidth() + locked.el.getBorderWidth('lr')); + } + locked.addCls(me.lockedGridCls); + locked.show(); + if (me.split) { + me.child('splitter').show(); + } + } else { + + + + locked.getView().clearViewEl(); + locked.hide(); + if (me.split) { + me.child('splitter').hide(); + } + } + + + lockedView.el.setStyle(lockedView.getOverflowStyle()); + + + me.ignoreMousewheel = lockedView.scrollFlags.y; + } + + + + else { + normal.hide(); + + + lockedViewEl.style.borderBottomWidth = '0'; + + + locked.flex = 1; + delete locked.width; + locked.removeCls(me.lockedGridCls); + locked.show(); + + + + lockedView.el.setStyle(normal.view.getOverflowStyle()); + me.ignoreMousewheel = true; + } + Ext.resumeLayouts(true); + return [lockedColCount, normalColCount]; + }, + + onLockedHeaderAdd: function() { + + + if (!this.ignoreAddLockedColumn) { + this.syncLockedWidth(); + } + }, + + onLockedHeaderResize: function() { + this.syncLockedWidth(); + }, + + onLockedHeaderHide: function() { + this.syncLockedWidth(); + }, + + onLockedHeaderShow: function() { + this.syncLockedWidth(); + }, + + onLockedHeaderSortChange: Ext.emptyFn, + + onNormalHeaderSortChange: Ext.emptyFn, + + + + 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; + + activeHd = activeHd || normalHCt.getMenu().activeHeader; + toCt = toCt || lockedGrid.headerCt; + ownerCt = activeHd.ownerCt; + + + if (!activeHd.isLockable()) { + return; + } + + + + if (activeHd.flex) { + activeHd.width = activeHd.getWidth(); + activeHd.flex = null; + } + + Ext.suspendLayouts(); + + + normalView.blockRefresh = lockedView.blockRefresh = true; + ownerCt.remove(activeHd, false); + activeHd.locked = true; + + + me.ignoreAddLockedColumn = true; + if (Ext.isDefined(toIdx)) { + toCt.insert(toIdx, activeHd); + } else { + toCt.add(activeHd); + } + me.ignoreAddLockedColumn = false; + normalView.blockRefresh = lockedView.blockRefresh = false; + + refreshFlags = me.syncLockedWidth(); + if (refreshFlags[0]) { + lockedGrid.getView().refresh(); + } + if (refreshFlags[1]) { + normalGrid.getView().refresh(); + } + Ext.resumeLayouts(true); + + me.fireEvent('lockcolumn', me, activeHd); + }, + + + + unlock: function(activeHd, toIdx, toCt) { + var me = this, + normalGrid = me.normalGrid, + lockedGrid = me.lockedGrid, + normalView = normalGrid.view, + lockedView = lockedGrid.view, + lockedHCt = lockedGrid.headerCt, + refreshFlags; + + + if (!Ext.isDefined(toIdx)) { + toIdx = 0; + } + activeHd = activeHd || lockedHCt.getMenu().activeHeader; + toCt = toCt || normalGrid.headerCt; + + Ext.suspendLayouts(); + + + normalView.blockRefresh = lockedView.blockRefresh = true; + activeHd.ownerCt.remove(activeHd, false); + activeHd.locked = false; + toCt.insert(toIdx, activeHd); + normalView.blockRefresh = lockedView.blockRefresh = false; + + + + refreshFlags = me.syncLockedWidth(); + + if (refreshFlags[0]) { + lockedGrid.getView().refresh(); + } + if (refreshFlags[1]) { + normalGrid.getView().refresh(); + } + Ext.resumeLayouts(true); + + me.fireEvent('unlockcolumn', me, activeHd); + }, + + + reconfigureLockable: function(store, columns) { + var me = this, + oldStore = me.store, + lockedGrid = me.lockedGrid, + normalGrid = me.normalGrid; + + Ext.suspendLayouts(); + if (columns) { + lockedGrid.headerCt.removeAll(); + normalGrid.headerCt.removeAll(); + + columns = me.processColumns(columns, lockedGrid); + + + me.ignoreAddLockedColumn = true; + lockedGrid.headerCt.add(columns.locked.items); + me.ignoreAddLockedColumn = false; + normalGrid.headerCt.add(columns.normal.items); + + + + me.syncLockedWidth(); + } + + if (store && store !== oldStore) { + store = Ext.data.StoreManager.lookup(store); + me.store = store; + lockedGrid.bindStore(store); + normalGrid.bindStore(store); + } else { + lockedGrid.getView().refresh(); + normalGrid.getView().refresh(); + } + Ext.resumeLayouts(true); + }, + + 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()); + + + 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]; + + + if (plugin.init) { + lockableScope = plugin.lockableScope; + } + + + else { + 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()); + + + lockedPlugin.lockingPartner = normalPlugin; + normalPlugin.lockingPartner = lockedPlugin; + + + + 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(){ + + Ext.destroy(this.view, this.headerCt); + } +}, function() { + this.borrow(Ext.AbstractComponent, ['constructPlugin']); +}); + + + +Ext.define('Ext.grid.plugin.BufferedRenderer', { + extend: Ext.AbstractPlugin , + + + + + alias: 'plugin.bufferedrenderer', + lockableScope: 'both', + + + percentageFromEdge: 0.35, + + + variableRowHeight: false, + + + numFromEdge: 8, + + + trailingBufferZone: 10, + + + leadingBufferZone: 20, + + + synchronousRender: true, + + + scrollToLoadBuffer: 200, + + + viewSize: 0, + + rowHeight: 21, + + position: 0, + lastScrollDirection: 1, + bodyTop: 0, + + + init: function(grid) { + var me = this, + view = grid.view, + viewListeners = { + scroll: { + fn: me.onViewScroll, + element: 'el', + scope: me + }, + boxready: me.onViewResize, + resize: me.onViewResize, + refresh: me.onViewRefresh, + scope: me, + destroyable: true + }, + initialConfig = view.initialConfig; + + + if (!me.variableRowHeight && grid.ownerLockable) { + grid.ownerLockable.syncRowHeight = false; + } + + + + if (grid.isTree || (grid.ownerLockable && grid.ownerLockable.isTree)) { + view.blockRefresh = false; + + + if (initialConfig && initialConfig.loadMask === undefined) { + view.loadMask = true; + } + } + + if (view.positionBody) { + viewListeners.refresh = me.onViewRefresh; + } + + me.grid = grid; + me.view = view; + me.isRTL = view.getHierarchyState().rtl; + view.bufferedRenderer = me; + view.preserveScrollOnRefresh = true; + view.animate = false; + + me.bindStore(view.dataSource); + view.getViewRange = function() { + return me.getViewRange(); + }; + + me.position = 0; + + me.gridListeners = grid.on('reconfigure', me.onReconfigure, me); + me.viewListeners = view.on(viewListeners); + }, + + bindStore: function (store) { + var me = this, + view = me.view, + dataSource = view.dataSource, + hasFeatureStore = dataSource && dataSource.isFeatureStore; + + + + + + + + + if (hasFeatureStore === store.isFeatureStore) { + if (me.store) { + me.unbindStore(); + } + me.storeListeners = store.on({ + scope: me, + clear: me.onStoreClear, + destroyable: true + }); + me.store = store; + } + + + if (me.view.componentLayout.layoutCount) { + me.onViewResize(me.view, 0, me.view.getHeight()); + } + }, + + onReconfigure: function(grid, store){ + if (store && store !== this.store) { + this.bindStore(store); + } + }, + + unbindStore: function() { + this.storeListeners.destroy(); + this.store = null; + }, + + onStoreClear: function() { + var me = this; + + + if (me.view.rendered && !me.store.isDestroyed) { + + + if (me.scrollTop !== 0) { + me.ignoreNextScrollEvent = true; + me.view.el.dom.scrollTop = 0; + } + + me.bodyTop = me.scrollTop = me.position = me.scrollHeight = 0; + me.lastScrollDirection = me.scrollOffset = null; + + + delete me.rowHeight; + } + }, + + onViewRefresh: function() { + var me = this, + view = me.view, + oldScrollHeight = me.scrollHeight, + scrollHeight; + + + if (view.all.getCount()) { + + + delete me.rowHeight; + } + + + + scrollHeight = me.getScrollHeight(); + + if (!oldScrollHeight || scrollHeight != oldScrollHeight) { + me.stretchView(view, scrollHeight); + } + + if (me.scrollTop !== view.el.dom.scrollTop) { + + + + me.onViewScroll(); + } else { + if (!me.hasOwnProperty('bodyTop')) { + me.bodyTop = view.all.startIndex * me.rowHeight; + view.el.dom.scrollTop = me.bodyTop; + + } + me.setBodyTop(me.bodyTop); + + + if (view.all.getCount()) { + me.viewSize = 0; + me.onViewResize(view, null, view.getHeight()); + } + } + }, + + onViewResize: function(view, width, height, oldWidth, oldHeight) { + var me = this, + newViewSize; + + + me.tableTopBorderWidth = view.body.getBorderWidth('t'); + + + if (!oldHeight || height !== oldHeight) { + + + newViewSize = Math.ceil(height / me.rowHeight) + me.trailingBufferZone + me.leadingBufferZone; + me.viewSize = me.setViewSize(newViewSize); + } + }, + + stretchView: function(view, scrollRange) { + var me = this, + recordCount = (me.store.buffered ? me.store.getTotalCount() : me.store.getCount()), + stretcherSpec, + el; + + if (me.stretcher) { + me.stretcher.dom.style.marginTop = (recordCount <= me.viewSize ? 0 : (scrollRange - 1)) + 'px'; + } else { + el = view.el; + + + + if (view.refreshCounter) { + view.fixedNodes++; + } + + + if (recordCount && (me.view.all.endIndex === recordCount - 1)) { + scrollRange = me.bodyTop + view.body.dom.offsetHeight; + } + stretcherSpec = { + style: { + width: '1px', + height: '1px', + 'marginTop': (scrollRange - 1) + 'px', + position: 'absolute' + } + }; + stretcherSpec.style[me.isRTL ? 'right' : 'left'] = 0; + this.stretcher = el.createChild(stretcherSpec, el.dom.firstChild); + } + }, + + setViewSize: function(viewSize) { + if (viewSize !== this.viewSize) { + + + this.scrollTop = this.view.el.dom.scrollTop; + + var me = this, + store = me.store, + elCount = me.view.all.getCount(), + start, end, + lockingPartner = me.lockingPartner; + + me.viewSize = store.viewSize = viewSize; + + + + if (elCount) { + start = me.view.all.startIndex; + end = Math.min(start + viewSize - 1, (store.buffered ? store.getTotalCount() : store.getCount()) - 1); + + + if (lockingPartner) { + lockingPartner.disable(); + } + me.renderRange(start, end); + if (lockingPartner) { + lockingPartner.enable(); + } + } + } + return viewSize; + }, + + getViewRange: function() { + var me = this, + rows = me.view.all, + store = me.store, + startIndex = rows.getCount() ? rows.startIndex : rows.startIndex = (store.currentPage - 1) * store.pageSize; + + + if (rows.getCount()) { + startIndex = rows.startIndex; + } + + + + else { + if (!store.currentPage) { + store.currentPage = 1; + } + startIndex = rows.startIndex = (store.currentPage - 1) * (store.pageSize || 1); + + + store.currentPage = 1; + } + + if (store.data.getCount()) { + return store.getRange(startIndex, startIndex + (me.viewSize || store.defaultViewSize) - 1); + } else { + return []; + } + }, + + + onReplace: function(store, startIndex, oldRecords, newRecords) { + var me = this, + view = me.view, + rows = view.all, + viewSize = me.viewSize, + overflow, + shortfall, + endIndex; + + + + + + me.position = view.el.dom.scrollTop; + + + endIndex = startIndex + ((oldRecords && oldRecords.length) ? (oldRecords.length - 1) : 0); + + + if (endIndex < rows.startIndex || startIndex > (rows.startIndex + viewSize - 1)) { + + me.stretchView(view, me.getScrollHeight()); + return; + } + + + if (startIndex <= rows.startIndex && endIndex >= rows.endIndex) { + me.refreshView(); + } + + + else if (startIndex < rows.startIndex) { + + + shortfall = endIndex - rows.startIndex + 1; + + + if (shortfall <= 0) { + me.refreshView(); + } + + + else { + startIndex = Math.max(startIndex - shortfall - me.trailingBufferZone, 0); + endIndex = Math.min(startIndex + viewSize, store.getCount()) - 1; + + + + + me.setBodyTop(startIndex * me.rowHeight); + + me.renderRange(startIndex, endIndex); + } + } + + + else { + + + overflow = startIndex + newRecords.length - 1 - rows.endIndex; + + + if (overflow > 0) { + startIndex = Math.max(me.getFirstVisibleRowIndex() - me.trailingBufferZone, 0); + endIndex = Math.min(startIndex + viewSize, store.getCount() - 1); + rows.removeRange(null, null, true); + me.renderRange(startIndex, endIndex); + view.selModel.onLastFocusChanged(null, view.selModel.lastFocused, true); + } + + + else { + + me.refreshView(); + } + } + + + me.stretchView(view, me.getScrollHeight()); + }, + + + scrollTo: function(recordIdx, doSelect, callback, scope) { + var me = this, + view = me.view, + viewDom = view.el.dom, + store = me.store, + total = store.buffered ? store.getTotalCount() : store.getCount(), + startIdx, endIdx, + targetRec, + targetRow, + tableTop, + groupingFeature, + group, + record; + + + + + if ((groupingFeature = view.dataSource.groupingFeature) && (groupingFeature.collapsible !== false)) { + + + recordIdx = Math.min(Math.max(recordIdx, 0), view.store.getCount() - 1); + record = view.store.getAt(recordIdx); + group = groupingFeature.getGroup(record); + + if (group.isCollapsed) { + groupingFeature.expand(group.name); + total = store.buffered ? store.getTotalCount() : store.getCount(); + } + + + recordIdx = groupingFeature.indexOf(record); + + } else { + + + recordIdx = Math.min(Math.max(recordIdx, 0), total - 1); + } + + + startIdx = Math.max(Math.min(recordIdx - (Math.floor((me.leadingBufferZone + me.trailingBufferZone) / 2)), total - me.viewSize + 1), 0); + tableTop = Math.max(startIdx * me.rowHeight - me.tableTopBorderWidth, 0); + endIdx = Math.min(startIdx + me.viewSize - 1, total - 1); + + store.getRange(startIdx, endIdx, { + callback: function(range, start, end) { + + me.renderRange(start, end, true); + + targetRec = store.data.getRange(recordIdx, recordIdx)[0]; + targetRow = view.getNode(targetRec, false); + view.body.dom.style.top = tableTop + 'px'; + me.position = me.scrollTop = viewDom.scrollTop = tableTop = Math.min(Math.max(0, tableTop - view.body.getOffsetsTo(targetRow)[1]), viewDom.scrollHeight - viewDom.clientHeight); + + + if (Ext.isIE) { + viewDom.scrollTop = tableTop; + } + if (doSelect) { + view.selModel.select(targetRec); + } + if (callback) { + callback.call(scope||me, recordIdx, targetRec); + } + } + }); + }, + + onViewScroll: function(e, t) { + var me = this, + store = me.store, + totalCount = (store.buffered ? store.getTotalCount() : store.getCount()), + vscrollDistance, + scrollDirection, + scrollTop = me.scrollTop = me.view.el.dom.scrollTop, + scrollHandled = false; + + + + if (me.ignoreNextScrollEvent) { + me.ignoreNextScrollEvent = false; + return; + } + + + + if (!(me.disabled || totalCount < me.viewSize)) { + + vscrollDistance = scrollTop - me.position; + scrollDirection = vscrollDistance > 0 ? 1 : -1; + + + if (Math.abs(vscrollDistance) >= 20 || (scrollDirection !== me.lastScrollDirection)) { + me.lastScrollDirection = scrollDirection; + me.handleViewScroll(me.lastScrollDirection); + scrollHandled = true; + } + } + + + if (!scrollHandled) { + if (me.lockingPartner && me.lockingPartner.scrollTop !== scrollTop) { + me.lockingPartner.view.el.dom.scrollTop = scrollTop; + } + } + }, + + handleViewScroll: function(direction) { + var me = this, + rows = me.view.all, + store = me.store, + viewSize = me.viewSize, + totalCount = (store.buffered ? store.getTotalCount() : store.getCount()), + requestStart, + requestEnd; + + + if (direction == -1) { + + + if (rows.startIndex) { + if ((me.getFirstVisibleRowIndex() - rows.startIndex) < me.numFromEdge) { + requestStart = Math.max(0, me.getLastVisibleRowIndex() + me.trailingBufferZone - viewSize); + } + } + } + + else { + + + if (rows.endIndex < totalCount - 1) { + if ((rows.endIndex - me.getLastVisibleRowIndex()) < me.numFromEdge) { + requestStart = Math.max(0, me.getFirstVisibleRowIndex() - me.trailingBufferZone); + } + } + } + + + if (requestStart != null) { + requestEnd = Math.min(requestStart + viewSize - 1, totalCount - 1); + + + if (requestStart !== rows.startIndex || requestEnd !== rows.endIndex) { + me.renderRange(requestStart, requestEnd); + return; + } + } + + + if (me.lockingPartner && me.lockingPartner.view.el && me.lockingPartner.scrollTop !== me.scrollTop) { + me.lockingPartner.view.el.dom.scrollTop = me.scrollTop; + } + }, + + + refreshView: function() { + var me = this, + selModel = me.view.selModel, + rows = me.view.all, + store = me.store, + maxIndex = store.getCount() - 1, + + + + + startIndex = Math.max(0, Math.min(rows.startIndex, maxIndex - me.viewSize + 1)), + + + endIndex = Math.min(rows.startIndex + me.viewSize - 1, maxIndex); + + rows.removeRange(null, null, true); + me.renderRange(startIndex, endIndex, true); + selModel.onLastFocusChanged(null, selModel.lastFocused, true); + }, + + renderRange: function(start, end, forceSynchronous) { + var me = this, + rows = me.view.all, + store = me.store; + + + + + if (!(start === rows.startIndex && end === rows.endIndex)) { + + + if (store.rangeCached(start, end)) { + me.cancelLoad(); + + if (me.synchronousRender || forceSynchronous) { + me.onRangeFetched(null, start, end); + } else { + if (!me.renderTask) { + me.renderTask = new Ext.util.DelayedTask(me.onRangeFetched, me, null, false); + } + + + + + me.renderTask.delay(1, null, null, [null, start, end]); + } + } + + + else { + me.attemptLoad(start, end); + } + } + }, + + onRangeFetched: function(range, start, end, fromLockingPartner) { + var me = this, + view = me.view, + oldStart, + rows = view.all, + removeCount, + increment = 0, + + + calculatedTop = start * me.rowHeight - me.tableTopBorderWidth, + top, + lockingPartner = me.lockingPartner, + newRows, + topAdditionSize, + i; + + + if (view.isDestroyed) { + return; + } + + + if (!range) { + range = me.store.getRange(start, end); + + + if (!range) { + return; + } + } + + + if (start < rows.startIndex && end > rows.endIndex) { + + + topAdditionSize = rows.startIndex - start; + rows.clear(true); + newRows = Ext.Array.slice(view.doAdd(range, start), 0, topAdditionSize); + + for (i = 0; i < newRows.length; i++) { + increment -= newRows[i].offsetHeight; + } + + + me.setBodyTop(me.bodyTop + increment); + return; + } + + + if (start > rows.endIndex || end < rows.startIndex) { + rows.clear(true); + top = calculatedTop; + } + + if (!rows.getCount()) { + view.doAdd(range, start); + } + + else if (end > rows.endIndex) { + removeCount = Math.max(start - rows.startIndex, 0); + + + if (me.variableRowHeight) { + increment = rows.item(rows.startIndex + removeCount, true).offsetTop; + } + rows.scroll(Ext.Array.slice(range, rows.endIndex + 1 - start), 1, removeCount, start, end); + + + if (me.variableRowHeight) { + + top = me.bodyTop + increment; + } else { + top = calculatedTop; + } + } + + else { + removeCount = Math.max(rows.endIndex - end, 0); + oldStart = rows.startIndex; + rows.scroll(Ext.Array.slice(range, 0, rows.startIndex - start), -1, removeCount, start, end); + + + if (me.variableRowHeight) { + + top = me.bodyTop - rows.item(oldStart, true).offsetTop; + } else { + top = calculatedTop; + } + } + + + me.position = me.scrollTop; + + + + if (view.positionBody) { + me.setBodyTop(top, calculatedTop); + } + + + + if (lockingPartner && !lockingPartner.disabled && !fromLockingPartner) { + lockingPartner.onRangeFetched(range, start, end, true); + if (lockingPartner.scrollTop !== me.scrollTop) { + lockingPartner.view.el.dom.scrollTop = me.scrollTop; + } + } + }, + + setBodyTop: function(bodyTop, calculatedTop) { + var me = this, + view = me.view, + store = me.store, + body = view.body.dom, + delta; + + bodyTop = Math.floor(bodyTop); + + + + + if (calculatedTop !== undefined) { + delta = bodyTop - calculatedTop; + bodyTop = calculatedTop; + } + body.style.position = 'absolute'; + body.style.top = (me.bodyTop = Math.max(bodyTop, 0)) + 'px'; + if (me.isRTL && Ext.supports.xOriginBug && view.scrollFlags.y) { + body.style.right = -Ext.getScrollbarSize().width + 'px'; + } + + + + if (delta) { + me.scrollTop = me.position = view.el.dom.scrollTop -= delta; + } + + + if (view.all.endIndex === (store.buffered ? store.getTotalCount() : store.getCount()) - 1) { + me.stretchView(view, me.bodyTop + body.offsetHeight); + } + }, + + getFirstVisibleRowIndex: function(startRow, endRow, viewportTop, viewportBottom) { + var me = this, + view = me.view, + rows = view.all, + elements = rows.elements, + clientHeight = view.el.dom.clientHeight, + target, + targetTop; + + + if (rows.getCount() && me.variableRowHeight) { + if (!arguments.length) { + startRow = rows.startIndex; + endRow = rows.endIndex; + viewportTop = me.scrollTop; + viewportBottom = viewportTop + clientHeight; + + + if (me.bodyTop > viewportBottom || me.bodyTop + view.body.getHeight() < viewportTop) { + return Math.floor(me.scrollTop / me.rowHeight); + } + + + 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 = me.bodyTop + elements[target].offsetTop; + + + if (targetTop + elements[target].offsetHeight < viewportTop) { + return me.getFirstVisibleRowIndex(target + 1, endRow, viewportTop, viewportBottom); + } + + + if (targetTop <= viewportTop) { + return target; + } + + else if (target !== startRow) { + return me.getFirstVisibleRowIndex(startRow, target - 1, viewportTop, viewportBottom); + } + } + return Math.floor(me.scrollTop / me.rowHeight); + }, + + getLastVisibleRowIndex: function(startRow, endRow, viewportTop, viewportBottom) { + var me = this, + view = me.view, + rows = view.all, + elements = rows.elements, + clientHeight = view.el.dom.clientHeight, + target, + targetTop, targetBottom; + + + if (rows.getCount() && me.variableRowHeight) { + if (!arguments.length) { + startRow = rows.startIndex; + endRow = rows.endIndex; + viewportTop = me.scrollTop; + viewportBottom = viewportTop + clientHeight; + + + if (me.bodyTop > viewportBottom || me.bodyTop + view.body.getHeight() < viewportTop) { + return Math.floor(me.scrollTop / me.rowHeight) + Math.ceil(clientHeight / me.rowHeight); + } + + + 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 = me.bodyTop + elements[target].offsetTop; + + + if (targetTop > viewportBottom) { + return me.getLastVisibleRowIndex(startRow, target - 1, viewportTop, viewportBottom); + } + targetBottom = targetTop + elements[target].offsetHeight; + + + if (targetBottom >= viewportBottom) { + return target; + } + + 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, + store = me.store, + doCalcHeight = !me.hasOwnProperty('rowHeight'), + storeCount = me.store.getCount(); + + if (!storeCount) { + return 0; + } + if (doCalcHeight) { + if (view.all.getCount()) { + me.rowHeight = Math.floor(view.body.getHeight() / view.all.getCount()); + } + } + return this.scrollHeight = Math.floor((store.buffered ? store.getTotalCount() : store.getCount()) * me.rowHeight); + }, + + 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.store.getRange(start, end, { + callback: me.onRangeFetched, + scope: me, + fireEvent: false + }); + } + }, + + cancelLoad: function() { + if (this.loadTask) { + this.loadTask.cancel(); + } + }, + + doAttemptLoad: function(start, end) { + this.store.getRange(start, end, { + callback: this.onRangeFetched, + scope: this, + fireEvent: false + }); + }, + + destroy: function() { + var me = this, + view = me.view; + + if (view && view.el) { + view.el.un('scroll', me.onViewScroll, me); + } + + + Ext.destroy(me.viewListeners, me.storeListeners, me.gridListeners); + } +}); + + + +Ext.define('Ext.grid.plugin.Editing', { + alias: 'editing.editing', + extend: Ext.AbstractPlugin , + + + + + + + mixins: { + observable: Ext.util.Observable + }, + + + clicksToEdit: 2, + + + triggerEvent: undefined, + + + + relayedEvents: [ + 'beforeedit', + 'edit', + 'validateedit', + 'canceledit' + ], + + + defaultFieldXType: 'textfield', + + + editStyle: '', + + constructor: function(config) { + var me = this; + + me.addEvents( + + 'beforeedit', + + + 'edit', + + + 'validateedit', + + 'canceledit' + + ); + me.callParent(arguments); + me.mixins.observable.constructor.call(me); + + me.on("edit", function(editor, e) { + me.fireEvent("afteredit", editor, e); + }); + }, + + + init: function(grid) { + var me = this; + + me.grid = grid; + me.view = grid.view; + me.initEvents(); + + + me.mon(grid, { + beforereconfigure: me.onBeforeReconfigure, + reconfigure: me.onReconfigure, + scope: me, + beforerender: { + fn: me.onReconfigure, + single: true, + scope: me + } + }); + + grid.relayEvents(me, me.relayedEvents); + + + if (me.grid.ownerLockable) { + me.grid.ownerLockable.relayEvents(me, me.relayedEvents); + } + + + grid.isEditable = true; + grid.editingPlugin = grid.view.editingPlugin = me; + }, + + onBeforeReconfigure: function() { + this.reconfiguring = true; + }, + + + onReconfigure: function() { + + + this.initFieldAccessors(this.grid.getTopLevelColumnManager().getColumns()); + delete this.reconfiguring; + }, + + + destroy: function() { + var me = this, + grid = me.grid; + + Ext.destroy(me.keyNav); + + me.clearListeners(); + + if (grid) { + grid.editingPlugin = grid.view.editingPlugin = me.grid = me.view = me.editor = me.keyNav = null; + } + }, + + + getEditStyle: function() { + return this.editStyle; + }, + + + initFieldAccessors: function(columns) { + + if (columns.isGroupHeader) { + columns = columns.getGridColumns(); + } + + + else if (!Ext.isArray(columns)) { + columns = [columns]; + } + + var me = this, + c, + cLen = columns.length, + column; + + for (c = 0; c < cLen; c++) { + column = columns[c]; + + if (!column.getEditor) { + column.getEditor = function(record, defaultField) { + return me.getColumnField(this, defaultField); + }; + } + if (!column.hasEditor) { + column.hasEditor = function() { + return me.hasColumnField(this); + }; + } + if (!column.setEditor) { + column.setEditor = function(field) { + me.setColumnField(this, field); + }; + } + } + }, + + + removeFieldAccessors: function(columns) { + + if (columns.isGroupHeader) { + columns = columns.getGridColumns(); + } + + + 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; + } + }, + + + + getColumnField: function(columnHeader, defaultField) { + var field = columnHeader.field; + if (!(field && field.isFormField)) { + field = columnHeader.field = this.createColumnField(columnHeader, defaultField); + } + return field; + }, + + + + hasColumnField: function(columnHeader) { + return !!(columnHeader.field && columnHeader.field.isComponent); + }, + + + + setColumnField: function(columnHeader, field) { + columnHeader.field = field; + columnHeader.field = this.createColumnField(columnHeader); + }, + + createColumnField: function (columnHeader, defaultField) { + var field = columnHeader.field, + dataIndex; + + if (!field && columnHeader.editor) { + field = columnHeader.editor; + columnHeader.editor = null; + } + + if (!field && defaultField) { + field = defaultField; + } + + if (field) { + dataIndex = columnHeader.dataIndex; + + if (field.isComponent) { + field.column = columnHeader; + } else { + if (Ext.isString(field)) { + field = { + name: dataIndex, + xtype: field, + column: columnHeader + }; + } else { + field = Ext.apply({ + name: dataIndex, + column: columnHeader + }, field); + } + field = Ext.ComponentManager.create(field, this.defaultFieldXType); + } + + + + + field.dataIndex = dataIndex; + + field.isEditorComponent = true; + columnHeader.field = field; + } + return field; + }, + + + initEvents: function() { + var me = this; + me.initEditTriggers(); + me.initCancelTriggers(); + }, + + + initCancelTriggers: Ext.emptyFn, + + + initEditTriggers: function() { + var me = this, + view = me.view; + + + if (me.triggerEvent == 'cellfocus') { + me.mon(view, 'cellfocus', me.onCellFocus, me); + } else if (me.triggerEvent == 'rowfocus') { + me.mon(view, 'rowfocus', me.onRowFocus, me); + } else { + + + + + + + + if (view.getSelectionModel().isCellModel) { + view.onCellFocus = Ext.Function.bind(me.beforeViewCellFocus, me); + } + + + me.mon(view, me.triggerEvent || ('cell' + (me.clicksToEdit === 1 ? 'click' : 'dblclick')), me.onCellClick, me); + } + + + + me.initAddRemoveHeaderEvents(); + + view.on('render', me.initKeyNavHeaderEvents, me, {single: true}); + }, + + + beforeViewCellFocus: function(position) { + + if (this.view.selModel.keyNavigation || !this.editing || !this.isCellEditable || !this.isCellEditable(position.row, position.columnHeader)) { + this.view.focusCell.apply(this.view, arguments); + } + }, + + + onRowFocus: function(record, row, rowIdx) { + this.startEdit(row, 0); + }, + + + onCellFocus: function(record, cell, position) { + this.startEdit(position.row, position.column); + }, + + + + onCellClick: function(view, cell, colIdx, record, row, rowIdx, e) { + + + + var expanderSelector = view.expanderSelector, + + columnHeader = view.ownerCt.getColumnManager().getHeaderAtIndex(colIdx), + editor = columnHeader.getEditor(record); + + if (this.shouldStartEdit(editor) && (!expanderSelector || !e.getTarget(expanderSelector))) { + this.startEdit(record, columnHeader); + } + }, + + initAddRemoveHeaderEvents: function(){ + var me = this, + headerCt = me.grid.headerCt; + + me.mon(headerCt, { + scope: me, + add: me.onColumnAdd, + columnmove: me.onColumnMove, + beforedestroy: me.beforeGridHeaderDestroy + }); + }, + + initKeyNavHeaderEvents: function() { + var me = this; + + me.keyNav = Ext.create('Ext.util.KeyNav', me.view.el, { + enter: me.onEnterKey, + esc: me.onEscKey, + scope: me + }); + }, + + + onColumnAdd: function(ct, column) { + this.initFieldAccessors(column); + }, + + + onColumnMove: Ext.emptyFn, + + + onEnterKey: function(e) { + var me = this, + grid = me.grid, + selModel = grid.getSelectionModel(), + record, + pos, + columnHeader; + + + + if (selModel.getCurrentPosition && (pos = selModel.getCurrentPosition())) { + record = pos.record; + columnHeader = pos.columnHeader; + } + + else { + record = selModel.getLastSelected(); + columnHeader = grid.getColumnManager().getHeaderAtIndex(0); + } + + + if (record && columnHeader) { + me.startEdit(record, columnHeader); + } + }, + + + onEscKey: function(e) { + return this.cancelEdit(); + }, + + + beforeEdit: Ext.emptyFn, + + shouldStartEdit: function(editor) { + return !!editor; + }, + + + startEdit: function(record, columnHeader) { + var me = this, + context, + layoutView = me.grid.lockable ? me.grid : me.view; + + + + if (!layoutView.componentLayoutCounter) { + layoutView.on({ + boxready: Ext.Function.bind(me.startEdit, me, [record, columnHeader]), + single: true + }); + return false; + } + + + if (me.grid.collapsed || !me.grid.view.isVisible(true)) { + return false; + } + + context = me.getEditingContext(record, columnHeader); + if (context == null) { + return false; + } + if (!me.preventBeforeCheck) { + if (me.beforeEdit(context) === false || me.fireEvent('beforeedit', me, context) === false || context.cancel) { + return false; + } + } + + return context; + }, + + + + getEditingContext: function(record, columnHeader) { + var me = this, + grid = me.grid, + colMgr = grid.columnManager, + view, + gridRow, + rowIdx, colIdx; + + + + if (Ext.isNumber(columnHeader)) { + columnHeader = colMgr.getHeaderAtIndex(columnHeader); + } + + + if (!columnHeader) { + return; + } + + + if (columnHeader.hidden) { + columnHeader = columnHeader.next(':not([hidden])') || columnHeader.prev(':not([hidden])'); + } + + + view = columnHeader.getOwnerHeaderCt().view; + + gridRow = view.getNode(record, true); + + + if (!gridRow) { + return; + } + + colIdx = colMgr.getHeaderIndex(columnHeader); + + if (Ext.isNumber(record)) { + + rowIdx = record; + record = view.getRecord(gridRow); + } else { + rowIdx = view.indexOf(gridRow); + } + + + + if (!record) { + return; + } + + return { + grid : grid, + view : view, + store : view.dataSource, + record : record, + field : columnHeader.dataIndex, + value : record.get(columnHeader.dataIndex), + row : gridRow, + column : columnHeader, + rowIdx : rowIdx, + colIdx : colIdx + }; + }, + + + cancelEdit: function() { + var me = this; + + me.editing = false; + me.fireEvent('canceledit', me, me.context); + }, + + + completeEdit: function() { + var me = this; + + if (me.editing && me.validateEdit()) { + me.fireEvent('edit', me, me.context); + } + + me.context = null; + me.editing = false; + }, + + + validateEdit: function() { + var me = this, + context = me.context; + + return me.fireEvent('validateedit', me, context) !== false && !context.cancel; + } +}); + + + +Ext.define('Ext.grid.plugin.CellEditing', { + alias: 'plugin.cellediting', + extend: Ext.grid.plugin.Editing , + + lockableScope: 'both', + + + + + + + init: function(grid) { + var me = this, + lockingPartner = me.lockingPartner; + + me.callParent(arguments); + + + if (lockingPartner) { + if (lockingPartner.editors) { + me.editors = lockingPartner.editors; + } else { + me.editors = lockingPartner.editors = new Ext.util.MixedCollection(false, function(editor) { + return editor.editorId; + }); + } + } else { + me.editors = new Ext.util.MixedCollection(false, function(editor) { + return editor.editorId; + }); + } + }, + + + 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]; + + + editor = me.editors.getByKey(column.getItemId()); + + + if (!editor && column.hasEditor && column.hasEditor()) { + editor = column.getEditor(); + } + + + Ext.destroy(editor); + me.removeFieldAccessors(column); + } + }, + + onReconfigure: function(grid, store, 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(arguments); + }, + + + + initCancelTriggers: function() { + var me = this, + grid = me.grid, + view = grid.view; + + me.mon(grid, { + columnresize: me.cancelEdit, + columnmove: me.cancelEdit, + scope: me + }); + }, + + isCellEditable: function(record, columnHeader) { + var me = this, + context = me.getEditingContext(record, columnHeader); + + if (me.grid.view.isVisible(true) && context) { + columnHeader = context.column; + record = context.record; + if (columnHeader && me.getEditor(record, columnHeader)) { + return true; + } + } + }, + + + startEdit: function(record, columnHeader, context) { + var me = this, + isEditorEditing, isFieldEditable, ed; + + if (!context) { + me.preventBeforeCheck = true; + context = me.callParent(arguments); + delete me.preventBeforeCheck; + if (context === false) { + return false; + } + } + + + + if (context && me.grid.view.isVisible(true)) { + + record = context.record; + columnHeader = context.column; + + context.originalValue = context.value = record.get(columnHeader.dataIndex); + + + + isFieldEditable = (columnHeader && columnHeader.getEditor(record)) && !(me.beforeEdit(context) === false || me.fireEvent('beforeedit', me, context) === false || context.cancel); + + if (isFieldEditable) { + ed = me.getEditor(record, columnHeader); + isEditorEditing = ed.editing; + } else { + return false; + } + + + + me.completeEdit(isEditorEditing); + + + me.context = context; + + + me.grid.view.cancelFocus(); + me.view.scrollCellIntoView(me.getCell(record, columnHeader)); + if (ed) { + if (Ext.isIE && isEditorEditing) { + + + + + + + ed.selectSameEditor = true; + } + me.showEditor(ed, context, context.value); + return true; + } + return false; + } + }, + + showEditor: function(ed, context, value) { + var me = this, + record = context.record, + columnHeader = context.column, + sm = me.grid.getSelectionModel(), + preventFocus = sm.preventFocus, + selection = sm.getCurrentPosition(); + + + + if (!columnHeader.up(me.view.ownerCt)) { + return me.lockingPartner.showEditor(ed, me.lockingPartner.getEditingContext(selection.record, selection.columnHeader), value); + } + + me.setEditingContext(context); + me.setActiveEditor(ed); + me.setActiveRecord(record); + me.setActiveColumn(columnHeader); + + + + + if (!selection || !sm.isCellSelected(me.view, record, columnHeader)) { + sm.preventFocus = true; + + sm.selectByPosition({ + row: record, + column: columnHeader, + view: me.view + }, true); + sm.preventFocus = preventFocus; + } + + + if (Ext.isIE && Ext.EventObject.type === 'click') { + Ext.Function.defer(ed.startEdit, 1, ed, [me.getCell(record, columnHeader), value, context]); + } else { + ed.startEdit(me.getCell(record, columnHeader), value, context); + } + me.editing = true; + me.scroll = me.view.el.getScroll(); + }, + + completeEdit: function(remainVisible) { + var activeEd = this.getActiveEditor(); + if (activeEd) { + activeEd.completeEdit(remainVisible); + this.editing = false; + } + }, + + + setEditingContext: function(context) { + this.context = context; + if (this.lockingPartner) { + this.lockingPartner.context = context; + } + }, + + setActiveEditor: function(ed) { + this.activeEditor = ed; + if (this.lockingPartner) { + this.lockingPartner.activeEditor = ed; + } + }, + + getActiveEditor: function() { + return this.activeEditor; + }, + + setActiveColumn: function(column) { + this.activeColumn = column; + if (this.lockingPartner) { + this.lockingPartner.activeColumn = column; + } + }, + + getActiveColumn: function() { + return this.activeColumn; + }, + + setActiveRecord: function(record) { + this.activeRecord = record; + if (this.lockingPartner) { + this.lockingPartner.activeRecord = record; + } + }, + + getActiveRecord: function() { + return this.activeRecord; + }, + + getEditor: function(record, column) { + var me = this, + editors = me.editors, + editorId = column.getItemId(), + editor = editors.getByKey(editorId), + + editorOwner = me.grid.ownerLockable || me.grid; + + if (!editor) { + editor = column.getEditor(record); + if (!editor) { + return false; + } + + + if (editor instanceof Ext.grid.CellEditor) { + editor.floating = true; + } + + else { + editor = new Ext.grid.CellEditor({ + floating: true, + editorId: editorId, + field: editor + }); + } + + + + editor.field.excludeForm = true; + editorOwner.add(editor); + editor.on({ + scope: me, + specialkey: me.onSpecialKey, + complete: me.onEditComplete, + canceledit: me.cancelEdit + }); + column.on('removed', me.cancelActiveEdit, me); + editors.add(editor); + } + + if (column.isTreeColumn) { + editor.isForTree = column.isTreeColumn; + editor.addCls(Ext.baseCSSPrefix + 'tree-cell-editor'); + } + + + + + editor.setGrid(me.grid); + + + editor.editingPlugin = me; + return editor; + }, + + cancelActiveEdit: function(column){ + var context = this.context; + if (context && context.column === column) { + this.cancelEdit(); + } + }, + + + setColumnField: function(column, field) { + var ed = this.editors.getByKey(column.getItemId()); + Ext.destroy(ed, column.field); + this.editors.removeAtKey(column.getItemId()); + this.callParent(arguments); + }, + + + getCell: function(record, column) { + return this.grid.getView().getCell(record, column); + }, + + onSpecialKey: function(ed, field, e) { + var sm; + + if (e.getKey() === e.TAB) { + e.stopEvent(); + + if (ed) { + + + ed.onEditorTab(e); + } + + sm = ed.up('tablepanel').getSelectionModel(); + if (sm.onEditorTab) { + return sm.onEditorTab(ed.editingPlugin, e); + } + } + }, + + onEditComplete : function(ed, value, startValue) { + var me = this, + activeColumn = me.getActiveColumn(), + context = me.context, + record; + + if (activeColumn) { + record = context.record; + + me.setActiveEditor(null); + me.setActiveColumn(null); + me.setActiveRecord(null); + + context.value = value; + if (!me.validateEdit()) { + me.editing = false; + return; + } + + + + if (!record.isEqual(value, startValue)) { + record.set(activeColumn.dataIndex, value); + } + + + + context.view.focusRow(context.rowIdx, 100); + me.fireEvent('edit', me, context); + me.editing = false; + } + }, + + + cancelEdit: function() { + var me = this, + context = me.context, + activeEd = me.getActiveEditor(); + + me.setActiveEditor(null); + me.setActiveColumn(null); + me.setActiveRecord(null); + if (activeEd) { + if (activeEd.field) { + me.context.value = ('editedValue' in activeEd) ? activeEd.editedValue : activeEd.getValue(); + activeEd.cancelEdit(); + } + + + + context.view.focusRow(context.rowIdx, 100); + + + me.callParent(arguments); + return; + } + + return true; + }, + + + startEditByPosition: function(position) { + var cm = this.grid.getColumnManager(), + index, + col; + + + if (!position.isCellContext) { + position = new Ext.grid.CellContext(this.view).setPosition(position); + } + + + + + index = cm.getHeaderIndex(position.columnHeader); + position.setColumn(cm.getVisibleHeaderClosestToIndex(index)); + + return this.startEdit(position.record, position.columnHeader); + } +}); + + +Ext.define('Ext.grid.plugin.DivRenderer', { + alias: 'plugin.divrenderer', + extend: Ext.AbstractPlugin , + + tableTpl: [ + '
    ', + '{%', + 'values.view.renderRows(values.rows, values.viewStartIndex, out);', + '%}', + '
    ', + { + priority: 0 + } + ], + + rowTpl: [ + '{%', + 'var dataRowCls = values.recordIndex === -1 ? "" : " ' + Ext.baseCSSPrefix + 'grid-data-row";', + '%}', + '
    ', + '' + + '{%', + 'parent.view.renderCell(values, parent.record, parent.recordIndex, xindex - 1, out, parent)', + '%}', + '', + '
    ', + { + priority: 0 + } + ], + + cellTpl: [ + '
    ', + '
    {style}" {ariaCellInnerAttr}>{value}
    ', + '
    ', { + priority: 0 + } + ], + + selectors: { + + bodySelector: 'div', + + + nodeContainerSelector: 'div', + + + itemSelector: 'dl.' + Ext.baseCSSPrefix + 'grid-row', + + + dataRowSelector: 'dl.' + Ext.baseCSSPrefix + 'grid-data-row', + + + cellSelector: 'dt.' + Ext.baseCSSPrefix + 'grid-cell', + + innerSelector: 'div.' + Ext.baseCSSPrefix + 'grid-cell-inner', + + getNodeContainerSelector: function() { + return this.getBodySelector(); + }, + + getNodeContainer: function() { + return this.el.getById(this.id + '-table', true); + } + }, + + init: function(grid) { + var view = grid.getView(); + view.tableTpl = Ext.XTemplate.getTpl(this, 'tableTpl'); + view.rowTpl = Ext.XTemplate.getTpl(this, 'rowTpl'); + view.cellTpl = Ext.XTemplate.getTpl(this, 'cellTpl'); + Ext.apply(view, this.selectors); + } +}); + + + +Ext.define('Ext.grid.plugin.DragDrop', { + extend: Ext.AbstractPlugin , + alias: 'plugin.gridviewdragdrop', + + + + + + + + + + + + + dragText : '{0} selected row{1}', + + + + ddGroup : "GridDD", + + + + + + + enableDrop: true, + + + enableDrag: true, + + + containerScroll: false, + + + + + + + + + + init : function(view) { + view.on('render', this.onViewRender, this, {single: true}); + }, + + + destroy: function() { + Ext.destroy(this.dragZone, this.dropZone); + }, + + 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, + scrollEl; + + 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)); + } + } +}); + + + +Ext.define('Ext.grid.plugin.RowEditing', { + extend: Ext.grid.plugin.Editing , + alias: 'plugin.rowediting', + + + + + + lockableScope: 'top', + + editStyle: 'row', + + + autoCancel: true, + + + + + errorSummary: true, + + constructor: function() { + var me = this; + + me.callParent(arguments); + + if (!me.clicksToMoveEditor) { + me.clicksToMoveEditor = me.clicksToEdit; + } + + me.autoCancel = !!me.autoCancel; + }, + + + destroy: function() { + Ext.destroy(this.editor); + this.callParent(arguments); + }, + + onBeforeReconfigure: function() { + this.callParent(arguments); + this.cancelEdit(); + }, + + onReconfigure: function(grid, store, columns) { + var ed = this.editor; + this.callParent(arguments); + + if (columns && ed && ed.rendered) { + ed.needsSyncFieldWidths = true; + } + }, + + shouldStartEdit: function(editor) { + return true; + }, + + + 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.callParent([record, columnHeader]); + if (context) { + me.context = context; + + + if (me.lockingPartner) { + me.lockingPartner.cancelEdit(); + } + editor.startEdit(context.record, context.column, context); + me.editing = true; + return true; + } + } + return false; + }, + + cancelEdit: function() { + var me = this; + + if (me.editing) { + me.getContextFieldValues(); + me.getEditor().cancelEdit(); + me.callParent(arguments); + return; + } + + return true; + }, + + onEnter: function (e) { + if (this.editor.down('#cancel').owns(e)) { + return this.cancelEdit(); + } else { + this.completeEdit(); + } + }, + + completeEdit: function() { + var me = this; + + if (me.editing && me.validateEdit()) { + me.editing = false; + me.fireEvent('edit', me, 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 + }); + }, + + + 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, + + editingPlugin: me + }, + item; + + for (b = 0; b < bLen; b++) { + item = btns[b]; + + if (Ext.isDefined(me[item])) { + cfg[item] = me[item]; + } + } + return cfg; + }, + + + 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 + }); + }, + + startEditByClick: function() { + var me = this; + if (!me.editing || me.clicksToMoveEditor === me.clicksToEdit) { + me.callParent(arguments); + } + }, + + moveEditorByClick: function() { + var me = this; + if (me.editing) { + me.superclass.onCellClick.apply(me, arguments); + } + }, + + + onColumnAdd: function(ct, column) { + if (column.isHeader) { + var me = this, + editor; + + me.initFieldAccessors(column); + + + + editor = me.editor; + if (editor) { + editor.onColumnAdd(column); + } + } + }, + + + 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 (column.hasEditor) { + if (column.hasEditor() && (field = column.getEditor())) { + field.destroy(); + } + this.removeFieldAccessors(column); + } + } + }, + + + onColumnResize: function(ct, column, width) { + if (column.isHeader) { + var me = this, + editor = me.getEditor(); + + if (editor) { + editor.onColumnResize(column, width); + } + } + }, + + + onColumnHide: function(ct, column) { + + var me = this, + editor = me.getEditor(); + + if (editor) { + editor.onColumnHide(column); + } + }, + + + onColumnShow: function(ct, column) { + + var me = this, + editor = me.getEditor(); + + if (editor) { + editor.onColumnShow(column); + } + }, + + + onColumnMove: function(ct, column, fromIdx, toIdx) { + + var me = this, + editor = me.getEditor(); + + + + me.initFieldAccessors(column); + + if (editor) { + + + editor.onColumnMove(column, fromIdx, toIdx); + } + }, + + + setColumnField: function(column, field) { + var me = this, + editor = me.getEditor(); + + if (editor) { + + 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]); + } +}); + + + +Ext.define('Ext.grid.property.Grid', { + + extend: Ext.grid.Panel , + + alias: 'widget.propertygrid', + + alternateClassName: 'Ext.grid.PropertyGrid', + + + + + + + + + + + + + + + + + + + + + + + + + valueField: 'value', + + + nameField: 'name', + + + inferTypes: true, + + + + + enableColumnMove: false, + columnLines: true, + stripeRows: false, + trackMouseOver: false, + clicksToEdit: 1, + enableHdMenu: false, + + gridCls: Ext.baseCSSPrefix + 'property-grid', + + + initComponent : function() { + var me = this; + + me.source = me.source || {}; + me.addCls(me.gridCls); + me.plugins = me.plugins || []; + + + me.plugins.push(new Ext.grid.plugin.CellEditing({ + clicksToEdit: me.clicksToEdit, + + + startEdit: function(record, column) { + + return this.self.prototype.startEdit.call(this, record, me.valueColumn); + } + })); + + me.selModel = { + selType: 'cellmodel', + onCellSelect: function(position) { + + position.columnHeader = me.valueColumn; + position.column = me.valueColumn.getVisibleIndex(); + return this.self.prototype.onCellSelect.call(this, position); + } + }; + + me.sourceConfig = Ext.apply({}, me.sourceConfig); + + + 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.addEvents( + + 'beforepropertychange', + + 'propertychange' + ); + me.callParent(); + + + me.getView().walkCells = this.walkCells; + + + 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 ]] + })}) + }; + + + 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.getConfig(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.setConfig(name, 'type', type); + } + } + } + }, + + getConfig: function(fieldName, key, defaultValue) { + var config = this.sourceConfig[fieldName], + out; + + if (config) { + out = config[key]; + } + return out || defaultValue; + }, + + setConfig: 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; + }, + + + configureLegacy: function(config){ + var me = this; + + me.copyLegacyObject(config, me.customRenderers, 'renderer'); + me.copyLegacyObject(config, me.customEditors, 'editor'); + me.copyLegacyObject(config, me.propertyNames, 'displayName'); + + + 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]; + } + } + }, + + + 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(); + } + } + }, + + + + 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); + + + pos.columnHeader = valueColumn; + pos.column = valueColumn.getVisibleIndex(); + return pos; + }, + + + + getCellEditor : function(record, column) { + var me = this, + propName = record.get(me.nameField), + val = record.get(me.valueField), + editor = me.getConfig(propName, 'editor'), + type = me.getConfig(propName, 'type'), + editors = me.editors; + + + + if (editor) { + if (!(editor instanceof Ext.grid.CellEditor)) { + if (!(editor instanceof Ext.form.field.Base)) { + editor = Ext.ComponentManager.create(editor, 'textfield'); + } + editor = me.setConfig(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': + editor = me.editors['boolean']; + 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)) { + editor = editors['boolean']; + } else { + editor = editors.string; + } + + + editor.editorId = propName; + 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]); + } + } + }, + + + 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); + }, + + + getSource: function() { + return this.propStore.getSource(); + }, + + + setProperty: function(prop, value, create) { + this.propStore.setValue(prop, value, create); + }, + + + removeProperty: function(prop) { + this.propStore.remove(prop); + } + + + +}); + + + +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', + + + + nameColumnCls: Ext.baseCSSPrefix + 'grid-property-name', + nameColumnInnerCls: Ext.baseCSSPrefix + 'grid-cell-inner-property-name', + + + 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, + renderer: Ext.Function.bind(me.renderProp, me), + itemId: grid.nameField, + menuDisabled: true, + tdCls: me.nameColumnCls, + innerCls: me.nameColumnInnerCls + }, { + header: me.valueText, + renderer: Ext.Function.bind(me.renderCell, me), + getEditor: Ext.Function.bind(me.getCellEditor, me), + sortable: grid.sortableColumns, + flex: 1, + fixed: true, + dataIndex: grid.valueField, + itemId: grid.valueField, + menuDisabled: true + }] + }]); + + + me.grid.valueColumn = me.items.items[1]; + }, + + getCellEditor: function(record){ + return this.grid.getCellEditor(record, this); + }, + + + + renderProp : function(v) { + return this.getPropertyName(v); + }, + + + + renderCell : function(val, meta, rec) { + var me = this, + grid = me.grid, + renderer = grid.getConfig(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); + }, + + + renderDate : Ext.util.Format.date, + + + renderBool : function(bVal) { + return this[bVal ? 'trueText' : 'falseText']; + }, + + + + getPropertyName : function(name) { + return this.grid.getConfig(name, 'displayName', name); + } +}); + + + +Ext.define('Ext.grid.property.Property', { + extend: Ext.data.Model , + + alternateClassName: 'Ext.PropGridProperty', + + fields: [{ + name: 'name', + type: 'string' + }, { + name: 'value' + }], + idProperty: 'name' +}); + + + +Ext.define('Ext.grid.property.Store', { + + extend: Ext.data.Store , + + alternateClassName: 'Ext.grid.PropertyStore', + + sortOnLoad: false, + + + + + 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() + }]); + }, + + + getProxy: function() { + if (!this.proxy) { + Ext.grid.property.Store.prototype.proxy = new Ext.data.proxy.Memory({ + model: Ext.grid.property.Property, + reader: this.getReader() + }); + } + return this.proxy; + }, + + + getReader: function() { + if (!this.reader) { + Ext.grid.property.Store.prototype.reader = new Ext.data.reader.Reader({ + model: Ext.grid.property.Property, + + buildExtractors: Ext.emptyFn, + + read: function(dataObject) { + return this.readRecords(dataObject); + }, + + readRecords: function(dataObject) { + var val, + propName, + result = { + records: [], + success: true + }; + + for (propName in dataObject) { + if (dataObject.hasOwnProperty(propName)) { + val = dataObject[propName]; + if (this.isEditableValue(val)) { + result.records.push(new Ext.grid.property.Property({ + name: propName, + value: val + }, propName)); + } + } + } + result.total = result.count = result.records.length; + return new Ext.data.ResultSet(result); + }, + + + isEditableValue: function(val){ + return Ext.isPrimitive(val) || Ext.isDate(val) || val === null; + } + }); + } + return this.reader; + }, + + + + setSource : function(dataObject) { + var me = this; + + me.source = dataObject; + me.suspendEvents(); + me.removeAll(); + me.proxy.data = dataObject; + me.load(); + me.resumeEvents(); + me.fireEvent('datachanged', me); + me.fireEvent('refresh', me); + }, + + + getProperty : function(row) { + return Ext.isNumber(row) ? this.getAt(row) : this.getById(row); + }, + + + 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) { + + me.source[prop] = value; + rec = new Ext.grid.property.Property({name: prop, value: value}, prop); + me.add(rec); + } + }, + + + remove : function(prop) { + var rec = this.getRec(prop); + if (rec) { + this.callParent([rec]); + delete this.source[prop]; + } + }, + + + getRec : function(prop) { + return this.getById(prop); + }, + + + + getSource : function() { + return this.source; + } +}); + + + + + + + + + + + + + + + + +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; + } + }, + + beginLayoutCycle: function (ownerContext) { + var target = ownerContext.target, + lastSize; + + this.callParent(arguments); + + + + + if (target.collapsed) { + ownerContext.setContentHeight(0); + + + ownerContext.restoreMinHeight = target.minHeight; + delete target.minHeight; + + + + + if (ownerContext.widthModel.shrinkWrap) { + lastSize = target.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; + } + }, + + calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) { + var border = ownerContext.getBorderInfo(), + legend = ownerContext.target.legend; + + + + return ownerContext.getProp('contentHeight') + + ownerContext.getPaddingInfo().height + + + ((Ext.isIEQuirks || Ext.isIE8m) ? + ownerContext.bodyContext.getPaddingInfo().top : 0) + + (legend ? legend.getHeight() : border.top) + + border.bottom; + }, + + publishInnerHeight: function (ownerContext, height) { + + + + var legend = ownerContext.target.legend; + if (legend) { + height -= legend.getHeight(); + } + this.callParent([ownerContext, height]); + }, + + getLayoutItems : function() { + var legend = this.owner.legend; + return legend ? [legend] : []; + } +}); + + + +Ext.define('Ext.layout.container.Absolute', { + + + + alias: 'layout.absolute', + extend: Ext.layout.container.Anchor , + alternateClassName: 'Ext.layout.AbsoluteLayout', + + + + targetCls: Ext.baseCSSPrefix + 'abs-layout-ct', + itemCls: Ext.baseCSSPrefix + 'abs-layout-item', + + + ignoreOnContentChange: true, + + type: 'absolute', + + + adjustWidthAnchor: function(value, childContext) { + var padding = this.targetPadding, + x = childContext.getStyle('left'); + + return value - x + padding.left; + }, + + + 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); + + + 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) | + ((ownerContext.widthModel.shrinkWrap ? 1 : 0) | + (ownerContext.heightModel.shrinkWrap ? 2 : 0)), + calcWidth = (containerDimensions & 1) || undefined, + calcHeight = (containerDimensions & 2) || undefined, + 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; + } + + + } + } +}); + + + +Ext.define('Ext.layout.container.Accordion', { + extend: Ext.layout.container.VBox , + alias: ['layout.accordion'], + alternateClassName: 'Ext.layout.AccordionLayout', + + targetCls: Ext.baseCSSPrefix + 'accordion-layout-ct', + itemCls: [Ext.baseCSSPrefix + 'box-item', Ext.baseCSSPrefix + 'accordion-item'], + + align: 'stretch', + + + fill : true, + + + + + titleCollapse : true, + + + hideCollapseTool : false, + + + collapseFirst : undefined, + + + animate : true, + + activeOnTop : false, + + multi: false, + + defaultAnimatePolicy: { + y: true, + height: true + }, + + constructor: function() { + var me = this; + + me.callParent(arguments); + + if (!me.multi && me.animate) { + me.animatePolicy = Ext.apply({}, me.defaultAnimatePolicy); + } else { + me.animatePolicy = null; + } + }, + + beforeRenderItems: function (items) { + var me = this, + ln = items.length, + i = 0, + owner = me.owner, + collapseFirst = me.collapseFirst, + hasCollapseFirst = Ext.isDefined(collapseFirst), + expandedItem = me.getExpanded(true)[0], + multi = me.multi, + comp; + + for (; i < ln; i++) { + comp = items[i]; + if (!comp.rendered) { + + 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) { + + + comp.titleCollapse = me.titleCollapse; + } + } + + delete comp.hideHeader; + delete comp.width; + comp.title = comp.title || ' '; + comp.addBodyCls(Ext.baseCSSPrefix + 'accordion-body'); + + + + + if (!multi) { + if (expandedItem) { + comp.collapsed = expandedItem !== comp; + } else if (comp.hasOwnProperty('collapsed') && comp.collapsed === false) { + expandedItem = comp; + } else { + comp.collapsed = true; + } + + + owner.mon(comp, 'show', me.onComponentShow, me); + } + + + comp.headerOverCls = Ext.baseCSSPrefix + 'accordion-hd-over'; + } + } + + + + 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); + + + + item.animCollapse = item.border = false; + + + if (this.fill) { + item.flex = 1; + } + }, + + beginLayout: function (ownerContext) { + this.callParent(arguments); + 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; + } + }, + + + + + onBeforeComponentExpand: function(toExpand) { + var me = this, + owner = me.owner, + multi = me.multi, + animate = me.animate, + moveToTop = !multi && !me.animate && me.activeOnTop, + expanded, + previousValue; + + if (!me.processing) { + me.processing = true; + previousValue = owner.deferLayouts; + owner.deferLayouts = true; + + if (!multi) { + expanded = me.getExpanded()[0]; + if (expanded && expanded !== toExpand) { + expanded.collapse(); + } + } + + if (moveToTop) { + + 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) { + + return false; + } + + if (!me.processing) { + me.processing = true; + previousValue = owner.deferLayouts; + owner.deferLayouts = true; + toExpand = comp.next() || comp.prev(); + + + + if (me.multi) { + expanded = me.getExpanded(); + + + + if (expanded.length === 1) { + toExpand.expand(); + } + + } else if (toExpand) { + toExpand.expand(); + } + owner.deferLayouts = previousValue; + me.processing = false; + } + }, + + onComponentShow: function(comp) { + this.onBeforeComponentExpand(comp); + }, + + onRemove: function(panel, isDestroying){ + 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; + + } +}); + + + +Ext.define('Ext.resizer.Splitter', { + extend: Ext.Component , + + + alias: 'widget.splitter', + + childEls: [ + 'collapseEl' + ], + + renderTpl: [ + '', + '', + '' + ], + + baseCls: Ext.baseCSSPrefix + 'splitter', + collapsedClsInternal: Ext.baseCSSPrefix + 'splitter-collapsed', + + + canResize: true, + + + collapsible: false, + + + + + collapseOnDblClick: true, + + + defaultSplitMin: 40, + + + defaultSplitMax: 1000, + + + + + collapseTarget: 'next', + + + + horizontal: false, + vertical: false, + + + size: 5, + + + tracker: null, + + ariaRole: 'separator', + + + getTrackerConfig: function () { + return Ext.apply({ + xclass: 'Ext.resizer.SplitterTracker', + el: this.el, + splitter: this + }, this.tracker); + }, + + beforeRender: function() { + var me = this, + target = me.getCollapseTarget(); + + me.callParent(); + + if (target.collapsed) { + me.addCls(me.collapsedClsInternal); + } + if (!me.canResize) { + me.addCls(me.baseCls + '-noresize'); + } + + Ext.applyIf(me.renderData, { + collapseDir: me.getCollapseDirection(), + collapsible: me.collapsible || target.collapsible + }); + + me.protoEl.unselectable(); + }, + + onRender: function() { + var me = this, + collapseEl, + cfg; + + me.callParent(arguments); + + + if (me.performCollapse !== false) { + if (me.renderData.collapsible) { + me.mon(me.collapseEl, 'click', me.toggleTargetCmp, me); + } + if (me.collapseOnDblClick) { + me.mon(me.el, 'dblclick', me.toggleTargetCmp, me); + } + } + + + me.mon(me.getCollapseTarget(), { + collapse: me.onTargetCollapse, + expand: me.onTargetExpand, + beforeexpand: me.onBeforeTargetExpand, + beforecollapse: me.onBeforeTargetCollapse, + scope: me + }); + + if (me.canResize) { + me.tracker = Ext.create(me.getTrackerConfig()); + + me.relayEvents(me.tracker, [ 'beforedragstart', 'dragstart', 'dragend' ]); + } + + collapseEl = me.collapseEl; + if (collapseEl) { + collapseEl.lastCollapseDirCls = me.collapseDirProps[me.collapseDirection].cls; + } + }, + + getCollapseDirection: function() { + var me = this, + dir = me.collapseDirection, + collapseTarget, idx, items, type; + + if (!dir) { + collapseTarget = me.collapseTarget; + if (collapseTarget.isComponent) { + dir = collapseTarget.collapseDirection; + } + + if (!dir) { + + + + + + + + type = me.ownerCt.layout.type; + if (collapseTarget.isComponent) { + items = me.ownerCt.items; + idx = Number(items.indexOf(collapseTarget) === items.indexOf(me) - 1) << 1 | Number(type === 'hbox'); + } else { + idx = Number(me.collapseTarget === 'prev') << 1 | Number(type === 'hbox'); + } + + + dir = ['bottom', 'right', 'top', 'left'][idx]; + } + + me.collapseDirection = dir; + } + + me.setOrientation((dir === 'top' || dir === 'bottom') ? 'horizontal' : 'vertical'); + + return dir; + }, + + getCollapseTarget: function() { + var me = this; + + return me.collapseTarget.isComponent ? me.collapseTarget + : me.collapseTarget === 'prev' ? me.previousSibling() : me.nextSibling(); + }, + + setCollapseEl: function(display){ + var el = this.collapseEl; + if (el) { + el.setDisplayed(display); + } + }, + + onBeforeTargetExpand: function(target) { + this.setCollapseEl('none'); + }, + + onBeforeTargetCollapse: function(){ + this.setCollapseEl('none'); + }, + + onTargetCollapse: function(target) { + this.el.addCls([this.collapsedClsInternal, this.collapsedCls]); + this.setCollapseEl(''); + }, + + onTargetExpand: function(target) { + this.el.removeCls([this.collapsedClsInternal, this.collapsedCls]); + this.setCollapseEl(''); + }, + + collapseDirProps: { + top: { + cls: Ext.baseCSSPrefix + 'layout-split-top' + }, + right: { + cls: Ext.baseCSSPrefix + 'layout-split-right' + }, + bottom: { + cls: Ext.baseCSSPrefix + 'layout-split-bottom' + }, + left: { + cls: Ext.baseCSSPrefix + 'layout-split-left' + } + }, + + orientationProps: { + horizontal: { + opposite: 'vertical', + fixedAxis: 'height', + stretchedAxis: 'width' + }, + vertical: { + opposite: 'horizontal', + fixedAxis: 'width', + stretchedAxis: 'height' + } + }, + + applyCollapseDirection: function () { + var me = this, + collapseEl = me.collapseEl, + collapseDirProps = me.collapseDirProps[me.collapseDirection], + cls; + + if (collapseEl) { + cls = collapseEl.lastCollapseDirCls; + if (cls) { + collapseEl.removeCls(cls); + } + + collapseEl.addCls(collapseEl.lastCollapseDirCls = collapseDirProps.cls); + } + }, + + applyOrientation: function () { + var me = this, + orientation = me.orientation, + orientationProps = me.orientationProps[orientation], + defaultSize = me.size, + fixedSizeProp = orientationProps.fixedAxis, + stretchSizeProp = orientationProps.stretchedAxis, + cls = me.baseCls + '-'; + + me[orientation] = true; + me[orientationProps.opposite] = false; + + if (!me.hasOwnProperty(fixedSizeProp) || me[fixedSizeProp] === '100%') { + me[fixedSizeProp] = defaultSize; + } + if (!me.hasOwnProperty(stretchSizeProp) || me[stretchSizeProp] === defaultSize) { + me[stretchSizeProp] = '100%'; + } + + me.removeCls(cls + orientationProps.opposite); + me.addCls(cls + orientation); + }, + + setOrientation: function (orientation) { + var me = this; + + if (me.orientation !== orientation) { + me.orientation = orientation; + me.applyOrientation(); + } + }, + + updateOrientation: function () { + delete this.collapseDirection; + this.getCollapseDirection(); + this.applyCollapseDirection(); + }, + + toggleTargetCmp: function(e, t) { + var cmp = this.getCollapseTarget(), + placeholder = cmp.placeholder, + toggle; + + + if (Ext.isFunction(cmp.expand) && Ext.isFunction(cmp.collapse)) { + if (placeholder && !placeholder.hidden) { + toggle = true; + } else { + toggle = !cmp.hidden; + } + + if (toggle) { + if (cmp.collapsed) { + cmp.expand(); + } else if (cmp.collapseDirection) { + cmp.collapse(); + } else { + cmp.collapse(this.renderData.collapseDir); + } + } + } + }, + + + setSize: function() { + var me = this; + me.callParent(arguments); + if (Ext.isIE && me.el) { + me.el.repaint(); + } + }, + + beforeDestroy: function(){ + Ext.destroy(this.tracker); + this.callParent(); + } +}); + + + +Ext.define('Ext.resizer.BorderSplitter', { + extend: Ext.resizer.Splitter , + + + + alias: 'widget.bordersplitter', + + + collapseTarget: null, + + getTrackerConfig: function () { + var trackerConfig = this.callParent(); + + trackerConfig.xclass = 'Ext.resizer.BorderSplitterTracker'; + + return trackerConfig; + } +}); + + + +Ext.define('Ext.layout.container.Border', { + + extend: Ext.layout.container.Container , + alias: 'layout.border', + alternateClassName: 'Ext.layout.BorderLayout', + + + + + + + + + + + targetCls: Ext.baseCSSPrefix + 'border-layout-ct', + + itemCls: [Ext.baseCSSPrefix + 'border-item', Ext.baseCSSPrefix + 'box-item'], + + type: 'border', + + isBorderLayout: true, + + + + + + + padding: undefined, + + percentageRe: /(\d+)%/, + + horzPositionProp: 'left', + padOnContainerProp: 'left', + padNotOnContainerProp: 'right', + + + 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' + } + }, + + + centerRegion: null, + + manageMargins: true, + + panelCollapseAnimate: true, + + panelCollapseMode: 'placeholder', + + + regionWeights: { + north: 20, + south: 10, + center: 0, + west: -10, + east: -20 + }, + + + + + + 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; + regions[comp.id] = childContext; + + if (comp.isCenter) { + center = childContext; + centerFlex = comp.flex; + ownerContext.centerRegion = center; + + continue; + } + + if (isVert !== childContext.isVert) { + continue; + } + + + + 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; + } + } + } + } + + + if (center) { + target = center.target; + + if ((placeholder = target.placeholderFor)) { + if (!centerFlex && isVert === placeholder.collapsedVertical()) { + + centerFlex = 0; + center.collapseAxis = name; + } + } else if (target.collapsed && (isVert === target.collapsedVertical())) { + + centerFlex = 0; + center.collapseAxis = name; + } + } + + if (centerFlex == null) { + + 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; + + + 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) { + 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(); + } + } + } + + + + + me.callParent(arguments); + + items = ownerContext.childItems; + length = items.length; + regions = {}; + + ownerContext.borderAxisHorz = me.beginAxis(ownerContext, regions, 'horz'); + ownerContext.borderAxisVert = me.beginAxis(ownerContext, regions, 'vert'); + + + + + for (i = 0; i < length; ++i) { + childContext = items[i]; + collapseTarget = me.getSplitterTarget(childContext.target); + + if (collapseTarget) { + region = regions[collapseTarget.id] + if (!region) { + + + + 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; + } + } + + + 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; + + + + 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); + + + + for (i = 0; i < length; ++i) { + childContext = childItems[i]; + childMargins = childContext.getMarginInfo(); + + + if (childContext.isHorz || childContext.isCenter) { + horz.addUnflexed(childMargins.width); + horzPercentTotal -= childMargins.width; + } + + if (childContext.isVert || childContext.isCenter) { + vert.addUnflexed(childMargins.height); + vertPercentTotal -= childMargins.height; + } + + + if (!childContext.flex && !childContext.percentage) { + if (childContext.isHorz || (childContext.isCenter && childContext.collapseAxis === 'horz')) { + size = childContext.getProp('width'); + + horz.addUnflexed(size); + + + if (childContext.collapseTarget) { + horzPercentTotal -= size; + } + } else if (childContext.isVert || (childContext.isCenter && childContext.collapseAxis === 'vert')) { + size = childContext.getProp('height'); + + vert.addUnflexed(size); + + + if (childContext.collapseTarget) { + vertPercentTotal -= size; + } + } + + } + } + + for (i = 0; i < length; ++i) { + childContext = childItems[i]; + childMargins = childContext.getMarginInfo(); + + + 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); + } + + } + } + + + + + + for (i = 0; i < length; ++i) { + childContext = childItems[i]; + + if (!childContext.isCenter) { + me.calculateChildAxis(childContext, horz); + me.calculateChildAxis(childContext, vert); + } + } + + + + + if (me.finishAxis(ownerContext, vert) + me.finishAxis(ownerContext, horz) < 2) { + me.done = false; + } else { + + + + me.finishPositions(childItems); + } + }, + + + 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) { + region = collapseTarget.region; + } else { + region = childContext.region; + flex = childContext.flex; + } + + isBegin = region == axis.borderBegin; + + if (!isBegin && region != axis.borderEnd) { + + + 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) { + + 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; + }, + + + 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; + }, + + + 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; + + + + + + + + + + + + + + + + + + + + + + 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(); + }, + + 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; + }, + + + + + + 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 + }, splitterCfg), + at = index + ((region === 'south' || region === 'east') ? 0 : 1); + + if (item.collapseMode === 'mini') { + splitter.collapsedCls = item.collapsedCls; + } + + item.splitter = this.owner.add(at, splitter); + }, + + + 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 (item.initBorderRegion) { + + + item.initBorderRegion(); + } + + isCenter = region === 'center'; + if (isCenter) { + if (me.centerRegion) { + Ext.Error.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') { + + + 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 (splitter && !owner.destroying) { + owner.doRemove(splitter, true); + } + delete comp.splitter; + } + + me.callParent(arguments); + + if (!destroying && !isDestroying && comp.rendered) { + + el = comp.getEl(); + el.setStyle('top', ''); + el.setStyle(me.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 (items) { + var edgeRegions = { + + + + + }, + 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) { + + 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); + } + } + } + }, + + + 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); +}); + + +Ext.define('ExtThemeNeptune.resizer.Splitter', { + override: 'Ext.resizer.Splitter', + size: 8 +}); + + + +Ext.define('Ext.layout.container.Column', { + + extend: Ext.layout.container.Auto , + alias: ['layout.column'], + alternateClassName: 'Ext.layout.ColumnLayout', + + type: 'column', + + itemCls: Ext.baseCSSPrefix + 'column', + + targetCls: Ext.baseCSSPrefix + 'column-layout-ct', + + + columnWidthSizePolicy: { + readsWidth: 0, + readsHeight: 1, + setsWidth: 1, + setsHeight: 0 + }, + + createsInnerCt: true, + + manageOverflow: true, + + isItemShrinkWrap: function(ownerContext){ + return true; + }, + + getItemSizePolicy: function (item, ownerSizeModel) { + if (item.columnWidth) { + if (!ownerSizeModel) { + ownerSizeModel = this.owner.getSizeModel(); + } + + if (!ownerSizeModel.width.shrinkWrap) { + return this.columnWidthSizePolicy; + } + } + return this.autoSizePolicy; + }, + + calculateItems: function (ownerContext, containerSize) { + var me = this, + targetContext = ownerContext.targetContext, + items = ownerContext.childItems, + len = items.length, + contentWidth = 0, + gotWidth = containerSize.gotWidth, + blocked, availableWidth, i, itemContext, itemMarginWidth, itemWidth; + + + if (gotWidth === false) { + + targetContext.domBlock(me, 'width'); + blocked = true; + } else if (gotWidth) { + availableWidth = containerSize.width; + } else { + + + return true; + } + + + + for (i = 0; i < len; ++i) { + itemContext = items[i]; + + + + + itemMarginWidth = itemContext.getMarginInfo().width; + + if (!itemContext.widthModel.calculated) { + itemWidth = itemContext.getProp('width'); + if (typeof itemWidth != 'number') { + itemContext.block(me, 'width'); + blocked = true; + } + + contentWidth += itemWidth + itemMarginWidth; + } + } + + if (!blocked) { + availableWidth = (availableWidth < contentWidth) ? 0 : availableWidth - contentWidth; + + for (i = 0; i < len; ++i) { + itemContext = items[i]; + if (itemContext.widthModel.calculated) { + itemMarginWidth = itemContext.marginInfo.width; + itemWidth = itemContext.target.columnWidth; + itemWidth = Math.floor(itemWidth * availableWidth) - itemMarginWidth; + itemWidth = itemContext.setWidth(itemWidth); + contentWidth += itemWidth + itemMarginWidth; + } + } + + ownerContext.setContentWidth(contentWidth + ownerContext.paddingContext.getPaddingInfo().width); + } + + + return !blocked; + }, + + setCtSizeIfNeeded: function(ownerContext, containerSize) { + var me = this, + padding = ownerContext.paddingContext.getPaddingInfo(); + + me.callParent(arguments); + + + + if ((Ext.isIEQuirks || Ext.isIE7m) && me.isShrinkWrapTpl && padding.right) { + ownerContext.outerCtContext.setProp('width', + containerSize.width + padding.left); + } + } + +}); + + + +Ext.define('Ext.layout.container.Form', { + + + + alias: 'layout.form', + extend: Ext.layout.container.Container , + alternateClassName: 'Ext.layout.FormLayout', + + + + tableCls: Ext.baseCSSPrefix + 'form-layout-table', + + type: 'form', + + createsInnerCt: true, + + manageOverflow: true, + + + lastOverflowAdjust: { + width: 0, + height: 0 + }, + + childEls: ['formTable'], + + padRow: '', + + renderTpl: [ + '', + '{%this.renderBody(out,values)%}', + '', + '{%this.renderPadder(out,values)%}' + ], + + getRenderData: function(){ + var data = this.callParent(); + data.tableCls = this.tableCls; + return data; + }, + + calculate : function (ownerContext) { + var me = this, + containerSize = me.getContainerSize(ownerContext, true), + tableWidth, + childItems, + i = 0, length, + shrinkwrapHeight = ownerContext.sizeModel.height.shrinkWrap; + + if (shrinkwrapHeight) { + if (ownerContext.hasDomProp('containerChildrenSizeDone')) { + ownerContext.setProp('contentHeight', me.formTable.dom.offsetHeight + ownerContext.targetContext.getPaddingInfo().height); + } else { + me.done = false; + } + } + + + if (containerSize.gotWidth) { + tableWidth = me.formTable.dom.offsetWidth; + childItems = ownerContext.childItems; + + for (length = childItems.length; i < length; ++i) { + childItems[i].setWidth(tableWidth, false); + } + } else { + me.done = false; + } + }, + + getRenderTarget: function() { + return this.formTable; + }, + + getRenderTree: function() { + var me = this, + result = me.callParent(arguments), + i, len; + + for (i = 0, len = result.length; i < len; i++) { + result[i] = me.transformItemRenderTree(result[i]); + } + return result; + }, + + transformItemRenderTree: function(item) { + + if (item.tag && item.tag == 'table') { + item.tag = 'tbody'; + item.role = 'presentation'; + delete item.cellspacing; + delete item.cellpadding; + + + + + + if (Ext.isIE6) { + item.cn = this.padRow; + } + + return item; + } + + return { + tag: 'tbody', + role: 'presentation', + cn: { + tag: 'tr', + role: 'presentation', + cn: { + tag: 'td', + role: 'presentation', + colspan: 3, + style: 'width:100%', + cn: item + } + } + }; + + }, + + isValidParent: function(item, target, position) { + return true; + }, + + isItemShrinkWrap: function(item) { + return ((item.shrinkWrap === true) ? 3 : item.shrinkWrap||0) & 2; + }, + + getItemSizePolicy: function(item) { + return { + setsWidth: 1, + setsHeight: 0 + }; + }, + + + + + + beginLayoutCycle: function (ownerContext, firstCycle) { + var padEl = this.overflowPadderEl; + + if (padEl) { + padEl.setStyle('display', 'none'); + } + + + + if (!ownerContext.state.overflowAdjust) { + ownerContext.state.overflowAdjust = this.lastOverflowAdjust; + } + }, + + + calculateOverflow: function (ownerContext, containerSize, dimensions) { + var me = this, + targetContext = ownerContext.targetContext, + manageOverflow = me.manageOverflow, + state = ownerContext.state, + overflowAdjust = state.overflowAdjust, + padWidth, padHeight, padElContext, padding, scrollRangeFlags, + scrollbarSize, contentW, contentH, ownerW, ownerH, scrollbars, + xauto, yauto; + + if (manageOverflow && !state.secondPass && !me.reserveScrollbar) { + + + xauto = (me.getOverflowXStyle(ownerContext) === 'auto'); + yauto = (me.getOverflowYStyle(ownerContext) === 'auto'); + + + + + if (!containerSize.gotWidth) { + xauto = false; + } + if (!containerSize.gotHeight) { + yauto = false; + } + + if (xauto || yauto) { + scrollbarSize = Ext.getScrollbarSize(); + + + + contentW = ownerContext.peek('contentWidth'); + contentH = ownerContext.peek('contentHeight'); + + + + + padding = targetContext.getPaddingInfo(); + contentW -= padding.width; + contentH -= padding.height; + ownerW = containerSize.width; + ownerH = containerSize.height; + + scrollbars = me.getScrollbarsNeeded(ownerW, ownerH, contentW, contentH); + state.overflowState = scrollbars; + + if (typeof dimensions == 'number') { + scrollbars &= ~dimensions; + } + + overflowAdjust = { + width: (xauto && (scrollbars & 2)) ? scrollbarSize.width : 0, + height: (yauto && (scrollbars & 1)) ? scrollbarSize.height : 0 + }; + + + + if (overflowAdjust.width !== me.lastOverflowAdjust.width || overflowAdjust.height !== me.lastOverflowAdjust.height) { + me.done = false; + + + + ownerContext.invalidate({ + state: { + overflowAdjust: overflowAdjust, + overflowState: state.overflowState, + secondPass: true + } + }); + } + } + } + + if (!me.done) { + return; + } + + padElContext = ownerContext.padElContext || + (ownerContext.padElContext = ownerContext.getEl('overflowPadderEl', me)); + + + + if (padElContext) { + scrollbars = state.overflowState; + padWidth = ownerContext.peek('contentWidth'); + + + padHeight = 1; + + if (scrollbars) { + padding = targetContext.getPaddingInfo(); + scrollRangeFlags = me.scrollRangeFlags; + + if ((scrollbars & 2) && (scrollRangeFlags & 1)) { + padHeight += padding.bottom; + } + + if ((scrollbars & 1) && (scrollRangeFlags & 4)) { + padWidth += padding.right; + } + padElContext.setProp('display', ''); + padElContext.setSize(padWidth, padHeight); + } else { + padElContext.setProp('display', 'none'); + } + } + }, + + completeLayout: function (ownerContext) { + + this.lastOverflowAdjust = ownerContext.state.overflowAdjust; + }, + + + doRenderPadder: function (out, renderData) { + + + + var me = renderData.$layout, + owner = me.owner, + scrollRangeFlags = me.getScrollRangeFlags(); + + if (me.manageOverflow) { + if (scrollRangeFlags & 5) { + out.push(''); + + me.scrollRangeFlags = scrollRangeFlags; + } + } + }, + + + getContainerSize : function(ownerContext, inDom, ignoreOverflow) { + + + + + + + var targetContext = ownerContext.targetContext, + frameInfo = targetContext.getFrameInfo(), + + padding = targetContext.getPaddingInfo(), + got = 0, + needed = 0, + overflowAdjust = ignoreOverflow ? null : ownerContext.state.overflowAdjust, + gotWidth, gotHeight, width, height; + + + + + + + + + + if (!ownerContext.widthModel.shrinkWrap) { + ++needed; + width = inDom ? targetContext.getDomProp('width') : targetContext.getProp('width'); + gotWidth = (typeof width == 'number'); + if (gotWidth) { + ++got; + width -= frameInfo.width + padding.width; + if (overflowAdjust) { + width -= overflowAdjust.width; + } + } + } + + if (!ownerContext.heightModel.shrinkWrap) { + ++needed; + height = inDom ? targetContext.getDomProp('height') : targetContext.getProp('height'); + gotHeight = (typeof height == 'number'); + if (gotHeight) { + ++got; + height -= frameInfo.height + padding.height; + if (overflowAdjust) { + height -= overflowAdjust.height; + } + } + } + + return { + width: width, + height: height, + needed: needed, + got: got, + gotAll: got == needed, + gotWidth: gotWidth, + gotHeight: gotHeight + }; + }, + + + getOverflowXStyle: function(ownerContext) { + var me = this; + + return me.overflowXStyle || + (me.overflowXStyle = me.owner.scrollFlags.overflowX || ownerContext.targetContext.getStyle('overflow-x')); + }, + + + getOverflowYStyle: function(ownerContext) { + var me = this; + + return me.overflowYStyle || + (me.overflowYStyle = me.owner.scrollFlags.overflowY || ownerContext.targetContext.getStyle('overflow-y')); + }, + + + getScrollRangeFlags: (function () { + var flags = -1; + + return function () { + if (flags < 0) { + var div = Ext.getBody().createChild({ + + cls: Ext.baseCSSPrefix + 'border-box', + role: 'presentation', + style: { + width: '100px', height: '100px', padding: '10px', + overflow: 'auto' + }, + children: [{ + role: 'presentation', + style: { + border: '1px solid red', + width: '150px', height: '150px', + margin: '0 5px 5px 0' + } + }] + }), + scrollHeight = div.dom.scrollHeight, + scrollWidth = div.dom.scrollWidth, + heightFlags = { + + 175: 0, + + 165: 1, + + 170: 2, + + 160: 3 + }, + widthFlags = { + + 175: 0, + + 165: 4, + + 170: 8, + + 160: 12 + }; + + flags = (heightFlags[scrollHeight] || 0) | (widthFlags[scrollWidth] || 0); + + div.remove(); + } + + return flags; + }; + }()), + + initLayout: function() { + var me = this, + scrollbarWidth = Ext.getScrollbarSize().width; + + me.callParent(); + + + + + if (scrollbarWidth && me.manageOverflow && !me.hasOwnProperty('lastOverflowAdjust')) { + if (me.owner.scrollFlags.y || me.reserveScrollbar) { + me.lastOverflowAdjust = { + width: scrollbarWidth, + height: 0 + }; + } + } + }, + + setupRenderTpl: function (renderTpl) { + this.callParent(arguments); + + renderTpl.renderPadder = this.doRenderPadder; + } +}); + + + + Ext.define('Ext.menu.ColorPicker', { + extend: Ext.menu.Menu , + + alias: 'widget.colormenu', + + + + + + + hideOnClick : true, + + + pickerId : null, + + + + + + + + initComponent : function(){ + var me = this, + cfg = Ext.apply({}, me.initialConfig); + + + 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'); + + + me.relayEvents(me.picker, ['select']); + + if (me.hideOnClick) { + me.on('select', me.hidePickerOnSelect, me); + } + }, + + + hidePickerOnSelect: function() { + Ext.menu.Manager.hideAll(); + } + }); + + + + Ext.define('Ext.menu.DatePicker', { + extend: Ext.menu.Menu , + + alias: 'widget.datemenu', + + + + + + + hideOnClick : true, + + + pickerId : null, + + + + + + initComponent : function(){ + var me = this, + cfg = Ext.apply({}, me.initialConfig); + + + delete cfg.listeners; + + Ext.apply(me, { + showSeparator: false, + plain: true, + bodyPadding: 0, + items: Ext.applyIf({ + cls: Ext.baseCSSPrefix + 'menu-date-item', + margin: 0, + border: false, + id: me.pickerId, + xtype: 'datepicker' + }, cfg) + }); + + me.callParent(arguments); + + me.picker = me.down('datepicker'); + + me.relayEvents(me.picker, ['select']); + + if (me.hideOnClick) { + me.on('select', me.hidePickerOnSelect, me); + } + }, + + hidePickerOnSelect: function() { + Ext.menu.Manager.hideAll(); + } + }); + + + +Ext.define('Ext.panel.Tool', { + extend: Ext.Component , + + alias: 'widget.tool', + + + isTool: true, + + baseCls: Ext.baseCSSPrefix + 'tool', + disabledCls: Ext.baseCSSPrefix + 'tool-disabled', + + + toolPressedCls: Ext.baseCSSPrefix + 'tool-pressed', + + toolOverCls: Ext.baseCSSPrefix + 'tool-over', + + ariaRole: 'button', + + childEls: [ + 'toolEl' + ], + + renderTpl: [ + '' + ], + + + toolOwner: null, + + + + + + + + + + + + + tooltipType: 'qtip', + + + stopEvent: true, + + + height: 15, + width: 15, + + _toolTypes: { + close:1, + collapse:1, + down:1, + expand:1, + gear:1, + help:1, + left:1, + maximize:1, + minimize:1, + minus:1, + + next:1, + pin:1, + plus:1, + prev:1, + print:1, + refresh:1, + + restore:1, + right:1, + save:1, + search:1, + toggle:1, + unpin:1, + up:1 + }, + + initComponent: function() { + var me = this; + me.addEvents( + + 'click' + ); + + if (me.id && me._toolTypes[me.id] && Ext.global.console) { + Ext.global.console.warn('When specifying a tool you should use the type option, the id can conflict now that tool is a Component'); + } + + me.type = me.type || me.id; + + Ext.applyIf(me.renderData, { + baseCls: me.baseCls, + blank: Ext.BLANK_IMAGE_URL, + type: me.type + }); + + + me.tooltip = me.tooltip || me.qtip; + me.callParent(); + }, + + + afterRender: function() { + var me = this, + attr; + + me.callParent(arguments); + + me.el.on({ + click: me.onClick, + mousedown: me.onMouseDown, + mouseover: me.onMouseOver, + mouseout: me.onMouseOut, + scope: me + }); + + if (me.tooltip) { + if (Ext.quickTipsActive && Ext.isObject(me.tooltip)) { + Ext.tip.QuickTipManager.register(Ext.apply({ + target: me.id + }, me.tooltip)); + } + else { + attr = me.tooltipType == 'qtip' ? 'data-qtip' : 'title'; + me.el.dom.setAttribute(attr, me.tooltip); + } + } + }, + + getFocusEl: function() { + return this.el; + }, + + + setType: function(type) { + var me = this, + oldType = me.type; + + me.type = type; + if (me.rendered) { + if (oldType) { + me.toolEl.removeCls(me.baseCls + '-' + oldType); + } + me.toolEl.addCls(me.baseCls + '-' + type); + } else { + me.renderData.type = type; + } + return me; + }, + + + onClick: function(e, target) { + var me = this; + + if (me.disabled) { + return false; + } + + + me.el.removeCls(me.toolPressedCls); + me.el.removeCls(me.toolOverCls); + + if (me.stopEvent !== false) { + e.stopEvent(); + } + + if (me.handler) { + Ext.callback(me.handler, me.scope || me, [e, target, me.ownerCt, me]); + } else if (me.callback) { + Ext.callback(me.callback, me.scope || me, [me.toolOwner || me.ownerCt, me, e]); + } + me.fireEvent('click', me, e); + return true; + }, + + + onDestroy: function(){ + var me = this; + + if (Ext.quickTipsActive && Ext.isObject(me.tooltip)) { + Ext.tip.QuickTipManager.unregister(me.id); + } + + + if (me.keyMap) { + me.keyMap.destroy(); + } + delete me.toolOwner; + me.callParent(); + }, + + + onMouseDown: function() { + 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); + } +}); + + +Ext.define('ExtThemeNeptune.panel.Tool', { + override: 'Ext.panel.Tool', + height: 16, + width: 16 +}); + + + +Ext.define('Ext.resizer.SplitterTracker', { + extend: Ext.dd.DragTracker , + + enabled: true, + + overlayCls: Ext.baseCSSPrefix + 'resizable-overlay', + + createDragOverlay: function () { + var overlay; + + overlay = this.overlay = Ext.getBody().createChild({ + role: 'presentation', + cls: this.overlayCls, + html: ' ' + }); + + overlay.unselectable(); + overlay.setSize(Ext.Element.getViewWidth(true), Ext.Element.getViewHeight(true)); + overlay.show(); + }, + + getPrevCmp: function() { + var splitter = this.getSplitter(); + return splitter.previousSibling(':not([hidden])'); + }, + + getNextCmp: function() { + var splitter = this.getSplitter(); + return splitter.nextSibling(':not([hidden])'); + }, + + + + onBeforeStart: function(e) { + var me = this, + prevCmp = me.getPrevCmp(), + nextCmp = me.getNextCmp(), + collapseEl = me.getSplitter().collapseEl, + target = e.getTarget(), + box; + + if (!prevCmp || !nextCmp) { + return false; + } + + if (collapseEl && target === me.getSplitter().collapseEl.dom) { + return false; + } + + + if (nextCmp.collapsed || prevCmp.collapsed) { + return false; + } + + + me.prevBox = prevCmp.getEl().getBox(); + me.nextBox = nextCmp.getEl().getBox(); + me.constrainTo = box = me.calculateConstrainRegion(); + + if (!box) { + return false; + } + + return box; + }, + + + onStart: function(e) { + var splitter = this.getSplitter(); + this.createDragOverlay(); + splitter.addCls(splitter.baseCls + '-active'); + }, + + + calculateConstrainRegion: function() { + var me = this, + splitter = me.getSplitter(), + splitWidth = splitter.getWidth(), + defaultMin = splitter.defaultSplitMin, + orient = splitter.orientation, + prevBox = me.prevBox, + prevCmp = me.getPrevCmp(), + nextBox = me.nextBox, + nextCmp = me.getNextCmp(), + + + + prevConstrainRegion, nextConstrainRegion, constrainOptions; + + + if (orient === 'vertical') { + constrainOptions = { + prevCmp: prevCmp, + nextCmp: nextCmp, + prevBox: prevBox, + nextBox: nextBox, + defaultMin: defaultMin, + splitWidth: splitWidth + }; + + + prevConstrainRegion = new Ext.util.Region( + prevBox.y, + me.getVertPrevConstrainRight(constrainOptions), + prevBox.bottom, + me.getVertPrevConstrainLeft(constrainOptions) + ); + + nextConstrainRegion = new Ext.util.Region( + nextBox.y, + me.getVertNextConstrainRight(constrainOptions), + nextBox.bottom, + me.getVertNextConstrainLeft(constrainOptions) + ); + } else { + + prevConstrainRegion = new Ext.util.Region( + prevBox.y + (prevCmp.minHeight || defaultMin), + prevBox.right, + + + (prevCmp.maxHeight ? prevBox.y + prevCmp.maxHeight : nextBox.bottom - (nextCmp.minHeight || defaultMin)) + splitWidth, + prevBox.x + ); + + nextConstrainRegion = new Ext.util.Region( + + + (nextCmp.maxHeight ? nextBox.bottom - nextCmp.maxHeight : prevBox.y + (prevCmp.minHeight || defaultMin)) - splitWidth, + nextBox.right, + nextBox.bottom - (nextCmp.minHeight || defaultMin), + nextBox.x + ); + } + + + return prevConstrainRegion.intersect(nextConstrainRegion); + }, + + + performResize: function(e, offset) { + var me = this, + splitter = me.getSplitter(), + orient = splitter.orientation, + prevCmp = me.getPrevCmp(), + nextCmp = me.getNextCmp(), + owner = splitter.ownerCt, + flexedSiblings = owner.query('>[flex]'), + len = flexedSiblings.length, + vertical = orient === 'vertical', + i = 0, + dimension = vertical ? 'width' : 'height', + totalFlex = 0, + item, size; + + + for (; i < len; i++) { + item = flexedSiblings[i]; + size = vertical ? item.getWidth() : item.getHeight(); + totalFlex += size; + item.flex = size; + } + + offset = vertical ? offset[0] : offset[1]; + + if (prevCmp) { + size = me.prevBox[dimension] + offset; + if (prevCmp.flex) { + prevCmp.flex = size; + } else { + prevCmp[dimension] = size; + } + } + if (nextCmp) { + size = me.nextBox[dimension] - offset; + if (nextCmp.flex) { + nextCmp.flex = size; + } else { + nextCmp[dimension] = size; + } + } + + owner.updateLayout(); + }, + + + + + endDrag: function () { + var me = this; + + if (me.overlay) { + me.overlay.remove(); + delete me.overlay; + } + + me.callParent(arguments); + }, + + + onEnd: function(e) { + var me = this, + splitter = me.getSplitter(); + + splitter.removeCls(splitter.baseCls + '-active'); + me.performResize(e, me.getResizeOffset()); + }, + + + + onDrag: function(e) { + var me = this, + offset = me.getOffset('dragTarget'), + splitter = me.getSplitter(), + splitEl = splitter.getEl(), + orient = splitter.orientation; + + if (orient === "vertical") { + splitEl.setX(me.startRegion.left + offset[0]); + } else { + splitEl.setY(me.startRegion.top + offset[1]); + } + }, + + getSplitter: function() { + return this.splitter; + }, + + getVertPrevConstrainRight: function(o) { + + + return (o.prevCmp.maxWidth ? o.prevBox.x + o.prevCmp.maxWidth : + o.nextBox.right - (o.nextCmp.minWidth || o.defaultMin)) + o.splitWidth; + }, + + getVertPrevConstrainLeft: function(o) { + return o.prevBox.x + (o.prevCmp.minWidth || o.defaultMin); + }, + + + getVertNextConstrainRight: function(o) { + return o.nextBox.right - (o.nextCmp.minWidth || o.defaultMin); + }, + + getVertNextConstrainLeft: function(o) { + + + return (o.nextCmp.maxWidth ? o.nextBox.right - o.nextCmp.maxWidth : + o.prevBox.x + (o.prevBox.minWidth || o.defaultMin)) - o.splitWidth; + }, + + getResizeOffset: function() { + return this.getOffset('dragTarget'); + } +}); + + + +Ext.define('Ext.resizer.BorderSplitterTracker', { + extend: Ext.resizer.SplitterTracker , + + + getPrevCmp: null, + getNextCmp: null, + + + 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), + + i, neighbor, minRange, maxRange, maxGrowth, maxShrink, targetSize; + + + minRange = (collapseTarget[minSizeProp] || Math.min(size,defaultSplitMin)) - size; + + + maxRange = collapseTarget[maxSizeProp]; + if (!maxRange) { + maxRange = 1e9; + } else { + maxRange -= size; + } + targetSize = size; + + for (i = 0; i < length; ++i) { + neighbor = neighbors[i]; + size = neighbor[getSizeMethod](); + + + maxGrowth = size - neighbor[maxSizeProp]; + maxShrink = size - (neighbor[minSizeProp] || Math.min(size,defaultSplitMin)); + + if (!isNaN(maxGrowth)) { + + + if (minRange < maxGrowth) { + minRange = maxGrowth; + } + } + + + + 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, + + targetSize: targetSize + }; + + return box; + }, + + constraintAdjusters: { + + left: function (box, minRange, maxRange, splitter) { + box[0] = box.x = box.left = box.right + minRange; + box.right += maxRange + splitter.getWidth(); + }, + + + top: function (box, minRange, maxRange, splitter) { + box[1] = box.y = box.top = box.bottom + minRange; + box.bottom += maxRange + splitter.getHeight(); + }, + + + bottom: function (box, minRange, maxRange, splitter) { + box.bottom = box.top - minRange; + box.top -= maxRange + splitter.getHeight(); + }, + + + 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, + collapseEl = me.getSplitter().collapseEl, + target = e.getTarget(), + length = neighbors.length, + i, neighbor; + + if (collapseEl && target === splitter.collapseEl.dom) { + return false; + } + + if (collapseTarget.collapsed) { + return false; + } + + + 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, + + adjusters = me.splitAdjusters[splitter.vertical ? 'horz' : 'vert'], + delta = offset[adjusters.index], + dragInfo = me.dragInfo, + + + + + + owner; + + if (collapseDirection == 'right' || collapseDirection == 'bottom') { + + delta = -delta; + } + + + delta = Math.min(Math.max(dragInfo.minRange, delta), dragInfo.maxRange); + + if (delta) { + (owner = splitter.ownerCt).suspendLayouts(); + + adjusters.adjustTarget(collapseTarget, dragInfo.targetSize, delta); + + + + + + + + + + owner.resumeLayouts(true); + } + }, + + splitAdjusters: { + horz: { + index: 0, + + + + adjustTarget: function (target, size, delta) { + target.flex = null; + target.setSize(size + delta); + } + }, + vert: { + index: 1, + + + + adjustTarget: function (target, targetSize, delta) { + target.flex = null; + target.setSize(undefined, targetSize + delta); + } + } + }, + + getCollapseDirection: function() { + return this.splitter.getCollapseDirection(); + } +}); + + + +Ext.define('Ext.resizer.Handle', { + extend: Ext.Component , + handleCls: '', + baseHandleCls: Ext.baseCSSPrefix + 'resizable-handle', + + + region: '', + + ariaRole: 'presentation', + + beforeRender: function() { + var me = this; + + me.callParent(); + + me.protoEl.unselectable(); + + me.addCls( + me.baseHandleCls, + me.baseHandleCls + '-' + me.region, + me.handleCls + ); + } +}); + + + +Ext.define('Ext.resizer.ResizeTracker', { + extend: Ext.dd.DragTracker , + dynamic: true, + preserveRatio: false, + + + 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); + + + if (me.preserveRatio && me.minWidth && me.minHeight) { + widthRatio = me.minWidth / me.el.getWidth(); + heightRatio = me.minHeight / me.el.getHeight(); + + + + + if (heightRatio > widthRatio) { + me.minWidth = me.el.getWidth() * heightRatio; + } else { + me.minHeight = me.el.getHeight() * widthRatio; + } + } + + + + 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) { + + this.startBox = this.target.getBox(); + }, + + + getProxy: function() { + var me = this; + + if (!me.dynamic && !me.proxy) { + me.proxy = me.createProxy(me.target || me.el); + + + + me.hideProxy = true; + } + if (me.proxy) { + me.proxy.show(); + return me.proxy; + } + }, + + + 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) { + + this.activeResizeHandle = Ext.get(this.getDragTarget().id); + + + if (!this.dynamic) { + this.resize(this.startBox); + } + }, + + onDrag: function(e) { + + 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, + 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 + }; + + + 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; + } + + + if (newBox.width < me.minWidth || newBox.width > me.maxWidth) { + newBox.width = Ext.Number.constrain(newBox.width, me.minWidth, me.maxWidth); + + + 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); + + + if (adjustY) { + newBox.y = box.y + (box.height - newBox.height); + } + } else { + me.lastY = newBox.y; + } + + + if (me.preserveRatio || e.shiftKey) { + ratio = me.startBox.width / me.startBox.height; + + + newHeight = Math.min(Math.max(me.minHeight, newBox.width / ratio), me.maxHeight); + newWidth = Math.min(Math.max(me.minWidth, newBox.height * ratio), me.maxWidth); + + + if (axis == 1) { + newBox.height = newHeight; + } + + + else if (axis == 2) { + newBox.width = newWidth; + } + + + else { + + + dragRatio = Math.abs(oppositeCorner[0] - this.lastXY[0]) / Math.abs(oppositeCorner[1] - this.lastXY[1]); + + + if (dragRatio > ratio) { + newBox.height = newHeight; + } else { + newBox.width = newWidth; + } + + + 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); + } + } + } + + + 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; + + + if (me.dynamic || (!me.dynamic && atEnd)) { + + if (setPosition) { + me.target.setBox(box); + } else { + me.target.setSize(box.width, box.height); + } + + } + + + 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; + } +}); + + + +Ext.define('Ext.resizer.Resizer', { + mixins: { + observable: Ext.util.Observable + }, + + + 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+/, + + + 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(config) { + var me = this, + resizeTarget, + tag, + handles = me.handles, + handleCls, + possibles, + len, + i = 0, + pos, + el, + handleEls = [], + eastWestStyle, style, + targetBaseCls, wrapTarget, + unselectableCls = Ext.dom.Element.unselectableCls; + + me.addEvents( + + 'beforeresize', + + 'resizedrag', + + 'resize' + ); + + if (Ext.isString(config) || Ext.isElement(config) || config.dom) { + resizeTarget = config; + config = arguments[1] || {}; + config.target = resizeTarget; + } + + me.mixins.observable.constructor.call(me, config); + + + + resizeTarget = me.target; + if (resizeTarget) { + if (resizeTarget.isComponent) { + + + + + 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 { + resizeTarget = me.target = me.el = Ext.get(me.el); + } + + + + me.el.addCls(Ext.AbstractComponent.prototype.borderBoxCls); + + + 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); + } + + + if (me.width !== null || me.height !== null) { + me.target.setSize(me.width, me.height); + } + + + tag = me.el.dom.tagName.toUpperCase(); + if (tag === 'TEXTAREA' || tag === 'IMG' || tag === 'TABLE') { + + me.originalTarget = me.target; + + wrapTarget = resizeTarget.isComponent ? resizeTarget.getEl() : resizeTarget; + + + me.el.addCls(me.wrappedCls); + + me.target = me.el = me.el.wrap({ + role: 'presentation', + cls: me.wrapCls, + id: me.el.id + '-rzwrap', + style: wrapTarget.getStyles('margin-top', 'margin-bottom') + }); + + + me.el.setPositioning(wrapTarget.getPositioning()); + wrapTarget.clearPositioning(); + me.el.setBox(wrapTarget.getBox()); + + + wrapTarget.setStyle('position', 'absolute'); + + me.isTargetWrapped = true; + } + + + + me.el.position(); + if (me.pinned) { + me.el.addCls(me.pinnedCls); + } + + + 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, + + + + 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 + }); + + + 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'; + } + } + + + eastWestStyle = Ext.isIE6 ? ' style="height:' + me.el.getHeight() + 'px"' : ''; + + for (; i < len; i++){ + + if (handles[i] && possibles[handles[i]]) { + pos = possibles[handles[i]]; + if (pos === 'east' || pos === 'west') { + style = eastWestStyle; + } else { + style = ''; + } + + handleEls.push( + '' + ); + } + } + Ext.DomHelper.append(me.el, handleEls.join('')); + + + handleEls.length = 0; + + + for (i = 0; i < len; i++){ + + 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; + + me.forceHandlesHeight(); + }, + + disable: function() { + this.resizeTracker.disable(); + }, + + enable: function() { + this.resizeTracker.enable(); + }, + + + onBeforeResize: function(tracker, e) { + var box = this.el.getBox(); + return this.fireEvent('beforeresize', this, box.width, box.height, e); + }, + + + onResize: function(tracker, e) { + var me = this, + box; + + me.forceHandlesHeight(); + if (me.hasListeners.resizeDrag) { + box = tracker.getResizeTarget().getBox(); + return me.fireEvent('resizedrag', me, box.width, box.height, e); + } + }, + + + onResizeEnd: function(tracker, e) { + var me = this, + box = me.el.getBox(); + + me.forceHandlesHeight(); + return me.fireEvent('resize', me, box.width, box.height, e); + }, + + + resizeTo : function(width, height) { + var me = this; + me.target.setSize(width, height); + me.fireEvent('resize', me, width, height, null); + }, + + + getEl : function() { + return this.el; + }, + + + getTarget: function() { + return this.target; + }, + + destroy: function() { + var me = this, + i, + handles = me.handles, + len = handles.length, + positions = me.possiblePositions, + handle; + + me.resizeTracker.destroy(); + + + if (me.isTargetWrapped) { + me.target.destroy(); + } + + for (i = 0; i < len; i++) { + if (handle = me[positions[handles[i]]]) { + handle.remove(); + } + } + }, + + + forceHandlesHeight : function() { + var me = this, + handle; + if (Ext.isIE6) { + handle = me.east; + if (handle) { + handle.setHeight(me.el.getHeight()); + } + handle = me.west; + if (handle) { + handle.setHeight(me.el.getHeight()); + } + me.el.repaint(); + } + } +}); + + + +Ext.define('Ext.selection.CellModel', { + extend: Ext.selection.Model , + alias: 'selection.cellmodel', + + + + + + + + + isCellModel: true, + + + enableKeyNav: true, + + + preventWrap: false, + + + noSelection: { + row: -1, + column: -1 + }, + + constructor: function() { + this.addEvents( + + 'deselect', + + + 'select' + ); + this.callParent(arguments); + }, + + bindComponent: function(view) { + var me = this, + grid = view.ownerCt; + me.primaryView = view; + me.views = me.views || []; + me.views.push(view); + me.bindStore(view.getStore(), true); + + view.on({ + cellclick: me.onCellClick, + refresh: me.onViewRefresh, + scope: me + }); + if (grid.optimizedColumnMove !== false) { + grid.on('columnmove', me.onColumnMove, me); + } + + if (me.enableKeyNav) { + me.initKeyNav(view); + } + }, + + initKeyNav: function(view) { + var me = this; + + if (!view.rendered) { + view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true}); + return; + } + + view.el.set({ + tabIndex: -1 + }); + + + + me.keyNav = new Ext.util.KeyNav({ + target: view.el, + ignoreInputFields: true, + up: me.onKeyUp, + down: me.onKeyDown, + right: me.onKeyRight, + left: me.onKeyLeft, + tab: me.onKeyTab, + scope: me + }); + }, + + getHeaderCt: function() { + var selection = this.getCurrentPosition(), + view = selection ? selection.view : this.primaryView; + + return view.headerCt; + }, + + onKeyUp: function(e) { + this.doMove('up', e); + }, + + onKeyDown: function(e) { + this.doMove('down', e); + }, + + onKeyLeft: function(e) { + this.doMove('left', e); + }, + + onKeyRight: function(e) { + this.doMove('right', e); + }, + + doMove: function(direction, e){ + this.keyNavigation = true; + this.move(direction, e); + this.keyNavigation = false; + }, + + selectWithEvent: function(record, e) { + this.select(record); + }, + + select: function(pos, keepExisting, suppressEvent) { + var me = this, + row, + oldPos = me.getCurrentPosition(), + 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(); + } + }, + + deselect: function(record, suppressEvent){ + this.selectByPosition(null, suppressEvent); + }, + + move: function(dir, e) { + var me = this, + pos = me.getCurrentPosition(), + newPos; + + if (pos) { + + newPos = pos.view.walkCells(pos, dir, e, me.preventWrap); + + if (newPos) { + return me.setCurrentPosition(newPos); + } + } + + return null; + }, + + + getCurrentPosition: function() { + + + return this.selecting ? this.nextSelection : this.selection; + }, + + + setCurrentPosition: function(pos, suppressEvent, preventCheck) { + var me = this, + last = me.selection; + + + me.lastSelection = last; + + + if (pos) { + pos = pos.isCellContext ? pos : new Ext.grid.CellContext(me.primaryView).setPosition(pos); + } + if (!preventCheck && last) { + + if (pos && (pos.record === last.record && pos.columnHeader === last.columnHeader && pos.view === last.view)) { + pos = null; + } else { + me.onCellDeselect(me.selection, suppressEvent); + } + } + + if (pos) { + me.nextSelection = pos; + + + me.selecting = true; + me.onCellSelect(me.nextSelection, suppressEvent); + me.selecting = false; + + return (me.selection = pos); + } + + return null; + }, + + isCellSelected: function(view, row, column) { + var me = this, + testPos, + pos = me.getCurrentPosition(); + + if (pos && pos.view === view) { + testPos = new Ext.grid.CellContext(view).setPosition({ + row: row, + column: column + }); + return (testPos.record === pos.record) && (testPos.columnHeader === pos.columnHeader); + } + }, + + + onStoreRemove: function(store, records, indexes) { + var me = this, + pos = me.getCurrentPosition(); + + me.callParent(arguments); + if (pos && store.getCount() && store.indexOf(pos.record) !== -1) { + me.setCurrentPosition({ + row: pos.record, + column: pos.columnHeader + }, true, true); + } else { + me.selection = null; + } + }, + + onStoreAdd: function() { + var me = this, + pos = me.getCurrentPosition(); + + me.callParent(arguments); + if (pos) { + me.setCurrentPosition({ + row: pos.record, + column: pos.columnHeader + }, true, true); + } else { + me.selection = null; + } + }, + + + onCellClick: function(view, cell, cellIndex, record, row, recordIndex, e) { + var newPos; + + + if (recordIndex !== -1) { + newPos = new Ext.grid.CellContext(view).setPosition({ + view: view, + row: row, + + column: view.ownerCt.getColumnManager().getHeaderAtIndex(cellIndex) + }); + this.setCurrentPosition(newPos); + } + }, + + + + onCellSelect: function(position, supressEvent) { + if (position && position.row !== undefined && position.row > -1) { + this.doSelect(position.record, false, supressEvent); + } + }, + + + + onCellDeselect: function(position, supressEvent) { + if (position && position.row !== 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.lastSelection || me.noSelection; + eventName = 'deselect'; + } + + + + + view = pos.view || me.primaryView; + + if ((suppressEvent || me.fireEvent('before' + eventName, me, record, pos.row, pos.column)) !== false && + commitFn() !== false) { + + if (isSelected) { + if (!me.preventFocus) { + view.focusCell(pos, true); + } + view.onCellSelect(pos); + } else { + view.onCellDeselect(pos); + delete me.selection; + } + + if (!suppressEvent) { + me.fireEvent(eventName, me, record, pos.row, pos.column); + } + } + }, + + + onKeyTab: function(e, t) { + var me = this, + pos = me.getCurrentPosition(), + editingPlugin; + + if (pos) { + editingPlugin = pos.view.editingPlugin; + + if (editingPlugin && me.wasEditing) { + me.onEditorTab(editingPlugin, e); + } else { + me.move(e.shiftKey ? 'left' : 'right', e); + } + } + }, + + onEditorTab: function(editingPlugin, e) { + var me = this, + direction = e.shiftKey ? 'left' : 'right', + pos = me.getCurrentPosition(), + position = pos.view.walkCells(pos, direction, e, me.preventWrap); + + + if (position) { + + if (editingPlugin.startEdit(position.record, position.columnHeader)) { + me.wasEditing = false; + } + + + + else { + me.setCurrentPosition(position); + me.wasEditing = true; + } + } + }, + + refresh: function() { + var pos = this.getCurrentPosition(), + selRowIdx; + + + if (pos && (selRowIdx = this.store.indexOf(this.selected.last())) !== -1) { + pos.row = selRowIdx; + } + }, + + + 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.getCurrentPosition(), + newPos, + headerCt = view.headerCt, + record, columnHeader; + + + + if (pos && pos.view === view) { + record = pos.record; + columnHeader = pos.columnHeader; + + + if (!columnHeader.isDescendantOf(headerCt)) { + + + + columnHeader = headerCt.queryById(columnHeader.id) || + headerCt.down('[text="' + columnHeader.text + '"]') || + headerCt.down('[dataIndex="' + columnHeader.dataIndex + '"]'); + } + + + + + + if (pos.record) { + if (columnHeader && (view.store.indexOfId(record.getId()) !== -1)) { + newPos = new Ext.grid.CellContext(view).setPosition({ + row: record, + column: columnHeader + }); + me.setCurrentPosition(newPos); + } + } else { + me.selection = null; + } + } + }, + + selectByPosition: function(position, suppressEvent) { + this.setCurrentPosition(position, suppressEvent); + } +}); + + + +Ext.define('Ext.tab.Tab', { + extend: Ext.button.Button , + alias: 'widget.tab', + + + + + + + isTab: true, + + baseCls: Ext.baseCSSPrefix + 'tab', + closeElOverCls: Ext.baseCSSPrefix + 'tab-close-btn-over', + + + activeCls: 'active', + + + + + closableCls: 'closable', + + + closable: true, + + + + closeText: 'Close Tab', + + + + active: false, + + + + childEls: [ + 'closeEl' + ], + + scale: false, + + position: 'top', + + ariaRole: 'tab', + + initComponent: function() { + var me = this; + + me.addEvents( + + 'activate', + + + 'deactivate', + + + 'beforeclose', + + + 'close' + ); + + me.callParent(arguments); + + if (me.card) { + me.setCard(me.card); + } + + me.overCls = ['over', me.position + '-over']; + }, + + getTemplateArgs: function() { + var me = this, + result = me.callParent(); + + result.closable = me.closable; + result.closeText = me.closeText; + + return result; + }, + + getFramingInfoCls: function(){ + return this.baseCls + '-' + this.ui + '-' + this.position; + }, + + beforeRender: function() { + var me = this, + tabBar = me.up('tabbar'), + tabPanel = me.up('tabpanel'); + + me.callParent(); + + me.addClsWithUI(me.position); + + if (me.active) { + me.addClsWithUI([me.activeCls, me.position + '-' + me.activeCls]); + } + + + + + me.syncClosableUI(); + + + 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.initKeyNav(); + }, + + initKeyNav: function() { + var me = this; + + me.keyNav = new Ext.util.KeyNav(me.el, { + enter: me.onEnterKey, + del: me.onDeleteKey, + scope: me + }); + }, + + setElOrientation: function() { + var position = this.position; + + if (position === 'left' || position === 'right') { + this.el.setVertical(position === 'right' ? 90 : 270); + } + }, + + + enable : function(silent) { + var me = this; + + me.callParent(arguments); + + me.removeClsWithUI(me.position + '-disabled'); + + return me; + }, + + + disable : function(silent) { + var me = this; + + me.callParent(arguments); + + me.addClsWithUI(me.position + '-disabled'); + + return me; + }, + + onDestroy: function() { + var me = this; + + Ext.destroy(me.keyNav); + delete me.keyNav; + + me.callParent(arguments); + }, + + + setClosable: function(closable) { + var me = this; + + + closable = (!arguments.length || !!closable); + + if (me.closable != closable) { + me.closable = closable; + + + if (me.card) { + me.card.closable = closable; + } + + me.syncClosableUI(); + + if (me.rendered) { + me.syncClosableElements(); + + + me.updateLayout(); + } + } + }, + + + syncClosableElements: function () { + var me = this, + closeEl = me.closeEl; + + if (me.closable) { + if (!closeEl) { + closeEl = me.closeEl = me.btnWrap.insertSibling({ + tag: 'a', + role: 'presentation', + cls: me.baseCls + '-close-btn', + href: '#', + title: me.closeText + }, 'after'); + } + closeEl.addClsOnOver(me.closeElOverCls); + } else if (closeEl) { + closeEl.remove(); + delete me.closeEl; + } + }, + + + syncClosableUI: function () { + var me = this, + classes = [me.closableCls, me.closableCls + '-' + me.position]; + + if (me.closable) { + me.addClsWithUI(classes); + } else { + me.removeClsWithUI(classes); + } + }, + + + setCard: function(card) { + var me = this; + + me.card = card; + me.setText(me.title || card.title); + me.setIconCls(me.iconCls || card.iconCls); + me.setIcon(me.icon || card.icon); + me.setGlyph(me.glyph || card.glyph); + }, + + + onCloseClick: function() { + var me = this; + + if (me.fireEvent('beforeclose', me) !== false) { + if (me.tabBar) { + if (me.tabBar.closeTab(me) === false) { + + return; + } + } else { + + me.fireClose(); + } + } + }, + + + fireClose: function(){ + this.fireEvent('close', this); + }, + + + onEnterKey: function(e) { + var me = this; + + if (me.tabBar) { + me.tabBar.onClick(e, me.el); + } + }, + + + onDeleteKey: function(e) { + if (this.closable) { + this.onCloseClick(); + } + }, + + + afterClick: function(isCloseClick) { + if (!isCloseClick) { + this.focus(); + } + }, + + + activate : function(supressEvent) { + var me = this; + + me.active = true; + me.addClsWithUI([me.activeCls, me.position + '-' + me.activeCls]); + + if (supressEvent !== true) { + me.fireEvent('activate', me); + } + }, + + + deactivate : function(supressEvent) { + var me = this; + + me.active = false; + me.removeClsWithUI([me.activeCls, me.position + '-' + me.activeCls]); + + if (supressEvent !== true) { + me.fireEvent('deactivate', me); + } + } +}); + + + +Ext.define('Ext.util.Point', { + + + extend: Ext.util.Region , + + statics: { + + + fromEvent: function(e) { + e = e.browserEvent || e; + e = (e.changedTouches && e.changedTouches.length > 0) ? e.changedTouches[0] : e; + return new this(e.pageX, e.pageY); + } + }, + + + + + constructor: function(x, y) { + this.callParent([y, x, y, x]); + }, + + + toString: function() { + return "Point[" + this.x + "," + this.y + "]"; + }, + + + equals: function(p) { + return (this.x == p.x && this.y == p.y); + }, + + + isWithin: function(p, threshold) { + if (!Ext.isObject(threshold)) { + threshold = { + x: threshold, + y: threshold + }; + } + + return (this.x <= p.x + threshold.x && this.x >= p.x - threshold.x && + this.y <= p.y + threshold.y && this.y >= p.y - threshold.y); + }, + + + isContainedBy: function(region) { + if (!(region instanceof Ext.util.Region)) { + region = Ext.get(region.el || region).getRegion(); + } + return region.contains(this); + }, + + + roundedEquals: function(p) { + return (Math.round(this.x) == Math.round(p.x) && Math.round(this.y) == Math.round(p.y)); + } +}, function() { + + this.prototype.translate = Ext.util.Region.prototype.translateBy; +}); + + + +Ext.define('Ext.tab.Bar', { + extend: Ext.panel.Header , + alias: 'widget.tabbar', + baseCls: Ext.baseCSSPrefix + 'tab-bar', + + + + + + + + isTabBar: true, + + + + + + + defaultType: 'tab', + + + plain: false, + + ariaRole: 'tablist', + + childEls: [ + 'body', 'strip' + ], + + + renderTpl: [ + '', + '' + ], + + + + + + _reverseDockNames: { + left: 'right', + right: 'left' + }, + + + initComponent: function() { + var me = this; + + if (me.plain) { + me.addCls(me.baseCls + '-plain'); + } + + me.addClsWithUI(me.orientation); + + me.addEvents( + + 'change' + ); + + + me.callParent(arguments); + Ext.merge(me.layout, me.initialConfig.layout); + + + me.layout.align = (me.orientation == 'vertical') ? 'left' : 'top'; + me.layout.overflowHandler = new Ext.layout.container.boxOverflow.Scroller(me.layout); + + me.remove(me.titleCmp); + delete me.titleCmp; + + Ext.apply(me.renderData, { + bodyCls: me.bodyCls, + dock: me.dock + }); + }, + + onRender: function() { + var me = this; + + me.callParent(); + + if (me.orientation === 'vertical' && (Ext.isIE8 || Ext.isIE9) && Ext.isStrict) { + me.el.on({ + mousemove: me.onMouseMove, + scope: me + }); + } + }, + + afterRender: function() { + var layout = this.layout; + + this.callParent(); + if (Ext.isIE9 && Ext.isStrict && this.orientation === 'vertical') { + + + layout.innerCt.on('scroll', function() { + layout.innerCt.dom.scrollLeft = 0; + }); + } + }, + + afterLayout: function() { + this.adjustTabPositions(); + this.callParent(arguments); + }, + + adjustTabPositions: function() { + var items = this.items.items, + i = items.length, + tab; + + + + + + + if (!Ext.isIE9m) { + if (this.dock === 'right') { + + + while (i--) { + tab = items[i]; + if (tab.isVisible()) { + tab.el.setStyle('left', tab.lastBox.width + 'px'); + } + } + } else if (this.dock === 'left') { + + + while (i--) { + tab = items[i]; + if (tab.isVisible()) { + tab.el.setStyle('left', -tab.lastBox.height + 'px'); + } + } + } + } + }, + + getLayout: function() { + var me = this; + me.layout.type = (me.orientation === 'horizontal') ? 'hbox' : 'vbox'; + return me.callParent(arguments); + }, + + + onAdd: function(tab) { + tab.position = this.dock; + this.callParent(arguments); + }, + + onRemove: function(tab) { + var me = this; + + if (tab === me.previousTab) { + me.previousTab = null; + } + me.callParent(arguments); + }, + + afterComponentLayout : function(width) { + var me = this, + needsScroll = me.needsScroll; + + me.callParent(arguments); + + if (needsScroll) { + me.layout.overflowHandler.scrollToItem(me.activeTab); + } + delete me.needsScroll; + }, + + + onClick: function(e, target) { + var me = this, + tabPanel = me.tabPanel, + tabEl, tab, isCloseClick, tabInfo; + + if (e.getTarget('.' + Ext.baseCSSPrefix + 'box-scroller')) { + return; + } + + if (me.orientation === 'vertical' && (Ext.isIE8 || Ext.isIE9) && Ext.isStrict) { + tabInfo = me.getTabInfoFromPoint(e.getXY()); + tab = tabInfo.tab; + isCloseClick = tabInfo.close; + } else { + + 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()) { + if (tab.closable && isCloseClick) { + tab.onCloseClick(); + } else { + if (tabPanel) { + + tabPanel.setActiveTab(tab.card); + } else { + me.setActiveTab(tab); + } + } + + tab.afterClick(isCloseClick); + } + }, + + + 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); + } + }, + + + + + + + + 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++) { + lastBox = tabs[i].lastBox; + 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)) { + tab = tabs[i]; + closeEl = tab.closeEl; + if (closeEl) { + + + + if (me._isTabReversed === undefined) { + me._isTabReversed = isTabReversed = + + + (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 + }; + }, + + + 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]; + }, + + + closeTab: function(toClose) { + var me = this, + card = toClose.card, + tabPanel = me.tabPanel, + toActivate; + + if (card && card.fireEvent('beforeclose', card) === false) { + return false; + } + + + + + toActivate = me.findNextActivatable(toClose); + + + + Ext.suspendLayouts(); + + if (tabPanel && card) { + + + delete toClose.ownerCt; + + + + card.fireEvent('close', card); + tabPanel.remove(card); + + + if (!tabPanel.getComponent(card)) { + + toClose.fireClose(); + me.remove(toClose); + } else { + + toClose.ownerCt = me; + Ext.resumeLayouts(true); + return false; + } + } + + + if (toActivate) { + + + if (tabPanel) { + tabPanel.setActiveTab(toActivate.card); + } else { + me.setActiveTab(toActivate); + } + toActivate.focus(); + } + Ext.resumeLayouts(true); + }, + + + + findNextActivatable: function(toClose) { + var me = this; + if (toClose.active && me.items.getCount() > 1) { + return (me.previousTab && me.previousTab !== toClose && !me.previousTab.disabled) ? me.previousTab : (toClose.next('tab[disabled=false]') || toClose.prev('tab[disabled=false]')); + } + }, + + + setActiveTab: function(tab, initial) { + var me = this; + + if (!tab.disabled && tab !== me.activeTab) { + if (me.activeTab) { + if (me.activeTab.isDestroyed) { + me.previousTab = null; + } else { + me.previousTab = me.activeTab; + me.activeTab.deactivate(); + } + } + tab.activate(); + + me.activeTab = tab; + me.needsScroll = true; + + + + if (!initial) { + me.fireEvent('change', me, tab, tab.card); + + me.updateLayout(); + } + } + } +}); + + +Ext.define('ExtThemeNeptune.tab.Tab', { + override: 'Ext.tab.Tab', + border: false +}); + + + +Ext.define('Ext.selection.CheckboxModel', { + alias: 'selection.checkboxmodel', + extend: Ext.selection.RowModel , + + + mode: 'MULTI', + + + injectCheckbox: 0, + + + checkOnly: false, + + + showHeaderCheckbox: undefined, + + + checkSelector: '.' + Ext.baseCSSPrefix + 'grid-row-checker', + + headerWidth: 24, + + + 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 (me.mode === 'SINGLE' && me.showHeaderCheckbox !== true) { + me.showHeaderCheckbox = false; + } + }, + + beforeViewRender: function(view) { + var me = this, + owner; + + me.callParent(arguments); + + + if (!me.hasLockedHeader() || view.headerCt.lockedCt) { + if (me.showHeaderCheckbox !== false) { + view.headerCt.on('headerclick', me.onHeaderClick, me); + } + me.addCheckbox(view, true); + owner = view.ownerCt; + + if (view.headerCt.lockedCt) { + owner = owner.ownerCt; + } + me.mon(owner, 'reconfigure', me.onReconfigure, me); + } + }, + + bindComponent: function(view) { + var me = this; + me.sortable = false; + me.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; + }, + + + addCheckbox: function(view, initial){ + var me = this, + checkbox = me.injectCheckbox, + headerCt = view.headerCt; + + + if (checkbox !== false) { + if (checkbox == 'first') { + checkbox = 0; + } else if (checkbox == 'last') { + checkbox = headerCt.getColumnCount(); + } + Ext.suspendLayouts(); + if (view.getStore().buffered) { + me.showHeaderCheckbox = false; + } + headerCt.add(checkbox, me.getHeaderConfig()); + Ext.resumeLayouts(); + } + + if (initial !== true) { + view.refresh(); + } + }, + + + onReconfigure: function(grid, store, columns) { + if(columns) { + this.addCheckbox(this.views[0]); + } + }, + + + 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); + } + } + }, + + + onHeaderClick: function(headerCt, header, e) { + if (header.isCheckerHd) { + e.stopEvent(); + var me = this, + isChecked = header.el.hasCls(Ext.baseCSSPrefix + 'grid-hd-checker-on'); + + + me.preventFocus = true; + if (isChecked) { + me.deselectAll(); + } else { + me.selectAll(); + } + delete me.preventFocus; + } + }, + + + getHeaderConfig: function() { + var me = this, + showCheck = me.showHeaderCheckbox !== false; + + return { + 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 ' : '', + renderer: Ext.Function.bind(me.renderer, me), + editRenderer: me.editRenderer || me.renderEmpty, + locked: me.hasLockedHeader() + }; + }, + + renderEmpty: function() { + return ' '; + }, + + + refresh: function() { + this.callParent(arguments); + this.updateHeaderState(); + }, + + + renderer: function(value, metaData, record, rowIndex, colIndex, store, view) { + return ''; + }, + + processSelection: function(view, record, item, index, e){ + var me = this, + checker = e.getTarget(me.checkSelector), + mode; + + + if (me.checkOnly && !checker) { + return; + } + + if (checker) { + mode = me.getSelectionMode(); + + + if (mode !== 'SINGLE') { + me.setSelectionMode('SIMPLE'); + } + me.selectWithEvent(record, e); + me.setSelectionMode(mode); + } else { + me.selectWithEvent(record, e); + } + }, + + + onSelectChange: function() { + this.callParent(arguments); + if (!this.suspendChange) { + this.updateHeaderState(); + } + }, + + + 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(); + } + }, + + + updateHeaderState: function() { + + var me = this, + store = me.store, + storeCount = store.getCount(), + views = me.views, + hdSelectStatus = false, + selectedCount = 0, + selected, len, i; + + if (!store.buffered && storeCount > 0) { + selected = me.selected; + hdSelectStatus = true; + for (i = 0, len = selected.getCount(); i < len; ++i) { + if (!me.getStoreRecord(selected.getAt(i))) { + break; + } + ++selectedCount; + } + hdSelectStatus = storeCount === selectedCount; + } + + if (views && views.length) { + me.toggleUiHeader(hdSelectStatus); + } + } +}); + + + +Ext.define('Ext.state.CookieProvider', { + extend: Ext.state.Provider , + + + + + + + + + + + constructor : function(config){ + var me = this; + me.path = "/"; + me.expires = new Date(Ext.Date.now() + (1000*60*60*24*7)); + me.domain = null; + me.secure = false; + me.callParent(arguments); + me.state = me.readCookies(); + }, + + + set : function(name, value){ + var me = this; + + if(typeof value == "undefined" || value === null){ + me.clear(name); + return; + } + me.setCookie(name, value); + me.callParent(arguments); + }, + + + clear : function(name){ + this.clearCookie(name); + this.callParent(arguments); + }, + + + 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; + }, + + + 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 == true) ? "; secure" : ""); + }, + + + 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 == true) ? "; secure" : ""); + } +}); + + + +Ext.define('Ext.util.LocalStorage', { + + id: null, + + + destroyed: false, + + + lazyKeys: true, + + + prefix: '', + + + session: false, + + + _keys: null, + + + _store: null, + + + _users: 0, + + statics: { + cache: {}, + + + get: function (id) { + var me = this, + cache = me.cache, + config = { + _users: 1 + }, + instance; + + if (Ext.isString(id)) { + config.id = id; + } else { + Ext.apply(config, id); + } + + if (!(instance = cache[config.id])) { + instance = new me(config); + } else { + if (instance === true) { + Ext.Error.raise('Creating a shared instance of private local store "' + + me.id + '".'); + } + + ++instance._users; + } + + return instance; + }, + + + supported: true + }, + + constructor: function (config) { + var me = this; + + Ext.apply(me, config); + + if (!me.hasOwnProperty('id')) { + Ext.Error.raise("No id was provided to the local store."); + } + + if (me._users) { + + + + Ext.util.LocalStorage.cache[me.id] = me; + } + else { + + + + if (Ext.util.LocalStorage.cache[me.id]) { + Ext.Error.raise('Cannot create duplicate instance of local store "' + + me.id + '". Use Ext.util.LocalStorage.get() to share instances.'); + } + + + + Ext.util.LocalStorage.cache[me.id] = true; + } + + me.init(); + }, + + + init: function () { + var me = this, + id = me.id; + + if (!me.prefix && id) { + me.prefix = id + '-'; + } + me._store = (me.session ? window.sessionStorage : window.localStorage); + }, + + + destroy: function () { + var me = this; + + if (me._users) { + Ext.log.warn('LocalStorage(id=' + me.id + ') destroyed while in use'); + } + + delete Ext.util.LocalStorage.cache[me.id]; + me._store = me._keys = null; + me.destroyed = true; + me.destroy = Ext.emptyFn; + }, + + + getKeys: function () { + var me = this, + store = me._store, + prefix = me.prefix, + keys = me._keys, + n = prefix.length, + i, key; + + if (!keys) { + me._keys = keys = []; + + for (i = store.length; i--; ) { + key = store.key(i); + if (key.length > n) { + if (prefix === key.substring(0, n)) { + keys.push(key.substring(n)); + } + } + } + } + + return keys; + }, + + + release: function () { + if (! --this._users) { + this.destroy(); + } + }, + + save: Ext.emptyFn, + + + clear: function () { + var me = this, + store = me._store, + prefix = me.prefix, + keys = me._keys || me.getKeys(), + i; + + for (i = keys.length; i--; ) { + store.removeItem(prefix + keys[i]); + } + + keys.length = 0; + }, + + + key: function (index) { + var keys = this._keys || this.getKeys(); + + return (0 <= index && index < keys.length) ? keys[index] : null; + }, + + + getItem: function (key) { + var k = this.prefix + key; + + return this._store.getItem(k); + }, + + + removeItem: function (key) { + var me = this, + k = me.prefix + key, + store = me._store, + keys = me._keys, + length = store.length; + + store.removeItem(k); + + if (keys && length !== store.length) { + if (me.lazyKeys) { + me._keys = null; + } else { + Ext.Array.remove(keys, key); + } + } + }, + + + setItem: function (key, value) { + var me = this, + k = me.prefix + key, + store = me._store, + length = store.length, + keys = me._keys; + + store.setItem(k, value); + + if (keys && length !== store.length) { + + keys.push(key); + } + } +}, function () { + var LocalStorage = this; + + if ('localStorage' in window) { + return; + } + if (!Ext.isIE) { + LocalStorage.supported = false; + LocalStorage.prototype.init = function () { + Ext.Error.raise("Local storage is not supported on this browser"); + }; + return; + } + + + + + + LocalStorage.override({ + + data: null, + + + + + flushDelay: 1, + + init: function () { + var me = this, + data = me.data, + el; + + me.el = el = document.createElement('div'); + + el.id = (me.id || (me.id = 'extjs-localstore')); + el.addBehavior('#default#userdata'); + + + Ext.getHead().dom.appendChild(el); + + el.load(me.id); + data = el.getAttribute('xdata'); + + me.data = data = (data ? Ext.decode(data) : {}); + + me._flushFn = function () { + me._timer = null; + me.save(0); + }; + }, + + destroy: function () { + var me = this, + el = me.el; + + if (el) { + + if (me._timer) { + me.save(); + } + + el.parentNode.removeChild(el); + me.data = me.el = null; + + me.callParent(); + } + }, + + getKeys: function () { + var me = this, + keys = me._keys; + + if (!keys) { + me._keys = keys = Ext.Object.getKeys(me.data); + } + + return keys; + }, + + + save: function (delay) { + var me = this; + + if (!delay) { + if (me._timer) { + clearTimeout(me._timer); + me._timer = null; + } + + me.el.setAttribute('xdata', Ext.encode(me.data)); + me.el.save(me.id); + } else if (!me._timer) { + me._timer = setTimeout(me._flushFn, delay); + } + }, + + clear: function () { + var me = this; + + me.data = {}; + me._keys = null; + me.save(me.flushDelay); + }, + + getItem: function (key) { + var data = this.data; + + return (key in data) ? data[key] : null; + }, + + removeItem: function (key) { + var me = this, + keys = me._keys, + data = me.data; + + if (key in data) { + delete data[key]; + + if (keys) { + if (me.lazyKeys) { + me._keys = null; + } else { + Ext.Array.remove(keys, key); + } + } + + me.save(me.flushDelay); + } + }, + + setItem: function (key, value) { + var me = this, + data = me.data, + keys = me._keys; + + if (keys && !(key in data)) { + keys.push(key); + } + + data[key] = value; + me.save(me.flushDelay); + } + }); +}); + + + +Ext.define('Ext.state.LocalStorageProvider', { + extend: Ext.state.Provider , + + + + + 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) { + me.store.setItem(name, me.encodeValue(value)); + me.callParent(arguments); + } + }, + + + 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 + }); + } +}); + + + +Ext.define('Ext.tab.Panel', { + extend: Ext.panel.Panel , + alias: 'widget.tabpanel', + alternateClassName: ['Ext.TabPanel'], + + + + + tabPosition : 'top', + + + + + + + + + + + removePanelHeader: true, + + + plain: false, + + + itemCls: Ext.baseCSSPrefix + 'tabpanel-child', + + + minTabWidth: undefined, + + + maxTabWidth: undefined, + + + deferredRender : true, + + + initComponent: function() { + var me = this, + dockedItems = [].concat(me.dockedItems || []), + activeTab = me.activeTab || (me.activeTab = 0), + tabPosition = me.tabPosition; + + + me.layout = new Ext.layout.container.Card(Ext.apply({ + owner: me, + deferredRender: me.deferredRender, + itemCls: me.itemCls, + activeItem: activeTab + }, me.layout)); + + + me.tabBar = new Ext.tab.Bar(Ext.apply({ + ui: me.ui, + dock: me.tabPosition, + orientation: (tabPosition == 'top' || tabPosition == 'bottom') ? 'horizontal' : 'vertical', + plain: me.plain, + cardLayout: me.layout, + tabPanel: me + }, me.tabBar)); + + dockedItems.push(me.tabBar); + me.dockedItems = dockedItems; + + me.addEvents( + + 'beforetabchange', + + + 'tabchange' + ); + + me.callParent(arguments); + + + activeTab = me.activeTab = me.getComponent(activeTab); + + + if (activeTab) { + me.tabBar.setActiveTab(activeTab.tab, true); + } + }, + + + setActiveTab: function(card) { + var me = this, + previous; + + card = me.getComponent(card); + if (card) { + previous = me.getActiveTab(); + + if (previous === card || me.fireEvent('beforetabchange', me, card, previous) === false) { + return false; + } + + + + if (!card.isComponent) { + Ext.suspendLayouts(); + card = me.add(card); + Ext.resumeLayouts(); + } + + + + me.activeTab = card; + + + + + Ext.suspendLayouts(); + me.layout.setActiveItem(card); + + + card = me.activeTab = me.layout.getActiveItem(); + + + if (card && card !== previous) { + + + me.tabBar.setActiveTab(card.tab); + Ext.resumeLayouts(true); + + + if (previous !== card) { + me.fireEvent('tabchange', me, card, previous); + } + } + + else { + Ext.resumeLayouts(true); + } + return card; + } + }, + + + getActiveTab: function() { + var me = this, + + result = me.getComponent(me.activeTab); + + + if (result && me.items.indexOf(result) != -1) { + me.activeTab = result; + } else { + me.activeTab = null; + } + + return me.activeTab; + }, + + + getTabBar: function() { + return this.tabBar; + }, + + + onAdd: function(item, index) { + var me = this, + cfg = item.tabConfig || {}, + defaultConfig = { + xtype: 'tab', + ui: me.tabBar.ui, + card: item, + disabled: item.disabled, + closable: item.closable, + hidden: item.hidden && !item.hiddenByLayout, + tooltip: item.tooltip, + tabBar: me.tabBar, + position: me.tabPosition + }; + + if (item.closeText !== undefined) { + defaultConfig.closeText = item.closeText; + } + + cfg = Ext.applyIf(cfg, defaultConfig); + + + item.tab = me.tabBar.insert(index, cfg); + + item.on({ + scope : me, + enable: me.onItemEnable, + disable: me.onItemDisable, + beforeshow: me.onItemBeforeShow, + iconchange: me.onItemIconChange, + iconclschange: me.onItemIconClsChange, + 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); + } + } + }, + + + onItemEnable: function(item){ + item.tab.enable(); + }, + + + onItemDisable: function(item){ + item.tab.disable(); + }, + + + onItemBeforeShow: function(item) { + if (item !== this.activeTab) { + this.setActiveTab(item); + return false; + } + }, + + + onItemIconChange: function(item, newIcon) { + item.tab.setIcon(newIcon); + }, + + + onItemIconClsChange: function(item, newIconCls) { + item.tab.setIconCls(newIconCls); + }, + + + onItemTitleChange: function(item, newTitle) { + item.tab.setText(newTitle); + }, + + + doRemove: function(item, autoDestroy) { + var me = this, + toActivate; + + + if (me.destroying || me.items.getCount() == 1) { + me.activeTab = null; + } + + + + else if ((toActivate = me.tabBar.items.indexOf(me.tabBar.findNextActivatable(item.tab))) !== -1) { + me.setActiveTab(toActivate); + } + this.callParent(arguments); + + + delete item.tab.card; + delete item.tab; + }, + + + onRemove: function(item, destroying) { + var me = this; + + item.un({ + scope : me, + enable: me.onItemEnable, + disable: me.onItemDisable, + beforeshow: me.onItemBeforeShow + }); + if (!me.destroying && item.tab.ownerCt === me.tabBar) { + me.tabBar.remove(item.tab); + } + } +}); + + + +Ext.define('Ext.toolbar.Spacer', { + extend: Ext.Component , + alias: 'widget.tbspacer', + alternateClassName: 'Ext.Toolbar.Spacer', + baseCls: Ext.baseCSSPrefix + 'toolbar-spacer', + focusable: false, + + ariaRole: 'presentation' +}); + + + +Ext.define('Ext.view.DragZone', { + extend: Ext.dd.DragZone , + containerScroll: false, + + constructor: function(config) { + var me = this, + view, + ownerCt, + el; + + Ext.apply(me, config); + + + + + + + if (!me.ddGroup) { + me.ddGroup = 'view-dd-zone-' + me.view.id; + } + + + + + + + + + view = me.view; + ownerCt = view.ownerCt; + + + if (ownerCt) { + el = ownerCt.getTargetEl().dom; + } else { + el = view.el.dom.parentNode; + } + me.callParent([el]); + + me.ddel = Ext.get(document.createElement('div')); + me.ddel.addCls(Ext.baseCSSPrefix + 'grid-dd-wrap'); + }, + + init: function(id, sGroup, config) { + var me = this; + + me.initTarget(id, sGroup, config); + me.view.on('itemmousedown', me.onItemMouseDown, me); + }, + + onValidDrop: function(target, e, id) { + this.callParent(); + + target.el.focus(); + }, + + onItemMouseDown: function(view, record, item, index, e) { + if (!this.isPreventDrag(e, record, item, index)) { + + + + if (view.focusRow) { + view.focusRow(record); + } + this.handleMouseDown(e); + } + }, + + + 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: new Ext.EventObjectImpl(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); + + + + if (!selectionModel.isSelected(record)) { + selectionModel.selectWithEvent(record, me.DDMInstance.mousedownEvent); + } + data.records = selectionModel.getSelection(); + + me.ddel.update(me.getDragText()); + me.proxy.update(me.ddel.dom); + 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; + } +}); + + + +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'; + + if (count === 1 && text) { + return text; + } else if (!text) { + suffix = ''; + } + return Ext.String.format(this.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) { + + for (r = 0; r < rLen; r++) { + + + item = view.getNode(records[r]); + + + + 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; + } +}); + + + +Ext.define('Ext.tree.ViewDropZone', { + extend: Ext.view.DropZone , + + + allowParentInserts: false, + + + allowContainerDrops: false, + + + appendOnly: false, + + + expandDelay : 500, + + indicatorCls: Ext.baseCSSPrefix + 'tree-ddindicator', + + + expandNode : function(node) { + var view = this.view; + this.expandProcId = false; + if (!node.isLeaf() && !node.isExpanded()) { + view.expand(node); + this.expandProcId = false; + } + }, + + + queueExpand : function(node) { + this.expandProcId = Ext.Function.defer(this.expandNode, this.expandDelay, this, [node]); + }, + + + 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.getPageY(), + noAppend = record.isLeaf(), + noBelow = false, + region = Ext.fly(node).getRegion(), + fragment; + + + if (record.isRoot()) { + return 'append'; + } + + + 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; + + + if (!(targetNode && position && dataLength)) { + return false; + } + + + for (i = 0; i < ln; i++) { + record = draggedRecords[i]; + if (record.isNode && record.contains(targetNode)) { + return false; + } + } + + + if (position === 'append' && targetNode.get('allowDrop') === false) { + return false; + } + else if (position != 'append' && targetNode.parentNode.get('allowDrop') === false) { + return false; + } + + + 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; + + + 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 (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'; + + indicator.hide(); + } + } else { + this.valid = false; + } + + this.currentCls = returnCls; + return returnCls; + }, + + + onNodeOut : function(n, dd, e, data){ + this.valid = false; + this.getIndicator().hide(); + }, + + onContainerOver : function(dd, e, data) { + return e.getTarget('.' + this.indicatorCls) ? this.currentCls : this.dropNotAllowed; + }, + + 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.getStore().treeStore.model, + records, i, len, record, + insertionMethod, argList, + needTargetExpand, + transferData; + + + 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(undefined, true)); + } else { + + data.records.push(new Model(record.data, record.getId())); + } + } + } + + + me.cancelExpand(); + + + + + + 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]; + } + + + transferData = function() { + var color, + n; + + + Ext.suspendLayouts(); + + + 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 (me.sortOnDrop) { + targetNode.sort(targetNode.getOwnerTree().store.generateComparator()); + } + + Ext.resumeLayouts(true); + + + + + 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 (needTargetExpand) { + targetNode.expand(false, transferData); + } + + + + + + else if (targetNode.isLoading()) { + targetNode.on({ + expand: transferData, + delay: 1, + single: true + }); + } + + else { + transferData(); + } + } +}); + + + +Ext.define('Ext.tree.plugin.TreeViewDragDrop', { + extend: Ext.AbstractPlugin , + alias: 'plugin.treeviewdragdrop', + + + + + + + + + + + + + dragText : '{0} selected node{1}', + + + + allowParentInserts: false, + + + allowContainerDrops: false, + + + appendOnly: false, + + + ddGroup : "TreeDD", + + + containerScroll: false, + + + + + + + + + expandDelay : 1000, + + + enableDrop: true, + + + enableDrag: true, + + + nodeHighlightColor: 'c3daf9', + + + nodeHighlightOnDrop: Ext.enableFx, + + + + + displayField: 'text', + + + + + + + + + + init : function(view) { + view.on('render', this.onViewRender, this, {single: true}); + }, + + + destroy: function() { + Ext.destroy(this.dragZone, this.dropZone); + }, + + onViewRender : function(view) { + var me = this, + scrollEl; + + 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; +}); + + + +Ext.define('Ext.util.Cookies', { + singleton: true, + + + 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" : ""); + }, + + + get : function(name) { + var parts = document.cookie.split('; '), + len = parts.length, + item, i, ret; + + + + + + + for (i = 0; i < len; ++i) { + item = parts[i].split('='); + if (item[0] === name) { + ret = item[1]; + return ret ? unescape(ret) : ''; + } + } + return null; + }, + + + clear : function(name, path){ + if (this.get(name)) { + path = path || '/'; + document.cookie = name + '=' + '; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=' + path; + } + }, + + + getCookieVal : function(offset){ + var cookie = document.cookie, + endstr = cookie.indexOf(";", offset); + + if (endstr == -1) { + endstr = cookie.length; + } + return unescape(cookie.substring(offset, endstr)); + } +}); + + + + +Ext.define('Ext.util.Grouper', { + + + + extend: Ext.util.Sorter , + + + + isGrouper: true, + + + getGroupString: function(instance) { + return instance.get(this.property); + } +}); + + + +Ext.define('Ext.util.History', { + singleton: true, + alternateClassName: 'Ext.History', + mixins: { + observable: Ext.util.Observable + }, + + + useTopWindow: true, + + + iframeId: Ext.baseCSSPrefix + 'history-frame', + + constructor: function() { + var me = this, + hash, + newHash; + + + me.oldIEMode = Ext.isIE7m || Ext.isIEQuirks && (Ext.isIE8 || Ext.isIE9); + me.iframe = null; + me.hiddenField = null; + me.ready = false; + me.currentToken = null; + me.mixins.observable.constructor.call(me); + me.onHashChange = function () { + newHash = me.getHash(); + if (newHash !== hash) { + hash = newHash; + me.handleStateChange(hash); + } + }; + }, + + getHash: function() { + return this.win.location.hash.substr(1); + }, + + setHash: function (hash) { + try { + this.win.location.hash = hash; + } catch (e) { + + } + }, + + handleStateChange: function(token) { + this.currentToken = token; + this.fireEvent('change', token); + }, + + updateIFrame: function(hash) { + var iframe = this.iframe, + doc = iframe && iframe.contentWindow && iframe.contentWindow.document; + + doc.open().close(); + doc.location.hash = hash; + return true; + }, + + checkIFrame: function () { + var me = this, + contentWindow = me.iframe.contentWindow, + oldToken, oldHash; + + if (!contentWindow || !contentWindow.document) { + Ext.Function.defer(me.checkIFrame, 10, me); + return; + } + + oldToken = contentWindow.location.hash.substr(1); + oldHash = me.getHash(); + + Ext.TaskManager.start({ + run: function () { + var newToken = contentWindow.location.hash.substr(1), + newHash = me.getHash(); + + if (newToken !== oldToken) { + oldToken = newToken; + me.handleStateChange(newToken); + me.setHash(newToken); + oldHash = newToken; + } else if (newHash !== oldHash) { + oldHash = newHash; + me.updateIFrame(newHash); + } + }, + interval: 50, + scope: me + }); + me.ready = true; + me.fireEvent('ready', me); + }, + + startUp: function () { + var me = this; + + me.currentToken = me.getHash(); + + if (me.oldIEMode) { + me.checkIFrame(); + } else { + if (Ext.supports.Hashchange) { + Ext.EventManager.on(me.win, 'hashchange', me.onHashChange); + } + else { + Ext.TaskManager.start({ + run: me.onHashChange, + interval: 50 + }); + } + me.ready = true; + me.fireEvent('ready', me); + } + }, + + + init: function (onReady, scope) { + var me = this; + + if (me.ready) { + Ext.callback(onReady, scope, [me]); + return; + } + + if (!Ext.isReady) { + Ext.onReady(function() { + me.init(onReady, scope); + }); + return; + } + + me.win = me.useTopWindow ? window.top : window; + if (me.oldIEMode) { + me.iframe = Ext.getDom(me.iframeId); + if (!me.iframe) { + me.iframe = Ext.DomHelper.append(document.body, { + tag: 'iframe', + role: 'presentation', + id: me.iframeId, + style: 'display:none;', + src: Ext.SSL_SECURE_URL + }); + } + } + + me.addEvents( + + 'ready', + + 'change' + ); + + if (onReady) { + me.on('ready', onReady, scope, {single: true}); + } + me.startUp(); + }, + + + add: function (token, preventDuplicates) { + var me = this; + + if (preventDuplicates !== false) { + if (me.getToken() === token) { + return true; + } + } + + if (me.oldIEMode) { + return me.updateIFrame(token); + } else { + me.setHash(token); + return true; + } + }, + + + back: function() { + var win = this.useTopWindow ? window.top : window; + win.history.go(-1); + }, + + + forward: function(){ + var win = this.useTopWindow ? window.top : window; + win.history.go(1); + }, + + + getToken: function() { + return this.ready ? this.currentToken : this.getHash(); + } +}); + + +Ext.define('baseapp.Application', { + +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/baseapp-prod.js b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/baseapp-prod.js new file mode 100644 index 0000000000000000000000000000000000000000..ccd31ab208794f5bdf38e3de105a8e0c72204176 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/baseapp-prod.js @@ -0,0 +1 @@ +var Ext=Ext||{};if(!Ext.Direct){Ext.Direct={}}if(!Ext.Toolbar){Ext.Toolbar={}}if(!Ext.app){Ext.app={}}if(!Ext.app.domain){Ext.app.domain={}}if(!Ext.button){Ext.button={}}if(!Ext.chart){Ext.chart={}}if(!Ext.chart.axis){Ext.chart.axis={}}if(!Ext.chart.series){Ext.chart.series={}}if(!Ext.chart.theme){Ext.chart.theme={}}if(!Ext.container){Ext.container={}}if(!Ext.core){Ext.core={}}if(!Ext.data){Ext.data={}}if(!Ext.data.association){Ext.data.association={}}if(!Ext.data.flash){Ext.data.flash={}}if(!Ext.data.proxy){Ext.data.proxy={}}if(!Ext.data.reader){Ext.data.reader={}}if(!Ext.data.writer){Ext.data.writer={}}if(!Ext.dd){Ext.dd={}}if(!Ext.direct){Ext.direct={}}if(!Ext.dom){Ext.dom={}}if(!Ext.draw){Ext.draw={}}if(!Ext.draw.engine){Ext.draw.engine={}}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.fx){Ext.fx={}}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.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.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.panel){Ext.panel={}}if(!Ext.perf){Ext.perf={}}if(!Ext.picker){Ext.picker={}}if(!Ext.resizer){Ext.resizer={}}if(!Ext.selection){Ext.selection={}}if(!Ext.slider){Ext.slider={}}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.ux){Ext.ux={}}if(!Ext.ux.data){Ext.ux.data={}}if(!Ext.ux.form){Ext.ux.form={}}if(!Ext.view){Ext.view={}}if(!Ext.window){Ext.window={}}var ExtThemeNeptune=ExtThemeNeptune||{};if(!ExtThemeNeptune.container){ExtThemeNeptune.container={}}if(!ExtThemeNeptune.form){ExtThemeNeptune.form={}}if(!ExtThemeNeptune.form.field){ExtThemeNeptune.form.field={}}if(!ExtThemeNeptune.grid){ExtThemeNeptune.grid={}}if(!ExtThemeNeptune.grid.column){ExtThemeNeptune.grid.column={}}if(!ExtThemeNeptune.layout){ExtThemeNeptune.layout={}}if(!ExtThemeNeptune.layout.component){ExtThemeNeptune.layout.component={}}if(!ExtThemeNeptune.menu){ExtThemeNeptune.menu={}}if(!ExtThemeNeptune.panel){ExtThemeNeptune.panel={}}if(!ExtThemeNeptune.picker){ExtThemeNeptune.picker={}}if(!ExtThemeNeptune.resizer){ExtThemeNeptune.resizer={}}if(!ExtThemeNeptune.tab){ExtThemeNeptune.tab={}}if(!ExtThemeNeptune.toolbar){ExtThemeNeptune.toolbar={}}var baseapp=baseapp||{};(function(j){var l=[],m=["constructor","toString","valueOf","toLocaleString"],k={},p={},b=0,h,c,o,g,a=function(){var r,q;c=Ext.Base;o=Ext.ClassManager;for(r=m.length;r-->0;){q=(1<0;){r=l[s];q[r]=c[r]}return q},d=function(v,y,R,q,x,F,w,O,t,H,B){var r=function A(){return this.constructor.apply(this,arguments)||null},Q=r,s={enumerableMembers:q&b,onCreated:B,onBeforeCreated:e,aliases:O},E=R.alternateClassName||[],M=Ext.global,I,L,N,D,K,U,T,u,J,z,P,G,C,S;for(N=l.length;N-->0;){T=l[N];r[T]=c[T]}if(R.$isFunction){R=R(r)}s.data=R;z=R.statics,R.$className=v;if("$className" in R){r.$className=R.$className}r.extend(y);J=r.prototype;r.xtype=R.xtype=x[0];if(x){J.xtypes=x}J.xtypesChain=F;J.xtypesMap=w;R.alias=O;Q.triggerExtended(r,R,s);if(R.onClassExtended){r.onExtended(R.onClassExtended,r);delete R.onClassExtended}if(z){for(P in z){if(z.hasOwnProperty(P)){S=z[P];if(S&&S.$isFunction&&!S.$isClass&&S!==Ext.emptyFn&&S!==Ext.identityFn){r[P]=C=S;C.$owner=r;C.$name=P}r[P]=S}}}delete R.statics;if(R.inheritableStatics){r.addInheritableStatics(R.inheritableStatics)}if(J.onClassExtended){Q.onExtended(J.onClassExtended,Q);delete J.onClassExtended}if(R.config){g(r,R)}s.onBeforeCreated(r,s.data,s);for(N=0,K=t&&t.length;N0){r--;p[r]="var Ext=window."+Ext.name+";"+p[r]}}n=p.join("");q=o[n];if(!q){q=Function.prototype.constructor.apply(Function.prototype,p);o[n]=q}return q},functionFactory:function(){var p=this,n=Array.prototype.slice.call(arguments),o;if(Ext.isSandboxed){o=n.length;if(o>0){o--;n[o]="var Ext=window."+Ext.name+";"+n[o]}}return Function.prototype.constructor.apply(Function.prototype,n)},Logger:{verbose:g,log:g,info:g,warn:g,error:function(n){throw new Error(n)},deprecate:g}});Ext.type=Ext.typeOf;h=Ext.app;if(!h){h=Ext.app={}}Ext.apply(h,{namespaces:{},collectNamespaces:function(p){var n=Ext.app.namespaces,o;for(o in p){if(p.hasOwnProperty(o)){n[o]=true}}},addNamespaces:function(p){var q=Ext.app.namespaces,o,n;if(!Ext.isArray(p)){p=[p]}for(o=0,n=p.length;on.length&&(p+"."===o.substring(0,p.length+1))){n=p}}return n===""?undefined:n}})}());Ext.globalEval=Ext.global.execScript?function(a){execScript(a)}:function($$code){(function(){var Ext=this.Ext;eval($$code)}())};(function(){var b="4.2.3.1477",e=[""],j=/([^\d\.])/,c=/[^\d]/g,a=/[\-+]/g,h=/\s/g,d=/_/g,g;Ext.Version=g=Ext.extend(Object,{isVersion:true,padModes:{"~":NaN,"^":Infinity},release:"",constructor:function(s,o){var t=this,m=t.padModes,k,q,n,p,u,l,r;if(s.isVersion){return s}t.version=r=String(s).toLowerCase().replace(d,".").replace(a,"");k=r.charAt(0);if(k in m){r=r.substring(1);n=m[k]}else{n=o?m[o]:0}t.pad=n;l=r.search(j);t.shortVersion=r;if(l!==-1){t.release=u=r.substr(l,s.length);t.shortVersion=r.substr(0,l);u=g.releaseValueMap[u]||u}t.releaseValue=u||n;t.shortVersion=t.shortVersion.replace(c,"");t.parts=p=r.split(".");for(q=p.length;q--;){p[q]=parseInt(p[q],10)}if(n===Infinity){p.push(n)}t.major=p[0]||n;t.minor=p[1]||n;t.patch=p[2]||n;t.build=p[3]||n;return t},compareTo:function(u){var v=this,o=v.pad,s=v.parts,w=s.length,n=u.isVersion?u:new g(u),l=n.pad,r=n.parts,q=r.length,k=Math.max(w,q),p,m,t;for(p=0;pt){return 1}}m=v.releaseValue;t=n.releaseValue;if(mt){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(k){return this.compareTo(k)>0},isGreaterThanOrEqual:function(k){return this.compareTo(k)>=0},isLessThan:function(k){return this.compareTo(k)<0},isLessThanOrEqual:function(k){return this.compareTo(k)<=0},equals:function(k){return this.compareTo(k)===0},match:function(k){k=String(k);return this.version.substr(0,k.length)===k},toArray:function(){var k=this;return[k.getMajor(),k.getMinor(),k.getPatch(),k.getBuild(),k.getRelease()]},getShortVersion:function(){return this.shortVersion},gt:function(k){return this.compareTo(k)>0},lt:function(k){return this.compareTo(k)<0},gtEq:function(k){return this.compareTo(k)>=0},ltEq:function(k){return this.compareTo(k)<=0}});Ext.apply(g,{releaseValueMap:{dev:-6,alpha:-5,a:-5,beta:-4,b:-4,rc:-3,"#":-2,p:-1,pl:-1},getComponentValue:function(k){return !k?0:(isNaN(k)?this.releaseValueMap[k]||k:parseInt(k,10))},compare:function(m,l){var k=m.isVersion?m:new g(m);return k.compareTo(l)}});Ext.apply(Ext,{versions:{},lastRegisteredVersion:null,setVersion:function(l,k){Ext.lastRegisteredVersion=Ext.versions[l]=new g(k);return this},getVersion:function(k){if(k===undefined){return Ext.lastRegisteredVersion}return Ext.versions[k]},checkVersion:function(o,w){var s=Ext.isArray(o),x=s?o:e,k=x.length,l=Ext.versions,v=l.ext||l.touch,p,u,r,m,n,y,q,t;if(!s){e[0]=o}for(p=0;p=0){y=y.replace(h,"")}u=y.indexOf("@");if(u<0){q=y;t=v}else{if(!(t=l[y.substring(0,u)])){if(w){return false}continue}q=y.substring(u+1)}u=q.indexOf("-");if(u<0){if(q.charAt(u=q.length-1)==="+"){m=q.substring(0,u);n=null}else{m=n=q}}else{if(u>0){m=q.substring(0,u);n=q.substring(u+1)}else{m=null;n=q.substring(u+1)}}r=true;if(m){m=new g(m,"~");r=m.ltEq(t)}if(r&&n){n=new g(n,"~");r=n.gtEq(t)}}if(r){if(!w){return true}}else{if(w){return false}}}return !!w},deprecate:function(k,m,n,l){if(g.compare(Ext.getVersion(k),m)<1){n.call(l)}}});Ext.setVersion("core",b)}());Ext.String=(function(){var l=/^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g,r=/('|\\)/g,k=/\{\d+\}/,c=/([-.*+?\^${}()|\[\]\/\\])/g,s=/^\s+|\s+$/g,n=/\s+/,p=/(^[^a-z]*|[^\w])/gi,g,b,j,e,h=function(u,t){return g[t]},o=function(u,t){return(t in b)?b[t]:String.fromCharCode(parseInt(t.substr(2),10))},d=function(u,t){if(u===null||u===undefined||t===null||t===undefined){return false}return t.length<=u.length},q={useFormat:false,compiled:true,stringFormat:true},m={},a=function(t){if(k.test(t)){t=new Ext.Template(t,q);return function(){return t.apply(arguments)}}else{return function(){return t}}};return{insert:function(v,w,u){if(!v){return w}if(!w){return v}var t=v.length;if(!u&&u!==0){u=t}if(u<0){u*=-1;if(u>=t){u=0}else{u=t-u}}if(u===0){v=w+v}else{if(u>=v.length){v+=w}else{v=v.substr(0,u)+w+v.substr(u)}}return v},startsWith:function(v,w,u){var t=d(v,w);if(t){if(u){v=v.toLowerCase();w=w.toLowerCase()}t=v.lastIndexOf(w,0)===0}return t},endsWith:function(w,u,v){var t=d(w,u);if(t){if(v){w=w.toLowerCase();u=u.toLowerCase()}t=w.indexOf(u,w.length-u.length)!==-1}return t},createVarName:function(t){return t.replace(p,"")},htmlEncode:function(t){return(!t)?t:String(t).replace(j,h)},htmlDecode:function(t){return(!t)?t:String(t).replace(e,o)},hasHtmlCharacters:function(t){return j.test(t)},addCharacterEntities:function(u){var t=[],x=[],v,w;for(v in u){w=u[v];b[v]=w;g[w]=v;t.push(w);x.push(v)}j=new RegExp("("+t.join("|")+")","g");e=new RegExp("("+x.join("|")+"|&#[0-9]{1,5};)","g")},resetCharacterEntities:function(){g={};b={};this.addCharacterEntities({"&":"&",">":">","<":"<",""":'"',"'":"'"})},urlAppend:function(u,t){if(!Ext.isEmpty(t)){return u+(u.indexOf("?")===-1?"?":"&")+t}return u},trim:function(t){return t.replace(l,"")},capitalize:function(t){return t.charAt(0).toUpperCase()+t.substr(1)},uncapitalize:function(t){return t.charAt(0).toLowerCase()+t.substr(1)},ellipsis:function(v,u,w){if(v&&v.length>u){if(w){var x=v.substr(0,u-2),t=Math.max(x.lastIndexOf(" "),x.lastIndexOf("."),x.lastIndexOf("!"),x.lastIndexOf("?"));if(t!==-1&&t>=(u-15)){return x.substr(0,t)+"..."}}return v.substr(0,u-3)+"..."}return v},escapeRegex:function(t){return t.replace(c,"\\$1")},escape:function(t){return t.replace(r,"\\$1")},toggle:function(u,v,t){return u===v?t:v},leftPad:function(u,v,w){var t=String(u);w=w||" ";while(t.lengthe)?e:d)},snap:function(h,e,g,j){var d;if(h===undefined||h=e){h+=e}else{if(d*2<-e){h-=e}}}}return b.constrain(h,g,j)},snapInRange:function(h,d,g,j){var e;g=(g||0);if(h===undefined||h=d){h+=d}}if(j!==undefined){if(h>(j=b.snapInRange(j,d,g))){h=j}}return h},toFixed:c?function(g,d){d=d||0;var e=a.pow(10,d);return(a.round(g*e)/e).toFixed(d)}:function(e,d){return e.toFixed(d)},from:function(e,d){if(isFinite(e)){e=parseFloat(e)}return !isNaN(e)?e:d},randomInt:function(e,d){return a.floor(a.random()*(d-e+1)+e)},correctFloat:function(d){return parseFloat(d.toPrecision(14))}});Ext.num=function(){return b.from.apply(this,arguments)}}();(function(){var g=Array.prototype,p=g.slice,r=(function(){var B=[],e,A=20;if(!B.splice){return false}while(A--){B.push("A")}B.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=B.length;B.splice(13,0,"XXX");if(e+1!=B.length){return false}return true}()),k="forEach" in g,v="map" in g,q="indexOf" in g,z="every" in g,c="some" in g,d="filter" in g,o=(function(){var e=[1,2,3,4,5].sort(function(){return 0});return e[0]===1&&e[1]===2&&e[2]===3&&e[3]===4&&e[4]===5}()),l=true,a,x,u,w;try{if(typeof document!=="undefined"){p.call(document.getElementsByTagName("body"))}}catch(t){l=false}function n(A,e){return(e<0)?Math.max(0,A.length+e):Math.min(A.length,e)}function y(H,G,A,K){var L=K?K.length:0,C=H.length,I=n(H,G),F,J,B,e,D,E;if(I===C){if(L){H.push.apply(H,K)}}else{F=Math.min(A,C-I);J=I+F;B=J+L-F;e=C-J;D=C-F;if(BJ){for(E=e;E--;){H[B+E]=H[J+E]}}}if(L&&I===D){H.length=D;H.push.apply(H,K)}else{H.length=D+L;for(E=0;E-1;A--){if(C.call(B||E[A],E[A],A,E)===false){return A}}}return true},forEach:k?function(B,A,e){B.forEach(A,e)}:function(D,B,A){var e=0,C=D.length;for(;ee){e=B}}}return e},mean:function(e){return e.length>0?a.sum(e)/e.length:undefined},sum:function(D){var A=0,e,C,B;for(e=0,C=D.length;e0){return setTimeout(Ext.supports.TimeoutActualLateness?function(){e()}:e,c)}e();return 0},createSequence:function(b,c,a){if(!c){return b}else{return function(){var d=b.apply(this,arguments);c.apply(a||this,arguments);return d}}},createBuffered:function(e,b,d,c){var a;return function(){var h=c||Array.prototype.slice.call(arguments,0),g=d||this;if(a){clearTimeout(a)}a=setTimeout(function(){e.apply(g,h)},b)}},createThrottled:function(e,b,d){var g,a,c,j,h=function(){e.apply(d||this,c);g=Ext.Date.now()};return function(){a=Ext.Date.now()-g;c=arguments;clearTimeout(j);if(!g||(a>=b)){h()}else{j=setTimeout(h,b-a)}}},interceptBefore:function(b,a,d,c){var e=b[a]||Ext.emptyFn;return(b[a]=function(){var g=d.apply(c||this,arguments);e.apply(this,arguments);return g})},interceptAfter:function(b,a,d,c){var e=b[a]||Ext.emptyFn;return(b[a]=function(){e.apply(this,arguments);return d.apply(c||this,arguments)})}};Ext.defer=Ext.Function.alias(Ext.Function,"defer");Ext.pass=Ext.Function.alias(Ext.Function,"pass");Ext.bind=Ext.Function.alias(Ext.Function,"bind");(function(){var a=function(){},b=Ext.Object={chain:Object.create||function(d){a.prototype=d;var c=new a();a.prototype=null;return c},clear:function(c){var d=b.getKeys(c),e=d.length;while(e--){delete c[d[e]]}return c},toQueryObjects:function(e,k,d){var c=b.toQueryObjects,j=[],g,h;if(Ext.isArray(k)){for(g=0,h=k.length;g0){k=o.split("=");w=decodeURIComponent(k[0]);n=(k[1]!==undefined)?decodeURIComponent(k[1]):"";if(!r){if(u.hasOwnProperty(w)){if(!Ext.isArray(u[w])){u[w]=[u[w]]}u[w].push(n)}else{u[w]=n}}else{h=w.match(/(\[):?([^\]]*)\]/g);t=w.match(/^([^\[]+)/);w=t[0];l=[];if(h===null){u[w]=n;continue}for(p=0,c=h.length;p 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;"].join("\n");function h(m){var l=Array.prototype.slice.call(arguments,1);return m.replace(c,function(n,o){return l[o]})}Ext.apply(d,{now:Date.now||function(){return +new Date()},toString:function(l){var m=Ext.String.leftPad;return l.getFullYear()+"-"+m(l.getMonth()+1,2,"0")+"-"+m(l.getDate(),2,"0")+"T"+m(l.getHours(),2,"0")+":"+m(l.getMinutes(),2,"0")+":"+m(l.getSeconds(),2,"0")},getElapsed:function(m,l){return Math.abs(m-(l||d.now()))},useStrict:false,formatCodeToRegex:function(m,l){var n=d.parseCodes[m];if(n){n=typeof n=="function"?n():n;d.parseCodes[m]=n}return n?Ext.applyIf({c:n.c?h(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(g);return n?new Date(((n[1]||"")+n[2])*1):null},time:function(m,l){var n=parseInt(m,10);if(n||n===0){return new Date(n)}return null},timestamp:function(m,l){var n=parseInt(m,10);if(n||n===0){return new Date(n*1000)}return null}},parseRegexes:[],formatFunctions:{MS:function(){return"\\/Date("+this.getTime()+")\\/"},time:function(){return this.getTime().toString()},timestamp:function(){return d.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 Ext.Date.monthNames[l].substring(0,3)},getShortDayName:function(l){return Ext.Date.dayNames[l].substring(0,3)},getMonthNumber:function(l){return Ext.Date.monthNumbers[l.substring(0,1).toUpperCase()+l.substring(1,3).toLowerCase()]},formatContainsHourInfo:function(l){return a.test(l.replace(k,""))},formatContainsDateInfo:function(l){return e.test(l.replace(k,""))},unescapeFormat:function(l){return l.replace(j,"")},formatCodes:{d:"Ext.String.leftPad(this.getDate(), 2, '0')",D:"Ext.Date.getShortDayName(this.getDay())",j:"this.getDate()",l:"Ext.Date.dayNames[this.getDay()]",N:"(this.getDay() ? this.getDay() : 7)",S:"Ext.Date.getSuffix(this)",w:"this.getDay()",z:"Ext.Date.getDayOfYear(this)",W:"Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",F:"Ext.Date.monthNames[this.getMonth()]",m:"Ext.String.leftPad(this.getMonth() + 1, 2, '0')",M:"Ext.Date.getShortMonthName(this.getMonth())",n:"(this.getMonth() + 1)",t:"Ext.Date.getDaysInMonth(this)",L:"(Ext.Date.isLeapYear(this) ? 1 : 0)",o:"(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",Y:"Ext.String.leftPad(this.getFullYear(), 4, '0')",y:"('' + this.getFullYear()).substring(2, 4)",a:"(this.getHours() < 12 ? 'am' : 'pm')",A:"(this.getHours() < 12 ? 'AM' : 'PM')",g:"((this.getHours() % 12) ? this.getHours() % 12 : 12)",G:"this.getHours()",h:"Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",H:"Ext.String.leftPad(this.getHours(), 2, '0')",i:"Ext.String.leftPad(this.getMinutes(), 2, '0')",s:"Ext.String.leftPad(this.getSeconds(), 2, '0')",u:"Ext.String.leftPad(this.getMilliseconds(), 3, '0')",O:"Ext.Date.getGMTOffset(this)",P:"Ext.Date.getGMTOffset(this, true)",T:"Ext.Date.getTimezone(this)",Z:"(this.getTimezoneOffset() * -60)",c:function(){var q,o,n,m,p;for(q="Y-m-dTH:i:sP",o=[],n=0,m=q.length;n me.y2kYear ? 1900 + ty : 2000 + ty;\n",s:"(\\d{1,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=[d.formatCodeToRegex("Y",1),d.formatCodeToRegex("m",2),d.formatCodeToRegex("d",3),d.formatCodeToRegex("H",4),d.formatCodeToRegex("i",5),d.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){",d.formatCodeToRegex("P",8).c,"}else{",d.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=Ext.Date.clone(o),l=o.getMonth(),p;for(p=0,q.setDate(1),q.setMonth(0);p28){m=Math.min(m,Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(o),Ext.Date.MONTH,r)).getDate())}s.setDate(m);s.setMonth(o.getMonth()+r);break;case Ext.Date.YEAR:m=o.getDate();if(m>28){m=Math.min(m,Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(o),Ext.Date.YEAR,r)).getDate())}s.setDate(m);s.setFullYear(o.getFullYear()+r);break}}if(q){switch(n.toLowerCase()){case Ext.Date.MILLI:p=1;break;case Ext.Date.SECOND:p=1000;break;case Ext.Date.MINUTE:p=1000*60;break;case Ext.Date.HOUR:p=1000*60*60;break;case Ext.Date.DAY:p=1000*60*60*24;break;case Ext.Date.MONTH:m=d.getDaysInMonth(s);p=1000*60*60*24*m;break;case Ext.Date.YEAR:m=(d.isLeapYear(s)?366:365);p=1000*60*60*24*m;break}if(p){s.setTime(s.getTime()+p*q)}}return s},subtract:function(m,l,n){return d.add(m,l,-n)},between:function(m,o,l){var n=m.getTime();return o.getTime()<=n&&n<=l.getTime()},compat:function(){var m=window.Date,l,t=["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"],q=["dateFormat","format","getTimezone","getGMTOffset","getDayOfYear","getWeekOfYear","isLeapYear","getFirstDayOfMonth","getLastDayOfMonth","getDaysInMonth","getSuffix","clone","isDST","clearTime","add","between"],r=t.length,n=q.length,o,u,v;for(v=0;v0){for(e=0;e0){if(B===A){return D[B]}C=D[B];A=A.substring(B.length+1)}if(C.length>0){C+="/"}return C.replace(c,"/")+A.replace(g,"/")+".js"},getPrefix:function(B){var D=l.config.paths,C,A="";if(D.hasOwnProperty(B)){return B}for(C in D){if(D.hasOwnProperty(C)&&C+"."===B.substring(0,C.length+1)){if(C.length>A.length){A=C}}}return A},isAClassNameWithAKnownPrefix:function(A){var B=l.getPrefix(A);return B!==""&&B!==A},require:function(C,B,A,D){if(B){B.call(A)}},syncRequire:function(){},exclude:function(A){return{require:function(D,C,B){return l.require(D,C,B,A)},syncRequire:function(D,C,B){return l.syncRequire(D,C,B,A)}}},onReady:function(D,C,E,A){var B;if(E!==false&&Ext.onDocumentReady){B=D;D=function(){Ext.onDocumentReady(B,C,A)}}D.call(C)}});var s=[],t={},w={},n={},u={},r={},y=[],z=[],j={},m=function(A,B){return B.priority-A.priority};Ext.apply(l,{documentHead:typeof document!="undefined"&&(document.head||document.getElementsByTagName("head")[0]),isLoading:false,queue:s,isClassFileLoaded:t,isFileLoaded:w,readyListeners:y,optionalRequires:z,requiresMap:j,numPendingFiles:0,numLoadedFiles:0,hasFileLoadError:false,classNameToFilePathMap:u,scriptsLoading:0,syncModeEnabled:false,scriptElements:r,refreshQueue:function(){var E=s.length,B,D,A,C;if(!E&&!l.scriptsLoading){return l.triggerReady()}for(B=0;B=200&&E<300)||(E===304)){if(!Ext.isIE){F="\n//@ sourceURL="+B}Ext.globalEval(K.responseText+F);I.call(L)}else{}}K=null}},syncRequire:function(){var A=l.syncModeEnabled;if(!A){l.syncModeEnabled=true}l.require.apply(l,arguments);if(!A){l.syncModeEnabled=false}l.refreshQueue()},require:function(S,J,D,F){var L={},C={},I=[],U=[],R=[],B=[],H,T,N,M,A,G,Q,P,O,K,E;if(F){F=(typeof F==="string")?[F]:F;for(P=0,K=F.length;P0){I=b.getNamesByExpression(A);for(O=0,E=I.length;O0){H=function(){var W=[],V,X;for(V=0,X=B.length;V0){U=b.getNamesByExpression(M);E=U.length;for(O=0;O0){if(!l.config.enabled){throw new Error("Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. Missing required class"+((R.length>1)?"es":"")+": "+R.join(", "))}}else{H.call(D);return l}T=l.syncModeEnabled;if(!T){s.push({requires:R.slice(),callback:H,scope:D})}K=R.length;for(P=0;Pwindow.innerWidth?"portrait":"landscape"},destroy:function(){var c=arguments.length,b,a;for(b=0;b0){e=e+c+h.shift()}e=e.replace(g,c);b.push(e)}return b}})()});Ext.ns=Ext.namespace;window.undefined=window.undefined;(function(){var r=function(e){return e.test(Ext.userAgent)},w=document.compatMode=="CSS1Compat",I=function(U,T){var e;return(U&&(e=T.exec(Ext.userAgent)))?parseFloat(e[1]):0},s=document.documentMode,a=r(/opera/),y=a&&r(/version\/10\.5/),N=r(/\bchrome\b/),C=r(/webkit/),c=!N&&r(/safari/),L=c&&r(/applewebkit\/4/),J=c&&r(/version\/3/),G=c&&r(/version\/4/),m=c&&r(/version\/5\.0/),F=c&&r(/version\/5/),l=!a&&(r(/msie/)||r(/trident/)),M=l&&((r(/msie 7/)&&s!=8&&s!=9&&s!=10)||s==7),K=l&&((r(/msie 8/)&&s!=7&&s!=9&&s!=10)||s==8),H=l&&((r(/msie 9/)&&s!=7&&s!=8&&s!=10)||s==9),j=l&&((r(/msie 10/)&&s!=7&&s!=8&&s!=9)||s==10),g=l&&((r(/trident\/7\.0/)&&s!=7&&s!=8&&s!=9&&s!=10)||s==11),P=l&&r(/msie 6/),b=!C&&!l&&r(/gecko/),S=b&&r(/rv:1\.9/),R=b&&r(/rv:2\.0/),Q=b&&r(/rv:5\./),u=b&&r(/rv:10\./),B=S&&r(/rv:1\.9\.0/),z=S&&r(/rv:1\.9\.1/),x=S&&r(/rv:1\.9\.2/),h=r(/windows|win32/),E=r(/macintosh|mac os x/),A=r(/linux/),o=null,p=I(true,/\bchrome\/(\d+\.\d+)/),k=I(true,/\bfirefox\/(\d+\.\d+)/),q=I(l,/msie (\d+\.\d+)/),v=I(a,/version\/(\d+\.\d+)/),d=I(c,/version\/(\d+\.\d+)/),D=I(C,/webkit\/(\d+\.\d+)/),t=/^https/i.test(window.location.protocol),n;try{document.execCommand("BackgroundImageCache",false,true)}catch(O){}n=function(){};n.info=n.warn=n.error=Ext.emptyFn;Ext.setVersion("ext","4.2.3.1477");Ext.setVersion("extjs","4.2.3.1477");Ext.apply(Ext,{SSL_SECURE_URL:t&&l?"javascript:''":"about:blank",plainTableCls:Ext.buildSettings.baseCSSPrefix+"table-plain",plainListCls:Ext.buildSettings.baseCSSPrefix+"list-plain",enableNestedListenerRemoval:false,USE_NATIVE_JSON:false,getDom:function(U,T){if(!U||!document){return null}if(U.dom){return U.dom}else{if(typeof U=="string"){var V=Ext.getElementById(U);if(V&&l&&T){if(U==V.getAttribute("id")){return V}else{return null}}return V}else{return U}}},removeNode:P||M||K?(function(){var e;return function(V){if(V&&V.tagName.toUpperCase()!="BODY"){(Ext.enableNestedListenerRemoval)?Ext.EventManager.purgeElement(V):Ext.EventManager.removeAll(V);var T=Ext.cache,U=V.id;if(T[U]){delete T[U].dom;delete T[U]}if(K&&V.parentNode){V.parentNode.removeChild(V)}e=e||document.createElement("div");e.appendChild(V);e.innerHTML=""}}}()):function(U){if(U&&U.parentNode&&U.tagName.toUpperCase()!="BODY"){(Ext.enableNestedListenerRemoval)?Ext.EventManager.purgeElement(U):Ext.EventManager.removeAll(U);var e=Ext.cache,T=U.id;if(e[T]){delete e[T].dom;delete e[T]}U.parentNode.removeChild(U)}},isStrict:w,isIEQuirks:l&&(!w&&(P||M||K||H)),isOpera:a,isOpera10_5:y,isWebKit:C,isChrome:N,isSafari:c,isSafari3:J,isSafari4:G,isSafari5:F,isSafari5_0:m,isSafari2:L,isIE:l,isIE6:P,isIE7:M,isIE7m:P||M,isIE7p:l&&!P,isIE8:K,isIE8m:P||M||K,isIE8p:l&&!(P||M),isIE9:H,isIE9m:P||M||K||H,isIE9p:l&&!(P||M||K),isIE10:j,isIE10m:P||M||K||H||j,isIE10p:l&&!(P||M||K||H),isIE11:g,isIE11m:P||M||K||H||j||g,isIE11p:l&&!(P||M||K||H||j),isGecko:b,isGecko3:S,isGecko4:R,isGecko5:Q,isGecko10:u,isFF3_0:B,isFF3_5:z,isFF3_6:x,isFF4:4<=k&&k<5,isFF5:5<=k&&k<6,isFF10:10<=k&&k<11,isLinux:A,isWindows:h,isMac:E,chromeVersion:p,firefoxVersion:k,ieVersion:q,operaVersion:v,safariVersion:d,webKitVersion:D,isSecure:t,BLANK_IMAGE_URL:(P||M)?"//www.sencha.com/s.gif":"data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==",value:function(U,e,T){return Ext.isEmpty(U,T)?e:U},escapeRe:function(e){return e.replace(/([-.*+?\^${}()|\[\]\/\\])/g,"\\$1")},addBehaviors:function(W){if(!Ext.isReady){Ext.onReady(function(){Ext.addBehaviors(W)})}else{var T={},V,e,U;for(e in W){if((V=e.split("@"))[1]){U=V[0];if(!T[U]){T[U]=Ext.select(U)}T[U].on(V[1],W[e])}}T=null}},getScrollbarSize:function(T){if(!Ext.isReady){return{}}if(T||!o){var e=document.body,U=document.createElement("div");U.style.width=U.style.height="100px";U.style.overflow="scroll";U.style.position="absolute";e.appendChild(U);o={width:U.offsetWidth-U.clientWidth,height:U.offsetHeight-U.clientHeight};e.removeChild(U)}return o},getScrollBarWidth:function(T){var e=Ext.getScrollbarSize(T);return e.width+2},copyTo:function(T,V,X,W){if(typeof X=="string"){X=X.split(/[,;\s]/)}var Y,U=X?X.length:0,e;for(Y=0;Y]+>/gi,j=/(?:)((\n|\r|.)*?)(?:<\/script>)/ig,e=/\r?\n/g,b=/#+$/,h=/[\d,\.#]+/,l=/[^\d\.#]/g,a,k,d={};Ext.apply(g,{thousandSeparator:",",decimalSeparator:".",currencyPrecision:2,currencySign:"$",currencyAtEnd:false,undef:function(m){return m!==undefined?m:""},defaultValue:function(n,m){return n!==undefined&&n!==""?n:m},substr:"ab".substr(-1)!="b"?function(n,p,m){var o=String(n);return(p<0)?o.substr(Math.max(o.length+p,0),m):o.substr(p,m)}:function(n,o,m){return String(n).substr(o,m)},lowercase:function(m){return String(m).toLowerCase()},uppercase:function(m){return String(m).toUpperCase()},usMoney:function(m){return g.currency(m,"$",2)},currency:function(o,q,n,m){var s="",r=",0",p=0;o=o-0;if(o<0){o=-o;s="-"}n=Ext.isDefined(n)?n:g.currencyPrecision;r+=(n>0?".":"");for(;p2){}else{if(u.length===2){r=u[1].length;A=u[1].match(b);if(A){s=A[0].length;o='trailingZeroes=new RegExp(Ext.String.escapeRegex(utilFormat.decimalSeparator) + "*0{0,'+s+'}$")'}}}m=["var utilFormat=Ext.util.Format,extNumber=Ext.Number,neg,absVal,fnum,parts"+(n?",thousandSeparator,thousands=[],j,n,i":"")+(w?',formatString="'+q+'",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, "+r+");",o,";"];if(n){if(r){m[m.length]='parts=fnum.split(".");';m[m.length]="fnum=parts[0];"}m[m.length]="if(absVal>=1000) {";m[m.length]="thousandSeparator=utilFormat.thousandSeparator;thousands.length=0;j=fnum.length;n=fnum.length%3||3;for(i=0;i")},capitalize:Ext.String.capitalize,ellipsis:Ext.String.ellipsis,format:Ext.String.format,htmlDecode:Ext.String.htmlDecode,htmlEncode:Ext.String.htmlEncode,leftPad:Ext.String.leftPad,trim:Ext.String.trim,parseBox:function(n){n=n||0;if(typeof n==="number"){return{top:n,right:n,bottom:n,left:n}}var o=n.split(" "),m=o.length;if(m==1){o[1]=o[2]=o[3]=o[0]}else{if(m==2){o[2]=o[0];o[3]=o[1]}else{if(m==3){o[3]=o[1]}}}return{top:parseInt(o[0],10)||0,right:parseInt(o[1],10)||0,bottom:parseInt(o[2],10)||0,left:parseInt(o[3],10)||0}},escapeRegex:function(m){return m.replace(/([\-.*+?\^${}()|\[\]\/\\])/g,"\\$1")}})}());(Ext.cmd.derive("Ext.util.TaskRunner",Ext.Base,{interval:10,timerId:null,constructor:function(a){var b=this;if(typeof a=="number"){b.interval=a}else{if(a){Ext.apply(b,a)}}b.tasks=[];b.timerFn=Ext.Function.bind(b.onTick,b)},newTask:function(b){var a=new Ext.util.TaskRunner.Task(b);a.manager=this;return a},start:function(a){var c=this,b=Ext.Date.now();if(!a.pending){c.tasks.push(a);a.pending=true}a.stopped=false;a.taskStartTime=b;a.taskRunTime=a.fireOnStart!==false?0:a.taskStartTime;a.taskRunCount=0;if(!c.firing){if(a.fireOnStart!==false){c.startTimer(0,b)}else{c.startTimer(a.interval,b)}}return a},stop:function(a){if(!a.stopped){a.stopped=true;if(a.onStop){a.onStop.call(a.scope||a,a)}}return a},stopAll:function(){Ext.each(this.tasks,this.stop,this)},firing:false,nextExpires:1e+99,onTick:function(){var m=this,e=m.tasks,a=Ext.Date.now(),n=1e+99,k=e.length,c,o,h,b,d,g;m.timerId=null;m.firing=true;for(h=0;hc){n=c}}}if(o){m.tasks=o}m.firing=false;if(m.tasks.length){m.startTimer(n-a,Ext.Date.now())}if(m.fireIdleEvent!==false){Ext.EventManager.idleEvent.fire()}},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',''," ({childCount} children)","",''," ({depth} deep)","",'',", {type}: {[this.time(values.sum)]} msec (","avg={[this.time(values.sum / parent.count)]}",")","","
    "].join(""),{time:function(o){return Math.round(o*100)/100}})}var n=this.getData(m);n.name=this.name;n.pure.type="Pure";n.total.type="Total";n.times=[n.pure,n.total];return d.apply(n)},getData:function(m){var n=this;return{count:n.count,childCount:n.childCount,depth:n.maxDepth,pure:g(n.count,n.childCount,m,n.pure),total:g(n.count,n.childCount,m,n.total)}},enter:function(){var m=this,n={accum:m,leave:e,childTime:0,parent:c};++m.depth;if(m.maxDepth','
    ',"",'
    ','
    ',"
    ",'
    ','
    '].join("");c.body.appendChild(d)}k=e[p];while(j--){m=l[j];q=k&&k[j];if(q!==undefined){o[m.identity]=q}else{if(h||m.early){o[m.identity]=m.fn.call(o,c,d)}else{g.push(m)}}}if(h){c.body.removeChild(d)}o.toRun=g},PointerEvents:"pointerEvents" in document.documentElement.style,LocalStorage:(function(){try{return"localStorage" in window&&window.localStorage!==null}catch(g){return false}})(),CSS3BoxShadow:"boxShadow" in document.documentElement.style||"WebkitBoxShadow" in document.documentElement.style||"MozBoxShadow" in document.documentElement.style,ClassList:!!document.documentElement.classList,OrientationChange:((typeof window.orientation!="undefined")&&("onorientationchange" in window)),DeviceMotion:("ondevicemotion" in window),Touch:("ontouchstart" in window)&&(!Ext.is.Desktop),TimeoutActualLateness:(function(){setTimeout(function(){Ext.supports.TimeoutActualLateness=arguments.length!==0},0)}()),tests:[{identity:"Transitions",fn:function(m,o){var l=["webkit","Moz","o","ms","khtml"],n="TransitionEnd",g=[l[0]+n,"transitionend",l[2]+n,l[3]+n,l[4]+n],k=l.length,j=0,h=false;for(;j

    ";return(h.childNodes.length==2)},early:true},{identity:"Float",fn:function(g){return"cssFloat" in g.documentElement.style},early:true},{identity:"AudioTag",fn:function(g){return !!g.createElement("audio").canPlayType},early:true},{identity:"History",fn:function(){var g=window.history;return !!(g&&g.pushState)},early:true},{identity:"Hashchange",fn:function(){var g=document.documentMode;return"onhashchange" in window&&(g===undefined||g>7)},early:true},{identity:"CSS3DTransform",fn:function(){return(typeof WebKitCSSMatrix!="undefined"&&new WebKitCSSMatrix().hasOwnProperty("m41"))},early:true},{identity:"CSS3LinearGradient",fn:function(m,g){var o="background-image:",n="-webkit-gradient(linear, left top, right bottom, from(black), to(white))",l="linear-gradient(left top, black, white)",k="-moz-"+l,h="-ms-"+l,j="-o-"+l,p=[o+n,o+l,o+k,o+h,o+j];g.style.cssText=p.join(";");return((""+g.style.backgroundImage).indexOf("gradient")!==-1)&&!Ext.isIE9},early:true},{identity:"CSS3BorderRadius",fn:function(j){var h=["borderRadius","BorderRadius","MozBorderRadius","WebkitBorderRadius","OBorderRadius","KhtmlBorderRadius"],g;for(g=0;g=534.16},early:true},{identity:"TextAreaMaxLength",fn:function(h){var g=h.createElement("textarea");return("maxlength" in g)},early:true},{identity:"GetPositionPercentage",fn:function(g,h){return a(h.childNodes[2],"left")=="10%"}},{identity:"PercentageHeightOverflowBug",fn:function(k){var g=false,j,h;if(Ext.getScrollbarSize().height){h=k.createElement("div");j=h.style;j.height="50px";j.width="50px";j.overflow="auto";j.position="absolute";h.innerHTML=['
    ','
    ',"
    "].join("");k.body.appendChild(h);if(h.firstChild.offsetHeight===50){g=true}k.body.removeChild(h)}return g}},{identity:"xOriginBug",fn:function(k,l){l.innerHTML='
    ';var j=document.getElementById("b1").getBoundingClientRect(),h=document.getElementById("b2").getBoundingClientRect(),g=document.getElementById("b3").getBoundingClientRect();return(h.left!==j.left&&g.right!==j.right)}},{identity:"ScrollWidthInlinePaddingBug",fn:function(k){var g=false,j,h;h=k.createElement("div");j=h.style;j.height="50px";j.width="50px";j.padding="10px";j.overflow="hidden";j.position="absolute";h.innerHTML='';k.body.appendChild(h);if(h.scrollWidth===70){g=true}k.body.removeChild(h);return g}},{identity:"rtlVertScrollbarOnRight",fn:function(j,k){k.innerHTML='
    ';var h=k.firstChild,g=h.firstChild;return(g.offsetLeft+g.offsetWidth!==h.offsetLeft+h.offsetWidth)}},{identity:"rtlVertScrollbarOverflowBug",fn:function(h,j){j.innerHTML='
    ';var g=j.firstChild;return g.clientHeight===g.offsetHeight}}]}}());Ext.supports.init();Ext.util.DelayedTask=function(e,d,b,h){var g=this,a,c=function(){clearInterval(g.id);g.id=null;e.apply(d,b||[]);Ext.EventManager.idleEvent.fire()};h=typeof h==="boolean"?h:true;g.id=null;g.delay=function(k,m,l,j){if(h){g.cancel()}if(typeof k==="number"){a=k}e=m||e;d=l||d;b=j||b;if(!g.id){g.id=setInterval(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,b=Ext.Array.toArray,c=Ext.util.DelayedTask;return{isEvent:true,suspended:0,noOptions:{},constructor:function(g,e){this.name=e;this.observable=g;this.listeners=[]},addListener:function(p,r,t){var n=this,o,j,q,e,s,m,h,l,k,g;r=r||n.observable;if(!n.isListening(p,r)){j=n.createListener(p,r,t);if(n.firing){n.listeners=n.listeners.slice(0)}o=n.listeners;l=h=o.length;q=t&&t.priority;s=n._highestNegativePriorityIndex;m=(s!==undefined);if(q){e=(q<0);if(!e||m){for(k=(e?s:0);k0},fire:function(){var l=this,j=l.listeners,k=j.length,h,g,m,e;if(!l.suspended&&k>0){l.firing=true;g=arguments.length?d.call(arguments,0):[];e=g.length;for(h=0;h111&&l.keyCode<124){l.keyCode=-1}}catch(m){}}},getRelatedTarget:function(l){l=l.browserEvent||l;var m=l.relatedTarget;if(!m){if(b.mouseLeaveRe.test(l.type)){m=l.toElement}else{if(b.mouseEnterRe.test(l.type)){m=l.fromElement}}}return b.resolveTextNode(m)},getPageX:function(l){return b.getPageXY(l)[0]},getPageY:function(l){return b.getPageXY(l)[1]},getPageXY:function(n){n=n.browserEvent||n;var m=n.pageX,p=n.pageY,o=j.documentElement,l=j.body;if(!m&&m!==0){m=n.clientX+(o&&o.scrollLeft||l&&l.scrollLeft||0)-(o&&o.clientLeft||l&&l.clientLeft||0);p=n.clientY+(o&&o.scrollTop||l&&l.scrollTop||0)-(o&&o.clientTop||l&&l.clientTop||0)}return[m,p]},getTarget:function(l){l=l.browserEvent||l;return b.resolveTextNode(l.target||l.srcElement)},resolveTextNode:Ext.isGecko?function(m){if(m){var l=HTMLElement.prototype.toString.call(m);if(l!=="[xpconnect wrapped native prototype]"&&l!=="[object XULElement]"){return m.nodeType==3?m.parentNode:m}}}:function(l){return l&&l.nodeType==3?l.parentNode:l},curWidth:0,curHeight:0,onWindowResize:function(o,n,m){var l=b.resizeEvent;if(!l){b.resizeEvent=l=new Ext.util.Event();b.on(e,"resize",b.fireResize,null,{buffer:100})}l.addListener(o,n,m)},fireResize:function(){var l=Ext.Element.getViewWidth(),m=Ext.Element.getViewHeight();if(b.curHeight!=m||b.curWidth!=l){b.curHeight=m;b.curWidth=l;b.resizeEvent.fire(l,m)}},removeResizeListener:function(n,m){var l=b.resizeEvent;if(l){l.removeListener(n,m)}},onWindowUnload:function(o,n,m){var l=b.unloadEvent;if(!l){b.unloadEvent=l=new Ext.util.Event();b.addListener(e,"unload",b.fireUnload)}if(o){l.addListener(o,n,m)}},fireUnload:function(){try{j=e=undefined;var q,m,o,n,l;b.unloadEvent.fire();if(Ext.isGecko3){q=Ext.ComponentQuery.query("gridview");m=0;o=q.length;for(;m=525:!((Ext.isGecko&&!Ext.isWindows)||(Ext.isOpera&&Ext.operaVersion<12)),getKeyEvent:function(){return b.useKeyDown?"keydown":"keypress"}});if(!h&&document.attachEvent){Ext.apply(b,{pollScroll:function(){var l=true;try{document.documentElement.doScroll("left")}catch(m){l=false}if(l&&document.body){b.onReadyEvent({type:"doScroll"})}else{b.scrollTimeout=setTimeout(b.pollScroll,20)}return l},scrollTimeout:null,readyStatesRe:/complete/i,checkReadyState:function(){var l=document.readyState;if(b.readyStatesRe.test(l)){b.onReadyEvent({type:l})}},bindReadyEvent:function(){var l=true;if(b.hasBoundOnReady){return}try{l=window.frameElement===undefined}catch(m){l=false}if(!l||!j.documentElement.doScroll){b.pollScroll=Ext.emptyFn}if(b.pollScroll()===true){return}if(j.readyState=="complete"){b.onReadyEvent({type:"already "+(j.readyState||"body")})}else{j.attachEvent("onreadystatechange",b.checkReadyState);window.attachEvent("onload",b.onReadyEvent);b.hasBoundOnReady=true}},onReadyEvent:function(l){if(l&&l.type){b.onReadyChain.push(l.type)}if(b.hasBoundOnReady){document.detachEvent("onreadystatechange",b.checkReadyState);window.detachEvent("onload",b.onReadyEvent)}if(Ext.isNumber(b.scrollTimeout)){clearTimeout(b.scrollTimeout);delete b.scrollTimeout}if(!Ext.isReady){b.fireDocReady()}},onReadyChain:[]})}Ext.onReady=function(n,m,l){Ext.Loader.onReady(n,m,true,l)};Ext.onDocumentReady=b.onDocumentReady;b.on=b.addListener;b.un=b.removeListener;Ext.onReady(g)}();Ext.setVersion("ext-theme-base","4.2.3.1477");Ext.setVersion("ext-theme-neptune","4.2.3.1477");Ext.setVersion("ext-theme-neutral","4.2.3.1477");Ext.setVersion("font-awesome","4.5.0");Ext.setVersion("rapture-theme","1.0.0");(Ext.cmd.derive("Ext.util.Observable",Ext.Base,function(a){var e=Ext.emptyFn,d=[],g=Array.prototype,h=g.slice,c=Ext.util.Event,b=function(j){if(j instanceof b){return j}this.observable=j;if(arguments[1].isObservable){this.managedListeners=true}this.args=h.call(arguments,1)};b.prototype.destroy=function(){this.observable[this.managedListeners?"mun":"un"].apply(this.observable,this.args)};return{statics:{releaseCapture:function(j){j.fireEventArgs=this.prototype.fireEventArgs},capture:function(m,k,j){var l=function(n,o){return k.apply(j,[n].concat(o))};this.captureArgs(m,l,j)},captureArgs:function(l,k,j){l.fireEventArgs=Ext.Function.createInterceptor(l.fireEventArgs,k,j)},observe:function(j,k){if(j){if(!j.isObservable){Ext.applyIf(j,new this());this.captureArgs(j.prototype,j.fireEventArgs,j)}if(Ext.isObject(k)){j.on(k)}}return j},prepareClass:function(l,k){if(!l.HasListeners){var m=function(){},j=l.superclass.HasListeners||(k&&k.HasListeners)||a.HasListeners;l.prototype.HasListeners=l.HasListeners=m;m.prototype=l.hasListeners=new j()}}},isObservable:true,eventsSuspended:0,constructor:function(j){var k=this;Ext.apply(k,j);if(!k.hasListeners){k.hasListeners=new k.HasListeners()}k.events=k.events||{};if(k.listeners){k.on(k.listeners);k.listeners=null}if(k.bubbleEvents){k.enableBubble(k.bubbleEvents)}},onClassExtended:function(j){if(!j.HasListeners){a.prepareClass(j)}},eventOptionsRe:/^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|destroyable|vertical|horizontal|freezeEvent|priority)$/,addManagedListener:function(q,m,o,r,s,l){var n=this,p=n.managedListeners=n.managedListeners||[],k,j;if(typeof m!=="string"){j=arguments.length>4?s:m;s=m;for(m in s){if(s.hasOwnProperty(m)){k=s[m];if(!n.eventOptionsRe.test(m)){n.addManagedListener(q,m,k.fn||k,k.scope||s.scope||r,k.fn?k:j,true)}}}if(s&&s.destroyable){return new b(n,q,s)}}else{if(typeof o==="string"){r=r||n;o=Ext.resolveMethod(o,r)}if(o!==e){p.push({item:q,ename:m,fn:o,scope:r,options:s});q.on(m,o,r,s);if(!l&&s&&s.destroyable){return new b(n,q,m,o,r)}}}},removeManagedListener:function(q,l,o,r){var n=this,s,k,p,j,m;if(typeof l!=="string"){s=l;for(l in s){if(s.hasOwnProperty(l)){k=s[l];if(!n.eventOptionsRe.test(l)){n.removeManagedListener(q,l,k.fn||k,k.scope||s.scope||r)}}}}else{p=n.managedListeners?n.managedListeners.slice():[];if(typeof o==="string"){r=r||n;o=Ext.resolveMethod(o,r)}for(m=0,j=p.length;m0;if(!j&&k){k=this.events[k];if(k&&k.isEvent){return k.isSuspended()}}return j},suspendEvents:function(j){this.eventsSuspended+=1;if(j&&!this.eventQueue){this.eventQueue=[]}},suspendEvent:function(k){var j=arguments.length,n=this.events,m,o,l;for(m=0;m=532){a=120}else{a=12}a*=3}else{a=120}}return a}()),clickRe:/(dbl)?click/,safariKeys:{3:13,63234:37,63235:39,63232:38,63233:40,63276:33,63277:34,63272:46,63273:36,63275:35},btnMap:Ext.isIE9m?{1:0,4:1,2:2}:{0:0,1:1,2:2},isEvent:true,constructor:function(a,b){if(a){this.setEvent(a.browserEvent||a,b)}},setEvent:function(c,d){var b=this,a;if(c===b||(c&&c.browserEvent)){return c}b.browserEvent=c;if(c){a=c.button?b.btnMap[c.button]:(c.which?c.which-1:-1);if(b.clickRe.test(c.type)&&a==-1){a=0}b.type=c.type;b.button=a;b.shiftKey=c.shiftKey;b.ctrlKey=c.ctrlKey||c.metaKey||false;b.altKey=c.altKey;b.keyCode=c.keyCode;b.charCode=c.charCode;b.target=Ext.EventManager.getTarget(c);b.relatedTarget=Ext.EventManager.getRelatedTarget(c);b.currentTarget=c.currentTarget;b.xy=(d?b.getXY():null)}else{b.button=-1;b.shiftKey=false;b.ctrlKey=false;b.altKey=false;b.keyCode=0;b.charCode=0;b.target=null;b.xy=[0,0]}return b},clone:function(){return new this.self(this.browserEvent,this)},stopEvent:function(){this.stopPropagation();this.preventDefault()},preventDefault:function(){if(this.browserEvent){Ext.EventManager.preventDefault(this.browserEvent)}},stopPropagation:function(){var a=this.browserEvent;if(a){if(a.type=="mousedown"){Ext.EventManager.stoppedMouseDownEvent.fire(this)}Ext.EventManager.stopPropagation(a)}},getCharCode:function(){return this.charCode||this.keyCode},getKey:function(){return this.normalizeKey(this.keyCode||this.charCode)},normalizeKey:function(a){return Ext.isWebKit?(this.safariKeys[a]||a):a},getPageX:function(){return this.getX()},getPageY:function(){return this.getY()},getX:function(){return this.getXY()[0]},getY:function(){return this.getXY()[1]},getXY:function(){if(!this.xy){this.xy=Ext.EventManager.getPageXY(this.browserEvent)}return this.xy},getTarget:function(b,c,a){if(b){return Ext.fly(this.target).findParent(b,c,a)}return a?Ext.get(this.target):this.target},getRelatedTarget:function(b,c,a){if(b&&this.relatedTarget){return Ext.fly(this.relatedTarget).findParent(b,c,a)}return a?Ext.get(this.relatedTarget):this.relatedTarget},correctWheelDelta:function(c){var b=this.WHEEL_SCALE,a=Math.round(c/b);if(!a&&c){a=(c<0)?-1:1}return a},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)}},getWheelDelta:function(){var a=this.getWheelDeltas();return a.y},within:function(d,e,b){if(d){var c=e?this.getRelatedTarget():this.getTarget(),a;if(c){a=Ext.fly(d,"_internal").contains(c);if(!a&&b){a=c==Ext.getDom(d)}return a}}return false},isNavKeyPress:function(){var b=this,a=this.normalizeKey(b.keyCode);return(a>=33&&a<=40)||a==b.RETURN||a==b.TAB||a==b.ESC},isSpecialKey:function(){var a=this.normalizeKey(this.keyCode);return(this.type=="keypress"&&this.ctrlKey)||this.isNavKeyPress()||(a==this.BACKSPACE)||(a>=16&&a<=20)||(a>=44&&a<=46)},getPoint:function(){var a=this.getXY();return new Ext.util.Point(a[0],a[1])},hasModifier:function(){var a=this;return !!(a.ctrlKey||a.altKey||a.shiftKey||a.metaKey)},injectEvent:(function(){var d,e={},c;if(!Ext.isIE9m&&document.createEvent){d={createHtmlEvent:function(l,j,h,g){var k=l.createEvent("HTMLEvents");k.initEvent(j,h,g);return k},createMouseEvent:function(v,t,n,m,p,l,j,k,g,s,r,o,q){var h=v.createEvent("MouseEvents"),u=v.defaultView||window;if(h.initMouseEvent){h.initMouseEvent(t,n,m,u,p,l,j,l,j,k,g,s,r,o,q)}else{h=v.createEvent("UIEvents");h.initEvent(t,n,m);h.view=u;h.detail=p;h.screenX=l;h.screenY=j;h.clientX=l;h.clientY=j;h.ctrlKey=k;h.altKey=g;h.metaKey=r;h.shiftKey=s;h.button=o;h.relatedTarget=q}return h},createUIEvent:function(n,l,j,h,k){var m=n.createEvent("UIEvents"),g=n.defaultView||window;m.initUIEvent(l,j,h,g,k);return m},fireEvent:function(j,g,h){j.dispatchEvent(h)},fixTarget:function(g){if(g==window&&!g.dispatchEvent){return document}return g}}}else{if(document.createEventObject){c={0:1,1:4,2:2};d={createHtmlEvent:function(l,j,h,g){var k=l.createEventObject();k.bubbles=h;k.cancelable=g;return k},createMouseEvent:function(u,t,n,m,p,l,j,k,g,s,r,o,q){var h=u.createEventObject();h.bubbles=n;h.cancelable=m;h.detail=p;h.screenX=l;h.screenY=j;h.clientX=l;h.clientY=j;h.ctrlKey=k;h.altKey=g;h.shiftKey=s;h.metaKey=r;h.button=c[o]||o;h.relatedTarget=q;return h},createUIEvent:function(m,k,h,g,j){var l=m.createEventObject();l.bubbles=h;l.cancelable=g;return l},fireEvent:function(j,g,h){j.fireEvent("on"+g,h)},fixTarget:function(g){if(g==document){return document.documentElement}return 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(j,k){var h=k[0],g=k[1];e[j]=function(n,l){var m=d.createHtmlEvent(j,h,g);d.fireEvent(n,j,m)}});function b(j,h){var g=(j!="mousemove");return function(n,k){var m=k.getXY(),l=d.createMouseEvent(n.ownerDocument,j,true,g,h,m[0],m[1],k.ctrlKey,k.altKey,k.shiftKey,k.metaKey,k.button,k.relatedTarget);d.fireEvent(n,j,l)}}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(j,k){var h=k[0],g=k[1];e[j]=function(n,l){var m=d.createUIEvent(n.ownerDocument,j,h,g,1);d.fireEvent(n,j,m)}});if(!d){e={};d={fixTarget:Ext.identityFn}}function a(h,g){}return function(k){var j=this,h=e[j.type]||a,g=k?(k.dom||k):j.getTarget();g=d.fixTarget(g);h(g,j)}}())},1,0,0,0,0,0,[Ext,"EventObjectImpl"],function(){Ext.EventObject=new Ext.EventObjectImpl()}));(Ext.cmd.derive("Ext.dom.AbstractQuery",Ext.Base,{select:function(k,b){var h=[],d,g,e,c,a;b=b||document;if(typeof b=="string"){b=document.getElementById(b)}k=Ext.splitAndUnescape(k,",");for(g=0,c=k.length;g")}else{b.push(">");if((a=j.tpl)){a.applyOut(j.tplData,b)}if((a=j.html)){b.push(a)}if((a=j.cn||j.children)){h.generateMarkup(a,b)}c=h.closeTags;b.push(c[k]||(c[k]=""))}}}return b},generateStyles:function(g,c,e){var b=c||[],d,h;for(d in g){if(g.hasOwnProperty(d)){h=g[d];d=this.decamelizeName(d);if(e&&Ext.String.hasHtmlCharacters(h)){h=Ext.String.htmlEncode(h)}b.push(d,":",h,";")}}return c||b.join("")},markup:function(a){if(typeof a=="string"){return a}var b=this.generateMarkup(a,[]);return b.join("")},applyStyles:function(c,d){if(d){var b=0,a;c=Ext.fly(c,"_applyStyles");if(typeof d=="function"){d=d.call()}if(typeof d=="string"){d=Ext.util.Format.trim(d).split(this.styleSepRe);for(a=d.length;b "'+c+'"'},insertBefore:function(a,c,b){return this.doInsert(a,c,b,"beforebegin")},insertAfter:function(a,c,b){return this.doInsert(a,c,b,"afterend","nextSibling")},insertFirst:function(a,c,b){return this.doInsert(a,c,b,"afterbegin","firstChild")},append:function(a,c,b){return this.doInsert(a,c,b,"beforeend","",true)},overwrite:function(a,c,b){a=Ext.getDom(a);a.innerHTML=this.markup(c);return b?Ext.get(a.firstChild):a.firstChild},doInsert:function(d,g,e,h,c,a){var b=this.insertHtml(h,Ext.getDom(d),this.markup(g));return e?Ext.get(b,true):b}},0,0,0,0,0,0,[Ext.dom,"AbstractHelper"],0));Ext.define("Ext.dom.AbstractElement_static",{override:"Ext.dom.AbstractElement",inheritableStatics:{unitRe:/\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,camelRe:/(-[a-z])/gi,msRe:/^-ms-/,cssRe:/([a-z0-9\-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*)?;?/gi,opacityRe:/alpha\(opacity=(.*)\)/i,propertyCache:{},defaultUnit:"px",borders:{l:"border-left-width",r:"border-right-width",t:"border-top-width",b:"border-bottom-width"},paddings:{l:"padding-left",r:"padding-right",t:"padding-top",b:"padding-bottom"},margins:{l:"margin-left",r:"margin-right",t:"margin-top",b:"margin-bottom"},addUnits:function(b,a){if(typeof b=="number"){return b+(a||this.defaultUnit||"px")}if(b===""||b=="auto"||b===undefined||b===null){return b||""}if(!this.unitRe.test(b)){return b||""}return b},isAncestor:function(b,d){var a=false;b=Ext.getDom(b);d=Ext.getDom(d);if(b&&d){if(b.contains){return b.contains(d)}else{if(b.compareDocumentPosition){return !!(b.compareDocumentPosition(d)&16)}else{while((d=d.parentNode)){a=d==b||a}}}}return a},parseBox:function(c){c=c||0;var a=typeof c,d,b;if(a==="number"){return{top:c,right:c,bottom:c,left:c}}else{if(a!=="string"){return c}}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:parseFloat(d[0])||0,right:parseFloat(d[1])||0,bottom:parseFloat(d[2])||0,left:parseFloat(d[3])||0}},unitizeBox:function(g,e){var d=this.addUnits,c=this.parseBox(g);return d(c.top,e)+" "+d(c.right,e)+" "+d(c.bottom,e)+" "+d(c.left,e)},camelReplaceFn:function(b,c){return c.charAt(1).toUpperCase()},normalize:function(a){if(a=="float"){a=Ext.supports.Float?"cssFloat":"styleFloat"}return this.propertyCache[a]||(this.propertyCache[a]=a.replace(this.msRe,"ms-").replace(this.camelRe,this.camelReplaceFn))},getDocumentHeight:function(){return Math.max(!Ext.isStrict?document.body.scrollHeight:document.documentElement.scrollHeight,this.getViewportHeight())},getDocumentWidth:function(){return Math.max(!Ext.isStrict?document.body.scrollWidth:document.documentElement.scrollWidth,this.getViewportWidth())},getViewportHeight:function(){return window.innerHeight},getViewportWidth:function(){return window.innerWidth},getViewSize:function(){return{width:window.innerWidth,height:window.innerHeight}},getOrientation:function(){if(Ext.supports.OrientationChange){return(window.orientation==0)?"portrait":"landscape"}return(window.innerHeight>window.innerWidth)?"portrait":"landscape"},fromPoint:function(a,b){return Ext.get(document.elementFromPoint(a,b))},parseStyles:function(c){var a={},b=this.cssRe,d;if(c){b.lastIndex=0;while((d=b.exec(c))){a[d[1]]=d[2]||""}}return a}}},function(){var c=document,b=null,a=c.compatMode=="CSS1Compat";if(!("activeElement" in c)&&c.addEventListener){c.addEventListener("focus",function(e){if(e&&e.target){b=(e.target==c)?null:e.target}},true)}function d(g,h,e){return function(){g.selectionStart=h;g.selectionEnd=e}}this.addInheritableStatics({getActiveElement:function(){var h;try{h=c.activeElement}catch(g){}h=h||b;if(!h){h=b=document.body}return h},getRightMarginFixCleaner:function(l){var h=Ext.supports,j=h.DisplayChangeInputSelectionBug,k=h.DisplayChangeTextAreaSelectionBug,m,e,n,g;if(j||k){m=c.activeElement||b;e=m&&m.tagName;if((k&&e=="TEXTAREA")||(j&&e=="INPUT"&&m.type=="text")){if(Ext.dom.Element.isAncestor(l,m)){n=m.selectionStart;g=m.selectionEnd;if(Ext.isNumber(n)&&Ext.isNumber(g)){return d(m,n,g)}}}}return Ext.emptyFn},getViewWidth:function(e){return e?Ext.dom.Element.getDocumentWidth():Ext.dom.Element.getViewportWidth()},getViewHeight:function(e){return e?Ext.dom.Element.getDocumentHeight():Ext.dom.Element.getViewportHeight()},getDocumentHeight:function(){return Math.max(!a?c.body.scrollHeight:c.documentElement.scrollHeight,Ext.dom.Element.getViewportHeight())},getDocumentWidth:function(){return Math.max(!a?c.body.scrollWidth:c.documentElement.scrollWidth,Ext.dom.Element.getViewportWidth())},getViewportHeight:function(){return Ext.isIE9m?(Ext.isStrict?c.documentElement.clientHeight:c.body.clientHeight):self.innerHeight},getViewportWidth:function(){return(!Ext.isStrict&&!Ext.isOpera)?c.body.clientWidth:Ext.isIE9m?c.documentElement.clientWidth:self.innerWidth},serializeForm:function(j){var k=j.elements||(document.forms[j]||Ext.getDom(j)).elements,u=false,t=encodeURIComponent,n="",m=k.length,p,g,s,w,v,q,l,r,h;for(q=0;q0?t:0},getWidth:function(t){var v=this.dom,u=t?(v.clientWidth-this.getPadding("lr")):v.offsetWidth;return u>0?u:0},setWidth:function(t){var u=this;u.dom.style.width=d.addUnits(t);return u},setHeight:function(t){var u=this;u.dom.style.height=d.addUnits(t);return u},getBorderWidth:function(t){return this.addStyles(t,m)},getPadding:function(t){return this.addStyles(t,g)},margins:o,applyStyles:function(v){if(v){var u,t,w=this.dom;if(typeof v=="function"){v=v.call()}if(typeof v=="string"){v=Ext.util.Format.trim(v).split(/\s*(?::|;)\s*/);for(u=0,t=v.length;u'+u+""):""});B=z.getSize();w.mask=D;if(v===document.body){B.height=window.innerHeight;if(z.orientationHandler){Ext.EventManager.unOrientationChange(z.orientationHandler,z)}z.orientationHandler=function(){B=z.getSize();B.height=window.innerHeight;D.setSize(B)};Ext.EventManager.onOrientationChange(z.orientationHandler,z)}D.setSize(B);if(Ext.is.iPad){Ext.repaint()}},unmask:function(){var u=this,w=(u.$cache||u.getCache()).data,t=w.mask,v=Ext.baseCSSPrefix;if(t){t.remove();delete w.mask}u.removeCls([v+"masked",v+"masked-relative"]);if(u.dom===document.body){Ext.EventManager.unOrientationChange(u.orientationHandler,u);delete u.orientationHandler}}});Ext.onReady(function(){var B=Ext.supports,t,z,x,u,A;function y(G,D,F,C){var E=C[this.name]||"";return c.test(E)?"transparent":E}function w(I,F,H,E){var C=E.marginRight,D,G;if(C!="0px"){D=I.style;G=D.display;D.display="inline-block";C=(H?E:I.ownerDocument.defaultView.getComputedStyle(I,null)).marginRight;D.display=G}return C}function v(J,G,I,F){var C=F.marginRight,E,D,H;if(C!="0px"){E=J.style;D=d.getRightMarginFixCleaner(J);H=E.display;E.display="inline-block";C=(I?F:J.ownerDocument.defaultView.getComputedStyle(J,"")).marginRight;E.display=H;D()}return C}t=d.prototype.styleHooks;if(B.init){B.init()}if(!B.RightMargin){t.marginRight=t["margin-right"]={name:"marginRight",get:(B.DisplayChangeInputSelectionBug||B.DisplayChangeTextAreaSelectionBug)?v:w}}if(!B.TransparentColor){z=["background-color","border-color","color","outline-color"];for(x=z.length;x--;){u=z[x];A=d.normalize(u);t[u]=t[A]={name:A,get:y}}}})});Ext.define("Ext.dom.AbstractElement_traversal",{override:"Ext.dom.AbstractElement",findParent:function(h,b,a){var e=this.dom,c=document.documentElement,g=0,d;b=b||50;if(isNaN(b)){d=Ext.getDom(b);b=Number.MAX_VALUE}while(e&&e.nodeType==1&&g "+a,c.dom);return b?d:Ext.get(d)},parent:function(a,b){return this.matchNode("parentNode","parentNode",a,b)},next:function(a,b){return this.matchNode("nextSibling","nextSibling",a,b)},prev:function(a,b){return this.matchNode("previousSibling","previousSibling",a,b)},first:function(a,b){return this.matchNode("nextSibling","firstChild",a,b)},last:function(a,b){return this.matchNode("previousSibling","lastChild",a,b)},matchNode:function(b,e,a,c){if(!this.dom){return null}var d=this.dom[e];while(d){if(d.nodeType==1&&(!a||Ext.DomQuery.is(d,a))){return !c?Ext.get(d):d}d=d[b]}return null},isAncestor:function(a){return this.self.isAncestor.call(this.self,this.dom,a)}});(Ext.cmd.derive("Ext.dom.AbstractElement",Ext.Base,{trimRe:/^\s+|\s+$/g,whitespaceRe:/\s/,inheritableStatics:{trimRe:/^\s+|\s+$/g,whitespaceRe:/\s/,get:function(c){var j=this,k=window.document,d=Ext.dom.Element,h,b,g,e,a;if(!c){return null}if(c.isFly){c=c.dom}if(typeof c=="string"){if(c==Ext.windowId){return d.get(window)}else{if(c==Ext.documentId){return d.get(k)}}h=Ext.cache[c];if(h&&h.skipGarbageCollection){g=h.el;return g}if(!(e=k.getElementById(c))){return null}if(h&&h.el){g=Ext.updateCacheEntry(h,e).el}else{g=new d(e,!!h)}return g}else{if(c.tagName){if(!(a=c.id)){a=Ext.id(c)}h=Ext.cache[a];if(h&&h.el){g=Ext.updateCacheEntry(h,c).el}else{g=new d(c,!!h)}return g}else{if(c instanceof j){if(c!=j.docEl&&c!=j.winEl){a=c.id;h=Ext.cache[a];if(h){Ext.updateCacheEntry(h,k.getElementById(a)||c.dom)}}return c}else{if(c.isComposite){return c}else{if(Ext.isArray(c)){return j.select(c)}else{if(c===k){if(!j.docEl){b=j.docEl=Ext.Object.chain(d.prototype);b.dom=k;b.el=b;b.id=Ext.id(k);j.addToCache(b)}return j.docEl}else{if(c===window){if(!j.winEl){j.winEl=Ext.Object.chain(d.prototype);j.winEl.dom=window;j.winEl.id=Ext.id(window);j.addToCache(j.winEl)}return j.winEl}}}}}}}return null},addToCache:function(a,b){if(a){Ext.addCacheEntry(b,a)}return a},addMethods:function(){this.override.apply(this,arguments)},mergeClsList:function(){var m,k={},g,b,d,h,c,n=[],e=false,a=this.trimRe,l=this.whitespaceRe;for(g=0,b=arguments.length;g",o=""+h,l=c+"",e=""+o,q=document.createElement("div"),n=["BeforeBegin","previousSibling"],k=["AfterEnd","nextSibling"],d={beforebegin:n,afterend:k},g={beforebegin:n,afterend:k,afterbegin:["AfterBegin","firstChild"],beforeend:["BeforeEnd","lastChild"]};return{tableRe:/^(?:table|thead|tbody|tr|td)$/i,tableElRe:/td|tr|tbody|thead/i,useDom:false,createDom:function(r,x){var s,A=document,v,y,t,z,w,u;if(Ext.isArray(r)){s=A.createDocumentFragment();for(w=0,u=r.length;w1){for(;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,"XTemplateParser"],0));(Ext.cmd.derive("Ext.XTemplateCompiler",Ext.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(c,d){var b=this,a="f"+b.definitions.length;b.definitions.push("function "+a+"("+b.fnArgs+") {"," try { with(values) {"," "+c," }} catch(e) {","}","}");b.body.push(a+b.callFn+"\n")},addFn:function(a){var c=this,b="f"+c.definitions.length;if(a==="."){c.definitions.push("function "+b+"("+c.fnArgs+") {"," return values","}")}else{if(a===".."){c.definitions.push("function "+b+"("+c.fnArgs+") {"," return parent","}")}else{c.definitions.push("function "+b+"("+c.fnArgs+") {"," try { with(values) {"," return("+a+")"," }} catch(e) {","}","}")}}return b},parseTag:function(b){var h=this,a=h.tagRe.exec(b),e,j,d,g,c;if(!a){return null}e=a[1];j=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(j&&h.useFormat){d=d?","+d:"";if(j.substr(0,5)!="this."){j="fm."+j+"("}else{j+="("}}else{return c}return j+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,"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,{emptyObj:{},apply:function(a,b){return this.applyOut(a,[],b).join("")},applyOut:function(a,b,d){var g=this,c;if(!g.fn){c=new Ext.XTemplateCompiler({useFormat:g.disableFormats!==true,definitions:g.definitions});g.fn=c.compile(g.html)}try{g.fn(b,a,d||g.emptyObj,1,1)}catch(h){}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.ns("Ext.core");Ext.dom.Query=Ext.core.DomQuery=Ext.DomQuery=(function(){var DQ,doc=document,cache,simpleCache,valueCache,useClassList=!!doc.documentElement.classList,useElementPointer=!!doc.documentElement.firstElementChild,useChildrenCollection=(function(){var d=doc.createElement("div");d.innerHTML="text";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.escapeId(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){if(!valueCache){DQ._valueCache=valueCache=new Ext.util.LruCache({maxSize:200})}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);n=n[0]?n[0]:n;if(typeof n.normalize=="function"){n.normalize()}v=(n&&n.firstChild?n.firstChild.nodeValue:null);return((v===null||v===undefined||v==="")?defaultValue: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,"");if(!simpleCache){DQ._simpleCache=simpleCache=new Ext.util.LruCache({maxSize:200})}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}")}};'}],operators:{"=":function(a,v){return a==v},"!=":function(a,v){return a!=v},"^=":function(a,v){return a&&a.substr(0,v.length)==v},"$=":function(a,v){return a&&a.substr(a.length-v.length)==v},"*=":function(a,v){return a&&a.indexOf(v)!==-1},"%=":function(a,v){return(a%v)===0},"|=":function(a,v){return a&&(a==v||a.substr(0,v.length+1)==v+"-")},"~=":function(a,v){return a&&(" "+a+" ").indexOf(" "+v+" ")!=-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(;ia.clientHeight||a.scrollWidth>a.clientWidth},getScroll:function(){var c=this,h=c.dom,g=document,a=g.body,b=g.documentElement,e,d;if(h===g||h===a){e=b.scrollLeft||(a?a.scrollLeft:0);d=b.scrollTop||(a?a.scrollTop:0)}else{e=h.scrollLeft;d=h.scrollTop}return{left:e,top:d}},getScrollLeft:function(){var b=this.dom,a=document;if(b===a||b===a.body){return this.getScroll().left}else{return b.scrollLeft}},getScrollTop:function(){var b=this.dom,a=document;if(b===a||b===a.body){return this.getScroll().top}else{return b.scrollTop}},setScrollLeft:function(a){this.dom.scrollLeft=a;return this},setScrollTop:function(a){this.dom.scrollTop=a;return this},scrollBy:function(b,a,c){var d=this,e=d.dom;if(b.length){c=a;a=b[1];b=b[0]}else{if(typeof b!="number"){c=a;a=b.y;b=b.x}}if(b){d.scrollTo("left",d.constrainScrollLeft(e.scrollLeft+b),c)}if(a){d.scrollTo("top",d.constrainScrollTop(e.scrollTop+a),c)}return d},scrollTo:function(c,e,a){var g=/top/i.test(c),d=this,j=g?"scrollTop":"scrollLeft",h=d.dom,b;if(!a||!d.anim){h[j]=e;h[j]=e}else{b={to:{}};b.to[j]=e;if(Ext.isObject(a)){Ext.applyIf(b,a)}d.animate(b)}return d},scrollIntoView:function(b,e,c,h){var n=this,l=n.dom,j=n.getOffsetsTo(b=Ext.getDom(b)||Ext.getBody().dom),g=j[0]+b.scrollLeft,o=j[1]+b.scrollTop,a=o+l.offsetHeight,p=g+l.offsetWidth,s=b.clientHeight,r=parseInt(b.scrollTop,10),d=parseInt(b.scrollLeft,10),k=r+s,q=d+b.clientWidth,m;if(h){if(c){c=Ext.apply({listeners:{afteranimate:function(){n.scrollChildFly.attach(l).highlight()}}},c)}else{n.scrollChildFly.attach(l).highlight()}}if(l.offsetHeight>s||ok){m=a-s}}if(m!=null){n.scrollChildFly.attach(b).scrollTo("top",m,c)}if(e!==false){m=null;if(l.offsetWidth>b.clientWidth||gq){m=p-b.clientWidth}}if(m!=null){n.scrollChildFly.attach(b).scrollTo("left",m,c)}}return n},scrollChildIntoView:function(b,a){this.scrollChildFly.attach(Ext.getDom(b)).scrollIntoView(this,a)},scroll:function(k,a,c){if(!this.isScrollable()){return false}k=k.charAt(0);var j=this,e=j.dom,h=k==="r"||k==="l"?"left":"top",b=false,d,g;if(k==="l"||k==="t"||k==="u"){a=-a}if(h==="left"){d=e.scrollLeft;g=j.constrainScrollLeft(d+a)}else{d=e.scrollTop;g=j.constrainScrollTop(d+a)}if(g!==d){this.scrollTo(h,g,c);b=true}return b},constrainScrollLeft:function(a){var b=this.dom;return Math.max(Math.min(a,b.scrollWidth-b.clientWidth),0)},constrainScrollTop:function(a){var b=this.dom;return Math.max(Math.min(a,b.scrollHeight-b.clientHeight),0)}},function(){this.prototype.scrollChildFly=new this.Fly();this.prototype.scrolltoFly=new this.Fly()});Ext.define("Ext.dom.Element_style",{override:"Ext.dom.Element"},function(){var s=this,o=document.defaultView,q=/table-row|table-.*-group/,a="_internal",u="hidden",r="height",h="width",e="isClipped",l="overflow",n="overflow-x",m="overflow-y",v="originalClip",b=/#document|body/i,w,g,p,d,t,j,x;if(!o||!o.getComputedStyle){s.prototype.getStyle=function(C,B){var O=this,J=O.dom,M=typeof C!="string",k=O.styleHooks,z=C,A=z,I=1,E=B,N,F,y,D,H,K,G;if(M){y={};z=A[0];G=0;if(!(I=A.length)){return y}}if(!J||J.documentElement){return y||""}F=J.style;if(B){K=F}else{K=J.currentStyle;if(!K){E=true;K=F}}do{D=k[z];if(!D){k[z]=D={name:s.normalize(z)}}if(D.get){H=D.get(J,O,E,K)}else{N=D.name;if(D.canThrow){try{H=K[N]}catch(L){H=""}}else{H=K?K[N]:""}}if(!M){return H}y[z]=H;z=A[++G]}while(G0&&C<0.5){k++}}}if(A){k-=z.getBorderWidth("tb")+z.getPadding("tb")}return(k<0)?0:k},getWidth:function(k,C){var A=this,D=A.dom,B=A.isStyle("display","none"),z,y,E;if(B){return 0}if(Ext.supports.BoundingClientRect){z=D.getBoundingClientRect();y=(A.vertical&&!Ext.isIE9&&!Ext.supports.RotatedBoundingClientRect)?(z.bottom-z.top):(z.right-z.left);y=C?y:Math.ceil(y)}else{y=D.offsetWidth}if(Ext.supports.Direct2DBug&&!A.vertical){E=A.adjustDirect2DDimension(h);if(C){y+=E}else{if(E>0&&E<0.5){y++}}}if(k){y-=A.getBorderWidth("lr")+A.getPadding("lr")}return(y<0)?0:y},setWidth:function(y,k){var z=this;y=z.adjustWidth(y);if(!k||!z.anim){z.dom.style.width=z.addUnits(y)}else{if(!Ext.isObject(k)){k={}}z.animate(Ext.applyIf({to:{width:y}},k))}return z},setHeight:function(k,y){var z=this;k=z.adjustHeight(k);if(!y||!z.anim){z.dom.style.height=z.addUnits(k)}else{if(!Ext.isObject(y)){y={}}z.animate(Ext.applyIf({to:{height:k}},y))}return z},applyStyles:function(k){Ext.DomHelper.applyStyles(this.dom,k);return this},setSize:function(z,k,y){var A=this;if(Ext.isObject(z)){y=k;k=z.height;z=z.width}z=A.adjustWidth(z);k=A.adjustHeight(k);if(!y||!A.anim){A.dom.style.width=A.addUnits(z);A.dom.style.height=A.addUnits(k)}else{if(y===true){y={}}A.animate(Ext.applyIf({to:{width:z,height:k}},y))}return A},getViewSize:function(){var z=this,A=z.dom,y=b.test(A.nodeName),k;if(y){k={width:s.getViewWidth(),height:s.getViewHeight()}}else{k={width:A.clientWidth,height:A.clientHeight}}return k},getSize:function(k){return{width:this.getWidth(k),height:this.getHeight(k)}},adjustWidth:function(k){var y=this,z=(typeof k=="number");if(z&&y.autoBoxAdjust&&!y.isBorderBox()){k-=(y.getBorderWidth("lr")+y.getPadding("lr"))}return(z&&k<0)?0:k},adjustHeight:function(k){var y=this,z=(typeof k=="number");if(z&&y.autoBoxAdjust&&!y.isBorderBox()){k-=(y.getBorderWidth("tb")+y.getPadding("tb"))}return(z&&k<0)?0:k},getColor:function(y,z,E){var B=this.getStyle(y),A=E||E===""?E:"#",D,k,C=0;if(!B||(/transparent|inherit/.test(B))){return z}if(/^r/.test(B)){B=B.slice(4,B.length-1).split(",");k=B.length;for(;C5?A.toLowerCase():z)},setOpacity:function(y,k){var z=this;if(!z.dom){return z}if(!k||!z.anim){z.setStyle("opacity",y)}else{if(typeof k!="object"){k={duration:350,easing:"ease-in"}}z.animate(Ext.applyIf({to:{opacity:y}},k))}return z},clearOpacity:function(){return this.setOpacity("")},adjustDirect2DDimension:function(z){var E=this,y=E.dom,C=E.getStyle("display"),B=y.style.display,F=y.style.position,D=z===h?0:1,k=y.currentStyle,A;if(C==="inline"){y.style.display="inline-block"}y.style.position=C.match(q)?"absolute":"static";A=(parseFloat(k[z])||parseFloat(k.msTransformOrigin.split(" ")[D])*2)%1;y.style.position=F;if(C==="inline"){y.style.display=B}return A},clip:function(){var y=this,z=(y.$cache||y.getCache()).data,k;if(!z[e]){z[e]=true;k=y.getStyle([l,n,m]);z[v]={o:k[l],x:k[n],y:k[m]};y.setStyle(l,u);y.setStyle(n,u);y.setStyle(m,u)}return y},unclip:function(){var y=this,z=(y.$cache||y.getCache()).data,k;if(z[e]){z[e]=false;k=z[v];if(k.o){y.setStyle(l,k.o)}if(k.x){y.setStyle(n,k.x)}if(k.y){y.setStyle(m,k.y)}}return y},boxWrap:function(k){k=k||Ext.baseCSSPrefix+"box";var y=Ext.get(this.insertHtml("beforeBegin",""));Ext.DomQuery.selectNode("."+k+"-mc",y.dom).appendChild(this.dom);return y},getComputedHeight:function(){var y=this,k=Math.max(y.dom.offsetHeight,y.dom.clientHeight);if(!k){k=parseFloat(y.getStyle(r))||0;if(!y.isBorderBox()){k+=y.getFrameWidth("tb")}}return k},getComputedWidth:function(){var y=this,k=Math.max(y.dom.offsetWidth,y.dom.clientWidth);if(!k){k=parseFloat(y.getStyle(h))||0;if(!y.isBorderBox()){k+=y.getFrameWidth("lr")}}return k},getFrameWidth:function(y,k){return(k&&this.isBorderBox())?0:(this.getPadding(y)+this.getBorderWidth(y))},addClsOnOver:function(z,C,y){var A=this,B=A.dom,k=Ext.isFunction(C);A.hover(function(){if(k&&C.call(y||A,A)===false){return}Ext.fly(B,a).addCls(z)},function(){Ext.fly(B,a).removeCls(z)});return A},addClsOnFocus:function(z,C,y){var A=this,B=A.dom,k=Ext.isFunction(C);A.on("focus",function(){if(k&&C.call(y||A,A)===false){return false}Ext.fly(B,a).addCls(z)});A.on("blur",function(){Ext.fly(B,a).removeCls(z)});return A},addClsOnClick:function(z,C,y){var A=this,B=A.dom,k=Ext.isFunction(C);A.on("mousedown",function(){if(k&&C.call(y||A,A)===false){return false}Ext.fly(B,a).addCls(z);var E=Ext.getDoc(),D=function(){Ext.fly(B,a).removeCls(z);E.removeListener("mouseup",D)};E.on("mouseup",D)});return A},getStyleSize:function(){var B=this,C=this.dom,y=b.test(C.nodeName),A,k,z;if(y){return{width:s.getViewWidth(),height:s.getViewHeight()}}A=B.getStyle([r,h],true);if(A.width&&A.width!="auto"){k=parseFloat(A.width);if(B.isBorderBox()){k-=B.getFrameWidth("lr")}}if(A.height&&A.height!="auto"){z=parseFloat(A.height);if(B.isBorderBox()){z-=B.getFrameWidth("tb")}}return{width:k||B.getWidth(true),height:z||B.getHeight(true)}},statics:{selectableCls:Ext.baseCSSPrefix+"selectable",unselectableCls:Ext.baseCSSPrefix+"unselectable"},selectable:function(){var k=this;k.dom.unselectable="";k.removeCls(s.unselectableCls);k.addCls(s.selectableCls);return k},unselectable:function(){var k=this;if(Ext.isOpera){k.dom.unselectable="on"}k.removeCls(s.selectableCls);k.addCls(s.unselectableCls);return k},setVertical:function(B,y){var A=this,z=s.prototype,k;A.vertical=true;if(y){A.addCls(A.verticalCls=y)}A.setWidth=z.setHeight;A.setHeight=z.setWidth;if(!Ext.isIE9m){A.getWidth=z.getHeight;A.getHeight=z.getWidth}A.styleHooks=(B===270)?s.prototype.verticalStyleHooks270:s.prototype.verticalStyleHooks90},setHorizontal:function(){var y=this,k=y.verticalCls;delete y.vertical;if(k){delete y.verticalCls;y.removeCls(k)}delete y.setWidth;delete y.setHeight;if(!Ext.isIE9m){delete y.getWidth;delete y.getHeight}delete y.styleHooks}});s.prototype.styleHooks=w=Ext.dom.AbstractElement.prototype.styleHooks;s.prototype.verticalStyleHooks90=g=Ext.Object.chain(s.prototype.styleHooks);s.prototype.verticalStyleHooks270=p=Ext.Object.chain(s.prototype.styleHooks);g.width={name:"height"};g.height={name:"width"};g["margin-top"]={name:"marginLeft"};g["margin-right"]={name:"marginTop"};g["margin-bottom"]={name:"marginRight"};g["margin-left"]={name:"marginBottom"};g["padding-top"]={name:"paddingLeft"};g["padding-right"]={name:"paddingTop"};g["padding-bottom"]={name:"paddingRight"};g["padding-left"]={name:"paddingBottom"};g["border-top"]={name:"borderLeft"};g["border-right"]={name:"borderTop"};g["border-bottom"]={name:"borderRight"};g["border-left"]={name:"borderBottom"};p.width={name:"height"};p.height={name:"width"};p["margin-top"]={name:"marginRight"};p["margin-right"]={name:"marginBottom"};p["margin-bottom"]={name:"marginLeft"};p["margin-left"]={name:"marginTop"};p["padding-top"]={name:"paddingRight"};p["padding-right"]={name:"paddingBottom"};p["padding-bottom"]={name:"paddingLeft"};p["padding-left"]={name:"paddingTop"};p["border-top"]={name:"borderRight"};p["border-right"]={name:"borderBottom"};p["border-bottom"]={name:"borderLeft"};p["border-left"]={name:"borderTop"};if(Ext.isIE7m){w.fontSize=w["font-size"]={name:"fontSize",canThrow:true};w.fontStyle=w["font-style"]={name:"fontStyle",canThrow:true};w.fontFamily=w["font-family"]={name:"fontFamily",canThrow:true}}if(Ext.isIEQuirks||Ext.isIE&&Ext.ieVersion<=8){function c(A,y,z,k){if(k[this.styleName]=="none"){return"0px"}return k[this.name]}d=["Top","Right","Bottom","Left"];t=d.length;while(t--){j=d[t];x="border"+j+"Width";w["border-"+j.toLowerCase()+"-width"]=w[x]={name:x,styleName:"border"+j+"Style",get:c}}}Ext.getDoc().on("selectstart",function(B,D){var C=document.documentElement,A=s.selectableCls,z=s.unselectableCls,k=D&&D.tagName;k=k&&k.toLowerCase();if(k==="input"||k==="textarea"){return}while(D&&D.nodeType===1&&D!==C){var y=Ext.fly(D);if(y.hasCls(A)){return}if(y.hasCls(z)){B.stopEvent();return}D=D.parentNode}})});Ext.onReady(function(){var c=/alpha\(opacity=(.*)\)/i,b=/^\s+|\s+$/g,a=Ext.dom.Element.prototype.styleHooks;a.opacity={name:"opacity",afterSet:function(g,e,d){if(d.isLayer){d.onOpacitySet(e)}}};if(!Ext.supports.Opacity&&Ext.isIE){Ext.apply(a.opacity,{get:function(h){var g=h.style.filter,e,d;if(g.match){e=g.match(c);if(e){d=parseFloat(e[1]);if(!isNaN(d)){return d?d/100:0}}}return 1},set:function(h,e){var d=h.style,g=d.filter.replace(c,"").replace(b,"");d.zoom=1;if(typeof(e)=="number"&&e>=0&&e<1){e*=100;d.filter=g+(g.length?" ":"")+"alpha(opacity="+e+")"}else{d.filter=g}}})}});(Ext.cmd.derive("Ext.util.Positionable",Ext.Base,{_positionTopLeft:["position","top","left"],_alignRe:/^([a-z]+)-([a-z]+)(\?)?$/,afterSetPosition:Ext.emptyFn,adjustForConstraints:function(c,b){var a=this.getConstrainVector(b,c);if(a){c[0]+=a[0];c[1]+=a[1]}return c},alignTo:function(c,a,g,b){var e=this,d=e.el;return e.setXY(e.getAlignToXY(c,a,g),d.anim&&!!b?d.anim(b):false)},anchorTo:function(h,e,b,a,k,l){var g=this,j=!Ext.isEmpty(k),c=function(){g.alignTo(h,e,b,a);Ext.callback(l,g)},d=g.getAnchor();g.removeAnchor();Ext.apply(d,{fn:c,scroll:j});Ext.EventManager.onWindowResize(c,null);if(j){Ext.EventManager.on(window,"scroll",c,null,{buffer:!isNaN(k)?k:50})}c();return g},calculateAnchorXY:function(g,j,h,d){var k=this,c=k.el,l=document,e=c.dom==l.body||c.dom==l,m=Math.round,n,b,a;g=(g||"tl").toLowerCase();d=d||{};b=d.width||(e?Ext.Element.getViewWidth():k.getWidth());a=d.height||(e?Ext.Element.getViewHeight():k.getHeight());switch(g){case"tl":n=[0,0];break;case"bl":n=[0,a];break;case"tr":n=[b,0];break;case"c":n=[m(b*0.5),m(a*0.5)];break;case"t":n=[m(b*0.5),0];break;case"l":n=[0,m(a*0.5)];break;case"r":n=[b,m(a*0.5)];break;case"b":n=[m(b*0.5),a];break;case"tc":n=[m(b*0.5),0];break;case"bc":n=[m(b*0.5),a];break;case"br":n=[b,a]}return[n[0]+j,n[1]+h]},convertPositionSpec:Ext.identityFn,getAlignToXY:function(k,z,e){var A=this,d,w,a,h,s,g,t,u,q,r,v,p,o,b,c,j,m,n,l;k=Ext.get(k.el||k);if(!k||!k.dom){}e=e||[0,0];z=(!z||z=="?"?"tl-bl?":(!(/-/).test(z)&&z!==""?"tl-"+z:z||"tl-bl")).toLowerCase();z=A.convertPositionSpec(z);a=z.match(A._alignRe);p=a[1];o=a[2];v=!!a[3];h=A.getAnchorXY(p,true);s=A.getAnchorToXY(k,o,false);n=s[0]-h[0]+e[0];l=s[1]-h[1]+e[1];if(v){d=A.constrainTo||A.container||A.el.parent();d=Ext.get(d.el||d);w=d.getViewRegion();w.right=w.left+d.el.dom.clientWidth;g=A.getWidth();t=A.getHeight();u=k.getRegion();b=p.charAt(0);c=p.charAt(p.length-1);j=o.charAt(0);m=o.charAt(o.length-1);q=(n=u.left)&&((b=="t"&&j=="b")||(b=="b"&&j=="t"));r=(l=u.top)&&((c=="r"&&m=="l")||(c=="l"&&m=="r"));if(n+g>w.right){if(r){n=u.left-g;r=false}else{n=w.right-g}}if(nw.bottom){if(q){l=u.top-t;q=false}else{l=w.bottom-t}}if(lh.right){k=true;d[0]=(h.right-a.right)}if(a.left+d[0]h.bottom){k=true;d[1]=(h.bottom-a.bottom)}if(a.top+d[1]0||q.scrollLeft>0){t[++p]=q}}return t};return{alternateClassName:["Ext.Element","Ext.core.Element"],isElement:true,tableTagRe:/^(?:tr|td|table|tbody)$/i,addUnits:function(){return a.addUnits.apply(a,arguments)},focus:function(s,r){var p=this;r=r||p.dom;try{if(Number(s)){Ext.defer(p.focus,s,p,[null,r])}else{Ext.globalEvents.fireEvent("beforefocus",r);r.focus()}}catch(q){}return p},blur:function(){var p=this,r=p.dom;if(r!==document.body){try{r.blur()}catch(q){}return p}else{return p.focus(undefined,r)}},isBorderBox:function(){var p=Ext.isBorderBox;if(Ext.isIE7m&&!p){p=((this.dom.tagName||"").toLowerCase() in d)}return p},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},getAttributeNS:function(q,p){return this.getAttribute(p,q)},getAttribute:(Ext.isIE&&!(Ext.isIE9p&&g.documentMode>=9))?function(p,r){var s=this.dom,q;if(r){q=typeof s[r+":"+p];if(q!="undefined"&&q!="unknown"){return s[r+":"+p]||null}return null}if(p==="for"){p="htmlFor"}return s[p]||null}:function(p,q){var r=this.dom;if(q){return r.getAttributeNS(q,p)||r.getAttribute(q+":"+p)}return r.getAttribute(p)||r[p]||null},cacheScrollValues:function(){var t=this,s,r,q,u=[],p=function(){for(q=0;q]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,replaceScriptTagRe=/(?:)((\n|\r|.)*?)(?:<\/script>)/ig,srcRe=/\ssrc=([\'\"])(.*?)\1/i,typeRe=/\stype=([\'\"])(.*?)\1/i,useDocForId=!Ext.isIE8m,internalFly;Element.boxMarkup=['",'",'"].join("");function garbageCollect(){if(!Ext.enableGarbageCollector){clearInterval(Element.collectorThreadId)}else{var eid,d,o,t;for(eid in EC){if(!EC.hasOwnProperty(eid)){continue}o=EC[eid];if(o.skipGarbageCollection){continue}d=o.dom;if(d&&(!d.parentNode||(!d.offsetParent&&!Ext.getElementById(eid)))){if(Ext.enableListenerCollection){Ext.EventManager.removeAll(d)}delete EC[eid]}}if(Ext.isIE){t={};for(eid in EC){if(!EC.hasOwnProperty(eid)){continue}t[eid]=EC[eid]}EC=Ext.cache=t}}}Element.collectorThreadId=setInterval(garbageCollect,30000);Element.addMethods({monitorMouseLeave:function(delay,handler,scope){var me=this,timer,listeners={mouseleave:function(e){timer=setTimeout(Ext.Function.bind(handler,scope||me,[e]),delay)},mouseenter:function(){clearTimeout(timer)},freezeEvent:true};me.on(listeners);return listeners},swallowEvent:function(eventName,preventDefault){var me=this,e,eLen,fn=function(e){e.stopPropagation();if(preventDefault){e.preventDefault()}};if(Ext.isArray(eventName)){eLen=eventName.length;for(e=0;e';interval=setInterval(function(){var hd,match,attrs,srcMatch,typeMatch,el,s;if(!(el=DOC.getElementById(id))){return false}clearInterval(interval);Ext.removeNode(el);hd=Ext.getHead().dom;while((match=scriptTagRe.exec(html))){attrs=match[1];srcMatch=attrs?attrs.match(srcRe):false;if(srcMatch&&srcMatch[2]){s=DOC.createElement("script");s.src=srcMatch[2];typeMatch=attrs.match(typeRe);if(typeMatch&&typeMatch[2]){s.type=typeMatch[2]}hd.appendChild(s)}else{if(match[2]&&match[2].length>0){if(window.execScript){window.execScript(match[2])}else{window.eval(match[2])}}}}Ext.callback(callback,me)},20);dom.innerHTML=html.replace(replaceScriptTagRe,"");return me},removeAllListeners:function(){this.removeAnchor();Ext.EventManager.removeAll(this.dom);return this},createProxy:function(config,renderTo,matchBox){config=(typeof config=="object")?config:{tag:"div",role:"presentation",cls:config};var me=this,proxy=renderTo?Ext.DomHelper.append(renderTo,config,true):Ext.DomHelper.insertBefore(me.dom,config,true);proxy.setVisibilityMode(Element.DISPLAY);proxy.hide();if(matchBox&&me.setBox&&me.getBox){proxy.setBox(me.getBox())}return proxy},needsTabIndex:function(){if(this.dom){if((this.dom.nodeName==="a")&&(!this.dom.href)){return true}return !focusRe.test(this.dom.nodeName)}},isFocusable:function(asFocusEl){var dom=this.dom,tabIndexAttr=dom.getAttributeNode("tabIndex"),tabIndex,nodeName=dom.nodeName,canFocus=false;if(tabIndexAttr&&tabIndexAttr.specified){tabIndex=tabIndexAttr.value}if(dom&&!dom.disabled){if(tabIndex==-1){canFocus=Ext.enableFocusManager&&asFocusEl}else{if(focusRe.test(nodeName)){if((nodeName!=="a")||dom.href){canFocus=true}}else{canFocus=tabIndex!=null&&tabIndex>=0}}canFocus=canFocus&&this.isVisible(true)}return canFocus}});if(Ext.isIE9m){Element.prototype.getById=function(id,asDom){var dom=this.dom,cacheItem,el,ret;if(dom){el=(useDocForId&&DOC.getElementById(id))||dom.all[id];if(el){if(asDom){ret=el}else{cacheItem=EC[id];if(cacheItem&&cacheItem.el){ret=Ext.updateCacheEntry(cacheItem,el).el}else{ret=new Element(el)}}return ret}}return asDom?Ext.getDom(id):Element.get(id)}}Element.createAlias({addListener:"on",removeListener:"un",clearListeners:"removeAllListeners",focusable:"isFocusable"});Element.Fly=AbstractElement.Fly=new Ext.Class({extend:Element,isFly:true,constructor:function(dom){this.dom=dom;this.el=this},attach:AbstractElement.Fly.prototype.attach});internalFly=new Element.Fly();if(Ext.isIE9m){Ext.getElementById=function(id){var el=DOC.getElementById(id),detachedBodyEl;if(!el&&(detachedBodyEl=AbstractElement.detachedBodyEl)){el=detachedBodyEl.dom.all[id]}return el}}else{if(!DOC.querySelector){Ext.getDetachedBody=Ext.getBody;Ext.getElementById=function(id){return DOC.getElementById(id)}}}}));(Ext.cmd.derive("Ext.dom.CompositeElementLite",Ext.Base,{alternateClassName:"Ext.CompositeElementLite",statics:{importElementMethods:function(){var b,c=Ext.dom.Element.prototype,a=this.prototype;for(b in c){if(typeof c[b]=="function"){(function(d){a[d]=a[d]||function(){return this.invoke(d,arguments)}}).call(a,b)}}}},constructor:function(b,a){this.elements=[];this.add(b,a);this.el=new Ext.dom.AbstractElement.Fly()},isComposite:true,getElement:function(a){return this.el.attach(a)},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.dom.Element.selectorFunction(c,a)}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;g0){for(;a.first&&b;b--){a.removeAtKey(a.first.key)}}}},1,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,k=Ext.dom.Query.operators,m=/(\d*)n\+?(\d*)/,e=/\D/,o=/^(\s)+/,n=/\\(.)/g,p=new Ext.util.LruCache({maxSize:100}),q=["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(""),r=function(v,u){return u.method.apply(this,[v].concat(u.args))},a=function(w,A){var u=[],x=0,z=w.length,y,v=A!==">";for(;x\^])\s?|\s|$)/,s=/^(#)?((?:\\\.|[\w\-])+|\*)(?:\((true|false)\))?/,c=[{re:/^\.((?:\\\.|[\w\-])+)(?:\((true|false)\))?/,method:d,argTransform:function(u){if(u[1]!==undefined){u[1]=u[1].replace(n,"$1")}return u.slice(1)}},{re:/^(?:\[((?:[@?$])?[\w\-]*)\s*(?:([\^$*~%!\/]?=)\s*(['"])?((?:\\\]|.)*?)\3)?(?!\\)\])/,method:b,argTransform:function(y){var u=y[0],z=y[1],w=y[2],v=y[3],x=y[4],A;if(x!==undefined){x=x.replace(n,"$1")}if(w==="/="){A=p.get(x);if(A){x=A}else{x=p.add(x,new RegExp(x))}}return[z,w,x]}},{re:/^#((?:\\\.|[\w\-])+)/,method:j},{re:/^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,method:t,argTransform:function(u){if(u[2]!==undefined){u[2]=u[2].replace(n,"$1")}return u.slice(1)}},{re:/^(?:\{([^\}]+)\})/,method:q}];g.Query=Ext.extend(Object,{constructor:function(u){u=u||{};Ext.apply(this,u)},execute:function(w){var x=this.operations,v=[],z,y,u;for(y=0,u=x.length;y=0;--y){w=u[y];A=w.mode;if(A){if(A==="^"){v=a(v," ")}else{if(A===">"){C=[];for(x=0,B=v.length;x1}});Ext.apply(this,{cache:new Ext.util.LruCache({maxSize:100}),pseudos:{not:function(A,u){var B=Ext.ComponentQuery,y=0,z=A.length,x=[],w=-1,v;for(;y0){u.push(v[0])}return u},last:function(w){var u=w.length,v=[];if(u>0){v.push(w[u-1])}return v},focusable:function(v){var u=v.length,x=[],w=0,y;for(;w=":function(a){return Ext.coerce(this.getRoot(a)[this.property],this.value)>=this.value},">":function(a){return Ext.coerce(this.getRoot(a)[this.property],this.value)>this.value},"!=":function(a){return Ext.coerce(this.getRoot(a)[this.property],this.value)!=this.value}},constructor:function(a){var b=this;b.initialConfig=a;Ext.apply(b,a);b.filter=b.filter||b.filterFn;if(b.filter===undefined){b.setValue(a.value)}},setValue:function(b){var a=this;a.value=b;if(a.property===undefined||a.value===undefined){}else{a.filter=a.createFilterFn()}a.filterFn=a.filter},setFilterFn:function(a){this.filterFn=this.filter=a},createFilterFn:function(){var a=this,c=a.createValueMatcher(),b=a.property;if(a.operator){return a.operatorFns[a.operator]}else{return function(d){var e=a.getRoot(d)[b];return c===null?e===null:c.test(e)}}},getRoot:function(b){var a=this.root;return a===undefined?b:b[a]},createValueMatcher:function(){var d=this,e=d.value,g=d.anyMatch,c=d.exactMatch,a=d.caseSensitive,b=Ext.String.escapeRegex;if(e===null){return e}if(!e.exec){e=String(e);if(g===true){e=b(e)}else{e="^"+b(e);if(c===true){e+="$"}}e=new RegExp(e,a?"":"i")}return e},serialize:function(){var b=this,a=Ext.apply({},b.initialConfig);a.value=b.value;return a}},1,0,0,0,0,0,[Ext.util,"Filter"],function(){this.prototype.operatorFns["=="]=this.prototype.operatorFns["="]}));(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)},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},updateKey:function(g,h){var d=this,e=d.map,a=d.indexOfKey(g),c=d.indexMap,b;if(a>-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=j;j=b;b=g}if(j<0){j=0}if(b==null||b>=a){b=a-1}c=d.slice(j,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;ce?1:(g0){b.sorters.removeRange(b.multiSortLimit,c)}break;case"prepend":b.sorters.insert(0,g);break;case"append":b.sorters.addAll(g);break;case undefined:case null:case"replace":b.sorters.clear();b.sorters.addAll(g);break;default:}}if(d!==false){b.fireEvent("beforesort",b,g);b.onBeforeSort(g);if(b.getSorterCount()){b.doSort(b.generateComparator())}}return g},getSorterCount:function(){return this.sorters.items.length},generateComparator:function(){var a=this.sorters.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,a=this.model?this.model.prototype.fields:null,e,b,c;for(c=0;c>1;h=d(e,b[c]);if(h>=0){j=c+1}else{if(h<0){a=c-1}}}return j},reorder:function(d){var h=this,b=h.items,c=0,g=b.length,a=[],e=[],j;h.suspendEvents();for(j in d){a[d[j]]=b[j]}for(c=0;ce?1:(g=e.duration),g,j;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()}j={};j[Ext.supports.CSS3TransitionEnd]=e.lastFrame;j.scope=e;j.single=true;g.on(j)}return g},jumpToEnd:function(a){var b=this.runAnim(a,true);this.applyAnimAttrs(b,b.anims[a.id])},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.addEvents("beforeanimate","keyframe","afteranimate");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(p,d,b,o,n,j){var k=3*d,m=3*(o-d)-k,a=1-k-m,h=3*b,l=3*(n-b)-h,q=1-h-l;function g(r){return((a*r+m)*r+k)*r}function c(r,u){var s=e(r,u);return((q*s+l)*s+h)*s}function e(r,z){var y,w,u,s,v,t;for(u=r,t=0;t<8;t++){s=g(u)-r;if(Math.abs(s)w){return w}while(ys){y=u}else{w=u}u=(w-y)/2+y}return u}return c(p,1/(200*j))},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.require("Ext.fx.CubicBezier",function(){var e=Math,h=e.PI,d=e.pow,b=e.sin,g=e.sqrt,a=e.abs,c=1.70158;Ext.define("Ext.fx.Easing",{singleton:true,linear:Ext.identityFn,ease:function(m){var j=0.07813-m/2,o=-0.25,p=g(0.0066+j*j),s=p-j,l=d(a(s),1/3)*(s<0?-1:1),r=-p-j,k=d(a(r),1/3)*(r<0?-1:1),u=l+k+0.25;return d(1-u,2)*3*u*0.1+(1-u)*3*u*u+u*u*u},easeIn:function(j){return d(j,1.7)},easeOut:function(j){return d(j,0.48)},easeInOut:function(s){var m=0.48-s/1.04,l=g(0.1734+m*m),j=l-m,r=d(a(j),1/3)*(j<0?-1:1),p=-l-m,o=d(a(p),1/3)*(p<0?-1:1),k=r+o+0.5;return(1-k)*3*k*k+k*k*k},backIn:function(j){return j*j*((c+1)*j-c)},backOut:function(j){j=j-1;return j*j*((c+1)*j+c)+1},elasticIn:function(l){if(l===0||l===1){return l}var k=0.3,j=k/4;return d(2,-10*l)*b((l-j)*(2*h)/k)+1},elasticOut:function(j){return 1-Ext.fx.Easing.elasticIn(1-j)},bounceIn:function(j){return 1-Ext.fx.Easing.bounceOut(1-j)},bounceOut:function(o){var k=7.5625,m=2.75,j;if(o<(1/m)){j=k*o*o}else{if(o<(2/m)){o-=(1.5/m);j=k*o*o+0.75}else{if(o<(2.5/m)){o-=(2.25/m);j=k*o*o+0.9375}else{o-=(2.625/m);j=k*o*o+0.984375}}}return j}},function(){var k=Ext.fx.Easing.self,j=k.prototype;k.implement({"back-in":j.backIn,"back-out":j.backOut,"ease-in":j.easeIn,"ease-out":j.easeOut,"elastic-in":j.elasticIn,"elastic-out":j.elasticOut,"bounce-in":j.bounceIn,"bounce-out":j.bounceOut,"ease-in-out":j.easeInOut})})});(Ext.cmd.derive("Ext.draw.Color",Ext.Base,{colorToHexRe:/(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/,rgbRe:/\s*rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)\s*/,hexRe:/\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/,lightnessFactor:0.2,constructor:function(d,c,a){var b=this,e=Ext.Number.constrain;b.r=e(d,0,255);b.g=e(c,0,255);b.b=e(a,0,255)},getRed:function(){return this.r},getGreen:function(){return this.g},getBlue:function(){return this.b},getRGB:function(){var a=this;return[a.r,a.g,a.b]},getHSL:function(){var k=this,a=k.r/255,j=k.g/255,m=k.b/255,n=Math.max(a,j,m),d=Math.min(a,j,m),o=n-d,e,p=0,c=0.5*(n+d);if(d!=n){p=(c<0.5)?o/(n+d):o/(2-n-d);if(a==n){e=60*(j-m)/o}else{if(j==n){e=120+60*(m-a)/o}else{e=240+60*(a-j)/o}}if(e<0){e+=360}if(e>=360){e-=360}}return[e,p,c]},getLighter:function(b){var a=this.getHSL();b=b||this.lightnessFactor;a[2]=Ext.Number.constrain(a[2]+b,0,1);return this.fromHSL(a[0],a[1],a[2])},getDarker:function(a){a=a||this.lightnessFactor;return this.getLighter(-a)},toString:function(){var h=this,c=Math.round,e=c(h.r).toString(16),d=c(h.g).toString(16),a=c(h.b).toString(16);e=(e.length==1)?"0"+e:e;d=(d.length==1)?"0"+d:d;a=(a.length==1)?"0"+a:a;return["#",e,d,a].join("")},toHex:function(b){if(Ext.isArray(b)){b=b[0]}if(!Ext.isString(b)){return""}if(b.substr(0,1)==="#"){return b}var e=this.colorToHexRe.exec(b),g,d,a,c;if(Ext.isArray(e)){g=parseInt(e[2],10);d=parseInt(e[3],10);a=parseInt(e[4],10);c=a|(d<<8)|(g<<16);return e[1]+"#"+("000000"+c.toString(16)).slice(-6)}else{return b}},fromString:function(l){var c,j,h,a,k=parseInt,e=l.substr(0,1),d;if(e!="#"){d=Ext.draw.Color.cssColors[l];if(d){l=d;e=l.substr(0,1)}}if((l.length==4||l.length==7)&&e==="#"){c=l.match(this.hexRe);if(c){j=k(c[1],16)>>0;h=k(c[2],16)>>0;a=k(c[3],16)>>0;if(l.length==4){j+=(j*16);h+=(h*16);a+=(a*16)}}}else{c=l.match(this.rgbRe);if(c){j=c[1];h=c[2];a=c[3]}}return(typeof j=="undefined")?undefined:new Ext.draw.Color(j,h,a)},getGrayscale:function(){return this.r*0.3+this.g*0.59+this.b*0.11},fromHSL:function(g,o,d){var a,b,c,e,k=[],n=Math.abs,j=Math.floor;if(o==0||g==null){k=[d,d,d]}else{g/=60;a=o*(1-n(2*d-1));b=a*(1-n(g-2*j(g/2)-1));c=d-a/2;switch(j(g)){case 0:k=[a,b,0];break;case 1:k=[b,a,0];break;case 2:k=[0,a,b];break;case 3:k=[0,b,a];break;case 4:k=[b,0,a];break;case 5:k=[a,0,b];break}k=[k[0]+c,k[1]+c,k[2]+c]}return new Ext.draw.Color(k[0]*255,k[1]*255,k[2]*255)}},3,0,0,0,0,0,[Ext.draw,"Color"],function(){var a=this.prototype;this.addStatics({fromHSL:function(){return a.fromHSL.apply(a,arguments)},fromString:function(){return a.fromString.apply(a,arguments)},toHex:function(){return a.toHex.apply(a,arguments)},cssColors:{aliceblue:"#F0F8FF",antiquewhite:"#FAEBD7",aqua:"#00FFFF",aquamarine:"#7FFFD4",azure:"#F0FFFF",beige:"#F5F5DC",bisque:"#FFE4C4",black:"#000000",blanchedalmond:"#FFEBCD",blue:"#0000FF",blueviolet:"#8A2BE2",brown:"#A52A2A",burlywood:"#DEB887",cadetblue:"#5F9EA0",chartreuse:"#7FFF00",chocolate:"#D2691E",coral:"#FF7F50",cornflowerblue:"#6495ED",cornsilk:"#FFF8DC",crimson:"#DC143C",cyan:"#00FFFF",darkblue:"#00008B",darkcyan:"#008B8B",darkgoldenrod:"#B8860B",darkgray:"#A9A9A9",darkgreen:"#006400",darkgrey:"#A9A9A9",darkkhaki:"#BDB76B",darkmagenta:"#8B008B",darkolivegreen:"#556B2F",darkorange:"#FF8C00",darkorchid:"#9932CC",darkred:"#8B0000",darksalmon:"#E9967A",darkseagreen:"#8FBC8F",darkslateblue:"#483D8B",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",darkturquoise:"#00CED1",darkviolet:"#9400D3",deeppink:"#FF1493",deepskyblue:"#00BFFF",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1E90FF",firebrick:"#B22222",floralwhite:"#FFFAF0",forestgreen:"#228B22",fuchsia:"#FF00FF",gainsboro:"#DCDCDC",ghostwhite:"#F8F8FF",gold:"#FFD700",goldenrod:"#DAA520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#ADFF2F",honeydew:"#F0FFF0",hotpink:"#FF69B4",indianred:"#CD5C5C",indigo:"#4B0082",ivory:"#FFFFF0",khaki:"#F0E68C",lavender:"#E6E6FA",lavenderblush:"#FFF0F5",lawngreen:"#7CFC00",lemonchiffon:"#FFFACD",lightblue:"#ADD8E6",lightcoral:"#F08080",lightcyan:"#E0FFFF",lightgoldenrodyellow:"#FAFAD2",lightgray:"#D3D3D3",lightgreen:"#90EE90",lightgrey:"#D3D3D3",lightpink:"#FFB6C1",lightsalmon:"#FFA07A",lightseagreen:"#20B2AA",lightskyblue:"#87CEFA",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#B0C4DE",lightyellow:"#FFFFE0",lime:"#00FF00",limegreen:"#32CD32",linen:"#FAF0E6",magenta:"#FF00FF",maroon:"#800000",mediumaquamarine:"#66CDAA",mediumblue:"#0000CD",mediumorchid:"#BA55D3",mediumpurple:"#9370DB",mediumseagreen:"#3CB371",mediumslateblue:"#7B68EE",mediumspringgreen:"#00FA9A",mediumturquoise:"#48D1CC",mediumvioletred:"#C71585",midnightblue:"#191970",mintcream:"#F5FFFA",mistyrose:"#FFE4E1",moccasin:"#FFE4B5",navajowhite:"#FFDEAD",navy:"#000080",oldlace:"#FDF5E6",olive:"#808000",olivedrab:"#6B8E23",orange:"#FFA500",orangered:"#FF4500",orchid:"#DA70D6",palegoldenrod:"#EEE8AA",palegreen:"#98FB98",paleturquoise:"#AFEEEE",palevioletred:"#DB7093",papayawhip:"#FFEFD5",peachpuff:"#FFDAB9",peru:"#CD853F",pink:"#FFC0CB",plum:"#DDA0DD",powderblue:"#B0E0E6",purple:"#800080",red:"#FF0000",rosybrown:"#BC8F8F",royalblue:"#4169E1",saddlebrown:"#8B4513",salmon:"#FA8072",sandybrown:"#F4A460",seagreen:"#2E8B57",seashell:"#FFF5EE",sienna:"#A0522D",silver:"#C0C0C0",skyblue:"#87CEEB",slateblue:"#6A5ACD",slategray:"#708090",slategrey:"#708090",snow:"#FFFAFA",springgreen:"#00FF7F",steelblue:"#4682B4",tan:"#D2B48C",teal:"#008080",thistle:"#D8BFD8",tomato:"#FF6347",turquoise:"#40E0D0",violet:"#EE82EE",wheat:"#F5DEB3",white:"#FFFFFF",whitesmoke:"#F5F5F5",yellow:"#FFFF00",yellowgreen:"#9ACD32"}})}));(Ext.cmd.derive("Ext.draw.Draw",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,availableAnimAttrs:{along:"along",blur:null,"clip-rect":"csv",cx:null,cy:null,fill:"color","fill-opacity":null,"font-size":null,height:null,opacity:null,path:"path",r:null,rotation:"csv",rx:null,ry:null,scale:"csv",stroke:"color","stroke-opacity":null,"stroke-width":null,translation:"csv",width:null,x:null,y:null},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},ellipsePath:function(b){var a=b.attr;return Ext.String.format("M{0},{1}A{2},{3},0,1,1,{0},{4}A{2},{3},0,1,1,{0},{1}z",a.x,a.y-a.ry,a.rx,a.ry,a.y+a.ry)},rectPath:function(b){var a=b.attr;if(a.radius){return Ext.String.format("M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z",a.x+a.radius,a.y,a.width-a.radius*2,a.radius,-a.radius,a.height-a.radius*2,a.radius*2-a.width,a.radius*2-a.height)}else{return Ext.String.format("M{0},{1}L{2},{1},{2},{3},{0},{3}z",a.x,a.y,a.width+a.x,a.height+a.y)}},path2string:function(){return this.join(",").replace(Ext.draw.Draw.pathToStringRE,"$1")},pathToString:function(a){return a.join(",").replace(Ext.draw.Draw.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,k){var j=[],h=e.toLowerCase();k.replace(b.pathValuesRE,function(m,l){l&&j.push(+l)});if(h=="m"&&j.length>2){c.push([e].concat(Ext.Array.splice(j,0,2)));h="l";e=(e=="m")?"l":"L"}while(j.length>=d[h]){c.push([e].concat(Ext.Array.splice(j,0,d[h])));if(!d[h]){break}}})}c.toString=b.path2string;return c},mapPath:function(l,g){if(!g){return l}var h,e,c,k,a,d,b;l=this.path2curve(l);for(c=0,k=l.length;c7){h[b].shift();e=h[b];while(e.length){Ext.Array.splice(h,b++,0,["C"].concat(Ext.Array.splice(e,0,6)))}Ext.Array.erase(h,b,1);c=h.length;b--}a=h[b];g=a.length;j.x=a[g-2];j.y=a[g-1];j.bx=parseFloat(a[g-4])||j.x;j.by=parseFloat(a[g-3])||j.y}return h},interpolatePaths:function(r,l){var j=this,d=j.pathToAbsolute(r),m=j.pathToAbsolute(l),n={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},a={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},b=function(p,s){if(p[s].length>7){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(a[1]-c[1])*(b[0]-c[0])},intersectIntersection:function(o,n,g,d){var c=[],b=g[0]-d[0],a=g[1]-d[1],l=o[0]-n[0],j=o[1]-n[1],m=g[0]*d[1]-g[1]*d[0],k=o[0]*n[1]-o[1]*n[0],h=1/(b*j-a*l);c[0]=(m*l-k*b)*h;c[1]=(m*j-k*a)*h;return c},intersect:function(o,c){var n=this,k=0,m=c.length,h=c[m-1],p=o,g,q,l,a,b,d;for(;k0){v.push(g)}}else{j=t-3*q+3*n-m;p=2*(t-q-q+n);h=t-q;u=p*p-4*j*h;e=j+j;if(u===0){g=p/e;if(g<1&&g>0){v.push(g)}}else{if(u>0){w=Math.sqrt(u);g=(w+p)/e;if(g<1&&g>0){v.push(g)}g=(p-w)/e;if(g<1&&g>0){v.push(g)}}}}k=Math.min(t,m);o=Math.max(t,m);for(l=0;l=d&&k>=v)||(k<=d&&k<=v)){h=m=s}else{h=g((l-e)/n(k-d));if(ds){c-=q}h+=c;m+=c;p=l-u*a(h);o=k+u*b(h);y=l+t*a(m);x=k+t*b(m);if((k>d&&od)){p+=n(d-o)*(p-l)/(o-k);o=d}if((k>v&&xv)){y-=n(v-x)*(y-l)/(x-k);x=v}return{x1:p,y1:o,x2:y,y2:x}},smooth:function(a,p){var o=this.path2curve(a),c=[o[0]],g=o[0][1],e=o[0][2],q,s,t=1,h=o.length,d=1,l=g,k=e,w,v,u,m,r,n,b;for(;t0){q=Math.floor((o-(n/10))/n)*n}if(u){for(p=0;p=0){d=0;while(d>o){d-=e;a++}o=+d.toFixed(10);d=0;while(d=15){m=1;if(++e>11){k++}}else{m=15}break;case 1/3:if(m>=20){m=1;if(++e>11){k++}}else{if(m>=10){m=20}else{m=10}}break;case 1/4:if(m>=22){m=1;if(++e>11){k++}}else{if(m>=15){m=22}else{if(m>=8){m=15}else{m=8}}}break}r.setYear(k);r.setMonth(e);r.setDate(m);l.push(new Date(r))}else{if(j){r=Ext.Date.add(r,h,g);l.push(new Date(r))}else{r=Ext.Date.add(r,h,g);l++}}}if(q){r=o}if(j){return{from:+c,to:+r,steps:l}}else{return{from:+c,to:+r,step:(r-c)/l,steps:l}}},sorter:function(d,c){return d.offset-c.offset},rad:function(a){return a%360*Math.PI/180},normalizeRadians:function(b){var a=2*Math.PI;if(b>=0){return b%a}return((b%a)+a)%a},degrees:function(a){return a*180/Math.PI%360},normalizeDegrees:function(a){if(a>=0){return a%360}return((a%360)+360)%360},withinBox:function(a,c,b){b=b||{};return(a>=b.x&&a<=(b.x+b.width)&&c>=b.y&&c<=(b.y+b.height))},parseGradient:function(l){var e=this,g=l.type||"linear",c=l.angle||0,j=e.radian,m=l.stops,a=[],k,b,h,d;if(g=="linear"){b=[0,0,Math.cos(c*j),Math.sin(c*j)];h=1/(Math.max(Math.abs(b[2]),Math.abs(b[3]))||1);b[2]*=h;b[3]*=h;if(b[2]<0){b[0]=-b[2];b[2]=0}if(b[3]<0){b[1]=-b[3];b[3]=0}}for(k in m){if(m.hasOwnProperty(k)&&e.stopsRE.test(k)){d={offset:parseInt(k,10),color:Ext.draw.Color.toHex(m[k].color)||"#ffffff",opacity:m[k].opacity||1};a.push(d)}}Ext.Array.sort(a,e.sorter);if(g=="linear"){return{id:l.id,type:g,vector:b,stops:a}}else{return{id:l.id,type:g,centerX:l.centerX,centerY:l.centerY,focalX:l.focalX,focalY:l.focalY,radius:l.radius,vector:b,stops:a}}}},0,0,0,0,0,0,[Ext.draw,"Draw"],0));(Ext.cmd.derive("Ext.fx.PropertyHandler",Ext.Base,{statics:{defaultHandler:{pixelDefaultsRE:/width|height|top$|bottom$|left$|right$/i,unitRE:/^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/,scrollRE:/^scroll/i,computeDelta:function(k,c,a,g,j){a=(typeof a=="number")?a:1;var h=this.unitRE,d=h.exec(k),b,e;if(d){k=d[1];e=d[2];if(!this.scrollRE.test(j)&&!e&&this.pixelDefaultsRE.test(j)){e="px"}}k=+k||0;d=h.exec(c);if(d){c=d[1];e=d[2]||e}c=+c||0;b=(g!=null)?g:k;return{from:k,delta:(c-b)*a,units:e}},get:function(o,b,a,n,k){var m=o.length,d=[],e,h,l,c,g;for(e=0;e=d){m=d;a=true}if(j.reverse){m=d-m}for(e in l){if(l.hasOwnProperty(e)){k=l[e];h=a?1:c(m/d);g[e]=b[e].set(k,h)}}j.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,{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"],function(){Ext.applyIf(Ext.Element.prototype,this.prototype);Ext.CompositeElementLite.importElementMethods()}));(Ext.cmd.derive("Ext.util.ElementContainer",Ext.Base,{childEls:[],constructor:function(){var b=this,a;if(b.hasOwnProperty("childEls")){a=b.childEls;delete b.childEls;b.addChildEls.apply(b,a)}},destroy:function(){var e=this,d=e.getChildEls(),g,a,c,b;for(c=d.length;c--;){a=d[c];if(typeof a!="string"){a=a.name}g=e[a];if(g){e[a]=null;g.remove()}}},addChildEls:function(){var b=this,a=arguments;if(b.hasOwnProperty("childEls")){b.childEls.push.apply(b.childEls,a)}else{b.childEls=b.getChildEls().concat(Array.prototype.slice.call(a))}b.prune(b.childEls,false)},applyChildEls:function(b,a){var e=this,g=e.getChildEls(),j,k,d,c,h;j=(a||e.id)+"-";for(d=g.length;d--;){k=g[d];if(typeof k=="string"){h=b.getById(j+k)}else{if((c=k.select)){h=Ext.select(c,true,b.dom)}else{if((c=k.selectNode)){h=Ext.get(Ext.DomQuery.selectNode(c,b.dom))}else{h=b.getById(k.id||(j+k.itemId))}}k=k.name}e[k]=h}},getChildEls:function(){var b=this,a;if(b.hasOwnProperty("childEls")){return b.childEls}a=b.self;return a.$childEls||b.getClassChildEls(a)},getClassChildEls:function(o){var k=this,p=o.$childEls,m,d,b,j,n,h,a,c,e,g,l;if(!p){g=o.superclass;if(g){g=g.self;c=[g.$childEls||k.getClassChildEls(g)];l=g.prototype.mixins||{}}else{c=[];l={}}e=o.prototype;h=e.mixins;for(a in h){if(h.hasOwnProperty(a)&&!l.hasOwnProperty(a)){n=h[a].self;c.push(n.$childEls||k.getClassChildEls(n))}}c.push(e.hasOwnProperty("childEls")&&e.childEls);for(d=0,b=c.length;d','
    {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);%}"],afterRender:function(){var d=this,e={},j=d.protoEl,h=d.el,c,g,a,b;d.finishRenderChildren();if(d.contentEl){g=Ext.baseCSSPrefix;a=g+"hide-";b=Ext.get(d.contentEl);b.removeCls([g+"hidden",a+"display",a+"offsets",a+"nosize"]);d.getContentTarget().appendChild(b.dom)}j.writeTo(e);c=e.removed;if(c){h.removeCls(c)}c=e.cls;if(c.length){h.addCls(c)}c=e.style;if(e.style){h.setStyle(c)}d.protoEl=null;if(!d.ownerCt){d.updateLayout()}},afterFirstLayout:function(c,l){var e=this,k=e.x,h=e.y,d,b,j,m,g=e.defaultAlign,a=e.alignOffset;if(!e.ownerLayout){d=Ext.isDefined(k);b=Ext.isDefined(h)}if(e.floating&&(!d||!b)){if(e.floatParent){j=e.floatParent.getTargetEl().getViewRegion();m=e.el.getAlignToXY(e.alignTarget||e.floatParent.getTargetEl(),g,a);j.x=m[0]-j.x;j.y=m[1]-j.y}else{m=e.el.getAlignToXY(e.alignTarget||e.container,g,a);j=e.container.translateXY(m[0],m[1])}k=d?k:j.x;h=b?h:j.y;d=b=true}if(d||b){e.setPosition(k,h)}e.onBoxReady(c,l)},applyRenderSelectors:function(){var d=this,b=d.renderSelectors,c=d.el,e=c.dom,a;d.applyChildEls(c);if(b){for(a in b){if(b.hasOwnProperty(a)&&b[a]){d[a]=Ext.get(Ext.DomQuery.selectNode(b[a],e))}}}},beforeRender:function(){var c=this,e=c.getTargetEl(),d=c.getOverflowEl(),b=c.getComponentLayout(),a=c.getOverflowStyle();c.frame=c.frame||c.alwaysFramed;if(!b.initialized){b.initLayout()}if(d){d.setStyle(a);c.overflowStyleSet=true}c.setUI(c.ui);if(c.disabled){c.disable(true)}},doApplyRenderTpl:function(c,a){var d=a.$comp,b;if(!d.rendered){b=d.initRenderTpl();b.applyOut(a.renderData,c)}},doAutoRender:function(){var a=this;if(!a.rendered){if(a.floating){a.render(a.renderTo||document.body)}else{a.render(Ext.isBoolean(a.autoRender)?Ext.getBody():a.autoRender)}}},doRenderContent:function(a,c){var b=c.$comp;if(b.html){Ext.DomHelper.generateMarkup(b.html,a);delete b.html}if(b.tpl){if(!b.tpl.isTemplate){b.tpl=new Ext.XTemplate(b.tpl)}if(b.data){b.tpl.applyOut(b.data,a);delete b.data}}},doRenderFramingDockedItems:function(a,c,d){var b=c.$comp;if(!b.rendered&&b.doRenderDockedItems){c.renderData.$skipDockedItems=true;b.doRenderDockedItems.call(this,a,c,d)}},finishRender:function(a){var d=this,b,e,c;if(!d.el||d.$pid){if(d.container){c=d.container.getById(d.id,true)}else{c=Ext.getDom(d.id)}if(!d.el){d.wrapPrimaryEl(c)}else{delete d.$pid;if(!d.el.dom){d.wrapPrimaryEl(d.el)}c.parentNode.insertBefore(d.el.dom,c);Ext.removeNode(c)}}else{if(!d.rendering){b=d.initRenderTpl();if(b){e=d.initRenderData();b.insertFirst(d.getTargetEl(),e)}}}if(!d.container){d.container=Ext.get(d.el.dom.parentNode)}if(d.ctCls){d.container.addCls(d.ctCls)}d.onRender(d.container,a);if(!d.overflowStyleSet){d.getOverflowEl().setStyle(d.getOverflowStyle())}d.el.setVisibilityMode(Ext.Element[d.hideMode.toUpperCase()]);if(d.overCls){d.el.hover(d.addOverCls,d.removeOverCls,d)}if(d.hasListeners.render){d.fireEvent("render",d)}d.afterRender();if(d.hasListeners.afterrender){d.fireEvent("afterrender",d)}d.initEvents();if(d.hidden){d.el.hide()}},finishRenderChildren:function(){var a=this.getComponentLayout();a.finishRender()},getElConfig:function(){var j=this,l=j.autoEl,g=j.getFrameInfo(),b={tag:"div",tpl:g?j.initFramingTpl(g.table):j.initRenderTpl()},a=j.protoEl,c,e,h,m,d,k;j.initStyles(a);a.writeTo(b);a.flush();if(Ext.isString(l)){b.tag=l}else{Ext.apply(b,l)}b.id=j.id;if(b.tpl){if(g){e=j.frameElNames;h=e.length;b.tplData=k=j.getFrameRenderData();k.renderData=j.initRenderData();d=k.fgid;for(c=0;c table")[1].remove()}else{if(g){g.remove()}if(d){d.remove()}if(c){c.remove()}}}}else{if(e.frame){e.applyRenderSelectors()}}},getFrameInfo:function(){if(Ext.supports.CSS3BorderRadius||!this.frame){return false}var x=this,p=x.frameInfoCache,e=x.getFramingInfoCls()+"-frameInfo",y=p[e],q=Math.max,o,l,t,n,z,g,k,b,c,m,h,s,u,j,a,d,w,r,v;if(y==null){o=Ext.fly(x.getStyleProxy(e),"frame-style-el");t=o.getStyle("font-family");if(t){t=t.split("-");d=parseInt(t[1],10);w=parseInt(t[2],10);r=parseInt(t[3],10);v=parseInt(t[4],10);b=parseInt(t[5],10);c=parseInt(t[6],10);m=parseInt(t[7],10);h=parseInt(t[8],10);s=parseInt(t[9],10);u=parseInt(t[10],10);j=parseInt(t[11],10);a=parseInt(t[12],10);n=q(b,q(d,w));z=q(c,q(w,r));g=q(m,q(v,r));k=q(h,q(d,v));y={table:t[0].charAt(0)==="t",vertical:t[0].charAt(1)==="v",top:n,right:z,bottom:g,left:k,width:k+z,height:n+g,maxWidth:q(n,z,g,k),border:{top:b,right:c,bottom:m,left:h,width:h+c,height:b+m},padding:{top:s,right:u,bottom:j,left:a,width:a+u,height:s+j},radius:{tl:d,tr:w,br:r,bl:v}}}else{y=false}p[e]=y}x.frame=!!y;x.frameSize=y;return y},getFramingInfoCls:function(){return this.baseCls+"-"+this.ui},getStyleProxy:function(b){var a=this.styleProxyEl||(Ext.AbstractComponent.prototype.styleProxyEl=Ext.getBody().createChild({role:"presentation",style:{position:"absolute",top:"-10000px"}},null,true));a.className=b;return a},getFrameTpl:function(a){return this.getTpl(a?"frameTableTpl":"frameTpl")},frameInfoCache:{}},0,0,0,0,0,0,[Ext.util,"Renderable"],0));(Ext.cmd.derive("Ext.state.Provider",Ext.Base,{prefix:"ext-",constructor:function(a){a=a||{};var b=this;Ext.apply(b,a);b.addEvents("statechange");b.state={};b.mixins.observable.constructor.call(b)},get:function(b,a){return typeof this.state[b]=="undefined"?a:this.state[b]},clear:function(a){var b=this;delete b.state[a];b.fireEvent("statechange",b,a,null)},set:function(a,c){var b=this;b.state[a]=c;b.fireEvent("statechange",b,a,c)},decodeValue:function(g){var c=this,l=/^(a|n|d|b|s|o|e)\:(.*)$/,b=l.exec(unescape(g)),h,d,a,k,e,j;if(!b||!b[1]){return}d=b[1];g=b[2];switch(d){case"e":return null;case"n":return parseFloat(g);case"d":return new Date(Date.parse(g));case"b":return(g=="1");case"a":h=[];if(g!=""){k=g.split("^");e=k.length;for(j=0;je){r=l;o=true}if(g&&a>q){n=a;o=true}if(m||g){k=u.el.getStyle("overflow");if(k!=="hidden"){u.el.setStyle("overflow","hidden")}}if(o){b=!Ext.isNumber(u.width);t=!Ext.isNumber(u.height);u.setSize(n,r);u.el.setSize(q,e);if(b){delete u.width}if(t){delete u.height}}if(g){d.width=a}if(m){d.height=l}}j=u.constrain;p=u.constrainHeader;if(j||p){u.constrain=u.constrainHeader=false;s=c.callback;c.callback=function(){u.constrain=j;u.constrainHeader=p;if(s){s.call(c.scope||u,arguments)}if(k!=="hidden"){u.el.setStyle("overflow",k)}}}return u.mixins.animate.animate.apply(u,arguments)},setHiddenState:function(a){var b=this.getHierarchyState();this.hidden=a;if(a){b.hidden=true}else{delete b.hidden}},onHide:function(){if(this.ownerLayout){this.updateLayout({isRoot:false})}},onShow:function(){this.updateLayout({isRoot:false})},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;a--){if((g=d.getAt(a)).is(b)){return g}}}else{if(a){return d.getAt(--a)}}}}return null},previousNode:function(b,d){var j=this,h=j.ownerCt,a,g,e,c;if(d&&j.is(b)){return j}if(h){for(g=h.items.items,e=Ext.Array.indexOf(g,j)-1;e>-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},nextNode:function(d,j){var b=this,c=b.ownerCt,k,e,h,g,a;if(j&&b.is(d)){return b}if(c){for(e=c.items.items,g=Ext.Array.indexOf(e,b)+1,h=e.length;g0}else{e=h.getTargetEl()}if(j){h.tpl[h.tplWriteMode](e,b||{})}else{e.update(h.html,c,a)}if(g){h.updateLayout()}}},setVisible:function(a){return this[a?"show":"hide"]()},isVisible:function(a){var b=this,c;if(b.hidden||!b.rendered||b.isDestroyed){c=true}else{if(a){c=b.isHierarchicallyHidden()}}return !c},isHierarchicallyHidden:function(){var d=this,c=false,b,a;for(;(b=d.ownerCt||d.floatParent);d=b){a=b.getHierarchyState();if(a.hidden){c=true;break}if(d.getHierarchyState().collapseImmune){if(b.collapsed&&!d.collapseImmune){c=true;break}}else{c=!!a.collapsed;break}}return c},onBoxReady:function(b,a){var c=this;if(c.disableOnBoxReady){c.onDisable()}else{if(c.enableOnBoxReady){c.onEnable()}}if(c.resizable){c.initResizable(c.resizable)}if(c.draggable){c.initDraggable()}if(c.hasListeners.boxready){c.fireEvent("boxready",c,b,a)}},enable:function(a){var b=this;delete b.disableOnBoxReady;b.removeCls(b.disabledCls);if(b.rendered){b.onEnable()}else{b.enableOnBoxReady=true}b.disabled=false;delete b.resetDisable;if(a!==true){b.fireEvent("enable",b)}return b},disable:function(a){var b=this;delete b.enableOnBoxReady;b.addCls(b.disabledCls);if(b.rendered){b.onDisable()}else{b.disableOnBoxReady=true}b.disabled=true;if(a!==true){delete b.resetDisable;b.fireEvent("disable",b)}return b},onEnable:function(){var a=this,b,c;if(a.maskOnDisable){b=a.el.dom;c=b.nodeName;if(a.disabledRe.test(c)){b.disabled=false}if(!a.nonMaskableRe.test(c)){a.unmask()}}},onDisable:function(){var c=this,b=c.focusCls,a=c.getFocusEl(),d,e;if(b&&a){a.removeCls(c.removeClsWithUI(b,true))}if(c.maskOnDisable){d=c.el.dom;e=d.nodeName;if(c.disabledRe.test(e)){d.disabled=true}if(!c.nonMaskableRe.test(e)){c.mask()}}},mask:function(e,c,a){var b=this.lastBox,d=this.getMaskTarget()||this.el;if(b){a=b.height}d.mask(e,c,a)},unmask:function(){(this.getMaskTarget()||this.el).unmask()},getMaskTarget:function(){return this.maskElement?this[this.maskElement]:null},isDisabled:function(){return this.disabled},setDisabled:function(a){return this[a?"disable":"enable"]()},isHidden:function(){return this.hidden},addCls:function(a){var c=this,b=c.rendered?c.el:c.protoEl;b.addCls.apply(b,arguments);return c},addClass:function(){return this.addCls.apply(this,arguments)},hasCls:function(a){var c=this,b=c.rendered?c.el:c.protoEl;return b.hasCls.apply(b,arguments)},removeCls:function(a){var c=this,b=c.rendered?c.el:c.protoEl;b.removeCls.apply(b,arguments);return c},addOverCls:function(){var a=this;if(!a.disabled){a.el.addCls(a.overCls)}},removeOverCls:function(){this.el.removeCls(this.overCls)},addListener:function(b,g,e,a){var h=this,d,c;if(Ext.isString(b)&&(Ext.isObject(g)||a&&a.element)){if(a.element){d=g;g={};g[b]=d;b=a.element;if(e){g.scope=e}for(c in a){if(a.hasOwnProperty(c)){if(h.eventOptionsRe.test(c)){g[c]=a[c]}}}}if(h[b]&&h[b].on){h.mon(h[b],g)}else{h.afterRenderEvents=h.afterRenderEvents||{};if(!h.afterRenderEvents[b]){h.afterRenderEvents[b]=[]}h.afterRenderEvents[b].push(g)}return}return h.mixins.observable.addListener.apply(h,arguments)},removeManagedListenerItem:function(b,a,j,d,g,e){var h=this,c=a.options?a.options.element:null;if(c){c=h[c];if(c&&c.un){if(b||(a.item===j&&a.ename===d&&(!g||a.fn===g)&&(!e||a.scope===e))){c.un(a.ename,a.fn,a.scope);if(!b){Ext.Array.remove(h.managedListeners,a)}}}}else{return h.mixins.observable.removeManagedListenerItem.apply(h,arguments)}},getBubbleTarget:function(){return this.ownerCt},isFloating:function(){return this.floating},isDraggable:function(){return !!this.draggable},isDroppable:function(){return !!this.droppable},onAdded:function(a,c){var b=this;b.ownerCt=a;if(b.hierarchyState){b.hierarchyState.invalid=true;delete b.hierarchyState}if(b.hasListeners.added){b.fireEvent("added",b,a,c)}},onRemoved:function(b){var a=this;if(a.hasListeners.removed){a.fireEvent("removed",a,a.ownerCt)}delete a.ownerCt;delete a.ownerLayout},beforeDestroy:Ext.emptyFn,onResize:function(c,a,b,e){var d=this;if(d.floating&&d.constrain){d.doConstrain()}if(d.hasListeners.resize){d.fireEvent("resize",d,c,a,b,e)}},setSize:function(b,a){var c=this;if(b&&typeof b=="object"){a=b.height;b=b.width}if(typeof b=="number"){c.width=Ext.Number.constrain(b,c.minWidth,c.maxWidth)}else{if(b===null){delete c.width}}if(typeof a=="number"){c.height=Ext.Number.constrain(a,c.minHeight,c.maxHeight)}else{if(a===null){delete c.height}}if(c.rendered&&c.isVisible()){c.updateLayout({isRoot:false})}return c},isLayoutRoot:function(){var a=this,b=a.ownerLayout;if(!b||a._isLayoutRoot||a.floating){return true}return b.isItemLayoutRoot(a)},isLayoutSuspended:function(){var a=this,b;while(a){if(a.layoutSuspendCount||a.suspendLayout){return true}b=a.ownerLayout;if(!b){break}a=b.owner}return 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.AbstractComponent.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.AbstractComponent.updateLayout(d,e)}}},getSizeModel:function(k){var o=this,a=Ext.layout.SizeModel,d=o.componentLayout.ownerContext,b=o.width,q=o.height,r,c,g,e,h,p,m,n,l,j;if(d){j=d.widthModel;h=d.heightModel}if(!j||!h){g=((r=typeof b)=="number");e=((c=typeof q)=="number");l=o.floating||!(p=o.ownerLayout);if(l){m=Ext.layout.Layout.prototype.autoSizePolicy;n=o.floating?3:o.shrinkWrap;if(g){j=a.configured}if(e){h=a.configured}}else{m=p.getItemSizePolicy(o,k);n=p.isItemShrinkWrap(o)}if(d){d.ownerSizePolicy=m}n=(n===true)?3:(n||0);if(l&&n){if(b&&r=="string"){n&=2}if(q&&c=="string"){n&=1}}if(n!==3){if(!k){k=o.ownerCt&&o.ownerCt.getSizeModel()}if(k){n|=(k.width.shrinkWrap?1:0)|(k.height.shrinkWrap?2:0)}}if(!j){if(!m.setsWidth){if(g){j=a.configured}else{j=(n&1)?a.shrinkWrap:a.natural}}else{if(m.readsWidth){if(g){j=a.calculatedFromConfigured}else{j=(n&1)?a.calculatedFromShrinkWrap:a.calculatedFromNatural}}else{j=a.calculated}}}if(!h){if(!m.setsHeight){if(e){h=a.configured}else{h=(n&2)?a.shrinkWrap:a.natural}}else{if(m.readsHeight){if(e){h=a.calculatedFromConfigured}else{h=(n&2)?a.calculatedFromShrinkWrap:a.calculatedFromNatural}}else{h=a.calculated}}}}return j.pairsByHeightOrdinal[h.ordinal]},isDescendant:function(a){if(a.isContainer){for(var b=this.ownerCt;b;b=b.ownerCt){if(b===a){return true}}}return false},doComponentLayout:function(){this.updateLayout();return this},forceComponentLayout:function(){this.updateLayout()},setComponentLayout:function(b){var a=this.componentLayout;if(a&&a.isLayout&&a!=b){a.setOwner(null)}this.componentLayout=b;b.setOwner(this)},getComponentLayout:function(){var a=this;if(!a.componentLayout||!a.componentLayout.isLayout){a.setComponentLayout(Ext.layout.Layout.create(a.componentLayout,"autocomponent"))}return a.componentLayout},afterComponentLayout:function(c,a,b,e){var d=this;if(++d.componentLayoutCounter===1){d.afterFirstLayout(c,a)}if(c!==b||a!==e){d.onResize(c,a,b,e)}},beforeComponentLayout:function(b,a){return true},setPosition:function(a,e,b){var c=this,d=c.beforeSetPosition.apply(c,arguments);if(d&&c.rendered){a=d.x;e=d.y;if(b){if(a!==c.getLocalX()||e!==c.getLocalY()){c.stopAnimation();c.animate(Ext.apply({duration:1000,listeners:{afteranimate:Ext.Function.bind(c.afterSetPosition,c,[a,e])},to:{left:a,top:e}},b))}}else{c.setLocalXY(a,e);c.afterSetPosition(a,e)}}return c},beforeSetPosition:function(a,e,b){var d,c;if(a){if(Ext.isNumber(c=a[0])){b=e;e=a[1];a=c}else{if((c=a.x)!==undefined){b=e;e=a.y;a=c}}}if(this.constrain||this.constrainHeader){d=this.calculateConstrainedPosition(null,[a,e],true);if(d){a=d[0];e=d[1]}}d={x:this.x=a,y:this.y=e,anim:b,hasX:a!==undefined,hasY:e!==undefined};return(d.hasX||d.hasY)?d:null},afterSetPosition:function(a,c){var b=this;b.onPosition(a,c);if(b.hasListeners.move){b.fireEvent("move",b,a,c)}},onPosition:Ext.emptyFn,setWidth:function(a){return this.setSize(a)},setHeight:function(a){return this.setSize(undefined,a)},getSize:function(a){return this.el.getSize(a)},getWidth:function(){return this.el.getWidth()},getHeight:function(){return this.el.getHeight()},getLoader:function(){var c=this,b=c.autoLoad?(Ext.isObject(c.autoLoad)?c.autoLoad:{url:c.autoLoad}):null,a=c.loader||b;if(a){if(!a.isLoader){c.loader=new Ext.ComponentLoader(Ext.apply({target:c,autoLoad:b},a))}else{a.setTarget(c)}return c.loader}return null},setDocked:function(b,c){var a=this;a.dock=b;if(c&&a.ownerCt&&a.rendered){a.ownerCt.updateLayout()}return a},setBorder:function(b,d){var c=this,a=!!d;if(c.rendered||a){if(!a){d=c.el}if(!b){b=0}else{if(b===true){b="1px"}else{b=this.unitizeBox(b)}}d.setStyle("border-width",b);if(!a){c.updateLayout()}}c.border=b},onDestroy:function(){var a=this;Ext.destroy(a.componentLayout,a.loadMask,a.floatingDescendants)},destroy:function(){var d=this,b=d.renderSelectors,a,c;if(!d.isDestroyed){if(!d.hasListeners.beforedestroy||d.fireEvent("beforedestroy",d)!==false){d.destroying=true;d.beforeDestroy();if(d.floating){delete d.floatParent;if(d.zIndexManager){d.zIndexManager.unregister(d)}}else{if(d.ownerCt&&d.ownerCt.remove){d.ownerCt.remove(d,false)}}d.stopAnimation();d.onDestroy();Ext.destroy(d.plugins);if(d.hasListeners.destroy){d.fireEvent("destroy",d)}Ext.ComponentManager.unregister(d);d.mixins.state.destroy.call(d);d.clearListeners();if(d.rendered){if(!d.preserveElOnDestroy){d.el.remove()}d.mixins.elementCt.destroy.call(d);if(b){for(a in b){if(b.hasOwnProperty(a)){c=d[a];if(c){delete d[a];c.remove()}}}}delete d.el;delete d.frameBody;delete d.rendered}delete d.initialConfig;d.destroying=false;d.isDestroyed=true}}},isDescendantOf:function(a){return !!this.findParentBy(function(b){return b===a})},getHierarchyState:function(a){var e=this,j=(a&&e.hierarchyStateInner)||e.hierarchyState,c=e.ownerCt,b,d,g,h;if(!j||j.invalid){b=e.getRefOwner();if(c){h=e.ownerLayout===c.layout}e.hierarchyState=j=Ext.Object.chain(b?b.getHierarchyState(h):Ext.rootHierarchyState);e.initHierarchyState(j);if((d=e.componentLayout).initHierarchyState){d.initHierarchyState(j)}if(e.isContainer){e.hierarchyStateInner=g=Ext.Object.chain(j);d=e.layout;if(d&&d.initHierarchyState){d.initHierarchyState(g,j)}if(a){j=g}}}return j},initHierarchyState:function(b){var a=this;if(a.collapsed){b.collapsed=true}if(a.hidden){b.hidden=true}if(a.collapseImmune){b.collapseImmune=true}},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)}},1,0,0,0,0,[["positionable",Ext.util.Positionable],["observable",Ext.util.Observable],["animate",Ext.util.Animate],["elementCt",Ext.util.ElementContainer],["renderable",Ext.util.Renderable],["state",Ext.state.Stateful]],[Ext,"AbstractComponent"],function(){var a=this;a.createAlias({on:"addListener",prev:"previousSibling",next:"nextSibling"});Ext.resumeLayouts=function(b){a.resumeLayouts(b)};Ext.suspendLayouts=function(){a.suspendLayouts()};Ext.batchLayouts=function(c,b){a.suspendLayouts();c.call(b);a.resumeLayouts(true)}}));(Ext.cmd.derive("Ext.AbstractPlugin",Ext.Base,{disabled:false,isPlugin:true,constructor:function(a){if(a){this.pluginConfig=a;Ext.apply(this,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:Ext.emptyFn,enable:function(){this.disabled=false},disable:function(){this.disabled=true},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]}}},1,0,0,0,0,0,[Ext,"AbstractPlugin"],0));(Ext.cmd.derive("Ext.Action",Ext.Base,{constructor:function(a){this.initialConfig=a;this.itemId=a.itemId=(a.itemId||a.id||Ext.id());this.items=[]},isAction:true,setText:function(a){this.initialConfig.text=a;this.callEach("setText",[a])},getText:function(){return this.initialConfig.text},setIconCls:function(a){this.initialConfig.iconCls=a;this.callEach("setIconCls",[a])},getIconCls:function(){return this.initialConfig.iconCls},setDisabled:function(a){this.initialConfig.disabled=a;this.callEach("setDisabled",[a])},enable:function(){this.setDisabled(false)},disable:function(){this.setDisabled(true)},isDisabled:function(){return this.initialConfig.disabled},setHidden:function(a){this.initialConfig.hidden=a;this.callEach("setVisible",[!a])},show:function(){this.setHidden(false)},hide:function(){this.setHidden(true)},isHidden:function(){return this.initialConfig.hidden},setHandler:function(b,a){this.initialConfig.handler=b;this.initialConfig.scope=a;this.callEach("setHandler",[b,a])},each:function(b,a){Ext.each(this.items,b,a)},callEach:function(g,c){var b=this.items,d=0,a=b.length,e;Ext.suspendLayouts();for(;d=8){a=new XDomainRequest()}else{Ext.Error.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=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}},createResponse:function(e){var j=this,l=e.xhr,c=j.isXdr,b={},m=c?[]:l.getAllResponseHeaders().replace(/\r\n/g,"\n").split("\n"),h=m.length,n,g,k,d,a;while(h--){n=m[h];g=n.indexOf(":");if(g>=0){k=n.substr(0,g).toLowerCase();if(n.charAt(g+1)==" "){++g}b[k]=n.substr(g+1)}}e.xhr=null;delete e.xhr;d={request:e,requestId:e.id,status:l.status,statusText:l.statusText,getResponseHeader:function(o){return b[o.toLowerCase()]},getAllResponseHeaders:function(){return b}};if(c){j.processXdrResponse(d,l)}if(e.binary){d.responseBytes=j.getByteArray(l)}else{d.responseText=l.responseText;d.responseXML=l.responseXML}l=null;return d},createException:function(a){return{request:a,requestId:a.id,status:a.aborted?-1:0,statusText:a.aborted?"transaction aborted":"communication failure",aborted:a.aborted,timedout:a.timedout}},getByteArray:function(k){var c=k.response,j=k.responseBody,b,g,a,d;if(k instanceof Ext.data.flash.BinaryXhr){b=k.responseBytes}else{if(window.Uint8Array){b=c?new Uint8Array(c):[]}else{if(Ext.isIE9p){try{b=new VBArray(j).toArray()}catch(h){b=[]}}else{if(Ext.isIE){if(!this.self.vbScriptInjected){this.injectVBScript()}getIEByteArray(k.responseBody,b=[])}else{b=[];g=k.responseText;a=g.length;for(d=0;d=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(d,g,a,c){var e=this;e.top=e.y+=d;e.left=e.x+=c;e.right+=g;e.bottom+=a;return e},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.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:Ext.baseCSSPrefix+"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)}}}}},_onLoad:function(){this.init();var a=Ext.EventManager;a.on(document,"mouseup",this.handleMouseUp,this,true);a.on(document,"mousemove",this.handleMouseMove,this,true);a.on(window,"unload",this._onUnload,this,true);a.on(window,"resize",this._onResize,this,true)},_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:3,clickTimeThresh:350,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]},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;cc.clickPixelThresh||a>c.clickPixelThresh){c.startDrag(c.startX,c.startY)}}if(c.dragThreshMet){d.b4Drag(g);d.onDrag(g);if(!d.moveOnly){c.fireEvents(g,false)}}c.stopEvent(g);return true},fireEvents:function(u,m){var w=this,g=w.dragCurrent,c,o,s=u.getPoint(),d,l,n=[],h=[],k=[],a=[],v=[],t=[],j,b,q,r,p;if(!g||g.isLocked()){return}if(!w.notifyOccluded&&(!Ext.supports.PointerEvents||Ext.isIE10m||Ext.isOpera)&&!(g.deltaX<0||g.deltaY<0)){c=g.getDragEl();o=c.style.top;c.style.visibility="hidden";j=u.getXY();u.target=document.elementFromPoint(j[0],j[1]);c.style.visibility="visible";c.style.top=o}for(q in w.dragOvers){d=w.dragOvers[q];delete w.dragOvers[q];if(!w.isTypeOfDD(d)||d.isDestroyed){continue}if(w.notifyOccluded){if(!this.isOverTarget(s,d,w.mode)){k.push(d)}}else{if(!u.within(d.getEl())){k.push(d)}}h[q]=true}for(p in g.groups){if("string"!=typeof p){continue}for(q in w.ids[p]){d=w.ids[p][q];if(w.isTypeOfDD(d)&&(l=d.getEl())&&(d.isTarget)&&(!d.isLocked())&&(Ext.fly(l).isVisible(true))&&((d!=g)||(g.ignoreSelf===false))){if(w.notifyOccluded){if((d.zIndex=w.getZIndex(l))!==-1){b=true}n.push(d)}else{if(u.within(d.getEl())){n.push(d);break}}}}}if(b){Ext.Array.sort(n,w.byZIndex)}for(q=0,r=n.length;q=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"),m=d.getSize(),j=Ext.Element,k,a,l,h;if(!this.deltaSetXY){k=this.cachedViewportSize={width:j.getDocumentWidth(),height:j.getDocumentHeight()};a=[Math.max(0,Math.min(g.x,k.width-m.width)),Math.max(0,Math.min(g.y,k.height-m.height))];d.setXY(a);l=this.getLocalX(d);h=d.getLocalY();this.deltaSetXY=[l-g.x,h-g.y]}else{k=this.cachedViewportSize;this.setLocalXY(d,Math.max(0,Math.min(g.x+this.deltaSetXY[0],k.width-m.width)),Math.max(0,Math.min(g.y+this.deltaSetXY[1],k.height-m.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.Element.getXY(this.getEl());this.lastPageX=c[0];this.lastPageY=c[1]}},autoScroll:function(m,l,e,n){if(this.scroll){var o=Ext.Element.getViewHeight(),b=Ext.Element.getViewWidth(),q=this.DDMInstance.getScrollTop(),d=this.DDMInstance.getScrollLeft(),k=e+l,p=n+m,j=(o+q-l-this.deltaY),g=(b+d-m-this.deltaX),c=40,a=(document.all)?80:30;if(k>o&&j0&&l-qb&&g0&&m-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(){this.callParent();this.scroll=(this.config.scroll!==false)},b4MouseDown:function(a){this.autoOffset(a.getPageX(),a.getPageY())},b4Drag:function(a){this.setDragElPos(a.getPageX(),a.getPageY())},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.util.Floating",Ext.Base,{focusOnToFront:true,shadow:"sides",constrain:false,constructor:function(b){var a=this;a.fixed=a.fixed&&!(Ext.isIE6||Ext.isIEQuirks);a.el=new Ext.dom.Layer(Ext.apply({preventSync:true,hideMode:a.hideMode,shadow:(typeof a.shadow!="undefined")?a.shadow:"sides",shadowOffset:a.shadowOffset,constrain:false,fixed:a.fixed,shim:(a.shim===false)?false:undefined},a.floating),b);if(a.modal&&!(Ext.enableFocusManager)){a.mon(a.el,{keydown:a.onKeyDown,scope:a})}a.mon(a.el,{mousedown:a.onMouseDown,scope:a});a.floating=true;a.registerWithOwnerCt();a.initHierarchyEvents()},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(b.hierarchyEventSource,{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)}},onKeyDown:function(d){var c=this,a,h,g,b;if(d.getKey()==Ext.EventObject.TAB){a=d.shiftKey;h=c.el.query(":focusable");g=h[0];b=h[h.length-1];if(g&&b&&d.target===(a?g:b)){d.stopEvent();(a?b:g).focus(false,true)}}},onMouseDown:function(b){var a=this.focusTask;if(this.floating&&(!a||!a.id)){this.toFront(!!b.getTarget(":focusable"))}},syncShadow:function(){if(this.floating){this.el.sync(true)}},onBeforeFloatLayout:function(){this.el.preventSync=true},onAfterFloatLayout:function(){delete this.el.preventSync;this.syncShadow()},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)}},toFront:function(c){var b=this,a=b.zIndexParent,d=b.preventFocusOnActivate;if(a&&b.bringParentToFront!==false){a.toFront(true)}if(!Ext.isDefined(c)){c=!b.focusOnToFront}if(c){b.preventFocusOnActivate=true}if(b.zIndexManager.bringToFront(b,c)){if(!c){b.focus(false,true)}if(b.hasListeners.tofront){b.fireEvent("tofront",b,b.el.getZIndex())}}b.preventFocusOnActivate=d;return b},setActive:function(b,c){var a=this;if(b){if(a.el.shadow&&!a.maximized){a.el.enableShadow(true)}if(!a.preventFocusOnActivate){a.focus(false,true)}a.fireEvent("activate",a)}else{if(a.isWindow&&(c&&c.isWindow)&&a.hideShadowOnDeactivate){a.el.disableShadow()}a.fireEvent("deactivate",a)}},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(false),d=e||(b.dom!==document.body)?[0,0]:b.getXY();a.x=d[0];a.y=d[1];g.setBox(a,c)}},1,0,0,0,0,0,[Ext.util,"Floating"],0));(Ext.cmd.derive("Ext.Component",Ext.AbstractComponent,{statics:{DIRECTION_TOP:"top",DIRECTION_RIGHT:"right",DIRECTION_BOTTOM:"bottom",DIRECTION_LEFT:"left",VERTICAL_DIRECTION_Re:/^(?:top|bottom)$/,INVALID_ID_CHARS_Re:/[\.,\s]/g},resizeHandles:"all",floating:false,defaultAlign:"c-c",alignTarget:null,toFrontOnShow:true,hideMode:"display",offsetsCls:Ext.baseCSSPrefix+"hide-offsets",bubbleEvents:[],defaultComponentLayoutType:"autocomponent",constructor:function(a){var b=this;a=a||{};if(a.initialConfig){if(a.isAction){b.baseAction=a}a=a.initialConfig}else{if(a.tagName||a.dom||Ext.isString(a)){a={applyTo:a,id:a.id||a}}}b.callParent([a]);if(b.baseAction){b.baseAction.addComponent(b)}},initComponent:function(){var a=this;a.callParent();if(a.listeners){a.on(a.listeners);a.listeners=null}a.enableBubble(a.bubbleEvents)},afterRender:function(){var a=this;a.callParent();if(!(a.x&&a.y)&&(a.pageX||a.pageY)){a.setPagePosition(a.pageX,a.pageY)}},setAutoScroll:function(a){var b=this;b.autoScroll=!!a;if(b.rendered){b.getOverflowEl().setStyle(b.getOverflowStyle())}b.updateLayout();return b},setOverflowXY:function(b,a){var d=this,e=arguments.length,c=d.ownerCt;if(e){d.overflowX=b||"";if(e>1){d.overflowY=a||""}}if(d.rendered){d.getOverflowEl().setStyle(d.getOverflowStyle())}(c||d).updateLayout();return d},beforeRender:function(){var b=this,c=b.floating,a;if(c){b.addCls(Ext.baseCSSPrefix+"layer");a=c.cls;if(a){b.addCls(a)}}return b.callParent()},beforeLayout:function(){this.callParent(arguments);if(this.floating){this.onBeforeFloatLayout()}},afterComponentLayout:function(){this.callParent(arguments);if(this.floating){this.onAfterFloatLayout()}},makeFloating:function(a){this.mixins.floating.constructor.call(this,a)},wrapPrimaryEl:function(a){if(this.floating){this.makeFloating(a)}else{this.callParent(arguments)}},initResizable:function(a){var b=this;a=Ext.apply({target:b,dynamic:false,constrainTo:b.constrainTo||(b.floatParent?b.floatParent.getTargetEl():null),handles:b.resizeHandles},a);a.target=b;b.resizer=new Ext.resizer.Resizer(a)},getDragEl:function(){return this.el},initDraggable:function(){var c=this,a=(c.resizer&&c.resizer.el!==c.el)?c.resizerComponent=new Ext.Component({ariaRole:"presentation",el:c.resizer.el,rendered:true,container:c.container}):c,b=Ext.applyIf({el:a.getDragEl(),constrainTo:(c.constrain||c.draggable.constrain)?(c.constrainTo||(c.floatParent?c.floatParent.getTargetEl():c.container)):undefined},c.draggable);if(c.constrain||c.constrainDelegate){b.constrain=c.constrain;b.constrainDelegate=c.constrainDelegate}c.dd=new Ext.util.ComponentDragger(a,b)},scrollBy:function(b,a,c){var d;if((d=this.getTargetEl())&&d.dom){d.scrollBy.apply(d,arguments)}},setLoading:function(c,d){var b=this,a={target:b};if(b.rendered){if(c!==false){if(Ext.isString(c)){a.msg=c}else{Ext.apply(a,c)}if(!b.loadMask||!b.loadMask.isLoadMask){if(d&&a.useTargetEl==null){a.useTargetEl=true}b.loadMask=new Ext.LoadMask(a)}else{Ext.apply(b.loadMask,a)}if(b.loadMask.isVisible()){b.loadMask.afterShow()}else{b.loadMask.show()}}else{if(b.loadMask&&b.loadMask.isLoadMask){b.loadMask.hide()}}}return b.loadMask},beforeSetPosition:function(){var b=this,c=b.callParent(arguments),a;if(c){a=b.adjustPosition(c.x,c.y);c.x=a.x;c.y=a.y}return c||null},afterSetPosition:function(b,a){this.onPosition(b,a);this.fireEvent("move",this,b,a)},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)}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},setPagePosition:function(a,g,b){var c=this,d,e;if(Ext.isArray(a)){g=a[1];a=a[0]}c.pageX=a;c.pageY=g;if(c.floating){if(c.isContainedFloater()){e=c.floatParent.getTargetEl().getViewRegion();if(Ext.isNumber(a)&&Ext.isNumber(e.left)){a-=e.left}if(Ext.isNumber(g)&&Ext.isNumber(e.top)){g-=e.top}}else{d=c.el.translateXY(a,g);a=d.x;g=d.y}c.setPosition(a,g,b)}else{d=c.el.translateXY(a,g);c.setPosition(d.x,d.y,b)}return c},isContainedFloater:function(){return(this.floating&&this.floatParent)},updateBox:function(a){this.setSize(a.width,a.height);this.setPagePosition(a.x,a.y);return this},getOuterSize:function(){var a=this.el;return{width:a.getWidth()+a.getMargin("lr"),height:a.getHeight()+a.getMargin("tb")}},adjustPosition:function(a,d){var b=this,c;if(b.isContainedFloater()){c=b.floatParent.getTargetEl().getViewRegion();a+=c.left;d+=c.top}return{x:a,y:d}},getPosition:function(a){var b=this,d,c=b.isContainedFloater(),e;if((a===true)&&!c){return[b.getLocalX(),b.getLocalY()]}d=b.getXY();if((a===true)&&c){e=b.floatParent.getTargetEl().getViewRegion();d[0]-=e.left;d[1]-=e.top}return d},getId:function(){var a=this,b;if(!a.id){b=a.getXType();if(b){b=b.replace(Ext.Component.INVALID_ID_CHARS_Re,"-")}else{b=Ext.name.toLowerCase()+"-comp"}a.id=b+"-"+a.getAutoId()}return a.id},show:function(d,a,b){var c=this,e=c.rendered;if(c.hierarchicallyHidden||(c.floating&&!e&&c.isHierarchicallyHidden())){if(!e){c.initHierarchyEvents()}if(arguments.length>1){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.getHierarchyState().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},onShowVeto:Ext.emptyFn,beforeShow:Ext.emptyFn,onShow:function(){var a=this;a.el.show();a.callParent(arguments);if(a.floating){if(a.maximized){a.fitContainer()}else{if(a.constrain){a.doConstrain()}}}},getAnimateTarget:function(a){a=a||this.animateTarget;if(a){a=a.isComponent?a.getEl():Ext.get(a)}return a||null},afterShow:function(h,b,e){var g=this,j=g.el,a,c,d;h=g.getAnimateTarget(h);if(!g.ghost){h=null}if(h){c={x:j.getX(),y:j.getY(),width:j.dom.offsetWidth,height:j.dom.offsetHeight};a={x:h.getX(),y:h.getY(),width:h.dom.offsetWidth,height:h.dom.offsetHeight};j.addCls(g.offsetsCls);d=g.ghost();d.el.stopAnimation();d.setX(-10000);g.ghostBox=c;d.el.animate({from:a,to:c,listeners:{afteranimate:function(){delete d.componentLayout.lastComponentSize;g.unghost();delete g.ghostBox;j.removeCls(g.offsetsCls);g.onShowComplete(b,e)}}})}else{g.onShowComplete(b,e)}g.fireHierarchyEvent("show")},onShowComplete:function(a,b){var c=this;if(c.floating){c.onFloatShow()}Ext.callback(a,b||c);c.fireEvent("show",c);delete c.hiddenByLayout},hide:function(e,b,c){var d=this,a;if(d.pendingShow){delete d.pendingShow}if(!(d.rendered&&!d.isVisible())){a=(d.fireEvent("beforehide",d)!==false);if(d.hierarchicallyHidden||a){d.hidden=true;d.getHierarchyState().hidden=true;if(d.rendered){d.onHide.apply(d,arguments)}}}return d},onHide:function(j,a,e){var g=this,c,d,b,h=Ext.Element.getActiveElement();if(h===g.el||g.el.contains(h)){Ext.fly(h).blur()}j=g.getAnimateTarget(j);if(!g.ghost){j=null}if(j){b={x:j.getX(),y:j.getY(),width:j.dom.offsetWidth,height:j.dom.offsetHeight};c=g.ghost();c.el.stopAnimation();d=g.getSize();c.el.animate({to:b,listeners:{afteranimate:function(){delete c.componentLayout.lastComponentSize;c.el.hide();c.setHiddenState(true);c.el.setSize(d);g.afterHide(a,e)}}})}g.el.hide();if(!j){g.afterHide(a,e)}},afterHide:function(a,b){var c=this;c.hiddenByLayout=null;Ext.AbstractComponent.prototype.onHide.call(c);Ext.callback(a,b||c);c.fireEvent("hide",c);c.fireHierarchyEvent("hide")},onDestroy:function(){var a=this;if(a.rendered){Ext.destroy(a.dd,a.resizer,a.proxy,a.proxyWrap,a.resizerComponent,a.loadMask)}delete a.focusTask;a.callParent()},deleteMembers:function(){var b=arguments,a=b.length,c=0;for(;c=0){n=o[l].splitterDelta;if(j.getAt(h+n)!==a){j.remove(a);h=j.indexOf(k);if(n>0){++h}j.insert(h,a)}}}if(m){if(e){k.expand(false)}b.remove(m);k.placeholder=null;if(e){k.collapse(null,false)}}b.updateLayout();Ext.resumeLayouts(true);k.fireEventArgs("changeregion",[k,d])}else{k.region=l}}return d},setRegionWeight: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}});Ext.define("ExtThemeNeptune.Component",{override:"Ext.Component",initComponent:function(){this.callParent();if(this.dock&&this.border===undefined){this.border=false}},initStyles:function(){var b=this,a=b.border;if(b.dock){b.border=null}b.callParent(arguments);b.border=a}});(Ext.cmd.derive("Ext.ElementLoader",Ext.Base,{statics:{Renderer:{Html:function(a,b,c){a.getTarget().update(b.responseText,c.scripts===true);return true}}},url:null,params:null,baseParams:null,autoLoad:false,target:null,loadMask:false,ajaxOptions:null,scripts:false,isLoader:true,constructor:function(b){var c=this,a;b=b||{};Ext.apply(c,b);c.setTarget(c.target);c.addEvents("beforeload","exception","load");c.mixins.observable.constructor.call(c);if(c.autoLoad){a=c.autoLoad;if(a===true){a={}}c.load(a)}},setTarget:function(b){var a=this;b=Ext.get(b);if(a.target&&a.target!=b){a.abort()}a.target=b},getTarget:function(){return this.target||null},abort:function(){var a=this.active;if(a!==undefined){Ext.Ajax.abort(a.request);if(a.mask){this.removeMask()}delete this.active}},removeMask:function(){this.target.unmask()},addMask:function(a){this.target.mask(a===true?null:a)},load:function(c){c=Ext.apply({},c);var e=this,a=Ext.isDefined(c.loadMask)?c.loadMask:e.loadMask,g=Ext.apply({},c.params),b=Ext.apply({},c.ajaxOptions),h=c.callback||e.callback,d=c.scope||e.scope||e;Ext.applyIf(b,e.ajaxOptions);Ext.applyIf(c,b);Ext.applyIf(g,e.params);Ext.apply(g,e.baseParams);Ext.applyIf(c,{url:e.url});Ext.apply(c,{scope:e,params:g,callback:e.onComplete});if(e.fireEvent("beforeload",e,c)===false){return}if(a){e.addMask(a)}e.active={options:c,mask:a,scope:d,callback:h,success:c.success||e.success,failure:c.failure||e.failure,renderer:c.renderer||e.renderer,scripts:Ext.isDefined(c.scripts)?c.scripts:e.scripts};e.active.request=Ext.Ajax.request(c);e.setOptions(e.active,c)},setOptions:Ext.emptyFn,onComplete:function(b,g,a){var d=this,e=d.active,c;if(e){c=e.scope;if(g){g=d.getRenderer(e.renderer).call(d,d,a,e)!==false}if(g){Ext.callback(e.success,c,[d,a,b]);d.fireEvent("load",d,a,b)}else{Ext.callback(e.failure,c,[d,a,b]);d.fireEvent("exception",d,a,b)}Ext.callback(e.callback,c,[d,g,a,b]);if(e.mask){d.removeMask()}}delete d.active},getRenderer:function(a){if(Ext.isFunction(a)){return a}return this.statics().Renderer.Html},startAutoRefresh:function(a,b){var c=this;c.stopAutoRefresh();c.autoRefresh=setInterval(function(){c.load(b)},a)},stopAutoRefresh:function(){clearInterval(this.autoRefresh);delete this.autoRefresh},isAutoRefreshing:function(){return Ext.isDefined(this.autoRefresh)},destroy:function(){var a=this;a.stopAutoRefresh();delete a.target;a.abort();a.clearListeners()}},1,0,0,0,0,[["observable",Ext.util.Observable]],[Ext,"ElementLoader"],0));(Ext.cmd.derive("Ext.ComponentLoader",Ext.ElementLoader,{statics:{Renderer:{Data:function(a,b,d){var g=true;try{a.getTarget().update(Ext.decode(b.responseText))}catch(c){g=false}return g},Component:function(a,c,h){var j=true,g=a.getTarget(),b=[];try{b=Ext.decode(c.responseText)}catch(d){j=false}if(j){g.suspendLayouts();if(h.removeAll){g.removeAll()}g.add(b);g.resumeLayouts(true)}return j}}},target:null,loadMask:false,renderer:"html",setTarget:function(b){var a=this;if(Ext.isString(b)){b=Ext.getCmp(b)}if(a.target&&a.target!=b){a.abort()}a.target=b},removeMask:function(){this.target.setLoading(false)},addMask:function(a){this.target.setLoading(a)},setOptions:function(b,a){b.removeAll=Ext.isDefined(a.removeAll)?a.removeAll:this.removeAll},getRenderer:function(b){if(Ext.isFunction(b)){return b}var a=this.statics().Renderer;switch(b){case"component":return a.Component;case"data":return a.Data;default:return Ext.ElementLoader.Renderer.Html}}},0,0,0,0,0,0,[Ext,"ComponentLoader"],0));(Ext.cmd.derive("Ext.layout.SizeModel",Ext.Base,{constructor:function(c){var e=this,d=e.self,a=d.sizeModelsArray,b;Ext.apply(e,c);e[b=e.name]=true;e.fixed=!(e.auto=e.natural||e.shrinkWrap);a[e.ordinal=a.length]=d[b]=d.sizeModels[b]=e},statics:{sizeModelsArray:[],sizeModels:{}},calculated:false,configured:false,constrainedMax:false,constrainedMin:false,natural:false,shrinkWrap:false,calculatedFromConfigured:false,calculatedFromNatural:false,calculatedFromShrinkWrap:false,names:null},1,0,0,0,0,0,[Ext.layout,"SizeModel"],function(){var e=this,a=e.sizeModelsArray,c,b,h,g,d;new e({name:"calculated"});new e({name:"configured",names:{width:"width",height:"height"}});new e({name:"natural"});new e({name:"shrinkWrap"});new e({name:"calculatedFromConfigured",configured:true,names:{width:"width",height:"height"}});new e({name:"calculatedFromNatural",natural:true});new e({name:"calculatedFromShrinkWrap",shrinkWrap:true});new e({name:"constrainedMax",configured:true,constrained:true,names:{width:"maxWidth",height:"maxHeight"}});new e({name:"constrainedMin",configured:true,constrained:true,names:{width:"minWidth",height:"minHeight"}});new e({name:"constrainedDock",configured:true,constrained:true,constrainedByMin:true,names:{width:"dockConstrainedWidth",height:"dockConstrainedHeight"}});for(c=0,h=a.length;c','","","{% } else if (values.shrinkWrapWidth) { %}",'','','','","","","","{% } else { %}",'","{% values.$layout.isShrinkWrapTpl = false %}","{% } %}"],tableTpl:['','','','","","",""],isShrinkWrapTpl:true,beginLayout:function(e){var d=this,a,b,c,g;d.callParent(arguments);d.initContextItems(e);if(!d.isShrinkWrapTpl){if(e.widthModel.shrinkWrap){g=true}if(Ext.isStrict&&Ext.isIE7){c=d.getOverflowXStyle(e);if((c==="auto"||c==="scroll")&&e.paddingContext.getPaddingInfo().right){g=true}}if(g){d.insertTableCt(e)}}if(!d.isShrinkWrapTpl&&Ext.isIE7&&Ext.isStrict&&!d.clearElHasPadding){a=e.paddingContext.getPaddingInfo().bottom;b=d.getOverflowYStyle(e);if(a&&(b==="auto"||b==="scroll")){d.clearEl.setStyle("height",a);d.clearElHasPadding=true}}},beforeLayoutCycle:function(c){var a=this.owner,d=a.hierarchyState,b=a.hierarchyStateInner;if(!d||d.invalid){d=a.getHierarchyState();b=a.hierarchyStateInner}if(c.widthModel.shrinkWrap&&this.isShrinkWrapTpl){b.inShrinkWrapTable=true}else{delete b.inShrinkWrapTable}},beginLayoutCycle:function(h){var m=this,c=m.outerCt,l=m.lastOuterCtWidth||"",k=m.lastOuterCtHeight||"",n=m.lastOuterCtTableLayout||"",b=h.state,o,g,j,p,d,a,e;m.callParent(arguments);j=p=d="";if(!h.widthModel.shrinkWrap&&m.isShrinkWrapTpl){if(Ext.isIE7m&&Ext.isStrict){g=m.getOverflowYStyle(h);if(g==="auto"||g==="scroll"){a=true}}if(!a){j="100%"}e=m.owner.hierarchyStateInner;o=m.getOverflowXStyle(h);d=(e.inShrinkWrapTable||o==="auto"||o==="scroll")?"":"fixed"}if(!h.heightModel.shrinkWrap&&!Ext.supports.PercentageHeightOverflowBug){p="100%"}if((j!==l)||m.hasOuterCtPxWidth){c.setStyle("width",j);m.lastOuterCtWidth=j;m.hasOuterCtPxWidth=false}if(d!==n){c.setStyle("table-layout",d);m.lastOuterCtTableLayout=d}if((p!==k)||m.hasOuterCtPxHeight){c.setStyle("height",p);m.lastOuterCtHeight=p;m.hasOuterCtPxHeight=false}if(m.hasInnerCtPxHeight){m.innerCt.setStyle("height","");m.hasInnerCtPxHeight=false}b.overflowAdjust=b.overflowAdjust||m.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,k,a,g,e,d,j;e=(h.getOverflowXStyle(c)==="auto");d=(h.getOverflowYStyle(c)==="auto");if(e||d){a=Ext.getScrollbarSize();j=c.overflowContext.el.dom;g=0;if(j.scrollWidth>j.clientWidth){g|=1}if(j.scrollHeight>j.clientHeight){g|=2}b=(d&&(g&2))?a.width:0;k=(e&&(g&1))?a.height:0;if(b!==h.lastOverflowAdjust.width||k!==h.lastOverflowAdjust.height){h.done=false;c.invalidate({state:{overflowAdjust:{width:b,height:k},overflowState:g,secondPass:true}})}}},completeLayout:function(a){this.lastOverflowAdjust=a.state.overflowAdjust},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;this.callParent(arguments);if(Ext.isIEQuirks||Ext.isIE8m){a.repaint()}if(Ext.isOpera){a.setStyle("position","relative");a.dom.scrollWidth;a.setStyle("position","")}},getContainerSize:function(b,c){var a=this.callParent(arguments),d=b.state.overflowAdjust;if(d){a.width-=d.width;a.height-=d.height}return a},getRenderData:function(){var a=this.owner,b=this.callParent();if((Ext.isIEQuirks||Ext.isIE7m)&&((a.shrinkWrap&1)||(a.floating&&!a.width))){b.shrinkWrapWidth=true}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(c){var b=this,d=c.target,a=b.owner.customOverflowEl;c.outerCtContext=c.getEl("outerCt",b);c.innerCtContext=c.getEl("innerCt",b);if(a){c.overflowContext=c.getEl(a)}else{c.overflowContext=c.targetContext}if(d[d.contentPaddingProperty]!==undefined){c.paddingContext=b.isShrinkWrapTpl?c.innerCtContext:c.outerCtContext}},initLayout:function(){var c=this,b=Ext.getScrollbarSize().width,a=c.owner;c.callParent();if(b&&c.manageOverflow&&!c.hasOwnProperty("lastOverflowAdjust")){if(a.autoScroll||c.reserveScrollbar){c.lastOverflowAdjust={width:b,height:0}}}},insertTableCt:function(b){var h=this,a=h.owner,c=0,e,g,k,d,j;e=Ext.XTemplate.getTpl(this,"tableTpl");e.renderPadding=h.doRenderPadding;h.outerCt.dom.removeChild(h.innerCt.dom);g=document.createDocumentFragment();k=h.innerCt.dom.childNodes;d=k.length;for(;ck.dom.clientHeight)){m-=q.width}}d.outerCtContext.setProp("width",m+g.width);t.hasOuterCtPxWidth=true}if(j&&!d.heightModel.shrinkWrap){if(Ext.supports.PercentageHeightOverflowBug){s=true}if(((Ext.isIE8&&Ext.isStrict)||Ext.isIE7m&&Ext.isStrict&&r)){h=true;c=!Ext.isIE8}if((s||h)&&p&&(k.dom.scrollWidth>k.dom.clientWidth)){j=Math.max(j-q.height,0)}if(s){d.outerCtContext.setProp("height",j+g.height);t.hasOuterCtPxHeight=true}if(h){if(c){j+=g.height}d.innerCtContext.setProp("height",j);t.hasInnerCtPxHeight=true}}if(Ext.isIE7&&Ext.isStrict&&!r&&(l==="auto")){a=(e==="auto")?"overflow-x":"overflow-y";k.setStyle(a,"hidden");k.setStyle(a,"auto")}},setupRenderTpl:function(a){this.callParent(arguments);a.renderPadding=this.doRenderPadding},getContentTarget:function(){return this.innerCt}},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},constructor:function(a){var b=this;b.map={};b.zIndexStack=[];b.front=null;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.EventManager.onWindowResize(b._onContainerResize,b);b.zseed=b.getNextZSeed();b.targetEl=Ext.get(a)}}else{Ext.EventManager.onWindowResize(b._onContainerResize,b);b.zseed=b.getNextZSeed();Ext.onDocumentReady(function(){b.targetEl=Ext.getBody()})}},getNextZSeed:function(){return(Ext.ZIndexManager.zBase+=10000)},setBase:function(b){this.zseed=b;var a=this.assignZIndices();this._activateLast();return a},assignZIndices:function(){var c=this.zIndexStack,b=c.length,e=0,h=this.zseed,d,g;for(;e=0&&a[c].hidden;--c){}if((b=a[c])){d._setActiveChild(b,d.front);if(b.modal){return}}else{if(d.front&&!d.front.destroying){d.front.setActive(false)}d.front=null}for(;c>=0;--c){b=a[c];if(b.isVisible()&&b.modal){d._showModalMask(b);return}}d._hideModalMask()},_showModalMask:function(b){var d=this,h=b.el.getStyle("zIndex")-4,c=b.floatParent?b.floatParent.getTargetEl():b.container,a=d.mask,g=d.maskShim,e;if(!a){if(Ext.isIE6){g=d.maskShim=Ext.getBody().createChild({tag:"iframe",role:"presentation",cls:Ext.baseCSSPrefix+"shim "+Ext.baseCSSPrefix+"mask-shim"});g.setVisibilityMode(Ext.Element.DISPLAY)}a=d.mask=Ext.getBody().createChild({role:"presentation",cls:Ext.baseCSSPrefix+"mask",style:"height:0;width:0"});a.setVisibilityMode(Ext.Element.DISPLAY);a.on("click",d._onMaskClick,d)}a.maskTarget=c;e=d.getMaskBox();if(g){g.setStyle("zIndex",h);g.show();g.setBox(e)}a.setStyle("zIndex",h);a.show();a.setBox(e)},_hideModalMask:function(){var b=this.mask,a=this.maskShim;if(b&&b.isVisible()){b.maskTarget=undefined;b.hide();if(a){a.hide()}}},_onMaskClick:function(){if(this.front){this.front.focus()}},getMaskBox:function(){var a=this.mask.maskTarget;if(a.dom===document.body){return{height:Math.max(document.body.scrollHeight,Ext.dom.Element.getDocumentHeight()),width:Math.max(document.body.scrollWidth,document.documentElement.clientWidth),x:0,y:0}}else{return a.getBox()}},_onContainerResize:function(){var c=this,b=c.mask,a=c.maskShim,d;if(b&&b.isVisible()){b.hide();if(a){a.hide()}d=c.getMaskBox();if(a){a.setSize(d);a.show()}b.setSize(d);b.show()}},register:function(b){var c=this,a=b.afterHide;if(b.zIndexManager){b.zIndexManager.unregister(b)}b.zIndexManager=c;c.map[b.id]=b;c.zIndexStack.push(b);b.afterHide=function(){a.apply(b,arguments);c.onComponentHide(b)}},unregister:function(a){var b=this,c=b.map;delete a.zIndexManager;if(c&&c[a.id]){delete c[a.id];delete a.afterHide;Ext.Array.remove(b.zIndexStack,a);b._activateLast()}},get:function(a){return a.isComponent?a:this.map[a]},bringToFront:function(b,d){var c=this,a=false,e=c.zIndexStack;b=c.get(b);if(b!==c.front){Ext.Array.remove(e,b);if(b.preventBringToFront){e.unshift(b)}else{e.push(b)}c.assignZIndices();if(!d){c._activateLast()}a=true;c.front=b;if(b.modal){c._showModalMask(b)}}return a},sendToBack:function(a){var b=this;a=b.get(a);Ext.Array.remove(b.zIndexStack,a);b.zIndexStack.unshift(a);b.assignZIndices();this._activateLast();return a},hideAll:function(){var b=this.map,a,c;for(c in b){if(b.hasOwnProperty(c)){a=b[c];if(a.isComponent&&a.isVisible()){a.hide()}}}},hide:function(){var d=0,b=this.zIndexStack,a=b.length,c;this.tempHidden=[];for(;d0;){b=a[c];if(b.isComponent&&e.call(d||b,b)===false){return}}},destroy:function(){var b=this,c=b.map,a,d;for(d in c){if(c.hasOwnProperty(d)){a=c[d];if(a.isComponent){a.destroy()}}}Ext.destroy(b.mask);Ext.destroy(b.maskShim);delete b.zIndexStack;delete b.map;delete b.container;delete b.targetEl}},1,0,0,0,0,0,[Ext,"ZIndexManager",Ext,"WindowGroup"],function(){Ext.WindowManager=Ext.WindowMgr=new this()}));(Ext.cmd.derive("Ext.Queryable",Ext.Base,{isQueryable:true,query:function(a){a=a||"*";return Ext.ComponentQuery.query(a,this)},queryBy:function(g,e){var c=[],b=this.getRefItems(true),d=0,a=b.length,h;for(;d[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="tabbar"}else{if(a.isMenu){b="menu"}else{b=a.getXType()}}}return b},getPrefixConfig:Ext.emptyFn,getSuffixConfig:Ext.emptyFn,getOverflowCls:function(){return""}},1,0,0,0,0,0,[Ext.layout.container.boxOverflow,"None",Ext.layout.boxOverflow,"None"],0));(Ext.cmd.derive("Ext.toolbar.Item",Ext.Component,{alternateClassName:"Ext.Toolbar.Item",enable:Ext.emptyFn,disable:Ext.emptyFn,focus:Ext.emptyFn},0,["tbitem"],["component","tbitem","box"],{component:true,tbitem:true,box:true},["widget.tbitem"],0,[Ext.toolbar,"Item",Ext.Toolbar,"Item"],0));(Ext.cmd.derive("Ext.toolbar.Separator",Ext.toolbar.Item,{alternateClassName:"Ext.Toolbar.Separator",baseCls:Ext.baseCSSPrefix+"toolbar-separator",focusable:false,ariaRole:"separator"},0,["tbseparator"],["tbseparator","component","tbitem","box"],{tbseparator:true,component:true,tbitem:true,box:true},["widget.tbseparator"],0,[Ext.toolbar,"Separator",Ext.Toolbar,"Separator"],0));(Ext.cmd.derive("Ext.button.Manager",Ext.Base,{singleton:true,alternateClassName:"Ext.ButtonToggleManager",groups:{},pressedButton:null,buttonSelector:"."+Ext.baseCSSPrefix+"btn",init:function(){var a=this;if(!a.initialized){Ext.getDoc().on({keydown:a.onDocumentKeyDown,mouseup:a.onDocumentMouseUp,scope:a});a.initialized=true}},onDocumentKeyDown:function(c){var a=c.getKey(),b;if(a===c.SPACE||a===c.ENTER){b=c.getTarget(this.buttonSelector);if(b){Ext.getCmp(b.id).onClick(c)}}},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;ad){h=s.constrainedMax;o=d}else{if(kd){g=s.constrainedMax;n=d}else{if(k0){a.hideAll()}},a)},hideAll:function(){var c=this.active,b,a,d;if(c&&c.length>0){b=Ext.Array.slice(c.items);d=b.length;for(a=0;a50&&b){if(Ext.isIE9m&&!Ext.getDoc().contains(h.target)){return}else{for(c=0;c/,beginLayout:function(c){var b=this,a=b.owner,d=a.text;b.callParent(arguments);c.btnWrapContext=c.getEl("btnWrap");c.btnElContext=c.getEl("btnEl");c.btnInnerElContext=c.getEl("btnInnerEl");c.btnIconElContext=c.getEl("btnIconEl");if(d&&b.htmlRE.test(d)){c.isHtmlText=true;a.btnInnerEl.setStyle("line-height","normal");a.btnInnerEl.setStyle("padding-top","")}},beginLayoutCycle:function(c){var a=this.owner,e=this.lastWidthModel,d=this.lastHeightModel,g=a.btnInnerEl,b=a.getFrameInfo().table;this.callParent(arguments);if(e&&!e.shrinkWrap&&c.widthModel.shrinkWrap){a.btnWrap.setStyle("height","");a.btnEl.setStyle("height","");g.setStyle("line-height","");if(b){g.setStyle("width","")}}if(b&&d&&!d.shrinkWrap&&c.heightModel.shrinkWrap){g.setStyle("height","")}},calculate:function(d){var h=this,c=h.owner,j=d.btnElContext,g=d.btnInnerElContext,m=d.btnWrapContext,e=Math.max,b,k,l,a;h.callParent(arguments);if(d.heightModel.shrinkWrap){l=c.btnEl.getHeight();if(d.isHtmlText){h.centerInnerEl(d,l);h.ieCenterIcon(d,l)}}else{b=d.getProp("height");if(b){k=b-d.getFrameInfo().height-d.getPaddingInfo().height;l=k;if((c.menu||c.split)&&c.arrowAlign==="bottom"){l-=m.getPaddingInfo().bottom}a=l;if((c.icon||c.iconCls||c.glyph)&&(c.iconAlign==="top"||c.iconAlign==="bottom")){a-=g.getPaddingInfo().height}m.setProp("height",e(0,k));j.setProp("height",e(0,l));if(d.isHtmlText){h.centerInnerEl(d,l)}else{g.setProp("line-height",e(0,a)+"px")}h.ieCenterIcon(d,l)}else{if(b!==0){h.done=false}}}},centerInnerEl:function(e,d){var c=this,b=e.btnInnerElContext,a=c.owner.btnInnerEl.getHeight();if(e.heightModel.shrinkWrap&&(da){b.setProp("padding-top",Math.round((d-a)/2)+b.getPaddingInfo().top)}}},ieCenterIcon:function(c,b){var a=this.owner.iconAlign;if((Ext.isIEQuirks||Ext.isIE6)&&(a==="left"||a==="right")){c.btnIconElContext.setHeight(b)}},publishInnerWidth:function(b,a){if(this.owner.getFrameInfo().table){b.btnInnerElContext.setWidth(a-b.getFrameInfo().width-b.getPaddingInfo().width-b.btnWrapContext.getPaddingInfo().width)}}},0,0,0,0,["layout.button"],0,[Ext.layout.component,"Button"],0));(Ext.cmd.derive("Ext.util.TextMetrics",Ext.Base,{statics:{shared:null,measure:function(a,d,e){var b=this,c=b.shared;if(!c){c=b.shared=new b(a,e)}c.bind(a);c.setFixedWidth(e||"auto");return c.getSize(d)},destroy:function(){var a=this;Ext.destroy(a.shared);a.shared=null}},constructor:function(a,d){var c=this,b=Ext.getBody().createChild({role:"presentation",cls:Ext.baseCSSPrefix+"textmetrics"});c.measure=b;if(a){c.bind(a)}b.position("absolute");b.setLocalXY(-1000,-1000);b.hide();if(d){b.setWidth(d)}},getSize:function(c){var b=this.measure,a;b.update(c);a=b.getSize();b.update("");return a},bind:function(a){var b=this;b.el=Ext.get(a);b.measure.setStyle(b.el.getStyles("font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing","word-break"))},setFixedWidth:function(a){this.measure.setWidth(a)},getWidth:function(a){this.measure.dom.style.width="auto";return this.getSize(a).width},getHeight:function(a){return this.getSize(a).height},destroy:function(){var a=this;a.measure.remove();delete a.el;delete a.measure}},1,0,0,0,0,0,[Ext.util,"TextMetrics"],function(){Ext.Element.addMethods({getTextWidth:function(c,b,a){return Ext.Number.constrain(Ext.util.TextMetrics.measure(this.dom,Ext.value(c,this.dom.innerHTML,true)).width,b||0,a||1000000)}})}));(Ext.cmd.derive("Ext.button.Button",Ext.Component,{alternateClassName:"Ext.Button",isButton:true,componentLayout:"button",hidden:false,disabled:false,pressed:false,tabIndex:0,enableToggle:false,menuAlign:"tl-bl?",showEmptyMenu:false,textAlign:"center",clickEvent:"click",preventDefault:true,handleMouseEvents:true,tooltipType:"qtip",baseCls:Ext.baseCSSPrefix+"btn",pressedCls:"pressed",overCls:"over",focusCls:"focus",menuActiveCls:"menu-active",hrefTarget:"_blank",destroyMenu:true,ariaRole:"button",childEls:["btnEl","btnWrap","btnInnerEl","btnIconEl"],renderTpl:[' {splitCls}','{childElCls}" unselectable="on">','','',"{text}","",'background-image:url({iconUrl});','font-family:{glyphFontFamily};">','&#{glyph}; ',"","","",'','',' title="{closeText}" aria-label="{closeText}"',"",">","",""],scale:"small",allowedScales:["small","medium","large"],iconAlign:"left",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(Ext.baseCSSPrefix+"unselectable");a.callParent(arguments);a.addEvents("click","toggle","mouseover","mouseout","menushow","menuhide","menutriggerover","menutriggerout","textchange","iconchange","glyphchange");if(a.menu){a.split=true;a.setMenu(a.menu,false)}if(a.url){a.href=a.url}if(a.href&&!a.hasOwnProperty("preventDefault")){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}a.glyphCls=a.baseCls+"-glyph"},getActionEl:function(){return this.el},getFocusEl:function(){return this.el},setComponentCls:function(){var b=this,a=b.getComponentCls();if(!Ext.isEmpty(b.oldCls)){b.removeClsWithUI(b.oldCls);b.removeClsWithUI(b.pressedCls)}b.oldCls=a;b.addClsWithUI(a)},getComponentCls:function(){var b=this,a;if(b.iconCls||b.icon||b.glyph){a=[b.text?"icon-text-"+b.iconAlign:"icon"]}else{if(b.text){a=["noicon"]}else{a=[]}}if(b.pressed){a[a.length]=b.pressedCls}return a},getElConfig:function(){var c=this,b=c.callParent(),a=c.getHref(),d=c.hrefTarget;if(b.tag==="a"){if(!c.disabled){b.tabIndex=c.tabIndex}if(a){b.href=a;if(d){b.target=d}}}return b},beforeRender:function(){var a=this;a.callParent();a.oldCls=a.getComponentCls();a.addClsWithUI(a.oldCls);Ext.applyIf(a.renderData,a.getTemplateArgs())},setMenu:function(d,c){var b=this,a=b.menu;if(a&&c!==false&&b.destroyMenu){a.destroy()}if(a){a.ownerCmp=a.ownerButton=null}if(d){d=Ext.menu.Manager.get(d);d.ownerCmp=d.ownerButton=b;b.mon(d,{scope:b,show:b.onMenuShow,hide:b.onMenuHide});if(!a){b.split=true;if(b.rendered){b.btnWrap.addCls(b.getSplitCls());b.updateLayout()}}b.menu=d}else{if(b.rendered){b.btnWrap.removeCls(b.getSplitCls());b.updateLayout()}b.split=false;b.menu=null}},onRender:function(){var c=this,d,a,b;c.doc=Ext.getDoc();c.callParent(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(c.menu){c.keyMap=new Ext.util.KeyMap({target:c.el,key:Ext.EventObject.DOWN,handler:c.onDownKey,scope:c})}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)},getTemplateArgs:function(){var c=this,b=c.glyph,d=Ext._glyphFontFamily,a;if(typeof b==="string"){a=b.split("@");b=a[0];d=a[1]}return{innerCls:c.getInnerCls(),splitCls:c.getSplitCls(),iconUrl:c.icon,iconCls:c.iconCls,glyph:b,glyphCls:b?c.glyphCls:"",glyphFontFamily:d,text:c.text||" ",closeText:c.closeText}},setHref:function(a){this.href=a;this.el.dom.href=this.getHref()},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(a){this.params=a;this.el.dom.href=this.getHref()},getSplitCls:function(){var a=this;return a.split?(a.baseCls+"-"+a.arrowCls)+" "+(a.baseCls+"-"+a.arrowCls+"-"+a.arrowAlign):""},getInnerCls:function(){return this.textAlign?this.baseCls+"-inner-"+this.textAlign:""},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.setComponentCls();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.setComponentCls();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,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=""}else{if(c!=g){b.dom.innerHTML="&#"+g+";"}}if(a){b.setStyle("font-family",a)}}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},setTextAlign:function(c){var b=this,a=b.btnEl;if(a){a.removeCls(b.baseCls+"-inner-"+b.textAlign);a.addCls(b.baseCls+"-inner-"+c)}b.textAlign=c;return b},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()}if(a.menu&&a.destroyMenu){a.menu.destroy()}Ext.destroy(a.btnInnerEl,a.repeater);a.callParent()},onDestroy:function(){var a=this;if(a.rendered){a.doc.un("mouseover",a.monitorMouseOver,a);delete a.doc;Ext.destroy(a.keyMap);delete a.keyMap}Ext.button.Manager.unregister(a);a.callParent()},setHandler:function(b,a){this.handler=b;this.scope=a;return this},setText:function(c){c=c||"";var b=this,a=b.text||"";if(c!=a){b.text=c;if(b.rendered){b.btnInnerEl.update(c||" ");b.setComponentCls();if(Ext.isStrict&&Ext.isIE8){b.el.repaint()}b.updateLayout()}b.fireEvent("textchange",b,a,c)}return b},didIconStateChange:function(a,c){var b=Ext.isEmpty(c);return Ext.isEmpty(a)?!b:b},getText:function(){return this.text},toggle:function(c,a){var b=this;c=c===undefined?!b.pressed:!!c;if(c!==b.pressed){if(b.rendered){b[c?"addClsWithUI":"removeClsWithUI"](b.pressedCls)}b.pressed=c;if(!a){b.fireEvent("toggle",b,c);Ext.callback(b.toggleHandler,b.scope||b,[b,c])}}return b},maybeShowMenu:function(){var a=this;if(a.menu&&!a.hasVisibleMenu()&&!a.ignoreNextClick){a.showMenu(true)}},showMenu:function(b){var a=this,c=a.menu;if(a.rendered){if(a.tooltip&&Ext.quickTipsActive&&a.getTipAttr()!="title"){Ext.tip.QuickTipManager.getQuickTip().cancelShow(a.el)}if(c.isVisible()){c.hide()}if(!b||a.showEmptyMenu||c.items.getCount()>0){c.showBy(a.el,a.menuAlign)}}return a},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)},onClick:function(b){var a=this;a.doPreventDefault(b);if(b.type!=="keydown"&&b.button!==0){return}if(!a.disabled){a.doToggle();a.maybeShowMenu();a.fireHandler(b)}},doPreventDefault:function(a){if(this.preventDefault||(this.disabled&&this.getHref())&&a){a.preventDefault()}},fireHandler:function(c){var b=this,a=b.handler;if(b.fireEvent("click",b,c)!==false){if(a){a.call(b.scope||b,b,c)}}},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(g){var c=this,b=c.el,d=c.overMenuTrigger,h,a;if(c.split){h=(c.arrowAlign==="right")?g.getX()-c.getX():g.getY()-b.getY();a=c.getTriggerRegion();if(h>a.begin&&h(None)',constructor:function(b){var a=this;a.callParent(arguments);a.triggerButtonCls=a.triggerButtonCls||Ext.baseCSSPrefix+"box-menu-after";a.menuItems=[]},beginLayout:function(a){this.callParent(arguments);this.clearOverflow(a)},beginLayoutCycle:function(b,a){this.callParent(arguments);if(!a){this.clearOverflow(b);this.layout.cacheChildItems(b)}},onRemove:function(a){Ext.Array.remove(this.menuItems,a)},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:Ext.layout.container.Box.prototype.innerCls+" "+d.triggerButtonCls+" "+Ext.baseCSSPrefix+"toolbar-item",plain:a.usePlainButtons,ownerCt:a,ownerLayout:c,iconCls:Ext.baseCSSPrefix+d.getOwnerType(a)+"-more-icon",ui:a instanceof Ext.toolbar.Toolbar?"default-toolbar":"default",menu:d.menu,showEmptyMenu:true,getSplitCls:function(){return""}});return d.menuTrigger.getRenderTree()},getOverflowCls:function(){return Ext.baseCSSPrefix+this.layout.direction+"-box-overflow-body"},handleOverflow:function(d){var c=this,b=c.layout,g=b.names,e=d.state.boxPlan,a=[null,null];c.showTrigger(d);if(c.layout.direction!=="vertical"){a[g.heightIndex]=(e.maxSize-c.menuTrigger[g.getHeight]())/2;c.menuTrigger.setPosition.apply(c.menuTrigger,a)}return{reservedSpace:c.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)}},_asLayoutRoot:{isRoot:true},clearOverflow:function(h){var g=this,b=g.menuItems,e,c=0,d=b.length,a=g.layout.owner,j=g._asLayoutRoot;a.suspendLayouts();g.captureChildElements();g.hideTrigger();a.resumeLayouts();for(;cb){j=q.target;o.menuItems.push(j);j.hide()}}a.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(l,k){return l.isXType("buttongroup")&&!(k instanceof Ext.toolbar.Separator)};j.suspendLayouts();h.clearMenu();j.removeAll();for(;d=this.getMaxScrollPosition()},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,b){var j=this,e=j.layout,c=e.owner,h=e.names,a,d,g;k=j.getItem(k);if(k!==undefined){if(k==c.items.first()){g=0}else{if(k===c.items.last()){g=j.getMaxScrollPosition()}else{a=j.getItemVisibility(k);if(!a.fullyVisible){d=k.getBox(false,true);g=d[h.x];if(a.hiddenEnd){g-=(j.layout.innerCt[h.getWidth]()-d[h.width])}}}}if(g!==undefined){j.scrollTo(g,b)}}},getItemVisibility:function(k){var h=this,b=h.getItem(k).getBox(true,true),c=h.layout,g=c.names,e=b[g.x],d=e+b[g.width],a=h.getScrollPosition(),j=a+c.innerCt[g.getWidth]();return{hiddenStart:ej,fullyVisible:e>a&&d','","","{%if (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;c.callParent(arguments);c.flexSortFn=Ext.Function.bind(c.flexSort,c);c.initOverflowHandler();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}},_percentageRe:/^\s*(\d+(?:\.\d*)?)\s*[%]\s*$/,getItemSizePolicy:function(p,q){var l=this,j=l.sizePolicy,h=l.align,g=p.flex,n=h,k=l.names,b=p[k.width],o=p[k.height],d=l._percentageRe,c=d.test(b),e=(h=="stretch"),a=(h=="stretchmax"),m=l.constrainAlign;if(!q&&(e||g||c||(m&&!a))){q=l.owner.getSizeModel()}if(e){if(!d.test(o)&&q[k.height].shrinkWrap){n="stretchmax"}}else{if(!a){if(d.test(o)){n="stretch"}else{if(m&&!q[k.height].shrinkWrap){n="stretchmax"}else{n=""}}}}if(g||c){if(!q[k.width].shrinkWrap){j=j.flex}}return j[n]},flexSort:function(p,o){var m=this.names.maxWidth,e=this.names.minWidth,n=Infinity,l=p.target,s=o.target,j=l.flex,h=s.flex,t=0,c,q,k,d,r,g;k=l[m]||n;d=s[m]||n;c=l[e]||0;q=s[e]||0;r=isFinite(c)||isFinite(q);g=isFinite(k)||isFinite(d);if(r||g){if(g){t=k-d}if(t===0&&r){t=q-c}if(t===0){if(g){t=h-j}else{t=j-h}}}return t},isItemBoxParent:function(a){return true},isItemShrinkWrap:function(a){return true},roundFlex:function(a){return Math.ceil(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(d){var c=this,a=c.owner,g=a.stretchMaxPartner,b=c.innerCt.dom.style,e=c.names;d.boxNames=e;c.overflowHandler.beginLayout(d);if(typeof g==="string"){g=Ext.getCmp(g)||a.query(g)[0]}d.stretchMaxPartner=g&&d.context.getCmp(g);c.callParent(arguments);d.innerCtContext=d.getEl("innerCt",c);c.scrollParallel=a.scrollFlags[e.x];c.scrollPerpendicular=a.scrollFlags[e.y];if(c.scrollParallel){c.scrollPos=a.getTargetEl().dom[e.scrollLeft]}b.width="";b.height=""},beginLayoutCycle:function(e,a){var d=this,h=d.align,g=e.boxNames,b=d.pack,c=g.heightModel;d.overflowHandler.beginLayoutCycle(e,a);d.callParent(arguments);e.parallelSizeModel=e[g.widthModel];e.perpendicularSizeModel=e[c];e.boxOptions={align:h={stretch:h=="stretch",stretchmax:h=="stretchmax",center:h==g.center,bottom:h==g.afterY},pack:b={center:b=="center",end:b=="end"}};if(h.stretch&&e.perpendicularSizeModel.shrinkWrap){h.stretchmax=true;h.stretch=false}h.nostretch=!(h.stretch||h.stretchmax);if(e.parallelSizeModel.shrinkWrap){b.center=b.end=false}d.cacheFlexes(e);d.targetEl.setWidth(20000)},cacheFlexes:function(k){var u=this,l=k.boxNames,a=l.widthModel,d=l.heightModel,c=k.boxOptions.align.nostretch,o=0,b=k.childItems,q=b.length,s=[],m=0,j=l.minWidth,g=u._percentageRe,r=0,t=0,e,n,p,h;while(q--){n=b[q];e=n.target;if(n[a].calculated){n.flex=p=e.flex;if(p){o+=p;s.push(n);m+=e[j]||0}else{h=g.exec(e[l.width]);n.percentageParallel=parseFloat(h[1])/100;++r}}if(c&&n[d].calculated){h=g.exec(e[l.height]);n.percentagePerpendicular=parseFloat(h[1])/100;++t}}k.flexedItems=s;k.flexedMinSize=m;k.totalFlex=o;k.percentageWidths=r;k.percentageHeights=t;Ext.Array.sort(s,u.flexSortFn)},calculate:function(e){var c=this,b=c.getContainerSize(e),h=e.boxNames,d=e.state,g=d.boxPlan||(d.boxPlan={}),a=e.targetContext;g.targetSize=b;if(!d.parallelDone){d.parallelDone=c.calculateParallel(e,h,g)}if(!d.perpendicularDone){d.perpendicularDone=c.calculatePerpendicular(e,h,g)}if(d.parallelDone&&d.perpendicularDone){if(c.owner.dock&&(Ext.isIE7m||Ext.isIEQuirks)&&!c.owner.width&&!c.horizontal){g.isIEVerticalDock=true;g.calculatedWidth=g.maxSize+e.getPaddingInfo().width+e.getFrameInfo().width;if(a!==e){g.calculatedWidth+=a.getPaddingInfo().width}}c.publishInnerCtSize(e,c.reserveOffset?c.availableSpaceOffset:0);if(c.done&&e.boxOptions.align.stretchmax&&!d.stretchMaxDone){c.calculateStretchMax(e,h,g);d.stretchMaxDone=true}c.overflowHandler.calculate(e)}else{c.done=false}},calculateParallel:function(l,o,b){var G=this,A=o.width,a=l.childItems,t=o.beforeX,e=o.afterX,r=o.setWidth,B=a.length,y=l.flexedItems,s=y.length,w=l.boxOptions.pack,n=G.padding,d=b.targetSize,j=d[A],C=0,g=n[t],F=g+n[e]+G.scrollOffset+(G.reserveOffset?G.availableSpaceOffset:0),x=Ext.getScrollbarSize()[o.width],v,m,h,z,p,u,E,q,D,c,k;if(!l.parallelSizeModel.shrinkWrap&&!d[o.gotWidth]){return false}if(x&&G.scrollPerpendicular&&l.parallelSizeModel.shrinkWrap&&!l.boxOptions.align.stretch&&!l.perpendicularSizeModel.shrinkWrap){if(!l.state.perpendicularDone){return false}D=true}for(v=0;vb.targetSize[o.height])){q+=x;l[o.hasOverflowY]=true;l.target.componentLayout[o.setWidthInDom]=true;l[o.invalidateScrollY]=Ext.isStrict&&Ext.isIE8}l[o.setContentWidth](q);return true},calculatePerpendicular:function(u,K,z){var t=this,d=u.perpendicularSizeModel.shrinkWrap,b=z.targetSize,j=u.childItems,y=j.length,m=Math.max,l=K.height,n=K.setHeight,h=K.beforeY,s=K.y,H=t.padding,k=H[h],o=b[l]-k-H[K.afterY],E=u.boxOptions.align,p=E.stretch,q=E.stretchmax,N=E.center,M=E.bottom,G=t.constrainAlign,F=0,B=0,D=t.onBeforeConstrainInvalidateChild,A=t.onAfterConstrainInvalidateChild,a=Ext.getScrollbarSize().height,x,I,C,v,w,c,r,e,L,J,g;if(p||((N||M)&&!d)){if(isNaN(o)){return false}}if(!p&&!u.parallelSizeModel.shrinkWrap&&!u.state.parallelDone&&t.scrollParallel){return false}if(t.scrollParallel&&z.tooNarrow){if(d){J=true}else{o-=a;z.targetSize[l]-=a}}if(p){c=o}else{for(I=0;Io){r.invalidate({before:D,after:A,layout:t,childHeight:o,names:K});u.state.parallelDone=false}if(isNaN(F=m(F,C+v,r.target[K.minHeight]||0))){return false}}if(J){F+=a;u[K.hasOverflowX]=true;u.target.componentLayout[K.setHeightInDom]=true;u[K.invalidateScrollX]=Ext.isStrict&&Ext.isIE8}e=u.stretchMaxPartner;if(e){u.setProp("maxChildHeight",F);L=e.childItems;if(L&&L.length){F=m(F,e.getProp("maxChildHeight"));if(isNaN(F)){return false}}}u[K.setContentHeight](F+t.padding[l]+u.targetContext.getPaddingInfo()[l]);if(J){F-=a}z.maxSize=F;if(q){c=F}else{if(N||M||B){if(G){c=d?F:o}else{c=d?F:m(o,F)}c-=u.innerCtContext.getBorderInfo()[l]}}}for(I=0;I0){x=k+Math[t.alignRoundingMethod](w/2)}}else{if(M){x=m(0,c-x-r.props[l])}}}r.setProp(s,x)}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 a=this;if(!a.layout&&a.enableOverflow){a.layout={overflowHandler:"Menu"}}if(a.dock==="right"||a.dock==="left"){a.vertical=true}a.layout=Ext.applyIf(Ext.isString(a.layout)?{type:a.layout}:a.layout||{},{type:a.vertical?"vbox":"hbox",align:a.vertical?"stretchmax":"middle"});if(a.vertical){a.addClsWithUI("vertical")}if(a.ui==="footer"){a.ignoreBorderManagement=true}a.callParent();a.addEvents("overflowchange")},getRefItems:function(a){var e=this,b=e.callParent(arguments),d=e.layout,c;if(a&&e.enableOverflow){c=d.overflowHandler;if(c&&c.menu){b=b.concat(c.menu.getRefItems(a))}}return b},lookupComponent:function(e){var d=arguments;if(typeof e=="string"){var 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 this.callParent(d)},applyDefaults:function(a){if(!Ext.isString(a)){a=this.callParent(arguments)}return a},trackMenu:function(c,a){if(this.trackMenus&&c.menu){var d=a?"mun":"mon",b=this;b[d](c,"mouseover",b.onButtonOver,b);b[d](c,"menushow",b.onButtonMenuShow,b);b[d](c,"menuhide",b.onButtonMenuHide,b)}},onBeforeAdd:function(b){var c=this,a=b.isButton;if(a&&c.defaultButtonUI&&b.ui==="default"&&!b.hasOwnProperty("ui")){b.ui=c.defaultButtonUI}else{if((a||b.isFormField)&&c.ui!=="footer"){b.ui=b.ui+"-toolbar";b.addCls(b.baseCls+"-toolbar")}}if(b instanceof Ext.toolbar.Separator){b.setUI((c.vertical)?"vertical":"horizontal")}c.callParent(arguments)},onAdd:function(a){this.callParent(arguments);this.trackMenu(a)},onRemove:function(a){this.callParent(arguments);this.trackMenu(a,true)},getChildItemsToDisable:function(){return this.items.getRange()},onButtonOver:function(a){if(this.activeMenuBtn&&this.activeMenuBtn!=a){this.activeMenuBtn.hideMenu();a.showMenu();this.activeMenuBtn=a}},onButtonMenuShow:function(a){this.activeMenuBtn=a},onButtonMenuHide:function(a){delete this.activeMenuBtn}},0,["toolbar"],["container","toolbar","component","box"],{container:true,toolbar:true,component:true,box:true},["widget.toolbar"],0,[Ext.toolbar,"Toolbar",Ext,"Toolbar"],0));(Ext.cmd.derive("Ext.panel.AbstractPanel",Ext.container.Container,{baseCls:Ext.baseCSSPrefix+"panel",isPanel:true,contentPaddingProperty:"bodyPadding",shrinkWrapDock:false,componentLayout:"dock",childEls:["body"],renderTpl:["{% this.renderDockedItems(out,values,0); %}",(Ext.isIE7m||Ext.isIEQuirks)?'
     
    ':"",'
    {bodyCls}',' {baseCls}-body-{ui}',' {parent.baseCls}-body-{parent.ui}-{.}','{childElCls}"',' role="{bodyRole}" role="presentation"',' style="{bodyStyle}">',"{%this.renderContainer(out,values);%}","
    ","{% this.renderDockedItems(out,values,1); %}"],bodyPosProps:{x:"x",y:"y"},border:true,emptyArray:[],initComponent:function(){this.initBorderProps();this.callParent()},initBorderProps:function(){var a=this;if(a.frame&&a.border&&a.bodyBorder===undefined){a.bodyBorder=false}if(a.frame&&a.border&&(a.bodyBorder===false||a.bodyBorder===0)){a.manageBodyBorders=true}},beforeDestroy:function(){this.destroyDockedItems();this.callParent()},initItems:function(){this.callParent();this.initDockingItems()},initRenderData:function(){var a=this,b=a.callParent();a.initBodyStyles();a.protoBody.writeTo(b);delete a.protoBody;return b},getComponent:function(a){var b=this.callParent(arguments);if(b===undefined&&!Ext.isNumber(a)){b=this.getDockedComponent(a)}return b},getProtoBody:function(){var b=this,a=b.protoBody;if(!a){b.protoBody=a=new Ext.util.ProtoElement({cls:b.bodyCls,style:b.bodyStyle,clsProp:"bodyCls",styleProp:"bodyStyle",styleIsText:true})}return a},initBodyStyles:function(){var b=this,a=b.getProtoBody();if(b.bodyPadding!==undefined){if(b.layout.managePadding){a.setStyle("padding",0)}else{a.setStyle("padding",this.unitizeBox((b.bodyPadding===true)?5:b.bodyPadding))}}b.initBodyBorder()},initBodyBorder:function(){var a=this;if(a.frame&&a.bodyBorder){if(!Ext.isNumber(a.bodyBorder)){a.bodyBorder=1}a.getProtoBody().setStyle("border-width",this.unitizeBox(a.bodyBorder))}},getCollapsedDockedItems:function(){var a=this;return a.header===false||a.collapseMode=="placeholder"?a.emptyArray:[a.getReExpander()]},setBodyStyle:function(b,d){var c=this,a=c.rendered?c.body:c.getProtoBody();if(Ext.isFunction(b)){b=b()}if(arguments.length==1){if(Ext.isString(b)){b=Ext.Element.parseStyles(b)}a.setStyle(b)}else{a.setStyle(b,d)}return c},addBodyCls:function(b){var c=this,a=c.rendered?c.body:c.getProtoBody();a.addCls(b);return c},removeBodyCls:function(b){var c=this,a=c.rendered?c.body:c.getProtoBody();a.removeCls(b);return c},addUIClsToElement:function(b){var c=this,a=c.callParent(arguments);c.addBodyCls([Ext.baseCSSPrefix+b,c.baseCls+"-body-"+b,c.baseCls+"-body-"+c.ui+"-"+b]);return a},removeUIClsFromElement:function(b){var c=this,a=c.callParent(arguments);c.removeBodyCls([Ext.baseCSSPrefix+b,c.baseCls+"-body-"+b,c.baseCls+"-body-"+c.ui+"-"+b]);return a},addUIToElement:function(){var a=this;a.callParent(arguments);a.addBodyCls(a.baseCls+"-body-"+a.ui)},removeUIFromElement:function(){var a=this;a.callParent(arguments);a.removeBodyCls(a.baseCls+"-body-"+a.ui)},getTargetEl:function(){return this.body},applyTargetCls:function(a){this.getProtoBody().addCls(a)},getRefItems:function(a){var b=this.callParent(arguments);return this.getDockingRefItems(a,b)},setupRenderTpl:function(a){this.callParent(arguments);this.setupDockingRenderTpl(a)}},0,0,["container","component","box"],{container:true,component:true,box:true},0,[["docking",Ext.container.DockingContainer]],[Ext.panel,"AbstractPanel"],0));(Ext.cmd.derive("Ext.panel.Header",Ext.container.Container,{isHeader:true,defaultType:"tool",indicateDrag:false,weight:-1,componentLayout:"body",childEls:["body"],renderTpl:['
    {parent.baseCls}-body-{parent.ui}-{.}"',' style="{bodyStyle}" role="presentation">',"{%this.renderContainer(out,values)%}","
    "],headingTpl:['',' role="{headerRole}"',"",">{title}"],shrinkWrap:3,titlePosition:0,headerCls:Ext.baseCSSPrefix+"header",initComponent:function(){var g=this,e=g.hasOwnProperty("titlePosition"),c=g.items,a=e?g.titlePosition:(c?c.length:0),b=[g.orientation,g.getDockName()],d=g.ownerCt;g.addEvents("click","dblclick");g.indicateDragCls=g.headerCls+"-draggable";g.title=g.title||" ";g.tools=g.tools||[];c=g.items=(c?Ext.Array.slice(c):[]);g.orientation=g.orientation||"horizontal";g.dock=(g.dock)?g.dock:(g.orientation=="horizontal")?"top":"left";if(d?(d.border===false&&!d.frame):g.border===false){b.push(g.orientation+"-noborder")}g.addClsWithUI(b);g.addCls([g.headerCls,g.headerCls+"-"+g.orientation]);if(g.indicateDrag){g.addCls(g.indicateDragCls)}if(g.iconCls||g.icon||g.glyph){g.initIconCmp();if(!e&&!c.length){++a}c.push(g.iconCmp)}g.titleCmp=new Ext.Component({ariaRole:"presentation",focusable:false,noWrap:true,flex:1,rtl:g.rtl,id:g.id+"_hd",style:g.titleAlign?("text-align:"+g.titleAlign):"",cls:g.headerCls+"-text-container "+g.baseCls+"-text-container "+g.baseCls+"-text-container-"+g.ui,renderTpl:g.getTpl("headingTpl"),renderData:{title:g.title,cls:g.baseCls,headerCls:g.headerCls,headerRole:g.headerRole,ui:g.ui},childEls:["textEl"],autoEl:{unselectable:"on"},listeners:{render:g.onTitleRender,scope:g}});g.layout=(g.orientation=="vertical")?{type:"vbox",align:"center",alignRoundingMethod:"ceil"}:{type:"hbox",align:"middle",alignRoundingMethod:"floor"};Ext.Array.push(c,g.tools);g.tools.length=0;g.callParent();if(c.lengthb){if(l){m.removeCls(e)}m.addCls(n)}}}},onAdd:function(b,a){var c=this.tools;this.callParent(arguments);if(b.isTool){c.push(b);c[b.type]=b}},initRenderData:function(){return Ext.applyIf(this.callParent(),{bodyCls:this.bodyCls,bodyTargetCls:this.bodyTargetCls,headerCls:this.headerCls})},getDockName:function(){return this.dock},getFramingInfoCls:function(){var c=this,b=c.callParent(),a=c.ownerCt;if(!c.expanding&&a&&(a.collapsed||c.isCollapsedExpander)){b+="-"+a.collapsedCls}return b+"-"+c.dock}},0,["header"],["container","component","header","box"],{container:true,component:true,header:true,box:true},["widget.header"],0,[Ext.panel,"Header"],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){setTimeout(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(){this.callParent();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.getEl(),a=this.getDragEl(),b=a.style;this._resizeProxy();if(this.centerFrame){this.setDelta(Math.round(parseInt(b.width,10)/2),Math.round(parseInt(b.height,10)/2))}this.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(b){var a=b.getPageX(),c=b.getPageY();this.autoOffset(a,c);this.setDragElPos(a,c)},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:Ext.baseCSSPrefix+"dd-drag-repair",ariaRole:"presentation",constructor:function(a){var b=this;a=a||{};Ext.apply(b,{hideMode:"visibility",hidden:true,floating:true,id:b.id||Ext.id(),cls:Ext.baseCSSPrefix+"dd-drag-proxy "+this.dropNotAllowed,shadow:a.shadow||false,renderTo:Ext.getDetachedBody()});b.callParent(arguments);this.dropStatus=this.dropNotAllowed},dropAllowed:Ext.baseCSSPrefix+"dd-drop-ok",dropNotAllowed:Ext.baseCSSPrefix+"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=Ext.baseCSSPrefix+"dd-drag-proxy ";c.el.replaceCls(a+c.dropAllowed,a+c.dropNotAllowed);c.dropStatus=c.dropNotAllowed;if(b){c.ghost.update("")}},update:function(a){if(typeof a=="string"){this.ghost.update(a)}else{this.ghost.update("");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){this.callParent();if(a){this.reset(true)}},stop:function(){if(this.anim&&this.anim.isAnimated&&this.anim.isAnimated()){this.anim.stop()}},sync:function(){this.el.sync()},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.hideUnders(true);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:Ext.baseCSSPrefix+"dd-drop-ok",dropNotAllowed:Ext.baseCSSPrefix+"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})}this.callParent([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();this.callParent(arguments)}},onBeforeDrag:function(a,b){return true},onStartDrag:Ext.emptyFn,alignElWithMouse:function(){this.proxy.ensureAttachedToBody(true);return this.callParent(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(){this.callParent();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.remove();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:Ext.baseCSSPrefix+"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;c.callParent([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(b){var a=b.getPageX(),c=b.getPageY();this.autoOffset(a,c)},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.util.Memento",Ext.Base,(function(){function d(j,h,k,g){j[g?g+k:k]=h[k]}function c(h,g,j){delete h[j]}function e(l,k,m,j){var g=j?j+m:m,h=l[g];if(h||l.hasOwnProperty(g)){a(k,m,h)}}function a(h,j,g){if(Ext.isDefined(g)){h[j]=g}else{delete h[j]}}function b(h,n,m,j,k){if(n){if(Ext.isArray(j)){var l,g=j.length;for(l=0;la){if(k.anchorToTarget){k.defaultAlign="r-l";if(k.mouseOffset){k.mouseOffset[0]*=-1}}k.anchor="right";return k.getTargetXY()}if(b[1]j){if(k.anchorToTarget){k.defaultAlign="b-t";if(k.mouseOffset){k.mouseOffset[1]*=-1}}k.anchor="bottom";return k.getTargetXY()}}k.anchorCls=Ext.baseCSSPrefix+"tip-anchor-"+k.getAnchorPosition();k.anchorEl.addCls(k.anchorCls);k.targetCounter=0;return b}else{d=k.getMouseOffset();return(k.targetXY)?[k.targetXY[0]+d[0],k.targetXY[1]+d[1]]:d}},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},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)b.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)}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,scope:b});b.clearStart();b.active=false;if(a){b.dragEnded=true;b.onEnd(c);b.fireEvent("dragend",b,c)}b._constrainRegion=Ext.EventObject.dragTracked=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]]},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.topid="{id}" class="{inputRowCls}">','','',"{beforeLabelTpl}",'',' class="{labelCls}"',' style="{labelStyle}"',' unselectable="on"',">","{beforeLabelTextTpl}",'{fieldLabel}','','{labelSeparator}',"","","{afterLabelTextTpl}","","{afterLabelTpl}","","",'',"{beforeBodyEl}","","{beforeLabelTpl}",'","{afterLabelTpl}","","{beforeSubTpl}","{[values.$comp.getSubTplMarkup(values)]}","{afterSubTpl}","","{afterBodyEl}","","",'',"","",'',"{afterBodyEl}","","","",{disableFormats:true}],activeErrorsTpl:undefined,htmlActiveErrorsTpl:['','
      ','','
      {fieldLabel}
      ',"
      ",'
    • {.}
    • ',"
    ","
    "],plaintextActiveErrorsTpl:['','','{fieldLabel}\n',"",'\n{.}',""],isFieldLabelable:true,formItemCls:Ext.baseCSSPrefix+"form-item",labelCls:Ext.baseCSSPrefix+"form-item-label",errorMsgCls:Ext.baseCSSPrefix+"form-error-msg",baseBodyCls:Ext.baseCSSPrefix+"form-item-body",inputRowCls:Ext.baseCSSPrefix+"form-item-input-row",fieldBodyCls:"",clearCls:Ext.baseCSSPrefix+"clear",invalidCls:Ext.baseCSSPrefix+"form-invalid",fieldLabel:undefined,labelAlign:"left",labelWidth:100,labelPad:5,labelSeparator:":",hideLabel:false,hideEmptyLabel:true,preventMark:false,autoFitErrors:true,msgTarget:"qtip",noWrap:true,labelableInsertions:["beforeBodyEl","afterBodyEl","beforeLabelTpl","afterLabelTpl","beforeSubTpl","afterSubTpl","beforeLabelTextTpl","afterLabelTextTpl","labelAttrTpl"],labelableRenderProps:["allowBlank","id","labelAlign","fieldBodyCls","extraFieldBodyCls","baseBodyCls","clearCls","labelSeparator","msgTarget","inputRowCls"],initLabelable:function(){var a=this,b=a.padding;if(b){a.padding=undefined;a.extraMargins=Ext.Element.parseBox(b)}if(!a.activeErrorsTpl){if(a.msgTarget=="title"){a.activeErrorsTpl=a.plaintextActiveErrorsTpl}else{a.activeErrorsTpl=a.htmlActiveErrorsTpl}}a.addCls(Ext.plainTableCls);a.addCls(a.formItemCls);a.lastActiveError="";a.addEvents("errorchange");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(b){b=b||"";var c=this,d=c.labelSeparator,a=c.labelEl;c.fieldLabel=b;if(c.rendered){if(Ext.isEmpty(b)&&c.hideEmptyLabel){a.parent().setDisplayed("none")}else{if(d){b=c.trimLabelSeparator()+d}a.update(b);a.parent().setDisplayed("")}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 b=this,c,d,a=b.labelAlign==="top";if(!Ext.form.Labelable.errorIconWidth){d=Ext.getBody().createChild({style:"position:absolute",cls:Ext.baseCSSPrefix+"form-invalid-icon"});Ext.form.Labelable.errorIconWidth=d.getWidth()+d.getMargin("l");d.remove()}c=Ext.copyTo({inFormLayout:b.ownerLayout&&b.ownerLayout.type==="form",inputId:b.getInputId(),labelOnLeft:!a,hideLabel:!b.hasVisibleLabel(),fieldLabel:b.getFieldLabel(),labelCellStyle:b.getLabelCellStyle(),labelCellAttrs:b.getLabelCellAttrs(),labelCls:b.getLabelCls(),labelStyle:b.getLabelStyle(),bodyColspan:b.getBodyColspan(),externalError:!b.autoFitErrors,errorMsgCls:b.getErrorMsgCls(),errorIconWidth:Ext.form.Labelable.errorIconWidth,boxLabel:b.boxLabel},b,b.labelableRenderProps,true);b.getInsertionRenderData(c,b.labelableInsertions);return c},xhooks:{beforeRender:function(){var a=this;a.setFieldDefaults(a.getHierarchyState().fieldDefaults);if(a.ownerLayout){a.addCls(Ext.baseCSSPrefix+a.ownerLayout.type+"-form-item")}},onRender:function(){var c=this,d,a,b={};if(c.extraMargins){d=c.el.getMargin();for(a in d){if(d.hasOwnProperty(a)){b["margin-"+a]=(d[a]+c.extraMargins[a])+"px"}}c.el.setStyle(b)}}},hasVisibleLabel:function(){if(this.hideLabel){return false}return !(this.hideEmptyLabel&&!this.getFieldLabel())},getLabelWidth:function(){var a=this;if(!a.hasVisibleLabel()){return 0}return a.labelWidth+a.labelPad},getBodyColspan:function(){var b=this,a;if(b.msgTarget==="side"&&(!b.autoFitErrors||b.hasActiveError())){a=1}else{a=2}if(b.labelAlign!=="top"&&!b.hasVisibleLabel()){a++}return a},getLabelCls:function(){var b=this.labelCls+" "+Ext.dom.Element.unselectableCls,a=this.labelClsExtra;return a?b+" "+a:b},getLabelCellStyle:function(){var b=this,a=b.hideLabel||(!b.getFieldLabel()&&b.hideEmptyLabel);return a?"display:none;":""},getErrorMsgCls:function(){var b=this,a=(b.hideLabel||(!b.fieldLabel&&b.hideEmptyLabel));return b.errorMsgCls+(!a&&b.labelAlign==="top"?" "+Ext.baseCSSPrefix+"lbl-top-err-icon":"")},getLabelCellAttrs:function(){var c=this,b=c.labelAlign,a="";if(b!=="top"){a='valign="top" halign="'+b+'" width="'+(c.labelWidth+c.labelPad)+'"'}return a+' class="'+Ext.baseCSSPrefix+'field-label-cell"'},getLabelStyle:function(){var d=this,c=d.labelPad,b=d.labelWidth,a="";if(d.labelAlign!=="top"){if(b){a="width:"+b+"px;"}if(c){a+=d.getLabelStyleMarginProp()+c+"px;"}}else{if(c){a="margin-bottom:"+c+"px;"}}return a+(d.labelStyle||"")},getLabelStyleMarginProp:function(){return"margin-right:"},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(c){var b=this,a;c=Ext.Array.from(c);a=b.getTpl("activeErrorsTpl");b.activeErrors=c;b.activeError=a.apply({fieldLabel:b.fieldLabel,errors:c,listCls:Ext.plainListCls});b.renderActiveError()},unsetActiveError:function(){delete this.activeError;delete this.activeErrors;this.renderActiveError()},renderActiveError:function(){var c=this,b=c.getActiveError(),a=!!b;if(b!==c.lastActiveError){c.fireEvent("errorchange",c,b);c.lastActiveError=b}if(c.rendered&&!c.isDestroyed&&!c.preventMark){c.el[a?"addCls":"removeCls"](c.invalidCls);if(c.errorEl){c.errorEl.dom.innerHTML=b}}},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"],0));(Ext.cmd.derive("Ext.form.field.Field",Ext.Base,{isFormField:true,disabled:false,submitValue:true,validateOnChange:true,suspendCheckChange:0,initField:function(){this.addEvents("change","validitychange","dirtychange");this.initValue()},initValue:function(){var a=this;a.value=a.transformOriginalValue(a.value);a.originalValue=a.lastValue=a.value;a.suspendCheckChange++;a.setValue(a.value);a.suspendCheckChange--},transformOriginalValue:Ext.identityFn,getFieldIdentifier:function(){return this.isEditorComponent?this.dataIndex:this.name},getName:function(){return this.name},getValue:function(){return this.value},setValue:function(b){var a=this;a.value=b;a.checkChange();return a},isEqual:function(b,a){return String(b)===String(a)},isEqualAsString:function(b,a){return String(Ext.value(b,""))===String(Ext.value(a,""))},getSubmitData:function(){var a=this,b=null;if(!a.disabled&&a.submitValue){b={};b[a.getName()]=""+a.getValue()}return b},getModelData:function(a,b){var c=this,d=null;if(!c.disabled&&(c.submitValue||!b)){d={};d[c.getFieldIdentifier()]=c.getValue()}return d},reset:function(){var a=this;a.beforeReset();a.setValue(a.originalValue);a.clearInvalid();delete a.wasValid},beforeReset:Ext.emptyFn,resetOriginalValue:function(){this.originalValue=this.getValue();this.checkDirty()},checkChange:function(){var c=this,b,a;if(!c.suspendCheckChange){b=c.getValue();a=c.lastValue;if(!c.isDestroyed&&c.didValueChange(b,a)){c.lastValue=b;c.fireEvent("change",c,b,a);c.onChange(b,a)}}},didValueChange:function(b,a){return !this.isEqual(b,a)},onChange:function(b,a){if(this.validateOnChange){this.validate()}this.checkDirty()},isDirty:function(){var a=this;return !a.disabled&&!a.isEqual(a.getValue(),a.originalValue)},checkDirty:function(){var a=this,b=a.isDirty();if(b!==a.wasDirty){a.fireEvent("dirtychange",a,b);a.onDirtyChange(b);a.wasDirty=b}},onDirtyChange:Ext.emptyFn,getErrors:function(a){return[]},isValid:function(){var a=this;return a.disabled||Ext.isEmpty(a.getErrors())},validate:function(){var a=this,b=a.isValid();if(b!==a.wasValid){a.wasValid=b;a.fireEvent("validitychange",a,b)}return b},batchChanges:function(a){try{this.suspendCheckChange++;a()}catch(b){throw b}finally{this.suspendCheckChange--}this.checkChange()},isFileUpload:function(){return false},extractFileInput:function(){return null},markInvalid:Ext.emptyFn,clearInvalid:Ext.emptyFn},0,0,0,0,0,0,[Ext.form.field,"Field"],0));(Ext.cmd.derive("Ext.layout.component.field.Field",Ext.layout.component.Auto,{type:"field",naturalSizingProp:"size",beginLayout:function(c){var b=this,a=b.owner;b.callParent(arguments);c.labelStrategy=b.getLabelStrategy();c.errorStrategy=b.getErrorStrategy();c.labelContext=c.getEl("labelEl");c.bodyCellContext=c.getEl("bodyEl");c.inputContext=c.getEl("inputEl");c.errorContext=c.getEl("errorEl");if(Ext.isIE7m&&Ext.isStrict&&c.inputContext){b.ieInputWidthAdjustment=c.inputContext.getPaddingInfo().width+c.inputContext.getBorderInfo().width}c.labelStrategy.prepare(c,a);c.errorStrategy.prepare(c,a)},beginLayoutCycle:function(g){var e=this,a=e.owner,c=g.widthModel,b=a[e.naturalSizingProp],d;e.callParent(arguments);if(c.shrinkWrap){e.beginLayoutShrinkWrap(g)}else{if(c.natural){if(typeof b=="number"&&!a.inputWidth){e.beginLayoutFixed(g,(d=b*6.5+20),"px")}else{e.beginLayoutShrinkWrap(g)}g.setWidth(d,false)}else{e.beginLayoutFixed(g,"100","%")}}},beginLayoutFixed:function(c,b,e){var a=c.target,d=a.inputEl,g=a.inputWidth;a.el.setStyle("table-layout","fixed");a.bodyEl.setStyle("width",b+e);if(d){if(g){d.setStyle("width",g+"px")}else{d.setStyle("width",a.stretchInputElFixed?"100%":"")}}c.isFixed=true},beginLayoutShrinkWrap:function(b){var a=b.target,c=a.inputEl,d=a.inputWidth;if(c&&c.dom){c.dom.removeAttribute("size");if(d){c.setStyle("width",d+"px")}else{c.setStyle("width","")}}a.el.setStyle("table-layout","auto");a.bodyEl.setStyle("width","")},finishedLayout:function(b){var a=this.owner;this.callParent(arguments);b.labelStrategy.finishedLayout(b,a);b.errorStrategy.finishedLayout(b,a)},calculateOwnerHeightFromContentHeight:function(b,a){return a},measureContentHeight:function(a){return a.el.getHeight()},measureContentWidth:function(a){return a.el.getWidth()},measureLabelErrorHeight:function(a){return a.labelStrategy.getHeight(a)+a.errorStrategy.getHeight(a)},onFocus:function(){this.getErrorStrategy().onFocus(this.owner)},getLabelStrategy:function(){var b=this,c=b.labelStrategies,a=b.owner.labelAlign;return c[a]||c.base},getErrorStrategy:function(){var c=this,a=c.owner,d=c.errorStrategies,b=a.msgTarget;return !a.preventMark&&Ext.isString(b)?(d[b]||d.elementId):d.none},labelStrategies:(function(){var a={prepare:function(e,b){var c=b.labelCls+"-"+b.labelAlign,d=b.labelEl;if(d){d.addCls(c)}},getHeight:function(){return 0},finishedLayout:Ext.emptyFn};return{base:a,top:Ext.applyIf({getHeight:function(e){var c=e.labelContext,d=c.props,b=d.height;if(b===undefined){d.height=b=c.el.getHeight()+c.getMarginInfo().height}return b}},a),left:a,right:a}}()),errorStrategies:(function(){function d(h){var j=Ext.layout.component.field.Field.tip,k;if(j&&j.isVisible()){k=j.activeTarget;if(k&&k.el===h.getActionEl().dom){j.toFront(true)}}}var c=Ext.applyIf,b=Ext.emptyFn,a=Ext.baseCSSPrefix+"form-invalid-icon",g,e={prepare:function(k,h){var j=h.errorEl;if(j){j.setDisplayed(false)}},getHeight:function(){return 0},onFocus:b,finishedLayout:b};return{none:e,side:c({prepare:function(l,j){var n=j.errorEl,k=j.sideErrorCell,h=j.hasActiveError(),m;if(!g){g=(m=Ext.getBody().createChild({style:"position:absolute",cls:a})).getWidth();m.remove()}n.addCls(a);n.set({"data-errorqtip":j.getActiveError()||""});if(j.autoFitErrors){n.setDisplayed(h)}else{n.setVisible(h)}if(k&&j.autoFitErrors){k.setDisplayed(h)}j.bodyEl.dom.colSpan=j.getBodyColspan();Ext.layout.component.field.Field.initTip()},onFocus:d},e),under:c({prepare:function(k,h){var l=h.errorEl,j=Ext.baseCSSPrefix+"form-invalid-under";l.addCls(j);l.setDisplayed(h.hasActiveError())},getHeight:function(l){var h=0,j,k;if(l.target.hasActiveError()){j=l.errorContext;k=j.props;h=k.height;if(h===undefined){k.height=h=j.el.getHeight()}}return h}},e),qtip:c({prepare:function(j,h){Ext.layout.component.field.Field.initTip();h.getActionEl().dom.setAttribute("data-errorqtip",h.getActiveError()||"")},onFocus:d},e),title:c({prepare:function(j,h){h.getActionEl().dom.setAttribute("title",h.getActiveError()||"")}},e),elementId:c({prepare:function(j,h){var k=Ext.fly(h.msgTarget);if(k){k.dom.innerHTML=h.getActiveError()||"";k.setDisplayed(h.hasActiveError())}}},e)}}()),statics:{initTip:function(){var a=this.tip;if(!a){a=this.tip=Ext.create("Ext.tip.QuickTip",{ui:"form-invalid"});a.tagConfig=Ext.apply({},{attribute:"errorqtip"},a.tagConfig)}},destroyTip:function(){var a=this.tip;if(a){a.destroy();delete this.tip}}}},0,0,0,0,["layout.field"],0,[Ext.layout.component.field,"Field"],0));(Ext.cmd.derive("Ext.form.field.Base",Ext.Component,{alternateClassName:["Ext.form.Field","Ext.form.BaseField"],fieldSubTpl:[' 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} {editableCls} {inputCls}" autocomplete="off"/>',{disableFormats:true}],subTplInsertions:["inputAttrTpl"],inputType:"text",invalidText:"The value in this field is invalid",fieldCls:Ext.baseCSSPrefix+"form-field",focusCls:"form-focus",dirtyCls:Ext.baseCSSPrefix+"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,componentLayout:"field",readOnly:false,readOnlyCls:Ext.baseCSSPrefix+"form-readonly",validateOnBlur:true,hasFocus:false,baseCls:Ext.baseCSSPrefix+"field",maskOnDisable:false,stretchInputElFixed:true,initComponent:function(){var a=this;a.callParent();a.subTplData=a.subTplData||{};a.addEvents("specialkey","writeablechange");a.initLabelable();a.initField();if(!a.name){a.name=a.getInputId()}if(a.readOnly){a.addCls(a.readOnlyCls)}a.addCls(Ext.baseCSSPrefix+"form-type-"+a.inputType)},getInputId:function(){return this.inputId||(this.inputId=this.id+"-inputEl")},getSubTplData:function(){var c=this,b=c.inputType,a=c.getInputId(),d;d=Ext.apply({id:a,cmpId:c.id,name:c.name||a,disabled:c.disabled,readOnly:c.readOnly,value:c.getRawValue(),type:b,fieldCls:c.fieldCls,fieldStyle:c.getFieldStyle(),tabIdx:c.tabIndex,inputCls:c.inputCls,typeCls:Ext.baseCSSPrefix+"form-"+(b==="password"?"text":b),role:c.ariaRole},c.subTplData);c.getInsertionRenderData(d,c.subTplInsertions);return d},applyRenderSelectors:function(){var a=this;a.callParent();a.addChildEls("inputEl");a.inputEl=a.el.getById(a.getInputId())},getSubTplMarkup:function(){return this.getTpl("fieldSubTpl").apply(this.getSubTplData())},initRenderTpl:function(){var a=this;if(!a.hasOwnProperty("renderTpl")){a.renderTpl=a.getTpl("labelableRenderTpl")}return a.callParent()},initRenderData:function(){return Ext.applyIf(this.callParent(),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.renderActiveError()},getFocusEl:function(){return this.inputEl},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.value(b.rawValue,""));b.rawValue=a;return a},setRawValue:function(b){var a=this;b=Ext.value(a.transformRawValue(b),"");a.rawValue=b;if(a.inputEl){a.bindPropertyChange(false);a.inputEl.dom.value=b;a.bindPropertyChange(true)}return b},transformRawValue:Ext.identityFn,valueToRaw:function(a){return""+Ext.value(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;a.callParent(arguments);if(a.setReadOnlyOnBoxReady){a.setReadOnly(a.readOnly)}},onDisable:function(){var a=this,b=a.inputEl;a.callParent();if(b){b.dom.disabled=true;if(a.hasActiveError()){a.clearInvalid();a.needsValidateOnEnable=true}}},onEnable:function(){var a=this,b=a.inputEl;a.callParent();if(b){b.dom.disabled=false;if(a.needsValidateOnEnable){delete a.needsValidateOnEnable;a.forceValidation=true;a.isValid();delete a.forceValidation}}},setReadOnly:function(c){var a=this,b=a.inputEl;c=!!c;a[c?"addCls":"removeCls"](a.readOnlyCls);a.readOnly=c;if(b){b.dom.readOnly=c}else{if(a.rendering){a.setReadOnlyOnBoxReady=true}}a.fireEvent("writeablechange",a,c)},fireKey:function(a){if(a.isSpecialKey()){this.fireEvent("specialkey",this,new Ext.EventObjectImpl(a))}},initEvents:function(){var h=this,j=h.inputEl,c,g,k=h.checkChangeEvents,l=h.ignoreChangeRe,b=k.length,d,a;if(j){h.mon(j,Ext.EventManager.getKeyEvent(),h.fireKey,h);c=new Ext.util.DelayedTask(h.checkChange,h);h.onChangeEvent=g=function(m){if(!(m.type=="propertychange"&&l.test(m.browserEvent.propertyName))){c.delay(h.checkChangeBuffer)}};for(d=0;dg.maxLength){k.push(l(g.maxLengthText,g.maxLength))}if(d){if(!h[d](m,g)){k.push(g.vtypeText||h[d+"Text"])}}if(j&&!j.test(m)){k.push(g.regexText||g.invalidText)}return k},selectText:function(j,a){var h=this,c=h.getRawValue(),d=true,g=h.inputEl.dom,e,b;if(c.length>0){j=j===e?0:j;a=a===e?c.length:a;if(g.setSelectionRange){g.setSelectionRange(j,a)}else{if(g.createTextRange){b=g.createTextRange();b.moveStart("character",j);b.moveEnd("character",a-c.length);b.select()}}d=Ext.isGecko||Ext.isOpera}if(d){h.focus()}},autoSize:function(){var a=this;if(a.grow&&a.rendered){a.autoSizing=true;a.updateLayout()}},afterComponentLayout:function(){var b=this,a;b.callParent(arguments);if(b.autoSizing){a=b.inputEl.getWidth();if(a!==b.lastInputWidth){b.fireEvent("autosize",b,a);b.lastInputWidth=a;delete b.autoSizing}}},onDestroy:function(){var a=this;a.callParent();if(a.inputFocusTask){a.inputFocusTask.cancel();a.inputFocusTask=null}}},0,["textfield"],["component","field","textfield","box"],{component:true,field:true,textfield:true,box:true},["widget.textfield"],0,[Ext.form.field,"Text",Ext.form,"TextField",Ext.form,"Text"],0));(Ext.cmd.derive("Ext.util.KeyNav",Ext.Base,{alternateClassName:"Ext.KeyNav",statics:{keyOptions:{left:37,right:39,up:38,down:40,space:32,pageUp:33,pageDown:34,del:46,backspace:8,home:36,end:35,enter:13,esc:27,tab:9}},constructor:function(a){var b=this;if(arguments.length===2){b.legacyConstructor.apply(b,arguments);return}b.setConfig(a)},legacyConstructor:function(b,a){this.setConfig(Ext.apply({target:b},a))},setConfig:function(b){var e=this,c={target:b.target,ignoreInputFields:b.ignoreInputFields,eventName:e.getKeyEvent("forceKeyDown" in b?b.forceKeyDown:e.forceKeyDown,b.eventName)},g,a,j,d,h;if(e.map){e.map.destroy()}if(b.processEvent){c.processEvent=b.processEvent;c.processEventScope=b.processEventScope||e}if(b.keyMap){g=e.map=b.keyMap}else{g=e.map=new Ext.util.KeyMap(c);e.destroyKeyMap=true}a=Ext.util.KeyNav.keyOptions;j=b.scope||e;for(d in b){h=b[d];if(h&&(d.length===1||(d=a[d])||(!isNaN(d=parseInt(d,10))))){if(typeof h==="function"){h={handler:h,defaultEventAction:(b.defaultEventAction!==undefined)?b.defaultEventAction:e.defaultEventAction}}g.addBinding({key:d,ctrl:h.ctrl,shift:h.shift,alt:h.alt,handler:Ext.Function.bind(e.handleEvent,h.scope||j,h.handler||h.fn,true),defaultEventAction:(h.defaultEventAction!==undefined)?h.defaultEventAction:e.defaultEventAction})}}g.disable();if(!b.disabled){g.enable()}},handleEvent:function(c,b,a){return a.call(this,b)},disabled:false,defaultEventAction:"stopEvent",forceKeyDown:false,eventName:"keypress",destroy:function(a){if(this.destroyKeyMap){this.map.destroy(a)}delete this.map},enable:function(){if(this.map){this.map.enable();this.disabled=false}},disable:function(){if(this.map){this.map.disable()}this.disabled=true},setDisabled:function(a){this.map.setDisabled(a);this.disabled=a},getKeyEvent:function(b,a){if(b||(Ext.EventManager.useKeyDown&&!a)){return"keydown"}else{return a||this.eventName}}},1,0,0,0,0,0,[Ext.util,"KeyNav",Ext,"KeyNav"],0));(Ext.cmd.derive("Ext.util.ComponentDragger",Ext.dd.DragTracker,{autoStart:500,constructor:function(a,b){this.comp=a;this.initialConstrainTo=b.constrainTo;this.callParent([b])},onStart:function(c){var b=this,a=b.comp;b.startPosition=a.getXY();if(a.ghost&&!a.liveDrag){b.proxy=a.ghost();b.dragTarget=b.proxy.header.el}if(b.constrain||b.constrainDelegate){b.constrainTo=b.calculateConstrainRegion()}if(a.beginDrag){a.beginDrag()}},calculateConstrainRegion:function(){var j=this,g=j.comp,h=j.initialConstrainTo,e=g.constraintInsets,k,b,d,c=j.proxy?j.proxy.el:g.el,a=(!j.constrainDelegate&&c.shadow&&g.constrainShadow&&!c.shadowDisabled)?c.shadow.getShadowSize():0;if(!(h instanceof Ext.util.Region)){k=Ext.fly(h);h=k.getViewRegion();h.right=h.left+k.dom.clientWidth}else{h=h.copy()}if(e){e=Ext.isObject(e)?e:Ext.Element.parseBox(e);h.adjust(e.top,e.right,e.bottom,e.length)}if(a){h.adjust(a[0],-a[1],-a[2],a[3])}if(!j.constrainDelegate){b=Ext.fly(j.dragTarget).getRegion();d=c.getRegion();h.adjust(b.top-d.top,b.right-d.right,b.bottom-d.bottom,b.left-d.left)}return h},onDrag:function(c){var b=this,a=(b.proxy&&!b.comp.liveDrag)?b.proxy:b.comp,d=b.getOffset(b.constrain||b.constrainDelegate?"dragTarget":null);a.setPagePosition(b.startPosition[0]+d[0],b.startPosition[1]+d[1])},onEnd:function(b){var a=this.comp;if(a.isDestroyed||a.destroying){return}if(this.proxy&&!a.liveDrag){a.unghost()}if(a.endDrag){a.endDrag()}}},1,0,0,0,0,0,[Ext.util,"ComponentDragger"],0));(Ext.cmd.derive("Ext.window.Window",Ext.panel.Panel,{alternateClassName:"Ext.Window",baseCls:Ext.baseCSSPrefix+"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:Ext.baseCSSPrefix+"window-item",initialAlphaNum:/^[a-z0-9]/,overlapHeader:true,ignoreHeaderBorderManagement:true,alwaysFramed:true,isRootCfg:{isRoot:true},isWindow:true,ariaRole:"dialog",initComponent:function(){var a=this;a.frame=false;a.callParent();a.addEvents("resize","maximize","minimize","restore");if(a.plain){a.addClsWithUI("plain")}a.addStateEvents(["maximize","restore","resize","dragend"])},getElConfig:function(){var b=this,a;a=b.callParent();a.tabIndex=-1;return a},getState:function(){var b=this,d=b.callParent()||{},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;c.callParent(arguments);c.focusEl=c.el;if(c.maximizable){c.header.on({scope:c,dblclick:c.toggleMaximize})}},afterRender:function(){var a=this,c=a.header,b;a.callParent();if(a.maximized){a.maximized=false;a.maximize();if(c){c.removeCls(c.indicateDragCls)}}if(a.closable){b=a.getKeyMap();b.on(27,a.onEsc,a)}else{b=a.keyMap}if(b&&a.hidden){b.disable()}},initDraggable:function(){this.initSimpleDraggable()},initResizable:function(){this.callParent(arguments);if(this.maximized){this.resizer.disable()}},onEsc:function(a,c){var b=Ext.FocusManager;if(!Ext.enableFocusManager||b.focusedCmp===this){c.stopEvent();this.close()}},beforeDestroy:function(){var a=this;if(a.rendered){delete a.animateTarget;a.hide();Ext.destroy(a.keyMap)}a.callParent()},addTools:function(){var a=this,c=a.tools,b=[];a.callParent();if(a.minimizable){c.push({type:"minimize",handler:Ext.Function.bind(a.minimize,a,b)})}if(a.maximizable){c.push({type:"maximize",handler:Ext.Function.bind(a.toggleMaximize,a,b)})}},getFocusEl:function(){return this.getDefaultFocus()},getDefaultFocus:function(){var c=this,b,d=c.defaultButton||c.defaultFocus,a;if(d!==undefined){if(Ext.isNumber(d)){b=c.query("button")[d]}else{if(Ext.isString(d)){a=d;if(a.match(c.initialAlphaNum)){b=c.down("#"+a)}if(!b){b=c.down(a)}}else{if(d.focus){b=d}}}}return b||c.el},onFocus:function(){var b=this,a;if(Ext.enableFocusManager||((a=b.getDefaultFocus())===b)){b.callParent(arguments)}else{a.focus()}},onShow:function(){var a=this;a.callParent(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()}a.callParent(arguments)},onWindowResize:function(){var b=this,a;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()}a.callParent(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()}a.callParent(arguments)},maximize:function(b){var e=this,j=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:Ext.isNumber(d)?d:null,height:Ext.isNumber(a)?a:null};e.restorePos=e.getPosition(true)}if(j){j.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(j){j.removeCls(j.indicateDragCls)}}if(e.resizer){e.resizer.disable()}e.el.addCls(Ext.baseCSSPrefix+"window-maximized");e.container.addCls(Ext.baseCSSPrefix+"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(Ext.baseCSSPrefix+"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(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(Ext.baseCSSPrefix+"window-maximized-ct");c.syncMonitorWindowResize();if(!b){c.el.enableShadow(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.isDestroyed;if(d&&!a){if(!c){Ext.EventManager.onWindowResize(b.onWindowResize,b,{buffer:1});b._monitoringResize=true}}else{if(c){Ext.EventManager.removeResizeListener(b.onWindowResize,b);b._monitoringResize=false}}},toggleMaximize:function(){return this[this.maximized?"restore":"maximize"]()},createGhost:function(){var a=this.callParent(arguments);a.xtype="window";return a}},0,["window"],["container","component","box","window","panel"],{container:true,component:true,box:true,window:true,panel:true},["widget.window"],0,[Ext.window,"Window",Ext,"Window"],0));(Ext.cmd.derive("Ext.layout.component.field.TextArea",Ext.layout.component.field.Text,{type:"textareafield",canGrowWidth:false,naturalSizingProp:"cols",beginLayout:function(a){this.callParent(arguments);a.target.inputEl.setStyle("height","")},measureContentHeight:function(b){var e=this,a=e.owner,l=e.callParent(arguments),c,j,h,g,d,k;if(a.grow&&!b.state.growHandled){c=b.inputContext;j=a.inputEl;d=j.getWidth(true);h=Ext.util.Format.htmlEncode(j.dom.value)||" ";h+=a.growAppend;h=h.replace(/\n/g,"
    ");k=Ext.util.TextMetrics.measure(j,h,d).height+c.getBorderInfo().height+c.getPaddingInfo().height;k=Ext.Number.constrain(k,a.growMin,a.growMax);c.setHeight(k);b.state.growHandled=true;c.domBlock(e,"height");l=NaN}return l}},0,0,0,0,["layout.textareafield"],0,[Ext.layout.component.field,"TextArea"],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-",cols:20,rows:4,enterIsSpecial:false,preventScrollbars:false,componentLayout:"textareafield",setGrowSizePolicy:Ext.emptyFn,returnRe:/\r/g,inputCls:Ext.baseCSSPrefix+"form-textarea",getSubTplData:function(){var c=this,b=c.getFieldStyle(),a=c.callParent();if(c.grow){if(c.preventScrollbars){a.fieldStyle=(b||"")+";overflow:hidden;height:"+c.growMin+"px"}}Ext.applyIf(a,{cols:c.cols,rows:c.rows});return a},afterRender:function(){var a=this;a.callParent(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)},transformOriginalValue:function(a){return this.stripReturns(a)},getValue:function(){return this.stripReturns(this.callParent())},valueToRaw:function(a){a=this.stripReturns(a);return this.callParent([a])},stripReturns:function(a){if(a&&typeof a==="string"){a=a.replace(this.returnRe,"")}return a},onPaste:function(b){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 b=this,a;if(b.grow&&b.rendered){b.updateLayout();a=b.inputEl.getHeight();if(a!==b.lastInputHeight){b.fireEvent("autosize",b,a);b.lastInputHeight=a}}},beforeDestroy:function(){var a=this.pasteTask;if(a){a.cancel();this.pasteTask=null}this.callParent()}},0,["textarea","textareafield"],["component","field","textfield","textarea","box","textareafield"],{component:true,field:true,textfield:true,textarea:true,box:true,textareafield:true},["widget.textarea","widget.textareafield"],0,[Ext.form.field,"TextArea",Ext.form,"TextArea"],0));(Ext.cmd.derive("Ext.form.field.Display",Ext.form.field.Base,{alternateClassName:["Ext.form.DisplayField","Ext.form.Display"],ariaRole:"textbox",fieldSubTpl:['
    style="{fieldStyle}"',' class="{fieldCls}">{value}
    ',{compiled:true,disableFormats:true}],readOnly:true,fieldCls:Ext.baseCSSPrefix+"form-display-field",fieldBodyCls:Ext.baseCSSPrefix+"form-display-field-body",htmlEncode:false,noWrap:false,validateOnChange:false,initEvents:Ext.emptyFn,submitValue:false,valueToRaw:function(a){if(!a&&a!==0){return""}else{return a}},isDirty:function(){return false},isValid:function(){return true},validate:function(){return true},getRawValue:function(){return this.rawValue},setRawValue:function(b){var a=this;b=Ext.value(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(){var a=this.callParent(arguments);a.value=this.getDisplayValue();return a}},0,["displayfield"],["component","field","box","displayfield"],{component:true,field:true,box:true,displayfield:true},["widget.displayfield"],0,[Ext.form.field,"Display",Ext.form,"DisplayField",Ext.form,"Display"],0));(Ext.cmd.derive("Ext.layout.container.Anchor",Ext.layout.container.Auto,{alternateClassName:"Ext.layout.AnchorLayout",type:"anchor",defaultAnchor:"100%",parseAnchorRE:/^(r|right|b|bottom)$/i,manageOverflow:true,beginLayoutCycle:function(c){var j=this,a=0,g,k,e,d,b,h;j.callParent(arguments);e=c.childItems;b=e.length;for(d=0;d','
    {text}
    ',"",'"],componentLayout:"progressbar",ariaRole:"progressbar",initComponent:function(){this.callParent();this.addEvents("update")},initRenderData:function(){var a=this;return Ext.apply(a.callParent(),{internalText:!a.hasOwnProperty("textEl"),text:a.text||" ",percentage:a.value?a.value*100:0})},onRender:function(){var a=this;a.callParent(arguments);if(a.textEl){a.textEl=Ext.get(a.textEl);a.updateText(a.text)}else{a.textEl=a.el.select("."+a.baseCls+"-text")}},updateProgress:function(d,e,a){var c=this,b=c.value;c.value=d||0;if(e){c.updateText(e)}if(c.rendered&&!c.isDestroyed){if(a===true||(a!==false&&c.animate)){c.bar.stopAnimation();c.bar.animate(Ext.apply({from:{width:(b*100)+"%"},to:{width:(c.value*100)+"%"}},c.animate))}else{c.bar.setStyle("width",(c.value*100)+"%")}}c.fireEvent("update",c,c.value,e);return c},updateText:function(b){var a=this;a.text=b;if(a.rendered){a.textEl.update(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()}}b.callParent()}},0,["progressbar"],["component","progressbar","box"],{component:true,progressbar:true,box:true},["widget.progressbar"],0,[Ext,"ProgressBar"],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:Ext.baseCSSPrefix+"message-box-info",WARNING:Ext.baseCSSPrefix+"message-box-warning",QUESTION:Ext.baseCSSPrefix+"message-box-question",ERROR:Ext.baseCSSPrefix+"message-box-error",hideMode:"offsets",closeAction:"hide",resizable:false,title:" ",defaultMinWidth:250,defaultMaxWidth:600,defaultMinHeight:110,defaultMaxHeight:500,minWidth:null,maxWidth:null,minHeight:null,maxHeight:null,constrain:true,cls:[Ext.baseCSSPrefix+"message-box",Ext.baseCSSPrefix+"hide-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"},iconHeight:35,iconWidth:50,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){var b=this,c,d;if(b.cfg.prompt||b.cfg.multiline){if(b.cfg.multiline){d=b.textArea}else{d=b.textField}c=d.getValue();d.reset()}b.hide();b.userCallback(a.itemId,c,b.cfg)},hide:function(){var b=this,a=b.cfg?b.cfg.cls:"";b.progressBar.reset();if(a){b.removeCls(a)}b.callParent(arguments)},constructor:function(a){var b=this;b.callParent(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({width:e.iconWidth,height:e.iconHeight}),e.promptContainer=new Ext.container.Container({flex:1,layout:"anchor",items:[e.msg=new Ext.form.field.Display({id:b+"-displayfield",cls:e.baseCls+"-text"}),e.textField=new Ext.form.field.Text({id:b+"-textfield",anchor:"100%",enableKeyEvents:true,listeners:{keydown:e.onPromptKey,scope:e}}),e.textArea=new Ext.form.field.TextArea({id:b+"-textarea",anchor:"100%",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);e.callParent()},onClose:function(){var a=this.header.child("[type=close]");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(b){var q=this,n=0,d=true,s=q.buttonText,c=q.resizer,l=q.header,p=l&&!l.isHeader,t,r,j,g,m,o,e,a,h,k;q.updateButtonText();b=b||{};q.cfg=b;if(b.width){j=b.width}if(b.height){g=b.height}q.minWidth=b.minWidth||q.defaultMinWidth;q.maxWidth=b.maxWidth||q.defaultMaxWidth;q.minHeight=b.minHeight||q.defaultMinHeight;q.maxHeight=b.maxHeight||q.defaultMaxHeight;if(c){t=c.resizeTracker;c.minWidth=t.minWidth=q.minWidth;c.maxWidth=t.maxWidth=q.maxWidth;c.minHeight=t.minHeight=q.minHeight;c.maxHeight=t.maxHeight=q.maxHeight}delete q.defaultFocus;if(b.defaultFocus){q.defaultFocus=b.defaultFocus}q.animateTarget=b.animateTarget||undefined;q.modal=b.modal!==false;q.setTitle(b.title||(p&&l.title)||q.title);q.setIconCls(b.iconCls||(p&&l.iconCls)||q.iconCls);if(Ext.isObject(b.buttons)){q.buttonText=b.buttons;n=0}else{q.buttonText=b.buttonText||q.buttonText;n=Ext.isNumber(b.buttons)?b.buttons:0}n=n|q.updateButtonText();q.buttonText=s;Ext.suspendLayouts();delete q.width;delete q.height;if(j||g){if(j){q.setWidth(j)}if(g){q.setHeight(g)}}q.hidden=false;if(!q.rendered){q.render(Ext.getBody())}q.closable=b.closable!==false&&!b.wait;l=q.header;if(l){l.child("[type=close]").setVisible(q.closable);if(!b.title&&!q.closable&&!b.iconCls){l.hide()}else{l.show()}}q.liveDrag=!b.proxyDrag;q.userCallback=Ext.Function.bind(b.callback||b.fn||Ext.emptyFn,b.scope||Ext.global);q.setIcon(b.icon,b.iconWidth,b.iconHeight);a=q.msg;if(b.msg){a.setValue(b.msg);a.show()}else{a.hide()}o=q.textArea;e=q.textField;if(b.prompt||b.multiline){q.multiline=b.multiline;if(b.multiline){o.setValue(b.value);o.setHeight(b.defaultTextHeight||q.defaultTextHeight);o.show();e.hide();q.defaultFocus=o}else{e.setValue(b.value);o.hide();e.show();q.defaultFocus=e}}else{o.hide();e.hide()}h=q.progressBar;if(b.progress||b.wait){h.show();q.updateProgress(0,b.progressText);if(b.wait===true){h.wait(b.waitConfig)}}else{h.hide()}k=q.msgButtons;for(m=0;m<4;m++){if(n&Math.pow(2,m)){if(!q.defaultFocus){q.defaultFocus=k[m]}k[m].show();d=false}else{k[m].hide()}}if(d){q.bottomTb.hide()}else{q.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.AbstractComponent.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;c.callParent();return c},onShow:function(){this.callParent(arguments);this.center()},updateText:function(a){this.msg.setValue(a)},setIcon:function(d,c,a){var e=this,g=e.iconComponent,b=e.messageIconCls;if(b){g.removeCls(b)}if(d){g.show();g.setSize(c||e.iconWidth,a||e.iconHeight);g.addCls(Ext.baseCSSPrefix+"dlg-icon");g.addCls(e.messageIconCls=d)}else{g.removeCls(Ext.baseCSSPrefix+"dlg-icon");g.hide()}return e},updateProgress:function(b,a,c){this.progressBar.updateProgress(b,a);if(c){this.updateText(c)}return this},onEsc:function(){if(this.closable!==false){this.callParent(arguments)}},confirm:function(a,d,c,b){if(Ext.isString(a)){a={title:a,icon:this.QUESTION,msg:d,buttons:this.YESNO,callback:c,scope:b}}return this.show(a)},prompt:function(b,g,d,c,a,e){if(Ext.isString(b)){b={prompt:true,title:b,minWidth:this.minPromptWidth,msg:g,buttons:this.OKCANCEL,callback:d,scope:c,multiline:a,value:e}}return this.show(b)},wait:function(a,c,b){if(Ext.isString(a)){a={title:c,msg:a,closable:false,wait:true,modal:true,minWidth:this.minProgressWidth,waitConfig:b}}return this.show(a)},alert:function(a,d,c,b){if(Ext.isString(a)){a={title:a,msg:d,buttons:this.OK,fn:c,scope:b,minWidth:this.minWidth}}return this.show(a)},progress:function(a,c,b){if(Ext.isString(a)){a={title:a,msg:c,progress:true,progressText:b}}return this.show(a)}},1,["messagebox"],["container","component","box","window","panel","messagebox"],{container:true,component:true,box:true,window:true,panel:true,messagebox:true},["widget.messagebox"],0,[Ext.window,"MessageBox"],function(){Ext.MessageBox=Ext.Msg=new this()}));(Ext.cmd.derive("Ext.layout.container.Fit",Ext.layout.container.Container,{alternateClassName:"Ext.layout.FitLayout",itemCls:Ext.baseCSSPrefix+"fit-item",targetCls:Ext.baseCSSPrefix+"layout-fit",type:"fit",defaultMargins:{top:0,right:0,bottom:0,left:0},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;t.callParent(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'+a.view.emptyText+""}a.view.getComponentLayout().headerCt=a.headerCt;a.mon(a.view,{uievent:a.processEvent,scope:a});b.view=a.view;a.headerCt.view=a.view;if(a.hasListeners.viewcreated){a.fireEvent("viewcreated",a,a.view)}}return a.view},getColumnManager:function(){return this.columnManager},getVisibleColumnManager:function(){return this.visibleColumnManager},getTopLevelColumnManager:function(){var a=this.ownerLockable;return a?a.getColumnManager():this.getColumnManager()},getTopLevelVisibleColumnManager:function(){var a=this.ownerLockable;return a?a.getVisibleColumnManager():this.getVisibleColumnManager()},setAutoScroll:Ext.emptyFn,processEvent:function(h,k,l,a,j,d,c,m){var g=this,b;if(j!==-1){b=g.getColumnManager().getHeaderAtIndex(j);return b.processEvent.apply(b,arguments)}},determineScrollbars:function(){},invalidateScroller:function(){},scrollByDeltaY:function(b,a){this.getView().scrollBy(0,b,a)},scrollByDeltaX:function(b,a){this.getView().scrollBy(b,0,a)},afterCollapse:function(){this.saveScrollPos();this.callParent(arguments)},afterExpand:function(){this.callParent(arguments);this.restoreScrollPos()},saveScrollPos:Ext.emptyFn,restoreScrollPos:Ext.emptyFn,onHeaderResize:Ext.emptyFn,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(a,b){if(this.view.refreshCounter){this.view.refreshView()}},onHeaderShow:function(a,b){if(this.view.refreshCounter){this.view.refreshView()}},onHeadersChanged:function(b,c){var a=this;if(a.rendered&&!a.reconfiguring){a.view.refreshView();a.delayScroll()}},delayScroll:function(){var a=this.getScrollTarget().el;if(a){this.scrollTask.delay(10,null,null,[a.dom])}},onViewReady:function(){this.fireEvent("viewready",this)},onRestoreHorzScroll:function(){var a=this.scrollLeft;if(a){this.syncHorizontalScroll(this,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))},getSelectionModel:function(){var c=this,a=c.selModel,e,d,b;if(!a){a={};e=true}if(!a.events){b=a.selType||c.selType;e=!a.mode;a=c.selModel=Ext.create("selection."+b,a)}if(c.simpleSelect){d="SIMPLE"}else{if(c.multiSelect){d="MULTI"}}Ext.applyIf(a,{allowDeselect:c.allowDeselect});if(d&&e){a.setSelectionMode(d)}if(!a.hasRelaySetup){c.relayEvents(a,["selectionchange","beforeselect","beforedeselect","select","deselect"]);a.hasRelaySetup=true}if(c.disableSelection){a.locked=true}return a},getScrollTarget:function(){var a=this.getScrollerOwner(),b=a.query("tableview");return b[1]||b[0]},onHorizontalScroll:function(a,b){this.syncHorizontalScroll(b)},syncHorizontalScroll:function(e,b){var c=this,d=e.scrollLeft,a;b=b===true;if(c.rendered&&(b||d!==c.scrollLeft)){if(b){a=c.getScrollTarget();a.el.dom.scrollLeft=d}c.headerCt.el.dom.scrollLeft=d;c.scrollLeft=d}},onStoreLoad:Ext.emptyFn,getEditorParent:function(){return this.body},bindStore:function(b,c){var d=this,a=d.getView(),e=b&&b.buffered,g;if(b){d.store=b;g=d.findPlugin("bufferedrenderer");if(g){d.verticalScroller=g;if(g.store){g.bindStore(b)}}else{if(e){d.verticalScroller=g=d.addPlugin(Ext.apply({ptype:"bufferedrenderer"},d.initialConfig.verticalScroller))}}if(a.store!==b){a.bindStore(b,false)}d.mon(b,{load:d.onStoreLoad,scope:d});d.storeRelayers=d.relayEvents(b,["filterchange","groupchange"]);if(g){d.invalidateScrollerOnRefresh=false}if(d.invalidateScrollerOnRefresh!==undefined){a.preserveScrollOnRefresh=!d.invalidateScrollerOnRefresh}}else{d.unbindStore()}},unbindStore:function(){var c=this,b=c.store,a;if(b){c.store=null;c.mun(b,{load:c.onStoreLoad,scope:c});Ext.destroy(c.storeRelayers);a=c.view;if(a.store){a.bindStore(null)}}},reconfigure:function(b,e){var g=this,a=g.getView(),d,j=g.store,h=g.headerCt,c=h?h.items.getRange():g.columns;if(arguments.length===1&&Ext.isArray(b)){e=b;b=null}if(e){e=Ext.Array.slice(e)}g.reconfiguring=true;g.fireEvent("beforereconfigure",g,b,e,j,c);if(g.lockable){g.reconfigureLockable(b,e)}else{Ext.suspendLayouts();if(e){delete g.scrollLeft;h.removeAll();h.add(e)}if(b&&(b=Ext.StoreManager.lookup(b))!==j){if(g.store){g.unbindStore()}d=a.deferInitialRefresh;a.deferInitialRefresh=false;g.bindStore(b);a.deferInitialRefresh=d}else{g.getView().refreshView()}h.setSortState();Ext.resumeLayouts(true)}g.fireEvent("reconfigure",g,b,e,j,c);delete g.reconfiguring},beforeDestroy:function(){var a=this.scrollTask;if(a){a.cancel();this.scrollTask=null}this.callParent()},onDestroy:function(){var a=this;if(a.lockable){a.destroyLockable()}a.callParent();a.columns=a.storeRelayers=a.columnManager=a.visibleColumnManager=null},destroy:function(){var a=this;a.callParent();if(a.isDestroyed){a.view=a.selModel=a.headerCt=null}}},0,["tablepanel"],["container","component","box","panel","tablepanel"],{container:true,component:true,box:true,panel:true,tablepanel:true},["widget.tablepanel"],0,[Ext.panel,"Table"],0));(Ext.cmd.derive("Ext.util.Bindable",Ext.Base,{bindStore:function(b,c,a){a=a||"store";var d=this,e=d[a];if(!c&&e){d.onUnbindStore(e,c,a);if(b!==e&&e.autoDestroy){e.destroyStore()}else{d.unbindStoreListeners(e)}}if(b){b=Ext.data.StoreManager.lookup(b);d.bindStoreListeners(b);d.onBindStore(b,c,a)}d[a]=b||null;return d},getStore:function(){return this.store},unbindStoreListeners:function(a){var b=this.storeListeners;if(b){a.un(b)}},bindStoreListeners:function(a){var c=this,b=Ext.apply({},c.getStoreListeners(a));if(!b.scope){b.scope=c}c.storeListeners=b;a.on(b)},getStoreListeners:Ext.emptyFn,onUnbindStore:Ext.emptyFn,onBindStore:Ext.emptyFn},0,0,0,0,0,0,[Ext.util,"Bindable"],0));(Ext.cmd.derive("Ext.LoadMask",Ext.Component,{isLoadMask:true,msg:"Loading...",msgCls:Ext.baseCSSPrefix+"mask-loading",maskCls:Ext.baseCSSPrefix+"mask",cls:Ext.baseCSSPrefix+"mask-msg",useMsg:true,useTargetEl:false,ariaRole:"presentation",childEls:["msgEl","msgTextEl"],renderTpl:['
    {ariaAttr}',' class="{[values.$comp.msgCls]} ',Ext.baseCSSPrefix,'mask-msg-inner {childElCls}">','
    {msg}
    ',"
    "],constructor:function(b){var c=this,a;if(arguments.length===2){a=c.target=b;b=arguments[1]}else{a=b.target}c.callParent([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)}},getRenderTree:function(){return[{cls:this.maskCls,style:"display:none"},this.callParent()]},onRender:function(){this.callParent(arguments);this.maskEl=this.el.prev()},initRenderData:function(){var a=this.callParent(arguments);a.msg=this.msg||"";return a},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(c.hierarchyEventSource,{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(){this.callParent(arguments);this.el.$cache.skipGarbageCollection=true;this.maskEl.$cache.skipGarbageCollection=true},onOwnerToFront:function(a,b){this.maskEl.setStyle("zIndex",b+1);this.el.setStyle("zIndex",b+2)},onContainerShow:function(a){this.onComponentShow()},onContainerHide:function(a){this.onComponentHide()},onContainerExpand:function(a){this.onComponentShow()},onContainerCollapse:function(a){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.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.maskEl.setSize(a.getSize()).alignTo(a,"tl-tl")}b.el.center(b.maskEl)}},bindStore:function(a,b){var c=this;c.mixins.bindable.bindStore.apply(c,arguments);a=c.store;if(a&&a.isLoading()){c.onBeforeLoad()}},getStoreListeners:function(b){var d=this.onLoad,c=this.onBeforeLoad,a={cachemiss:c,cachefilled:d};if(!b.proxy.isSynchronous){a.beforeLoad=c;a.load=d}return a},onDisable:function(){this.callParent(arguments);if(this.loading){this.onLoad()}},getOwner:function(){return this.ownerCt||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(){if(this.isElement){this.ownerCt.unmask();this.fireEvent("hide",this);return}delete this.showNext;this.maskEl.setDisplayed(false);return this.callParent(arguments)},show:function(){if(this.isElement){this.ownerCt.mask(this.useMsg?this.msg:"",this.msgCls);this.fireEvent("show",this);return}this.maskEl.setDisplayed(true);return this.callParent(arguments)},afterShow:function(){var a=this;a.loading=true;a.callParent(arguments);if(a.hasOwnProperty("maskCls")){a.maskEl.dom.className=a.maskCls}if(a.useMsg){a.msgTextEl.update(a.msg)}else{a.el.hide()}a.sizeMask()},onLoad:function(){this.loading=false;this.hide()},beforeDestroy:function(){this.ownerCt=null;this.callParent()},onDestroy:function(){var a=this;if(a.isElement){a.ownerCt.unmask()}Ext.destroy(a.maskEl);a.callParent()}},1,["loadmask"],["component","box","loadmask"],{component:true,box:true,loadmask:true},["widget.loadmask"],[["bindable",Ext.util.Bindable]],[Ext,"LoadMask"],0));(Ext.cmd.derive("Ext.data.ResultSet",Ext.Base,{loaded:true,count:0,total:0,success:false,constructor:function(a){Ext.apply(this,a);this.totalRecords=this.total;if(a.count===undefined){this.count=this.records.length}}},1,0,0,0,0,0,[Ext.data,"ResultSet"],0));(Ext.cmd.derive("Ext.data.reader.Reader",Ext.Base,{alternateClassName:["Ext.data.Reader","Ext.data.DataReader"],totalProperty:"total",successProperty:"success",root:"",implicitIncludes:true,readRecordsOnFailure:true,isReader:true,applyDefaults:true,lastFieldGeneration:null,constructor:function(a){var b=this;b.mixins.observable.constructor.call(b,a);b.model=Ext.ModelManager.getModel(b.model);if(b.model&&b.model.prototype.fields){b.buildExtractors()}this.addEvents("exception")},setModel:function(a,d){var b=this,e=b.model,c=true;a=b.model=Ext.ModelManager.getModel(a);if(a&&e===a){c=b.lastFieldGeneration!==a.prototype.fields.generation}if(a){b.buildExtractors(c)}if(d&&b.proxy){b.proxy.setModel(a,true)}},read:function(a){var b;if(a){b=a.responseText?this.getResponseData(a):this.readRecords(a)}return b||this.nullResultSet},readRecords:function(c){var d=this,j,b,a,g,e,h,k;if(d.lastFieldGeneration!==d.model.prototype.fields.generation){d.buildExtractors(true)}d.rawData=c;c=d.getData(c);j=true;b=0;a=[];if(d.successProperty){h=d.getSuccess(c);if(h===false||h==="false"){j=false}}if(d.messageProperty){k=d.getMessage(c)}if(d.readRecordsOnFailure||j){g=Ext.isArray(c)?c:d.getRoot(c);if(g){e=g.length}if(d.totalProperty){h=parseInt(d.getTotal(c),10);if(!isNaN(h)){e=h}}if(g){a=d.extractData(g);b=a.length}}return new Ext.data.ResultSet({total:e||b,count:b,records:a,success:j,message:k})},extractData:function(l){var k=this,j=k.model,b=l.length,d=new Array(b),a,e,c,h,g;if(!l.length&&Ext.isObject(l)){l=[l];b=1}for(g=0;g',' ,__field{#} = fields.map["{name}"]\n',"",";\n","return function(dest, source, record) {\n",'','{% var fieldAccessExpression = this.createFieldAccessExpression(values, "__field" + xindex, "source");'," if (fieldAccessExpression) { %}",' value = {[ this.createFieldAccessExpression(values, "__field" + xindex, "source") ]};\n','',' dest["{name}"] = value === undefined ? __field{#}.convert(__field{#}.defaultValue, record) : __field{#}.convert(value, record);\n',''," if (value === undefined) {\n"," if (me.applyDefaults) {\n",'',' dest["{name}"] = __field{#}.convert(__field{#}.defaultValue, record);\n',"",' dest["{name}"] = __field{#}.defaultValue\n',""," };\n"," } else {\n",'',' dest["{name}"] = __field{#}.convert(value, record);\n',"",' dest["{name}"] = value;\n',""," };\n",""," if (value !== undefined) {\n",'',' dest["{name}"] = __field{#}.convert(value, record);\n',"",' dest["{name}"] = value;\n',""," }\n","","{% } else { %}",'','',' dest["{name}"] = __field{#}.convert(__field{#}.defaultValue, record);\n',"",' dest["{name}"] = __field{#}.defaultValue\n',"","","{% } %}","",'',' if (record && (internalId = {[ this.createFieldAccessExpression({mapping: values.clientIdProp}, null, "source") ]})) {\n',' record.{["internalId"]} = internalId;\n'," }\n","","};"],buildRecordDataExtractor:function(){var c=this,a=c.model.prototype,b={clientIdProp:a.clientIdProperty,fields:a.fields.items};c.recordDataExtractorTemplate.createFieldAccessExpression=function(){return c.createFieldAccessExpression.apply(c,arguments)};return Ext.functionFactory("fields",c.recordDataExtractorTemplate.apply(b)).call(c,c.model.prototype.fields)},destroyReader:function(){var a=this;delete a.proxy;delete a.model;delete a.convertRecordData;delete a.getId;delete a.getTotal;delete a.getSuccess;delete a.getMessage}},1,0,0,0,0,[["observable",Ext.util.Observable]],[Ext.data.reader,"Reader",Ext.data,"Reader",Ext.data,"DataReader"],function(){var a=this.prototype;Ext.apply(a,{nullResultSet:new Ext.data.ResultSet({total:0,count:0,records:[],success:true,message:""}),recordDataExtractorTemplate:new Ext.XTemplate(a.recordDataExtractorTemplate)})}));(Ext.cmd.derive("Ext.data.reader.Json",Ext.data.reader.Reader,{alternateClassName:"Ext.data.JsonReader",root:"",metaProperty:"metaData",useSimpleAccessors:false,readRecords:function(b){var a=this,c;if(a.getMeta){c=a.getMeta(b);if(c){a.onMetaChange(c)}}else{if(b.metaData){a.onMetaChange(b.metaData)}}a.jsonData=b;return a.callParent([b])},getResponseData:function(a){var d,b;try{d=Ext.decode(a.responseText);return this.readRecords(d)}catch(c){b=new Ext.data.ResultSet({total:0,count:0,records:[],success:false,message:c.message});this.fireEvent("exception",this,a,b);Ext.Logger.warn("Unable to parse the JSON returned by the server");return b}},buildExtractors:function(){var b=this,a=b.metaProperty;b.callParent(arguments);if(b.root){b.getRoot=b.createAccessor(b.root)}else{b.getRoot=Ext.identityFn}if(a){b.getMeta=b.createAccessor(a)}},extractData:function(a){var e=this.record,d=[],c,b;if(e){c=a.length;if(!c&&Ext.isObject(a)){c=1;a=[a]}for(b=0;b=0){return Ext.functionFactory("obj","return obj"+(b>0?".":"")+c)}}return function(d){return d[c]}}}()),createFieldAccessExpression:(function(){var a=/[\[\.]/;return function(o,d,e){var b=o.mapping,m=b||b===0,c=m?b:o.name,p,g;if(b===false){return}if(typeof c==="function"){p=d+".mapping("+e+", this)"}else{if(this.useSimpleAccessors===true||((g=String(c).search(a))<0)){if(!m||isNaN(c)){c='"'+c+'"'}p=e+"["+c+"]"}else{if(g===0){p=e+c}else{var j=c.split("."),l=j.length,k=1,n=e+"."+j[0],h=[n];for(;k1){c[l]=d.internalId}}else{if(this.writeRecordId){e=g.get(d.idProperty)[this.nameProperty]||d.idProperty;c[e]=d.getId()}}return c},writeValue:function(e,g,b){var c=g[this.nameProperty],a=this.dateFormat||g.dateWriteFormat||g.dateFormat,d=b.get(g.name);if(c==null){c=g.name}if(g.serialize){e[c]=g.serialize(d,b)}else{if(g.type===Ext.data.Types.DATE&&a&&Ext.isDate(d)){e[c]=Ext.Date.format(d,a)}else{e[c]=d}}}},1,0,0,0,["writer.base"],0,[Ext.data.writer,"Writer",Ext.data,"DataWriter",Ext.data,"Writer"],0));(Ext.cmd.derive("Ext.data.writer.Json",Ext.data.writer.Writer,{alternateClassName:"Ext.data.JsonWriter",root:undefined,encode:false,allowSingle:true,expandData:false,getExpandedData:function(d){var b=d.length,e=0,k,a,g,c,h,l=function(j,m){var n={};n[j]=m;return n};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(b,c){var a=this.root;if(this.expandData){c=this.getExpandedData(c)}if(this.allowSingle&&c.length===1){c=c[0]}if(this.encode){if(a){b.params[a]=Ext.encode(c)}else{}}else{b.jsonData=b.jsonData||{};if(a){b.jsonData[a]=c}else{b.jsonData=c}}return b}},0,0,0,0,["writer.json"],0,[Ext.data.writer,"Json",Ext.data,"JsonWriter"],0));(Ext.cmd.derive("Ext.data.proxy.Proxy",Ext.Base,{alternateClassName:["Ext.data.DataProxy","Ext.data.Proxy"],batchOrder:"create,update,destroy",batchActions:true,defaultReaderType:"json",defaultWriterType:"json",isProxy:true,isSynchronous:false,constructor:function(a){var b=this;a=a||{};b.proxyConfig=a;b.mixins.observable.constructor.call(b,a);if(b.model!==undefined&&!(b.model instanceof Ext.data.Model)){b.setModel(b.model)}else{if(b.reader){b.setReader(b.reader)}if(b.writer){b.setWriter(b.writer)}}},setModel:function(a,b){var c=this;c.model=Ext.ModelManager.getModel(a);c.setReader(this.reader);c.setWriter(this.writer);if(b&&c.store){c.store.setModel(c.model)}},getModel:function(){return this.model},setReader:function(a){var c=this,b=true,d=c.reader;if(a===undefined||typeof a=="string"){a={type:a};b=false}if(a.isReader){a.setModel(c.model)}else{if(b){a=Ext.apply({},a)}Ext.applyIf(a,{proxy:c,model:c.model,type:c.defaultReaderType});a=Ext.createByAlias("reader."+a.type,a)}if(a!==d&&a.onMetaChange){a.onMetaChange=Ext.Function.createSequence(a.onMetaChange,this.onMetaChange,this)}c.reader=a;return c.reader},getReader:function(){return this.reader},onMetaChange:function(a){this.fireEvent("metachange",this,a)},setWriter:function(c){var b=this,a=true;if(c===undefined||typeof c=="string"){c={type:c};a=false}if(!c.isWriter){if(a){c=Ext.apply({},c)}Ext.applyIf(c,{model:b.model,type:b.defaultWriterType});c=Ext.createByAlias("writer."+c.type,c)}b.writer=c;return b.writer},getWriter:function(){return this.writer},create:Ext.emptyFn,read:Ext.emptyFn,update:Ext.emptyFn,destroy:Ext.emptyFn,batch:function(p,m){var l=this,k=l.batchActions,h,c,g,d,e,n,b,o,j;if(p.operations===undefined){p={operations:p,listeners:m}}if(p.batch){if(Ext.isDefined(p.batch.runOperation)){h=Ext.applyIf(p.batch,{proxy:l,listeners:{}})}}else{p.batch={proxy:l,listeners:p.listeners||{}}}if(!h){h=new Ext.data.Batch(p.batch)}h.on("complete",Ext.bind(l.onBatchComplete,l,[p],0));g=l.batchOrder.split(",");d=g.length;for(n=0;n=h.total){h.success=false;h.count=0;h.records=[]}else{h.records=Ext.Array.slice(h.records,c.start,c.start+c.limit);h.count=h.records.length}}}if(h.success){c.setSuccessful()}else{g.fireEvent("exception",g,null,c)}Ext.callback(j,k||g,[c])},clear:Ext.emptyFn},1,0,0,0,["proxy.memory"],0,[Ext.data.proxy,"Memory",Ext.data,"MemoryProxy"],0));(Ext.cmd.derive("Ext.data.Operation",Ext.Base,{synchronous:true,action:undefined,filters:undefined,sorters:undefined,groupers:undefined,start:undefined,limit:undefined,batch:undefined,callback:undefined,scope:undefined,started:false,running:false,complete:false,success:undefined,exception:false,error:undefined,actionCommitRecordsRe:/^(?:create|update)$/i,actionSkipSyncRe:/^destroy$/i,constructor:function(a){Ext.apply(this,a||{})},commitRecords:function(n){var j=this,m=j.actionCommitRecordsRe.test(j.action),l,h,a,e,b,d,g,k,c;if(!j.actionSkipSyncRe.test(j.action)){a=j.records;if(a&&a.length){if(m){c=[]}if(a.length>1){if(j.action=="update"||a[0].clientIdProperty){l=new Ext.util.MixedCollection();l.addAll(n);for(h=a.length;h--;){b=a[h];e=l.findBy(j.matchClientRec,b);k=b.copyFrom(e);if(m){c.push(k)}}}else{for(d=0,g=a.length;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){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};if(b.batchUpdateMode=="operation"){a.operationcomplete=b.onBatchOperationComplete}else{a.complete=b.onBatchComplete}return a},save:function(){return this.sync.apply(this,arguments)},load:function(b){var c=this,a={action:"read"};if(c.remoteFilter){a.filters=c.filters.items}if(c.remoteSort){a.sorters=c.getSorters()}Ext.apply(a,b);c.lastOptions=a;a=new Ext.data.Operation(a);if(c.fireEvent("beforeload",c,a)!==false){c.loading=true;c.proxy.read(a,c.onProxyLoad,c)}return c},reload:function(a){var b=Ext.apply({},a,this.lastOptions);return this.load(b)},afterEdit:function(a,e){var d=this,b,c;if(d.autoSync&&!d.autoSyncSuspended){for(b=e.length;b--;){if(a.fields.get(e[b]).persist){c=true;break}}if(c){d.sync()}}d.onUpdate(a,Ext.data.Model.EDIT,e);d.fireEvent("update",d,a,Ext.data.Model.EDIT,e)},afterReject:function(a){this.onUpdate(a,Ext.data.Model.REJECT,null);this.fireEvent("update",this,a,Ext.data.Model.REJECT,null)},afterCommit:function(a,b){if(!b){b=null}this.onUpdate(a,Ext.data.Model.COMMIT,b);this.fireEvent("update",this,a,Ext.data.Model.COMMIT,b)},onUpdate:Ext.emptyFn,onIdChanged:function(c,d,b,a){this.fireEvent("idchanged",this,c,d,b,a)},destroyStore:function(){var a=this;if(!a.isDestroyed){a.clearListeners();if(a.storeId){Ext.data.StoreManager.unregister(a)}a.destroyClear();if(a.reader){a.reader.destroyReader()}a.data=a.tree=a.sorters=a.filters=a.groupers=a.proxy=a.reader=a.writer=a.model=null;a.isDestroyed=true}},destroyClear:function(){this.clearData()},getState:function(){var e=this,c,a,b=!!e.groupers,g=[],h=[],d=[];if(b){e.groupers.each(function(j){g[g.length]=j.serialize();c=true})}if(e.sorters){e.sorters.each(function(j){if(b&&!e.groupers.contains(j)){h[h.length]=j.serialize();c=true}})}if(e.filters&&e.statefulFilters){e.filters.each(function(j){d[d.length]=j.serialize();c=true})}if(c){a={};if(g.length){a.groupers=g}if(h.length){a.sorters=h}if(d.length){a.filters=d}return a}},applyState:function(g){var e=this,c=!!e.sorters,b=!!e.groupers,a=!!e.filters,d;if(b&&g.groupers){e.groupers.clear();e.groupers.addAll(e.decodeGroupers(g.groupers))}if(c&&g.sorters){e.sorters.clear();e.sorters.addAll(e.decodeSorters(g.sorters))}if(a&&g.filters){e.filters.clear();e.filters.addAll(e.decodeFilters(g.filters))}if(c&&b){e.sorters.insert(0,e.groupers.getRange())}if(e.autoLoad&&(e.remoteSort||e.remoteGroup||e.remoteFilter)){if(e.autoLoadTask){e.autoLoadTask.cancel();delete e.autoLoadTask}if(e.autoLoad===true){e.reload()}else{e.reload(e.autoLoad)}}if(a&&e.filters.length&&!e.remoteFilter){e.filter();d=e.sortOnFilter}if(c&&e.sorters.length&&!e.remoteSort&&!d){e.sort()}},doSort:function(a){var b=this;if(b.remoteSort){b.load()}else{b.data.sortBy(a);b.fireEvent("datachanged",b);b.fireEvent("refresh",b)}b.fireEvent("sort",b,b.sorters.getRange())},clearData:Ext.emptyFn,getCount:Ext.emptyFn,getById:Ext.emptyFn,removeAll:Ext.emptyFn,isLoading:function(){return !!this.loading},suspendAutoSync:function(){this.autoSyncSuspended=true},resumeAutoSync:function(){this.autoSyncSuspended=false}},1,0,0,0,0,[["observable",Ext.util.Observable],["sortable",Ext.util.Sortable]],[Ext.data,"AbstractStore"],0));(Ext.cmd.derive("Ext.data.StoreManager",Ext.util.MixedCollection,{alternateClassName:["Ext.StoreMgr","Ext.data.StoreMgr","Ext.StoreManager"],singleton:true,register:function(){for(var a=0,b;(b=arguments[a]);a++){this.add(b)}},unregister:function(){for(var a=0,b;(b=arguments[a]);a++){this.remove(this.lookup(b))}},lookup:function(c){if(Ext.isArray(c)){var b=["field1"],e=!Ext.isArray(c[0]),g=c,d,a;if(e){g=[];for(d=0,a=c.length;d1){b=[];for(j=0;j=c){h.deselectRange(h.lastFocused,c-1)}else{if(k!==d){h.selectRange(k,d,g.ctrlKey)}}}h.lastSelected=d;h.setLastFocused(d)}else{if(g.ctrlKey&&b){h.setLastFocused(d)}else{if(g.ctrlKey){h.setLastFocused(d)}else{h.doSelect(d,false)}}}}break;case"SIMPLE":if(b){if(h.allowDeselect){h.doDeselect(d)}}else{h.doSelect(d,true)}break;case"SINGLE":if(m){if(b){if(h.allowDeselect){h.doDeselect(d);h.setLastFocused(d)}}else{h.doSelect(d)}}else{if(g.ctrlKey){h.setLastFocused(d)}else{if(m&&h.allowDeselect&&b){h.doDeselect(d)}else{h.doSelect(d,false);h.setLastFocused(d)}}}break}if(!g.shiftKey){if(h.isSelected(d)){h.selectionStart=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]},onModelIdChanged:function(a,d,e,c,b){this.selected.updateKey(b,c)},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||!d.store){return}if(typeof c==="number"){a=d.store.getAt(c);if(!a){return}c=[a]}if(d.selectionMode=="SINGLE"&&c){a=c.length?c[0]:c;d.doSingleSelect(a,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,b.getRange(),k);if(m[0]){h.maybeFireSelectionChange(m[1]>0&&!k);return}}c=function(){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}if(c.getCount()){d.suspendChanges();if(!d.doDeselect(d.lastSelected,b)){d.resumeChanges();return}d.resumeChanges()}e=function(){c.add(a);d.lastSelected=a;g=true};d.onSelectChange(a,true,b,e);if(g){if(!b&&!d.preventFocus){d.setLastFocused(a)}d.maybeFireSelectionChange(!b)}},setLastFocused:function(c,b){var d=this,a=d.lastFocused;if(c!==a){d.lastFocused=c;d.onLastFocusChanged(a,c,b)}},isFocused:function(a){return a===this.getLastFocused()},maybeFireSelectionChange:function(a){var b=this;if(a&&!b.suspendChange){b.fireEvent("selectionchange",b,b.getSelection())}},getLastSelected:function(){return this.lastSelected},getLastFocused:function(){return this.lastFocused},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(){return this.selected.getCount()>0},getSelectionId:function(a){return a.internalId},pruneIf:function(){var g=this,d=g.selected,c=[],a=d.length,b,e;if(g.pruneRemoved){for(b=0;b0){this.clearSelections();this.maybeFireSelectionChange(true)}},onStoreRemove:function(c,b,d,a){var e=this;if(e.selectionStart&&Ext.Array.contains(b,e.selectionStart)){e.selectionStart=null}if(a||e.locked||!e.pruneRemoved){return}e.deselectDeletedRecords(b)},deselectDeletedRecords:function(b){var g=this,d=g.selected,c,e=b.length,h=0,a;for(c=0;c=c){a=0}}e.select(a)},onSelectChange:function(b,e,d,h){var g=this,a=g.view,c=e?"select":"deselect";if((d||g.fireEvent("before"+c,g,b))!==false&&h()!==false){if(a){if(e){a.onItemSelect(b)}else{a.onItemDeselect(b)}}if(!d){g.fireEvent(c,g,b)}}},onLastFocusChanged:function(d,b,c){var a=this.view;if(a&&!c&&b){a.focusNode(b);this.fireEvent("focuschange",this,d,b)}},destroy:function(){Ext.destroy(this.keyNav);this.callParent()}},1,0,0,0,0,0,[Ext.selection,"DataViewModel"],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.boundView)}},deferInitialRefresh:true,itemCls:Ext.baseCSSPrefix+"dataview-item",loadingText:"Loading...",loadMask:true,loadingUseMsg:true,selectedItemCls:Ext.baseCSSPrefix+"item-selected",emptyText:"",deferEmptyText:true,trackOver:false,blockRefresh:false,preserveScrollOnRefresh:false,ariaRole:"listbox",itemAriaRole:"option",last:false,triggerEvent:"itemclick",triggerCtEvent:"containerclick",refreshNeeded:true,addCmpEvents:Ext.emptyFn,initComponent:function(){var c=this,a=Ext.isDefined,d=c.itemTpl,b={};if(d){if(Ext.isArray(d)){d=d.join("")}else{if(Ext.isObject(d)){b=Ext.apply(b,d.initialConfig);d=d.html}}if(!c.itemSelector){c.itemSelector="."+c.itemCls}d=Ext.String.format('
    {1}
    ',c.itemCls,d,c.itemAriaRole);c.tpl=new Ext.XTemplate(d,b)}c.callParent();c.tpl=c.getTpl("tpl");if(c.overItemCls){c.trackOver=true}c.addEvents("beforerefresh","refresh","viewready","itemupdate","itemadd","itemremove");c.addCmpEvents();c.store=Ext.data.StoreManager.lookup(c.store||"ext-empty-store");if(!c.dataSource){c.dataSource=c.store}c.bindStore(c.dataSource,true,"dataSource");if(!c.all){c.all=new Ext.CompositeElementLite()}c.scrollState={top:0,left:0};c.on({scroll:c.onViewScroll,element:"el",scope:c})},onRender:function(){var d=this,b=d.loadMask,c=d.getMaskStore(),a={target:d,msg:d.loadingText,useMsg:d.loadingUseMsg,store:c};d.callParent(arguments);if(b&&!c.proxy.isSynchronous){if(d.loadingCls){a.msgCls=d.loadingCls}if(Ext.isObject(b)){a=Ext.apply(a,b)}d.loadMask=new Ext.LoadMask(a);d.loadMask.on({scope:d,beforeshow:d.onMaskBeforeShow,hide:d.onMaskHide})}},beforeLayout:function(){var a=this;a.callParent(arguments);if(a.refreshNeeded&&!a.pendingRefresh){if(a.refreshCounter){a.refresh()}else{a.doFirstRefresh(a.dataSource)}}},getMaskStore:function(){return this.store},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(){this.callParent(arguments);this.getSelectionModel().beforeViewRender(this)},afterRender:function(){this.callParent(arguments);this.getSelectionModel().bindComponent(this)},getSelectionModel:function(){var a=this,b="SINGLE";if(a.simpleSelect){b="SIMPLE"}else{if(a.multiSelect){b="MULTI"}}if(!a.selModel||!a.selModel.events){a.selModel=new Ext.selection.DataViewModel(Ext.apply({allowDeselect:a.allowDeselect,mode:b},a.selModel))}if(!a.selModel.hasRelaySetup){a.relayEvents(a.selModel,["selectionchange","beforeselect","beforedeselect","select","deselect","focuschange"]);a.selModel.hasRelaySetup=true}if(a.disableSelection){a.selModel.locked=true}return a.selModel},refresh:function(){var c=this,j,b,e,d,h,a,g;if(!c.rendered||c.isDestroyed){return}if(!c.hasListeners.beforerefresh||c.fireEvent("beforerefresh",c)!==false){j=c.getTargetEl();a=c.getViewRange();h=j.dom;if(!c.preserveScrollOnRefresh){b=h.parentNode;e=h.style.display;h.style.display="none";d=h.nextSibling;b.removeChild(h)}if(c.refreshCounter){g=true;c.clearViewEl()}else{c.fixedNodes=j.dom.childNodes.length;c.refreshCounter=1}c.tpl.append(j,c.collectData(a,c.all.startIndex||0));if(!c.preserveScrollOnRefresh){b.insertBefore(h,d);h.style.display=e}if(a.length<1){if(!this.store.loading&&(!c.deferEmptyText||g)){Ext.core.DomHelper.insertHtml("beforeEnd",j.dom,c.emptyText)}c.all.clear()}else{c.collectNodes(j.dom);c.updateIndexes(0)}if(g){if(c.refreshSelmodelOnRefresh!==false){c.selModel.refresh()}else{c.selModel.pruneIf()}}c.refreshNeeded=false;this.refreshSize();c.fireEvent("refresh",c);if(!c.viewReady){c.viewReady=true;c.fireEvent("viewready",c)}}},collectNodes:function(b){var a=this.all;a.fill(Ext.query(this.getItemSelector(),Ext.getDom(b)),a.startIndex||0)},getViewRange:function(){return this.dataSource.getRange()},refreshSize:function(){var a=this.getSizeModel();if(a.height.shrinkWrap||a.width.shrinkWrap){this.updateLayout()}},clearViewEl:function(){var b=this,a=b.getTargetEl();if(b.fixedNodes){while(a.dom.childNodes[b.fixedNodes]){a.dom.removeChild(a.dom.childNodes[b.fixedNodes])}}else{a.update("")}b.refreshCounter++},onViewScroll:Ext.emptyFn,onIdChanged:Ext.emptyFn,saveScrollState:function(){if(this.rendered){var b=this.el.dom,a=this.scrollState;a.left=b.scrollLeft;a.top=b.scrollTop}},restoreScrollState:function(){if(this.rendered){var b=this.el.dom,a=this.scrollState;b.scrollLeft=a.left;b.scrollTop=a.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){c=d.bufferRender([a],b)[0];if(d.getNode(a)){d.all.replaceElement(b,c,true);d.updateIndexes(b,b);d.selModel.onUpdate(a);if(d.hasListeners.itemupdate){d.fireEvent("itemupdate",a,b,c)}return c}}}},onReplace:function(k,l,b,c){var h=this,g,n=h.all,a,m,e,d;if(h.rendered){a=h.bufferRender(c,l,true);m=n.item(l);if(m){n.item(l).insertSibling(a,"before",true)}else{h.appendNodes(a)}n.insert(l,a);l+=c.length;g=l+b.length-1;n.removeRange(l,g,true);if(h.refreshSelmodelOnRefresh!==false){h.selModel.refresh()}h.updateIndexes(l);h.refreshSize();if(h.hasListeners.itemremove){for(e=b.length,d=g;e>=0;--e,--d){h.fireEvent("itemremove",b[e],d)}}if(h.hasListeners.itemadd){h.fireEvent("itemadd",c,l,a)}}},onAdd:function(c,b,d){var e=this,a;if(e.rendered){if(e.all.getCount()===0){e.refresh();a=e.all.slice()}else{a=e.doAdd(b,d);if(e.refreshSelmodelOnRefresh!==false){e.selModel.refresh()}e.updateIndexes(d);e.refreshSize()}if(e.hasListeners.itemadd){e.fireEvent("itemadd",b,d,a)}}},appendNodes:function(b){var c=document.createDocumentFragment(),a=b.length,d;for(d=0;da){j.appendNodes(b)}else{if(d<=e){g.item(e).insertSibling(b,"before",true)}else{g.item(d).insertSibling(b,"before",true)}}g.insert(d,b);return b},onRemove:function(h,b,j){var e=this,g=e.hasListeners.itemremove,d,a,c;if(e.all.getCount()){if(e.dataSource.getCount()===0){if(g){for(d=j.length-1;d>=0;--d){e.fireEvent("itemremove",b[d],j[d])}}e.refresh()}else{for(d=j.length-1;d>=0;--d){a=b[d];c=j[d];if(e.all.item(c)){e.doRemove(a,c);if(g){e.fireEvent("itemremove",a,c)}}}e.updateIndexes(j[0])}this.refreshSize()}},doRemove:function(a,b){this.all.removeElement(b,true)},refreshNode:function(a){this.onUpdate(this.dataSource,this.store.getAt(a))},updateIndexes:function(e,d){var b=this.all.elements,a=this.getViewRange(),c;e=e||0;d=d||((d===0)?0:(b.length-1));for(c=e;c<=d;c++){b[c].viewIndex=c;b[c].viewRecordId=a[c].internalId;if(!b[c].boundView){b[c].boundView=this.id}}},getStore:function(){return this.store},bindStore:function(a,b,d){var c=this;c.mixins.bindable.bindStore.apply(c,arguments);if(!b){c.getSelectionModel().bindStore(a)}if(c.componentLayoutCounter){c.doFirstRefresh(a)}},doFirstRefresh:function(a){var b=this;if(a&&!a.loading){if(b.deferInitialRefresh){b.applyFirstRefresh()}else{b.refresh()}}},applyFirstRefresh:function(){var a=this;if(a.isDestroyed){return}a.pendingRefresh=0;if(a.up("[isCollapsingOrExpanding]")){a.pendingRefresh=Ext.Function.defer(a.applyFirstRefresh,100,a)}else{a.pendingRefresh=Ext.Function.defer(function(){a.pendingRefresh=0;if(!a.isDestroyed){a.refresh()}},1)}},onUnbindStore:function(a){this.setMaskBind(null)},onBindStore:function(a,b,c){this.setMaskBind(a);if(!b&&c==="store"){this.bindStore(a,false,"dataSource")}},setMaskBind:function(b){var a=this.loadMask;if(a&&a.bindStore){a.bindStore(b)}},getStoreListeners:function(){var a=this;return{idchanged:a.onIdChanged,refresh:a.onDataRefresh,replace:a.onReplace,add:a.onAdd,bulkremove:a.onRemove,update:a.onUpdate,clear:a.refresh}},onDataRefresh:function(){this.refreshView()},refreshView:function(){var b=this,a=b.blockRefresh||!b.rendered||b.up("[collapsed],[isCollapsingOrExpanding],[hidden]");if(a){b.refreshNeeded=true;b.deferInitialRefresh=false}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.selModel.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)}Ext.fly(e).set({tabIndex:-1});e.focus()}},bindStore:function(b,c,a){var d=this[a];if(d&&d.isFeatureStore){if(b.isFeatureStore){this.bindStoreListeners(b);d.bindStore(d.store)}else{d.bindStore(b)}}else{this.callParent(arguments)}}},0,["dataview"],["component","box","dataview"],{component:true,box:true,dataview:true},["widget.dataview"],0,[Ext.view,"View",Ext,"DataView"],0));(Ext.cmd.derive("Ext.grid.CellContext",Ext.Base,{isCellContext:true,constructor:function(a){this.view=a},isEqual:function(a){if(a){return this.record===a.record&&this.columnHeader===a.columnHeader}return false},setPosition:function(c,a){var b=this;if(arguments.length===1){if(c.view){b.view=c.view}a=c.column;c=c.row}b.setRow(c);b.setColumn(a);return b},setRow:function(b){var a=this;if(b!==undefined){if(typeof b==="number"){a.row=Math.max(Math.min(b,a.view.dataSource.getCount()-1),0);a.record=a.view.dataSource.getAt(b)}else{if(b.isModel){a.record=b;a.row=a.view.indexOf(b)}else{if(b.tagName){a.record=a.view.getRecord(b);a.row=a.view.indexOf(a.record)}}}}},setColumn:function(a){var b=this,c=b.view.ownerCt.getColumnManager();if(a!==undefined){if(typeof a==="number"){b.column=a;b.columnHeader=c.getHeaderAtIndex(a)}else{if(a.isHeader){b.columnHeader=a;b.column=c.getHeaderIndex(a)}}}}},1,0,0,0,0,0,[Ext.grid,"CellContext"],0));(Ext.cmd.derive("Ext.util.CSS",Ext.Base,function(){var c,e=null,d=document,b=/(-[a-z])/gi,a=function(g,h){return h.charAt(1).toUpperCase()};return{singleton:true,rules:e,initialized:false,constructor:function(){c=this},createStyleSheet:function(j,l){var h,g=d.getElementsByTagName("head")[0],k=d.createElement("style");k.setAttribute("type","text/css");if(l){k.setAttribute("id",l)}h=k.styleSheet;if(h){g.appendChild(k);h.cssText=j}else{k.appendChild(d.createTextNode(j));g.appendChild(k);h=k.sheet}c.cacheStyleSheet(h);return h},removeStyleSheet:function(h){var g=d.getElementById(h);if(g){g.parentNode.removeChild(g)}},swapStyleSheet:function(j,g){var h;c.removeStyleSheet(j);h=d.createElement("link");h.setAttribute("rel","stylesheet");h.setAttribute("type","text/css");h.setAttribute("id",j);h.setAttribute("href",g);d.getElementsByTagName("head")[0].appendChild(h)},cacheStyleSheet:function(m){if(!e){e=c.rules={}}try{var p=m.cssRules||m.rules,l=p.length-1,h=m.imports,g=h?h.length:0,o,k;for(k=0;k=0;--l){o=p[l];if(o.styleSheet){c.cacheStyleSheet(o.styleSheet)}c.cacheRule(o,m)}}catch(n){}},cacheRule:function(h,m){if(h.styleSheet){return c.cacheStyleSheet(h.styleSheet)}var l=h.selectorText,k,g;if(l){l=l.split(",");k=l.length;for(g=0;g=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;c-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(b,g,d){var j=this,a=j.elements,e,h,c,k;if(g==null){g=j.endIndex+1}else{g=Math.min(j.endIndex+1,g+1)}if(b==null){b=j.startIndex}c=g-b;for(h=b,k=g;h<=j.endIndex;h++,k++){if(d&&h=h.startIndex&&k<=h.endIndex){m[m.length]=k}}Ext.Array.sort(m);e=m.length}else{if(mh.endIndex){return}e=1;m=[m]}for(g=j=m[0],b=0;g<=h.endIndex;g++,j++){if(b=h.startIndex){d=a[g]=a[j];d.setAttribute("data-recordIndex",g)}else{delete a[g]}}h.endIndex-=e;h.count-=e},scroll:function(e,l,c){var k=this,a=k.elements,n=e.length,h,d,b,g,j=k.view.getNodeContainer(),m=document.createDocumentFragment();if(l==-1){for(h=(k.endIndex-c)+1;h<=k.endIndex;h++){d=a[h];delete a[h];d.parentNode.removeChild(d)}k.endIndex-=c;g=k.view.bufferRender(e,k.startIndex-=n);for(h=0;h',"{[view.renderColumnSizer(out)]}","{[view.renderTHead(values, out)]}","{[view.renderTFoot(values, out)]}",'',"{%","view.renderRows(values.rows, values.viewStartIndex, out);","%}","","",{priority:0}],rowTpl:["{%",'var dataRowCls = values.recordIndex === -1 ? "" : " '+Ext.baseCSSPrefix+'grid-data-row";',"%}",'','{%',"parent.view.renderCell(values, parent.record, parent.recordIndex, parent.rowIndex, xindex - 1, out, parent)","%}","","",{priority:0}],cellTpl:['','
    {style}" {ariaCellInnerAttr}>{value}
    ',"",{priority:0}],refreshSelmodelOnRefresh:false,tableValues:{},rowValues:{itemClasses:[],rowClasses:[]},cellValues:{classes:[Ext.baseCSSPrefix+"grid-cell "+Ext.baseCSSPrefix+"grid-td"]},renderBuffer:document.createElement("div"),constructor:function(a){if(a.grid.isTree){a.baseCls=Ext.baseCSSPrefix+"tree-view"}this.callParent([a])},initComponent:function(){var b=this,a=b.scroll;this.addEvents("beforecellclick","cellclick","beforecelldblclick","celldblclick","beforecellcontextmenu","cellcontextmenu","beforecellmousedown","cellmousedown","beforecellmouseup","cellmouseup","beforecellkeydown","cellkeydown");b.body=new Ext.dom.Element.Fly();b.body.id=b.id+"gridBody";b.autoScroll=undefined;if(!b.trackOver){b.overItemCls=null;b.beforeOverItemCls=null}if(a===true||a==="both"){b.autoScroll=true}else{if(a==="horizontal"){b.overflowX="auto"}else{if(a==="vertical"){b.overflowY="auto"}}}b.selModel.view=b.headerCt.view=b;b.grid.view=b;b.initFeatures(b.grid);delete b.grid;b.itemSelector=b.getItemSelector();b.all=new Ext.view.NodeCache(b);b.callParent()},getVisibleColumnManager:function(){var a=this.ownerCt,b=a.ownerLockable;return b?b.getVisibleColumnManager():a.getVisibleColumnManager()},beforeLayout:function(){var b=this,a=!b.firstRefreshDone&&b.headerCt.layout.running;b.callParent(arguments);if(a&&b.body.dom){b.headerCt.layout.injectViewContext(b.headerCt.layout.ownerContext,b)}},moveColumn:function(a,o,d){var n=this,l=(d>1)?document.createDocumentFragment():undefined,c=o,p=n.getGridColumns().length,h=p-1,b=(n.firstCls||n.lastCls)&&(o===0||o==p||a===0||a==h),g,e,s,k,m,r,q;if(n.rendered&&o!==a){s=n.el.query(n.getDataRowSelector());if(o>a&&l){c-=1}for(g=0,k=s.length;g-1){return b.store.data.getAt(a)}}}return b.dataSource.getByInternalId(c.getAttribute("data-recordId"))}},indexOf:function(a){a=this.getNode(a,false);if(!a&&a!==0){return -1}return this.all.indexOf(a)},indexInStore:function(a){return this.dataSource.indexOf(this.getRecord(a))},renderRows:function(e,d,b){var g=this.rowValues,a=e.length,c;g.view=this;g.columns=this.ownerCt.getVisibleColumnManager().getColumns();for(c=0;c');for(c=0;c')}b.push("")},renderRow:function(g,a,e){var j=this,d=a===-1,h=j.selModel,m=j.rowValues,c=m.itemClasses,b=m.rowClasses,l,k=j.rowTpl;m.rowAttr={};m.record=g;m.recordId=g.internalId;m.recordIndex=j.store.indexOf(g);m.rowIndex=a;m.rowId=j.getRowId(g);m.itemCls=m.rowCls="";if(!m.columns){m.columns=j.ownerCt.getVisibleColumnManager().getColumns()}c.length=b.length=0;if(!d){c[0]=Ext.baseCSSPrefix+"grid-row";if(!j.ownerCt.disableSelection&&h.isRowSelected){if(h.isRowSelected(g)){c.push(j.selectedItemCls)}if(j.rowValues.recordIndex0){a.removeRowCls(b-1,c)}}else{a.addRowCls(b-1,c)}},onRowDeselect:function(b){var a=this;a.removeRowCls(b,[a.selectedItemCls,a.focusedItemCls]);if(a.isRowStyleFirst(b)){a.toggleRowTableCls(b,[a.tableFocusedFirstCls,a.tableSelectedFirstCls],false)}else{a.removeRowCls(b-1,[a.beforeFocusedItemCls,a.beforeSelectedItemCls])}},onCellSelect:function(b){var a=this.getCellByPosition(b);if(a){a.addCls(this.selectedCellCls)}},onCellDeselect:function(b){var a=this.getCellByPosition(b,true);if(a){Ext.fly(a).removeCls(this.selectedCellCls)}},getCellByPosition:function(a,b){if(a){var c=this.getNode(a.row,true),d=this.ownerCt.getColumnManager().getHeaderAtIndex(a.column);if(d&&c){return Ext.fly(c).down(this.getCellSelector(d),b)}}return false},getFocusEl:function(){return this.focusEl},onRowFocus:function(d,b,a){var c=this;if(b){c.addRowCls(d,c.focusedItemCls);if(c.isRowStyleFirst(d)){c.toggleRowTableCls(d,c.tableFocusedFirstCls,true)}else{c.addRowCls(d-1,c.beforeFocusedItemCls)}if(!a){c.focusRow(d)}}else{c.removeRowCls(d,c.focusedItemCls);if(c.isRowStyleFirst(d)){c.toggleRowTableCls(d,c.tableFocusedFirstCls,false)}else{c.removeRowCls(d-1,c.beforeFocusedItemCls)}}if((Ext.isIE6||Ext.isIE7)&&!c.ownerCt.rowLines){c.repaintRow(d)}},focusRow:function(e,b){var d=this,a,c=d.getFocusTask();if(b){c.delay(Ext.isNumber(b)?b:10,d.focusRow,d,[e,false]);return}c.cancel();if(d.isVisible(true)&&(e=d.getNode(e,true))){d.scrollRowIntoView(e);a=d.getRecord(e);d.selModel.setLastFocused(a);d.doFocus(e);d.fireEvent("rowfocus",a,e,d.indexInStore(e))}},scrollRowIntoView:function(a){a=this.getNode(a,true);if(a){Ext.fly(a).scrollIntoView(this.el,false)}},focusCell:function(b,c){var e=this,a,d=e.getFocusTask();if(c){d.delay(Ext.isNumber(c)?c:10,e.focusCell,e,[b,false]);return}d.cancel();if(e.isVisible(true)&&(a=e.getCellByPosition(b))){e.scrollCellIntoView(a);e.doFocus(e.getNode(b.row,true));e.fireEvent("cellfocus",b.record,a,b)}},doFocus:function(b){var c=this,a=Ext.isIE,d;if(a){d=c.el.getScrollLeft();c.ignoreScroll=true}(c.focusEl=Ext.get(b)).focus();if(a){c.el.setScrollLeft(d);c.ignoreScroll=false}},scrollCellIntoView:function(a){if(a.row!=null&&a.column!=null){a=this.getCellByPosition(a)}if(a){Ext.fly(a).scrollIntoView(this.el)}},scrollByDelta:function(c,b){b=b||"scrollTop";var a=this.el.dom;a[b]=(a[b]+=c)},isDataRow:function(a){return Ext.fly(a).hasCls(Ext.baseCSSPrefix+"grid-data-row")},syncRowHeights:function(g,a){g=Ext.get(g);a=Ext.get(a);g.dom.style.height=a.dom.style.height="";var d=this,e=d.rowTpl,b=g.dom.offsetHeight,c=a.dom.offsetHeight;if(b!==c){while(e){if(e.syncRowHeights){if(e.syncRowHeights(g,a)===false){break}}e=e.nextTpl}b=g.dom.offsetHeight;c=a.dom.offsetHeight;if(b!==c){g=g.down("[data-recordId]")||g;a=a.down("[data-recordId]")||a;if(g&&a){g.dom.style.height=a.dom.style.height="";b=g.dom.offsetHeight;c=a.dom.offsetHeight;if(b>c){g.setHeight(b);a.setHeight(b)}else{if(c>b){g.setHeight(c);a.setHeight(c)}}}}}},onIdChanged:function(a,h,g,c,b){var e=this,d;if(e.viewReady){d=e.getNodeById(b);if(d){d.setAttribute("data-recordId",h.internalId);d.id=e.getRowId(h)}}},onUpdate:function(g,c,m,r){var v=this,o=v.rowTpl,s,b,k,l,n,t,u,e,p,q,j,d,w,h,a;if(v.viewReady){b=v.getNodeByRecord(c,false);if(b){p=v.overItemCls;q=v.beforeOverItemCls;j=v.focusedItemCls;d=v.beforeFocusedItemCls;w=v.selectedItemCls;h=v.beforeSelectedItemCls;s=Ext.fly(b,"_internal");l=v.createRowElement(c,v.dataSource.data.indexOf(c));if(s.hasCls(p)){Ext.fly(l).addCls(p)}if(s.hasCls(q)){Ext.fly(l).addCls(q)}if(s.hasCls(j)){Ext.fly(l).addCls(j)}if(s.hasCls(d)){Ext.fly(l).addCls(d)}if(s.hasCls(w)){Ext.fly(l).addCls(w)}if(s.hasCls(h)){Ext.fly(l).addCls(h)}a=v.ownerCt.getVisibleColumnManager().getColumns();if(Ext.isIE9m&&b.mergeAttributes){b.mergeAttributes(l,true)}else{n=l.attributes;t=n.length;for(e=0;e0){if(Ext.supports.ScrollWidthInlinePaddingBug){h+=g.getCellPaddingAfter(l[0])}if(g.columnLines){h+=Ext.fly(l[0].parentNode).getBorderWidth("lr")}}a.setWidth(1);d.titleEl.setStyle("text-overflow","clip");k=d.textEl.dom.offsetWidth+d.titleEl.getPadding("lr");d.titleEl.setStyle("text-overflow","");for(;c=c:k<=c){return l||c}k+=g;if((b=Ext.fly(d.getNode(k,true)))&&b.isVisible(true)){e+=g;l=k}}while(e!==a);return k},walkRecs:function(b,a){var h=this,l=h.dataSource,j=0,m=b,c,e=(a<0)?0:(l.buffered?l.getTotalCount():l.getCount())-1,k=e?1:-1,g=l.indexOf(b),d;do{if(e?g>=e:g<=e){return m}g+=k;d=l.getAt(g);if(!d.isCollapsedPlaceholder&&(c=Ext.fly(h.getNodeByRecord(d,true)))&&c.isVisible(true)){j+=k;m=d}}while(j!==a);return m},getFirstVisibleRowIndex:function(){var c=this,b=(c.dataSource.buffered?c.dataSource.getTotalCount():c.dataSource.getCount()),a=c.indexOf(c.all.first())-1;do{a+=1;if(a===b){return}}while(!Ext.fly(c.getNode(a,true)).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.getNode(a,true)).isVisible(true));return a},getHeaderCt:function(){return this.headerCt},getPosition:function(a,b){return new Ext.grid.CellContext(this).setPosition(a,b)},beforeDestroy:function(){var a=this;if(a.rendered){a.el.removeAllListeners()}a.callParent(arguments)},onDestroy:function(){var d=this,c=d.featuresMC,a,b;if(c){for(b=0,a=c.getCount();b=0){h.associationKeyFunction=Ext.functionFactory("obj","return obj"+(c>0?".":"")+b)}}h.initialConfig=d;h.ownerModel=e;h.associatedModel=j;Ext.applyIf(h,{ownerName:k,associatedName:a});h.associationId="association"+(++h.statics().AUTO_ID)},getReader:function(){var c=this,a=c.reader,b=c.associatedModel;if(a){if(Ext.isString(a)){a={type:a}}if(a.isReader){a.setModel(b)}else{Ext.applyIf(a,{model:b,type:c.defaultReaderType})}c.reader=Ext.createByAlias("reader."+a.type,a)}return c.reader||null}},1,0,0,0,0,0,[Ext.data.association,"Association",Ext.data,"Association"],0));(Ext.cmd.derive("Ext.ModelManager",Ext.AbstractManager,{alternateClassName:"Ext.ModelMgr",singleton:true,typeName:"mtype",associationStack:[],registerType:function(c,b){var d=b.prototype,a;if(d&&d.isModel){a=b}else{if(!b.extend){b.extend="Ext.data.Model"}a=Ext.define(c,b)}this.types[c]=a;return a},unregisterType:function(a){delete this.types[a]},onModelDefined:function(c){var a=this.associationStack,g=a.length,e=[],b,d,h;for(d=0;d]+>/gi,asText:function(a){return String(a).replace(this.stripTagsRE,"")},asUCText:function(a){return String(a).toUpperCase().replace(this.stripTagsRE,"")},asUCString:function(a){return String(a).toUpperCase()},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(/,/g,""));return isNaN(b)?0:b},asInt:function(a){var b=parseInt(String(a).replace(/,/g,""),10);return isNaN(b)?0:b}},0,0,0,0,0,0,[Ext.data,"SortTypes"],0));(Ext.cmd.derive("Ext.data.Types",Ext.Base,{singleton:true},0,0,0,0,0,0,[Ext.data,"Types"],function(b){var a=Ext.data.SortTypes;Ext.apply(b,{stripRe:/[\$,%]/g,AUTO:{sortType:a.none,type:"auto"},STRING:{convert:function(d){var c=this.useNull?null:"";return(d===undefined||d===null)?c:String(d)},sortType:a.asUCString,type:"string"},INT:{convert:function(c){if(typeof c=="number"){return parseInt(c)}return c!==undefined&&c!==null&&c!==""?parseInt(String(c).replace(b.stripRe,""),10):(this.useNull?null:0)},sortType:a.none,type:"int"},FLOAT:{convert:function(c){if(typeof c==="number"){return c}return c!==undefined&&c!==null&&c!==""?parseFloat(String(c).replace(b.stripRe,""),10):(this.useNull?null:0)},sortType:a.none,type:"float"},BOOL:{convert:function(c){if(typeof c==="boolean"){return c}if(this.useNull&&(c===undefined||c===null||c==="")){return null}return c==="true"||c==1},sortType:a.none,type:"bool"},DATE:{convert:function(d){var e=this.dateReadFormat||this.dateFormat,c;if(!d){return null}if(d instanceof Date){return d}if(e){return Ext.Date.parse(d,e)}c=Date.parse(d);return c?new Date(c):null},sortType:a.asDate,type:"date"}});b.BOOLEAN=b.BOOL;b.INTEGER=b.INT;b.NUMBER=b.FLOAT}));(Ext.cmd.derive("Ext.data.Field",Ext.Base,{isField:true,constructor:function(b){var d=this,c=Ext.data.Types,a;if(Ext.isString(b)){b={name:b}}Ext.apply(d,b);a=d.sortType;if(d.type){if(Ext.isString(d.type)){d.type=c[d.type.toUpperCase()]||c.AUTO}}else{d.type=c.AUTO}if(Ext.isString(a)){d.sortType=Ext.data.SortTypes[a]}else{if(Ext.isEmpty(a)){d.sortType=d.type.sortType}}if(!b.hasOwnProperty("convert")){d.convert=d.type.convert}else{if(!d.convert&&d.type.convert&&!b.hasOwnProperty("defaultValue")){d.defaultValue=d.type.convert(d.defaultValue)}}if(b.convert){d.hasCustomConvert=true}},dateFormat:null,dateReadFormat:null,dateWriteFormat:null,useNull:false,defaultValue:"",mapping:null,sortType:null,sortDir:"ASC",allowBlank:true,persist:true},1,0,0,0,["data.field"],0,[Ext.data,"Field"],0));(Ext.cmd.derive("Ext.data.Errors",Ext.util.MixedCollection,{isValid:function(){return this.length===0},getByField:function(d){var c=[],a,b;for(b=0;ba)){return false}else{return true}},email:function(b,a){return Ext.data.validations.emailRe.test(a)},format:function(a,b){return !!(a.matcher&&a.matcher.test(b))},inclusion:function(a,b){return a.list&&Ext.Array.indexOf(a.list,b)!=-1},exclusion:function(a,b){return a.list&&Ext.Array.indexOf(a.list,b)==-1}},0,0,0,0,0,0,[Ext.data,"validations"],0));(Ext.cmd.derive("Ext.data.Model",Ext.Base,{alternateClassName:"Ext.data.Record",compareConvertFields:function(a,d){var c=a.convert&&a.type&&a.convert!==a.type.convert,b=d.convert&&d.type&&d.convert!==d.type.convert;if(c&&!b){return 1}if(!c&&b){return -1}return 0},itemNameFn:function(a){return a.name},onClassExtended:function(b,c,a){var d=a.onBeforeCreated;a.onBeforeCreated=function(g,F){var E=this,G=Ext.getClassName(g),t=g.prototype,z=g.prototype.superclass,j=F.validations||[],v=F.fields||[],h,o=F.associations||[],e=function(I,K){var J=0,H,L;if(I){I=Ext.Array.from(I);for(H=I.length;J0;if(e){c.afterEdit(d)}}}},getModifiedFieldNames:function(d){var c=this,e=c[c.persistenceProperty],a=[],b;d=d||c.dataSave;for(b in e){if(e.hasOwnProperty(b)){if(!c.isEqual(e[b],d[b])){a.push(b)}}}return a},getChanges:function(){var a=this.modified,b={},c;for(c in a){if(a.hasOwnProperty(c)){b[c]=this.get(c)}}return b},isModified:function(a){return this.modified.hasOwnProperty(a)},setDirty:function(){var c=this,a=c.fields.items,g=a.length,e,b,d;c.dirty=true;for(d=0;d0){b=p.data.items;h=b.length;for(r=0;r0;if(l){if(d){w[c]=u[0].property;w[n]=u[0].direction||"ASC"}else{w[c]=x.encodeSorters(u)}}if(e&&a&&a.length>0){if(m){k=0;if(a.length>1&&l){k=1}w[e]=a[k].property;w[p]=a[k].direction}else{w[e]=x.encodeSorters(a)}}if(r&&o&&o.length>0){w[r]=x.encodeFilters(o)}return w},buildUrl:function(c){var b=this,a=b.getUrl(c);if(b.noCache){a=Ext.urlAppend(a,Ext.String.format("{0}={1}",b.cacheString,Ext.Date.now()))}return a},getUrl:function(a){return a.url||this.api[a.action]||this.url},doRequest:function(a,c,b){},afterRequest:Ext.emptyFn,onDestroy:function(){Ext.destroy(this.reader,this.writer)}},1,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"],actionMethods:{create:"POST",read:"GET",update:"POST",destroy:"POST"},defaultActionMethods:{create:"POST",read:"GET",update:"POST",destroy:"POST"},binary:false,paramsAsJson:false,doRequest:function(a,h,b){var d=this,e=d.getWriter(),c=d.buildRequest(a),g=d.getMethod(c);if(a.allowWrite()){c=e.write(c)}Ext.apply(c,{binary:d.binary,headers:d.headers,timeout:d.timeout,scope:d,callback:d.createRequestCallback(c,a,h,b),method:g,disableCaching:false});if(g.toUpperCase()!=="GET"&&d.paramsAsJson){c=Ext.apply({jsonData:c.params},c);delete c.params}Ext.Ajax.request(c);return c},getMethod:function(a){var c=this.actionMethods,b=a.action,d;if(c){d=c[b]}return d||this.defaultActionMethods[b]},createRequestCallback:function(d,a,e,b){var c=this;return function(h,j,g){c.processResponse(j,a,d,g,e,b)}}},0,0,0,0,["proxy.ajax"],0,[Ext.data.proxy,"Ajax",Ext.data,"HttpProxy",Ext.data,"AjaxProxy"],0));(Ext.cmd.derive("Ext.data.PageMap",Ext.util.LruCache,{clear:function(a){var b=this;b.pageMapGeneration=(b.pageMapGeneration||0)+1;b.callParent(arguments)},forEach:function(k,m){var h=this,d=Ext.Object.getKeys(h.map),a=d.length,c,b,l,e,g;for(c=0;c0},fireGroupChange:function(){this.fireEvent("groupchange",this,this.groupers)},getGroups:function(b){var d=this.data.items,a=d.length,c=[],k={},g,h,j,e;for(e=0;e-1){r.push({record:b,index:g})}if(d){d.remove(b)}}r=Ext.Array.sort(r,function(y,x){var A=y.index,z=x.index;return A===x.index2?0:(Ab-1)?b-1:d.prefetchEnd,c;a=Math.max(0,a);c=e.data.getRange(g,a);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,b)},prefetchRange:function(g,b){var d=this,c,a,e;if(!d.rangeCached(g,b)){c=d.getPageFromRecordIndex(g);a=d.getPageFromRecordIndex(b);d.data.maxSize=d.purgePageCount?(a-c+1)+d.purgePageCount:0;for(e=c;e<=a;e++){if(!d.pageCached(e)){d.prefetchPage(e)}}}},primeCache:function(d,a,c){var b=this;if(c===-1){d=Math.max(d-b.leadingBufferZone,0);a=Math.min(a+b.trailingBufferZone,b.totalCount-1)}else{if(c===1){d=Math.max(Math.min(d-b.trailingBufferZone,b.totalCount-b.pageSize),0);a=Math.min(a+b.leadingBufferZone,b.totalCount-1)}else{d=Math.min(Math.max(Math.floor(d-((b.leadingBufferZone+b.trailingBufferZone)/2)),0),b.totalCount-b.pageSize);a=Math.min(Math.max(Math.ceil(a+((b.leadingBufferZone+b.trailingBufferZone)/2)),0),b.totalCount-1)}}b.prefetchRange(d,a)},sort:function(b){var a=this;if(b&&a.buffered&&a.remoteSort){a.data.clear()}return a.callParent(arguments)},generateComparator:function(){var d=this,g=d.sorters.items,c=g.length,e=c?d.groupers.getRange():d.groupers.items,b,h,a;if(e.length){for(b=0;b=h.totalCount)?d:g;j=c===0?0:c-1;b=g===d?g:g+1;h.lastRequestStart=c;h.lastRequestEnd=g;if(h.rangeCached(j,b)){h.onGuaranteedRange(l);k=h.data.getRange(c,g)}else{h.fireEvent("cachemiss",h,c,g);a=function(n,m){if(h.rangeCached(j,b)){h.fireEvent("cachefilled",h,c,g);h.data.un("pageAdded",a);h.onGuaranteedRange(l)}};h.data.on("pageAdded",a);h.prefetchRange(c,g)}h.primeCache(c,g,c0){c=b[0].get(g)}for(;d0){a=c[0].get(g)}for(;da){a=e}}return a},average:function(c,a){var b=this;if(a&&b.isGrouped()){return b.aggregate(b.getAverage,b,true,[c])}else{return b.getAverage(b.data.items,c)}},getAverage:function(b,e){var c=0,a=b.length,d=0;if(b.length>0){for(;c=0;b--){e=d[b];c.insert(e.removedFrom||0,e);e.reject()}c.removed.length=0}},1,0,0,0,["store.store"],0,[Ext.data,"Store"],function(){Ext.regStore("ext-empty-store",{fields:[],proxy:"memory"})}));(Ext.cmd.derive("Ext.data.NodeInterface",Ext.Base,{statics:{decorate:function(b){var a,c,d;if(typeof b=="string"){b=Ext.ModelManager.getModel(b)}else{if(b.isModel){b=Ext.ModelManager.getModel(b.modelName)}}if(b.prototype.isNode){return}a=b.prototype.idProperty;c=b.prototype.fields.get(a);d=b.prototype.fields.get(a).type.type;b.override(this.getPrototypeBody());this.applyFields(b,[{name:"parentId",type:d,defaultValue:null,useNull:c.useNull},{name:"index",type:"int",defaultValue:-1,persist:false,convert:null},{name:"depth",type:"int",defaultValue:0,persist:false,convert:null},{name:"expanded",type:"bool",defaultValue:false,persist:false,convert:null},{name:"expandable",type:"bool",defaultValue:true,persist:false,convert:null},{name:"checked",type:"auto",defaultValue:null,persist:false,convert:null},{name:"leaf",type:"bool",defaultValue:false},{name:"cls",type:"string",defaultValue:"",persist:false,convert:null},{name:"iconCls",type:"string",defaultValue:"",persist:false,convert:null},{name:"icon",type:"string",defaultValue:"",persist:false,convert:null},{name:"root",type:"boolean",defaultValue:false,persist:false,convert:null},{name:"isLast",type:"boolean",defaultValue:false,persist:false,convert:null},{name:"isFirst",type:"boolean",defaultValue:false,persist:false,convert:null},{name:"allowDrop",type:"boolean",defaultValue:true,persist:false,convert:null},{name:"allowDrag",type:"boolean",defaultValue:true,persist:false,convert:null},{name:"loaded",type:"boolean",defaultValue:false,persist:false,convert:null},{name:"loading",type:"boolean",defaultValue:false,persist:false,convert:null},{name:"href",type:"string",defaultValue:"",persist:false,convert:null},{name:"hrefTarget",type:"string",defaultValue:"",persist:false,convert:null},{name:"qtip",type:"string",defaultValue:"",persist:false,convert:null},{name:"qtitle",type:"string",defaultValue:"",persist:false,convert:null},{name:"qshowDelay",type:"int",defaultValue:0,persist:false,convert:null},{name:"children",type:"auto",defaultValue:null,persist:false,convert:null},{name:"visible",type:"boolean",defaultValue:true,persist:false,convert:null}])},applyFields:function(c,b){var h=c.prototype,a=h.fields,g=a.keys,e=b.length,j,d;for(d=0;d0},isExpandable:function(){var b=this;if(b.get("expandable")){return !(b.isLeaf()||(b.isLoaded()&&!b.phantom&&!b.hasChildNodes()))}return false},triggerUIUpdate:function(){this.afterEdit([])},appendChild:function(c,l,d){var j=this,e,h,g,k,b,m={isLast:true,parentId:j.getId(),depth:(j.data.depth||0)+1};if(Ext.isArray(c)){j.callStore("suspendAutoSync");for(e=0,h=c.length-1;e0){if(!l){l=h.getTreeStore().generateComparator()}Ext.Array.sort(j,l);h.setFirstChild(j[0]);h.setLastChild(j[g-1]);for(e=0;e0){e=[];for(d=0;d','",""].join("")},"after");return{record:a,node:e,el:d,expanding:false,collapsing:false,animating:false,animateEl:d.down("div"),targetEl:d.down("tbody")}},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(c,h){var j=this,a=j.bufferRender(c,h,true),e=c[0],k=e.parentNode,l=j.all,n,d=j.getAnimWrap(k),m,b,g;if(!d||!d.expanding){return j.callParent(arguments)}k=d.record;m=d.targetEl;b=m.dom.childNodes;g=b.length;n=h-j.indexInStore(k)-1;if(!g||n>=g){m.appendChild(a)}else{Ext.fly(b[n]).insertSibling(a,"before",true)}l.insert(h,a);if(d.isAnimating){j.onExpand(k)}},onRemove:function(g,a,b){var d=this,e,c;if(d.viewReady){e=d.store.getCount()===0;if(e){d.refresh()}else{for(c=b.length-1;c>=0;--c){d.doRemove(a[c],b[c])}}if(d.hasListeners.itemremove){for(c=b.length-1;c>=0;--c){d.fireEvent("itemremove",a[c],b[c])}}}},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 h.callParent(arguments)}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).remove()}}a.expanding=true;a.collapsing=false}}},onExpand:function(k){var j=this,g=j.animQueue,a=k.getId(),c=j.getNode(k),h=c?j.indexOf(c):-1,e,b,l,d=Ext.isIEQuirks?1:0;if(j.singleExpand){j.ensureSingleExpand(k)}if(h===-1){return}e=j.getAnimWrap(k,false);if(!e){j.refreshSelection();k.isExpandingOrCollapsing=false;j.fireEvent("afteritemexpand",k,h,c);j.refreshSize();return}b=e.animateEl;l=e.targetEl;b.stopAnimation();g[a]=true;b.dom.style.height=d+"px";b.animate({from:{height:d},to:{height:l.getHeight()},duration:j.expandDuration,listeners:{afteranimate:function(){var m=l.query(j.itemSelector);if(m.length){e.el.insertSibling(m,"before",true)}e.el.remove();j.refreshSize();delete j.animWraps[e.record.internalId];delete g[a]}},callback:function(){j.refreshSelection();k.isExpandingOrCollapsing=false;j.fireEvent("afteritemexpand",k,h,c)}});e.isAnimating=true},onBeforeCollapse:function(e,b,c,h,d){var g=this,a;if(g.rendered&&g.all.getCount()){if(g.animate){if(Ext.Array.contains(e.stores,g.store)){a=g.getAnimWrap(e);if(!a){a=g.animWraps[e.internalId]=g.createAnimWrap(e,c)}else{if(a.expanding){a.targetEl.select(this.itemSelector).remove()}}a.expanding=false;a.collapsing=true;a.callback=h;a.scope=d}}else{g.onCollapseCallback=h;g.onCollapseScope=d}g.onRowDeselect(g.indexOf(e.firstChild))}},onCollapse:function(d){var g=this,a=g.animQueue,j=d.getId(),e=g.getNode(d),c=e?g.indexOf(e):-1,b=g.getAnimWrap(d),h;if(!g.all.getCount()||!Ext.Array.contains(d.stores,g.store)){return}if(!b){g.refreshSelection();d.isExpandingOrCollapsing=false;g.fireEvent("afteritemcollapse",d,c,e);g.refreshSize();Ext.callback(g.onCollapseCallback,g.onCollapseScope);g.onCollapseCallback=g.onCollapseScope=null;return}h=b.animateEl;a[j]=true;h.stopAnimation();h.animate({to:{height:Ext.isIEQuirks?1:0},duration:g.collapseDuration,listeners:{afteranimate:function(){b.el.remove();if(!g.isDestroyed){g.refreshSize()}delete g.animWraps[b.record.internalId];delete a[j]}},callback:function(){g.refreshSelection();d.isExpandingOrCollapsing=false;g.fireEvent("afteritemcollapse",d,c,e);Ext.callback(b.callback,b.scope);b.callback=b.scope=null}});b.isAnimating=true},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,e,c){var d=this,b=d.editingPlugin;d.callParent(arguments);if(d.toggleOnDblClick&&a.isExpandable()&&!(b&&b.clicksToEdit===2)){d.toggle(a)}},onBeforeItemMouseDown:function(a,c,b,d){if(d.getTarget(this.expanderSelector,c)){return false}return this.callParent(arguments)},onItemClick:function(a,c,b,d){if(d.getTarget(this.expanderSelector,c)&&a.isExpandable()){this.toggle(a,d.ctrlKey);return false}return this.callParent(arguments)},onExpanderMouseOver:function(b,a){b.getTarget(this.cellSelector,10,true).addCls(this.expanderIconOverCls)},onExpanderMouseOut:function(b,a){b.getTarget(this.cellSelector,10,true).removeCls(this.expanderIconOverCls)},getStoreListeners:function(){var b=this,a=b.callParent(arguments);return Ext.apply(a,{beforeexpand:b.onBeforeExpand,expand:b.onExpand,beforecollapse:b.onBeforeCollapse,collapse:b.onCollapse,write:b.onStoreWrite,datachanged:b.onStoreDataChanged})},onBindStore:function(){var a=this,b=a.getTreeStore();a.callParent(arguments);a.mon(b,{scope:a,beforefill:a.onBeforeFill,fillcomplete:a.onFillComplete});if(!b.remoteSort){a.mon(b,{scope:a,beforesort:a.onBeforeSort,sort:a.onSort})}},onUnbindStore:function(){var a=this,b=a.getTreeStore();a.callParent(arguments);a.mun(b,{scope:a,beforefill:a.onBeforeFill,fillcomplete:a.onFillComplete});if(!b.remoteSort){a.mun(b,{scope:a,beforesort:a.onBeforeSort,sort:a.onSort})}},getTreeStore:function(){return this.panel.store},ensureSingleExpand:function(b){var a=b.parentNode;if(a){a.eachChild(function(c){if(c!==b&&c.isExpanded()){c.collapse()}})}},shouldUpdateCell:function(b,e,d){if(d){var c=0,a=d.length;for(;cd.getHeaderIndex(r),h,g;if(j.pos==="after"){q++;b+=m.isGroupHeader?m.query(":not([hidden]):not([isGroupHeader])").length:1}if(w.isLock){h=v.up("[scrollerOwner]");h.lock(r,q,a)}else{if(w.isUnlock){h=v.up("[scrollerOwner]");h.unlock(r,q,a)}else{this.invalidateDrop();g=r.getWidth();if(l){if(q>n){q-=1}if(q===n){k.onHeaderMoved(r,o,s,b);return}}Ext.suspendLayouts();if(l){a.move(n,q)}else{if(u&&(b===q)){q-=1}v.isDDMoveInGrid=a.isDDMoveInGrid=!w.crossPanel;v.remove(r,false);a.insert(q,r);v.isDDMoveInGrid=a.isDDMoveInGrid=false}if(a.isGroupHeader){if(!l){r.savedFlex=r.flex;delete r.flex;r.width=g}}else{if(r.savedFlex){r.flex=r.savedFlex;delete r.width}}Ext.resumeLayouts(true);if(!l){k.onHeaderMoved(r,o,s,b)}}}}}},1,0,0,0,0,0,[Ext.grid.header,"DropZone"],0));(Ext.cmd.derive("Ext.grid.plugin.HeaderReorderer",Ext.AbstractPlugin,{init:function(a){this.headerCt=a;a.on({render:this.onHeaderCtRender,single:true,scope:this})},destroy:function(){Ext.destroy(this.dragZone,this.dropZone)},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:Ext.baseCSSPrefix+"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:Ext.baseCSSPrefix+"column-header-open",menuSortAscCls:Ext.baseCSSPrefix+"hmenu-sort-asc",menuSortDescCls:Ext.baseCSSPrefix+"hmenu-sort-desc",menuColsIcon:Ext.baseCSSPrefix+"cols-icon",ddLock:false,dragging:false,sortable:true,enableColumnHide:true,initComponent:function(){var a=this;a.headerCounter=0;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=false;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;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)}}Ext.applyIf(a.defaults,{sortable:a.sortable});a.menuTask=new Ext.util.DelayedTask(a.updateMenuDisabledState,a);a.callParent();a.addEvents("columnresize","headerclick","headercontextmenu","headertriggerclick","columnmove","columnhide","columnshow","columnschanged","sortchange","menucreate")},initEvents:function(){var a=this;a.callParent();if(!a.isColumn&&!a.isGroupHeader){a.mon(a.el,{click:a.onHeaderCtEvent,dblclick:a.onHeaderCtEvent,contextmenu:a.onHeaderCtEvent,mouseover:a.onHeaderCtMouseOver,mouseout:a.onHeaderCtMouseOut,scope:a})}},onHeaderCtEvent:function(d,a){var c=this,j=d.getTarget("."+Ext.grid.column.Column.prototype.baseCls),h,g,b;if(j&&!c.ddLock){h=Ext.getCmp(j.id);if(h){g=h[h.clickTargetName];if(d.within(g)){if(d.type==="click"){b=h.onTitleElClick(d,g);if(b){c.onHeaderTriggerClick(h,d,a)}else{c.onHeaderClick(h,d,a)}}else{if(d.type==="contextmenu"){c.onHeaderContextMenu(h,d,a)}else{if(d.type==="dblclick"&&h.resizable){h.onTitleElDblClick(d,g.dom)}}}}}}},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),j,h;if(b!==a){if(b){j=Ext.getCmp(b.id);if(j){h=j[j.clickTargetName];j.onTitleMouseOut(g,h.dom)}}if(a){j=Ext.getCmp(a.id);if(j){h=j[j.clickTargetName];j.onTitleMouseOver(g,h.dom)}}}},isLayoutRoot:function(){if(this.hiddenHeaders){return false}return this.callParent()},getOwnerHeaderCt: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();a.callParent();Ext.destroy(a.visibleColumnManager,a.columnManager,a.menu);a.columnManager=a.visibleColumnManager=null},applyColumnsState:function(e){if(!e||!e.length){return}var m=this,k=m.items.items,j=k.length,g=0,b=e.length,l,d,a,h,n=false;for(l=0;l=a.visibleFromIdx){c++}g.callParent(arguments);if(a.isGroupHeader){j=a.visibleColumnManager.getColumns().length}e.onHeaderMoved(a,j,a.visibleFromIdx,c)},onRemove:function(d){var b=this,a=b.ownerCt;b.callParent(arguments);if(!b.destroying){if(!b.isDDMoveInGrid){b.onHeadersChanged(d,false)}if(b.isGroupHeader&&!b.items.getCount()&&a){b.detachComponent(d);Ext.suspendLayouts();a.remove(b);Ext.resumeLayouts(true)}}},onHeadersChanged:function(e,a){var b,d=this.getOwnerHeaderCt();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()}}},applyDefaults:function(b){var a;if(b&&!b.isComponent&&b.xtype=="rownumberer"){a=b}else{a=this.callParent(arguments);if(!b.isGroupHeader&&!("width" in a)&&!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)}}}},0,["headercontainer"],["container","component","headercontainer","box"],{container:true,component:true,headercontainer:true,box:true},["widget.headercontainer"],0,[Ext.grid.header,"Container"],0));(Ext.cmd.derive("Ext.grid.ColumnComponentLayout",Ext.layout.component.Auto,{type:"columncomponent",setWidthInDom:true,beginLayout:function(a){this.callParent(arguments);a.titleContext=a.getEl("titleEl");a.triggerContext=a.getEl("triggerEl")},beginLayoutCycle:function(b){var a=this.owner;this.callParent(arguments);if(b.widthModel.shrinkWrap){a.el.setWidth("")}a.titleEl.setStyle({paddingTop:"",paddingBottom:""})},publishInnerHeight:function(e,k){var h=this,b=h.owner,a,d,j,g,l,c;if(b.getOwnerHeaderCt().hiddenHeaders){e.setProp("innerHeight",0);return}a=k-e.getBorderInfo().height;d=a;if(!b.noWrap&&!e.hasDomProp("width")){h.done=false;return}if(e.hasRawContent){g=d;j=b.textEl.getHeight();if(j){d-=j;if(d>0){l=Math.floor(d/2);c=d-l;e.titleContext.setProp("padding-top",l);e.titleContext.setProp("padding-bottom",c)}}}else{g=b.titleEl.getHeight();e.setProp("innerHeight",a-g,false)}if((Ext.isIE6||Ext.isIEQuirks)&&e.triggerContext){e.triggerContext.setHeight(g)}},measureContentHeight:function(a){return a.el.dom.offsetHeight},publishOwnerHeight:function(b,a){this.callParent(arguments);if((Ext.isIE6||Ext.isIEQuirks)&&b.triggerContext){b.triggerContext.setHeight(a)}},publishInnerWidth:function(a,b){if(!a.hasRawContent){a.setProp("innerWidth",b-a.getBorderInfo().width,false)}},calculateOwnerHeightFromContentHeight:function(c,b){var a=this.callParent(arguments);if(!c.hasRawContent){if(this.owner.noWrap||c.hasDomProp("width")){return b+this.owner.titleEl.getHeight()+c.getBorderInfo().height}return null}return a},calculateOwnerWidthFromContentWidth:function(g,b){var a=this.owner,e=g.getPaddingInfo().width,d=this.getTriggerOffset(a,g),c;if(a.isGroupHeader){c=b}else{c=Math.max(b,a.textEl.getWidth()+g.titleContext.getPaddingInfo().width)}return c+e+d},getTriggerOffset:function(a,c){var b=0;if(c.widthModel.shrinkWrap&&!a.menuDisabled){if(a.query(">:not([hidden])").length===0){b=a.self.triggerElWidth}}return b}},0,0,0,0,["layout.columncomponent"],0,[Ext.grid,"ColumnComponentLayout"],0));(Ext.cmd.derive("Ext.grid.column.Column",Ext.grid.header.Container,{alternateClassName:"Ext.grid.Column",baseCls:Ext.baseCSSPrefix+"column-header",hoverCls:Ext.baseCSSPrefix+"column-header-over",handleWidth:4,ariaRole:"columnheader",sortState:null,possibleSortStates:["ASC","DESC"],childEls:["titleEl","triggerEl","textEl"],noWrap:true,renderTpl:['","{%this.renderContainer(out,values)%}"],dataIndex:null,text:" ",menuText:null,emptyCellText:" ",sortable:true,resizable:true,hideable:true,menuDisabled:false,renderer:false,editRenderer:false,align:"left",draggable:true,tooltipType:"qtip",initDraggable:Ext.emptyFn,tdCls:"",isHeader:true,isColumn:true,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",detachOnRemove:true,initResizable:Ext.emptyFn,initComponent:function(){var a=this,b;if(a.header!=null){a.text=a.header;a.header=null}if(a.columns!=null){a.isGroupHeader=true;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(Ext.baseCSSPrefix+"column-header-align-"+a.align);b=a.renderer;if(b){if(typeof b=="string"){a.renderer=Ext.util.Format[b]}a.hasCustomRenderer=true}else{if(a.defaultRenderer){a.renderer=a.defaultRenderer;a.usingDefaultRenderer=true}}a.callParent(arguments)},initItems:function(){var a=this;a.callParent(arguments);if(a.isGroupHeader){if(!a.hasVisibleChildColumns()){a.hide()}}},hasVisibleChildColumns:function(){var b=this.items.items,a=b.length,c,d;for(c=0;c: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(">:not([hidden])").length>1){return false}},hide:function(c){var j=this,e=j.getOwnerHeaderCt(),b=j.ownerCt,a,k,h,g,d;if(!e){j.callParent();return j}if(j.rendered&&!j.isVisible()){return j}if(e.forceFit){j.visibleSiblingCount=e.getVisibleGridColumns().length-1;if(j.flex){j.savedWidth=j.getWidth();j.flex=null}}a=b.isGroupHeader;if(a&&!c){h=b.query(">:not([hidden])");if(h.length===1&&h[0]===j){j.ownerCt.hide();return}}Ext.suspendLayouts();if(j.isGroupHeader){h=j.items.items;for(d=0,g=h.length;d','lineempty" role="presentation"/>',"
    ",'-end
    -plus {expanderCls}" role="presentation"/>','',' {checkboxCls}-checked"/>',"
    ",'leafparent {iconCls}"','style="background-image:url({icon})"/>','','{value}',"",'{value}',""],initComponent:function(){var a=this,b=a.renderer;if(typeof b=="string"){b=Ext.util.Format[b]}a.origRenderer=b;a.origScope=a.scope||window;a.renderer=a.treeRenderer;a.scope=a;a.callParent()},treeRenderer:function(j,a,e,b,c,k,h){var g=this,l=e.get("cls"),d;if(l){a.tdCls+=" "+l}d=g.initTemplateRendererData(j,a,e,b,c,k,h);return g.getTpl("cellTpl").apply(d)},initTemplateRendererData:function(m,a,e,b,d,n,k){var j=this,h=j.origRenderer,c=e.data,l=e.parentNode,o=k.rootVisible,p=[],g;while(l&&(o||l.data.depth>0)){g=l.data;p[o?g.depth:g.depth-1]=g.isLast?0:1;l=l.parentNode}return{record:e,baseIconCls:j.iconCls,iconCls:c.iconCls,icon:c.icon,checkboxCls:j.checkboxCls,checked:c.checked,elbowCls:j.elbowCls,expanderCls:j.expanderCls,textCls:j.textCls,leaf:c.leaf,expandable:e.isExpandable(),isLast:c.isLast,blankUrl:Ext.BLANK_IMAGE_URL,href:c.href,hrefTarget:c.hrefTarget,lines:p,metaData:a,childCls:j.getChildCls?j.getChildCls()+" ":"",value:h?h.apply(j.origScope,arguments):m}}},0,["treecolumn"],["container","component","gridcolumn","headercontainer","box","treecolumn"],{container:true,component:true,gridcolumn:true,headercontainer:true,box:true,treecolumn:true},["widget.treecolumn"],0,[Ext.tree,"Column"],0));(Ext.cmd.derive("Ext.data.Tree",Ext.Base,{root:null,constructor:function(a){var b=this;b.mixins.observable.constructor.call(b);if(a){b.setRootNode(a)}b.on({scope:b,idchanged:b.onNodeIdChanged,insert:b.onNodeInsert,append:b.onNodeAppend,remove:b.onNodeRemove})},getRootNode:function(){return this.root},setRootNode:function(b){var a=this;a.root=b;if(b.rootOf){b.rootOf.removeRootNode()}else{if(b.parentNode){b.parentNode.removeChild(b)}}b.rootOf=a;if(b.fireEventArgs("beforeappend",[null,b])!==false){b.set("root",true);b.updateInfo(true,{isFirst:true,isLast:true,depth:0,index:0,parentId:null});a.nodeHash={};b.fireEvent("append",null,b);b.fireEvent("rootchange",b)}return b},removeRootNode:function(){var b=this,a=b.root;a.set("root",false);a.fireEvent("remove",null,a,false);a.fireEvent("rootchange",null);a.rootOf=b.root=null;return a},flatten:function(){return Ext.Object.getValues(this.nodeHash)},onNodeInsert:function(a,b){this.registerNode(b,true)},onNodeAppend:function(a,b){this.registerNode(b,true)},onNodeRemove:function(a,b){this.unregisterNode(b,true)},onNodeIdChanged:function(d,e,b,a){var c=this.nodeHash;delete c[e||a];c[b]=d},getNodeById:function(a){return this.nodeHash[a]},registerNode:function(g,a){var e=this,c,d,b;e.nodeHash[g.getId()||g.internalId]=g;if(a===true){c=g.childNodes;d=c.length;for(b=0;b1){b.expandPath(h.join(a),d,a,function(n,m){var l=m;if(n&&m){m=m.findChild(d,e);if(m){b.getSelectionModel().select(m);Ext.callback(g,j||b,[true,m]);return}}Ext.callback(g,j||b,[false,l])},b)}else{c=b.getRootNode();if(c.getId()===e){b.getSelectionModel().select(c);Ext.callback(g,j||b,[true,c])}else{Ext.callback(g,j||b,[false,null])}}}},1,["treepanel"],["container","component","treepanel","box","panel","tablepanel"],{container:true,component:true,treepanel:true,box:true,panel:true,tablepanel:true},["widget.treepanel"],0,[Ext.tree,"Panel",Ext.tree,"TreePanel",Ext,"TreePanel"],0));(Ext.cmd.derive("Ext.layout.container.Card",Ext.layout.container.Fit,{alternateClassName:"Ext.layout.CardLayout",type:"card",hideInactive:true,deferredRender:false,getRenderTree:function(){var a=this,b=a.getActiveItem();if(b){if(b.hasListeners.beforeactivate&&b.fireEvent("beforeactivate",b)===false){b=a.activeItem=a.owner.activeItem=null}else{if(b.hasListeners.activate){b.on({boxready:function(){b.fireEvent("activate",b)},single:true})}}if(a.deferredRender){if(b){return a.getItemsRenderTree([b])}}else{return a.callParent(arguments)}}},renderChildren:function(){var a=this,b=a.getActiveItem();if(!a.deferredRender){a.callParent()}else{if(b){a.renderItems([b],a.getRenderTarget())}}},isValidParent:function(c,d,a){var b=c.el?c.el.dom:Ext.getDom(c);return(b&&b.parentNode===(d.dom||d))||false},getActiveItem:function(){var b=this,a=b.parseActiveItem(b.activeItem||(b.owner&&b.owner.activeItem));if(a&&b.owner.items.indexOf(a)!=-1){b.activeItem=a}else{b.activeItem=null}return b.activeItem},parseActiveItem:function(a){if(a&&a.isComponent){return a}else{if(typeof a=="number"||a===undefined){return this.getLayoutItems()[a||0]}else{return this.owner.getComponent(a)}}},configureItem:function(a){if(a===this.getActiveItem()){a.hidden=false}else{a.hidden=true}this.callParent(arguments)},onRemove:function(a){this.callParent(arguments);if(a===this.activeItem){this.activeItem=null}},getAnimation:function(b,a){var c=(b||{}).cardSwitchAnimation;if(c===false){return false}return c||a.cardSwitchAnimation},getNext:function(){var c=arguments[0],a=this.getLayoutItems(),b=Ext.Array.indexOf(a,this.activeItem);return a[b+1]||(c?a[0]:false)},next:function(){var b=arguments[0],a=arguments[1];return this.setActiveItem(this.getNext(a),b)},getPrev:function(){var c=arguments[0],a=this.getLayoutItems(),b=Ext.Array.indexOf(a,this.activeItem);return a[b-1]||(c?a[a.length-1]:false)},prev:function(){var b=arguments[0],a=arguments[1];return this.setActiveItem(this.getPrev(a),b)},setActiveItem:function(b){var e=this,a=e.owner,d=e.activeItem,g=a.rendered,c;b=e.parseActiveItem(b);c=a.items.indexOf(b);if(c==-1){c=a.items.items.length;Ext.suspendLayouts();b=a.add(b);Ext.resumeLayouts()}if(b&&d!=b){if(b.fireEvent("beforeactivate",b,d)===false){return false}if(d&&d.fireEvent("beforedeactivate",d,b)===false){return false}if(g){Ext.suspendLayouts();if(!b.rendered){e.renderItem(b,e.getRenderTarget(),a.items.length)}if(d){if(e.hideInactive){d.hide();d.hiddenByLayout=true}d.fireEvent("deactivate",d,b)}if(b.hidden){b.show()}if(!b.hidden){e.activeItem=b}Ext.resumeLayouts(true)}else{e.activeItem=b}b.fireEvent("activate",b,d);return e.activeItem}return false}},0,0,0,0,["layout.card"],0,[Ext.layout.container,"Card",Ext.layout,"CardLayout"],0));Ext.define("Ext.grid.plugin.BufferedRendererTreeView",{override:"Ext.tree.View",onRemove:function(c,b,g,a,d){var e=this,h=e.bufferedRenderer;if(e.rendered&&h){if(d){h.onReplace(c,g[0],b,[])}else{h.refreshView()}}else{e.callParent([c,b,g])}}});Ext.define("Ext.grid.plugin.BufferedRendererTableView",{override:"Ext.view.Table",onReplace:function(b,e,a,d){var c=this,g=c.bufferedRenderer;if(c.rendered&&g){g.onReplace(b,e,a,d)}else{c.callParent(arguments)}},onAdd:function(b,a,c){var d=this,e=d.bufferedRenderer;if(d.rendered&&e){e.onReplace(b,c,[],a)}else{d.callParent([b,a,c])}},onRemove:function(c,b,g,a,d){var e=this,h=e.bufferedRenderer;if(e.rendered&&h){if(d){h.onReplace(c,g[0],b,[])}else{h.refreshView()}}else{e.callParent([c,b,g])}},onDataRefresh:function(){var a=this;if(a.bufferedRenderer){a.all.clear();a.bufferedRenderer.onStoreClear()}a.callParent()}});Ext.define("ExtThemeNeptune.panel.Table",{override:"Ext.panel.Table",bodyBorder:true});(Ext.cmd.derive("Ext.ux.IFrame",Ext.Component,{loadMask:"Loading...",src:"about:blank",renderTpl:[''],initComponent:function(){this.callParent();this.frameName=this.frameName||this.id+"-frame";this.addEvents("beforeload","load");Ext.apply(this.renderSelectors,{iframeEl:"iframe"})},initEvents:function(){var a=this;a.callParent();a.iframeEl.on("load",a.onLoad,a)},initRenderData:function(){return Ext.apply(this.callParent(),{src:this.src,frameName:this.frameName})},getBody:function(){var a=this.getDoc();return a.body||a.documentElement},getDoc:function(){try{return this.getWin().document}catch(a){return null}},getWin:function(){var b=this,a=b.frameName,c=Ext.isIE?b.iframeEl.dom.contentWindow:window.frames[a];return c},getFrame:function(){var a=this;return a.iframeEl.dom},beforeDestroy:function(){this.cleanupListeners(true);this.callParent()},cleanupListeners:function(c){var b,d;if(this.rendered){try{b=this.getDoc();if(b){Ext.EventManager.removeAll(b);if(c){for(d in b){if(b.hasOwnProperty&&b.hasOwnProperty(d)){delete b[d]}}}}}catch(a){}}},onLoad:function(){var b=this,d=b.getDoc(),a=b.onRelayedEvent;if(d){try{Ext.EventManager.removeAll(d);Ext.EventManager.on(d,{mousedown:a,mousemove:a,mouseup:a,click:a,dblclick:a,scope:b})}catch(c){}Ext.EventManager.on(this.getWin(),"beforeunload",b.cleanupListeners,b);this.el.unmask();this.fireEvent("load",this)}else{if(b.src&&b.src!=""){this.el.unmask();this.fireEvent("error",this)}}},onRelayedEvent:function(c){var b=this.iframeEl,d=Ext.Element.getTrueXY(b),e=c.getXY(),a=Ext.EventManager.getPageXY(c.browserEvent);c.xy=[d[0]+a[0],d[1]+a[1]];c.injectEvent(b);c.xy=e},load:function(d){var a=this,c=a.loadMask,b=a.getFrame();if(a.fireEvent("beforeload",a,d)!==false){if(c&&a.el){a.el.mask(c)}b.src=a.src=(d||a.src)}}},0,["uxiframe"],["component","uxiframe","box"],{component:true,uxiframe:true,box:true},["widget.uxiframe"],0,[Ext.ux,"IFrame"],0));(Ext.cmd.derive("Ext.Img",Ext.Component,{autoEl:"img",baseCls:Ext.baseCSSPrefix+"img",src:"",alt:"",title:"",imgCls:"",ariaRole:"img",maskOnDisable:false,initComponent:function(){if(this.glyph){this.autoEl="div"}this.callParent()},getElConfig:function(){var e=this,h=e.autoEl,b=e.callParent(),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]}b.html="&#"+d+";";if(g){b.style="font-family:"+g}}else{b.cn=[a={tag:"img",role:e.ariaRole,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}if(e.title){(a||b).title=e.title}return b},onRender:function(){var b=this,c=b.autoEl,a;b.callParent(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(){Ext.destroy(this.imgEl);this.imgEl=null;this.callParent()},setSrc:function(c){var a=this,b=a.imgEl;a.src=c;if(b){b.dom.src=c||Ext.BLANK_IMAGE_URL}},setGlyph:function(c){var b=this,d=Ext._glyphFontFamily,a,e;if(c!=b.glyph){if(typeof c==="string"){a=c.split("@");c=a[0];d=a[1]}e=b.el.dom;e.innerHTML="&#"+c+";";if(d){e.style="font-family:"+d}}}},0,["image","imagecomponent"],["image","component","box","imagecomponent"],{image:true,component:true,box:true,imagecomponent:true},["widget.image","widget.imagecomponent"],0,[Ext,"Img"],0));(Ext.cmd.derive("Ext.grid.View",Ext.view.Table,{stripeRows:true,autoScroll:true},0,["gridview"],["component","box","tableview","dataview","gridview"],{component:true,box:true,tableview:true,dataview:true,gridview:true},["widget.gridview"],0,[Ext.grid,"View"],0));(Ext.cmd.derive("Ext.grid.Panel",Ext.panel.Table,{alternateClassName:["Ext.list.ListView","Ext.ListView","Ext.grid.GridPanel"],viewType:"gridview",lockable:false,rowLines:true},0,["grid","gridpanel"],["container","component","grid","gridpanel","box","panel","tablepanel"],{container:true,component:true,grid:true,gridpanel:true,box:true,panel:true,tablepanel:true},["widget.grid","widget.gridpanel"],0,[Ext.grid,"Panel",Ext.list,"ListView",Ext,"ListView",Ext.grid,"GridPanel"],0));(Ext.cmd.derive("Ext.toolbar.TextItem",Ext.toolbar.Item,{alternateClassName:"Ext.Toolbar.TextItem",text:"",renderTpl:"{text}",baseCls:Ext.baseCSSPrefix+"toolbar-text",ariaRole:null,beforeRender:function(){var a=this;a.callParent();Ext.apply(a.renderData,{text:a.text})},setText:function(b){var a=this;a.text=b;if(a.rendered){a.el.update(b);a.updateLayout()}}},0,["tbtext"],["component","tbtext","tbitem","box"],{component:true,tbtext:true,tbitem:true,box:true},["widget.tbtext"],0,[Ext.toolbar,"TextItem",Ext.Toolbar,"TextItem"],0));(Ext.cmd.derive("Ext.form.CheckboxManager",Ext.util.MixedCollection,{singleton:true,getByName:function(a,b){return this.filterBy(function(c){return c.name==a&&c.getFormId()==b})}},0,0,0,0,0,0,[Ext.form,"CheckboxManager"],0));(Ext.cmd.derive("Ext.form.field.Checkbox",Ext.form.field.Base,{alternateClassName:"Ext.form.Checkbox",componentLayout:"field",stretchInputElFixed:false,childEls:["boxLabelEl"],fieldSubTpl:['",{disableFormats:true,compiled:true}],subTplInsertions:["beforeBoxLabelTpl","afterBoxLabelTpl","beforeBoxLabelTextTpl","afterBoxLabelTextTpl","boxLabelAttrTpl","inputAttrTpl"],isCheckbox:true,focusCls:"form-checkbox-focus",extraFieldBodyCls:Ext.baseCSSPrefix+"form-cb-wrap",checked:false,checkedCls:Ext.baseCSSPrefix+"form-cb-checked",boxLabelCls:Ext.baseCSSPrefix+"form-cb-label",boxLabelAlign:"after",afterLabelCls:Ext.baseCSSPrefix+"form-cb-after",wrapInnerCls:Ext.baseCSSPrefix+"form-cb-wrap-inner",noBoxLabelCls:Ext.baseCSSPrefix+"form-cb-wrap-inner-no-box-label",inputValue:"on",checkChangeEvents:[],inputType:"checkbox",ariaRole:"checkbox",onRe:/^on$/i,inputCls:Ext.baseCSSPrefix+"form-cb",initComponent:function(){var a=this,b=a.value;if(b!==undefined){a.checked=a.isChecked(b,a.inputValue)}a.callParent(arguments);a.getManager().add(a)},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 a.callParent()},getSubTplData:function(){var b=this,a=b.boxLabel,d=b.boxLabelAlign,c=a&&d==="before";return Ext.apply(b.callParent(),{disabled:b.readOnly||b.disabled,wrapInnerCls:b.wrapInnerCls,boxLabel:a,boxLabelCls:b.boxLabelCls,boxLabelAlign:d,labelAlignedBefore:c,afterLabelCls:c?b.afterLabelCls:"",noBoxLabelCls:!a?b.noBoxLabelCls:"",role:b.ariaRole})},initEvents:function(){var a=this;a.callParent();a.mon(a.inputEl,"click",a.onBoxClick,a)},setBoxLabel:function(a){var b=this;b.boxLabel=a;if(b.rendered){b.boxLabelEl.update(a)}},onBoxClick:function(b){var a=this;if(!a.disabled&&!a.readOnly){this.setValue(!this.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,a=b.isChecked(c,b.inputValue);if(d){b[a?"addCls":"removeCls"](b.checkedCls)}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','','',"{[values.view.renderRowWrapColumnSizer(out)]}","{%","values.itemClasses.length = 0;","this.nextTpl.applyOut(values, out, parent)","%}","
    ","","",{priority:200}],getTargetSelector:function(){return this.itemSelector},init:function(b){var c=this,a=c.view;a.addTableTpl(c.tableTpl);a.addRowTpl(Ext.XTemplate.getTpl(c,"wrapTpl"));a.renderRowWrapColumnSizer=c.view.renderColumnSizer;a.renderColumnSizer=Ext.emptyFn;a.isRowWrapped=true;a.getTargetSelector=c.getTargetSelector}},0,0,0,0,["feature.rowwrap"],0,[Ext.grid.feature,"RowWrap"],0));(Ext.cmd.derive("Ext.grid.feature.RowBody",Ext.grid.feature.RowWrap,{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",hasFeatureEvent:true,colSpanDecrement:0,tableTpl:{before:function(b,c){if(!this.rowBody){return}var a=b.view,d=a.rowValues;this.rowBody.setup(b.rows,d)},after:function(b,c){if(!this.rowBody){return}var a=b.view,d=a.rowValues;this.rowBody.cleanup(b.rows,d)},priority:100},extraRowTpl:["{%","values.view.rowBodyFeature.setupRowData(values.record, values.recordIndex, values);","this.nextTpl.applyOut(values, out, parent);","%}",'','','
    {rowBody}
    ',"","",{priority:100,syncRowHeights:function(g,c){var a=this.owner,b=Ext.fly(g).down(a.eventSelector,true),h,d,e;if(b&&(h=Ext.fly(c).down(a.eventSelector,true))){if((d=b.offsetHeight)>(e=h.offsetHeight)){Ext.fly(h).setHeight(d)}else{if(e>d){Ext.fly(b).setHeight(e)}}}},syncContent:function(b,e){var a=this.owner,c=Ext.fly(b).down(a.eventSelector,true),d;if(c&&(d=Ext.fly(e).down(a.eventSelector,true))){Ext.fly(c).syncContent(d)}}}],init:function(b){var c=this,a=c.view=b.getView();a.rowBodyFeature=c;c.mon(b.getStore(),"remove",c.onStoreRemove,c);a.headerCt.on({columnschanged:c.onColumnsChanged,scope:c});a.addTableTpl(c.tableTpl).rowBody=c;a.addRowTpl(Ext.XTemplate.getTpl(this,"extraRowTpl"));a.mouseOverOutBuffer=0;c.callParent(arguments)},onStoreRemove:function(b,d,c){var a=this.view,e;if(a.rendered){e=a.getNode(c);if(e){e=Ext.fly(e).next(this.eventSelector);if(e){e.remove()}}}},getSelectedRow:function(a,c){var b=a.getNode(c,false);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;cg){h.setHeight(a)}else{j.setHeight(g)}}else{j.setHeight("")}}else{j.setHeight(q?g:"")}if(e){n.refreshSize()}}else{p=n}p.fireEvent(q?"expandbody":"collapsebody",h.dom,k,l);Ext.resumeLayouts(true)},refreshRowHeights:function(){Ext.globalEvents.on({idle:this.doRefreshRowHeights,scope:this,single:true})},doRefreshRowHeights:function(){var h=this,k=h.recordsExpanded,j,e,l=h.grid.ownerLockable.lockedGrid.view,a=h.grid.ownerLockable.normalGrid.view,c,d,g,b;for(j in k){if(k.hasOwnProperty(j)){e=this.view.store.data.get(j);d=l.getNode(e,false);c=a.getNode(e,false);d.style.height=c.style.height="";g=d.offsetHeight;b=c.offsetHeight;if(b>g){d.style.height=b+"px"}else{if(g>b){c.style.height=g+"px"}}}}},getHeaderConfig:function(){var a=this;return{width:a.columnWidth,lockable:false,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(c,b){if(!a.grid.ownerLockable){b.tdAttr+=' rowspan="2"'}return''},processEvent:function(h,d,b,k,g,j,c){if(j.getTarget("."+Ext.baseCSSPrefix+"grid-row-expander")){if(h=="click"){a.toggleRow(k,c);return a.selectRowOnExpand}}}}}},0,0,0,0,["plugin.rowexpander"],0,[Ext.grid.plugin,"RowExpander"],0));(Ext.cmd.derive("Ext.ShadowPool",Ext.Base,{singleton:true,markup:(function(){return Ext.String.format('',Ext.baseCSSPrefix,Ext.isIE&&!Ext.supports.CSS3BoxShadow?"ie":"css")}()),shadows:[],pull:function(){var a=this.shadows.shift();if(!a){a=Ext.get(Ext.DomHelper.insertHtml("afterBegin",document.body,this.markup));a.autoBoxAdjust=false}return a},push:function(a){this.shadows.push(a)},reset:function(){var c=[].concat(this.shadows),b,a=c.length;for(b=0;b=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(Ext.baseCSSPrefix+"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(){Ext.destroy(this.tracker)}},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"],["container","component","box","tip","panel","slidertip"],{container:true,component:true,box:true,tip:true,panel:true,slidertip:true},["widget.slidertip"],0,[Ext.slider,"Tip"],0));(Ext.cmd.derive("Ext.layout.component.field.Slider",Ext.layout.component.field.Field,{type:"sliderfield",beginLayout:function(a){this.callParent(arguments);a.endElContext=a.getEl("endEl");a.innerElContext=a.getEl("innerEl");a.bodyElContext=a.getEl("bodyEl")},publishInnerHeight:function(d,a){var e=a-this.measureLabelErrorHeight(d),c,b;if(this.owner.vertical){c=d.endElContext.getPaddingInfo();b=d.inputContext.getPaddingInfo();d.innerElContext.setHeight(e-b.height-c.height)}else{d.bodyElContext.setHeight(e)}},publishInnerWidth:function(d,c){if(!this.owner.vertical){var b=d.endElContext.getPaddingInfo(),a=d.inputContext.getPaddingInfo();d.innerElContext.setWidth(c-a.left-b.right-d.labelContext.getProp("width"))}},beginLayoutFixed:function(d,a,e){var b=this,c=b.ieInputWidthAdjustment;if(c){b.owner.bodyEl.setStyle("padding-right",c+"px")}b.callParent(arguments)}},0,0,0,0,["layout.sliderfield"],0,[Ext.layout.component.field,"Slider"],0));(Ext.cmd.derive("Ext.slider.Multi",Ext.form.field.Base,{alternateClassName:"Ext.slider.MultiSlider",childEls:["endEl","innerEl"],fieldSubTpl:['
    tabIndex="{tabIdx}"',' aria-orientation="vertical" aria-orientation="horizontal"',">",'","
    ",{renderThumbs:function(g,e){var j=e.$comp,h=0,c=j.thumbs,b=c.length,d,a;for(;hg){e.setValue(d,g,false)}}e.syncThumbs()},setValue:function(e,k,b,d){var j=this,h=j.thumbs,a,g,c,l;if(Ext.isArray(e)){l=e;b=k;for(c=0,g=l.length;c',"{text}","",' target="{hrefTarget}"',' hidefocus="true"',' unselectable="on"','',' tabIndex="{tabIndex}"',"",">",'{text}','','","",'','","",'','',"","","
    "],maskOnDisable:false,iconAlign:"left",activate:function(b){var a=this;if(b||(!a.activated&&a.canActivate&&a.rendered&&!a.isDisabled()&&a.isVisible())){if(!a.plain){a.el.addCls(a.activeCls)}a.focus(false,true);a.activated=true;if(a.hasListeners.activate){a.fireEvent("activate",a)}}},getFocusEl:function(){return this.itemEl},deactivate:function(){var b=this,a;if(b.activated){a=b.up("");if(!b.plain){b.el.removeCls(b.activeCls)}if(a){a.focus(false,true)}b.hideMenu();b.activated=false;if(b.hasListeners.deactivate){b.fireEvent("deactivate",b)}}},deferHideMenu:function(){if(this.menu.isVisible()){this.menu.hide()}},cancelDeferHide:function(){clearTimeout(this.hideMenuTimer)},deferHideParentMenus:function(){var a;Ext.menu.Manager.hideAll();if(!Ext.Element.getActiveElement()){a=this.up(":not([hidden])");if(a){a.focus()}}},expandMenu:function(a){var b=this;if(b.menu){b.cancelDeferHide();if(a===0){b.doExpandMenu()}else{clearTimeout(b.expandMenuTimer);b.expandMenuTimer=Ext.defer(b.doExpandMenu,Ext.isNumber(a)?a:b.menuExpandDelay,b)}}},doExpandMenu:function(){var a=this,b=a.menu;if(a.activated&&(!b.rendered||!b.isVisible())){a.parentMenu.activeChild=b;b.ownerCmp=b.ownerItem=a;b.parentMenu=a.parentMenu;b.constrainTo=document.body;b.showBy(a,a.menuAlign)}},getRefItems:function(a){var c=this.menu,b;if(c){b=c.getRefItems(a);b.unshift(c)}return b||[]},hideMenu:function(a){var b=this;if(b.menu){clearTimeout(b.expandMenuTimer);b.hideMenuTimer=Ext.defer(b.deferHideMenu,Ext.isNumber(a)?a:b.menuHideDelay,b)}},initComponent:function(){var b=this,c=Ext.baseCSSPrefix,a=[c+"menu-item"],d;b.addEvents("activate","click","deactivate","textchange","iconchange");if(b.plain){a.push(c+"menu-item-plain")}if(b.cls){a.push(b.cls)}b.cls=a.join(" ");if(b.menu){d=b.menu;delete b.menu;b.setMenu(d)}b.callParent(arguments)},onClick:function(d){var c=this,b=c.clickHideDelay,g=d.browserEvent,a;if(!c.href||c.disabled){d.stopEvent()}if(c.disabled||c.handlingClick){return}if(c.hideOnClick){if(!b){c.deferHideParentMenus()}else{c.deferHideParentMenusTimer=Ext.defer(c.deferHideParentMenus,b,c)}}Ext.callback(c.handler,c.scope||c,[c,d]);c.fireEvent("click",c,d);if(Ext.isIE9m||Ext.isIEQuirks){a=(g.returnValue===false)?true:false}else{a=!!g.defaultPrevented}if(c.href&&!a){c.handlingClick=true;c.itemEl.dom.click();delete c.handlingClick}if(!c.hideOnClick){c.focus()}},onRemoved:function(){var a=this;if(a.activated&&a.parentMenu.activeItem===a){a.parentMenu.deactivateActiveItem()}a.callParent(arguments);a.parentMenu=a.ownerCmp=a.ownerButton=null},beforeDestroy:function(){var a=this;if(a.rendered){a.clearTip()}a.callParent()},onDestroy:function(){var a=this;clearTimeout(a.expandMenuTimer);a.cancelDeferHide();clearTimeout(a.deferHideParentMenusTimer);a.setMenu(null);a.callParent(arguments)},beforeRender:function(){var k=this,l=k.glyph,j=Ext._glyphFontFamily,e=!!(k.icon||k.iconCls||l),m=!!k.menu,g=((k.iconAlign==="right")&&!m),c=k.isMenuCheckItem,a=[],d=k.ownerCt,h=d.plain,b;k.callParent();if(e){if(m&&k.showCheckbox){e=false}}if(typeof l==="string"){b=l.split("@");l=b[0];j=b[1]}if(!h||(e&&!g)||c){if(d.showSeparator&&!h){a.push(k.indentCls)}else{a.push(k.indentNoSeparatorCls)}}if(m){a.push(k.indentRightArrowCls)}else{if(e&&(g||c)){a.push(k.indentRightIconCls)}}Ext.applyIf(k.renderData,{href:k.href||"#",hrefTarget:k.hrefTarget,icon:k.icon,iconCls:k.iconCls,glyph:l,glyphCls:l?Ext.baseCSSPrefix+"menu-item-glyph":undefined,glyphFontFamily:j,hasIcon:e,hasMenu:m,indent:!h||e||c,isCheckItem:c,rightIcon:g,plain:k.plain,text:k.text,arrowCls:k.arrowCls,baseIconCls:k.baseIconCls,textCls:k.textCls,indentCls:a.join(" "),linkCls:k.linkCls,groupCls:k.group?k.groupCls:"",tabIndex:k.tabIndex})},onRender:function(){var a=this;a.callParent(arguments);if(a.tooltip){a.setTooltip(a.tooltip,true)}},setMenu:function(e,d){var c=this,b=c.menu,a=c.arrowEl;if(b){b.ownerCmp=b.ownerItem=b.parentMenu=null;if(d===true||(d!==false&&c.destroyMenu)){Ext.destroy(b)}}if(e){c.menu=Ext.menu.Manager.get(e);c.menu.ownerCmp=c.menu.ownerItem=c}else{c.menu=null}if(c.rendered&&!c.destroying&&a){a[c.menu?"addCls":"removeCls"](c.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.update(d||"");c.ownerCt.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}},0,["menuitem"],["component","box","menuitem"],{component:true,box:true,menuitem:true},["widget.menuitem"],[["queryable",Ext.Queryable]],[Ext.menu,"Item",Ext.menu,"TextItem"],0));(Ext.cmd.derive("Ext.menu.CheckItem",Ext.menu.Item,{checkedCls:Ext.baseCSSPrefix+"menu-item-checked",uncheckedCls:Ext.baseCSSPrefix+"menu-item-unchecked",groupCls:Ext.baseCSSPrefix+"menu-group-icon",hideOnClick:false,checkChangeDisabled:false,ariaRole:"menuitemcheckbox",childEls:["itemEl","iconEl","textEl","checkEl"],showCheckbox:true,isMenuCheckItem:true,checkboxCls:Ext.baseCSSPrefix+"menu-item-checkbox",initComponent:function(){var a=this;a.checked=!!a.checked;a.addEvents("beforecheckchange","checkchange");a.callParent(arguments);Ext.menu.Manager.registerCheckable(a);if(a.group){if(a.initialConfig.hideOnClick!==false){a.hideOnClick=true}}},beforeRender:function(){var a=this;a.callParent();Ext.apply(a.renderData,{checkboxCls:a.checkboxCls,showCheckbox:a.showCheckbox})},afterRender:function(){var a=this;a.callParent();a.checked=!a.checked;a.setChecked(!a.checked,true);if(a.checkChangeDisabled){a.disableCheckChange()}},disableCheckChange:function(){var b=this,a=b.checkEl;if(a){a.addCls(b.disabledCls)}if(!(Ext.isIE10p||(Ext.isIE9&&Ext.isStrict))&&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},onClick:function(b){var a=this;if(!a.disabled&&!a.checkChangeDisabled&&!(a.checked&&a.group)){a.setChecked(!a.checked)}this.callParent([b])},onDestroy:function(){Ext.menu.Manager.unregisterCheckable(this);this.callParent(arguments)},setChecked:function(d,b){var c=this,e=c.checkedCls,g=c.uncheckedCls,a=c.el;if(c.checked!==d&&(b||c.fireEvent("beforecheckchange",c,d)!==false)){if(a){if(d){a.addCls(e);a.removeCls(g)}else{a.addCls(g);a.removeCls(e)}}c.checked=d;Ext.menu.Manager.onCheckChange(c,d);if(!b){Ext.callback(c.checkHandler,c.scope||c,[c,d]);c.fireEvent("checkchange",c,d)}}}},0,["menucheckitem"],["component","box","menucheckitem","menuitem"],{component:true,box:true,menucheckitem:true,menuitem:true},["widget.menucheckitem"],0,[Ext.menu,"CheckItem"],0));(Ext.cmd.derive("Ext.menu.KeyNav",Ext.util.KeyNav,{constructor:function(a){var b=this;b.menu=a.target;b.callParent([Ext.apply({down:b.down,enter:b.enter,esc:b.escape,left:b.left,right:b.right,space:b.enter,tab:b.tab,up:b.up},a)])},down:function(b){var a=this,c=a.menu.focusedItem;if(c&&b.getKey()==Ext.EventObject.DOWN&&a.isWhitelisted(c)){return true}a.focusNextItem(1)},enter:function(b){var c=this.menu,a=c.focusedItem;if(c.activeItem){c.onClick(b)}else{if(a&&a.isFormField){return true}}},escape:function(a){Ext.menu.Manager.hideAll()},focusNextItem:function(b){var a=this.menu,e=a.items,h=a.focusedItem,g=h?e.indexOf(h):-1,j=g+b,d=e.length,c=0,k;while(c=d){j=0}}k=e.getAt(j);if(a.canActivateItem(k)){a.setActiveItem(k);break}j+=b;++c}},isWhitelisted:function(a){var b=Ext.FocusManager;return b&&b.isWhitelisted(a)},left:function(a){var b=this.menu,c=b.focusedItem;if(c&&this.isWhitelisted(c)){return true}if(b.parentMenu){b.hide();b.parentMenu.focus()}},right:function(c){var d=this.menu,g=d.focusedItem,a=d.activeItem,b;if(g&&this.isWhitelisted(g)){return true}if(a){b=d.activeItem.menu;if(b){a.expandMenu(0);b.setActiveItem(b.child(":focusable"))}}},tab:function(b){var a=this;if(b.shiftKey){a.up(b)}else{a.down(b)}},up:function(b){var a=this,c=a.menu.focusedItem;if(c&&b.getKey()==Ext.EventObject.UP&&a.isWhitelisted(c)){return true}a.focusNextItem(-1)}},1,0,0,0,0,0,[Ext.menu,"KeyNav"],0));(Ext.cmd.derive("Ext.menu.Separator",Ext.menu.Item,{canActivate:false,focusable:false,hideOnClick:false,plain:true,separatorCls:Ext.baseCSSPrefix+"menu-item-separator",text:" ",ariaRole:"separator",beforeRender:function(a,c){var b=this;b.callParent();b.addCls(b.separatorCls)}},0,["menuseparator"],["component","menuseparator","box","menuitem"],{component:true,menuseparator:true,box:true,menuitem: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?",initComponent:function(){var b=this,d=Ext.baseCSSPrefix,a=[d+"menu"],c=b.bodyCls?[b.bodyCls]:[],e=b.floating!==false;b.addEvents("click","mouseenter","mouseleave","mouseover");Ext.menu.Manager.register(b);if(b.plain){a.push(d+"menu-plain")}b.cls=a.join(" ");c.push(d+"menu-body",Ext.dom.Element.unselectableCls);b.bodyCls=c.join(" ");if(!b.layout){b.layout={type:"vbox",align:"stretchmax",overflowHandler:"Scroller"}}if(e){if(b.minWidth===undefined){b.minWidth=b.defaultMinWidth}}else{b.hidden=!!b.initialConfig.hidden;b.constrain=false}b.callParent(arguments);Ext.override(b.getLayout(),{configureItem:b.configureItem})},initFloatConstrain:Ext.emptyFn,initHierarchyEvents:Ext.emptyFn,getHierarchyState:function(){var a=this.callParent();a.hidden=this.hidden;return a},beforeRender:function(){this.callParent(arguments);if(!this.getSizeModel().width.shrinkWrap){this.layout.align="stretch"}},onBoxReady:function(){var a=this;a.callParent(arguments);if(a.showSeparator){a.iconSepEl=a.layout.getElementTarget().insertFirst({role:"presentation",cls:Ext.baseCSSPrefix+"menu-icon-separator",html:" "})}a.mon(a.el,{click:a.onClick,mouseover:a.onMouseOver,scope:a});a.mouseMonitor=a.el.monitorMouseLeave(100,a.onMouseLeave,a);if(a.enableKeyNav){a.keyNav=new Ext.menu.KeyNav({target:a,keyMap:a.getKeyMap()})}},canActivateItem:function(a){return a&&!a.isDisabled()&&a.isVisible()&&(a.canActivate||!a.isMenuItem)},deactivateActiveItem:function(b){var c=this,d=c.activeItem,a=c.focusedItem;if(d){d.deactivate();if(!d.activated){delete c.activeItem}}if(a&&b){a.blur();delete c.focusedItem}},getFocusEl:function(){return this.focusedItem||this.items.items[0]},hide:function(){this.deactivateActiveItem(true);this.callParent(arguments)},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)}}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(d){var c=this,a=c.owner,e=Ext.baseCSSPrefix,b;if(c.owner.items.getCount()>1&&!d.rendered&&!d.isMenuItem&&!d.dock){b=[e+"menu-item-cmp"];if(!a.plain&&(d.indent!==false||d.iconCls==="no-icon")){b.push(e+"menu-item-indent")}if(d.rendered){d.el.addCls(b)}else{d.cls=(d.cls||"")+" "+b.join(" ")}}this.callParent(arguments)},onClick:function(c){var b=this,a;if(b.disabled){c.stopEvent();return}a=(c.type==="click")?b.getItemFromEvent(c):b.activeItem;if(a&&a.isMenuItem){if(!a.menu||!b.ignoreParentClicks){a.onClick(c)}else{c.stopEvent()}}if(!a||a.disabled){a=undefined}b.fireEvent("click",b,a,c)},onDestroy:function(){var a=this;Ext.menu.Manager.unregister(a);a.parentMenu=a.ownerCmp=a.ownerButton=null;if(a.rendered){a.el.un(a.mouseMonitor);Ext.destroy(a.keyNav);a.keyNav=null}a.callParent(arguments)},onMouseLeave:function(b){var a=this;a.deactivateActiveItem();if(a.disabled){return}a.fireEvent("mouseleave",a,b)},onMouseOver:function(h){var g=this,j=h.getRelatedTarget(),b=!g.el.contains(j),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&&!d.activated){g.setActiveItem(d);if(d.activated&&d.expandMenu){d.expandMenu()}}if(b){g.fireEvent("mouseenter",g,h)}g.fireEvent("mouseover",g,d,h)},setActiveItem:function(b){var a=this;if(b&&(b!=a.activeItem)){a.deactivateActiveItem();if(a.canActivateItem(b)){if(b.activate){b.activate(true);if(b.activated){a.activeItem=b;a.focusedItem=b}}else{b.focus();a.focusedItem=b}}}},beforeShow:function(){var b=this,a;if(b.floating){b.savedMaxHeight=b.maxHeight;a=b.container.getViewSize().height;b.maxHeight=Math.min(b.maxHeight||a,a)}b.callParent(arguments)},afterShow:function(){var a=this;a.callParent(arguments);if(a.floating){a.maxHeight=a.savedMaxHeight}}},0,["menu"],["container","component","box","panel","menu"],{container:true,component:true,box:true,panel:true,menu:true},["widget.menu"],0,[Ext.menu,"Menu"],0));Ext.define("ExtThemeNeptune.menu.Menu",{override:"Ext.menu.Menu",showSeparator:false});Ext.define("ExtThemeNeptune.menu.Separator",{override:"Ext.menu.Separator",border:true});(Ext.cmd.derive("Ext.layout.component.field.Trigger",Ext.layout.component.field.Field,{type:"triggerfield",setWidthInDom:true,borderWidths:{},beginLayout:function(d){var c=this,a=c.owner,b;d.triggerWrap=d.getEl("triggerWrap");c.callParent(arguments);b=a.getTriggerStateFlags();if(b!=a.lastTriggerStateFlags){a.lastTriggerStateFlags=b;c.updateEditState()}},beginLayoutCycle:function(a){this.callParent(arguments);if(a.widthModel.shrinkWrap&&!this.owner.inputWidth){a.inputContext.el.setStyle("width","")}},beginLayoutFixed:function(g,c,h){var d=this,a=g.target,e=d.ieInputWidthAdjustment||0,j="100%",b=a.triggerWrap;d.callParent(arguments);a.inputCell.setStyle("width","100%");if(e){d.adjustIEInputPadding(g);if(h==="px"){if(a.inputWidth){j=a.inputWidth-d.getExtraWidth(g)}else{j=c-e-d.getExtraWidth(g)}j+="px"}}a.inputEl.setStyle("width",j);j=a.inputWidth;if(j){b.setStyle("width",j+(e)+"px")}else{b.setStyle("width",c+h)}b.setStyle("table-layout","fixed")},adjustIEInputPadding:function(a){this.owner.inputCell.setStyle("padding-right",this.ieInputWidthAdjustment+"px")},getExtraWidth:function(d){var b=this,a=b.owner,e=b.borderWidths,c=a.ui+a.triggerEl.getCount();if(!(c in e)){e[c]=d.triggerWrap.getBorderInfo().width}return e[c]+a.getTriggerWidth()},beginLayoutShrinkWrap:function(c){var a=c.target,e="",d=a.inputWidth,b=a.triggerWrap;this.callParent(arguments);if(d){b.setStyle("width",d+"px");d=(d-this.getExtraWidth(c))+"px";a.inputEl.setStyle("width",d);a.inputCell.setStyle("width",d)}else{a.inputCell.setStyle("width",e);a.inputEl.setStyle("width",e);b.setStyle("width",e);b.setStyle("table-layout","auto")}},getTextWidth:function(){var b=this,a=b.owner,d=a.inputEl,c;c=(d.dom.value||(a.hasFocus?"":a.emptyText)||"")+a.growAppend;return d.getTextWidth(c)},publishOwnerWidth:function(c,b){var a=this.owner;this.callParent(arguments);if(!a.grow&&!a.inputWidth){b-=this.getExtraWidth(c);if(a.labelAlign!="top"){b-=a.getLabelWidth()}c.inputContext.setWidth(b)}},publishInnerHeight:function(b,a){b.inputContext.setHeight(a-this.measureLabelErrorHeight(b))},measureContentWidth:function(h){var g=this,b=g.owner,e=g.callParent(arguments),j=h.inputContext,d,a,c;if(b.grow&&!h.state.growHandled){d=g.getTextWidth()+h.inputContext.getFrameInfo().width;a=b.growMax;c=Math.min(a,e);a=Math.max(b.growMin,a,c);d=Ext.Number.constrain(d,b.growMin,a);j.setWidth(d);h.state.growHandled=true;j.domBlock(g,"width");e=NaN}else{if(!b.inputWidth){e-=g.getExtraWidth(h)}}return e},updateEditState:function(){var c=this,a=c.owner,e=a.inputEl,d=Ext.baseCSSPrefix+"trigger-noedit",b,g;if(c.owner.readOnly){e.addCls(d);g=true;b=false}else{if(c.owner.editable){e.removeCls(d);g=false}else{e.addCls(d);g=true}b=!c.owner.hideTrigger}a.triggerCell.setDisplayed(b);e.dom.readOnly=g}},0,0,0,0,["layout.triggerfield"],0,[Ext.layout.component.field,"Trigger"],0));(Ext.cmd.derive("Ext.form.field.Trigger",Ext.form.field.Text,{alternateClassName:["Ext.form.TriggerField","Ext.form.TwinTriggerField","Ext.form.Trigger"],childEls:[{name:"triggerCell",select:"."+Ext.baseCSSPrefix+"trigger-cell"},{name:"triggerEl",select:"."+Ext.baseCSSPrefix+"form-trigger"},"triggerWrap","inputCell"],triggerBaseCls:Ext.baseCSSPrefix+"form-trigger",triggerWrapCls:Ext.baseCSSPrefix+"form-trigger-wrap",triggerNoEditCls:Ext.baseCSSPrefix+"trigger-noedit",hideTrigger:false,editable:true,readOnly:false,repeatTriggerClick:false,autoSize:Ext.emptyFn,monitorTab:true,mimicing:false,triggerIndexRe:/trigger-index-(\d+)/,extraTriggerCls:"",componentLayout:"triggerfield",initComponent:function(){this.wrapFocusCls=this.triggerWrapCls+"-focus";this.callParent(arguments)},initEvents:function(){this.mon(Ext.globalEvents,"beforefocus",this.onOtherFocus,this);this.callParent()},getSubTplMarkup:function(b){var c=this,a=b.childElCls,d=c.callParent(arguments);return['','','','",c.getTriggerMarkup(),"","",""].join("")},getSubTplData:function(){var b=this,c=b.callParent(),d=b.readOnly===true,a=b.editable!==false;return Ext.apply(c,{editableCls:(d||!a)?" "+b.triggerNoEditCls:"",readOnly:!a||d})},getLabelableRenderData:function(){var b=this,c=b.triggerWrapCls,a=b.callParent(arguments);return Ext.applyIf(a,{triggerWrapCls:c,triggerMarkup:b.getTriggerMarkup()})},getTriggerMarkup:function(){var e=this,c=0,j=(e.readOnly||e.hideTrigger),a,g=e.triggerBaseCls,h=[],d=Ext.dom.Element.unselectableCls,b="width:"+e.triggerWidth+"px;"+(j?"display:none;":""),k=e.extraTriggerCls+" "+Ext.baseCSSPrefix+"trigger-cell "+d;if(!e.trigger1Cls){e.trigger1Cls=e.triggerCls}for(c=0;(a=e["trigger"+(c+1)+"Cls"])||c<1;c++){h.push({tag:"td",role:"presentation",valign:"top",cls:k,style:b,cn:{cls:[Ext.baseCSSPrefix+"trigger-index-"+c,g,a].join(" "),role:"presentation"}})}h[0].cn.cls+=" "+g+"-first";return Ext.DomHelper.markup(h)},disableCheck:function(){return !this.disabled},beforeRender:function(){var a=this,b=a.triggerBaseCls,c;if(!a.triggerWidth){c=Ext.getBody().createChild({role:"presentation",style:"position: absolute;",cls:Ext.baseCSSPrefix+"form-trigger"});Ext.form.field.Trigger.prototype.triggerWidth=c.getWidth();c.remove()}a.callParent();if(b!==Ext.baseCSSPrefix+"form-trigger"){a.addChildEls({name:"triggerEl",select:"."+b})}a.lastTriggerStateFlags=a.getTriggerStateFlags()},onRender:function(){var a=this;a.callParent(arguments);a.doc=Ext.getDoc();a.initTrigger()},getTriggerWidth:function(){var b=this,a=0;if(b.triggerWrap&&!b.hideTrigger&&!b.readOnly){a=b.triggerEl.getCount()*b.triggerWidth}return a},setHideTrigger:function(a){if(a!=this.hideTrigger){this.hideTrigger=a;this.updateLayout()}},setEditable:function(a){if(a!=this.editable){this.editable=a;this.updateLayout()}},setReadOnly:function(c){var b=this,a=b.readOnly;b.callParent(arguments);if(c!=a){b.updateLayout()}},initTrigger:function(){var g=this,h=g.triggerWrap,l=g.triggerEl,a=g.disableCheck,c,e,b,d,j,k;if(g.repeatTriggerClick){g.triggerRepeater=new Ext.util.ClickRepeater(h,{preventDefault:true,handler:g.onTriggerWrapClick,listeners:{mouseup:g.onTriggerWrapMouseup,scope:g},scope:g})}else{g.mon(h,{click:g.onTriggerWrapClick,mouseup:g.onTriggerWrapMouseup,scope:g})}l.setVisibilityMode(Ext.Element.DISPLAY);l.addClsOnOver(g.triggerBaseCls+"-over",a,g);c=l.elements;e=c.length;for(d=0;d0;){p[d].hasListeners._incr_(m)}s=j[m]||(j[m]={});s=s[c]||(s[c]={});h=s[g.id]||(s[g.id]=[]);h.push(a)}}}}},match:function(c,a){var b=this.idProperty;if(b){return a==="*"||c[b]===a}return false},monitor:function(d){var b=this,a=d.isInstance?d:d.prototype,c=a.fireEventArgs;b.monitoredClasses.push(d);a.fireEventArgs=function(h,g){var e=c.apply(this,arguments);if(e!==false){e=b.dispatch(this,h,g)}return e}},unlisten:function(e){var b=this.bus,g,d,a,c;for(d in b){d=d.toLowerCase();if(b.hasOwnProperty(d)&&(c=b[d])){for(a in c){g=c[a];delete g[e]}}}}},1,0,0,0,0,0,[Ext.app,"EventDomain"],0));(Ext.cmd.derive("Ext.app.domain.Component",Ext.app.EventDomain,{singleton:true,type:"component",constructor:function(){var a=this;a.callParent();a.monitor(Ext.Component)},match:function(b,a){return b.is(a)}},1,0,0,0,0,0,[Ext.app.domain,"Component"],0));(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:function(){return true}},1,0,0,0,0,0,[Ext.app.domain,"Global"],0));(Ext.cmd.derive("Ext.app.domain.Store",Ext.app.EventDomain,{singleton:true,type:"store",idProperty:"storeId",constructor:function(){var a=this;a.callParent();a.monitor(Ext.data.AbstractStore)}},1,0,0,0,0,0,[Ext.app.domain,"Store"],0));(Ext.cmd.derive("Ext.app.Controller",Ext.Base,{statics:{strings:{model:{getter:"getModel",upper:"Model"},view:{getter:"getView",upper:"View"},controller:{getter:"getController",upper:"Controller"},store:{getter:"getStore",upper:"Store"}},controllerRegex:/^(.*)\.controller\./,createGetter:function(a,b){return function(){return this[a](b)}},getGetterName:function(c,a){var d="get",e=c.split("."),g=e.length,b;for(b=0;b0){a=c.substring(0,b);g=c.substring(b+1)+"."+a}else{if(c.indexOf(".")>0&&(Ext.ClassManager.isCreated(c)||Ext.Loader.isAClassNameWithAKnownPrefix(c))){g=c}else{if(d){g=d+"."+e+"."+c;a=c}else{g=c}}}return{absoluteName:g,shortName:a}}},application:null,onClassExtended:function(b,c,a){var d=a.onBeforeCreated;a.onBeforeCreated=function(n,h){var g=Ext.app.Controller,l=g.controllerRegex,o=[],m,e,o,k,j;k=n.prototype;m=Ext.getClassName(n);e=h.$namespace||Ext.app.getNamespace(m)||((j=l.exec(m))&&j[1]);if(e){k.$namespace=e}g.processDependencies(k,o,e,"model",h.models);g.processDependencies(k,o,e,"view",h.views);g.processDependencies(k,o,e,"store",h.stores);g.processDependencies(k,o,e,"controller",h.controllers);Ext.require(o,Ext.Function.pass(d,arguments,this))}},constructor:function(a){var b=this;b.mixins.observable.constructor.call(b,a);if(b.refs){b.ref(b.refs)}b.eventbus=Ext.app.EventBus;b.initAutoGetters()},initAutoGetters:function(){var b=this.self.prototype,c,a;for(c in b){a=b[c];if(a&&a["Ext.app.getter"]){a.call(this)}}},doInit:function(b){var a=this;if(!a._initialized){a.init(b);a._initialized=true}},finishInit:function(g){var d=this,e=d.controllers,b,c,a;if(d._initialized&&e&&e.length){for(c=0,a=e.length;cb){e=d[a];for(c in e){if(e[c]){e[c].hide(true)}}}}},1,0,0,0,0,0,[Ext.chart,"Callout"],0));(Ext.cmd.derive("Ext.draw.CompositeSprite",Ext.util.MixedCollection,{autoDestroy:false,isCompositeSprite:true,constructor:function(a){var b=this;a=a||{};Ext.apply(b,a);b.addEvents("mousedown","mouseup","mouseover","mouseout","click");b.id=Ext.id(null,"ext-sprite-group-");b.callParent()},onClick:function(a){this.fireEvent("click",a)},onMouseUp:function(a){this.fireEvent("mouseup",a)},onMouseDown:function(a){this.fireEvent("mousedown",a)},onMouseOver:function(a){this.fireEvent("mouseover",a)},onMouseOut:function(a){this.fireEvent("mouseout",a)},attachEvents:function(b){var a=this;b.on({scope:a,mousedown:a.onMouseDown,mouseup:a.onMouseUp,mouseover:a.onMouseOver,mouseout:a.onMouseOut,click:a.onClick})},add:function(b,c){var a=this.callParent(arguments);this.attachEvents(a);return a},insert:function(a,b,c){return this.callParent(arguments)},remove:function(b){var a=this;b.un({scope:a,mousedown:a.onMouseDown,mouseup:a.onMouseUp,mouseover:a.onMouseOver,mouseout:a.onMouseOut,click:a.onClick});return a.callParent(arguments)},getBBox:function(){var e=0,n,j,k=this.items,g=this.length,h=Infinity,c=h,m=-h,b=h,l=-h,d,a;for(;e0){b=d.first();d.remove(b);a.remove(b,c)}}d.clearListeners()}},1,0,0,0,0,[["animate",Ext.util.Animate]],[Ext.draw,"CompositeSprite"],0));(Ext.cmd.derive("Ext.draw.Surface",Ext.Base,{separatorRe:/[, ]+/,enginePriority:["Svg","Vml"],statics:{create:function(b,d){d=d||this.prototype.enginePriority;var c=0,a=d.length;for(;c1,a,d,c,h,g;if(j||Ext.isArray(b[0])){a=j?b:b[0];d=[];for(c=0,h=a.length;ch){b=j-1}else{if(a-1;b--){this.remove(a[b],d)}},onRemove:Ext.emptyFn,onDestroy:Ext.emptyFn,applyViewBox:function(){var d=this,m=d.viewBox,a=d.width||1,h=d.height||1,g,e,k,b,j,c,l;if(m&&(a||h)){g=m.x;e=m.y;k=m.width;b=m.height;j=h/b;c=a/k;l=Math.min(c,j);if(k*l0.85){e=e.getDarker(0.3)}else{if(h>0.7){e=e.getDarker(0.15)}}}}c.colors=[e.getDarker(0.3).toString(),e.getDarker(0.15).toString(),e.toString(),e.getLighter(0.15).toString(),e.getLighter(0.3).toString()];delete c.baseColor}if(c.colors){a=c.colors.slice();s=b.markerThemes;r=b.seriesThemes;j=a.length;b.colors=a;for(;m0?q:q+a)+h.el.getX(),y:(o>0?n:n+o)+h.el.getY(),width:s(a),height:s(o)};h.mask.updateBox(h.maskSelection);h.mask.show();h.maskSprite.setAttributes({hidden:true},true)}else{if(t=="horizontal"){r=["M",q,k,"L",q,o]}else{if(t=="vertical"){r=["M",m,n,"L",a,n]}else{r=["M",q,k,"L",q,o,"M",m,n,"L",a,n]}}h.maskSprite.setAttributes({path:r,"stroke-width":t===true?1:1,hidden:false},true)}}},onMouseLeave:function(b){var a=this;a.mouseMoved=false;a.mouseDown=false;a.maskMouseDown=false;a.mask.hide();a.maskSprite.hide(true)}},1,0,0,0,0,0,[Ext.chart,"Mask"],0));(Ext.cmd.derive("Ext.chart.Navigation",Ext.Base,{setZoom:function(p){var t=this,o=t.axes.items,r,m,c,a=t.chartBBox,u=a.width,d=a.height,g={x:p.x-t.el.getX(),y:p.y-t.el.getY(),width:p.width,height:p.height},j,n,q,b,h,l,k,e,s;for(r=0,m=o.length;r0.5?0.2:0.8;K.setAttributes({fill:String(n.fromHSL.apply({},F))},true)}if(q.stacked&&d&&(y.totalPositiveValues||y.totalNegativeValues)){T=(y.totalPositiveValues||0);I=(y.totalNegativeValues||0);B=T+I;if(d=="total"){o=z(B)}else{if(d=="balances"){if(T==0&&I==0){o=z(0)}else{o=z(T);N=z(I)}}}if(o){K=s.getAt(c);if(!K){K=q.onCreateLabel(S,y,P,"over")}F=n.fromString(K.attr.color||K.attr.fill).getHSL();K.setAttributes({text:o,style:E.font,fill:String(n.fromHSL.apply({},F))},true);q.onPlaceLabel(K,S,y,P,"over",M,C);c++}if(N){K=s.getAt(c);if(!K){K=q.onCreateLabel(S,y,P,"under")}F=n.fromString(K.attr.color||K.attr.fill).getHSL();K.setAttributes({text:N,style:E.font,fill:String(n.fromHSL.apply({},F))},true);q.onPlaceLabel(K,S,y,P,"under",M,C);c++}}}J++;C++}}m=s.length;while(m>c){Q.push(c);c++}}q.hideLabels(Q)},hideLabels:function(b){var a=this.labelsGroup,c=!!b&&b.length;if(!a){return}if(c===false){c=a.getCount();while(c--){a.getAt(c).hide(true)}}else{while(c--){a.getAt(b[c]).hide(true)}}}},1,0,0,0,0,0,[Ext.chart,"Label"],0));(Ext.cmd.derive("Ext.chart.TipSurface",Ext.draw.Component,{spriteArray:false,renderFirst:true,constructor:function(a){this.callParent([a]);if(a.sprites){this.spriteArray=[].concat(a.sprites);delete a.sprites}},onRender:function(){var c=this,b=0,a=0,d,e;this.callParent(arguments);e=c.spriteArray;if(c.renderFirst&&e){c.renderFirst=false;for(a=e.length;br){t=r}if(x=0){u[v].positiveValue+=r;if(x0){t=0}}else{u[v].negativeValue+=r;if(t>u[v].negativeValue){t=u[v].negativeValue}if(x<0){x=0}}}}}}if(!isFinite(x)){x=C.prevMax||0}if(!isFinite(t)){t=C.prevMin||0}if(typeof t==="number"){t=Ext.Number.correctFloat(t)}if(typeof x==="number"){x=Ext.Number.correctFloat(x)}if(t!=x&&(x!=Math.floor(x)||t!=Math.floor(t))){t=Math.floor(t);x=Math.floor(x)+1}if(!isNaN(C.minimum)){t=C.minimum}if(!isNaN(C.maximum)){x=C.maximum}if(t>=x){t=Math.floor(t);x=t+1}return{min:t,max:x}},calcEnds:function(){var h=this,d=h.getRange(),g=d.min,a=d.max,c,j,e,b;c=(Ext.isNumber(h.majorTickSteps)?h.majorTickSteps+1:h.steps);j=!(Ext.isNumber(h.maximum)&&Ext.isNumber(h.minimum)&&Ext.isNumber(h.majorTickSteps)&&h.majorTickSteps>0);e=Ext.draw.Draw.snapEnds(g,a,c,j);if(Ext.isNumber(h.maximum)){e.to=h.maximum;b=true}if(Ext.isNumber(h.minimum)){e.from=h.minimum;b=true}if(h.adjustMaximumByMajorUnit){e.to=Math.ceil(e.to/e.step)*e.step;b=true}if(h.adjustMinimumByMajorUnit){e.from=Math.floor(e.from/e.step)*e.step;b=true}if(b){e.steps=Math.ceil((e.to-e.from)/e.step)}h.prevMin=(g==a?0:g);h.prevMax=a;return e},drawAxis:function(P){var o=this,R,J=o.x,I=o.y,W=o.dashSize,r=o.length,K=o.position,b=(K=="left"||K=="right"),m=[],l=(o.isNumericAxis),v=o.applyData(),B=v.step,F=v.steps,H=Ext.isArray(F),k=v.from,V=v.to,j=(V-k)||1,T,A,z,N,D=o.minorTickSteps||0,C=o.minorTickSteps||0,q=Math.max(D+1,0),p=Math.max(C+1,0),L=(K=="left"||K=="top"?-1:1),e=W*L,d=o.chart.series.items,O=d[0],s=Ext.clone(O?O.nullGutters:o.nullGutters),U,g,c,Q,S,t,G=0,E=0,a,M,w,u,h,n;o.from=k;o.to=V;if(o.hidden||(k>V)){return}if((H&&(F.length==0))||(!H&&isNaN(B))){return}if(H){F=Ext.Array.filter(F,function(y,x,X){return(+y>+o.from&&+y<+o.to)},this);F=Ext.Array.union([o.from],F,[o.to])}else{F=new Array();for(u=+o.from;u<+o.to;u+=B){F.push(u)}F.push(+o.to)}E=F.length;for(R=0,w=d.length;R0){t=B/(S+1)}}if(s&&t){for(a=0;a=0){if(!this.sprites){for(e=0;e<=l;e++){n=a.add({type:"path",path:["M",d+(m-c)*o(e/l*g-g),b+(m-c)*k(e/l*g-g),"L",d+m*o(e/l*g-g),b+m*k(e/l*g-g),"Z"],stroke:"#ccc"});n.setAttributes({hidden:false},true);h.push(n)}}else{h=this.sprites;for(e=0;e<=l;e++){h[e].setAttributes({path:["M",d+(m-c)*o(e/l*g-g),b+(m-c)*k(e/l*g-g),"L",d+m*o(e/l*g-g),b+m*k(e/l*g-g),"Z"],stroke:"#ccc"},true)}}}this.sprites=h;this.drawLabel();if(this.title){this.drawTitle()}},drawTitle:function(){var e=this,d=e.chart,a=d.surface,g=d.chartBBox,c=e.titleSprite,b;if(!c){e.titleSprite=c=a.add(Ext.apply({type:"text",zIndex:2},e.axisTitleStyle,e.labelTitle))}c.setAttributes(Ext.apply({text:e.title},e.label||{}),true);b=c.getBBox();c.setAttributes({x:g.x+(g.width/2)-(b.width/2),y:g.y+g.height-(b.height/2)-4},true)},setTitle:function(a){this.title=a;this.drawTitle()},drawLabel:function(){var y=this,n=y.chart,r=n.surface,b=n.chartBBox,k=b.x+(b.width/2),h=b.y+b.height,o=y.margin||10,d=Math.min(b.width,2*b.height)/2+2*o,w=Math.round,p=[],g,u=y.maximum||0,m=y.minimum||0,t=y.steps,v=Math.PI,c=Math.cos,a=Math.sin,e=this.label,q=e.renderer||Ext.identityFn,j=y.reverse,s,x,l;if(!this.labelArray){for(s=0;s<=t;s++){x=(s===0||s===t)?7:0;l=j?t-s:s;g=r.add({type:"text",text:q(w(m+l/t*(u-m))),x:k+d*c(s/t*v-v),y:h+d*a(s/t*v-v)-x,"text-anchor":"middle","stroke-width":0.2,zIndex:10,stroke:"#333"});g.setAttributes({hidden:false},true);p.push(g)}}else{p=this.labelArray;for(s=0;s<=t;s++){x=(s===0||s===t)?7:0;l=j?t-s:s;p[s].setAttributes({text:q(w(m+l/t*(u-m))),x:k+d*c(s/t*v-v),y:h+d*a(s/t*v-v)-x},true)}}this.labelArray=p}},0,0,0,0,["axis.gauge"],0,[Ext.chart.axis,"Gauge"],0));(Ext.cmd.derive("Ext.chart.axis.Numeric",Ext.chart.axis.Axis,{alternateClassName:"Ext.chart.NumericAxis",type:"Numeric",isNumericAxis:true,constructor:function(c){var d=this,a=!!(c.label&&c.label.renderer),b;d.callParent([c]);b=d.label;if(c.constrain==null){d.constrain=(c.minimum!=null&&c.maximum!=null)}if(!a){b.renderer=function(e){return d.roundToDecimal(e,d.decimals)}}},roundToDecimal:function(a,c){var b=Math.pow(10,c||0);return Math.round(a*b)/b},minimum:NaN,maximum:NaN,constrain:true,decimals:2,scale:"linear",doConstrain:function(){var u=this,h=u.chart,b=h.getChartStore(),j=b.data.items,t,w,a,e=h.series.items,k=u.fields,c=k.length,g=u.calcEnds(),n=g.from,q=g.to,r,o,s=false,m,v=[],p;for(t=0,w=j.length;t+q){p=false;break}}if(p){v.push(a)}}h.setSubStore(new Ext.data.Store({model:b.model,data:v}))},position:"left",adjustMaximumByMajorUnit:false,adjustMinimumByMajorUnit:false,processView:function(){var e=this,d=e.chart,c=d.series.items,b,a;for(b=0,a=c.length;b>0),e)}}}},processView:function(){var a=this;if(a.fromDate){a.minimum=+a.fromDate}if(a.toDate){a.maximum=+a.toDate}if(a.constrain){a.doConstrain()}},calcEnds:function(){var c=this,a,b=c.step;if(b){a=c.getRange();a=Ext.draw.Draw.snapEndsByDateAndStep(new Date(a.min),new Date(a.max),Ext.isNumber(b)?[Date.MILLI,b]:b);if(c.minimum){a.from=c.minimum}if(c.maximum){a.to=c.maximum}return a}else{return c.callParent(arguments)}}},1,0,0,0,["axis.time"],0,[Ext.chart.axis,"Time",Ext.chart,"TimeAxis"],0));(Ext.cmd.derive("Ext.chart.series.Series",Ext.Base,{type:null,title:null,showInLegend:true,renderer:function(e,a,c,d,b){return c},shadowAttributes:null,animating:false,nullGutters:{lower:0,upper:0,verticalAxis:undefined},nullPadding:{left:0,right:0,width:0,bottom:0,top:0,height:0},constructor:function(a){var b=this;if(a){Ext.apply(b,a)}b.shadowGroups=[];b.mixins.labels.constructor.call(b,a);b.mixins.highlights.constructor.call(b,a);b.mixins.tips.constructor.call(b,a);b.mixins.callouts.constructor.call(b,a);b.addEvents({scope:b,itemclick:true,itemdblclick:true,itemmouseover:true,itemmouseout:true,itemmousedown:true,itemmouseup:true,mouseleave:true,afterdraw:true,titlechange:true});b.mixins.observable.constructor.call(b,a);b.on({scope:b,itemmouseover:b.onItemMouseOver,itemmouseout:b.onItemMouseOut,mouseleave:b.onMouseLeave});if(b.style){Ext.apply(b.seriesStyle,b.style)}},initialize:Ext.emptyFn,onRedraw:Ext.emptyFn,eachRecord:function(c,b){var a=this.chart;a.getChartStore().each(c,b)},getRecordCount:function(){var b=this.chart,a=b.getChartStore();return a?a.getCount():0},isExcluded:function(a){var b=this.__excludes;return !!(b&&b[a])},setBBox:function(a){var d=this,c=d.chart,b=c.chartBBox,h=a?{left:0,right:0,bottom:0,top:0}:c.maxGutters,e,g;e={x:b.x,y:b.y,width:b.width,height:b.height};d.clipBox=e;g={x:(e.x+h.left)-(c.zoom.x*c.zoom.width),y:(e.y+h.bottom)-(c.zoom.y*c.zoom.height),width:(e.width-(h.left+h.right))*c.zoom.width,height:(e.height-(h.bottom+h.top))*c.zoom.height};d.bbox=g},onAnimate:function(b,a){var c=this;b.stopAnimation();if(c.animating){return b.animate(Ext.applyIf(a,c.chart.animate))}else{c.animating=true;return b.animate(Ext.apply(Ext.applyIf(a,c.chart.animate),{callback:function(){c.animating=false;c.fireEvent("afterrender",c)}}))}},getGutters:function(){return this.nullGutters},getPadding:function(){return this.nullPadding},onItemMouseOver:function(b){var a=this;if(b.series===a){if(a.highlight){a.highlightItem(b)}if(a.tooltip){a.showTip(b)}}},onItemMouseOut:function(b){var a=this;if(b.series===a){a.unHighlightItem();if(a.tooltip){a.hideTip(b)}}},onMouseLeave:function(){var a=this;a.unHighlightItem();if(a.tooltip){a.hideTip()}},getItemForPoint:function(a,j){if(!this.items||!this.items.length||this.seriesIsHidden){return null}var g=this,b=g.items,h=g.bbox,e,c,d;if(!Ext.draw.Draw.withinBox(a,j,h)){return null}for(c=0,d=b.length;c0){c=Infinity;m=-c;for(e=0,j=d.length;em){m=b}if(bm){m=s}if(s0){b=Infinity;m=-b;for(d=0,j=c.length;dm){m=o}if(n-1){b="top"}else{if(Ext.Array.indexOf(d,"bottom")>-1){b="bottom"}else{if(m.get("top")&&m.get("bottom")){for(h=0,l=p.length;h-1){a=g?"right":"left"}else{if(Ext.Array.indexOf(d,"right")>-1){a=g?"left":"right"}else{if(m.get("left")&&m.get("right")){for(h=0,l=e.length;h0&&r){E=M[0].get(j.xField);if(typeof E!="number"){E=+E;if(isNaN(E)){r=false}}}for(J=0;Jk.width)&&j.areas){I=j.shrink(z,D,k.width);z=I.x;D=I.y}return{bbox:k,minX:C,minY:B,xValues:z,yValues:D,xScale:h,yScale:F,areasLen:A}},getPaths:function(){var B=this,o=B.chart,c=o.getChartStore(),e=true,g=B.getBounds(),a=g.bbox,p=B.items=[],A=[],b,d=0,r=[],h=B.reverse,u,k,l,j,s,w,m,C,t,z,q,v,n;k=g.xValues.length;for(u=0;ua.x+a.width){h=a.x+a.width-m}}g=g-l;if(ga.y+a.height){g-=2*l}}if(u.chart.animate&&!u.chart.resizing){e.show(true);u.onAnimate(e,{to:{x:h,y:g}})}else{e.setAttributes({x:h,y:g},true);if(r&&u.animation){u.animation.on("afteranimate",function(){e.show(true)})}else{e.show(true)}}},onPlaceCallout:function(m,r,J,G,F,d,k){var M=this,s=M.chart,D=s.surface,H=s.resizing,L=M.callouts,t=M.items,v=(G==0)?false:t[G-1].point,z=(G==t.length-1)?false:t[G+1].point,c=J.point,A,g,N,K,o,q,b=(m&&m.label?m.label.getBBox():{width:0,height:0}),I=30,C=10,B=3,h,e,j,w,u,E=M.clipRect,n,l;if(!b.width||!b.height){return}if(!v){v=c}if(!z){z=c}K=(z[1]-v[1])/(z[0]-v[0]);o=(c[1]-v[1])/(c[0]-v[0]);q=(z[1]-c[1])/(z[0]-c[0]);g=Math.sqrt(1+K*K);A=[1/g,K/g];N=[-A[1],A[0]];if(o>0&&q<0&&N[1]<0||o<0&&q>0&&N[1]>0){N[0]*=-1;N[1]*=-1}else{if(Math.abs(o)Math.abs(q)&&N[0]>0){N[0]*=-1;N[1]*=-1}}n=c[0]+N[0]*I;l=c[1]+N[1]*I;h=n+(N[0]>0?0:-(b.width+2*B));e=l-b.height/2-B;j=b.width+2*B;w=b.height+2*B;if(h(E[0]+E[2])){N[0]*=-1}if(e(E[1]+E[3])){N[1]*=-1}n=c[0]+N[0]*I;l=c[1]+N[1]*I;h=n+(N[0]>0?0:-(b.width+2*B));e=l-b.height/2-B;j=b.width+2*B;w=b.height+2*B;m.lines.setAttributes({path:["M",c[0],c[1],"L",n,l,"Z"]},true);m.box.setAttributes({x:h,y:e,width:j,height:w},true);m.label.setAttributes({x:n+(N[0]>0?B:-(b.width+B)),y:l},true);for(u in m){m[u].show(true)}},isItemInPoint:function(k,j,n,c){var h=this,b=n.pointsUp,e=n.pointsDown,r=Math.abs,q=false,m=false,d=h.reverse,g=Infinity,a,o,l;for(a=0,o=b.length;ar(k-l[0])){g=r(k-l[0]);q=true;if(m){++a}}if(!q||(q&&m)){l=b[a-1];if(j>=l[1]&&(!e.length||j<=(e[a-1][1]))){idx=d?o-a:a-1;n.storeIndex=idx;n.storeField=h.yField[c];n.storeItem=h.chart.getChartStore().getAt(idx);n._points=e.length?[l,e[a-1]]:[l];return true}else{break}}}return false},highlightSeries:function(){var a,c,b;if(this._index!==undefined){a=this.areas[this._index];if(a.__highlightAnim){a.__highlightAnim.paused=true}a.__highlighted=true;a.__prevOpacity=a.__prevOpacity||a.attr.opacity||1;a.__prevFill=a.__prevFill||a.attr.fill;a.__prevLineWidth=a.__prevLineWidth||a.attr.lineWidth;b=Ext.draw.Color.fromString(a.__prevFill);c={lineWidth:(a.__prevLineWidth||0)+2};if(b){c.fill=b.getLighter(0.2).toString()}else{c.opacity=Math.max(a.__prevOpacity-0.3,0)}if(this.chart.animate){a.__highlightAnim=new Ext.fx.Anim(Ext.apply({target:a,to:c},this.chart.animate))}else{a.setAttributes(c,true)}}},unHighlightSeries:function(){var a;if(this._index!==undefined){a=this.areas[this._index];if(a.__highlightAnim){a.__highlightAnim.paused=true}if(a.__highlighted){a.__highlighted=false;a.__highlightAnim=new Ext.fx.Anim({target:a,to:{fill:a.__prevFill,opacity:a.__prevOpacity,lineWidth:a.__prevLineWidth}})}}},highlightItem:function(c){var b=this,a,d;if(!c){this.highlightSeries();return}a=c._points;if(a.length===2){d=["M",a[0][0],a[0][1],"L",a[1][0],a[1][1]]}else{d=["M",a[0][0],a[0][1],"L",a[0][0],b.bbox.y+b.bbox.height]}b.highlightSprite.setAttributes({path:d,hidden:false},true)},unHighlightItem:function(a){if(!a){this.unHighlightSeries()}if(this.highlightSprite){this.highlightSprite.hide(true)}},hideAll:function(a){var b=this;a=(isNaN(b._index)?a:b._index)||0;b.__excludes[a]=true;b.areas[a].hide(true);b.redraw()},showAll:function(a){var b=this;a=(isNaN(b._index)?a:b._index)||0;b.__excludes[a]=false;b.areas[a].show(true);b.redraw()},redraw:function(){var a=this,b;b=a.chart.legend.rebuild;a.chart.legend.rebuild=false;a.chart.redraw();a.chart.legend.rebuild=b},hide:function(){if(this.areas){var h=this,b=h.areas,d,c,a,g,e;if(b&&b.length){for(d=0,g=b.length;d0)][U]+=p(O)}}B[+(u>0)].push(p(u));B[+(K>0)].push(p(K));h=l.apply(z,B[0]);e=l.apply(z,B[1]);E=(N?t.height-S.height:t.width-S.width)/(e+h);a=a+h*E*(N?-1:1)}else{if(K/u<0){a=a-K*E*(N?-1:1)}}if(r.boundColumn){x=W.axes.get(L.xAxis);if(x){T=x.applyData();M=T.from;w=T.to}if(r.xField&&!Ext.isNumber(M)){J=r.getMinMaxYValues();M=J[0];w=J[1]}if(!Ext.isNumber(M)){M=0}if(!Ext.isNumber(w)){w=0}v=r.getGutters();P=(t.width-(v.lower+v.upper))/((w-M)||1);k=t.x+v.lower;n=[];for(U=0,C=V.length;U=0){ai+=ah}else{G+=ah}g=Math.round((ah-p(K.minY,0))*K.scale);ad=k+(w>1?aa:0);s={fill:S[ad%l]};if(R){ad=T?(I-ac-1):ac;C=T?(w-h-1):h;if(v.boundColumn){E=K.barsLoc[ad]}else{if(v.configuredColumnGirth&&K.barsLoc.length){E=K.barsLoc[ad]+C*K.groupBarWidth*(1+d)*!X}else{E=y.x+ab.left+(D-O)*0.5+ad*D*(1+n)+C*K.groupBarWidth*(1+d)*!X}}Ext.apply(s,{height:g,width:p(K.groupBarWidth,0),x:E,y:b-g})}else{Q=(I-1)-ac;a=g+(b==K.zero);E=b+(b!=K.zero);if(T){E=K.zero+y.width-a-(B===0?1:0);if(X){E-=B;B+=a}}if(v.configuredColumnGirth&&K.barsLoc.length){ag=K.barsLoc[ac]+h*K.groupBarWidth*(1+d)*!X}else{ag=y.y+ab.top+(D-O)*0.5+Q*D*(1+n)+h*K.groupBarWidth*(1+d)*!X+1}Ext.apply(s,{height:p(K.groupBarWidth,0),width:a,x:E,y:ag})}if(g<0){if(R){s.y=m;s.height=u(g)}else{s.x=m+g;s.width=u(g)}}if(X){if(g<0){m+=g*(R?-1:1)}else{b+=g*(R?-1:1)}N+=u(g);if(g<0){H+=u(g)}}s.x=Math.floor(s.x)+1;L=Math.floor(s.y);if(Ext.isIE8m&&s.y>L){L--}s.y=L;s.width=Math.floor(s.width);s.height=Math.floor(s.height);z.push({series:v,yField:U[aa],storeItem:P,value:[P.get(v.xField),ah],attr:s,point:R?[s.x+s.width/2,ah>=0?s.y:s.y+s.height]:[ah>=0?s.x+s.width:s.x,s.y+s.height/2]});if(Z&&af.resizing){q=R?{x:s.x,y:K.zero,width:s.width,height:0}:{x:K.zero,y:s.y,width:0,height:s.height};if(o&&(X&&!t||!X)){t=true;for(e=0;e=0?(l.y-v.y):(v.y+v.height-l.y-l.height));if(zl.height){q=k}}if(!C){C=l.y;if(S>=0){switch(q){case r:C+=l.height+(O?-F:-c/2);break;case Q:C+=(O?c+H:c/2);break;case k:C+=(O?-F:-c/2);break}}else{switch(q){case r:C+=(O?c+F:c/2);break;case Q:C+=(O?l.height-F:l.height-c/2);break;case k:C+=(O?l.height+c+F:l.height+c/2);break}}}}else{if(!a||!c||(L&&!l.width)){K.hide(true);return}C=l.y+(O?(d+c)/2:d/2);if(q==k){var z=(S>=0?(v.x+v.width-l.x-l.width):(l.x-v.x));if(z=l.width){if(L){if(c>l.width){K.hide(true);return}E=l.x+l.width/2;O=true}else{q=k}}}if(!E){E=l.x;if(S>=0){switch(q){case r:if(J){E+=l.width+(O?-a/2:-a-H)}else{E+=(O?a/2:H)}break;case Q:if(J){E-=O?-a/2:-a-H}else{E+=l.width+(O?-a/2:-a-H)}break;case k:if(J){E-=a+(O?a/2:H)}else{E+=l.width+(O?a/2:H)}break}}else{switch(q){case r:if(J){E-=O?-a/2:-a-H}else{E+=l.width+(O?-a/2:-a-H)}break;case Q:if(J){E+=l.width+(O?-a/2:-a-H)}else{E+=(O?a/2:H)}break;case k:if(J){E-=a+(O?a/2:H)}else{E+=(O?-a/2:-a-H)}break}}}}}else{if(q==G||q==o){if(L&&h){s=K.attr.text;K.setAttributes({style:Ext.applyIf((K.attr&&K.attr.style)||{},{"font-weight":"bold","font-size":"14px"})});g=m.getLabelSize(s,K.attr.style);a=g.width;c=g.height;switch(q){case G:if(I){E=l.x+(O?d/2:(d-a)/2);C=b-(t.totalDim-t.totalNegDim)-c/2-F}else{E=b+(t.totalDim-t.totalNegDim)+H;C=l.y+(O?(d+c)/2:d/2)}break;case o:if(I){E=l.x+(O?d/2:(d-a)/2);C=b+t.totalNegDim+c/2}else{E=b-t.totalNegDim-a-H;C=l.y+(O?(d+c)/2:d/2)}break}}}}if(E==undefined||C==undefined){K.hide(true);return}K.isOutside=(q==k);K.setAttributes({text:s});A={x:E,y:C};A.rotate=O?{x:E,y:C,degrees:270}:m.defaultRotate;if(M&&D){if(I){E=l.x+l.width/2;C=b}else{E=b;C=l.y+l.height/2}K.setAttributes({x:E,y:C},true);if(O){K.setAttributes({rotate:{x:E,y:C,degrees:270}},true)}}if(M){m.onAnimate(K,{zero:t.point[0],to:A})}else{K.setAttributes(Ext.apply(A,{hidden:false}),true)}},getLabelSize:function(j,g){var m=this.testerLabel,a=this.label,d=Ext.apply({},a,g,this.seriesLabelStyle||{}),b=a.orientation==="vertical",l,k,e,c;if(!m){m=this.testerLabel=this.chart.surface.add(Ext.apply({type:"text",opacity:0},d))}m.setAttributes({style:g,text:j},true);l=m.getBBox();k=l.width;e=l.height;return{width:b?e:k,height:b?k:e}},onAnimate:function(l,e){var g=this,k=e.to,c=g.stacked,d=g.reverse,a=0,b,m,h,j;l.show();if(!g.column){if(d){m=l.getBBox();b=l.type=="text";if(!g.inHighlight){if(!c){if(b){h=m.x>=5?h:e.zero}else{if(m.width){a=m.width}h=m.width?m.x:k.x+k.width}}else{h=e.zero}}e.from={x:h,width:a}}if(c){j=e.from;if(!j){j=e.from={}}j.y=k.y;if(!d){j.x=e.zero;if(l.isShadow){j.width=0}}}}return this.callParent(arguments)},isItemInPoint:function(a,d,b){var c=b.sprite.getBBox();return c.x<=a&&c.y<=d&&(c.x+c.width)>=a&&(c.y+c.height)>=d},hideAll:function(a){var e=this.chart.axes,c=e.items,d=c.length,b=0;a=(isNaN(this._index)?a:this._index)||0;if(!this.__excludes){this.__excludes=[]}this.__excludes[a]=true;this.drawSeries();for(b;b0){d.yField.push(e[0].get(b.field))}},getSegment:function(b){var D=this,C=D.rad,d=Math.cos,a=Math.sin,o=Math.abs,l=D.centerX,j=D.centerY,z=0,w=0,v=0,t=0,h=0,g=0,e=0,c=0,A=0.01,n=b.endRho-b.startRho,s=b.startAngle,q=b.endAngle,k=(s+q)/2*C,m=b.margin||0,u=o(q-s)>180,E=Math.min(s,q)*C,B=Math.max(s,q)*C,p=false;l+=m*d(k);j+=m*a(k);z=l+b.startRho*d(E);h=j+b.startRho*a(E);w=l+b.endRho*d(E);g=j+b.endRho*a(E);v=l+b.startRho*d(B);e=j+b.startRho*a(B);t=l+b.endRho*d(B);c=j+b.endRho*a(B);if(o(z-v)<=A&&o(h-e)<=A){p=true}if(p){return{path:[["M",z,h],["L",w,g],["A",b.endRho,b.endRho,0,+u,1,t,c],["Z"]]}}else{return{path:[["M",z,h],["L",w,g],["A",b.endRho,b.endRho,0,+u,1,t,c],["L",v,e],["A",b.startRho,b.startRho,0,+u,0,z,h],["Z"]]}}},calcMiddle:function(q){var l=this,m=l.rad,p=q.slice,o=l.centerX,n=l.centerY,k=p.startAngle,e=p.endAngle,j=Math.max(("rho" in p)?p.rho:l.radius,l.label.minMargin),h=+l.donut,b=Math.min(k,e)*m,a=Math.max(k,e)*m,d=-(b+(a-b)/2),g=o+(q.endRho+q.startRho)/2*Math.cos(d),c=n-(q.endRho+q.startRho)/2*Math.sin(d);q.middle={x:g,y:c}},drawSeries:function(){var x=this,W=x.chart,b=W.getChartStore(),B=x.group,S=x.chart.animate,E=x.chart.axes.get(0),F=E&&E.minimum||x.minimum||0,J=E&&E.maximum||x.maximum||0,o=x.angleField||x.field||x.xField,L=W.surface,I=W.chartBBox,k=x.rad,d=+x.donut,X={},C=[],n=x.seriesStyle,a=x.seriesLabelStyle,h=x.colorArrayStyle,A=h&&h.length||0,l=Math.cos,t=Math.sin,c=-180,P=x.reverse,u,g,e,w,s,D,N,G,H,K,U,T,m,V,y,q,Q,R,r,z,v,O,M;Ext.apply(n,x.style||{});x.setBBox();z=x.bbox;if(x.colorSet){h=x.colorSet;A=h.length}if(!b||!b.getCount()||x.seriesIsHidden){x.hide();x.items=[];return}g=x.centerX=I.x+(I.width/2);e=x.centerY=I.y+I.height;x.radius=Math.min(g-I.x,e-I.y);x.slices=s=[];x.items=C=[];if(!x.value){K=b.getAt(0);x.value=K.get(o)}N=P?J-x.value:x.value;if(x.needle){O={series:x,value:N,startAngle:c,endAngle:0,rho:x.radius};v=c*(1-(N-F)/(J-F));s.push(O)}else{v=c*(1-(N-F)/(J-F));O={series:x,value:N,startAngle:c,endAngle:v,rho:x.radius};M={series:x,value:J-N,startAngle:v,endAngle:0,rho:x.radius};if(P){s.push(M,O)}else{s.push(O,M)}}for(U=0,H=s.length;U=g&&b=n.startRho&&k<=n.endRho)},getLegendColor:function(b){var a=this.colorSet||this.colorArrayStyle;return a[b%a.length]}},1,0,0,0,["series.gauge"],0,[Ext.chart.series,"Gauge"],0));(Ext.cmd.derive("Ext.chart.series.Line",Ext.chart.series.Cartesian,{alternateClassName:["Ext.chart.LineSeries","Ext.chart.LineChart"],type:"line",selectionTolerance:20,showMarkers:true,markerConfig:{},style:{},smooth:false,defaultSmoothness:3,fill:false,constructor:function(c){this.callParent(arguments);var e=this,a=e.chart.surface,g=e.chart.shadow,d,b;c.highlightCfg=Ext.Object.merge({"stroke-width":3},c.highlightCfg);Ext.apply(e,c,{shadowAttributes:[{"stroke-width":6,"stroke-opacity":0.05,stroke:"rgb(0, 0, 0)",translate:{x:1,y:1}},{"stroke-width":4,"stroke-opacity":0.1,stroke:"rgb(0, 0, 0)",translate:{x:1,y:1}},{"stroke-width":2,"stroke-opacity":0.15,stroke:"rgb(0, 0, 0)",translate:{x:1,y:1}}]});e.group=a.getGroup(e.seriesId);if(e.showMarkers){e.markerGroup=a.getGroup(e.seriesId+"-markers")}if(g){for(d=0,b=e.shadowAttributes.length;dax.width){a=aq.shrink(aE,ah,ax.width);aE=a.x;ah=a.y}aq.items=[];n=0;aC=aE.length;for(R=0;Ra.x+a.width){k-=A}else{k+=A}h.setAttributes({rotation:{x:k,y:j,degrees:-45}},true)}else{if(q=="under"||q=="over"){h.setAttributes({rotation:{degrees:0}},true);if(ka.x+a.width){k=a.x+a.width-o}}g=p.height/2+n;j=j+(q=="over"?-g:g);if(ja.y+a.height){j-=2*g}}}}if(z.chart.animate&&!z.chart.resizing){h.show(true);z.onAnimate(h,{to:{x:k,y:j}})}else{h.setAttributes({x:k,y:j},true);if(u&&m.animate){z.on({single:true,afterrender:function(){h.show(true)}})}else{h.show(true)}}},highlightItem:function(){var b=this,a=b.line;b.callParent(arguments);if(a&&!b.highlighted){if(!("__strokeWidth" in a)){a.__strokeWidth=parseFloat(a.attr["stroke-width"])||0}if(a.__anim){a.__anim.paused=true}a.__anim=new Ext.fx.Anim({target:a,to:{"stroke-width":a.__strokeWidth+3}});b.highlighted=true}},unHighlightItem:function(){var c=this,a=c.line,b;c.callParent(arguments);if(a&&c.highlighted){b=a.__strokeWidth||parseFloat(a.attr["stroke-width"])||0;a.__anim=new Ext.fx.Anim({target:a,to:{"stroke-width":b}});c.highlighted=false}},onPlaceCallout:function(m,r,J,G,F,d,k){if(!F){return}var M=this,s=M.chart,D=s.surface,H=s.resizing,L=M.callouts,t=M.items,v=G==0?false:t[G-1].point,z=(G==t.length-1)?false:t[G+1].point,c=[+J.point[0],+J.point[1]],A,g,N,K,o,q,I=L.offsetFromViz||30,C=L.offsetToSide||10,B=L.offsetBox||3,h,e,j,w,u,E=M.clipRect,b={width:L.styles.width||10,height:L.styles.height||10},n,l;if(!v){v=c}if(!z){z=c}K=(z[1]-v[1])/(z[0]-v[0]);o=(c[1]-v[1])/(c[0]-v[0]);q=(z[1]-c[1])/(z[0]-c[0]);g=Math.sqrt(1+K*K);A=[1/g,K/g];N=[-A[1],A[0]];if(o>0&&q<0&&N[1]<0||o<0&&q>0&&N[1]>0){N[0]*=-1;N[1]*=-1}else{if(Math.abs(o)Math.abs(q)&&N[0]>0){N[0]*=-1;N[1]*=-1}}n=c[0]+N[0]*I;l=c[1]+N[1]*I;h=n+(N[0]>0?0:-(b.width+2*B));e=l-b.height/2-B;j=b.width+2*B;w=b.height+2*B;if(h(E[0]+E[2])){N[0]*=-1}if(e(E[1]+E[3])){N[1]*=-1}n=c[0]+N[0]*I;l=c[1]+N[1]*I;h=n+(N[0]>0?0:-(b.width+2*B));e=l-b.height/2-B;j=b.width+2*B;w=b.height+2*B;if(s.animate){M.onAnimate(m.lines,{to:{path:["M",c[0],c[1],"L",n,l,"Z"]}});if(m.panel){m.panel.setPosition(h,e,true)}}else{m.lines.setAttributes({path:["M",c[0],c[1],"L",n,l,"Z"]},true);if(m.panel){m.panel.setPosition(h,e)}}for(u in m){m[u].show(true)}},isItemInPoint:function(j,g,A,q){var C=this,n=C.items,s=C.selectionTolerance,k=null,z,c,p,v,h,w,b,t,a,l,B,e,d,o,u,r,D=Math.sqrt,m=Math.abs;c=n[q];z=q&&n[q-1];if(q>=h){z=n[h-1]}p=z&&z.point;v=c&&c.point;w=z?p[0]:v[0]-s;b=z?p[1]:v[1];t=c?v[0]:p[0]+s;a=c?v[1]:p[1];e=D((j-w)*(j-w)+(g-b)*(g-b));d=D((j-t)*(j-t)+(g-a)*(g-a));o=Math.min(e,d);if(o<=s){return o==e?z:c}return false},toggleAll:function(a){var e=this,b,d,g,c;if(!a){Ext.chart.series.Cartesian.prototype.hideAll.call(e)}else{Ext.chart.series.Cartesian.prototype.showAll.call(e)}if(e.line){e.line.setAttributes({hidden:!a},true);if(e.line.shadows){for(b=0,c=e.line.shadows,d=c.length;b1?U:V)%w]}||{}));D=Ext.apply({},o.segment,{slice:r,series:s,storeItem:r.storeItem,index:V});s.calcMiddle(D);if(g){D.shadows=r.shadowAttrs[U]}y[V]=D;if(!z){m=Ext.apply({type:"path",group:x,middle:D.middle},Ext.apply(h,e&&{fill:e[(L>1?U:V)%w]}||{}));z=K.add(Ext.apply(m,o))}r.sprite=r.sprite||[];D.sprite=z;r.sprite.push(z);r.point=[D.middle.x,D.middle.y];if(T){o=s.renderer(z,J,o,V,a);z._to=o;z._animating=true;s.onAnimate(z,{to:o,listeners:{afteranimate:{fn:function(){this._animating=false},scope:z}}})}else{o=s.renderer(z,J,Ext.apply(o,{hidden:false}),V,a);z.setAttributes(o,true)}B+=q}}G=x.getCount();for(V=0;V>0]&&x.getAt(V)){x.getAt(V).hide(true)}}if(g){ab=R.length;for(E=0;E>0]){for(U=0;U=0?l:-l);S.setAttributes({rotation:{degrees:0}},true);V=S.getBBox();a=V.width/2*Math.cos(B);c=V.height/2*Math.sin(B);a+=z;c+=v;K+=Math.sqrt(a*a+c*c);C.x=K*Math.cos(B)+e;C.y=K*Math.sin(B)+d;break;case"rotate":s=Ext.draw.Draw.normalizeDegrees(s);s=(s>90&&s<270)?s+180:s;j=S.attr.rotation.degrees;if(j!=null&&Math.abs(j-s)>180*0.5){if(s>j){s-=360}else{s+=360}s=s%360}else{s=Ext.draw.Draw.normalizeDegrees(s)}C.rotate={degrees:s,x:C.x,y:C.y};break;default:break}C.translate={x:0,y:0};if(W&&!N&&(E!="rotate"||j!=null)){A.onAnimate(S,{to:C})}else{S.setAttributes(C,true)}S._from=u;if(S.isOutside&&U){var o=S.lineSprite,b=W,w={x:(F.endRho-D/2)*Math.cos(B)+e,y:(F.endRho-D/2)*Math.sin(B)+d},M={x:C.x,y:C.y},ac={};function r(y){return y?y<0?-1:1:0}if(U&&U.length){ac={x:(F.endRho+U.length)*Math.cos(B)+e,y:(F.endRho+U.length)*Math.sin(B)+d}}else{var t=Ext.draw.Draw.normalizeRadians(-B),h=Math.cos(t),q=Math.sin(t),k=(V.width+D+4)/2,Q=(V.height+D+4)/2;if(Math.abs(h)*Q>Math.abs(q)*k){ac.x=M.x-k*r(h);ac.y=M.y+k*q/h*r(h)}else{ac.x=M.x-Q*h/q*r(q);ac.y=M.y+Q*r(q)}}if(!o){o=S.lineSprite=A.createLabelLine(X,C.hidden);b=false}A.drawLabelLine(S,w,ac,b)}else{delete S.lineSprite}},onPlaceCallout:function(l,o,z,v,u,d,e){var A=this,q=A.chart,j=A.centerX,h=A.centerY,B=z.middle,b={x:B.x,y:B.y},m=B.x-j,k=B.y-h,c=1,n,g=Math.atan2(k,m||1),a=(l&&l.label?l.label.getBBox():{width:0,height:0}),w=20,t=10,s=10,r;if(!a.width||!a.height){return}c=z.endRho+w;n=(z.endRho+z.startRho)/2+(z.endRho-z.startRho)/3;b.x=c*Math.cos(g)+j;b.y=c*Math.sin(g)+h;m=n*Math.cos(g);k=n*Math.sin(g);if(q.animate){A.onAnimate(l.lines,{to:{path:["M",m+j,k+h,"L",b.x,b.y,"Z","M",b.x,b.y,"l",m>0?t:-t,0,"z"]}});A.onAnimate(l.box,{to:{x:b.x+(m>0?t:-(t+a.width+2*s)),y:b.y+(k>0?(-a.height-s/2):(-a.height-s/2)),width:a.width+2*s,height:a.height+2*s}});A.onAnimate(l.label,{to:{x:b.x+(m>0?(t+s):-(t+a.width+s)),y:b.y+(k>0?-a.height/4:-a.height/4)}})}else{l.lines.setAttributes({path:["M",m+j,k+h,"L",b.x,b.y,"Z","M",b.x,b.y,"l",m>0?t:-t,0,"z"]},true);l.box.setAttributes({x:b.x+(m>0?t:-(t+a.width+2*s)),y:b.y+(k>0?(-a.height-s/2):(-a.height-s/2)),width:a.width+2*s,height:a.height+2*s},true);l.label.setAttributes({x:b.x+(m>0?(t+s):-(t+a.width+s)),y:b.y+(k>0?-a.height/4:-a.height/4)},true)}for(r in l){l[r].show(true)}},onAnimate:function(b,a){b.show();return this.callParent(arguments)},isItemInPoint:function(l,j,n,e){var h=this,d=h.centerX,c=h.centerY,p=Math.abs,o=p(l-d),m=p(j-c),g=n.startAngle,a=n.endAngle,k=Math.sqrt(o*o+m*m),b=Math.atan2(j-c,l-d)/h.rad;if(h.clockwise){if(bh.firstAngle){b-=h.accuracy}}return(b<=g&&b>a&&k>=n.startRho&&k<=n.endRho)},hideAll:function(e){var c,a,g,b,d,j,h;e=(isNaN(this._index)?e:this._index)||0;this.__excludes=this.__excludes||[];this.__excludes[e]=true;h=this.slices[e].sprite;for(d=0,j=h.length;d=q){j=q-1}g=[];L=[];for(N=0;N45&&n<135)||(n>225&&n<315)){w=(n>45&&n<135?1:-1);b.y+=w*r.height/2}else{w=(n>=135&&n<=225?-1:1);b.x+=w*r.width/2}if(u){h.setAttributes({x:k,y:j},true)}if(d){h.show(true);A.onAnimate(h,{to:b})}else{h.setAttributes(b,true);h.show(true)}},toggleAll:function(a){var e=this,b,d,g,c;if(!a){Ext.chart.series.Radar.superclass.hideAll.call(e)}else{Ext.chart.series.Radar.superclass.showAll.call(e)}if(e.radar){e.radar.setAttributes({hidden:!a},true);if(e.radar.shadows){for(b=0,c=e.radar.shadows,d=c.length;ba.x+a.width){k-=B}else{k+=B}h.setAttributes({rotation:{x:k,y:j,degrees:-45}},true)}else{if(r=="under"||r=="over"){h.setAttributes({rotation:{degrees:0}},true);if(ka.x+a.width){k=a.x+a.width-o}}g=p.height/2+n;j=j+(r=="over"?-g:g);if(ja.y+a.height){j-=2*g}}}}if(!m.animate){h.setAttributes({x:k,y:j},true);h.show(true)}else{if(v){q=w.sprite.getActiveAnimation();if(q){q.on("afteranimate",function(){h.setAttributes({x:k,y:j},true);h.show(true)})}else{h.show(true)}}else{A.onAnimate(h,{to:{x:k,y:j}})}}},onPlaceCallout:function(k,m,B,z,w,c,h){var E=this,n=E.chart,u=n.surface,A=n.resizing,D=E.callouts,o=E.items,b=B.point,F,a=k.label.getBBox(),C=30,t=10,s=3,e,d,g,r,q,v=E.bbox,l,j;F=[Math.cos(Math.PI/4),-Math.sin(Math.PI/4)];l=b[0]+F[0]*C;j=b[1]+F[1]*C;e=l+(F[0]>0?0:-(a.width+2*s));d=j-a.height/2-s;g=a.width+2*s;r=a.height+2*s;if(e(v[0]+v[2])){F[0]*=-1}if(d(v[1]+v[3])){F[1]*=-1}l=b[0]+F[0]*C;j=b[1]+F[1]*C;e=l+(F[0]>0?0:-(a.width+2*s));d=j-a.height/2-s;g=a.width+2*s;r=a.height+2*s;if(n.animate){E.onAnimate(k.lines,{to:{path:["M",b[0],b[1],"L",l,j,"Z"]}},true);E.onAnimate(k.box,{to:{x:e,y:d,width:g,height:r}},true);E.onAnimate(k.label,{to:{x:l+(F[0]>0?s:-(a.width+s)),y:j}},true)}else{k.lines.setAttributes({path:["M",b[0],b[1],"L",l,j,"Z"]},true);k.box.setAttributes({x:e,y:d,width:g,height:r},true);k.label.setAttributes({x:l+(F[0]>0?s:-(a.width+s)),y:j},true)}for(q in k){k[q].show(true)}},onAnimate:function(b,a){b.show();return this.callParent(arguments)},isItemInPoint:function(c,h,e){var b,d=10,a=Math.abs;function g(j){var l=a(j[0]-c),k=a(j[1]-h);return Math.sqrt(l*l+k*k)}b=e.point;return(b[0]-d<=c&&b[0]+d>=c&&b[1]-d<=h&&b[1]+d>=h)}},1,0,0,0,["series.scatter"],0,[Ext.chart.series,"Scatter"],0));(Ext.cmd.derive("Ext.layout.container.Table",Ext.layout.container.Container,{alternateClassName:"Ext.layout.TableLayout",type:"table",createsInnerCt:true,targetCls:Ext.baseCSSPrefix+"table-layout-ct",tableCls:Ext.baseCSSPrefix+"table-layout",cellCls:Ext.baseCSSPrefix+"table-layout-cell",tableAttrs:null,getItemSizePolicy:function(a){return this.autoSizePolicy},initHierarchyState:function(a){a.inShrinkWrapTable=true},getLayoutItems:function(){var g=this,b=[],c=g.callParent(),e,a=c.length,d;for(d=0;d=h||n[d]>0){if(d>=h){d=0;a=0;b++;for(c=0;c0){n[c]--}}}else{d++}}m.push({rowIdx:b,cellIdx:a});for(c=l.colspan||1;c;--c){n[d]=l.rowspan||1;++d}++a}return m},getRenderTree:function(){var k=this,h=k.getLayoutItems(),o,p=[],q=Ext.apply({tag:"table",role:"presentation",cls:k.tableCls,cellspacing:0,cellpadding:0,cn:{tag:"tbody",role:"presentation",cn:p}},k.tableAttrs),c=k.tdAttrs,d=k.needsDivWrap(),e,g=h.length,n,m,j,b,a,l;o=k.calculateCells(h);for(e=0;e0){--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;d1){for(c=0,a=b.length;c0){g.timeout=setTimeout(Ext.bind(k.handleTimeout,k,[g]),n)}k.setupErrorHandling(g);k[m]=Ext.bind(k.handleResponse,k,[g],true);k.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(a,b){var c=true;if(b.timeout){clearTimeout(b.timeout)}delete this[b.callbackName];delete this.requests[b.id];this.cleanupErrorHandling(b);Ext.fly(b.script).remove();if(b.errorType){c=false;Ext.callback(b.failure,b.scope,[b.errorType])}else{Ext.callback(b.success,b.scope,[a])}Ext.callback(b.callback,b.scope,[c,a,b.errorType]);Ext.EventManager.idleEvent.fire()},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",defaultWriterType:"base",callbackKey:"callback",recordParam:"records",autoAppendParams:true,constructor:function(){this.addEvents("exception");this.callParent(arguments)},doRequest:function(a,g,b){var d=this,c=d.buildRequest(a),e=c.params;Ext.apply(c,{callbackKey:d.callbackKey,timeout:d.timeout,scope:d,disableCaching:false,callback:d.createRequestCallback(c,a,g,b)});if(d.autoAppendParams){c.params={}}c.jsonp=Ext.data.JsonP.request(c);c.params=e;a.setStarted();d.lastRequest=c;return c},createRequestCallback:function(d,a,e,b){var c=this;return function(j,g,h){delete c.lastRequest;c.processResponse(j,a,d,g,e,b)}},setException:function(b,a){b.setException(b.request.jsonp.errorType)},buildUrl:function(h){var k=this,a=k.callParent(arguments),d=h.records,e=k.getWriter(),g,c,b,j;if(e&&h.operation.allowWrite()){h=e.write(h)}g=h.params;c=g.filters,delete g.filters;if(c&&c.length){for(j=0;j0){g[k.recordParam]=k.encodeRecords(d)}if(k.autoAppendParams){a=Ext.urlAppend(a,Ext.Object.toQueryString(g))}return a},abort:function(){var a=this.lastRequest;if(a){Ext.data.JsonP.abort(a.jsonp)}},encodeRecords:function(b){var d=[],c=0,a=b.length;for(;c0){b=[];Ext.each(k.records,function(l){var q=true,r=a.length,m;for(m=0;m0){g=function(n,m){var l=e[0].sort(n,m),p=e.length,o;for(o=1;ok){j=j.substring(j.length-k)}else{if(j.length>>16)&4095)|(k.version<<12),4);l[3]=a(128|((k.clockSeq>>>8)&63),2)+a(k.clockSeq&255,2);l[4]=a(k.salt.hi,4)+a(k.salt.lo,8);if(k.version==4){k.init()}else{++j.lo;if(j.lo>=c){j.lo=0;++j.hi}}return l.join("-").toLowerCase()},getRecId:function(j){return j.getId()},init:function(){var k=this,j,l;if(k.version==4){k.clockSeq=d(0,h-1);j=k.salt||(k.salt={});l=k.timestamp||(k.timestamp={});j.lo=d(0,c-1);j.hi=d(0,g-1);l.lo=d(0,c-1);l.hi=d(0,e-1)}else{k.salt=b(k.salt);k.timestamp=b(k.timestamp);k.salt.hi|=256}},reconfigure:function(j){Ext.apply(this,j);this.init()}}}()),1,0,0,0,["idgen.uuid"],0,[Ext.data,"UuidGenerator"],0));(Ext.cmd.derive("Ext.data.reader.Xml",Ext.data.reader.Reader,{alternateClassName:"Ext.data.XmlReader",createAccessor:function(b){var a=this;if(Ext.isEmpty(b)){return Ext.emptyFn}if(Ext.isFunction(b)){return b}return function(c){return a.getNodeValue(Ext.DomQuery.selectNode(b,c))}},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,d;if(!c){d="XML data not found in the response";b=new Ext.data.ResultSet({total:0,count:0,records:[],success:false,message:d});this.fireEvent("exception",this,a,b);Ext.Logger.warn(d);return b}return this.readRecords(c)},getData:function(a){return a.documentElement||a},getRoot:function(b){var c=b.nodeName,a=this.root;if(!a||(c&&c==a)){return b}else{if(Ext.DomQuery.isXml(b)){return Ext.DomQuery.selectNode(a,b)}}},extractData:function(a){var b=this.record;if(b!=a.nodeName){a=Ext.DomQuery.select(b,a)}else{a=[a]}return this.callParent([a])},getAssociatedDataRoot:function(b,a){return Ext.DomQuery.select(a,b)[0]},readRecords:function(a){if(Ext.isArray(a)){a=a[0]}this.xmlData=a;return this.callParent([a])},createFieldAccessExpression:function(g,d,c){var e=this.namespace,b,a;b=g.mapping||((e?e+"|":"")+g.name);if(typeof b==="function"){a=d+".mapping("+c+", this)"}else{a='me.getNodeValue(Ext.DomQuery.selectNode("'+b+'", '+c+"))"}return a}},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",documentRoot:"xmlData",defaultDocumentRoot:"xmlData",header:"",record:"record",writeRecords:function(a,b){var h=this,d=[],c=0,g=b.length,j=h.documentRoot,e=h.record,m=b.length!==1,l,k;d.push(h.header||"");if(!j&&m){j=h.defaultDocumentRoot}if(j){d.push("<",j,">")}for(;c");for(k in l){if(l.hasOwnProperty(k)){d.push("<",k,">",l[k],"")}}d.push("")}if(j){d.push("")}a.xmlData=d.join("");return a}},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);this.callParent([a])}},1,0,0,0,["store.xml"],0,[Ext.data,"XmlStore"],0));(Ext.cmd.derive("Ext.data.association.BelongsTo",Ext.data.association.Association,{alternateClassName:"Ext.data.BelongsToAssociation",constructor:function(c){this.callParent(arguments);var e=this,a=e.ownerModel.prototype,g=e.associatedName,d=e.getterName||"get"+g,b=e.setterName||"set"+g;Ext.applyIf(e,{name:g,foreignKey:g.toLowerCase()+"_id",instanceName:g+"BelongsToInstance",associationKey:g.toLowerCase()});a[d]=e.createGetter();a[b]=e.createSetter()},createSetter:function(){var c=this,b=c.foreignKey,a=c.instanceName;return function(h,e,g){var j=h&&h.isModel,d=j?h.getId():h;if(j){this[a]=h}else{if(this[a] instanceof Ext.data.Model&&!this.isEqual(this.get(b),d)){delete this[a]}}this.set(b,d);if(Ext.isFunction(e)){e={callback:e,scope:g||this}}if(Ext.isObject(e)){return this.save(e)}}},createGetter:function(){var d=this,e=d.associatedName,g=d.associatedModel,c=d.foreignKey,b=d.primaryKey,a=d.instanceName;return function(l,m){l=l||{};var k=this,n=k.get(c),o,h,j;if(l.reload===true||k[a]===undefined){if(Ext.isEmpty(n)){return null}h=Ext.ModelManager.create({},e);h.set(b,n);if(typeof l=="function"){l={callback:l,scope:m||k}}o=l.success;l.success=function(p){k[a]=p;if(o){o.apply(this,arguments)}};g.load(n,l);k[a]=h;return h}else{h=k[a];j=[h];m=m||l.scope||k;if(l){Ext.callback(l,m,j);Ext.callback(l.success,m,j);Ext.callback(l.callback,m,j)}return h}}},read:function(b,a,c){b[this.instanceName]=a.read([c]).records[0]}},1,0,0,0,["association.belongsto"],0,[Ext.data.association,"BelongsTo",Ext.data,"BelongsToAssociation"],0));(Ext.cmd.derive("Ext.util.Inflector",Ext.Base,{singleton:true,plurals:[[(/(quiz)$/i),"$1zes"],[(/^(ox)$/i),"$1en"],[(/([m|l])ouse$/i),"$1ice"],[(/(matr|vert|ind)ix|ex$/i),"$1ices"],[(/(x|ch|ss|sh)$/i),"$1es"],[(/([^aeiouy]|qu)y$/i),"$1ies"],[(/(hive)$/i),"$1s"],[(/(?:([^f])fe|([lr])f)$/i),"$1$2ves"],[(/sis$/i),"ses"],[(/([ti])um$/i),"$1a"],[(/(buffal|tomat|potat)o$/i),"$1oes"],[(/(bu)s$/i),"$1ses"],[(/(alias|status|sex)$/i),"$1es"],[(/(octop|vir)us$/i),"$1i"],[(/(ax|test)is$/i),"$1es"],[(/^person$/),"people"],[(/^man$/),"men"],[(/^(child)$/),"$1ren"],[(/s$/i),"s"],[(/$/),"s"]],singulars:[[(/(quiz)zes$/i),"$1"],[(/(matr)ices$/i),"$1ix"],[(/(vert|ind)ices$/i),"$1ex"],[(/^(ox)en/i),"$1"],[(/(alias|status)es$/i),"$1"],[(/(octop|vir)i$/i),"$1us"],[(/(cris|ax|test)es$/i),"$1is"],[(/(shoe)s$/i),"$1"],[(/(o)es$/i),"$1"],[(/(bus)es$/i),"$1"],[(/([m|l])ice$/i),"$1ouse"],[(/(x|ch|ss|sh)es$/i),"$1"],[(/(m)ovies$/i),"$1ovie"],[(/(s)eries$/i),"$1eries"],[(/([^aeiouy]|qu)ies$/i),"$1y"],[(/([lr])ves$/i),"$1f"],[(/(tive)s$/i),"$1"],[(/(hive)s$/i),"$1"],[(/([^f])ves$/i),"$1fe"],[(/(^analy)ses$/i),"$1sis"],[(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i),"$1$2sis"],[(/([ti])a$/i),"$1um"],[(/(n)ews$/i),"$1ews"],[(/people$/i),"person"],[(/s$/i),""]],uncountable:["sheep","fish","series","species","money","rice","information","equipment","grass","mud","offspring","deer","means"],singular:function(b,a){this.singulars.unshift([b,a])},plural:function(b,a){this.plurals.unshift([b,a])},clearSingulars:function(){this.singulars=[]},clearPlurals:function(){this.plurals=[]},isTransnumeral:function(a){return Ext.Array.indexOf(this.uncountable,a)!=-1},pluralize:function(g){if(this.isTransnumeral(g)){return g}var e=this.plurals,d=e.length,a,c,b;for(b=0;b0);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){l.remainingChildDimensions+=2}}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 g=this,d=g.ownerCtContext,a=g.target,c=g.widthModel,h=a.getHierarchyState(),b;if(c.fixed){h.inShrinkWrapTable=false}else{delete h.inShrinkWrapTable}if(e){if(d&&c.shrinkWrap){b=d.isBoxParent?d:d.boxParent;if(b){b.addBoxChild(g)}}else{if(c.natural){g.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)}},addCls:function(a){this.getClassList().addMany(a)},removeCls:function(a){this.getClassList().removeMany(a)},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",j=e[a]||(e[a]={}),b=e.context,d=b.currentLayout,c=j[g]||(j[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(b.classList&&b.classList.dirty){b.classList.flush()}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 p=this,c=p.previousSize,m,o,e,h,g,d,k,n,l,a,b;if(c){m=p.target;o=m.getAnimationProps();e=o.duration;h=Ext.Object.getKeys(p.animatePolicy);g=Ext.apply({},{from:{},to:{},duration:e||Ext.fx.Anim.prototype.duration},o);for(d=0,k=0,n=h.length;k0||m>0)){if(!x.frameBodyContext){u=x.paddingInfo.width;l=x.paddingInfo.height}if(q){q=s(parseInt(q,10)-(x.borderInfo.width+u),0);j.width=q+"px";++h}if(m){m=s(parseInt(m,10)-(x.borderInfo.height+l),0);j.height=m+"px";++h}}if(x.wrapsComponent&&Ext.isIE9&&Ext.isStrict){if((g=q!==undefined&&x.hasOverflowY)||(a=m!==undefined&&x.hasOverflowX)){p=x.isAbsolute;if(p===undefined){p=false;n=x.target.getTargetEl();t=n.getStyle("position");if(t=="absolute"){t=n.getStyle("box-sizing");p=(t=="border-box")}x.isAbsolute=p}if(p){r=Ext.getScrollbarSize();if(g){q=parseInt(q,10)+r.width;j.width=q+"px";++h}if(a){m=parseInt(m,10)+r.height;j.height=m+"px";++h}}}}if(h){c.setStyle(j)}}},1,0,0,0,0,0,[Ext.layout,"ContextItem"],function(){var c={dom:true,parseInt:true,suffix:"px"},b={dom:true},a={dom:false};this.prototype.styleInfo={containerChildrenSizeDone:a,containerLayoutDone:a,displayed:a,done:a,x:a,y:a,columnWidthsDone:a,left:c,top:c,right:c,bottom:c,width:c,height:c,"border-top-width":c,"border-right-width":c,"border-bottom-width":c,"border-left-width":c,"margin-top":c,"margin-right":c,"margin-bottom":c,"margin-left":c,"padding-top":c,"padding-right":c,"padding-bottom":c,"padding-left":c,"line-height":b,display:b}}));(Ext.cmd.derive("Ext.layout.Context",Ext.Base,{remainingLayouts:0,state:0,constructor:function(a){var b=this;Ext.apply(b,a);b.items={};b.layouts={};b.blockCount=0;b.cycleCount=0;b.flushCount=0;b.calcCount=0;b.animateQueue=b.newQueue();b.completionQueue=b.newQueue();b.finalizeQueue=b.newQueue();b.finishQueue=b.newQueue();b.flushQueue=b.newQueue();b.invalidateData={};b.layoutQueue=b.newQueue();b.invalidQueue=[];b.triggers={data:{},dom:{}}},callLayout:function(b,a){this.currentLayout=b;b[a](this.getCmp(b.owner))},cancelComponent:function(j,a,m){var p=this,h=j,l=!j.isComponent,b=l?h.length:1,d,c,o,n,g,s,q,r,t,e;for(d=0;d0},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){if(b){for(d=0,a=b.length;d0){c.sendRequest(a==1?b[0]:b);c.callBuffer=[]}},configureFormRequest:function(e,a,h){var j=this,b=h[0],k=h[1],l=h[2],c,g,d;c=new Ext.direct.Transaction({provider:j,action:e,method:a.name,args:[b,k,l],callback:l&&Ext.isFunction(k)?Ext.Function.bind(k,l):k,isForm:true});if(j.fireEvent("beforecall",j,c,a)!==false){Ext.direct.Manager.addTransaction(c);g=String(b.getAttribute("enctype")).toLowerCase()=="multipart/form-data";d={extTID:c.id,extAction:e,extMethod:a.name,extType:"rpc",extUpload:String(g)};Ext.apply(c,{form:Ext.getDom(b),isUpload:g,params:k&&Ext.isObject(k.params)?Ext.apply(d,k.params):d});j.fireEvent("call",j,c,a);j.sendFormRequest(c)}},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})}},1,0,0,0,["direct.remotingprovider"],0,[Ext.direct,"RemotingProvider"],0));(Ext.cmd.derive("Ext.dom.Layer",Ext.Element,{alternateClassName:"Ext.Layer",statics:{shims:[]},isLayer:true,localXYNames:{get:"getLocalXY",set:"setLocalXY"},constructor:function(c,b){c=c||{};var d=this,e=Ext.DomHelper,h=c.parentEl,g=h?Ext.getDom(h):document.body,j=c.hideMode,a=Ext.baseCSSPrefix+(c.fixed&&!(Ext.isIE6||Ext.isIEQuirks)?"fixed-layer":"layer");d.el=d;if(b){d.dom=Ext.getDom(b)}if(!d.dom){d.dom=e.append(g,c.dh||{tag:"div",cls:a})}else{d.addCls(a);if(!d.dom.parentNode){g.appendChild(d.dom)}}if(c.preventSync){d.preventSync=true}if(c.id){d.id=d.dom.id=c.id}else{d.id=Ext.id(d.dom)}Ext.Element.addToCache(d);if(c.cls){d.addCls(c.cls)}d.constrain=c.constrain!==false;if(j){d.setVisibilityMode(Ext.Element[j.toUpperCase()]);if(d.visibilityMode==Ext.Element.ASCLASS){d.visibilityCls=c.visibilityCls}}else{if(c.useDisplay){d.setVisibilityMode(Ext.Element.DISPLAY)}else{d.setVisibilityMode(Ext.Element.VISIBILITY)}}if(c.shadow){d.shadowOffset=c.shadowOffset||4;d.shadow=new Ext.Shadow({offset:d.shadowOffset,mode:c.shadow,fixed:c.fixed});d.disableShadow()}else{d.shadowOffset=0}d.useShim=c.shim!==false&&Ext.useShims;if(c.hidden===true){d.hide()}else{if(c.hidden===false){d.show()}}},getZIndex:function(){return parseInt((this.getShim()||this).getStyle("z-index"),10)},getShim:function(){var b=this,c,a;if(!b.useShim){return null}if(!b.shim){c=b.self.shims.shift();if(!c){c=b.createShim();c.enableDisplayMode("block");c.hide()}a=b.dom.parentNode;if(c.dom.parentNode!=a){a.insertBefore(c.dom,b.dom)}b.shim=c}return b.shim},hideShim:function(){var a=this;if(a.shim){a.shim.setDisplayed(false);a.self.shims.push(a.shim);delete a.shim}},disableShadow:function(){var a=this;if(a.shadow&&!a.shadowDisabled){a.shadowDisabled=true;a.shadow.hide();a.lastShadowOffset=a.shadowOffset;a.shadowOffset=0}},enableShadow:function(a){var b=this;if(b.shadow&&b.shadowDisabled){b.shadowDisabled=false;b.shadowOffset=b.lastShadowOffset;delete b.lastShadowOffset;if(a){b.sync(true)}}},sync:function(b){var j=this,o=j.shadow,g,d,a,c,p,l,k,n,e,m;if(j.preventSync){return}if(!j.updating&&j.isVisible()&&(o||j.useShim)){c=j.getShim();p=j[j.localXYNames.get]();l=p[0];k=p[1];n=j.dom.offsetWidth;e=j.dom.offsetHeight;if(o&&!j.shadowDisabled){if(b&&!o.isVisible()){o.show(j)}else{o.realign(l,k,n,e)}if(c){m=c.getStyle("z-index");if(m>j.zindex){j.shim.setStyle("z-index",j.zindex-2)}c.show();if(o.isVisible()){g=o.el.getXY();d=c.dom.style;a=o.el.getSize();if(Ext.supports.CSS3BoxShadow){a.height+=6;a.width+=4;g[0]-=2;g[1]-=4}d.left=(g[0])+"px";d.top=(g[1])+"px";d.width=(a.width)+"px";d.height=(a.height)+"px"}else{c.setSize(n,e);c[j.localXYNames.set](l,k)}}}else{if(c){m=c.getStyle("z-index");if(m>j.zindex){j.shim.setStyle("z-index",j.zindex-2)}c.show();c.setSize(n,e);c[j.localXYNames.set](l,k)}}}return j},remove:function(){this.hideUnders();this.callParent()},beginUpdate:function(){this.updating=true},endUpdate:function(){this.updating=false;this.sync(true)},hideUnders:function(){if(this.shadow){this.shadow.hide()}this.hideShim()},constrainXY:function(){if(this.constrain){var g=Ext.Element.getViewWidth(),b=Ext.Element.getViewHeight(),m=Ext.getDoc().getScroll(),l=this.getXY(),j=l[0],e=l[1],a=this.shadowOffset,k=this.dom.offsetWidth+a,c=this.dom.offsetHeight+a,d=false;if((j+k)>g+m.left){j=g-k-a;d=true}if((e+c)>b+m.top){e=b-c-a;d=true}if(j-1)&&(p[o] in g)){p[o]=g[p[o]]}if(o=="hidden"&&r.type=="text"){continue}if(o in s){c.dom.setAttribute(o,s[o](p[o],r,m))}else{c.dom.setAttribute(o,p[o])}}}if(r.type=="text"){m.tuneText(r,p)}r.dirtyFont=false;b=j.style;if(b){c.setStyle(b)}r.dirty=false;if(Ext.isSafari3){m.webkitRect.show();setTimeout(function(){m.webkitRect.hide()})}},setClip:function(b,g){var e=this,d=g["clip-rect"],a,c;if(d){if(b.clip){b.clip.parentNode.parentNode.removeChild(b.clip.parentNode)}a=e.createSvgElement("clipPath");c=e.createSvgElement("rect");a.id=Ext.id(null,"ext-clip-");c.setAttribute("x",d.x);c.setAttribute("y",d.y);c.setAttribute("width",d.width);c.setAttribute("height",d.height);a.appendChild(c);e.getDefs().appendChild(a);b.el.dom.setAttribute("clip-path","url(#"+a.id+")");b.clip=c}},applyZIndex:function(d){var g=this,b=g.items,a=b.indexOf(d),e=d.el,c;if(g.el.dom.childNodes[a+2]!==e.dom){if(a>0){do{c=b.getAt(--a).el}while(!c&&a>0)}e.insertAfter(c||g.bgRect)}d.zIndexDirty=false},createItem:function(a){var b=new Ext.draw.Sprite(a);b.surface=this;return b},addGradient:function(h){h=Ext.draw.Draw.parseGradient(h);var e=this,d=h.stops.length,a=h.vector,l=Ext.isSafari&&!Ext.isStrict,j,g,k,c,b;b=e.gradientsMap||{};if(!l){if(h.type=="linear"){j=e.createSvgElement("linearGradient");j.setAttribute("x1",a[0]);j.setAttribute("y1",a[1]);j.setAttribute("x2",a[2]);j.setAttribute("y2",a[3])}else{j=e.createSvgElement("radialGradient");j.setAttribute("cx",h.centerX);j.setAttribute("cy",h.centerY);j.setAttribute("r",h.radius);if(Ext.isNumber(h.focalX)&&Ext.isNumber(h.focalY)){j.setAttribute("fx",h.focalX);j.setAttribute("fy",h.focalY)}}j.id=h.id;e.getDefs().appendChild(j);for(c=0;c"},text:function(v){var s=v.attr,r=c.exec(s.font),x=(r&&r[1])||"12",q=(r&&r[3])||"Arial",w=s.text,u=(Ext.isFF3_0||Ext.isFF3_5)?2:4,p="",t;v.getBBox();p+='';p+=Ext.htmlEncode(w)+"";t=d({x:s.x,y:s.y,"font-size":x,"font-family":q,"font-weight":s["font-weight"],"text-anchor":s["text-anchor"],fill:s.fill||"#000","fill-opacity":s.opacity,transform:v.matrix.toSvg()});return""+p+""},rect:function(q){var p=q.attr,r=d({x:p.x,y:p.y,rx:p.rx,ry:p.ry,width:p.width,height:p.height,fill:p.fill||"none","fill-opacity":p.opacity,stroke:p.stroke,"stroke-opacity":p["stroke-opacity"],"stroke-width":p["stroke-width"],transform:q.matrix&&q.matrix.toSvg()});return""},circle:function(q){var p=q.attr,r=d({cx:p.x,cy:p.y,r:p.radius,fill:p.translation.fill||p.fill||"none","fill-opacity":p.opacity,stroke:p.stroke,"stroke-opacity":p["stroke-opacity"],"stroke-width":p["stroke-width"],transform:q.matrix.toSvg()});return""},image:function(q){var p=q.attr,r=d({x:p.x-(p.width/2>>0),y:p.y-(p.height/2>>0),width:p.width,height:p.height,"xlink:href":p.src,transform:q.matrix.toSvg()});return""}},a=function(){var p='';p+='';return p},m=function(){var x='',q="",I,G,w,r,H,K,A,y,u,z,C,p,L,v,F,D,J,E,t,s;w=g.items.items;G=w.length;H=function(P){var W=P.childNodes,T=W.length,S=0,Q,R,M="",N,V,O,U;for(;S0){M+=H(N)}M+=""}return M};if(g.getDefs){q=H(g.getDefs())}else{y=g.gradientsColl;if(y){u=y.keys;z=y.items;C=0;p=u.length}for(;C';var B=r.colors.replace(k,"rgb($1|$2|$3)");B=B.replace(h,"rgba($1|$2|$3|$4)");K=B.split(",");for(F=0,J=K.length;F'}q+=""}}x+=""+q+"";x+=l.rect({attr:{width:"100%",height:"100%",fill:"#fff",stroke:"none",opacity:"0"}});E=new Array(G);for(F=0;F";return x},d=function(r){var q="",p;for(p in r){if(r.hasOwnProperty(p)&&r[p]!=null){q+=p+'="'+r[p]+'" '}}return q};return{singleton:true,generate:function(p,q){q=q||{};o(p);return a()+m()}}},0,0,0,0,0,0,[Ext.draw.engine,"SvgExporter"],0));(Ext.cmd.derive("Ext.draw.engine.Vml",Ext.draw.Surface,{engine:"Vml",map:{M:"m",L:"l",C:"c",Z:"x",m:"t",l:"r",c:"v",z:"x"},bitesRe:/([clmz]),?([^clmz]*)/gi,valRe:/-?[^,\s\-]+/g,fillUrlRe:/^url\(\s*['"]?([^\)]+?)['"]?\s*\)$/i,pathlike:/^(path|rect)$/,NonVmlPathRe:/[ahqstv]/ig,partialPathRe:/[clmz]/g,fontFamilyRe:/^['"]+|['"]+$/g,baseVmlCls:Ext.baseCSSPrefix+"vml-base",vmlGroupCls:Ext.baseCSSPrefix+"vml-group",spriteCls:Ext.baseCSSPrefix+"vml-sprite",measureSpanCls:Ext.baseCSSPrefix+"vml-measure-span",zoom:21600,coordsize:1000,coordorigin:"0 0",zIndexShift:0,orderSpritesByZIndex:false,path2vml:function(t){var n=this,u=n.NonVmlPathRe,b=n.map,e=n.valRe,s=n.zoom,d=n.bitesRe,g=Ext.Function.bind(Ext.draw.Draw.pathToAbsolute,Ext.draw.Draw),m,o,c,a,k,q,h,l;if(String(t).match(u)){g=Ext.Function.bind(Ext.draw.Draw.path2curve,Ext.draw.Draw)}else{if(!String(t).match(n.partialPathRe)){m=String(t).replace(d,function(v,x,p){var w=[],j=x.toLowerCase()=="m",r=b[x];p.replace(e,function(y){if(j&&w.length===2){r+=w+b[x=="m"?"l":"L"];w=[]}w.push(Math.round(y*s))});return r+w});return m}}o=g(t);m=[];for(k=0,q=o.length;k")}a.W=h.span.offsetWidth;a.H=h.span.offsetHeight+2;if(c["text-anchor"]=="middle"){e["v-text-align"]="center"}else{if(c["text-anchor"]=="end"){e["v-text-align"]="right";a.bbx=-Math.round(a.W/2)}else{e["v-text-align"]="left";a.bbx=Math.round(a.W/2)}}}a.X=c.x;a.Y=c.y;a.path.v=Ext.String.format("m{0},{1}l{2},{1}",Math.round(a.X*k),Math.round(a.Y*k),Math.round(a.X*k)+1);j.bbox.plain=null;j.bbox.transform=null;j.dirtyFont=false},setText:function(a,b){a.vml.textpath.string=Ext.htmlDecode(b)},hide:function(){this.el.hide()},show:function(){this.el.show()},hidePrim:function(a){a.el.addCls(Ext.baseCSSPrefix+"hide-visibility")},showPrim:function(a){a.el.removeCls(Ext.baseCSSPrefix+"hide-visibility")},setSize:function(b,a){var c=this;b=b||c.width;a=a||c.height;c.width=b;c.height=a;if(c.el){if(b!=undefined){c.el.setWidth(b)}if(a!=undefined){c.el.setHeight(a)}}c.callParent(arguments)},applyViewBox:function(){var g=this,h=g.viewBox,e=g.width,b=g.height,c,a,d;g.callParent();if(h&&(e||b)){c=g.items.items;a=c.length;for(d=0;d')}}catch(d){c.createNode=function(e){return g.createElement("<"+e+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}}if(!c.el){b=g.createElement("div");c.el=Ext.get(b);c.el.addCls(c.baseVmlCls);c.span=g.createElement("span");Ext.get(c.span).addCls(c.measureSpanCls);b.appendChild(c.span);c.el.setSize(c.width||0,c.height||0);a.appendChild(b);c.el.on({scope:c,mouseup:c.onMouseUp,mousedown:c.onMouseDown,mouseover:c.onMouseOver,mouseout:c.onMouseOut,mousemove:c.onMouseMove,mouseenter:c.onMouseEnter,mouseleave:c.onMouseLeave,click:c.onClick,dblclick:c.onDblClick})}c.renderAll()},renderAll:function(){this.items.each(this.renderItem,this)},redraw:function(a){a.dirty=true;this.renderItem(a)},renderItem:function(a){if(!this.el){return}if(!a.el){this.createSpriteElement(a)}if(a.dirty){this.applyAttrs(a);if(a.dirtyTransform){this.applyTransformations(a)}}},rotationCompensation:function(d,c,a){var b=new Ext.draw.Matrix();b.rotate(-d,0.5,0.5);return{x:b.x(c,a),y:b.y(c,a)}},transform:function(x,I){var H=this,b=H.getBBox(x,true),j=b.x+b.width*0.5,h=b.y+b.height*0.5,B=new Ext.draw.Matrix(),q=x.transformations,v=q.length,C=0,o=0,d=1,c=1,n="",g=x.el,E=g.dom,z=E.style,a=H.zoom,k=x.skew,D=H.viewBoxShift,G,F,s,l,r,p,A,w,u,t,e,m;for(;C32767){m[0]=32767}else{if(m[0]<-32768){m[0]=-32768}}if(m[1]>32767){m[1]=32767}else{if(m[1]<-32768){m[1]=-32768}}k.offset=m}else{z.filter=B.toFilter();z.left=Math.min(B.x(b.x,b.y),B.x(b.x+b.width,b.y),B.x(b.x,b.y+b.height),B.x(b.x+b.width,b.y+b.height))+"px";z.top=Math.min(B.y(b.x,b.y),B.y(b.x+b.width,b.y),B.y(b.x,b.y+b.height),B.y(b.x+b.width,b.y+b.height))+"px"}},createItem:function(a){return Ext.create("Ext.draw.Sprite",a)},getRegion:function(){return this.el.getRegion()},addCls:function(a,b){if(a&&a.el){a.el.addCls(b)}},removeCls:function(a,b){if(a&&a.el){a.el.removeCls(b)}},addGradient:function(g){var d=this.gradientsColl||(this.gradientsColl=Ext.create("Ext.util.MixedCollection")),a=[],j=Ext.create("Ext.util.MixedCollection"),l,e,b,h,k,c;j.addAll(g.stops);j.sortByKey("ASC",function(n,m){n=parseInt(n,10);m=parseInt(m,10);return n>m?1:(n',"{%this.renderContainer(out,values)%}",""],initComponent:function(){var a=this;a.initLabelable();a.initFieldAncestor();a.callParent();a.initMonitor()},getOverflowEl:function(){return this.containerEl},onAdd:function(b){var a=this;if(b.isLabelable&&Ext.isGecko&&a.layout.type==="absolute"&&!a.hideLabel&&a.labelAlign!=="top"){b.x+=(a.labelWidth+a.labelPad)}a.callParent(arguments);if(b.isLabelable&&a.combineLabels){b.oldHideLabel=b.hideLabel;b.hideLabel=true}a.updateLabel()},onRemove:function(c,a){var b=this;b.callParent(arguments);if(!a){if(c.isLabelable&&b.combineLabels){c.hideLabel=c.oldHideLabel}b.updateLabel()}},initRenderTpl:function(){var a=this;if(!a.hasOwnProperty("renderTpl")){a.renderTpl=a.getTpl("labelableRenderTpl")}return a.callParent()},initRenderData:function(){var a=this,b=a.callParent();b.containerElCls=a.containerElCls;return Ext.applyIf(b,a.getLabelableRenderData())},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(){var c=this,a=c.getTpl("fieldSubTpl"),b;if(!a.renderContent){c.setupRenderTpl(a)}b=a.apply(c.getSubTplData());return b},updateLabel:function(){var b=this,a=b.labelEl;if(a){b.setFieldLabel(b.getFieldLabel())}},onFieldErrorChange:function(e,b){if(this.combineErrors){var d=this,g=d.getActiveError(),c=Ext.Array.filter(d.query("[isFormField]"),function(h){return h.hasActiveError()}),a=d.getCombinedErrors(c);if(a){d.setActiveErrors(a)}else{d.unsetActiveError()}if(g!==d.getActiveError()){d.doComponentLayout()}}},getCombinedErrors:function(e){var l=[],c,m=e.length,j,d,k,b,g,h;for(c=0;c','
    ','','","","
    "],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;k.callParent(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 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}b.callParent()},initComponent:function(){var b=this,a=b.baseCls;b.initFieldAncestor();b.callParent();b.layout.managePadding=b.layout.manageOverflow=false;b.addEvents("beforeexpand","beforecollapse","expand","collapse");if(b.collapsed){b.addCls(a+"-collapsed");b.collapse()}if(b.title||b.checkboxToggle||b.collapsible){b.addTitleClasses();b.legend=Ext.widget(b.createLegendCt())}b.initMonitor()},initPadding:function(e){var c=this,a=c.getProtoBody(),d=c.padding,b;if(d!==undefined){if(Ext.isIEQuirks||Ext.isIE8m){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))}},getProtoBody:function(){var b=this,a=b.protoBody;if(!a){b.protoBody=a=new Ext.util.ProtoElement({styleProp:"bodyStyle",styleIsText:true})}return a},initRenderData:function(){var a=this,b=a.callParent();b.bodyTargetCls=a.bodyTargetCls;a.protoBody.writeTo(b);delete a.protoBody;return b},getState:function(){var a=this.callParent();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=[],b={xtype:"container",baseCls:c.baseCls+"-header",id:c.id+"-legend",autoEl:"legend",ariaRole:null,ariaLabelledBy:"."+c.baseCls+"-header-text",items:a,ownerCt:c,shrinkWrap:true,ownerLayout:c.componentLayout};if(c.checkboxToggle){a.push(c.createCheckboxCmp())}else{if(c.collapsible){a.push(c.createToggleCmp())}}a.push(c.createTitleCmp());return b},createTitleCmp:function(){var b=this,a={xtype:"component",html:b.title,cls:b.baseCls+"-header-text",id:b.id+"-legendTitle"};if(b.collapsible&&b.toggleOnTitleClick){a.listeners={click:{element:"el",scope:b,fn:b.toggle}};a.cls+=" "+b.baseCls+"-header-text-collapsible"}return(b.titleCmp=Ext.widget(a))},createCheckboxCmp:function(){var a=this,b="-checkbox";a.checkboxCmp=Ext.widget({xtype:"checkbox",hideEmptyLabel:true,name:a.checkboxName||a.id+b,cls:a.baseCls+"-header"+b,id:a.id+"-legendChk",checked:!a.collapsed,msgTarget:"none",listeners:{change:a.onCheckChange,scope:a}});return a.checkboxCmp},createToggleCmp:function(){var a=this;a.toggleCmp=Ext.widget({xtype:"tool",height:15,width:15,type:"toggle",handler:a.toggle,id:a.id+"-legendToggle",scope:a});return a.toggleCmp},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)}},finishRender:function(){var a=this.legend;this.callParent();if(a){a.finishRender()}},getCollapsed:function(){return this.collapsed?"top":false},getCollapsedDockedItems:function(){var a=this.legend;return a?[a]:[]},setTitle:function(d){var c=this,b=c.legend,a=c.baseCls;c.title=d;if(c.rendered){if(!b){c.legend=b=Ext.widget(c.createLegendCt());c.addTitleClasses();b.ownerLayout.configureItem(b);b.render(c.el,0)}c.titleCmp.update(d)}else{if(b){c.titleCmp.update(d)}else{c.addTitleClasses();c.legend=Ext.widget(c.createLegendCt())}}return c},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")}},applyTargetCls:function(a){this.bodyTargetCls=a},getTargetEl:function(){return this.body||this.frameBody||this.el},getDefaultContentTarget:function(){return this.body},expand:function(){return this.setExpanded(true)},collapse:function(){return this.setExpanded(false)},setExpanded:function(b){var c=this,d=c.checkboxCmp,a=b?"expand":"collapse";if(!c.rendered||c.fireEvent("before"+a,c)!==false){b=!!b;if(d){d.setValue(b)}if(b){c.removeCls(c.baseCls+"-collapsed")}else{c.addCls(c.baseCls+"-collapsed")}c.collapsed=!b;if(b){delete c.getHierarchyState().collapsed}else{c.getHierarchyState().collapsed=true}if(c.rendered){c.updateLayout({isRoot:false});c.fireEvent(a,c)}}return c},getRefItems:function(a){var c=this.callParent(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)},onCheckChange:function(b,a){this.setExpanded(a)},setupRenderTpl:function(a){this.callParent(arguments);a.renderLegend=this.doRenderLegend}},0,["fieldset"],["container","component","fieldset","box"],{container:true,component:true,fieldset:true,box:true},["widget.fieldset"],[["fieldAncestor",Ext.form.FieldAncestor]],[Ext.form,"FieldSet"],0));(Ext.cmd.derive("Ext.layout.component.BoundList",Ext.layout.component.Auto,{type:"component",beginLayout:function(d){var c=this,a=c.owner,b=a.pagingToolbar;c.callParent(arguments);if(a.floating){d.savedXY=a.getXY();a.setXY([0,-9999])}if(b){d.toolbarContext=d.context.getCmp(b)}d.listContext=d.getEl("listEl")},beginLayoutCycle:function(b){var a=this.owner;this.callParent(arguments);if(b.heightModel.auto){a.el.setHeight("auto");a.listEl.setHeight("auto")}},getLayoutItems:function(){var a=this.owner.pagingToolbar;return a?[a]:[]},isValidParent:function(){return true},finishedLayout:function(a){var b=a.savedXY;this.callParent(arguments);if(b){this.owner.setXY(b)}},measureContentWidth:function(a){return this.owner.listEl.getWidth()},measureContentHeight:function(a){return this.owner.listEl.getHeight()},publishInnerHeight:function(c,a){var b=c.toolbarContext,d=0;if(b){d=b.getProp("height")}if(d===undefined){this.done=false}else{c.listContext.setHeight(a-c.getFrameInfo().height-d)}},calculateOwnerHeightFromContentHeight:function(c){var a=this.callParent(arguments),b=c.toolbarContext;if(b){a+=b.getProp("height")}return a}},0,0,0,0,["layout.boundlist"],0,[Ext.layout.component,"BoundList"],0));(Ext.cmd.derive("Ext.form.field.Spinner",Ext.form.field.Trigger,{alternateClassName:"Ext.form.Spinner",trigger1Cls:Ext.baseCSSPrefix+"form-spinner-up",trigger2Cls:Ext.baseCSSPrefix+"form-spinner-down",spinUpEnabled:true,spinDownEnabled:true,keyNavEnabled:true,mouseWheelEnabled:true,repeatTriggerClick:true,onSpinUp:Ext.emptyFn,onSpinDown:Ext.emptyFn,ariaRole:"spinbutton",triggerTpl:'',initComponent:function(){this.callParent();this.addEvents("spin","spinup","spindown")},onRender:function(){var b=this,a;b.callParent(arguments);a=b.triggerEl;b.spinUpEl=a.item(0);b.spinDownEl=a.item(1);b.triggerCell=b.spinUpEl.parent();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)}},getSubTplMarkup:function(b){var c=this,a=b.childElCls,d=Ext.form.field.Base.prototype.getSubTplMarkup.apply(c,arguments);return'"+c.getTriggerMarkup()+""},getTriggerMarkup:function(){return this.getTpl("triggerTpl").apply(this.getTriggerData())},getTriggerData:function(){var a=this,b=(a.readOnly||a.hideTrigger);return{triggerCls:Ext.baseCSSPrefix+"trigger-cell",triggerStyle:b?"display:none":"",spinnerUpCls:!a.spinUpEnabled?a.trigger1Cls+"-disabled":"",spinnerDownCls:!a.spinDownEnabled?a.trigger2Cls+"-disabled":""}},getTriggerWidth:function(){var b=this,a=0;if(b.triggerWrap&&!b.hideTrigger&&!b.readOnly){a=b.triggerWidth}return a},onTrigger1Click:function(){this.spinUp()},onTrigger2Click:function(){this.spinDown()},onTriggerWrapMouseup:function(){this.inputEl.focus()},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.spinUpEl[a?"removeCls":"addCls"](b.trigger1Cls+"-disabled")}},setSpinDownEnabled:function(a){var b=this,c=b.spinDownEnabled;b.spinDownEnabled=a;if(c!==a&&b.rendered){b.spinDownEl[a?"removeCls":"addCls"](b.trigger2Cls+"-disabled")}},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","spinUpEl","spinDownEl");this.callParent()}},0,["spinnerfield"],["spinnerfield","component","field","triggerfield","textfield","box","trigger"],{spinnerfield:true,component:true,field:true,triggerfield:true,textfield:true,box:true,trigger: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}a.callParent();a.setMinValue(a.minValue);a.setMaxValue(a.maxValue)},getErrors:function(c){var b=this,e=b.callParent(arguments),d=Ext.String.format,a;c=Ext.isDefined(c)?c:this.processRawValue(this.getRawValue());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=a.callParent();if(!a.submitLocaleSeparator){b=b.replace(a.decimalSeparator,".")}return b},onChange:function(){this.toggleSpinners();this.callParent(arguments)},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(b){var a=this,c;a.minValue=Ext.Number.from(b,Number.NEGATIVE_INFINITY);a.toggleSpinners();if(a.disableKeyFilter!==true){c=a.baseChars+"";if(a.allowExponential){c+=a.decimalSeparator+"e+-"}else{if(a.allowDecimals){c+=a.decimalSeparator}if(a.minValue<0){c+="-"}}c=Ext.String.escapeRegex(c);a.maskRe=new RegExp("["+c+"]");if(a.autoStripChars){a.stripCharsRe=new RegExp("[^"+c+"]","gi")}}},setMaxValue:function(a){this.maxValue=Ext.Number.from(a,Number.MAX_VALUE);this.toggleSpinners()},parseValue:function(a){a=parseFloat(String(a).replace(this.decimalSeparator,"."));return isNaN(a)?null:a},fixPrecision:function(d){var c=this,b=isNaN(d),a=c.decimalPrecision;if(b||!d){return b?"":d}else{if(!c.allowDecimals||a<=0){a=0}}return parseFloat(Ext.Number.toFixed(parseFloat(d),a))},beforeBlur:function(){var b=this,a=b.rawToValue(b.getRawValue());if(!Ext.isEmpty(a)){b.setValue(a)}},setSpinUpEnabled:function(b,a){this.callParent(arguments);if(!a){delete this.spinUpDisabledByToggle}else{this.spinUpDisabledByToggle=!b}},onSpinUp:function(){var a=this;if(!a.readOnly){a.setSpinValue(Ext.Number.constrain(a.getValue()+a.step,a.minValue,a.maxValue))}},setSpinDownEnabled:function(b,a){this.callParent(arguments);if(!a){delete this.spinDownDisabledByToggle}else{this.spinDownDisabledByToggle=!b}},onSpinDown:function(){var a=this;if(!a.readOnly){a.setSpinValue(Ext.Number.constrain(a.getValue()-a.step,a.minValue,a.maxValue))}},setSpinValue:function(c){var b=this,a;if(b.enforceMaxLength){if(b.fixPrecision(c).toString().length>b.maxLength){return}}b.setValue(c)}},0,["numberfield"],["spinnerfield","component","field","triggerfield","textfield","numberfield","box","trigger"],{spinnerfield:true,component:true,field:true,triggerfield:true,textfield:true,numberfield:true,box:true,trigger: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,getPagingItems:function(){var b=this,a={scope:b,blur:b.onPagingBlur};a[Ext.EventManager.getKeyEvent()]=b.onPagingKeyDown;return[{itemId:"first",tooltip:b.firstText,overflowText:b.firstText,iconCls:Ext.baseCSSPrefix+"tbar-page-first",disabled:true,handler:b.moveFirst,scope:b},{itemId:"prev",tooltip:b.prevText,overflowText:b.prevText,iconCls:Ext.baseCSSPrefix+"tbar-page-prev",disabled:true,handler:b.movePrevious,scope:b},"-",b.beforePageText,{xtype:"numberfield",itemId:"inputItem",name:"inputItem",cls:Ext.baseCSSPrefix+"tbar-page-number",allowDecimals:false,minValue:1,hideTrigger:true,enableKeyEvents:true,keyNavEnabled:false,selectOnFocus:true,submitValue:false,isFormField:false,width:b.inputItemWidth,margins:"-1 2 3 2",listeners:a},{xtype:"tbtext",itemId:"afterTextItem",text:Ext.String.format(b.afterPageText,1)},"-",{itemId:"next",tooltip:b.nextText,overflowText:b.nextText,iconCls:Ext.baseCSSPrefix+"tbar-page-next",disabled:true,handler:b.moveNext,scope:b},{itemId:"last",tooltip:b.lastText,overflowText:b.lastText,iconCls:Ext.baseCSSPrefix+"tbar-page-last",disabled:true,handler:b.moveLast,scope:b},"-",{itemId:"refresh",tooltip:b.refreshText,overflowText:b.refreshText,iconCls:Ext.baseCSSPrefix+"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"})}b.callParent();b.addEvents("change","beforechange")},beforeRender:function(){var a=this;a.callParent(arguments);if(!a.store.isLoading()){a.calledFromRender=true;a.onLoad();delete a.calledFromRender}},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,j,e;g=h.store.getCount();j=g===0;if(!j){d=h.getPageData();b=d.currentPage;c=d.pageCount;if(b>c){h.store.loadPage(c);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.setText(a)}e=h.getInputItem();if(e){e.setDisabled(j).setValue(b)}h.setChildDisabled("#first",b===1||j);h.setChildDisabled("#prev",b===1||j);h.setChildDisabled("#next",b===c||j);h.setChildDisabled("#last",b===c||j);h.setChildDisabled("#refresh",false);h.updateInfo();Ext.resumeLayouts(true);if(!h.calledFromRender){h.fireEvent("change",h,d)}},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(j,h){var d=this,b=h.getKey(),c=d.getPageData(),a=h.shiftKey?10:1,g;if(b==h.RETURN){h.stopEvent();g=d.readPageFromInput(c);if(g!==false){g=Math.min(Math.max(1,g),c.pageCount);if(g!==c.currentPage&&d.fireEvent("beforechange",d,g)!==false){d.store.loadPage(g)}}}else{if(b==h.HOME||b==h.END){h.stopEvent();g=b==h.HOME?1:c.pageCount;j.setValue(g)}else{if(b==h.UP||b==h.PAGE_UP||b==h.DOWN||b==h.PAGE_DOWN){h.stopEvent();g=d.readPageFromInput(c);if(g){if(b==h.DOWN||b==h.PAGE_DOWN){a*=-1}g+=a;if(g>=1&&g<=c.pageCount){j.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}},unbind:function(a){this.bindStore(null)},bind:function(a){this.bindStore(a)},onDestroy:function(){this.unbind();this.callParent()}},0,["pagingtoolbar"],["container","toolbar","component","box","pagingtoolbar"],{container:true,toolbar:true,component:true,box:true,pagingtoolbar:true},["widget.pagingtoolbar"],[["bindable",Ext.util.Bindable]],[Ext.toolbar,"Paging",Ext,"PagingToolbar"],0));(Ext.cmd.derive("Ext.view.BoundList",Ext.view.View,{alternateClassName:"Ext.BoundList",pageSize:0,baseCls:Ext.baseCSSPrefix+"boundlist",itemCls:Ext.baseCSSPrefix+"boundlist-item",listItemCls:"",shadow:false,trackOver:true,refreshed:0,preserveScrollOnRefresh:true,deferInitialRefresh:false,componentLayout:"boundlist",childEls:["listEl"],renderTpl:['',"{%","var me=values.$comp, pagingToolbar=me.pagingToolbar;","if (pagingToolbar) {","pagingToolbar.ownerLayout = me.componentLayout;","Ext.DomHelper.generateMarkup(pagingToolbar.getRenderTree(), out);","}","%}",{disableFormats:true}],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;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()}b.callParent()},getRefOwner:function(){return this.pickerField||this.callParent()},getRefItems:function(){var b=this,a=[];if(b.pagingToolbar){a.push(b.pagingToolbar)}if(b.loadMask){a.push(b.loadMask)}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()})},finishRenderChildren:function(){var a=this.pagingToolbar;this.callParent(arguments);if(a){a.finishRender()}},refresh:function(){var c=this,a=c.tpl,b=c.pagingToolbar,d=c.rendered;a.field=c.pickerField;a.store=c.store;c.callParent();a.field=a.store=null;if(d&&b&&b.rendered&&!c.preserveScrollOnRefresh){c.el.appendChild(b.el)}if(d&&Ext.isIE6&&Ext.isStrict){c.listEl.repaint()}},bindStore:function(a,b){var c=this.pagingToolbar;this.callParent(arguments);if(c){c.bindStore(a,b)}},getTargetEl:function(){return this.listEl||this.el},getNodeContainer:function(){return Ext.get(this.listEl.dom.firstChild)},getInnerTpl:function(a){return"{"+a+"}"},onDestroy:function(){Ext.destroyMembers(this,"pagingToolbar","listEl");this.callParent()}},0,["boundlist"],["component","box","boundlist","dataview"],{component:true,box:true,boundlist:true,dataview:true},["widget.boundlist"],[["queryable",Ext.Queryable]],[Ext.view,"BoundList",Ext,"BoundList"],0));(Ext.cmd.derive("Ext.ux.form.MultiSelect",Ext.form.FieldContainer,{alternateClassName:"Ext.ux.Multiselect",layout:"anchor",ddReorder:false,appendOnly:false,displayField:"text",allowBlank:true,minSelections:0,maxSelections:Number.MAX_VALUE,blankText:"This field is required",minSelectionsText:"Minimum {0} item(s) required",maxSelectionsText:"Maximum {0} item(s) required",delimiter:",",dragText:"{0} Item{1}",ignoreSelectChange:0,initComponent:function(){var a=this;a.bindStore(a.store,true);if(a.store.autoCreated){a.valueField=a.displayField="field1";if(!a.store.expanded){a.displayField="field2"}}if(!Ext.isDefined(a.valueField)){a.valueField=a.displayField}a.items=a.setupItems();a.callParent();a.initField();a.addEvents("drop")},setupItems:function(){var a=this;a.boundList=Ext.create("Ext.view.BoundList",Ext.apply({anchor:"none 100%",deferInitialRefresh:false,border:1,multiSelect:true,store:a.store,displayField:a.displayField,disabled:a.disabled},a.listConfig));a.boundList.getSelectionModel().on("selectionchange",a.onSelectChange,a);if(!a.title){return a.boundList}a.boundList.border=false;return{border:true,anchor:"none 100%",layout:"anchor",title:a.title,tbar:a.tbar,items:a.boundList}},onSelectChange:function(a,b){if(!this.ignoreSelectChange){this.setValue(b)}},getSelected:function(){return this.boundList.getSelectionModel().getSelection()},isEqual:function(e,d){var b=Ext.Array.from,c=0,a;e=b(e);d=b(d);a=e.length;if(a!==d.length){return false}for(;ca.maxSelections){e.push(c(a.maxSelectionsText,a.maxSelections))}return e},onDestroy:function(){var a=this;a.bindStore(null);Ext.destroy(a.dragZone,a.dropZone);a.callParent()},onBindStore:function(a){var b=this.boundList;if(b){b.bindStore(a)}}},0,["multiselectfield","multiselect"],["container","multiselectfield","multiselect","component","fieldcontainer","box"],{container:true,multiselectfield:true,multiselect:true,component:true,fieldcontainer:true,box:true},["widget.multiselect","widget.multiselectfield"],[["bindable",Ext.util.Bindable],["field",Ext.form.field.Field]],[Ext.ux.form,"MultiSelect",Ext.ux,"Multiselect"],0));(Ext.cmd.derive("Ext.ux.form.ItemSelector",Ext.ux.form.MultiSelect,{alternateClassName:["Ext.ux.ItemSelector"],hideNavIcons:false,buttons:["top","up","add","remove","down","bottom"],buttonsText:{top:"Move to Top",up:"Move Up",add:"Add to Selected",remove:"Remove from Selected",down:"Move Down",bottom:"Move to Bottom"},layout:{type:"hbox",align:"stretch"},initComponent:function(){var a=this;a.ddGroup=a.id+"-dd";a.callParent();a.bindStore(a.store)},createList:function(b){var a=this;return Ext.create("Ext.ux.form.MultiSelect",{submitValue:false,getSubmitData:function(){return null},getModelData:function(){return null},flex:1,dragGroup:a.ddGroup,dropGroup:a.ddGroup,title:b,store:{model:a.store.model,data:[]},displayField:a.displayField,valueField:a.valueField,disabled:a.disabled,listeners:{boundList:{scope:a,itemdblclick:a.onItemDblClick,drop:a.syncValue}}})},setupItems:function(){var a=this;a.fromField=a.createList(a.fromTitle);a.toField=a.createList(a.toTitle);return[a.fromField,{xtype:"container",margins:"0 4",layout:{type:"vbox",pack:"center"},items:a.createButtons()},a.toField]},createButtons:function(){var b=this,a=[];if(!b.hideNavIcons){Ext.Array.forEach(b.buttons,function(c){a.push({xtype:"button",tooltip:b.buttonsText[c],handler:b["on"+Ext.String.capitalize(c)+"BtnClick"],cls:Ext.baseCSSPrefix+"form-itemselector-btn",iconCls:Ext.baseCSSPrefix+"form-itemselector-"+c,navBtn:true,scope:b,margin:"4 0 0 0"})})}return a},getSelections:function(b){var a=b.getStore();return Ext.Array.sort(b.getSelectionModel().getSelection(),function(d,c){d=a.indexOf(d);c=a.indexOf(c);if(dc){return 1}}return 0})},onTopBtnClick:function(){var c=this.toField.boundList,a=c.getStore(),b=this.getSelections(c);a.suspendEvents();a.remove(b,true);a.insert(0,b);a.resumeEvents();c.refresh();this.syncValue();c.getSelectionModel().select(b)},onBottomBtnClick:function(){var c=this.toField.boundList,a=c.getStore(),b=this.getSelections(c);a.suspendEvents();a.remove(b,true);a.add(b);a.resumeEvents();c.refresh();this.syncValue();c.getSelectionModel().select(b)},onUpBtnClick:function(){var g=this.toField.boundList,b=g.getStore(),e=this.getSelections(g),h,d=0,a=e.length,c=0;b.suspendEvents();for(;d-1;--c,b--){g=d[c];b=Math.min(b,a.indexOf(g)+1);a.remove(g,true);a.insert(b,g)}a.resumeEvents();e.refresh();this.syncValue();e.getSelectionModel().select(d)},onAddBtnClick:function(){var b=this,a=b.getSelections(b.fromField.boundList);b.moveRec(true,a);b.toField.boundList.getSelectionModel().select(a)},onRemoveBtnClick:function(){var b=this,a=b.getSelections(b.toField.boundList);b.moveRec(false,a);b.fromField.boundList.getSelectionModel().select(a)},moveRec:function(g,e){var c=this,h=c.fromField,a=c.toField,b=g?h.store:a.store,d=g?a.store:h.store;b.suspendEvents();d.suspendEvents();b.remove(e);d.add(e);b.resumeEvents();d.resumeEvents();h.boundList.refresh();a.boundList.refresh();c.syncValue()},syncValue:function(){var a=this;a.mixins.field.setValue.call(a,a.setupValue(a.toField.store.getRange()))},onItemDblClick:function(a,b){this.moveRec(a===this.fromField.boundList,b)},setValue:function(g){var d=this,h=d.fromField,a=d.toField,b=h.store,e=a.store,c;if(!d.fromStorePopulated){d.fromField.store.on({load:Ext.Function.bind(d.setValue,d,[g]),single:true});return}g=d.setupValue(g);d.mixins.field.setValue.call(d,g);c=d.getRecordsForValue(g);b.suspendEvents();e.suspendEvents();b.removeAll();e.removeAll();d.populateFromStore(d.store);Ext.Array.forEach(c,function(j){if(b.indexOf(j)>-1){b.remove(j)}e.add(j)});b.resumeEvents();e.resumeEvents();Ext.suspendLayouts();h.boundList.refresh();a.boundList.refresh();Ext.resumeLayouts(true)},onBindStore:function(a,b){var c=this;if(c.fromField){c.fromField.store.removeAll();c.toField.store.removeAll();if(a.getCount()){c.populateFromStore(a)}else{c.store.on("load",c.populateFromStore,c)}}},populateFromStore:function(a){var b=this.fromField.store;this.fromStorePopulated=true;b.add(a.getRange());b.fireEvent("load",b)},onEnable:function(){var a=this;a.callParent();a.fromField.enable();a.toField.enable();Ext.Array.forEach(a.query("[navBtn]"),function(b){b.enable()})},onDisable:function(){var a=this;a.callParent();a.fromField.disable();a.toField.disable();Ext.Array.forEach(a.query("[navBtn]"),function(b){b.disable()})},onDestroy:function(){this.bindStore(null);this.callParent()}},0,["itemselector","itemselectorfield"],["container","multiselectfield","multiselect","component","itemselector","fieldcontainer","box","itemselectorfield"],{container:true,multiselectfield:true,multiselect:true,component:true,itemselector:true,fieldcontainer:true,box:true,itemselectorfield:true},["widget.itemselector","widget.itemselectorfield"],0,[Ext.ux.form,"ItemSelector",Ext.ux,"ItemSelector"],0));Ext.define("ExtThemeNeptune.toolbar.Paging",{override:"Ext.toolbar.Paging",defaultButtonUI:"plain-toolbar",inputItemWidth:40});(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(a.callParent(),{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",ariaRole:"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();a.callParent();a.relayEvents(a.form,["beforeaction","actionfailed","actioncomplete","validitychange","dirtychange"]);if(a.pollForChanges){a.startPolling(a.pollInterval||500)}},initItems:function(){this.callParent();this.initMonitor();this.form=this.createForm()},afterFirstLayout:function(){this.callParent(arguments);this.form.initialize()},createForm:function(){var b={},d=this.basicFormConfigs,a=d.length,c=0,e;for(;c0?c-1:d.getCount()-1;e.highlightAt(a)},down:function(){var e=this,b=e.boundList,d=b.all,g=b.highlightedItem,c=g?b.indexOf(g):-1,a=cc){c=g;k=m}}a=Math.max(h.callParent(arguments),b.inputEl.getTextWidth(k+b.growAppend));if(!h.startingWidth||b.removingRecords){h.startingWidth=a;if(a',' value="{[Ext.util.Format.htmlEncode(values.value)]}"
    ',' name="{name}"',' placeholder="{placeholder}"',' size="{size}"',' maxlength="{maxLength}"',' readonly="readonly"',' disabled="disabled"',' tabIndex="{tabIdx}"',' style="{fieldStyle}"',"/>",{compiled:true,disableFormats:true}],getSubTplData:function(){var a=this;Ext.applyIf(a.subTplData,{hiddenDataCls:a.hiddenDataCls});return a.callParent(arguments)},afterRender:function(){var a=this;a.callParent(arguments);a.setHiddenValue(a.value)},multiSelect:false,delimiter:", ",displayField:"text",triggerAction:"all",allQuery:"",queryParam:"query",queryMode:"remote",queryCaching:true,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,ignoreSelection:0,removingRecords:null,resizeComboToGrow:function(){var a=this;return a.grow&&a.growToLongestValue},initComponent:function(){var e=this,c=Ext.isDefined,b=e.store,d=e.transform,h=e.displayTpl,a,g;Ext.applyIf(e.renderSelectors,{hiddenDataEl:"."+e.hiddenDataCls.split(" ").join(".")});e.addEvents("beforequery","select","beforeselect","beforedeselect");if(d){a=Ext.getDom(d);if(a){if(!e.store){b=Ext.Array.map(Ext.Array.from(a.options),function(j){return[j.value,j.text]})}if(!e.name){e.name=a.name}if(!("value" in e)){e.value=a.value}}}e.bindStore(b||"ext-empty-store",true,true);b=e.store;if(b.autoCreated){e.queryMode="local";e.valueField=e.displayField="field1";if(!b.expanded){e.displayField="field2"}}if(!c(e.valueField)){e.valueField=e.displayField}g=e.queryMode==="local";if(!c(e.queryDelay)){e.queryDelay=g?10:500}if(!c(e.minChars)){e.minChars=g?0:4}if(!h){e.displayTpl=new Ext.XTemplate('{[typeof values === "string" ? values : values["'+e.displayField+'"]]}'+e.delimiter+"")}else{if(!h.isTemplate){e.displayTpl=new Ext.XTemplate(h)}}e.callParent();e.doQueryTask=new Ext.util.DelayedTask(e.doRawQuery,e);if(e.store.getCount()>0){e.setValue(e.value)}if(a){if(e.transformInPlace){e.render(a.parentNode,a);delete e.renderTo}Ext.removeNode(a)}},getStore:function(){return this.store},beforeBlur:function(){var b=this,a=b.queryFilter;b.doQueryTask.cancel();b.assertValue();if(a&&!a.disabled&&b.queryMode==="local"&&b.clearFilterOnBlur){a.disabled=true;b.store.filter()}},onFocus:function(){var b=this,a=b.queryFilter;b.callParent(arguments);if(!b.duringTriggerClick&&b.triggerAction!=="all"&&a&&a.disabled&&b.queryMode==="local"&&b.clearFilterOnBlur){delete b.lastQuery;b.doRawQuery()}},assertValue:function(){var b=this,c=b.getRawValue(),d,a;if(b.forceSelection){if(b.multiSelect){if(c!==b.getDisplayValue()){b.setValue(b.lastSelection)}}else{d=b.findRecordByDisplay(c);if(d){a=b.value;if(!b.findRecordByValue(a)){b.select(d,true)}}else{b.setValue(b.lastSelection)}}}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;this.callParent();if(a&&!a.disabled){a.disabled=true;this.store.filter()}},onUnbindStore:function(a){var d=this,b=d.picker,c=d.queryFilter;if(c){d.store.removeFilter(c)}if(b){b.bindStore(null)}},onBindStore:function(a,c){var b=this.picker;if(!c){this.resetToDefault()}if(b){b.bindStore(a)}},bindStore:function(a,e,b){var d=this,c=d.queryFilter;d.mixins.bindable.bindStore.call(d,a,b);a=d.getStore();if(a&&c){c.disabled=!!e;a.addFilter(c)}},getStoreListeners:function(){var a=this;return{beforeload:a.onBeforeLoad,clear:a.onClear,datachanged:a.onDataChanged,load:a.onLoad,exception:a.onException,remove:a.onRemove}},onBeforeLoad:function(){++this.ignoreSelection},onDataChanged:function(){var a=this;if(a.resizeComboToGrow()){a.updateLayout()}},onClear:function(){var a=this;if(a.resizeComboToGrow()){a.removingRecords=true;a.onDataChanged()}},onRemove:function(){var a=this;if(a.resizeComboToGrow()){a.removingRecords=true}},onException:function(){if(this.ignoreSelection>0){--this.ignoreSelection}this.collapse()},onLoad:function(b,a,d){var c=this;if(c.ignoreSelection>0){--c.ignoreSelection}if(d&&!b.lastOptions.rawQuery){if(c.value==null){if(c.store.getCount()){c.doAutoSelect()}else{c.setValue(c.value)}}else{c.setValue(c.value)}}},doRawQuery:function(){this.doQuery(this.getRawValue(),false,true)},doQuery:function(e,b,d){var c=this,a=c.beforeQuery({query:e||"",rawQuery:d,forceAll:b,combo:c,cancel:false});if(a===false||a.cancel){return false}if(c.queryCaching&&a.query===c.lastQuery){c.expand();if(c.queryMode==="local"){c.doAutoSelect()}}else{c.lastQuery=a.query;if(c.queryMode==="local"){c.doLocalQuery(a)}else{c.doRemoteQuery(a)}}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){c=a.getSelectionModel().lastSelected;d=a.getNode(c||0);if(d){a.highlightItem(d);a.listEl.scrollChildIntoView(d,false)}}},doTypeAhead:function(){var a=this;if(!a.typeAheadTask){a.typeAheadTask=new Ext.util.DelayedTask(a.onTypeAhead,a)}if(a.lastKey!=Ext.EventObject.BACKSPACE&&a.lastKey!=Ext.EventObject.DELETE){a.typeAheadTask.delay(a.typeAheadDelay)}},onTriggerClick:function(){var a=this;a.duringTriggerClick=true;if(!a.readOnly&&!a.disabled){if(a.isExpanded){a.collapse()}else{a.onFocus({});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)}}}a.inputEl.focus()}delete a.duringTriggerClick},onPaste:function(){var a=this;if(!a.readOnly&&!a.disabled&&a.editable){a.doQueryTask.delay(a.queryDelay)}},onKeyUp:function(d,b){var c=this,a=d.getKey();if(!c.readOnly&&!c.disabled&&c.editable){c.lastKey=a;if(!d.isSpecialKey()||a==d.BACKSPACE||a==d.DELETE){c.doQueryTask.delay(c.queryDelay)}}if(c.enableKeyEvents){c.callParent(arguments)}},initEvents:function(){var a=this;a.callParent();if(!a.enableKeyEvents){a.mon(a.inputEl,"keyup",a.onKeyUp,a)}a.mon(a.inputEl,"paste",a.onPaste,a)},onDestroy:function(){var a=this;if(a.typeAheadTask){a.typeAheadTask.cancel();a.typeAheadTask=null}Ext.destroy(a.listKeyNav);a.bindStore(null);a.callParent()},onAdded:function(){var a=this;a.callParent(arguments);if(a.picker){a.picker.ownerCt=a.up("[floating]");a.picker.registerWithOwnerCt()}},createPicker:function(){var c=this,b,a=Ext.apply({xtype:"boundlist",pickerField:c,selModel:{mode:c.multiSelect?"SIMPLE":"SINGLE"},floating:true,hidden:true,store:c.store,displayField:c.displayField,focusOnToFront:false,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)}c.mon(b,{itemclick:c.onItemClick,refresh:c.onListRefresh,scope:c});c.mon(b.getSelectionModel(),{beforeselect:c.onBeforeSelect,beforedeselect:c.onBeforeDeselect,selectionchange:c.onListSelectionChange,scope:c});return b},alignPicker:function(){var b=this,a=b.getPicker(),e=b.getPosition()[1]-Ext.getBody().getScroll().top,d=Ext.Element.getViewHeight()-e-b.getHeight(),c=Math.max(e,d);if(a.height){delete a.height;a.updateLayout()}if(a.getHeight()>c-5){a.setHeight(c-5)}b.callParent()},onListRefresh:function(){if(!this.expanding){this.alignPicker()}this.syncSelection()},onItemClick:function(c,a){var e=this,d=e.picker.getSelectionModel().getSelection(),b=e.valueField;if(!e.multiSelect&&d.length){if(a.get(b)===d[0].get(b)){e.displayTplData=[a.data];e.setRawValue(e.getDisplayValue());e.collapse()}}},onBeforeSelect:function(b,a){return this.fireEvent("beforeselect",this,a,a.index)},onBeforeDeselect:function(b,a){return this.fireEvent("beforedeselect",this,a,a.index)},onListSelectionChange:function(b,d){var a=this,e=a.multiSelect,c=d.length>0;if(!a.ignoreSelection&&a.isExpanded){if(!e){Ext.defer(a.collapse,1,a)}if(e||c){a.setValue(d,false)}if(c){a.fireEvent("select",a,d)}a.inputEl.focus()}},onExpand:function(){var d=this,a=d.listKeyNav,c=d.selectOnTab,b=d.getPicker();if(a){a.enable()}else{a=d.listKeyNav=new Ext.view.BoundListKeyNav(d.inputEl,{boundList:b,forceKeyDown:true,tab:function(g){if(c){this.selectHighlighted(g);d.triggerBlur()}return true},enter:function(j){var g=b.getSelectionModel(),h=g.getCount();this.selectHighlighted(j);if(!d.multiSelect&&h===g.getCount()){d.collapse()}}})}if(c){d.ignoreMonitorTab=true}Ext.defer(a.enable,1,a);d.inputEl.focus()},onCollapse:function(){var b=this,a=b.listKeyNav;if(a){a.disable();b.ignoreMonitorTab=false}},select:function(d,a){var c=this,b=c.picker,e;if(d&&d.isModel&&a===true&&b){e=!b.getSelectionModel().isSelected(d)}c.setValue(d,true);if(e){c.fireEvent("select",c,d)}},findRecord:function(d,c){var b=this.store,a=b.findExact(d,c);return a!==-1?b.getAt(a):false},findRecordByValue:function(a){return this.findRecord(this.valueField,a)},findRecordByDisplay:function(a){return this.findRecord(this.displayField,a)},setValue:function(m,e){var k=this,c=k.valueNotFoundText,n=k.inputEl,g,j,h,a,l=[],b=[],d=[];if(k.store.loading){k.value=m;k.setHiddenValue(k.value);return k}m=Ext.Array.from(m);for(g=0,j=m.length;g0){e.hiddenDataEl.update(Ext.DomHelper.markup({tag:"input",type:"hidden",name:a}));c=1;h=b.firstChild}while(c>g){b.removeChild(k[0]);--c}while(c=0){g.push(j)}}h.ignoreSelection++;c=d.getSelectionModel();c.deselectAll();if(g.length){c.select(g,undefined,true)}h.ignoreSelection--}},onEditorTab:function(b){var a=this.listKeyNav;if(this.selectOnTab&&a){a.selectHighlighted(b)}}},0,["combo","combobox"],["component","pickerfield","field","triggerfield","textfield","combo","box","trigger","combobox"],{component:true,pickerfield:true,field:true,triggerfield:true,textfield:true,combo:true,box:true,trigger:true,combobox:true},["widget.combo","widget.combobox"],[["bindable",Ext.util.Bindable]],[Ext.form.field,"ComboBox",Ext.form,"ComboBox"],0));(Ext.cmd.derive("Ext.picker.Month",Ext.Component,{alternateClassName:"Ext.MonthPicker",childEls:["bodyEl","prevEl","nextEl","monthEl","yearEl"],renderTpl:['
    ','
    ','','
    ','{.}',"
    ","
    ","
    ",'
    ','
    ','
    ','',"
    ",'
    ','',"
    ","
    ",'','
    ','{.}',"
    ","
    ","
    ",'
    ','','
    {%',"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:Ext.baseCSSPrefix+"monthpicker",showButtons:true,measureWidth:35,measureMaxHeight:20,smallCls:Ext.baseCSSPrefix+"monthpicker-small",totalYears:10,yearOffset:5,monthOffset:6,initComponent:function(){var a=this;a.selectedCls=a.baseCls+"-selected";a.addEvents("cancelclick","monthclick","monthdblclick","okclick","select","yearclick","yeardblclick");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({text:a.okText,handler:a.onOkClick,scope:a});a.cancelBtn=new Ext.button.Button({text:a.cancelText,handler:a.onCancelClick,scope:a})}this.callParent()},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()}g.callParent();for(;cd.measureMaxHeight){--c;a.setStyle("margin","0 "+c+"px")}return c},getLargest:function(a){var b=0;this.months.each(function(d){var c=d.getHeight();if(c>b){b=c}});return b},setValue:function(d){var c=this,e=c.activeYear,g=c.monthOffset,b,a;if(!d){c.value=[null,null]}else{if(Ext.isDate(d)){c.value=[d.getMonth(),d.getFullYear()]}else{c.value=[d[0],d[1]]}}if(c.rendered){b=c.value[1];if(b!==null){if((be+c.yearOffset)){c.activeYear=b-c.yearOffset+1}}c.updateBody()}return c},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.isEndOfWeek}",'","","","
    ','
    {.:this.firstInitial}
    ',"
    ','',"
    ",'','',"","",{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)}}],todayText:"Today",ariaTitle:"Date Picker: {0}",ariaTitleDateFormat:"F d, Y",todayTip:"{0} (Spacebar)",minText:"This date is before the minimum date",maxText:"This date is after the maximum date",disabledDaysText:"Disabled",disabledDatesText:"Disabled",nextText:"Next Month (Control+Right)",prevText:"Previous Month (Control+Left)",monthYearText:"Choose a month (Control+Up/Down to move years)",monthYearFormat:"F Y",startDay:0,showToday:true,disableAnim:false,baseCls:Ext.baseCSSPrefix+"datepicker",longDayFormat:"F d, Y",focusOnShow:false,focusOnSelect:true,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));b.callParent();b.value=b.value?a(b.value,true):a(new Date());b.addEvents("select");b.initDisabledDays()},getRefOwner:function(){return this.pickerField||this.callParent()},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 b=this,c=new Array(b.numDays),a=Ext.Date.format(new Date(),b.format);if(b.padding&&!b.width){b.cacheWidth()}b.monthBtn=new Ext.button.Split({ownerCt:b,ownerLayout:b.getComponentLayout(),text:"",tooltip:b.monthYearText,listeners:{click:b.doShowMonthPicker,arrowclick:b.doShowMonthPicker,scope:b}});if(b.showToday){b.todayBtn=new Ext.button.Button({ownerCt:b,ownerLayout:b.getComponentLayout(),text:Ext.String.format(b.todayText,a),tooltip:Ext.String.format(b.todayTip,a),tooltipType:"title",handler:b.selectToday,scope:b})}b.callParent();Ext.applyIf(b,{renderData:{}});Ext.apply(b.renderData,{dayNames:b.dayNames,showToday:b.showToday,prevText:b.prevText,nextText:b.nextText,days:c});b.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.remove()},finishRenderChildren:function(){var a=this;a.callParent();a.monthBtn.finishRender();if(a.showToday){a.todayBtn.finishRender()}},onRender:function(b,a){var c=this;c.callParent(arguments);c.cells=c.eventEl.select("tbody td");c.textNodes=c.eventEl.query("tbody td a");c.mon(c.eventEl,{scope:c,mousewheel:c.handleMouseWheel,click:{fn:c.handleDateClick,delegate:"a."+c.baseCls+"-date"}})},initEvents:function(){var c=this,a=Ext.Date,b=a.DAY;c.callParent();c.prevRepeater=new Ext.util.ClickRepeater(c.prevEl,{handler:c.showPrevMonth,scope:c,preventDefault:true,stopDefault:true});c.nextRepeater=new Ext.util.ClickRepeater(c.nextEl,{handler:c.showNextMonth,scope:c,preventDefault:true,stopDefault:true});c.keyNav=new Ext.util.KeyNav(c.eventEl,Ext.apply({scope:c,left:function(d){if(d.ctrlKey){c.showPrevMonth()}else{c.update(a.add(c.activeDate,b,-1))}},right:function(d){if(d.ctrlKey){c.showNextMonth()}else{c.update(a.add(c.activeDate,b,1))}},up:function(d){if(d.ctrlKey){c.showNextYear()}else{c.update(a.add(c.activeDate,b,-7))}},down:function(d){if(d.ctrlKey){c.showPrevYear()}else{c.update(a.add(c.activeDate,b,7))}},pageUp:function(d){if(d.altKey){c.showPrevYear()}else{c.showPrevMonth()}},pageDown:function(d){if(d.altKey){c.showNextYear()}else{c.showNextMonth()}},tab:function(d){c.doCancelFieldFocus=true;c.handleTabClick(d);delete c.doCancelFieldFocus;return true},enter:function(d){d.stopPropagation();return true},home:function(d){c.update(a.getFirstDateOfMonth(c.activeDate))},end:function(d){c.update(a.getLastDateOfMonth(c.activeDate))}},c.keyNavConfig));if(c.showToday){c.todayKeyListener=c.eventEl.addKeyListener(Ext.EventObject.SPACE,c.selectToday,c)}c.update(c.value)},handleTabClick: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)){if(c.pickerField){d.stopEvent()}c.doCancelFocus=true;c.setValue(new Date(a.dateValue));delete c.doCancelFocus;c.fireEvent("select",c,c.value);if(b){b.call(c.scope||c,c,c.value)}c.onSelect()}},getSelectedDate:function(a){var d=this,j=a.getTime(),k=d.cells,l=d.selectedCls,g=k.elements,b,e=g.length,h;k.removeCls(l);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.doCancelFocus=c.focusOnSelect===false;c.setValue(new Date(a.dateValue));delete c.doCancelFocus;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(a){var d=this,j=a.getTime(),k=d.cells,l=d.selectedCls,g=k.elements,b,e=g.length,h;k.removeCls(l);for(b=0;bv||(C&&x&&C.test(o.dateFormat(F,x)))||(H&&H.indexOf(F.getDay())!=-1));if(!E.disabled){E.todayBtn.setDisabled(a);E.todayKeyListener.setDisabled(a)}}n=function(I,J){s=+o.clearTime(r,true);I.title=o.format(r,b);I.firstChild.dateValue=s;if(s==z){J+=" "+E.todayCls;I.title=E.todayText;E.todayElSpan=Ext.DomHelper.append(I.firstChild,{tag:"span",cls:Ext.baseCSSPrefix+"hide-clip",html:E.todayText},true)}if(s==m){J+=" "+E.selectedCls;E.fireEvent("highlightitem",E,I);if(e&&E.floating){Ext.fly(I.firstChild).focus(50)}}if(sv){J+=" "+G;I.title=E.maxText}else{if(H&&H.indexOf(r.getDay())!==-1){I.title=B;J+=" "+G}else{if(C&&x){j=o.dateFormat(r,x);if(C.test(j)){I.title=t.replace("%0",j);J+=" "+G}}}}}I.className=J+" "+E.cellCls};for(;w=l){p=(++D);c=E.nextCls}else{p=w-h+1;c=E.activeCls}}d[w].innerHTML=p;r.setDate(r.getDate()+1);n(g[w],c)}E.monthBtn.setText(Ext.Date.format(A,E.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.todayKeyListener,a.keyNav,a.monthPicker,a.monthBtn,a.nextRepeater,a.prevRepeater,a.todayBtn);delete a.textNodes;delete a.cells.elements}a.callParent()},onShow:function(){this.callParent(arguments);if(this.focusOnShow){this.focus()}}},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",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",disabledDatesText:"Disabled",minText:"The date in this field must be equal to or after {0}",maxText:"The date in this field must be equal to or before {0}",invalidText:"{0} is not a valid date - it must be in the format {1}",triggerCls:Ext.baseCSSPrefix+"form-date-trigger",showToday:true,useStrict:undefined,initTime:"12",initTimeFormat:"H",matchFieldWidth:false,startDay:0,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();d.callParent()},initValue:function(){var a=this,b=a.value;if(Ext.isString(b)){a.value=a.rawToValue(b)}a.callParent()},initDisabledDays:function(){if(this.disabledDates){var b=this.disabledDates,a=b.length-1,g="(?:",h,e=b.length,c;for(h=0;hk(h).getTime()){o.push(p(j.maxText,j.formatDate(h)))}if(n){l=q.getDay();for(;e {splitCls}
    ','{childElCls}" unselectable="on">','','',"{text}","",'background-image:url({iconUrl});','font-family:{glyphFontFamily};">','&#{glyph}; ',"","","",''],getTemplateArgs:function(){var a=this.callParent();a.inputCls=this.inputCls;a.inputName=this.inputName;a.tabIndex=this.ownerCt.tabIndex;return a},afterRender:function(){var a=this;a.callParent(arguments);a.fileInputEl.on("change",a.fireChange,a)},fireChange:function(a){this.fireEvent("change",this,a,this.fileInputEl.dom.value)},createFileInput:function(a){var b=this;b.fileInputEl=b.el.createChild({name:b.inputName,id:!a?b.id+"-fileInputEl":undefined,cls:b.inputCls,tag:"input",type:"file",size:1,role:"button"});b.fileInputEl.on("change",b.fireChange,b)},reset:function(a){if(a){this.fileInputEl.remove()}this.createFileInput(!a)},restoreInput:function(a){this.fileInputEl.remove();a=Ext.get(a);this.el.appendChild(a);this.fileInputEl=a},onDisable:function(){this.callParent();this.fileInputEl.dom.disabled=true},onEnable:function(){this.callParent();this.fileInputEl.dom.disabled=false}},0,["filebutton"],["button","component","filebutton","box"],{button:true,component:true,filebutton:true,box:true},["widget.filebutton"],0,[Ext.form.field,"FileButton"],0));(Ext.cmd.derive("Ext.form.field.File",Ext.form.field.Trigger,{alternateClassName:["Ext.form.FileUploadField","Ext.ux.form.FileUploadField","Ext.form.File"],buttonText:"Browse...",buttonOnly:false,buttonMargin:3,clearOnSubmit:true,extraFieldBodyCls:Ext.baseCSSPrefix+"form-file-wrap",readOnly:true,submitValue:false,triggerNoEditCls:"",componentLayout:"triggerfield",childEls:["browseButtonWrap"],onRender:function(){var a=this,c=a.id,b;a.callParent(arguments);b=a.inputEl;b.dom.name="";a.button=new Ext.form.field.FileButton(Ext.apply({renderTo:c+"-browseButtonWrap",ownerCt:a,ownerLayout:a.componentLayout,id:c+"-button",ui:a.ui,disabled:a.disabled,text:a.buttonText,style:a.buttonOnly?"":a.getButtonMarginProp()+a.buttonMargin+"px",inputName:a.getName(),listeners:{scope:a,change:a.onFileChange}},a.buttonConfig));a.fileInputEl=a.button.fileInputEl;if(a.buttonOnly){a.inputCell.setDisplayed(false);a.shrinkWrap=3}a.browseButtonWrap.dom.style.width=(a.browseButtonWrap.dom.lastChild.offsetWidth+a.button.getEl().getMargin("lr"))+"px";if(Ext.isIE){a.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},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)}}b.callParent()},onShow:function(){this.callParent();this.button.updateLayout()},onDisable:function(){this.callParent();this.button.disable()},onEnable:function(){this.callParent();this.button.enable()},isFileUpload:function(){return true},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=Ext.baseCSSPrefix+"hide-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(){Ext.destroyMembers(this,"button");delete this.fileInputEl;this.callParent()},getButtonMarginProp:function(){return"margin-left:"}},0,["fileuploadfield","filefield"],["component","field","fileuploadfield","triggerfield","filefield","textfield","box","trigger"],{component:true,field:true,fileuploadfield:true,triggerfield:true,filefield:true,textfield:true,box:true,trigger: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",inputType:"hidden",hideLabel:true,hidden:true,ariaRole:"presentation",initComponent:function(){this.formItemCls+="-hidden";this.callParent()},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"],["hiddenfield","component","field","hidden","box"],{hiddenfield:true,component:true,field:true,hidden:true,box:true},["widget.hidden","widget.hiddenfield"],0,[Ext.form.field,"Hidden",Ext.form,"Hidden"],0));(Ext.cmd.derive("Ext.picker.Color",Ext.Component,{alternateClassName:"Ext.ColorPalette",componentCls:Ext.baseCSSPrefix+"color-picker",selectedCls:Ext.baseCSSPrefix+"color-picker-selected",itemCls:Ext.baseCSSPrefix+"color-picker-item",value:null,clickEvent:"click",allowReselect:false,colors:["000000","993300","333300","003300","003366","000080","333399","333333","800000","FF6600","808000","008000","008080","0000FF","666699","808080","FF0000","FF9900","99CC00","339966","33CCCC","3366FF","800080","969696","FF00FF","FFCC00","FFFF00","00FF00","00FFFF","00CCFF","993366","C0C0C0","FF99CC","FFCC99","FFFF99","CCFFCC","CCFFFF","99CCFF","CC99FF","FFFFFF"],colorRe:/(?:^|\s)color-(.{6})(?:\s|$)/,renderTpl:['','',' ',"",""],initComponent:function(){var a=this;a.callParent(arguments);a.addEvents("select");if(a.handler){a.on("select",a.handler,a.scope,true)}},initRenderData:function(){var a=this;return Ext.apply(a.callParent(),{itemCls:a.itemCls,colors:a.colors})},onRender:function(){var b=this,a=b.clickEvent;b.callParent(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;a.callParent(arguments);if(a.value){b=a.value;a.value=null;a.select(b,true)}},handleClick:function(c,d){var b=this,a;c.stopEvent();if(!b.disabled){a=d.className.match(b.colorRe)[1];b.select(a.toUpperCase())}},select:function(b,a){var d=this,g=d.selectedCls,e=d.value,c;b=b.replace("#","");if(!d.rendered){d.value=b;return}if(b!=e||d.allowReselect){c=d.el;if(d.value){c.down("a.color-"+e).removeCls(g)}c.down("a.color-"+b).addCls(g);d.value=b;if(a!==true){d.fireEvent("select",d,b)}}},clear:function(){var b=this,c=b.value,a;if(c&&b.rendered){a=b.el.down("a.color-"+c);a.removeCls(b.selectedCls)}b.value=null},getValue:function(){return this.value||null}},0,["colorpicker"],["component","colorpicker","box"],{component:true,colorpicker:true,box: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=""}this.callParent(arguments);b.toolbarContext=b.context.getCmp(a.toolbar);b.inputCmpContext=b.context.getCmp(a.inputCmp);b.textAreaContext=b.getEl("textareaEl");b.iframeContext=b.getEl("iframeEl")},beginLayoutCycle:function(h){var g=this,c=h.widthModel,b=h.heightModel,a=g.owner,e=a.iframeEl,d=a.textareaEl;g.callParent(arguments);if(c.shrinkWrap){e.setStyle("width","");d.setStyle("width","")}else{if(c.natural){h.bodyCellContext.setWidth(g.naturalWidth)}}if(b.natural||b.shrinkWrap){e.setHeight(g.naturalHeight);d.setHeight(g.naturalHeight)}},finishedLayout:function(){var a=this.owner;this.callParent(arguments);if(Ext.isIE9m&&Ext.isIEQuirks){a.el.repaint()}if(Ext.isGecko){a.textareaEl.dom.value=this.lastValue}},publishOwnerWidth:function(b,a){this.callParent(arguments);a-=b.inputCmpContext.getBorderInfo().width;b.textAreaContext.setWidth(a);b.iframeContext.setWidth(a)},publishInnerWidth:function(e,c){var b=e.inputCmpContext.getBorderInfo().width,d=Ext.isStrict&&Ext.isIE8m,a=e.widthModel.natural;this.callParent(arguments);c=e.bodyCellContext.props.width-b;if(a){if(d){c-=2}e.textAreaContext.setWidth(c);e.iframeContext.setWidth(c)}else{if(d){e.textAreaContext.setWidth(c)}}},publishInnerHeight:function(c,a){var d=c.toolbarContext.getProp("height"),b=this.owner.sourceEditMode;this.callParent(arguments);a=c.bodyCellContext.props.height;if(d!==undefined){a-=d+c.inputCmpContext.getFrameInfo().height;if(Ext.isIE8&&Ext.isStrict){a-=2}else{if(Ext.isIEQuirks&&(Ext.isIE8||Ext.isIE9)){a-=4}}c.iframeContext.setHeight(a);c.textAreaContext.setHeight(a)}else{this.done=false}}},0,0,0,0,["layout.htmleditor"],0,[Ext.layout.component.field,"HtmlEditor"],0));(Ext.cmd.derive("Ext.form.field.HtmlEditor",Ext.form.FieldContainer,{alternateClassName:"Ext.form.HtmlEditor",componentLayout:"htmleditor",componentTpl:["{beforeTextAreaTpl}",'","{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||Ext.isIE6)?" ":"​",extraFieldBodyCls:Ext.baseCSSPrefix+"html-editor-wrap",initialized:false,activated:false,sourceEditMode:false,iframePad:3,hideMode:"offsets",maskOnDisable:true,containerElCls:Ext.baseCSSPrefix+"html-editor-container",reStripQuotes:/^['"]*|['"]*$/g,initComponent:function(){var a=this;a.addEvents("initialize","activate","beforesync","beforepush","sync","push","editmodechange");a.items=[a.createToolbar(),a.createInputCmp()];a.layout={type:"vbox",align:"stretch"};a.callParent(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:Ext.baseCSSPrefix+"hidden",value:a.value,iframeName:Ext.id(),iframeSrc:Ext.SSL_SECURE_URL,iframeCls:Ext.baseCSSPrefix+"htmleditor-iframe"};a.getInsertionRenderData(b,a.subTplInsertions);return{flex:1,xtype:"component",tpl:a.getTpl("componentTpl"),childEls:["iframeEl","textareaEl"],id:c,cls:Ext.baseCSSPrefix+"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=Ext.baseCSSPrefix,j,g;function c(m,k,l){return{itemId:m,cls:d+"btn-icon",iconCls:d+"edit-"+m,enableToggle:k!==false,scope:h,handler:l||h.relayBtnCmd,clickEvent:"mousedown",tooltip:a?h.buttonTips[m]||g:g,overflowText:h.buttonTips[m].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 k=this.selectEl;if(k){k.dom.disabled=true}Ext.Component.prototype.onDisable.apply(this,arguments)},onEnable:function(){var k=this.selectEl;if(k){k.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(l,k){h.relayCmd("forecolor",Ext.isWebKit||Ext.isIE?"#"+k:k);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(l,k){if(Ext.isGecko){h.execCmd("useCSS",false);h.execCmd("hilitecolor","#"+k);h.execCmd("useCSS",true);h.deferFocus()}else{h.relayCmd(Ext.isOpera?"hilitecolor":"backcolor",Ext.isWebKit||Ext.isIE||Ext.isOpera?"#"+k:k)}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(k){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)}}},deferFocus:function(){this.focus(false,true)},getFocusEl:function(){var a=this,b=a.win;return b&&!a.sourceEditMode?b:a.textareaEl},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(){try{var g=this,d=g.getEditorBody(),b=g.textareaEl.getStyles("font-size","font-family","background-image","background-repeat","background-color","color"),j,c;b["background-attachment"]="fixed";d.bgProperties="fixed";Ext.DomHelper.applyStyles(d,b);j=g.getDoc();if(j){try{Ext.EventManager.removeAll(j)}catch(h){}}c=Ext.Function.bind(g.onEditorEvent,g);Ext.EventManager.on(j,{mousedown:c,dblclick:c,click:c,keyup:c,buffer:100});c=g.onRelayedEvent;Ext.EventManager.on(j,{mousedown:c,mousemove:c,mouseup:c,click:c,dblclick:c,scope:g});if(Ext.isGecko){Ext.EventManager.on(j,"keypress",g.applyCommand,g)}if(g.fixKeys){Ext.EventManager.on(j,"keydown",g.fixKeys,g)}if(g.fixKeysAfter){Ext.EventManager.on(j,"keyup",g.fixKeysAfter,g)}if(Ext.isIE9&&Ext.isStrict){Ext.EventManager.on(j.documentElement,"focus",g.focus,g)}if(Ext.isIE8m||(Ext.isIE9&&!Ext.isStrict)){Ext.EventManager.on(j,"focusout",function(){g.savedSelection=j.selection.type!=="None"?j.selection.createRange():null},g);Ext.EventManager.on(j,"focusin",function(){if(g.savedSelection){g.savedSelection.select()}},g)}Ext.EventManager.onWindowUnload(g.beforeDestroy,g);j.editorInitialized=true;g.initialized=true;g.pushValue();g.setReadOnly(g.readOnly);g.fireEvent("initialize",g)}catch(a){}},beforeDestroy:function(){var a=this,d=a.monitorTask,c,g;if(d){Ext.TaskManager.stop(d)}if(a.rendered){Ext.EventManager.removeUnloadListener(a.beforeDestroy,a);try{c=a.getDoc();if(c){Ext.EventManager.removeAll(Ext.fly(c));for(g in c){if(c.hasOwnProperty&&c.hasOwnProperty(g)){delete c[g]}}}}catch(b){}a.iframeEl.remove();delete a.iframeEl;delete a.textareaEl;delete a.toolbar;delete a.inputCmp}a.callParent()},onRelayedEvent:function(c){var b=this.iframeEl,d=Ext.Element.getTrueXY(b),e=c.getXY(),a=Ext.EventManager.getPageXY(c.browserEvent);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(a){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 l;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(c){var b=this,a;if(b.activated){b.win.focus();if(Ext.isIE){a=b.getDoc().selection.createRange();if(a){a.pasteHTML(c);b.syncValue();b.deferFocus()}}else{b.execCmd("InsertHTML",c);b.deferFocus()}}},fixKeys:(function(){var a;if(Ext.isIE){return function(j){var d=this,c=j.getKey(),h=d.getDoc(),l=d.readOnly,b,g;if(c===j.TAB){j.stopEvent();if(!l){b=h.selection.createRange();if(b){if(b.collapse){b.collapse(true);b.pasteHTML("    ")}d.deferFocus()}}}else{if(c===j.ENTER){if(!l){if(Ext.isIE10m){b=h.selection.createRange();if(b){g=b.parentElement();if(!g||g.tagName.toLowerCase()!=="li"){j.stopEvent();b.pasteHTML("
    ");b.collapse(false);b.select()}}}else{b=h.getSelection().getRangeAt(0);if(b&&b.commonAncestorContainer.parentNode.tagName.toLowerCase()!=="li"){j.stopEvent();a=h.createElement("div");b.insertNode(a)}}}}}}}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: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"}}},0,["htmleditor"],["container","component","fieldcontainer","box","htmleditor"],{container:true,component:true,fieldcontainer:true,box:true,htmleditor:true},["widget.htmleditor"],[["field",Ext.form.field.Field]],[Ext.form.field,"HtmlEditor",Ext.form,"HtmlEditor"],0));Ext.define("ExtThemeNeptune.form.field.HtmlEditor",{override:"Ext.form.field.HtmlEditor",defaultButtonUI:"plain-toolbar"});(Ext.cmd.derive("Ext.picker.Time",Ext.view.BoundList,{increment:15,format:"g:i A",displayField:"disp",initDate:[2008,0,1],componentCls:Ext.baseCSSPrefix+"timepicker",loadMask:false,initComponent:function(){var c=this,a=Ext.Date,b=a.clearTime,d=c.initDate;c.absMin=b(new Date(d[0],d[1],d[2]));c.absMax=a.add(b(new Date(d[0],d[1],d[2])),"mi",(24*60)-1);c.store=c.createStore();c.store.addFilter(c.rangeFilter=new Ext.util.Filter({id:"time-picker-filter"}),false);c.updateList();c.callParent()},setMinValue:function(a){this.minValue=a;this.updateList()},setMaxValue:function(a){this.maxValue=a;this.updateList()},normalizeDate:function(a){var b=this.initDate;a.setFullYear(b[0],b[1],b[2]);return a},updateList:function(){var c=this,b=c.normalizeDate(c.minValue||c.absMin),a=c.normalizeDate(c.maxValue||c.absMax);c.rangeFilter.setFilterFn(function(d){var e=d.get("date");return e>=b&&e<=a});c.store.filter()},createStore:function(){var d=this,c=Ext.Date,e=[],b=d.absMin,a=d.absMax;while(b<=a){e.push({disp:c.dateFormat(b,d.format),date:b});b=c.add(b,"mi",d.increment)}return new Ext.data.Store({model:d.modelType,autoDestroy:true,data:e})},focusNode:function(a){return false}},0,["timepicker"],["component","timepicker","box","boundlist","dataview"],{component:true,timepicker:true,box:true,boundlist:true,dataview: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:Ext.baseCSSPrefix+"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",increment:15,pickerMaxHeight:300,selectOnTab:true,snapToIncrement:false,initDate:"1/1/2008",initDateParts:[2008,0,1],initDateFormat:"j/n/Y",ignoreSelection:0,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.store=c.getPicker().store;c.displayTpl=new Ext.XTemplate('{[typeof values === "string" ? values : this.formatDate(values["'+c.displayField+'"])]}'+c.delimiter+"",{formatDate:Ext.Function.bind(c.formatDate,c)});c.callParent()},transformOriginalValue:function(a){if(Ext.isDefined(a)){return this.rawToValue(a)||a||null}return a},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){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;c0){c=c[0];if(c&&Ext.Date.isEqual(a.get("date"),c.get("date"))){d.collapse()}}}},syncSelection:function(){var n=this,k=n.picker,m=Ext.Date.isEqual,c=[],h,o,p,e,j,q,b,l,g,a;if(k){k.clearHighlight();o=n.getValue();h=k.getSelectionModel();n.ignoreSelection++;if(o===null){h.deselectAll()}else{p=Ext.Array.from(o);b=k.store.data.items;g=b.length;for(e=0,j=p.length;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},getHeaderById:function(e){var c=this.getColumns(),a=c.length,b,d;for(b=0;ba;g=c?1:0;d=e.getGridColumns();for(h=0,l=d.length;h0){a=Math.max(a+e.getHeight()+e.floatingButtons.getHeight()-d.clientHeight-b.getBorderWidth("b"),0)}}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-1}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}d=0}else{if(b._buttonsOnTop!==false){a.setButtonPosition("bottom");b._buttonsOnTop=false}}return d},syncEditorClip:function(){var b=this,c=b.getScrollDelta(),a;if(c){b.isOverflowing=true;a=b.floatingButtons.getHeight();if(c>0){b.clipBottom(Math.max(b.getHeight()-c+a,-a))}else{if(c<0){c=Math.abs(c);b.clipTop(Math.max(c,0))}}}else{if(b.isOverflowing){b.clearClip();b.isOverflowing=false}}},focusContextCell:function(){var a=this.context.column,b;if(!a.isDestroyed){b=this.getEditor(a);if(b&&b.focus){b.focus()}}},cancelEdit:function(){var g=this,e=g.getForm(),a=e.getFields(),b=a.items,d=b.length,c;g.hide();e.clearInvalid();for(c=0;cg&&a[isFormField]"),c=a.length,b;for(b=0;b'+e.join("")+""},createErrorListItem:function(a){return'
  • '+a+"
  • "},beforeDestroy:function(){Ext.destroy(this.floatingButtons,this.tooltip);this.callParent()},clipBottom:function(a){this.el.setStyle("clip","rect(-1000px auto "+a+"px auto)")},clipTop:function(a){this.el.setStyle("clip","rect("+a+"px auto 1000px auto)")},clearClip:function(a){this.el.setStyle("clip",Ext.isIE8m||Ext.isIEQuirks?"rect(-1000px auto 1000px auto)":"auto")}},0,["roweditor"],["container","component","form","box","roweditor","panel"],{container:true,component:true,form:true,box:true,roweditor:true,panel:true},["widget.roweditor"],0,[Ext.grid,"RowEditor"],0));Ext.define("ExtThemeNeptune.grid.RowEditor",{override:"Ext.grid.RowEditor",buttonUI:"default-toolbar"});(Ext.cmd.derive("Ext.view.DropZone",Ext.dd.DropZone,{indicatorCls:Ext.baseCSSPrefix+"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}b.callParent([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.getPageY();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,true),h=e?a.getRecord(e):null;return h&&Ext.Array.contains(d,h)},positionIndicator:function(b,c,d){var g=this,j=g.view,h=g.getPosition(d,b),l=j.getRecord(b),a=c.records,k;if(!Ext.Array.contains(a,l)&&(h=="before"&&!g.containsRecordAtOffset(a,l,-1)||h=="after"&&!g.containsRecordAtOffset(a,l,1))){g.valid=true;if(g.overRecord!=l||g.currentPosition!=h){k=Ext.fly(b).getY()-j.el.getY()-1;if(h=="after"){k+=Ext.fly(b).getHeight()}g.getIndicator().setWidth(Ext.fly(j.el).getWidth()).showAt(0,k);g.overRecord=l;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;b.callParent(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(j,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",j,g,d.overRecord,d.currentPosition)},cancelDrop:function(){d.invalidateDrop();c=true}},k=false;if(d.valid){k=d.fireViewEvent("beforedrop",j,g,d.overRecord,d.currentPosition,b);if(b.wait){return}if(k!==false){if(!c){b.processDrop()}}}return k},destroy:function(){Ext.destroy(this.indicator);delete this.indicator;this.callParent()}},1,0,0,0,0,0,[Ext.view,"DropZone"],0));(Ext.cmd.derive("Ext.grid.ViewDropZone",Ext.view.DropZone,{indicatorHtml:'',indicatorCls:Ext.baseCSSPrefix+"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;cActions",sortable:false,innerCls:Ext.baseCSSPrefix+"grid-cell-inner-action-col",actionIconCls:Ext.baseCSSPrefix+"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;g.callParent([b]);g.items=c;for(e=0,a=c.length;e"}return j},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("."+Ext.baseCSSPrefix+"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("."+Ext.baseCSSPrefix+"action-col-"+b).addCls(c.disabledCls);if(!a){c.fireEvent("disable",c)}},destroy:function(){delete this.items;delete this.renderer;return this.callParent(arguments)},processEvent:function(k,n,p,b,l,h,d,r){var j=this,g=h.getTarget(),c,q,m,o=(k==="keydown"&&h.getKey()),a;if(k==="mousedown"){return false}if(o&&!Ext.fly(g).findParent(n.getCellSelector())){g=Ext.fly(p).down("."+Ext.baseCSSPrefix+"action-col-icon",true)}if(g&&(c=g.className.match(j.actionIdRe))){q=j.items[parseInt(c[1],10)];a=q.disabled||(q.isDisabled?q.isDisabled.call(q.scope||j.origScope||j,n,b,l,q,d):false);if(q&&!a){if(k=="click"||(o==h.ENTER||o==h.SPACE)){m=q.handler||j.handler;if(m){m.call(q.scope||j.origScope||j,n,b,l,q,h,d,r)}if(q.stopSelection!==false){return false}}}}return j.callParent(arguments)},cascade:function(b,a){b.call(a||this,this)},getRefItems:function(){return[]}},1,["actioncolumn"],["container","component","gridcolumn","actioncolumn","headercontainer","box"],{container:true,component:true,gridcolumn:true,actioncolumn:true,headercontainer:true,box: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:" ",defaultRenderer:function(a){if(a===undefined){return this.undefinedText}if(!a||a==="false"){return this.falseText}return this.trueText}},0,["booleancolumn"],["container","component","gridcolumn","headercontainer","box","booleancolumn"],{container:true,component:true,gridcolumn:true,headercontainer:true,box: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",stopSelection:true,tdCls:Ext.baseCSSPrefix+"grid-cell-checkcolumn",innerCls:Ext.baseCSSPrefix+"grid-cell-inner-checkcolumn",clickTargetName:"el",constructor:function(){this.addEvents("beforecheckchange","checkchange");this.scope=this;this.callParent(arguments)},processEvent:function(h,k,o,b,j,d,c,p){var g=this,n=h==="keydown"&&d.getKey(),a=h=="mousedown";if(!g.disabled&&(a||(n==d.ENTER||n==d.SPACE))){var l=g.dataIndex,m=!c.get(l);if(g.fireEvent("beforecheckchange",g,b,m)!==false){c.set(l,m);g.fireEvent("checkchange",g,b,m);if(a){d.stopEvent()}if(!g.stopSelection){k.selModel.selectByPosition({row:b,column:j})}return false}else{return !g.stopSelection}}else{return g.callParent(arguments)}},onEnable:function(){this.callParent(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)}},renderer:function(b,c){var d=Ext.baseCSSPrefix,a=d+"grid-checkcolumn";if(this.disabled){c.tdCls+=" "+this.disabledCls}if(b){a+=" "+d+"grid-checkcolumn-checked"}return''}},1,["checkcolumn"],["container","component","gridcolumn","headercontainer","box","checkcolumn"],{container:true,component:true,gridcolumn:true,headercontainer:true,box: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",initComponent:function(){if(!this.format){this.format=Ext.Date.defaultFormat}this.callParent(arguments)},defaultRenderer:function(a){return Ext.util.Format.date(a,this.format)}},0,["datecolumn"],["container","component","gridcolumn","headercontainer","box","datecolumn"],{container:true,component:true,gridcolumn:true,headercontainer:true,box: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",format:"0,000.00",defaultRenderer:function(a){return Ext.util.Format.number(a,this.format)}},0,["numbercolumn"],["container","numbercolumn","component","gridcolumn","headercontainer","box"],{container:true,numbercolumn:true,component:true,gridcolumn:true,headercontainer:true,box: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",text:" ",width:23,sortable:false,draggable:false,autoLock:true,lockable:false,align:"right",constructor:function(a){var b=this;b.width=b.width;b.callParent(arguments);b.scope=b},beforeRender:function(){var a=this.up("tablepanel").view.findFeature("rowbody");this.callParent(arguments);if(a&&this.ownerCt.items.indexOf(this)===1){a.colSpanDecrement=a.colSpanDecrement+1;this.rowspan=2}},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,renderer:function(k,b,g,c,e,a,j){var d=this.rowspan,h=a.currentPage,l=j.store.indexOf(g);if(d){b.tdAttr='rowspan="'+d+'"'}if(h>1){l+=(h-1)*a.pageSize}return l+1}},1,["rownumberer"],["container","component","gridcolumn","headercontainer","box","rownumberer"],{container:true,component:true,gridcolumn:true,headercontainer:true,box:true,rownumberer:true},["widget.rownumberer"],0,[Ext.grid.column,"RowNumberer",Ext.grid,"RowNumberer"],0));Ext.define("ExtThemeNeptune.grid.column.RowNumberer",{override:"Ext.grid.column.RowNumberer",width:25});(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;a.callParent(arguments)},defaultRenderer:function(c,d,a){var b=Ext.apply({},a.data,a.getAssociatedData());return this.tpl.apply(b)}},0,["templatecolumn"],["container","component","gridcolumn","templatecolumn","headercontainer","box"],{container:true,component:true,gridcolumn:true,templatecolumn:true,headercontainer:true,box:true},["widget.templatecolumn"],0,[Ext.grid.column,"Template",Ext.grid,"TemplateColumn"],0));(Ext.cmd.derive("Ext.grid.feature.AbstractSummary",Ext.grid.feature.Feature,{summaryRowCls:Ext.baseCSSPrefix+"grid-row-summary",summaryTableCls:Ext.plainTableCls+" "+Ext.baseCSSPrefix+"grid-table",summaryRowSelector:"."+Ext.baseCSSPrefix+"grid-row-summary",summaryRowTpl:{before:function(a,b){if(a.record.isSummary&&this.summaryFeature.showSummaryRow){this.summaryFeature.outputSummaryRecord(a.record,a,b);return false}},priority:1000},showSummaryRow:true,init:function(){var a=this;a.view.summaryFeature=a;a.rowTpl=a.view.self.prototype.rowTpl;a.view.addRowTpl(a.summaryRowTpl).summaryFeature=a;a.summaryData={}},toggleSummaryRow:function(a){this.showSummaryRow=!!a},createRenderer:function(e,b){var g=this,c=b.ownerGroup,a=c?g.summaryData[c]:g.summaryData,d=e.dataIndex||e.id;return function(){return e.summaryRenderer?e.summaryRenderer(b.get(d),a,d):b.get(d)}},outputSummaryRecord:function(g,l,d){var h=l.view,a=h.rowValues,c=l.columns||h.headerCt.getVisibleGridColumns(),k=c.length,e,b,j={view:h,record:g,rowStyle:"",rowClasses:[this.summaryRowCls],itemClasses:[],recordIndex:-1,rowId:h.getRowId(g),columns:c};for(e=0;e',' ',Ext.baseCSSPrefix,'grid-group-row" {ariaRowAttr}>','','',"{%",'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.groupInfo, parent) || " "]}',"
    ","
    ","
    ",'',' ',Ext.baseCSSPrefix,'grid-table-summary"','border="0" cellspacing="0" cellpadding="0" style="width:100%" {ariaSummaryTableAttr}>',"{[values.view.renderColumnSizer(out)]}",'',"{%","values.itemClasses.length = 0;","this.nextTpl.applyOut(values, out, parent);","%}","",'',"{%me.outputSummaryRecord(values.summaryRecord, values, out);%}","","
    ","
    ","","","","{%this.nextTpl.applyOut(values, out, parent);%}","",{priority:200,syncRowHeights:function(d,k){d=Ext.fly(d,"syncDest");k=Ext.fly(k,"sycSrc");var b=this.owner,e=d.down(b.eventSelector,true),g,h=d.down(b.summaryRowSelector,true),c,a,j;if(e&&(g=k.down(b.eventSelector,true))){e.style.height=g.style.height="";if((a=e.offsetHeight)>(j=g.offsetHeight)){Ext.fly(g).setHeight(a)}else{if(j>a){Ext.fly(e).setHeight(j)}}}if(h&&(c=k.down(b.summaryRowSelector,true))){h.style.height=c.style.height="";if((a=h.offsetHeight)>(j=c.offsetHeight)){Ext.fly(c).setHeight(a)}else{if(j>a){Ext.fly(h).setHeight(j)}}}},syncContent:function(b,h){b=Ext.fly(b,"syncDest");h=Ext.fly(h,"sycSrc");var a=this.owner,d=b.down(a.eventSelector,true),c=h.down(a.eventSelector,true),g=b.down(a.summaryRowSelector,true),e=h.down(a.summaryRowSelector,true);if(d&&c){Ext.fly(d).syncContent(c)}if(g&&e){Ext.fly(g).syncContent(e)}}}],constructor:function(){this.groupCache={};this.callParent(arguments)},init:function(c){var d=this,a=d.view,b=a.store;if(b.isGrouped()){a.isGrouping=true}if(d.lockingPartner&&d.lockingPartner.groupCache){d.groupCache=d.lockingPartner.groupCache}d.mixins.summary.init.call(d);d.callParent(arguments);a.headerCt.on({columnhide:d.onColumnHideShow,columnshow:d.onColumnHideShow,columnmove:d.onColumnMove,scope:d});a.addTableTpl(d.tableTpl).groupingFeature=d;a.addRowTpl(Ext.XTemplate.getTpl(d,"groupTpl")).groupingFeature=d;a.preserveScrollOnRefresh=true;if(b.buffered){d.collapsible=false}else{if(this.lockingPartner&&this.lockingPartner.dataSource){d.dataSource=a.dataSource=this.lockingPartner.dataSource}else{d.dataSource=a.dataSource=new Ext.grid.feature.GroupStore(d,b)}}d.grid.on({reconfigure:d.onReconfigure});a.on({afterrender:d.afterViewRender,scope:d,single:true})},indexOf:function(a){return this.dataSource.indexOf(a)},isInCollapsedGroup:function(a){var c,b=this.view.store;if(b.isGrouped()&&(c=this.getGroup(a))){return c.isCollapsed||false}return false},clearGroupCache:function(){var b=this,a=b.groupCache={};if(b.lockingPartner){b.lockingPartner.groupCache=a}return a},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=a.store,d;a.isGrouping=true;if(c.lastGroupers){c.block();b.group(c.lastGroupers);c.lastGroupers=null;c.unblock()}c.callParent();d=c.view.headerCt.getMenu().down("#groupToggleMenuItem");if(d){d.setChecked(true,true)}c.refreshIf()},disable:function(){var c=this,a=c.view,b=a.store,d,e=b.groupers.getRange();a.isGrouping=false;if(e.length){c.lastGroupers=e;c.block();b.clearGrouping();c.unblock()}c.callParent();d=c.view.headerCt.getMenu().down("#groupToggleMenuItem");if(d){d.setChecked(false,true)}c.refreshIf()},refreshIf:function(){var b=this.grid.ownerCt,a=this.view;if(!a.store.remoteGroup&&!this.blockRefresh){if(b&&b.lockable){b.view.refresh()}else{a.refresh()}}},afterViewRender:function(){var b=this,a=b.view;a.on({scope:b,groupclick:b.onGroupClick});if(b.enableGroupingMenu){b.injectGroupingMenu()}b.pruneGroupedHeader();b.lastGroupers=b.view.store.groupers.getRange();b.block();b.onGroupChange();b.unblock();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,g){var l=this.view,b=l.headerCt,a=b.getMenu(),c=a.activeHeader,m=a.down("#groupMenuItem"),h,n=this.grid.getVisibleColumnManager().getColumns().length,k,j,e;if(c&&m){h=c.groupable===false||c.dataIndex==null||this.view.headerCt.getVisibleGridColumns().length<2?"disable":"enable";m[h]()}if(l.rendered){k=l.el.query("."+this.ctCls);for(e=0,j=k.length;e','','',"
    ",""],style:"overflow:hidden",itemId:"summaryBar",cls:[c.dockedSummaryCls,c.dockedSummaryCls+"-"+c.dock],xtype:"component",dock:c.dock,weight:10000000})[0]},afterrender:function(){b.body.addCls(c.panelBodyCls+c.dock);a.mon(a.el,{scroll:c.onViewScroll,scope:c});c.onStoreUpdate()},single:true});b.headerCt.afterComponentLayout=Ext.Function.createSequence(b.headerCt.afterComponentLayout,function(){var e=this.getTableWidth(),g=c.summaryBar.innerCt,d;if(a.hasVerticalScroll()){d=Ext.getScrollbarSize().width;e-=d;g.down("table").setStyle(c.scrollPadProperty,d+"px")}g.setWidth(e)})}else{c.view.addFooterFn(c.renderTFoot)}b.on({columnmove:c.onStoreUpdate,scope:c});a.mon(a.store,{update:c.onStoreUpdate,datachanged:c.onStoreUpdate,scope:c})},renderTFoot:function(b,c){var a=b.view,d=a.findFeature("summary");if(d.showSummaryRow){c.push("");d.outputSummaryRecord(d.createSummaryRecord(a),b,c);c.push("")}},toggleSummaryRow:function(c){var b=this,a=b.summaryBar;b.callParent(arguments);if(a){a.setVisible(b.showSummaryRow);b.onViewScroll()}},vetoEvent:function(a,c,d,b){return !b.getTarget(this.summaryRowSelector)},onViewScroll:function(){this.summaryBar.el.dom.scrollLeft=this.view.el.dom.scrollLeft},createSummaryRecord:function(h){var d=h.headerCt.getVisibleGridColumns(),b={records:h.store.getRange()},k=d.length,e,c,g=this.summaryRecord||(this.summaryRecord=new h.store.model(null,h.id+"-summary-record")),j,a;g.beginEdit();for(e=0;e=c){d=Ext.apply({},d);d.column-=c;return this.normalView.getCellByPosition(d,b)}else{return this.lockedView.getCellByPosition(d,b)}},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)},addElListener:function(a,c,b){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 b=this,a=b.loadMask;this.isDestroyed=true;b.clearListeners();if(a&&a.bindStore){a.bindStore(null)}}},1,0,0,0,0,[["observable",Ext.util.Observable],["bindable",Ext.util.Bindable]],[Ext.grid.locking,"View",Ext.grid,"LockingView"],function(){this.borrow(Ext.AbstractComponent,["up"]);this.borrow(Ext.view.AbstractView,["doFirstRefresh","applyFirstRefresh"])}));(Ext.cmd.derive("Ext.grid.locking.Lockable",Ext.Base,{alternateClassName:"Ext.grid.Lockable",supportsOverflowX:"overflow-x" in document.documentElement.style,syncRowHeight:true,headerCounter:0,scrollDelta:40,lockedGridCls:Ext.baseCSSPrefix+"grid-inner-locked",normalGridCls:Ext.baseCSSPrefix+"grid-inner-normal",unlockText:"Unlock",lockText:"Lock",bothCfgCopy:["invalidateScrollerOnRefresh","hideHeaders","enableColumnHide","enableColumnMove","enableColumnResize","sortableColumns","multiColumnSort","columnLines","rowLines","deferRowRender"],normalCfgCopy:["verticalScroller","verticalScrollDock","verticalScrollerType","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.lockable=true;this.hasView=true;var t=this,e=Ext.getScrollbarSize().height,c=t.store=Ext.StoreManager.lookup(t.store),j=t.getSelectionModel(),l,m,r,h,n,b,d,q,g,u,k,s=t.viewConfig,a=s&&s.loadMask,o=(a!==undefined)?a:t.loadMask,p=t.findPlugin("bufferedrenderer");l=t.constructLockableFeatures();if(t.features){t.features=null}m=t.constructLockablePlugins();t.plugins=m.topPlugins;r=Ext.apply({id:t.id+"-locked",isLocked:true,ownerLockable:t,xtype:t.determineXTypeToCreate(true),store:c,scrollerOwner:false,animate:false,scroll:e?false:"vertical",selModel:j,border:false,cls:t.lockedGridCls,isLayoutRoot:function(){return this.floatedFromCollapse||t.normalGrid.floatedFromCollapse},features:l.lockedFeatures,plugins:m.lockedPlugins},t.lockedGridConfig);h=Ext.apply({id:t.id+"-normal",isLocked:false,ownerLockable:t,xtype:t.determineXTypeToCreate(),store:c,scrollerOwner:false,selModel:j,border:false,cls:t.normalGridCls,isLayoutRoot:function(){return this.floatedFromCollapse||t.lockedGrid.floatedFromCollapse},features:l.normalFeatures,plugins:m.normalPlugins},t.normalGridConfig);t.addCls(Ext.baseCSSPrefix+"grid-locked");Ext.copyTo(h,t,t.bothCfgCopy,true);Ext.copyTo(r,t,t.bothCfgCopy,true);Ext.copyTo(h,t,t.normalCfgCopy,true);Ext.copyTo(r,t,t.lockedCfgCopy,true);for(n=0;n>#normalHeaderCt",items:l},j={itemId:"normalHeaderCt",stretchMaxPartner:"^^>>#lockedHeaderCt",items:c},p={lockedWidth:d.width||0,locked:b,normal:j},o=n.shrinkWrapLocked=!(d.width||d.flex),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}for(h=0,k=g.length;h0&&c)){h.stopEvent();b.scrollTop+=a;d.normalGrid.getView().el.dom.scrollTop=b.scrollTop;d.onNormalViewScroll()}}},onLockedViewScroll:function(){var a=this,c=a.normalGrid.getView().el.dom,b=a.lockedGrid.getView().el.dom;if(c.scrollTop!==b.scrollTop){c.scrollTop=b.scrollTop}},onNormalViewScroll:function(){var a=this,c=a.normalGrid.getView().el.dom,b=a.lockedGrid.getView().el.dom;if(c.scrollTop!==b.scrollTop){b.scrollTop=c.scrollTop}},syncRowHeights:function(){var e=this,a,d=e.lockedGrid.getView(),b=e.normalGrid.getView(),g=d.all.slice(),j=b.all.slice(),c=g.length,h;if(j.length===c){for(a=0;a(m.startIndex+h-1)){g.stretchView(j,g.getScrollHeight());return}if(l<=m.startIndex&&d>=m.endIndex){g.refreshView()}else{if(l0){l=Math.max(g.getFirstVisibleRowIndex()-g.trailingBufferZone,0);d=Math.min(l+h,k.getCount()-1);m.removeRange(null,null,true);g.renderRange(l,d);j.selModel.onLastFocusChanged(null,j.selModel.lastFocused,true)}else{g.refreshView()}}}g.stretchView(j,g.getScrollHeight())},scrollTo:function(m,d,q,s){var j=this,l=j.view,r=l.el.dom,n=j.store,k=n.buffered?n.getTotalCount():n.getCount(),h,b,g,a,p,c,o,e;if((c=l.dataSource.groupingFeature)&&(c.collapsible!==false)){m=Math.min(Math.max(m,0),l.store.getCount()-1);e=l.store.getAt(m);o=c.getGroup(e);if(o.isCollapsed){c.expand(o.name);k=n.buffered?n.getTotalCount():n.getCount()}m=c.indexOf(e)}else{m=Math.min(Math.max(m,0),k-1)}h=Math.max(Math.min(m-(Math.floor((j.leadingBufferZone+j.trailingBufferZone)/2)),k-j.viewSize+1),0);p=Math.max(h*j.rowHeight-j.tableTopBorderWidth,0);b=Math.min(h+j.viewSize-1,k-1);n.getRange(h,b,{callback:function(u,v,t){j.renderRange(v,t,true);g=n.data.getRange(m,m)[0];a=l.getNode(g,false);l.body.dom.style.top=p+"px";j.position=j.scrollTop=r.scrollTop=p=Math.min(Math.max(0,p-l.body.getOffsetsTo(a)[1]),r.scrollHeight-r.clientHeight);if(Ext.isIE){r.scrollTop=p}if(d){l.selModel.select(g)}if(q){q.call(s||j,m,g)}}})},onViewScroll:function(d,l){var g=this,j=g.store,k=(j.buffered?j.getTotalCount():j.getCount()),c,a,b=g.scrollTop=g.view.el.dom.scrollTop,h=false;if(g.ignoreNextScrollEvent){g.ignoreNextScrollEvent=false;return}if(!(g.disabled||k0?1:-1;if(Math.abs(c)>=20||(a!==g.lastScrollDirection)){g.lastScrollDirection=a;g.handleViewScroll(g.lastScrollDirection);h=true}}if(!h){if(g.lockingPartner&&g.lockingPartner.scrollTop!==b){g.lockingPartner.view.el.dom.scrollTop=b}}},handleViewScroll:function(h){var e=this,g=e.view.all,b=e.store,j=e.viewSize,a=(b.buffered?b.getTotalCount():b.getCount()),d,c;if(h==-1){if(g.startIndex){if((e.getFirstVisibleRowIndex()-g.startIndex)r.endIndex){p=r.startIndex-b;r.clear(true);k=Ext.Array.slice(o.doAdd(j,b),0,p);for(h=0;hr.endIndex||er.endIndex){a=Math.max(b-r.startIndex,0);if(m.variableRowHeight){q=r.item(r.startIndex+a,true).offsetTop}r.scroll(Ext.Array.slice(j,r.endIndex+1-b),1,a,b,e);if(m.variableRowHeight){n=m.bodyTop+q}else{n=g}}else{a=Math.max(r.endIndex-e,0);d=r.startIndex;r.scroll(Ext.Array.slice(j,0,r.startIndex-b),-1,a,b,e);if(m.variableRowHeight){n=m.bodyTop-r.item(d,true).offsetTop}else{n=g}}}m.position=m.scrollTop;if(o.positionBody){m.setBodyTop(n,g)}if(l&&!l.disabled&&!c){l.onRangeFetched(j,b,e,true);if(l.scrollTop!==m.scrollTop){l.view.el.dom.scrollTop=m.scrollTop}}},setBodyTop:function(d,g){var e=this,b=e.view,c=e.store,a=b.body.dom,h;d=Math.floor(d);if(g!==undefined){h=d-g;d=g}a.style.position="absolute";a.style.top=(e.bodyTop=Math.max(d,0))+"px";if(e.isRTL&&Ext.supports.xOriginBug&&b.scrollFlags.y){a.style.right=-Ext.getScrollbarSize().width+"px"}if(h){e.scrollTop=e.position=b.el.dom.scrollTop-=h}if(b.all.endIndex===(c.buffered?c.getTotalCount():c.getCount())-1){e.stretchView(b,e.bodyTop+a.offsetHeight)}},getFirstVisibleRowIndex:function(k,c,b,g){var h=this,j=h.view,m=j.all,a=m.elements,d=j.el.dom.clientHeight,e,l;if(m.getCount()&&h.variableRowHeight){if(!arguments.length){k=m.startIndex;c=m.endIndex;b=h.scrollTop;g=b+d;if(h.bodyTop>g||h.bodyTop+j.body.getHeight()g||j.bodyTop+k.body.getHeight()g){return j.getLastVisibleRowIndex(l,e-1,b,g)}h=m+a[e].offsetHeight;if(h>=g){return e}else{if(e!==c){return j.getLastVisibleRowIndex(e+1,c,b,g)}}}return j.getFirstVisibleRowIndex()+Math.ceil(d/j.rowHeight)},getScrollHeight:function(){var d=this,a=d.view,b=d.store,c=!d.hasOwnProperty("rowHeight"),e=d.store.getCount();if(!e){return 0}if(c){if(a.all.getCount()){d.rowHeight=Math.floor(a.body.getHeight()/a.all.getCount())}}return this.scrollHeight=Math.floor((b.buffered?b.getTotalCount():b.getCount())*d.rowHeight)},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.store.getRange(c,a,{callback:b.onRangeFetched,scope:b,fireEvent:false})}},cancelLoad:function(){if(this.loadTask){this.loadTask.cancel()}},doAttemptLoad:function(b,a){this.store.getRange(b,a,{callback:this.onRangeFetched,scope:this,fireEvent:false})},destroy:function(){var b=this,a=b.view;if(a&&a.el){a.el.un("scroll",b.onViewScroll,b)}Ext.destroy(b.viewListeners,b.storeListeners,b.gridListeners)}},0,0,0,0,["plugin.bufferedrenderer"],0,[Ext.grid.plugin,"BufferedRenderer"],0));(Ext.cmd.derive("Ext.grid.plugin.Editing",Ext.AbstractPlugin,{clicksToEdit:2,triggerEvent:undefined,relayedEvents:["beforeedit","edit","validateedit","canceledit"],defaultFieldXType:"textfield",editStyle:"",constructor:function(a){var b=this;b.addEvents("beforeedit","edit","validateedit","canceledit");b.callParent(arguments);b.mixins.observable.constructor.call(b);b.on("edit",function(c,d){b.fireEvent("afteredit",c,d)})},init:function(a){var b=this;b.grid=a;b.view=a.view;b.initEvents();b.mon(a,{beforereconfigure:b.onBeforeReconfigure,reconfigure:b.onReconfigure,scope:b,beforerender:{fn:b.onReconfigure,single:true,scope:b}});a.relayEvents(b,b.relayedEvents);if(b.grid.ownerLockable){b.grid.ownerLockable.relayEvents(b,b.relayedEvents)}a.isEditable=true;a.editingPlugin=a.view.editingPlugin=b},onBeforeReconfigure:function(){this.reconfiguring=true},onReconfigure:function(){this.initFieldAccessors(this.grid.getTopLevelColumnManager().getColumns());delete this.reconfiguring},destroy:function(){var b=this,a=b.grid;Ext.destroy(b.keyNav);b.clearListeners();if(a){a.editingPlugin=a.view.editingPlugin=b.grid=b.view=b.editor=b.keyNav=null}},getEditStyle:function(){return this.editStyle},initFieldAccessors:function(a){if(a.isGroupHeader){a=a.getGridColumns()}else{if(!Ext.isArray(a)){a=[a]}}var d=this,g,e=a.length,b;for(g=0;g',"{%","values.view.renderRows(values.rows, values.viewStartIndex, out);","%}","",{priority:0}],rowTpl:["{%",'var dataRowCls = values.recordIndex === -1 ? "" : " '+Ext.baseCSSPrefix+'grid-data-row";',"%}",'
    ",'{%',"parent.view.renderCell(values, parent.record, parent.recordIndex, xindex - 1, out, parent)","%}","","
    ",{priority:0}],cellTpl:['
    ','
    {style}" {ariaCellInnerAttr}>{value}
    ',"
    ",{priority:0}],selectors:{bodySelector:"div",nodeContainerSelector:"div",itemSelector:"dl."+Ext.baseCSSPrefix+"grid-row",dataRowSelector:"dl."+Ext.baseCSSPrefix+"grid-data-row",cellSelector:"dt."+Ext.baseCSSPrefix+"grid-cell",innerSelector:"div."+Ext.baseCSSPrefix+"grid-cell-inner",getNodeContainerSelector:function(){return this.getBodySelector()},getNodeContainer:function(){return this.el.getById(this.id+"-table",true)}},init:function(b){var a=b.getView();a.tableTpl=Ext.XTemplate.getTpl(this,"tableTpl");a.rowTpl=Ext.XTemplate.getTpl(this,"rowTpl");a.cellTpl=Ext.XTemplate.getTpl(this,"cellTpl");Ext.apply(a,this.selectors)}},0,0,0,0,["plugin.divrenderer"],0,[Ext.grid.plugin,"DivRenderer"],0));(Ext.cmd.derive("Ext.grid.plugin.DragDrop",Ext.AbstractPlugin,{dragText:"{0} selected row{1}",ddGroup:"GridDD",enableDrop:true,enableDrag:true,containerScroll:false,init:function(a){a.on("render",this.onViewRender,this,{single:true})},destroy:function(){Ext.destroy(this.dragZone,this.dropZone)},enable:function(){var a=this;if(a.dragZone){a.dragZone.unlock()}if(a.dropZone){a.dropZone.unlock()}a.callParent()},disable:function(){var a=this;if(a.dragZone){a.dragZone.lock()}if(a.dropZone){a.dropZone.lock()}a.callParent()},onViewRender:function(a){var b=this,c;if(b.enableDrag){if(b.containerScroll){c=a.getEl()}b.dragZone=new Ext.view.DragZone(Ext.apply({view:a,ddGroup:b.dragGroup||b.ddGroup,dragText:b.dragText,containerScroll:b.containerScroll,scrollEl:c},b.dragZone))}if(b.enableDrop){b.dropZone=new Ext.grid.ViewDropZone(Ext.apply({view:a,ddGroup:b.dropGroup||b.ddGroup},b.dropZone))}}},0,0,0,0,["plugin.gridviewdragdrop"],0,[Ext.grid.plugin,"DragDrop"],0));(Ext.cmd.derive("Ext.grid.plugin.RowEditing",Ext.grid.plugin.Editing,{lockableScope:"top",editStyle:"row",autoCancel:true,errorSummary:true,constructor:function(){var a=this;a.callParent(arguments);if(!a.clicksToMoveEditor){a.clicksToMoveEditor=a.clicksToEdit}a.autoCancel=!!a.autoCancel},destroy:function(){Ext.destroy(this.editor);this.callParent(arguments)},onBeforeReconfigure:function(){this.callParent(arguments);this.cancelEdit()},onReconfigure:function(d,b,c){var a=this.editor;this.callParent(arguments);if(c&&a&&a.rendered){a.needsSyncFieldWidths=true}},shouldStartEdit:function(a){return true},startEdit:function(a,e){var d=this,c=d.getEditor(),b;if(Ext.isEmpty(e)){e=d.grid.getTopLevelVisibleColumnManager().getHeaderAtIndex(0)}if(c.beforeEdit()!==false){b=d.callParent([a,e]);if(b){d.context=b;if(d.lockingPartner){d.lockingPartner.cancelEdit()}c.startEdit(b.record,b.column,b);d.editing=true;return true}}return false},cancelEdit:function(){var a=this;if(a.editing){a.getContextFieldValues();a.getEditor().cancelEdit();a.callParent(arguments);return}return true},onEnter:function(a){if(this.editor.down("#cancel").owns(a)){return this.cancelEdit()}else{this.completeEdit()}},completeEdit:function(){var a=this;if(a.editing&&a.validateEdit()){a.editing=false;a.fireEvent("edit",a,a.context)}},validateEdit:function(){this.getContextFieldValues();return this.callParent(arguments)&&this.getEditor().completeEdit()},getEditor:function(){var a=this;if(!a.editor){a.editor=a.initEditor()}return a.editor},getContextFieldValues:function(){var g=this.editor,b=this.context,e=b.record,l={},c={},j=g.query(">[isFormField]"),h=j.length,d,a,k;for(d=0;d0){b.insert(0,l)}}}}},getItemsRenderTree:function(a){this.beforeRenderItems(a);return this.callParent(arguments)},renderItems:function(a,b){this.beforeRenderItems(a);this.callParent(arguments)},configureItem:function(a){this.callParent(arguments);a.animCollapse=a.border=false;if(this.fill){a.flex=1}},beginLayout:function(a){this.callParent(arguments);this.updatePanelClasses(a)},updatePanelClasses:function(e){var c=e.visibleItems,d=c.length,a=true,b,h,g;for(b=0;b','",""],baseCls:Ext.baseCSSPrefix+"splitter",collapsedClsInternal:Ext.baseCSSPrefix+"splitter-collapsed",canResize:true,collapsible:false,collapseOnDblClick:true,defaultSplitMin:40,defaultSplitMax:1000,collapseTarget:"next",horizontal:false,vertical:false,size:5,tracker:null,ariaRole:"separator",getTrackerConfig:function(){return Ext.apply({xclass:"Ext.resizer.SplitterTracker",el:this.el,splitter:this},this.tracker)},beforeRender:function(){var a=this,b=a.getCollapseTarget();a.callParent();if(b.collapsed){a.addCls(a.collapsedClsInternal)}if(!a.canResize){a.addCls(a.baseCls+"-noresize")}Ext.applyIf(a.renderData,{collapseDir:a.getCollapseDirection(),collapsible:a.collapsible||b.collapsible});a.protoEl.unselectable()},onRender:function(){var c=this,b,a;c.callParent(arguments);if(c.performCollapse!==false){if(c.renderData.collapsible){c.mon(c.collapseEl,"click",c.toggleTargetCmp,c)}if(c.collapseOnDblClick){c.mon(c.el,"dblclick",c.toggleTargetCmp,c)}}c.mon(c.getCollapseTarget(),{collapse:c.onTargetCollapse,expand:c.onTargetExpand,beforeexpand:c.onBeforeTargetExpand,beforecollapse:c.onBeforeTargetCollapse,scope:c});if(c.canResize){c.tracker=Ext.create(c.getTrackerConfig());c.relayEvents(c.tracker,["beforedragstart","dragstart","dragend"])}b=c.collapseEl;if(b){b.lastCollapseDirCls=c.collapseDirProps[c.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(a){this.el.addCls([this.collapsedClsInternal,this.collapsedCls]);this.setCollapseEl("")},onTargetExpand:function(a){this.el.removeCls([this.collapsedClsInternal,this.collapsedCls]);this.setCollapseEl("")},collapseDirProps:{top:{cls:Ext.baseCSSPrefix+"layout-split-top"},right:{cls:Ext.baseCSSPrefix+"layout-split-right"},bottom:{cls:Ext.baseCSSPrefix+"layout-split-bottom"},left:{cls:Ext.baseCSSPrefix+"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;a.callParent(arguments);if(Ext.isIE&&a.el){a.el.repaint()}},beforeDestroy:function(){Ext.destroy(this.tracker);this.callParent()}},0,["splitter"],["component","box","splitter"],{component:true,box:true,splitter:true},["widget.splitter"],0,[Ext.resizer,"Splitter"],0));(Ext.cmd.derive("Ext.resizer.BorderSplitter",Ext.resizer.Splitter,{collapseTarget:null,getTrackerConfig:function(){var a=this.callParent();a.xclass="Ext.resizer.BorderSplitterTracker";return a}},0,["bordersplitter"],["component","bordersplitter","box","splitter"],{component:true,bordersplitter:true,box:true,splitter:true},["widget.bordersplitter"],0,[Ext.resizer,"BorderSplitter"],0));(Ext.cmd.derive("Ext.layout.container.Border",Ext.layout.container.Container,{alternateClassName:"Ext.layout.BorderLayout",targetCls:Ext.baseCSSPrefix+"border-layout-ct",itemCls:[Ext.baseCSSPrefix+"border-item",Ext.baseCSSPrefix+"box-item"],type:"border",isBorderLayout:true,padding:undefined,percentageRe:/(\d+)%/,horzPositionProp:"left",padOnContainerProp:"left",padNotOnContainerProp:"right",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"}},centerRegion:null,manageMargins:true,panelCollapseAnimate:true,panelCollapseMode:"placeholder",regionWeights:{north:20,south:10,center:0,west:-10,east:-20},beginAxis:function(m,b,w){var u=this,c=u.axisProps[w],r=!c.horizontal,l=c.sizeProp,p=0,a=m.childItems,g=a.length,t,q,o,h,s,e,k,n,d,v,j;for(q=0;q',renderTpl:['',"{%this.renderBody(out,values)%}","","{%this.renderPadder(out,values)%}"],getRenderData:function(){var a=this.callParent();a.tableCls=this.tableCls;return a},calculate:function(g){var e=this,j=e.getContainerSize(g,true),a,h,b=0,d,c=g.sizeModel.height.shrinkWrap;if(c){if(g.hasDomProp("containerChildrenSizeDone")){g.setProp("contentHeight",e.formTable.dom.offsetHeight+g.targetContext.getPaddingInfo().height)}else{e.done=false}}if(j.gotWidth){a=e.formTable.dom.offsetWidth;h=g.childItems;for(d=h.length;b');c.scrollRangeFlags=e}}},getContainerSize:function(d,j,b){var e=d.targetContext,h=e.getFrameInfo(),m=e.getPaddingInfo(),l=0,n=0,a=b?null:d.state.overflowAdjust,g,k,c,o;if(!d.widthModel.shrinkWrap){++n;c=j?e.getDomProp("width"):e.getProp("width");g=(typeof c=="number");if(g){++l;c-=h.width+m.width;if(a){c-=a.width}}}if(!d.heightModel.shrinkWrap){++n;o=j?e.getDomProp("height"):e.getProp("height");k=(typeof o=="number");if(k){++l;o-=h.height+m.height;if(a){o-=a.height}}}return{width:c,height:o,needed:n,got:l,gotAll:l==n,gotWidth:g,gotHeight:k}},getOverflowXStyle:function(b){var a=this;return a.overflowXStyle||(a.overflowXStyle=a.owner.scrollFlags.overflowX||b.targetContext.getStyle("overflow-x"))},getOverflowYStyle:function(b){var a=this;return a.overflowYStyle||(a.overflowYStyle=a.owner.scrollFlags.overflowY||b.targetContext.getStyle("overflow-y"))},getScrollRangeFlags:(function(){var a=-1;return function(){if(a<0){var g=Ext.getBody().createChild({cls:Ext.baseCSSPrefix+"border-box",role:"presentation",style:{width:"100px",height:"100px",padding:"10px",overflow:"auto"},children:[{role:"presentation",style:{border:"1px solid red",width:"150px",height:"150px",margin:"0 5px 5px 0"}}]}),d=g.dom.scrollHeight,c=g.dom.scrollWidth,e={175:0,165:1,170:2,160:3},b={175:0,165:4,170:8,160:12};a=(e[d]||0)|(b[c]||0);g.remove()}return a}}()),initLayout:function(){var b=this,a=Ext.getScrollbarSize().width;b.callParent();if(a&&b.manageOverflow&&!b.hasOwnProperty("lastOverflowAdjust")){if(b.owner.scrollFlags.y||b.reserveScrollbar){b.lastOverflowAdjust={width:a,height:0}}}},setupRenderTpl:function(a){this.callParent(arguments);a.renderPadder=this.doRenderPadder}},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:Ext.baseCSSPrefix+"menu-color-item",margin:0,id:b.pickerId,xtype:"colorpicker"},a)});b.callParent(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"],["container","component","box","panel","menu","colormenu"],{container:true,component:true,box:true,panel:true,menu:true,colormenu:true},["widget.colormenu"],0,[Ext.menu,"ColorPicker"],0));(Ext.cmd.derive("Ext.menu.DatePicker",Ext.menu.Menu,{hideOnClick:true,pickerId:null,initComponent:function(){var b=this,a=Ext.apply({},b.initialConfig);delete a.listeners;Ext.apply(b,{showSeparator:false,plain:true,bodyPadding:0,items:Ext.applyIf({cls:Ext.baseCSSPrefix+"menu-date-item",margin:0,border:false,id:b.pickerId,xtype:"datepicker"},a)});b.callParent(arguments);b.picker=b.down("datepicker");b.relayEvents(b.picker,["select"]);if(b.hideOnClick){b.on("select",b.hidePickerOnSelect,b)}},hidePickerOnSelect:function(){Ext.menu.Manager.hideAll()}},0,["datemenu"],["container","component","box","datemenu","panel","menu"],{container:true,component:true,box:true,datemenu:true,panel:true,menu:true},["widget.datemenu"],0,[Ext.menu,"DatePicker"],0));(Ext.cmd.derive("Ext.panel.Tool",Ext.Component,{isTool:true,baseCls:Ext.baseCSSPrefix+"tool",disabledCls:Ext.baseCSSPrefix+"tool-disabled",toolPressedCls:Ext.baseCSSPrefix+"tool-pressed",toolOverCls:Ext.baseCSSPrefix+"tool-over",ariaRole:"button",childEls:["toolEl"],renderTpl:[''],toolOwner:null,tooltipType:"qtip",stopEvent:true,height:15,width:15,initComponent:function(){var a=this;a.addEvents("click");a.type=a.type||a.id;Ext.applyIf(a.renderData,{baseCls:a.baseCls,blank:Ext.BLANK_IMAGE_URL,type:a.type});a.tooltip=a.tooltip||a.qtip;a.callParent()},afterRender:function(){var b=this,a;b.callParent(arguments);b.el.on({click:b.onClick,mousedown:b.onMouseDown,mouseover:b.onMouseOver,mouseout:b.onMouseOut,scope:b});if(b.tooltip){if(Ext.quickTipsActive&&Ext.isObject(b.tooltip)){Ext.tip.QuickTipManager.register(Ext.apply({target:b.id},b.tooltip))}else{a=b.tooltipType=="qtip"?"data-qtip":"title";b.el.dom.setAttribute(a,b.tooltip)}}},getFocusEl:function(){return this.el},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},onClick:function(c,b){var a=this;if(a.disabled){return false}a.el.removeCls(a.toolPressedCls);a.el.removeCls(a.toolOverCls);if(a.stopEvent!==false){c.stopEvent()}if(a.handler){Ext.callback(a.handler,a.scope||a,[c,b,a.ownerCt,a])}else{if(a.callback){Ext.callback(a.callback,a.scope||a,[a.toolOwner||a.ownerCt,a,c])}}a.fireEvent("click",a,c);return true},onDestroy:function(){var a=this;if(Ext.quickTipsActive&&Ext.isObject(a.tooltip)){Ext.tip.QuickTipManager.unregister(a.id)}if(a.keyMap){a.keyMap.destroy()}delete a.toolOwner;a.callParent()},onMouseDown:function(){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.define("ExtThemeNeptune.panel.Tool",{override:"Ext.panel.Tool",height:16,width:16});(Ext.cmd.derive("Ext.resizer.SplitterTracker",Ext.dd.DragTracker,{enabled:true,overlayCls:Ext.baseCSSPrefix+"resizable-overlay",createDragOverlay:function(){var a;a=this.overlay=Ext.getBody().createChild({role:"presentation",cls:this.overlayCls,html:" "});a.unselectable();a.setSize(Ext.Element.getViewWidth(true),Ext.Element.getViewHeight(true));a.show()},getPrevCmp:function(){var a=this.getSplitter();return a.previousSibling(":not([hidden])")},getNextCmp:function(){var a=this.getSplitter();return a.nextSibling(":not([hidden])")},onBeforeStart:function(j){var d=this,g=d.getPrevCmp(),a=d.getNextCmp(),c=d.getSplitter().collapseEl,h=j.getTarget(),b;if(!g||!a){return false}if(c&&h===d.getSplitter().collapseEl.dom){return false}if(a.collapsed||g.collapsed){return false}d.prevBox=g.getEl().getBox();d.nextBox=a.getEl().getBox();d.constrainTo=b=d.calculateConstrainRegion();if(!b){return false}return b},onStart:function(b){var a=this.getSplitter();this.createDragOverlay();a.addCls(a.baseCls+"-active")},calculateConstrainRegion:function(){var h=this,a=h.getSplitter(),j=a.getWidth(),k=a.defaultSplitMin,b=a.orientation,e=h.prevBox,l=h.getPrevCmp(),c=h.nextBox,g=h.getNextCmp(),n,m,d;if(b==="vertical"){d={prevCmp:l,nextCmp:g,prevBox:e,nextBox:c,defaultMin:k,splitWidth:j};n=new Ext.util.Region(e.y,h.getVertPrevConstrainRight(d),e.bottom,h.getVertPrevConstrainLeft(d));m=new Ext.util.Region(c.y,h.getVertNextConstrainRight(d),c.bottom,h.getVertNextConstrainLeft(d))}else{n=new Ext.util.Region(e.y+(l.minHeight||k),e.right,(l.maxHeight?e.y+l.maxHeight:c.bottom-(g.minHeight||k))+j,e.x);m=new Ext.util.Region((g.maxHeight?c.bottom-g.maxHeight:e.y+(l.minHeight||k))-j,c.right,c.bottom-(g.minHeight||k),c.x)}return n.intersect(m)},performResize:function(n,h){var p=this,a=p.getSplitter(),j=a.orientation,q=p.getPrevCmp(),o=p.getNextCmp(),b=a.ownerCt,l=b.query(">[flex]"),m=l.length,c=j==="vertical",k=0,g=c?"width":"height",d=0,r,s;for(;kq){v=q}}if(v-m<2){return null}n=new Ext.util.Region(p,x,k,g);y.constraintAdjusters[y.getCollapseDirection()](n,m,v,a);y.dragInfo={minRange:m,maxRange:v,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(h){var k=this,b=k.splitter,a=b.collapseTarget,m=b.neighbors,d=k.getSplitter().collapseEl,j=h.getTarget(),c=m.length,g,l;if(d&&j===b.collapseEl.dom){return false}if(a.collapsed){return false}for(g=0;gc){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,j,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(Ext.baseCSSPrefix+"proxy-el");return b},onStart:function(a){this.activeResizeHandle=Ext.get(this.getDragTarget().id);if(!this.dynamic){this.resize(this.startBox)}},onDrag:function(a){if(this.dynamic||this.proxy){this.updateDimensions(a)}},updateDimensions:function(r,n){var s=this,c=s.activeResizeHandle.region,g=s.getOffset(s.constrainTo?"dragTarget":null),l=s.startBox,h,p=0,t=0,k,q,a=0,v=0,u,j,b,d,o,m;c=s.convertRegionName(c);switch(c){case"south":t=g[1];b=2;break;case"north":t=-g[1];v=-t;b=2;break;case"east":p=g[0];b=1;break;case"west":p=-g[0];a=-p;b=1;break;case"northeast":t=-g[1];v=-t;p=g[0];j=[l.x,l.y+l.height];b=3;break;case"southeast":t=g[1];p=g[0];j=[l.x,l.y];b=3;break;case"southwest":p=-g[0];a=-p;t=g[1];j=[l.x+l.width,l.y];b=3;break;case"northwest":t=-g[1];v=-t;p=-g[0];a=-p;j=[l.x+l.width,l.y+l.height];b=3;break}d={width:l.width+p,height:l.height+t,x:l.x+a,y:l.y+v};k=Ext.Number.snap(d.width,s.widthIncrement);q=Ext.Number.snap(d.height,s.heightIncrement);if(k!=d.width||q!=d.height){switch(c){case"northeast":d.y-=q-d.height;break;case"north":d.y-=q-d.height;break;case"southwest":d.x-=k-d.width;break;case"west":d.x-=k-d.width;break;case"northwest":d.x-=k-d.width;d.y-=q-d.height}d.width=k;d.height=q}if(d.widths.maxWidth){d.width=Ext.Number.constrain(d.width,s.minWidth,s.maxWidth);if(a){d.x=l.x+(l.width-d.width)}}else{s.lastX=d.x}if(d.heights.maxHeight){d.height=Ext.Number.constrain(d.height,s.minHeight,s.maxHeight);if(v){d.y=l.y+(l.height-d.height)}}else{s.lastY=d.y}if(s.preserveRatio||r.shiftKey){h=s.startBox.width/s.startBox.height;o=Math.min(Math.max(s.minHeight,d.width/h),s.maxHeight);m=Math.min(Math.max(s.minWidth,d.height*h),s.maxWidth);if(b==1){d.height=o}else{if(b==2){d.width=m}else{u=Math.abs(j[0]-this.lastXY[0])/Math.abs(j[1]-this.lastXY[1]);if(u>h){d.height=o}else{d.width=m}if(c=="northeast"){d.y=l.y-(d.height-l.height)}else{if(c=="northwest"){d.y=l.y-(d.height-l.height);d.x=l.x-(d.width-l.width)}else{if(c=="southwest"){d.x=l.x-(d.width-l.width)}}}}}}s.setPosition=d.x!==s.startBox.x||d.y!==s.startBox.y;s.resize(d,n)},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: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+/,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(c){var m=this,q,s,r=m.handles,d,p,h,g=0,o,b,n=[],j,a,l,e,k=Ext.dom.Element.unselectableCls;m.addEvents("beforeresize","resizedrag","resize");if(Ext.isString(c)||Ext.isElement(c)||c.dom){q=c;c=arguments[1]||{};c.target=q}m.mixins.observable.constructor.call(m,c);q=m.target;if(q){if(q.isComponent){q.addClsWithUI("resizable");if(q.minWidth){m.minWidth=q.minWidth}if(q.minHeight){m.minHeight=q.minHeight}if(q.maxWidth){m.maxWidth=q.maxWidth}if(q.maxHeight){m.maxHeight=q.maxHeight}if(q.floating){if(!m.hasOwnProperty("handles")){m.handles="n ne e se s sw w nw"}}m.el=q.getEl()}else{q=m.el=m.target=Ext.get(q)}}else{q=m.target=m.el=Ext.get(m.el)}m.el.addCls(Ext.AbstractComponent.prototype.borderBoxCls);if(Ext.isNumber(m.width)){m.width=Ext.Number.constrain(m.width,m.minWidth,m.maxWidth)}if(Ext.isNumber(m.height)){m.height=Ext.Number.constrain(m.height,m.minHeight,m.maxHeight)}if(m.width!==null||m.height!==null){m.target.setSize(m.width,m.height)}s=m.el.dom.tagName.toUpperCase();if(s==="TEXTAREA"||s==="IMG"||s==="TABLE"){m.originalTarget=m.target;e=q.isComponent?q.getEl():q;m.el.addCls(m.wrappedCls);m.target=m.el=m.el.wrap({role:"presentation",cls:m.wrapCls,id:m.el.id+"-rzwrap",style:e.getStyles("margin-top","margin-bottom")});m.el.setPositioning(e.getPositioning());e.clearPositioning();m.el.setBox(e.getBox());e.setStyle("position","absolute");m.isTargetWrapped=true}m.el.position();if(m.pinned){m.el.addCls(m.pinnedCls)}m.resizeTracker=new Ext.resizer.ResizeTracker({disabled:m.disabled,target:q,el:m.el,constrainTo:m.constrainTo,handleCls:m.handleCls,overCls:m.overCls,throttle:m.throttle,proxy:m.originalTarget?m.el:null,dynamic:m.originalTarget?true:m.dynamic,originalTarget:m.originalTarget,delegate:"."+m.handleCls,preserveRatio:m.preserveRatio,heightIncrement:m.heightIncrement,widthIncrement:m.widthIncrement,minHeight:m.minHeight,maxHeight:m.maxHeight,minWidth:m.minWidth,maxWidth:m.maxWidth});m.resizeTracker.on({mousedown:m.onBeforeResize,drag:m.onResize,dragend:m.onResizeEnd,scope:m});if(m.handles=="all"){m.handles="n s e w ne nw se sw"}r=m.handles=m.handles.split(m.delimiterRe);p=m.possiblePositions;h=r.length;d=m.handleCls+" "+m.handleCls+"-{0}";if(m.target.isComponent){l=m.target.baseCls;d+=" "+l+"-handle "+l+"-handle-{0}";if(Ext.supports.CSS3BorderRadius){d+=" "+l+"-handle-{0}-br"}}j=Ext.isIE6?' style="height:'+m.el.getHeight()+'px"':"";for(;g")}}Ext.DomHelper.append(m.el,n.join(""));n.length=0;for(g=0;g-1){this.doSelect(a.record,false,b)}},onCellDeselect:function(a,b){if(a&&a.row!==undefined){this.doDeselect(a.record,b)}},onSelectChange:function(b,e,d,h){var g=this,j,c,a;if(e){j=g.nextSelection;c="select"}else{j=g.lastSelection||g.noSelection;c="deselect"}a=j.view||g.primaryView;if((d||g.fireEvent("before"+c,g,b,j.row,j.column))!==false&&h()!==false){if(e){if(!g.preventFocus){a.focusCell(j,true)}a.onCellSelect(j)}else{a.onCellDeselect(j);delete g.selection}if(!d){g.fireEvent(c,g,b,j.row,j.column)}}},onKeyTab:function(d,b){var c=this,g=c.getCurrentPosition(),a;if(g){a=g.view.editingPlugin;if(a&&c.wasEditing){c.onEditorTab(a,d)}else{c.move(d.shiftKey?"left":"right",d)}}},onEditorTab:function(b,g){var c=this,d=g.shiftKey?"left":"right",h=c.getCurrentPosition(),a=h.view.walkCells(h,d,g,c.preventWrap);if(a){if(b.startEdit(a.record,a.columnHeader)){c.wasEditing=false}else{c.setCurrentPosition(a);c.wasEditing=true}}},refresh:function(){var b=this.getCurrentPosition(),a;if(b&&(a=this.store.indexOf(this.selected.last()))!==-1){b.row=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 d=this,h=d.getCurrentPosition(),c,g=b.headerCt,a,e;if(h&&h.view===b){a=h.record;e=h.columnHeader;if(!e.isDescendantOf(g)){e=g.queryById(e.id)||g.down('[text="'+e.text+'"]')||g.down('[dataIndex="'+e.dataIndex+'"]')}if(h.record){if(e&&(b.store.indexOfId(a.getId())!==-1)){c=new Ext.grid.CellContext(b).setPosition({row:a,column:e});d.setCurrentPosition(c)}}else{d.selection=null}}},selectByPosition:function(a,b){this.setCurrentPosition(a,b)}},1,0,0,0,["selection.cellmodel"],0,[Ext.selection,"CellModel"],0));(Ext.cmd.derive("Ext.tab.Tab",Ext.button.Button,{isTab:true,baseCls:Ext.baseCSSPrefix+"tab",closeElOverCls:Ext.baseCSSPrefix+"tab-close-btn-over",activeCls:"active",closableCls:"closable",closable:true,closeText:"Close Tab",active:false,childEls:["closeEl"],scale:false,position:"top",ariaRole:"tab",initComponent:function(){var a=this;a.addEvents("activate","deactivate","beforeclose","close");a.callParent(arguments);if(a.card){a.setCard(a.card)}a.overCls=["over",a.position+"-over"]},getTemplateArgs:function(){var b=this,a=b.callParent();a.closable=b.closable;a.closeText=b.closeText;return a},getFramingInfoCls:function(){return this.baseCls+"-"+this.ui+"-"+this.position},beforeRender:function(){var b=this,a=b.up("tabbar"),c=b.up("tabpanel");b.callParent();b.addClsWithUI(b.position);if(b.active){b.addClsWithUI([b.activeCls,b.position+"-"+b.activeCls])}b.syncClosableUI();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();a.callParent(arguments);if(a.closable){a.closeEl.addClsOnOver(a.closeElOverCls)}a.initKeyNav()},initKeyNav:function(){var a=this;a.keyNav=new Ext.util.KeyNav(a.el,{enter:a.onEnterKey,del:a.onDeleteKey,scope:a})},setElOrientation:function(){var a=this.position;if(a==="left"||a==="right"){this.el.setVertical(a==="right"?90:270)}},enable:function(a){var b=this;b.callParent(arguments);b.removeClsWithUI(b.position+"-disabled");return b},disable:function(a){var b=this;b.callParent(arguments);b.addClsWithUI(b.position+"-disabled");return b},onDestroy:function(){var a=this;Ext.destroy(a.keyNav);delete a.keyNav;a.callParent(arguments)},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.syncClosableUI();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:"a",role:"presentation",cls:a.baseCls+"-close-btn",href:"#",title:a.closeText},"after")}b.addClsOnOver(a.closeElOverCls)}else{if(b){b.remove();delete a.closeEl}}},syncClosableUI:function(){var b=this,a=[b.closableCls,b.closableCls+"-"+b.position];if(b.closable){b.addClsWithUI(a)}else{b.removeClsWithUI(a)}},setCard:function(a){var b=this;b.card=a;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)}},onDeleteKey:function(a){if(this.closable){this.onCloseClick()}},afterClick:function(a){if(!a){this.focus()}},activate:function(b){var a=this;a.active=true;a.addClsWithUI([a.activeCls,a.position+"-"+a.activeCls]);if(b!==true){a.fireEvent("activate",a)}},deactivate:function(b){var a=this;a.active=false;a.removeClsWithUI([a.activeCls,a.position+"-"+a.activeCls]);if(b!==true){a.fireEvent("deactivate",a)}}},0,["tab"],["button","component","tab","box"],{button:true,component:true,tab:true,box:true},["widget.tab"],0,[Ext.tab,"Tab"],0));(Ext.cmd.derive("Ext.util.Point",Ext.util.Region,{statics:{fromEvent:function(a){a=a.browserEvent||a;a=(a.changedTouches&&a.changedTouches.length>0)?a.changedTouches[0]:a;return new this(a.pageX,a.pageY)}},constructor:function(a,b){this.callParent([b,a,b,a])},toString:function(){return"Point["+this.x+","+this.y+"]"},equals:function(a){return(this.x==a.x&&this.y==a.y)},isWithin:function(b,a){if(!Ext.isObject(a)){a={x:a,y:a}}return(this.x<=b.x+a.x&&this.x>=b.x-a.x&&this.y<=b.y+a.y&&this.y>=b.y-a.y)},isContainedBy:function(a){if(!(a instanceof Ext.util.Region)){a=Ext.get(a.el||a).getRegion()}return a.contains(this)},roundedEquals:function(a){return(Math.round(this.x)==Math.round(a.x)&&Math.round(this.y)==Math.round(a.y))}},3,0,0,0,0,0,[Ext.util,"Point"],function(){this.prototype.translate=Ext.util.Region.prototype.translateBy}));(Ext.cmd.derive("Ext.tab.Bar",Ext.panel.Header,{baseCls:Ext.baseCSSPrefix+"tab-bar",isTabBar:true,defaultType:"tab",plain:false,ariaRole:"tablist",childEls:["body","strip"],renderTpl:['",'"],_reverseDockNames:{left:"right",right:"left"},initComponent:function(){var a=this;if(a.plain){a.addCls(a.baseCls+"-plain")}a.addClsWithUI(a.orientation);a.addEvents("change");a.callParent(arguments);Ext.merge(a.layout,a.initialConfig.layout);a.layout.align=(a.orientation=="vertical")?"left":"top";a.layout.overflowHandler=new Ext.layout.container.boxOverflow.Scroller(a.layout);a.remove(a.titleCmp);delete a.titleCmp;Ext.apply(a.renderData,{bodyCls:a.bodyCls,dock:a.dock})},onRender:function(){var a=this;a.callParent();if(a.orientation==="vertical"&&(Ext.isIE8||Ext.isIE9)&&Ext.isStrict){a.el.on({mousemove:a.onMouseMove,scope:a})}},afterRender:function(){var a=this.layout;this.callParent();if(Ext.isIE9&&Ext.isStrict&&this.orientation==="vertical"){a.innerCt.on("scroll",function(){a.innerCt.dom.scrollLeft=0})}},afterLayout:function(){this.adjustTabPositions();this.callParent(arguments)},adjustTabPositions:function(){var a=this.items.items,b=a.length,c;if(!Ext.isIE9m){if(this.dock==="right"){while(b--){c=a[b];if(c.isVisible()){c.el.setStyle("left",c.lastBox.width+"px")}}}else{if(this.dock==="left"){while(b--){c=a[b];if(c.isVisible()){c.el.setStyle("left",-c.lastBox.height+"px")}}}}}},getLayout:function(){var a=this;a.layout.type=(a.orientation==="horizontal")?"hbox":"vbox";return a.callParent(arguments)},onAdd:function(a){a.position=this.dock;this.callParent(arguments)},onRemove:function(a){var b=this;if(a===b.previousTab){b.previousTab=null}b.callParent(arguments)},afterComponentLayout:function(b){var c=this,a=c.needsScroll;c.callParent(arguments);if(a){c.layout.overflowHandler.scrollToItem(c.activeTab)}delete c.needsScroll},onClick:function(h,g){var d=this,k=d.tabPanel,j,c,b,a;if(h.getTarget("."+Ext.baseCSSPrefix+"box-scroller")){return}if(d.orientation==="vertical"&&(Ext.isIE8||Ext.isIE9)&&Ext.isStrict){a=d.getTabInfoFromPoint(h.getXY());c=a.tab;b=a.close}else{j=h.getTarget("."+Ext.tab.Tab.prototype.baseCls);c=j&&Ext.getCmp(j.id);b=c&&c.closeEl&&(g===c.closeEl.dom)}if(b){h.preventDefault()}if(c&&c.isDisabled&&!c.isDisabled()){if(c.closable&&b){c.onCloseClick()}else{if(k){k.setActiveTab(c.card)}else{d.setActiveTab(c)}}c.afterClick(b)}},onMouseMove:function(g){var d=this,b=d._overTab,a,c;if(g.getTarget("."+Ext.baseCSSPrefix+"box-scroller")){return}a=d.getTabInfoFromPoint(g.getXY());c=a.tab;if(c!==b){if(b&&b.rendered){b.onMouseLeave(g);d._overTab=null}if(c){c.onMouseEnter(g);d._overTab=c;if(!c.disabled){d.el.setStyle("cursor","pointer")}}else{d.el.setStyle("cursor","default")}}},onMouseLeave:function(b){var a=this._overTab;if(a&&a.rendered){a.onMouseLeave(b)}},getTabInfoFromPoint:function(g){var A=this,w=A.items.items,e=w.length,o=A.layout.innerCt,u=o.getXY(),t=new Ext.util.Point(g[0],g[1]),v=0,x,b,a,p,y,j,h,d,r,l,k,n,m,s,q,z,c;for(;v1){return(b.previousTab&&b.previousTab!==a&&!b.previousTab.disabled)?b.previousTab:(a.next("tab[disabled=false]")||a.prev("tab[disabled=false]"))}},setActiveTab:function(b,a){var c=this;if(!b.disabled&&b!==c.activeTab){if(c.activeTab){if(c.activeTab.isDestroyed){c.previousTab=null}else{c.previousTab=c.activeTab;c.activeTab.deactivate()}}b.activate();c.activeTab=b;c.needsScroll=true;if(!a){c.fireEvent("change",c,b,b.card);c.updateLayout()}}}},0,["tabbar"],["container","component","header","tabbar","box"],{container:true,component:true,header:true,tabbar:true,box:true},["widget.tabbar"],0,[Ext.tab,"Bar"],0));Ext.define("ExtThemeNeptune.tab.Tab",{override:"Ext.tab.Tab",border:false});(Ext.cmd.derive("Ext.selection.CheckboxModel",Ext.selection.RowModel,{mode:"MULTI",injectCheckbox:0,checkOnly:false,showHeaderCheckbox:undefined,checkSelector:"."+Ext.baseCSSPrefix+"grid-row-checker",headerWidth:24,checkerOnCls:Ext.baseCSSPrefix+"grid-hd-checker-on",tdCls:Ext.baseCSSPrefix+"grid-cell-special "+Ext.baseCSSPrefix+"grid-cell-row-checker",constructor:function(){var a=this;a.callParent(arguments);if(a.mode==="SINGLE"&&a.showHeaderCheckbox!==true){a.showHeaderCheckbox=false}},beforeViewRender:function(b){var c=this,a;c.callParent(arguments);if(!c.hasLockedHeader()||b.headerCt.lockedCt){if(c.showHeaderCheckbox!==false){b.headerCt.on("headerclick",c.onHeaderClick,c)}c.addCheckbox(b,true);a=b.ownerCt;if(b.headerCt.lockedCt){a=a.ownerCt}c.mon(a,"reconfigure",c.onReconfigure,c)}},bindComponent:function(a){var b=this;b.sortable=false;b.callParent(arguments)},hasLockedHeader:function(){var a=this.views,c=a.length,b;for(b=0;b '},processSelection:function(b,a,h,d,j){var g=this,c=j.getTarget(g.checkSelector),k;if(g.checkOnly&&!c){return}if(c){k=g.getSelectionMode();if(k!=="SINGLE"){g.setSelectionMode("SIMPLE")}g.selectWithEvent(a,j);g.setSelectionMode(k)}else{g.selectWithEvent(a,j)}},onSelectChange:function(){this.callParent(arguments);if(!this.suspendChange){this.updateHeaderState()}},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(a){if(a&&!this.suspendChange){this.updateHeaderState()}this.callParent(arguments)},resumeChanges:function(){this.callParent();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.buffered&&e>0){b=g.selected;k=true;for(c=0,d=b.getCount();ch){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=j.top&&h<(j.top+d)){return"before"}else{if(!a&&(l||(h>=(j.bottom-d)&&h<=j.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;c2)?a[2]:null,h=(j>3)?a[3]:"/",d=(j>4)?a[4]:null,g=(j>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=Ks?i*=10:o>=tf?i*=5:o>=nf&&(i*=2),n=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:e}})}function g(t,n){for(var e,r=0,i=t.length;r=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}})}function S(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;rn?1:t>=n?0:NaN}function q(t){return function(){this.removeAttribute(t)}}function U(t){return function(){this.removeAttributeNS(t.space,t.local)}}function D(t,n){return function(){this.setAttribute(t,n)}}function O(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function F(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function I(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function Y(t){return function(){this.style.removeProperty(t)}}function B(t,n,e){return function(){this.style.setProperty(t,n,e)}}function j(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function H(t){return function(){delete this[t]}}function X(t,n){return function(){this[t]=n}}function V(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function W(t){return t.trim().split(/^|\s+/)}function $(t){return t.classList||new Z(t)}function Z(t){this._node=t,this._names=W(t.getAttribute("class")||"")}function G(t,n){for(var e=$(t),r=-1,i=n.length;++r>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1)):(n=Ol.exec(t))?kt(parseInt(n[1],16)):(n=Fl.exec(t))?new At(n[1],n[2],n[3],1):(n=Il.exec(t))?new At(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=Yl.exec(t))?St(n[1],n[2],n[3],n[4]):(n=Bl.exec(t))?St(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=jl.exec(t))?Ct(n[1],n[2]/100,n[3]/100,1):(n=Hl.exec(t))?Ct(n[1],n[2]/100,n[3]/100,n[4]):Xl.hasOwnProperty(t)?kt(Xl[t]):"transparent"===t?new At(NaN,NaN,NaN,0):null}function kt(t){return new At(t>>16&255,t>>8&255,255&t,1)}function St(t,n,e,r){return r<=0&&(t=n=e=NaN),new At(t,n,e,r)}function Nt(t){return t instanceof Mt||(t=Tt(t)),t?(t=t.rgb(),new At(t.r,t.g,t.b,t.opacity)):new At}function Et(t,n,e,r){return 1===arguments.length?Nt(t):new At(t,n,e,null==r?1:r)}function At(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function Ct(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Rt(t,n,e,r)}function zt(t){if(t instanceof Rt)return new Rt(t.h,t.s,t.l,t.opacity);if(t instanceof Mt||(t=Tt(t)),!t)return new Rt;if(t instanceof Rt)return t;t=t.rgb();var n=t.r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),u=NaN,a=o-i,c=(o+i)/2;return a?(u=n===o?(e-r)/a+6*(e0&&c<1?0:u,new Rt(u,a,c,t.opacity)}function Pt(t,n,e,r){return 1===arguments.length?zt(t):new Rt(t,n,e,null==r?1:r)}function Rt(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Lt(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}function qt(t){if(t instanceof Dt)return new Dt(t.l,t.a,t.b,t.opacity);if(t instanceof Ht){var n=t.h*Vl;return new Dt(t.l,Math.cos(n)*t.c,Math.sin(n)*t.c,t.opacity)}t instanceof At||(t=Nt(t));var e=Yt(t.r),r=Yt(t.g),i=Yt(t.b),o=Ot((.4124564*e+.3575761*r+.1804375*i)/Zl),u=Ot((.2126729*e+.7151522*r+.072175*i)/Gl);return new Dt(116*u-16,500*(o-u),200*(u-Ot((.0193339*e+.119192*r+.9503041*i)/Jl)),t.opacity)}function Ut(t,n,e,r){return 1===arguments.length?qt(t):new Dt(t,n,e,null==r?1:r)}function Dt(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function Ot(t){return t>nh?Math.pow(t,1/3):t/th+Ql}function Ft(t){return t>Kl?t*t*t:th*(t-Ql)}function It(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function Yt(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Bt(t){if(t instanceof Ht)return new Ht(t.h,t.c,t.l,t.opacity);t instanceof Dt||(t=qt(t));var n=Math.atan2(t.b,t.a)*Wl;return new Ht(n<0?n+360:n,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}function jt(t,n,e,r){return 1===arguments.length?Bt(t):new Ht(t,n,e,null==r?1:r)}function Ht(t,n,e,r){this.h=+t,this.c=+n,this.l=+e,this.opacity=+r}function Xt(t){if(t instanceof Wt)return new Wt(t.h,t.s,t.l,t.opacity);t instanceof At||(t=Nt(t));var n=t.r/255,e=t.g/255,r=t.b/255,i=(sh*r+ah*n-ch*e)/(sh+ah-ch),o=r-i,u=(uh*(e-i)-ih*o)/oh,a=Math.sqrt(u*u+o*o)/(uh*i*(1-i)),c=a?Math.atan2(u,o)*Wl-120:NaN;return new Wt(c<0?c+360:c,a,i,t.opacity)}function Vt(t,n,e,r){return 1===arguments.length?Xt(t):new Wt(t,n,e,null==r?1:r)}function Wt(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function $t(t,n,e,r,i){var o=t*t,u=o*t;return((1-3*t+3*o-u)*n+(4-6*o+3*u)*e+(1+3*t+3*o-3*u)*r+u*i)/6}function Zt(t,n){return function(e){return t+e*n}}function Gt(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}function Jt(t,n){var e=n-t;return e?Zt(t,e>180||e<-180?e-360*Math.round(e/360):e):yh(isNaN(t)?n:t)}function Qt(t){return 1===(t=+t)?Kt:function(n,e){return e-n?Gt(n,e,t):yh(isNaN(n)?e:n)}}function Kt(t,n){var e=n-t;return e?Zt(t,e):yh(isNaN(t)?n:t)}function tn(t){return function(n){var e,r,i=n.length,o=new Array(i),u=new Array(i),a=new Array(i);for(e=0;e180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:Th(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}function a(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:Th(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}function c(t,n,e,r,o,u){if(t!==e||n!==r){var a=o.push(i(o)+"scale(",null,",",null,")");u.push({i:a-4,x:Th(t,e)},{i:a-2,x:Th(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}return function(n,e){var r=[],i=[];return n=t(n),e=t(e),o(n.translateX,n.translateY,e.translateX,e.translateY,r,i),u(n.rotate,e.rotate,r,i),a(n.skewX,e.skewX,r,i),c(n.scaleX,n.scaleY,e.scaleX,e.scaleY,r,i),n=e=null,function(t){for(var n,e=-1,o=i.length;++e=0&&n._call.call(null,t),n=n._next;--$h}function mn(){Kh=(Qh=np.now())+tp,$h=Zh=0;try{yn()}finally{$h=0,bn(),Kh=0}}function xn(){var t=np.now(),n=t-Qh;n>Jh&&(tp-=n,Qh=t)}function bn(){for(var t,n,e=dh,r=1/0;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:dh=n);vh=t,wn(r)}function wn(t){if(!$h){Zh&&(Zh=clearTimeout(Zh));var n=t-Kh;n>24?(t<1/0&&(Zh=setTimeout(mn,n)),Gh&&(Gh=clearInterval(Gh))):(Gh||(Qh=Kh,Gh=setInterval(xn,Jh)),$h=1,ep(mn))}}function Mn(t,n){var e=t.__transition;if(!e||!(e=e[n])||e.state>ap)throw new Error("too late");return e}function Tn(t,n){var e=t.__transition;if(!e||!(e=e[n])||e.state>sp)throw new Error("too late");return e}function kn(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("too late");return e}function Sn(t,n,e){function r(t){e.state=cp,e.timer.restart(i,e.delay,e.time),e.delay<=t&&i(t-e.delay)}function i(r){var s,f,l,h;if(e.state!==cp)return u();for(s in c)if(h=c[s],h.name===e.name){if(h.state===fp)return rp(i);h.state===lp?(h.state=pp,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete c[s]):+s=0&&(t=t.slice(0,n)),!t||"start"===t})}function Hn(t,n,e){var r,i,o=jn(n)?Mn:Tn;return function(){var u=o(this,t),a=u.on;a!==r&&(i=(r=a).copy()).on(n,e),u.on=i}}function Xn(t){return function(){var n=this.parentNode;for(var e in this.__transition)if(+e!==t)return;n&&n.removeChild(this)}}function Vn(t,n){var e,r,i;return function(){var o=sl(this).getComputedStyle(this,null),u=o.getPropertyValue(t),a=(this.style.removeProperty(t),o.getPropertyValue(t));return u===a?null:u===e&&a===r?i:i=n(e=u,r=a)}}function Wn(t){return function(){this.style.removeProperty(t)}}function $n(t,n,e){var r,i;return function(){var o=sl(this).getComputedStyle(this,null).getPropertyValue(t);return o===e?null:o===r?i:i=n(r=o,e)}}function Zn(t,n,e){var r,i,o;return function(){var u=sl(this).getComputedStyle(this,null),a=u.getPropertyValue(t),c=e(this);return null==c&&(this.style.removeProperty(t),c=u.getPropertyValue(t)),a===c?null:a===r&&c===i?o:o=n(r=a,i=c)}}function Gn(t,n,e){function r(){var r=this,i=n.apply(r,arguments);return i&&function(n){r.style.setProperty(t,i(n),e)}}return r._value=n,r}function Jn(t){return function(){this.textContent=t}}function Qn(t){return function(){var n=t(this);this.textContent=null==n?"":n}}function Kn(t,n,e,r){this._groups=t,this._parents=n,this._name=e,this._id=r}function te(t){return vt().transition(t)}function ne(){return++Up}function ee(t){return+t}function re(t){return t*t}function ie(t){return t*(2-t)}function oe(t){return((t*=2)<=1?t*t:--t*(2-t)+1)/2}function ue(t){return t*t*t}function ae(t){return--t*t*t+1}function ce(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}function se(t){return 1-Math.cos(t*jp)}function fe(t){return Math.sin(t*jp)}function le(t){return(1-Math.cos(Bp*t))/2}function he(t){return Math.pow(2,10*t-10)}function pe(t){return 1-Math.pow(2,-10*t)}function de(t){return((t*=2)<=1?Math.pow(2,10*t-10):2-Math.pow(2,10-10*t))/2}function ve(t){return 1-Math.sqrt(1-t*t)}function _e(t){return Math.sqrt(1- --t*t)}function ge(t){return((t*=2)<=1?1-Math.sqrt(1-t*t):Math.sqrt(1-(t-=2)*t)+1)/2}function ye(t){return 1-me(1-t)}function me(t){return(t=+t)Math.abs(t[1]-O[1])?M=!0:w=!0),O=t,b=!0,_d(),o()}function o(){var t;switch(m=O[0]-D[0],x=O[1]-D[1],S){case yd:case gd:N&&(m=Math.max(P-l,Math.min(L-v,m)),h=l+m,_=v+m),E&&(x=Math.max(R-p,Math.min(q-g,x)),d=p+x,y=g+x);break;case md:N<0?(m=Math.max(P-l,Math.min(L-l,m)),h=l+m,_=v):N>0&&(m=Math.max(P-v,Math.min(L-v,m)),h=l,_=v+m),E<0?(x=Math.max(R-p,Math.min(q-p,x)),d=p+x,y=g):E>0&&(x=Math.max(R-g,Math.min(q-g,x)),d=p,y=g+x);break;case xd:N&&(h=Math.max(P,Math.min(L,l-m*N)),_=Math.max(P,Math.min(L,v+m*N))),E&&(d=Math.max(R,Math.min(q,p-x*E)),y=Math.max(R,Math.min(q,g+x*E)))}_0&&(l=h-m),E<0?g=y-x:E>0&&(p=d-x),S=yd,Y.attr("cursor",Td.selection),o());break;default:return}_d()}function s(){switch(t.event.keyCode){case 16:U&&(w=M=U=!1,o());break;case 18:S===xd&&(N<0?v=_:N>0&&(l=h),E<0?g=y:E>0&&(p=d),S=md,o());break;case 32:S===yd&&(t.event.altKey?(N&&(v=_-m*N,l=h+m*N),E&&(g=y-x*E,p=d+x*E),S=xd):(N<0?v=_:N>0&&(l=h),E<0?g=y:E>0&&(p=d),S=md),Y.attr("cursor",Td[k]),o());break;default:return}_d()}if(t.event.touches){if(t.event.changedTouches.length=(o=(v+g)/2))?v=o:g=o,(f=e>=(u=(_+y)/2))?_=u:y=u,i=p,!(p=p[l=f<<1|s]))return i[l]=d,t;if(a=+t._x.call(null,p.data),c=+t._y.call(null,p.data),n===a&&e===c)return d.next=p,i?i[l]=d:t._root=d,t;do i=i?i[l]=new Array(4):t._root=new Array(4),(s=n>=(o=(v+g)/2))?v=o:g=o,(f=e>=(u=(_+y)/2))?_=u:y=u;while((l=f<<1|s)===(h=(c>=u)<<1|a>=o));return i[h]=p,i[l]=d,t}function Qe(t){var n,e,r,i,o=t.length,u=new Array(o),a=new Array(o),c=1/0,s=1/0,f=-(1/0),l=-(1/0);for(e=0;ef&&(f=r),il&&(l=i));for(f",i=n[3]||"-",o=n[4]||"",u=!!n[5],a=n[6]&&+n[6],c=!!n[7],s=n[8]&&+n[8].slice(1),f=n[9]||"";"n"===f?(c=!0,f="g"):Iv[f]||(f=""),(u||"0"===e&&"="===r)&&(u=!0,e="0",r="="),this.fill=e,this.align=r,this.sign=i,this.symbol=o,this.zero=u,this.width=a,this.comma=c,this.precision=s,this.type=f}function pr(n){ +return Bv=Xv(n),t.format=Bv.format,t.formatPrefix=Bv.formatPrefix,Bv}function dr(){this.reset()}function vr(t,n,e){var r=t.s=n+e,i=r-n,o=r-i;t.t=n-o+(e-i)}function _r(t){return t>1?0:t<-1?C_:Math.acos(t)}function gr(t){return t>1?z_:t<-1?-z_:Math.asin(t)}function yr(t){return(t=H_(t/2))*t}function mr(){}function xr(t,n){t&&Z_.hasOwnProperty(t.type)&&Z_[t.type](t,n)}function br(t,n,e){var r,i=-1,o=t.length-e;for(n.lineStart();++i=0?1:-1,i=r*e,o=F_(n),u=H_(n),a=t_*u,c=Kv*o+a*F_(i),s=a*r*H_(i);J_.add(O_(s,c)),Qv=t,Kv=o,t_=u}function Nr(t){return[O_(t[1],t[0]),gr(t[2])]}function Er(t){var n=t[0],e=t[1],r=F_(e);return[r*F_(n),r*H_(n),H_(e)]}function Ar(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function Cr(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function zr(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function Pr(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function Rr(t){var n=V_(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}function Lr(t,n){s_.push(f_=[n_=t,r_=t]),ni_&&(i_=n)}function qr(t,n){var e=Er([t*q_,n*q_]);if(c_){var r=Cr(c_,e),i=[r[1],-r[0],0],o=Cr(i,r);Rr(o),o=Nr(o);var u,a=t-o_,c=a>0?1:-1,s=o[0]*L_*c,f=U_(a)>180;f^(c*o_i_&&(i_=u)):(s=(s+360)%360-180,f^(c*o_i_&&(i_=n))),f?tYr(n_,r_)&&(r_=t):Yr(t,r_)>Yr(n_,r_)&&(n_=t):r_>=n_?(tr_&&(r_=t)):t>o_?Yr(n_,t)>Yr(n_,r_)&&(r_=t):Yr(t,r_)>Yr(n_,r_)&&(n_=t)}else s_.push(f_=[n_=t,r_=t]);ni_&&(i_=n),c_=e,o_=t}function Ur(){eg.point=qr}function Dr(){f_[0]=n_,f_[1]=r_,eg.point=Lr,c_=null}function Or(t,n){if(c_){var e=t-o_;ng.add(U_(e)>180?e+(e>0?360:-360):e)}else u_=t,a_=n;K_.point(t,n),qr(t,n)}function Fr(){K_.lineStart()}function Ir(){Or(u_,a_),K_.lineEnd(),U_(ng)>E_&&(n_=-(r_=180)),f_[0]=n_,f_[1]=r_,c_=null}function Yr(t,n){return(n-=t)<0?n+360:n}function Br(t,n){return t[0]-n[0]}function jr(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nC_?t-R_:t<-C_?t+R_:t,n]}function ni(t,n,e){return(t%=R_)?n||e?ag(ri(t),ii(n,e)):ri(t):n||e?ii(n,e):ti}function ei(t){return function(n,e){return n+=t,[n>C_?n-R_:n<-C_?n+R_:n,e]}}function ri(t){var n=ei(t);return n.invert=ei(-t),n}function ii(t,n){function e(t,n){var e=F_(n),a=F_(t)*e,c=H_(t)*e,s=H_(n),f=s*r+a*i;return[O_(c*o-f*u,a*r-s*i),gr(f*o+c*u)]}var r=F_(t),i=H_(t),o=F_(n),u=H_(n);return e.invert=function(t,n){var e=F_(n),a=F_(t)*e,c=H_(t)*e,s=H_(n),f=s*o-c*u;return[O_(c*o+s*u,a*r+f*i),gr(f*r-a*i)]},e}function oi(t,n,e,r,i,o){if(e){var u=F_(n),a=H_(n),c=r*e;null==i?(i=n+r*R_,o=n-c/2):(i=ui(u,i),o=ui(u,o),(r>0?io)&&(i+=r*R_));for(var s,f=i;r>0?f>o:f0){do s.point(0===f||3===f?t:e,f>1?r:n);while((f=(f+a+4)%4)!==l)}else s.point(o[0],o[1])}function u(r,i){return U_(r[0]-t)0?0:3:U_(r[0]-e)0?2:1:U_(r[1]-n)0?1:0:i>0?3:2}function a(t,n){return c(t.x,n.x)}function c(t,n){var e=u(t,1),r=u(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(u){function c(t,n){i(t,n)&&S.point(t,n)}function s(){for(var n=0,e=0,i=_.length;er&&(l-o)*(r-u)>(h-u)*(t-o)&&++n:h<=r&&(l-o)*(r-u)<(h-u)*(t-o)&&--n;return n}function f(){S=N,v=[],_=[],k=!0}function l(){var t=s(),n=k&&t,e=(v=hf(v)).length;(n||e)&&(u.polygonStart(),n&&(u.lineStart(),o(null,null,1,u),u.lineEnd()),e&&Tg(v,a,t,o,u),u.polygonEnd()),S=u,v=_=g=null}function h(){E.point=d,_&&_.push(g=[]),T=!0,M=!1,b=w=NaN}function p(){v&&(d(y,m),x&&M&&N.rejoin(),v.push(N.result())),E.point=c,M&&S.lineEnd()}function d(o,u){var a=i(o,u);if(_&&g.push([o,u]),T)y=o,m=u,x=a,T=!1,a&&(S.lineStart(),S.point(o,u));else if(a&&M)S.point(o,u);else{var c=[b=Math.max(Sg,Math.min(kg,b)),w=Math.max(Sg,Math.min(kg,w))],s=[o=Math.max(Sg,Math.min(kg,o)),u=Math.max(Sg,Math.min(kg,u))];wg(c,s,t,n,e,r)?(M||(S.lineStart(),S.point(c[0],c[1])),S.point(s[0],s[1]),a||S.lineEnd(),k=!1):a&&(S.lineStart(),S.point(o,u),k=!1)}b=o,w=u,M=a}var v,_,g,y,m,x,b,w,M,T,k,S=u,N=bg(),E={point:c,lineStart:h,lineEnd:p,polygonStart:f,polygonEnd:l};return E}}function fi(){zg.point=hi,zg.lineEnd=li}function li(){zg.point=zg.lineEnd=mr}function hi(t,n){t*=q_,n*=q_,cg=t,sg=H_(n),fg=F_(n),zg.point=pi}function pi(t,n){t*=q_,n*=q_;var e=H_(n),r=F_(n),i=U_(t-cg),o=F_(i),u=H_(i),a=r*u,c=fg*e-sg*r*o,s=sg*e+fg*r*o;Cg.add(O_(V_(a*a+c*c),s)),cg=t,sg=e,fg=r}function di(t,n){return!(!t||!Dg.hasOwnProperty(t.type))&&Dg[t.type](t,n)}function vi(t,n){return 0===qg(t,n)}function _i(t,n){var e=qg(t[0],t[1]);return qg(t[0],n)+qg(n,t[1])<=e+E_}function gi(t,n){return!!Ag(t.map(yi),mi(n))}function yi(t){return t=t.map(mi),t.pop(),t}function mi(t){return[t[0]*q_,t[1]*q_]}function xi(t,n,e){var r=Qs(t,n-E_,e).concat(n);return function(t){return r.map(function(n){return[t,n]})}}function bi(t,n,e){var r=Qs(t,n-E_,e).concat(n);return function(t){return r.map(function(n){return[n,t]})}}function wi(){function t(){return{type:"MultiLineString",coordinates:n()}}function n(){return Qs(I_(o/_)*_,i,_).map(h).concat(Qs(I_(s/g)*g,c,g).map(p)).concat(Qs(I_(r/d)*d,e,d).filter(function(t){return U_(t%_)>E_}).map(f)).concat(Qs(I_(a/v)*v,u,v).filter(function(t){return U_(t%g)>E_}).map(l))}var e,r,i,o,u,a,c,s,f,l,h,p,d=10,v=d,_=90,g=360,y=2.5;return t.lines=function(){return n().map(function(t){return{type:"LineString",coordinates:t}})},t.outline=function(){return{type:"Polygon",coordinates:[h(o).concat(p(c).slice(1),h(i).reverse().slice(1),p(s).reverse().slice(1))]}},t.extent=function(n){return arguments.length?t.extentMajor(n).extentMinor(n):t.extentMinor()},t.extentMajor=function(n){return arguments.length?(o=+n[0][0],i=+n[1][0],s=+n[0][1],c=+n[1][1],o>i&&(n=o,o=i,i=n),s>c&&(n=s,s=c,c=n),t.precision(y)):[[o,s],[i,c]]},t.extentMinor=function(n){return arguments.length?(r=+n[0][0],e=+n[1][0],a=+n[0][1],u=+n[1][1],r>e&&(n=r,r=e,e=n),a>u&&(n=a,a=u,u=n),t.precision(y)):[[r,a],[e,u]]},t.step=function(n){return arguments.length?t.stepMajor(n).stepMinor(n):t.stepMinor()},t.stepMajor=function(n){return arguments.length?(_=+n[0],g=+n[1],t):[_,g]},t.stepMinor=function(n){return arguments.length?(d=+n[0],v=+n[1],t):[d,v]},t.precision=function(n){return arguments.length?(y=+n,f=xi(a,u,90),l=bi(r,e,y),h=xi(s,c,90),p=bi(o,i,y),t):y},t.extentMajor([[-180,-90+E_],[180,90-E_]]).extentMinor([[-180,-80-E_],[180,80+E_]])}function Mi(){return wi()()}function Ti(){jg.point=ki}function ki(t,n){jg.point=Si,lg=pg=t,hg=dg=n}function Si(t,n){Bg.add(dg*t-pg*n),pg=t,dg=n}function Ni(){Si(lg,hg)}function Ei(t,n){tVg&&(Vg=t),nWg&&(Wg=n)}function Ai(t,n){Zg+=t,Gg+=n,++Jg}function Ci(){iy.point=zi}function zi(t,n){iy.point=Pi,Ai(gg=t,yg=n)}function Pi(t,n){var e=t-gg,r=n-yg,i=V_(e*e+r*r);Qg+=i*(gg+t)/2,Kg+=i*(yg+n)/2,ty+=i,Ai(gg=t,yg=n)}function Ri(){iy.point=Ai}function Li(){iy.point=Ui}function qi(){Di(vg,_g)}function Ui(t,n){iy.point=Di,Ai(vg=gg=t,_g=yg=n)}function Di(t,n){var e=t-gg,r=n-yg,i=V_(e*e+r*r);Qg+=i*(gg+t)/2,Kg+=i*(yg+n)/2,ty+=i,i=yg*t-gg*n,ny+=i*(gg+t),ey+=i*(yg+n),ry+=3*i,Ai(gg=t,yg=n)}function Oi(t){this._context=t}function Fi(t,n){ly.point=Ii,uy=cy=t,ay=sy=n}function Ii(t,n){cy-=t,sy-=n,fy.add(V_(cy*cy+sy*sy)),cy=t,sy=n}function Yi(){this._string=[]}function Bi(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function ji(t){return t.length>1}function Hi(t,n){return((t=t.x)[0]<0?t[1]-z_-E_:z_-t[1])-((n=n.x)[0]<0?n[1]-z_-E_:z_-n[1])}function Xi(t){var n,e=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),n=1},point:function(o,u){var a=o>0?C_:-C_,c=U_(o-e);U_(c-C_)0?z_:-z_),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(a,r),t.point(o,r),n=0):i!==a&&c>=C_&&(U_(e-i)E_?D_((H_(n)*(o=F_(r))*H_(e)-H_(r)*(i=F_(n))*H_(t))/(i*o*u)):(n+r)/2}function Wi(t,n,e,r){var i;if(null==t)i=e*z_,r.point(-C_,i),r.point(0,i),r.point(C_,i),r.point(C_,0),r.point(C_,-i),r.point(0,-i),r.point(-C_,-i),r.point(-C_,0),r.point(-C_,i);else if(U_(t[0]-n[0])>E_){var o=t[0]4*n&&v--){var x=u+h,b=a+p,w=c+d,M=V_(x*x+b*b+w*w),T=gr(w/=M),k=U_(U_(w)-1)n||U_((g*A+y*C)/m-.5)>.3||u*h+a*p+c*d2?t[2]%360*q_:0,i()):[b*L_,w*L_,M*L_]},n.precision=function(t){return arguments.length?(A=my(r,E=t*t),o()):V_(E)},n.fitExtent=function(t,e){return Gi(n,t,e)},n.fitSize=function(t,e){return Ji(n,t,e)},function(){return u=t.apply(this,arguments),n.invert=u.invert&&e,i()}}function eo(t){var n=0,e=C_/3,r=no(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*q_,e=t[1]*q_):[n*L_,e*L_]},i}function ro(t){function n(t,n){return[t*e,H_(n)/e]}var e=F_(t);return n.invert=function(t,n){return[t/e,gr(n*e)]},n}function io(t,n){function e(t,n){var e=V_(o-2*i*H_(n))/i;return[e*H_(t*=i),u-e*F_(t)]}var r=H_(t),i=(r+H_(n))/2;if(U_(i)0?n<-z_+E_&&(n=-z_+E_):n>z_-E_&&(n=z_-E_);var e=o/j_(fo(n),i);return[e*H_(i*t),o-e*F_(i*t)]}var r=F_(t),i=t===n?H_(t):B_(r/F_(n))/B_(fo(n)/fo(t)),o=r*j_(fo(t),i)/i;return i?(e.invert=function(t,n){var e=o-n,r=X_(i)*V_(t*t+e*e);return[O_(t,U_(e))/i*X_(e),2*D_(j_(o/r,1/i))-z_]},e):co}function ho(t,n){return[t,n]}function po(t,n){function e(t,n){var e=o-n,r=i*t;return[e*H_(r),o-e*F_(r)]}var r=F_(t),i=t===n?H_(t):(r-F_(n))/(n-t),o=r/i+t;return U_(i)=0;)n+=e[r].value;else n=1;t.value=n}function Eo(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;for(t=e.pop(),n=r.pop();t===n;)i=t,t=e.pop(),n=r.pop();return i}function Ao(t,n){var e,r,i,o,u,a=new Lo(t),c=+t.value&&(a.value=t.value),s=[a];for(null==n&&(n=zo);e=s.pop();)if(c&&(e.value=+e.data.value),(i=n(e.data))&&(u=i.length))for(e.children=new Array(u),o=u-1;o>=0;--o)s.push(r=e.children[o]=new Lo(i[o])),r.parent=e,r.depth=e.depth+1;return a.eachBefore(Ro)}function Co(){return Ao(this).eachBefore(Po)}function zo(t){return t.children}function Po(t){t.data=t.data.data}function Ro(t){var n=0;do t.height=n;while((t=t.parent)&&t.height<++n)}function Lo(t){this.data=t,this.depth=this.height=0,this.parent=null}function qo(t){this._=t,this.next=null}function Uo(t,n){var e=n.x-t.x,r=n.y-t.y,i=t.r-n.r;return i*i+1e-6>e*e+r*r}function Do(t,n){var e,r,i,o=null,u=t.head;switch(n.length){case 1:e=Oo(n[0]);break;case 2:e=Fo(n[0],n[1]);break;case 3:e=Io(n[0],n[1],n[2])}for(;u;)i=u._,r=u.next,e&&Uo(e,i)?o=u:(o?(t.tail=o,o.next=null):t.head=t.tail=null,n.push(i),e=Do(t,n),n.pop(),t.head?(u.next=t.head,t.head=u):(u.next=null,t.head=t.tail=u),o=t.tail,o.next=r),u=r;return t.tail=o,e}function Oo(t){return{x:t.x,y:t.y,r:t.r}}function Fo(t,n){var e=t.x,r=t.y,i=t.r,o=n.x,u=n.y,a=n.r,c=o-e,s=u-r,f=a-i,l=Math.sqrt(c*c+s*s);return{x:(e+o+c/l*f)/2,y:(r+u+s/l*f)/2,r:(l+i+a)/2}}function Io(t,n,e){var r=t.x,i=t.y,o=t.r,u=n.x,a=n.y,c=n.r,s=e.x,f=e.y,l=e.r,h=2*(r-u),p=2*(i-a),d=2*(c-o),v=r*r+i*i-o*o-u*u-a*a+c*c,_=2*(r-s),g=2*(i-f),y=2*(l-o),m=r*r+i*i-o*o-s*s-f*f+l*l,x=_*p-h*g,b=(p*m-g*v)/x-r,w=(g*d-p*y)/x,M=(_*v-h*m)/x-i,T=(h*y-_*d)/x,k=w*w+T*T-1,S=2*(b*w+M*T+o),N=b*b+M*M-o*o,E=(-S-Math.sqrt(S*S-4*k*N))/(2*k);return{x:b+w*E+r,y:M+T*E+i,r:E}}function Yo(t,n,e){var r=t.x,i=t.y,o=n.r+e.r,u=t.r+e.r,a=n.x-r,c=n.y-i,s=a*a+c*c;if(s){var f=.5+((u*=u)-(o*=o))/(2*s),l=Math.sqrt(Math.max(0,2*o*(u+s)-(u-=s)*u-o*o))/(2*s);e.x=r+f*a+l*c,e.y=i+f*c-l*a}else e.x=r+u,e.y=i}function Bo(t,n){var e=n.x-t.x,r=n.y-t.y,i=t.r+n.r;return i*i-1e-6>e*e+r*r}function jo(t,n){for(var e=t._.r;t!==n;)e+=2*(t=t.next)._.r;return e-n._.r}function Ho(t,n,e){var r=t._,i=t.next._,o=r.r+i.r,u=(r.x*i.r+i.x*r.r)/o-n,a=(r.y*i.r+i.y*r.r)/o-e;return u*u+a*a}function Xo(t){this._=t,this.next=null,this.previous=null}function Vo(t){if(!(i=t.length))return 0;var n,e,r,i;if(n=t[0],n.x=0,n.y=0,!(i>1))return n.r;if(e=t[1],n.x=-e.r,e.x=n.r,e.y=0,!(i>2))return n.r+e.r;Yo(e,n,r=t[2]);var o,u,a,c,s,f,l,h=n.r*n.r,p=e.r*e.r,d=r.r*r.r,v=h+p+d,_=h*n.x+p*e.x+d*r.x,g=h*n.y+p*e.y+d*r.y;n=new Xo(n),e=new Xo(e),r=new Xo(r),n.next=r.previous=e,e.next=n.previous=r,r.next=e.previous=n;t:for(a=3;ajo(c,e)?n=c:e=c,n.next=e,e.previous=n,--a;continue t}f+=c._.r,c=c.next}else{if(Bo(s._,r._)){jo(n,s)>l+n._.r+e._.r?n=s:e=s,n.next=e,e.previous=n,--a;continue t}l+=s._.r,s=s.previous}while(c!==s.next);for(r.previous=n,r.next=e,n.next=e.previous=e=r,v+=d=r._.r*r._.r,_+=d*r._.x,g+=d*r._.y,h=Ho(n,o=_/v,u=g/v);(r=r.next)!==e;)(d=Ho(r,o,u))=0;)n=i[o],n.z+=e,n.m+=e,e+=n.s+(r+=n.c)}function au(t,n,e){return t.a.parent===n.parent?t.a:e}function cu(t,n){this._=t,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=n}function su(t){for(var n,e,r,i,o,u=new cu(t,0),a=[u];n=a.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)a.push(e=n.children[i]=new cu(r[i],i)),e.parent=n;return(u.parent=new cu(null,0)).children=[u],u}function fu(t,n,e,r,i,o){for(var u,a,c,s,f,l,h,p,d,v,_,g=[],y=n.children,m=0,x=0,b=y.length,w=n.value;mh&&(h=a),_=f*f*v,p=Math.max(h/_,_/l),p>d){f-=a;break}d=p}g.push(u={value:f,dice:c1&&gm(t[e[r-2]],t[e[r-1]],t[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function pu(t){if(!(t>=1))throw new Error;this._size=t,this._call=this._error=null,this._tasks=[],this._data=[],this._waiting=this._active=this._ended=this._start=0}function du(t){if(!t._start)try{vu(t)}catch(n){if(t._tasks[t._ended+t._active-1])gu(t,n);else if(!t._data)throw n}}function vu(t){for(;t._start=t._waiting&&t._active=0;)if((e=t._tasks[r])&&(t._tasks[r]=null,e.abort))try{e.abort()}catch(t){}t._active=NaN,yu(t)}function yu(t){if(!t._active&&t._call){var n=t._data;t._data=void 0,t._call(t._error,n)}}function mu(t){return new pu(arguments.length?+t:1/0)}function xu(t){return function(n,e){t(null==n?e:null)}}function bu(t){var n=t.responseType;return n&&"text"!==n?t.response:t.responseText}function wu(t,n){return function(e){return t(e.responseText,n)}}function Mu(t){function n(n){var o=n+"",u=e.get(o);if(!u){if(i!==Ym)return i;e.set(o,u=r.push(n))}return t[(u-1)%t.length]}var e=Ye(),r=[],i=Ym;return t=null==t?[]:Im.call(t),n.domain=function(t){if(!arguments.length)return r.slice();r=[],e=Ye();for(var i,o,u=-1,a=t.length;++u=e?1:r(t)}}}function Au(t){return function(n,e){var r=t(n=+n,e=+e);return function(t){return t<=0?n:t>=1?e:r(t)}}}function Cu(t,n,e,r){var i=t[0],o=t[1],u=n[0],a=n[1];return o2?zu:Cu,o=u=null,r}function r(n){return(o||(o=i(a,c,f?Eu(t):t,s)))(+n)}var i,o,u,a=Hm,c=Hm,s=Ah,f=!1;return r.invert=function(t){return(u||(u=i(c,a,Nu,f?Au(n):n)))(+t)},r.domain=function(t){return arguments.length?(a=Fm.call(t,jm),e()):a.slice()},r.range=function(t){return arguments.length?(c=Im.call(t),e()):c.slice()},r.rangeRound=function(t){return c=Im.call(t),s=Ch,e()},r.clamp=function(t){return arguments.length?(f=!!t,e()):f},r.interpolate=function(t){return arguments.length?(s=t,e()):s},e()}function Lu(t){var n=t.domain;return t.ticks=function(t){var e=n();return ef(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){return Xm(n(),t,e)},t.nice=function(e){var i=n(),o=i.length-1,u=null==e?10:e,a=i[0],c=i[o],s=r(a,c,u);return s&&(s=r(Math.floor(a/s)*s,Math.ceil(c/s)*s,u),i[0]=Math.floor(a/s)*s,i[o]=Math.ceil(c/s)*s,n(i)),t},t}function qu(){var t=Ru(Nu,Th);return t.copy=function(){return Pu(t,qu())},Lu(t)}function Uu(){function t(t){return+t}var n=[0,1];return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=Fm.call(e,jm),t):n.slice()},t.copy=function(){return Uu().domain(n)},Lu(t)}function Du(t,n){return(n=Math.log(n/t))?function(e){return Math.log(e/t)/n}:Bm(n)}function Ou(t,n){return t<0?function(e){return-Math.pow(-n,e)*Math.pow(-t,1-e)}:function(e){return Math.pow(n,e)*Math.pow(t,1-e)}}function Fu(t){return isFinite(t)?+("1e"+t):t<0?0:t}function Iu(t){return 10===t?Fu:t===Math.E?Math.exp:function(n){return Math.pow(t,n)}}function Yu(t){return t===Math.E?Math.log:10===t&&Math.log10||2===t&&Math.log2||(t=Math.log(t),function(n){return Math.log(n)/t})}function Bu(t){return function(n){return-t(-n)}}function ju(){function n(){return o=Yu(i),u=Iu(i),r()[0]<0&&(o=Bu(o),u=Bu(u)),e}var e=Ru(Du,Ou).domain([1,10]),r=e.domain,i=10,o=Yu(10),u=Iu(10);return e.base=function(t){return arguments.length?(i=+t,n()):i},e.domain=function(t){return arguments.length?(r(t),n()):r()},e.ticks=function(t){var n,e=r(),a=e[0],c=e[e.length-1];(n=c0){for(;hc)break;v.push(l)}}else for(;h=1;--f)if(l=s*f,!(lc)break;v.push(l)}}else v=ef(h,p,Math.min(p-h,d)).map(u);return n?v.reverse():v},e.tickFormat=function(n,r){if(null==r&&(r=10===i?".0e":","),"function"!=typeof r&&(r=t.format(r)),n===1/0)return r;null==n&&(n=10);var a=Math.max(1,i*n/e.ticks().length);return function(t){var n=t/u(Math.round(o(t)));return n*i0?i[n-1]:e[0],n=i?[o[i-1],r]:[o[n-1],o[n]]},t.copy=function(){return $u().domain([e,r]).range(u)},Lu(t)}function Zu(){function t(t){if(t<=t)return e[Os(n,t,0,r)]}var n=[.5],e=[0,1],r=1;return t.domain=function(i){return arguments.length?(n=Im.call(i),r=Math.min(n.length,e.length-1),t):n.slice()},t.range=function(i){return arguments.length?(e=Im.call(i),r=Math.min(n.length,e.length-1),t):e.slice()},t.invertExtent=function(t){var r=e.indexOf(t);return[n[r-1],n[r]]},t.copy=function(){return Zu().domain(n).range(e)},t}function Gu(t,n,e,r){function i(n){return t(n=new Date(+n)),n}return i.floor=i,i.ceil=function(e){return t(e=new Date(e-1)),n(e,1),t(e),e},i.round=function(t){var n=i(t),e=i.ceil(t);return t-n0))return u;do u.push(new Date(+e));while(n(e,o),t(e),e=n)for(;t(n),!e(n);)n.setTime(n-1)},function(t,r){if(t>=t)for(;--r>=0;)for(;n(t,1),!e(t););})},e&&(i.count=function(n,r){return Wm.setTime(+n),$m.setTime(+r),t(Wm),t($m),Math.floor(e(Wm,$m))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(n){return r(n)%t===0}:function(n){return i.count(0,n)%t===0}):i:null}),i}function Ju(t){return Gu(function(n){n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+7*n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Qm)/nx})}function Qu(t){return Gu(function(n){n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+7*n)},function(t,n){return(n-t)/nx})}function Ku(t){if(0<=t.y&&t.y<100){var n=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return n.setFullYear(t.y),n}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function ta(t){if(0<=t.y&&t.y<100){var n=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return n.setUTCFullYear(t.y),n}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function na(t){return{y:t,m:0,d:1,H:0,M:0,S:0,L:0}}function ea(t){function n(t,n){return function(e){var r,i,o,u=[],a=-1,c=0,s=t.length;for(e instanceof Date||(e=new Date(+e));++a=c)return-1;if(i=n.charCodeAt(u++),37===i){if(i=n.charAt(u++),o=B[i in Kx?n.charAt(u++):i],!o||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function i(t,n,e){var r=C.exec(n.slice(e));return r?(t.p=z[r[0].toLowerCase()],e+r[0].length):-1}function o(t,n,e){var r=L.exec(n.slice(e));return r?(t.w=q[r[0].toLowerCase()],e+r[0].length):-1}function u(t,n,e){var r=P.exec(n.slice(e));return r?(t.w=R[r[0].toLowerCase()],e+r[0].length):-1}function a(t,n,e){var r=O.exec(n.slice(e));return r?(t.m=F[r[0].toLowerCase()],e+r[0].length):-1}function c(t,n,e){var r=U.exec(n.slice(e));return r?(t.m=D[r[0].toLowerCase()],e+r[0].length):-1}function s(t,n,e){return r(t,w,n,e)}function f(t,n,e){return r(t,M,n,e)}function l(t,n,e){return r(t,T,n,e)}function h(t){return N[t.getDay()]}function p(t){return S[t.getDay()]}function d(t){return A[t.getMonth()]}function v(t){return E[t.getMonth()]}function _(t){return k[+(t.getHours()>=12)]}function g(t){return N[t.getUTCDay()]}function y(t){return S[t.getUTCDay()]}function m(t){return A[t.getUTCMonth()]}function x(t){return E[t.getUTCMonth()]}function b(t){return k[+(t.getUTCHours()>=12)]}var w=t.dateTime,M=t.date,T=t.time,k=t.periods,S=t.days,N=t.shortDays,E=t.months,A=t.shortMonths,C=oa(k),z=ua(k),P=oa(S),R=ua(S),L=oa(N),q=ua(N),U=oa(E),D=ua(E),O=oa(A),F=ua(A),I={a:h,A:p,b:d,B:v,c:null,d:ba,e:ba,H:wa,I:Ma,j:Ta,L:ka,m:Sa,M:Na,p:_,S:Ea,U:Aa,w:Ca,W:za,x:null,X:null,y:Pa,Y:Ra,Z:La,"%":Za},Y={a:g,A:y,b:m,B:x,c:null,d:qa,e:qa,H:Ua,I:Da,j:Oa,L:Fa,m:Ia,M:Ya,p:b,S:Ba,U:ja,w:Ha,W:Xa,x:null,X:null,y:Va,Y:Wa,Z:$a,"%":Za},B={a:o,A:u,b:a,B:c,c:s,d:da,e:da,H:_a,I:_a,j:va,L:ma,m:pa,M:ga,p:i,S:ya,U:ca,w:aa,W:sa,x:f,X:l,y:la,Y:fa,Z:ha,"%":xa};return I.x=n(M,I),I.X=n(T,I),I.c=n(w,I),Y.x=n(M,Y),Y.X=n(T,Y),Y.c=n(w,Y),{format:function(t){var e=n(t+="",I);return e.toString=function(){return t},e},parse:function(t){var n=e(t+="",Ku);return n.toString=function(){return t},n},utcFormat:function(t){var e=n(t+="",Y);return e.toString=function(){return t},e},utcParse:function(t){var n=e(t,ta);return n.toString=function(){return t},n}}}function ra(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o68?1900:2e3),e+r[0].length):-1}function ha(t,n,e){var r=/^(Z)|([+-]\d\d)(?:\:?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function pa(t,n,e){var r=tb.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function da(t,n,e){var r=tb.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function va(t,n,e){var r=tb.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function _a(t,n,e){var r=tb.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function ga(t,n,e){var r=tb.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function ya(t,n,e){var r=tb.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function ma(t,n,e){var r=tb.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function xa(t,n,e){var r=nb.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function ba(t,n){return ra(t.getDate(),n,2)}function wa(t,n){return ra(t.getHours(),n,2)}function Ma(t,n){return ra(t.getHours()%12||12,n,2)}function Ta(t,n){return ra(1+cx.count(Sx(t),t),n,3)}function ka(t,n){return ra(t.getMilliseconds(),n,3)}function Sa(t,n){return ra(t.getMonth()+1,n,2)}function Na(t,n){return ra(t.getMinutes(),n,2)}function Ea(t,n){return ra(t.getSeconds(),n,2)}function Aa(t,n){return ra(fx.count(Sx(t),t),n,2)}function Ca(t){return t.getDay()}function za(t,n){return ra(lx.count(Sx(t),t),n,2)}function Pa(t,n){return ra(t.getFullYear()%100,n,2)}function Ra(t,n){return ra(t.getFullYear()%1e4,n,4)}function La(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+ra(n/60|0,"0",2)+ra(n%60,"0",2)}function qa(t,n){return ra(t.getUTCDate(),n,2)}function Ua(t,n){return ra(t.getUTCHours(),n,2)}function Da(t,n){return ra(t.getUTCHours()%12||12,n,2)}function Oa(t,n){return ra(1+Px.count(Gx(t),t),n,3)}function Fa(t,n){return ra(t.getUTCMilliseconds(),n,3)}function Ia(t,n){return ra(t.getUTCMonth()+1,n,2)}function Ya(t,n){return ra(t.getUTCMinutes(),n,2)}function Ba(t,n){return ra(t.getUTCSeconds(),n,2)}function ja(t,n){return ra(Lx.count(Gx(t),t),n,2)}function Ha(t){return t.getUTCDay()}function Xa(t,n){return ra(qx.count(Gx(t),t),n,2)}function Va(t,n){return ra(t.getUTCFullYear()%100,n,2)}function Wa(t,n){return ra(t.getUTCFullYear()%1e4,n,4)}function $a(){return"+0000"}function Za(){return"%"}function Ga(n){return Jx=ea(n),t.timeFormat=Jx.format,t.timeParse=Jx.parse,t.utcFormat=Jx.utcFormat,t.utcParse=Jx.utcParse,Jx}function Ja(t){return t.toISOString()}function Qa(t){var n=new Date(t);return isNaN(n)?null:n}function Ka(t){return new Date(t)}function tc(t){return t instanceof Date?+t:+new Date(+t)}function nc(t,n,e,i,o,u,a,c,s){function f(r){return(a(r)1?0:t<-1?Ob:Math.acos(t)}function oc(t){return t>=1?Fb:t<=-1?-Fb:Math.asin(t)}function uc(t){return t.innerRadius}function ac(t){return t.outerRadius}function cc(t){return t.startAngle}function sc(t){return t.endAngle}function fc(t){return t&&t.padAngle}function lc(t,n,e,r,i,o,u,a){var c=e-t,s=r-n,f=u-i,l=a-o,h=(f*(n-o)-l*(t-i))/(l*c-f*s);return[t+h*c,n+h*s]}function hc(t,n,e,r,i,o,u){var a=t-e,c=n-r,s=(u?o:-o)/Ub(a*a+c*c),f=s*c,l=-s*a,h=t+f,p=n+l,d=e+f,v=r+l,_=(h+d)/2,g=(p+v)/2,y=d-h,m=v-p,x=y*y+m*m,b=i-o,w=h*v-d*p,M=(m<0?-1:1)*Ub(Rb(0,b*b*x-w*w)),T=(w*m-y*M)/x,k=(-w*y-m*M)/x,S=(w*m+y*M)/x,N=(-w*y+m*M)/x,E=T-_,A=k-g,C=S-_,z=N-g;return E*E+A*A>C*C+z*z&&(T=S,k=N),{cx:T,cy:k,x01:-f,y01:-l,x11:T*(i/b-1),y11:k*(i/b-1)}}function pc(t){this._context=t}function dc(t){return t[0]}function vc(t){return t[1]}function _c(t){this._curve=t}function gc(t){function n(n){return new _c(t(n))}return n._curve=t,n}function yc(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n(gc(t)):n()._curve},t}function mc(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function xc(t){this._context=t}function bc(t){this._context=t}function wc(t){this._context=t}function Mc(t,n){this._basis=new xc(t),this._beta=n}function Tc(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function kc(t,n){this._context=t,this._k=(1-n)/6}function Sc(t,n){this._context=t,this._k=(1-n)/6}function Nc(t,n){this._context=t,this._k=(1-n)/6}function Ec(t,n,e){var r=t._x1,i=t._y1,o=t._x2,u=t._y2;if(t._l01_a>Db){var a=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*a-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*a-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>Db){var s=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,f=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*s+t._x1*t._l23_2a-n*t._l12_2a)/f,u=(u*s+t._y1*t._l23_2a-e*t._l12_2a)/f}t._context.bezierCurveTo(r,i,o,u,t._x2,t._y2)}function Ac(t,n){this._context=t,this._alpha=n}function Cc(t,n){this._context=t,this._alpha=n}function zc(t,n){this._context=t,this._alpha=n}function Pc(t){this._context=t}function Rc(t){return t<0?-1:1}function Lc(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),u=(e-t._y1)/(i||r<0&&-0),a=(o*i+u*r)/(r+i);return(Rc(o)+Rc(u))*Math.min(Math.abs(o),Math.abs(u),.5*Math.abs(a))||0}function qc(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function Uc(t,n,e){var r=t._x0,i=t._y0,o=t._x1,u=t._y1,a=(o-r)/3;t._context.bezierCurveTo(r+a,i+a*n,o-a,u-a*e,o,u)}function Dc(t){this._context=t}function Oc(t){this._context=new Fc(t)}function Fc(t){this._context=t}function Ic(t){return new Dc(t)}function Yc(t){return new Oc(t)}function Bc(t){this._context=t}function jc(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),u=new Array(r);for(i[0]=0,o[0]=2,u[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(u[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n0)){if(o/=d,d<0){if(o0){if(o>p)return;o>h&&(h=o)}if(o=r-c,d||!(o<0)){if(o/=d,d<0){if(o>p)return;o>h&&(h=o)}else if(d>0){if(o0)){if(o/=v,v<0){if(o0){if(o>p)return;o>h&&(h=o)}if(o=i-s,v||!(o<0)){if(o/=v,v<0){if(o>p)return;o>h&&(h=o)}else if(v>0){if(o0||p<1)||(h>0&&(t[0]=[c+h*d,s+h*v]),p<1&&(t[1]=[c+p*d,s+p*v]),!0)}}}}}function us(t,n,e,r,i){var o=t[1];if(o)return!0;var u,a,c=t[0],s=t.left,f=t.right,l=s[0],h=s[1],p=f[0],d=f[1],v=(l+p)/2,_=(h+d)/2;if(d===h){if(v=r)return;if(l>p){if(c){if(c[1]>=i)return}else c=[v,e];o=[v,i]}else{if(c){if(c[1]1)if(l>p){if(c){if(c[1]>=i)return}else c=[(e-a)/u,e];o=[(i-a)/u,i]}else{if(c){if(c[1]=r)return}else c=[n,u*n+a];o=[r,u*r+a]}else{if(c){if(c[0]Gw||Math.abs(i[0][1]-i[1][1])>Gw)||delete Ww[o]}function cs(t){return Xw[t.index]={site:t,halfedges:[]}}function ss(t,n){var e=t.site,r=n.left,i=n.right;return e===i&&(i=r,r=e),i?Math.atan2(i[1]-r[1],i[0]-r[0]):(e===r?(r=n[1],i=n[0]):(r=n[0],i=n[1]),Math.atan2(r[0]-i[0],i[1]-r[1]))}function fs(t,n){return n[+(n.left!==t.site)]}function ls(t,n){return n[+(n.left===t.site)]}function hs(){for(var t,n,e,r,i=0,o=Xw.length;iGw||Math.abs(v-h)>Gw)&&(c.splice(a,0,Ww.push(rs(u,p,Math.abs(d-t)Gw?[t,Math.abs(l-t)Gw?[Math.abs(h-r)Gw?[e,Math.abs(l-e)Gw?[Math.abs(h-n)=-Jw)){var p=c*c+s*s,d=f*f+l*l,v=(l*p-s*d)/h,_=(c*d-f*p)/h,g=$w.pop()||new ds;g.arc=t,g.site=i,g.x=v+u,g.y=(g.cy=_+a)+Math.sqrt(v*v+_*_),t.circle=g;for(var y=null,m=Vw._;m;)if(g.yGw)a=a.L;else{if(i=o-Ms(a,u),!(i>Gw)){r>-Gw?(n=a.P,e=a):i>-Gw?(n=a,e=a.N):n=e=a;break}if(!a.R){n=a;break}a=a.R}cs(t);var c=ys(t);if(Hw.insert(n,c),n||e){if(n===e)return _s(n),e=ys(n.site),Hw.insert(c,e),c.edge=e.edge=es(n.site,c.site),vs(n),void vs(e);if(!e)return void(c.edge=es(n.site,c.site));_s(n),_s(e);var s=n.site,f=s[0],l=s[1],h=t[0]-f,p=t[1]-l,d=e.site,v=d[0]-f,_=d[1]-l,g=2*(h*_-p*v),y=h*h+p*p,m=v*v+_*_,x=[(_*y-p*m)/g+f,(h*m-v*y)/g+l];is(e.edge,s,d,x),c.edge=es(s,t,null,x),e.edge=es(t,d,null,x),vs(n),vs(e)}}function ws(t,n){var e=t.site,r=e[0],i=e[1],o=i-n;if(!o)return r;var u=t.P;if(!u)return-(1/0);e=u.site;var a=e[0],c=e[1],s=c-n;if(!s)return a;var f=a-r,l=1/o-1/s,h=f/s;return l?(-h+Math.sqrt(h*h-2*l*(f*f/(-2*s)-c+s/2+i-o/2)))/l+r:(r+a)/2}function Ms(t,n){var e=t.N;if(e)return ws(e,n);var r=t.site;return r[1]===n?r[0]:1/0}function Ts(t,n,e){return(t[0]-e[0])*(n[1]-t[1])-(t[0]-n[0])*(e[1]-t[1])}function ks(t,n){return n[1]-t[1]||n[0]-t[0]}function Ss(t,n){var e,r,i,o=t.sort(ks).pop();for(Ww=[],Xw=new Array(t.length),Hw=new Jc,Vw=new Jc;;)if(i=jw,o&&(!i||o[1]n?1:t>=n?0:NaN},Us=function(t){return 1===t.length&&(t=n(t)),{left:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r>>1;t(n[o],e)<0?r=o+1:i=o}return r},right:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r>>1;t(n[o],e)>0?i=o:r=o+1}return r}}},Ds=Us(qs),Os=Ds.right,Fs=Ds.left,Is=function(t,n){null==n&&(n=e);for(var r=0,i=t.length-1,o=t[0],u=new Array(i<0?0:i);rt?1:n>=t?0:NaN},js=function(t){return null===t?NaN:+t},Hs=function(t,n){var e,r,i=t.length,o=0,u=0,a=-1,c=0;if(null==n)for(;++a1)return u/(c-1)},Xs=function(t,n){var e=Hs(t,n);return e?Math.sqrt(e):e},Vs=function(t,n){var e,r,i,o=-1,u=t.length;if(null==n){for(;++o=r){e=i=r;break}for(;++or&&(e=r),i=r){e=i=r;break}for(;++or&&(e=r),i=f;)l.pop(),--h;var p,d=new Array(h+1);for(i=0;i<=h;++i)p=d[i]=[],p.x0=i>0?l[i-1]:s,p.x1=i=1)return+e(t[r-1],r-1,t);var r,i=(r-1)*n,o=Math.floor(i),u=+e(t[o],o,t);return u+(+e(t[o+1],o+1,t)-u)*(i-o)}},af=function(t,n,e){return t=Zs.call(t,js).sort(qs),Math.ceil((e-n)/(2*(uf(t,.75)-uf(t,.25))*Math.pow(t.length,-1/3)))},cf=function(t,n,e){return Math.ceil((e-n)/(3.5*Xs(t)*Math.pow(t.length,-1/3)))},sf=function(t,n){var e,r,i=-1,o=t.length;if(null==n){for(;++i=r){e=r;break}for(;++ie&&(e=r)}else{for(;++i=r){e=r;break}for(;++ie&&(e=r)}return e},ff=function(t,n){var e,r=0,i=t.length,o=-1,u=i;if(null==n)for(;++o=0;)for(r=t[i],n=r.length;--n>=0;)e[--u]=r[n];return e},pf=function(t,n){var e,r,i=-1,o=t.length;if(null==n){for(;++i=r){e=r;break}for(;++ir&&(e=r)}else{for(;++i=r){e=r;break}for(;++ir&&(e=r)}return e},df=function(t,n){for(var e=n.length,r=new Array(e);e--;)r[e]=t[n[e]];return r},vf=function(t,n){if(e=t.length){var e,r,i=0,o=0,u=t[o];for(n||(n=qs);++i0)for(var e,r,i=new Array(e),o=0;o=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),Af.hasOwnProperty(n)?{space:Af[n],local:t}:t},zf=function(t){var n=Cf(t);return(n.local?x:m)(n)},Pf=0;w.prototype=b.prototype={constructor:w,get:function(t){for(var n=this._;!(n in t);)if(!(t=t.parentNode))return;return t[n]},set:function(t,n){return t[this._]=n},remove:function(t){return this._ in t&&delete t[this._]},toString:function(){return this._}};var Rf=function(t){return function(){return this.matches(t)}};if("undefined"!=typeof document){var Lf=document.documentElement;if(!Lf.matches){var qf=Lf.webkitMatchesSelector||Lf.msMatchesSelector||Lf.mozMatchesSelector||Lf.oMatchesSelector;Rf=function(t){return function(){return qf.call(this,t)}}}}var Uf=Rf,Df={};if(t.event=null,"undefined"!=typeof document){"onmouseenter"in document.documentElement||(Df={mouseenter:"mouseover",mouseleave:"mouseout"})}var Of=function(t,n,e){var r,i,o=k(t+""),u=o.length;{if(!(arguments.length<2)){for(a=n?N:S,null==e&&(e=!1),r=0;r=x&&(x=m+1);!(y=_[x])&&++x=0;)(r=i[o])&&(u&&u!==r.nextSibling&&u.parentNode.insertBefore(r,u),u=r);return this},nl=function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=L);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?Y:"function"==typeof n?j:B)(t,n,null==e?"":e)):sl(r=this.node()).getComputedStyle(r,null).getPropertyValue(t)},ll=function(t,n){return arguments.length>1?this.each((null==n?H:"function"==typeof n?V:X)(t,n)):this.node()[t]};Z.prototype={add:function(t){this._names.indexOf(t)<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var n=this._names.indexOf(t);n>=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var hl=function(t,n){var e=W(t+"");if(arguments.length<2){for(var r=$(this.node()),i=-1,o=e.length;++i=240?t-240:t+120,i,r),Lt(t,i,r),Lt(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1}}));var Vl=Math.PI/180,Wl=180/Math.PI,$l=18,Zl=.95047,Gl=1,Jl=1.08883,Ql=4/29,Kl=6/29,th=3*Kl*Kl,nh=Kl*Kl*Kl;zl(Dt,Ut,wt(Mt,{brighter:function(t){return new Dt(this.l+$l*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new Dt(this.l-$l*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,n=isNaN(this.a)?t:t+this.a/500,e=isNaN(this.b)?t:t-this.b/200;return t=Gl*Ft(t),n=Zl*Ft(n),e=Jl*Ft(e),new At(It(3.2404542*n-1.5371385*t-.4985314*e),It(-.969266*n+1.8760108*t+.041556*e),It(.0556434*n-.2040259*t+1.0572252*e),this.opacity)}})),zl(Ht,jt,wt(Mt,{brighter:function(t){return new Ht(this.h,this.c,this.l+$l*(null==t?1:t),this.opacity)},darker:function(t){return new Ht(this.h,this.c,this.l-$l*(null==t?1:t),this.opacity)},rgb:function(){return qt(this).rgb()}}));var eh=-.14861,rh=1.78277,ih=-.29227,oh=-.90649,uh=1.97294,ah=uh*oh,ch=uh*rh,sh=rh*ih-oh*eh;zl(Wt,Vt,wt(Mt,{brighter:function(t){return t=null==t?Rl:Math.pow(Rl,t),new Wt(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Pl:Math.pow(Pl,t),new Wt(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=isNaN(this.h)?0:(this.h+120)*Vl,n=+this.l,e=isNaN(this.s)?0:this.s*n*(1-n),r=Math.cos(t),i=Math.sin(t);return new At(255*(n+e*(eh*r+rh*i)),255*(n+e*(ih*r+oh*i)),255*(n+e*(uh*r)),this.opacity)}}));var fh,lh,hh,ph,dh,vh,_h=function(t){var n=t.length-1;return function(e){var r=e<=0?e=0:e>=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1];return $t((e-r/n)*n,r>0?t[r-1]:2*i-o,i,o,ro&&(i=n.slice(o,i),a[u]?a[u]+=i:a[++u]=i),(e=e[0])===(r=r[0])?a[u]?a[u]+=r:a[++u]=r:(a[++u]=null,c.push({i:u,x:Th(e,r)})),o=Nh.lastIndex;return osp&&e.statecp&&e.name===n)return new Kn([[t]],hd,n,+r)}return null},dd=function(t){return function(){return t}},vd=function(t,n,e){this.target=t,this.type=n,this.selection=e},_d=function(){t.event.preventDefault(),t.event.stopImmediatePropagation()},gd={name:"drag"},yd={name:"space"},md={name:"handle"},xd={name:"center"},bd={name:"x",handles:["e","w"].map(Me),input:function(t,n){return t&&[[t[0],n[0][1]],[t[1],n[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},wd={name:"y",handles:["n","s"].map(Me),input:function(t,n){return t&&[[n[0][0],t[0]],[n[1][0],t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},Md={name:"xy",handles:["n","e","s","w","nw","ne","se","sw"].map(Me),input:function(t){return t},output:function(t){return t}},Td={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},kd={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},Sd={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},Nd={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},Ed={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1},Ad=function(){return ze(Md)},Cd=Math.cos,zd=Math.sin,Pd=Math.PI,Rd=Pd/2,Ld=2*Pd,qd=Math.max,Ud=function(){function t(t){var o,u,a,c,s,f,l=t.length,h=[],p=Qs(l),d=[],v=[],_=v.groups=new Array(l),g=new Array(l*l);for(o=0,s=-1;++sYd)if(Math.abs(f*a-c*s)>Yd&&i){var h=e-o,p=r-u,d=a*a+c*c,v=h*h+p*p,_=Math.sqrt(d),g=Math.sqrt(l),y=i*Math.tan((Fd-Math.acos((d+l-v)/(2*_*g)))/2),m=y/g,x=y/_;Math.abs(m-1)>Yd&&(this._+="L"+(t+m*s)+","+(n+m*f)),this._+="A"+i+","+i+",0,0,"+ +(f*h>s*p)+","+(this._x1=t+x*a)+","+(this._y1=n+x*c)}else this._+="L"+(this._x1=t)+","+(this._y1=n);else;},arc:function(t,n,e,r,i,o){t=+t,n=+n,e=+e;var u=e*Math.cos(r),a=e*Math.sin(r),c=t+u,s=n+a,f=1^o,l=o?r-i:i-r;if(e<0)throw new Error("negative radius: "+e);null===this._x1?this._+="M"+c+","+s:(Math.abs(this._x1-c)>Yd||Math.abs(this._y1-s)>Yd)&&(this._+="L"+c+","+s),e&&(l>Bd?this._+="A"+e+","+e+",0,1,"+f+","+(t-u)+","+(n-a)+"A"+e+","+e+",0,1,"+f+","+(this._x1=c)+","+(this._y1=s):(l<0&&(l=l%Id+Id),this._+="A"+e+","+e+",0,"+ +(l>=Fd)+","+f+","+(this._x1=t+e*Math.cos(i))+","+(this._y1=n+e*Math.sin(i))))},rect:function(t,n,e,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+n)+"h"+ +e+"v"+ +r+"h"+-e+"Z"},toString:function(){return this._}};var jd=function(){function t(){var t,a=Dd.call(arguments),c=n.apply(this,a),s=e.apply(this,a),f=+r.apply(this,(a[0]=c,a)),l=i.apply(this,a)-Rd,h=o.apply(this,a)-Rd,p=f*Cd(l),d=f*zd(l),v=+r.apply(this,(a[0]=s,a)),_=i.apply(this,a)-Rd,g=o.apply(this,a)-Rd;if(u||(u=t=Le()),u.moveTo(p,d),u.arc(0,0,f,l,h),l===_&&h===g||(u.quadraticCurveTo(0,0,v*Cd(_),v*zd(_)),u.arc(0,0,v,_,g)),u.quadraticCurveTo(0,0,p,d),u.closePath(),t)return u=null,t+""||null}var n=qe,e=Ue,r=De,i=Oe,o=Fe,u=null;return t.radius=function(n){return arguments.length?(r="function"==typeof n?n:Od(+n),t):r},t.startAngle=function(n){return arguments.length?(i="function"==typeof n?n:Od(+n),t):i},t.endAngle=function(n){return arguments.length?(o="function"==typeof n?n:Od(+n),t):o},t.source=function(e){return arguments.length?(n=e,t):n},t.target=function(n){return arguments.length?(e=n,t):e},t.context=function(n){return arguments.length?(u=null==n?null:n,t):u},t},Hd="$";Ie.prototype=Ye.prototype={constructor:Ie,has:function(t){return Hd+t in this},get:function(t){return this[Hd+t]},set:function(t,n){return this[Hd+t]=n,this},remove:function(t){var n=Hd+t;return n in this&&delete this[n]},clear:function(){for(var t in this)t[0]===Hd&&delete this[t]},keys:function(){var t=[];for(var n in this)n[0]===Hd&&t.push(n.slice(1));return t},values:function(){var t=[];for(var n in this)n[0]===Hd&&t.push(this[n]);return t},entries:function(){var t=[];for(var n in this)n[0]===Hd&&t.push({key:n.slice(1),value:this[n]});return t},size:function(){var t=0;for(var n in this)n[0]===Hd&&++t;return t},empty:function(){for(var t in this)if(t[0]===Hd)return!1;return!0},each:function(t){for(var n in this)n[0]===Hd&&t(this[n],n.slice(1),this)}};var Xd=function(){function t(n,i,u,a){if(i>=o.length)return null!=r?r(n):null!=e?n.sort(e):n;for(var c,s,f,l=-1,h=n.length,p=o[i++],d=Ye(),v=u();++lo.length)return t;var i,a=u[e-1];return null!=r&&e>=o.length?i=t.entries():(i=[],t.each(function(t,r){i.push({key:r,values:n(t,e)})})),null!=a?i.sort(function(t,n){return a(t.key,n.key)}):i}var e,r,i,o=[],u=[];return i={object:function(n){return t(n,0,Be,je)},map:function(n){return t(n,0,He,Xe)},entries:function(e){return n(t(e,0,He,Xe),0)},key:function(t){return o.push(t),i},sortKeys:function(t){return u[o.length-1]=t,i},sortValues:function(t){return e=t,i},rollup:function(t){return r=t,i}}},Vd=Ye.prototype;Ve.prototype=We.prototype={constructor:Ve,has:Vd.has,add:function(t){return t+="",this[Hd+t]=t,this},remove:Vd.remove,clear:Vd.clear,values:Vd.keys,size:Vd.size,empty:Vd.empty,each:Vd.each};var Wd=function(t){var n=[];for(var e in t)n.push(e);return n},$d=function(t){var n=[];for(var e in t)n.push(t[e]);return n},Zd=function(t){var n=[];for(var e in t)n.push({key:e,value:t[e]});return n},Gd=function(t){function n(t,n){var r,i,o=e(t,function(t,e){if(r)return r(t,e-1);i=t,r=n?Ze(t,n):$e(t)});return o.columns=i,o}function e(t,n){function e(){if(f>=s)return u;if(i)return i=!1,o;var n,e=f;if(34===t.charCodeAt(e)){for(var r=e;r++t||t>i||r>n||n>o))return this;var u,a,c=i-e,s=this._root;switch(a=(n<(r+o)/2)<<1|t<(e+i)/2){case 0:do u=new Array(4),u[a]=s,s=u;while(c*=2,i=e+c,o=r+c,t>i||n>o);break;case 1:do u=new Array(4),u[a]=s,s=u;while(c*=2,e=i-c,o=r+c,e>t||n>o);break;case 2:do u=new Array(4),u[a]=s,s=u;while(c*=2,i=e+c,r=o-c,t>i||r>n);break;case 3:do u=new Array(4),u[a]=s,s=u;while(c*=2,e=i-c,r=o-c,e>t||r>n)}this._root&&this._root.length&&(this._root=s)}return this._x0=e,this._y0=r,this._x1=i,this._y1=o,this},hv=function(){var t=[];return this.visit(function(n){if(!n.length)do t.push(n.data);while(n=n.next)}),t},pv=function(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]},dv=function(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i},vv=function(t,n,e){var r,i,o,u,a,c,s,f=this._x0,l=this._y0,h=this._x1,p=this._y1,d=[],v=this._root;for(v&&d.push(new dv(v,f,l,h,p)),null==e?e=1/0:(f=t-e,l=n-e,h=t+e,p=n+e,e*=e);c=d.pop();)if(!(!(v=c.node)||(i=c.x0)>h||(o=c.y0)>p||(u=c.x1)=g)<<1|t>=_)&&(c=d[d.length-1],d[d.length-1]=d[d.length-1-s],d[d.length-1-s]=c)}else{var y=t-+this._x.call(null,v.data),m=n-+this._y.call(null,v.data),x=y*y+m*m;if(x=(a=(d+_)/2))?d=a:_=a,(f=u>=(c=(v+g)/2))?v=c:g=c,n=p,!(p=p[l=f<<1|s]))return this;if(!p.length)break;(n[l+1&3]||n[l+2&3]||n[l+3&3])&&(e=n,h=l)}for(;p.data!==t;)if(r=p,!(p=p.next))return this;return(i=p.next)&&delete p.next,r?(i?r.next=i:delete r.next,this):n?(i?n[l]=i:delete n[l],(p=n[0]||n[1]||n[2]||n[3])&&p===(n[3]||n[2]||n[1]||n[0])&&!p.length&&(e?e[h]=p:this._root=p),this):(this._root=i,this)},gv=function(){return this._root},yv=function(){var t=0;return this.visit(function(n){if(!n.length)do++t;while(n=n.next)}),t},mv=function(t){var n,e,r,i,o,u,a=[],c=this._root;for(c&&a.push(new dv(c,this._x0,this._y0,this._x1,this._y1));n=a.pop();)if(!t(c=n.node,r=n.x0,i=n.y0,o=n.x1,u=n.y1)&&c.length){var s=(r+o)/2,f=(i+u)/2;(e=c[3])&&a.push(new dv(e,s,f,o,u)),(e=c[2])&&a.push(new dv(e,r,f,s,u)),(e=c[1])&&a.push(new dv(e,s,i,o,f)),(e=c[0])&&a.push(new dv(e,r,i,s,f))}return this},xv=function(t){var n,e=[],r=[];for(this._root&&e.push(new dv(this._root,this._x0,this._y0,this._x1,this._y1));n=e.pop();){var i=n.node;if(i.length){var o,u=n.x0,a=n.y0,c=n.x1,s=n.y1,f=(u+c)/2,l=(a+s)/2;(o=i[0])&&e.push(new dv(o,u,a,f,l)),(o=i[1])&&e.push(new dv(o,f,a,c,l)),(o=i[2])&&e.push(new dv(o,u,l,f,s)),(o=i[3])&&e.push(new dv(o,f,l,c,s))}r.push(n)}for(;n=r.pop();)t(n.node,n.x0,n.y0,n.x1,n.y1);return this},bv=function(t){return arguments.length?(this._x=t,this):this._x},wv=function(t){return arguments.length?(this._y=t,this):this._y},Mv=er.prototype=rr.prototype;Mv.copy=function(){var t,n,e=new rr(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=ir(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=ir(n));return e},Mv.add=fv,Mv.addAll=Qe,Mv.cover=lv,Mv.data=hv,Mv.extent=pv,Mv.find=vv,Mv.remove=_v,Mv.removeAll=Ke,Mv.root=gv,Mv.size=yv,Mv.visit=mv,Mv.visitAfter=xv,Mv.x=bv,Mv.y=wv;var Tv,kv=function(t){function n(){function t(t,n,e,r,i){var o=t.data,a=t.r,p=l+a;{if(!o)return n>s+p||rf+p||ic.index){var d=s-o.x-o.vx,v=f-o.y-o.vy,_=d*d+v*v;_t.r&&(t.r=t[n].r)}function r(){if(i){var n,e,r=i.length;for(o=new Array(r),n=0;n1?(null==n?l.remove(t):l.set(t,i(n)),o):l.get(t)},find:function(n,e,r){var i,o,u,a,c,s=0,f=t.length;for(null==r?r=1/0:r*=r,s=0;s1?(p.on(t,n),o):p.on(t)}}},Cv=function(){function t(t){var n,a=i.length,c=er(i,sr,fr).visitAfter(e);for(u=t,n=0;n=f)){(t.data!==o||t.next)&&(0===i&&(i=sv(),p+=i*i),0===c&&(c=sv(),p+=c*c),p1?r[0]+r.slice(2):r,+t.slice(e+1)]},Lv=function(t){return t=Rv(Math.abs(t)),t?t[1]:NaN},qv=function(t,n){return function(e,r){for(var i=e.length,o=[],u=0,a=t[0],c=0;i>0&&a>0&&(c+a+1>r&&(a=Math.max(1,r-c)),o.push(e.substring(i-=a,i+a)),!((c+=a+1)>r));)a=t[u=(u+1)%t.length];return o.reverse().join(n)}},Uv=function(t){return function(n){return n.replace(/[0-9]/g,function(n){return t[+n]})}},Dv=function(t,n){t=t.toPrecision(n);t:for(var e,r=t.length,i=1,o=-1;i0&&(o=0)}return o>0?t.slice(0,o)+t.slice(e+1):t},Ov=function(t,n){var e=Rv(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(Tv=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,u=r.length;return o===u?r:o>u?r+new Array(o-u+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+Rv(t,Math.max(0,n+o-1))[0]},Fv=function(t,n){var e=Rv(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")},Iv={"":Dv,"%":function(t,n){return(100*t).toFixed(n)},b:function(t){return Math.round(t).toString(2)},c:function(t){return t+""},d:function(t){return Math.round(t).toString(10)},e:function(t,n){return t.toExponential(n)},f:function(t,n){return t.toFixed(n)},g:function(t,n){return t.toPrecision(n)},o:function(t){return Math.round(t).toString(8)},p:function(t,n){return Fv(100*t,n)},r:Fv,s:Ov,X:function(t){return Math.round(t).toString(16).toUpperCase()},x:function(t){return Math.round(t).toString(16)}},Yv=/^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?$/i;lr.prototype=hr.prototype,hr.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(null==this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(null==this.precision?"":"."+Math.max(0,0|this.precision))+this.type};var Bv,jv=function(t){return t},Hv=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"],Xv=function(t){function n(t){function n(t){var n,i,s,m=v,x=_;if("c"===d)x=g(t)+x,t="";else{t=+t;var b=t<0;if(t=g(Math.abs(t),p),b&&0===+t&&(b=!1),m=(b?"("===c?c:"-":"-"===c||"("===c?"":c)+m,x=x+("s"===d?Hv[8+Tv/3]:"")+(b&&"("===c?")":""),y)for(n=-1,i=t.length;++ns||s>57){x=(46===s?o+t.slice(n+1):t.slice(n))+x,t=t.slice(0,n);break}}h&&!f&&(t=r(t,1/0));var w=m.length+t.length+x.length,M=w>1)+m+t+x+M.slice(w);break;default:t=M+m+t+x}return u(t)}t=lr(t);var e=t.fill,a=t.align,c=t.sign,s=t.symbol,f=t.zero,l=t.width,h=t.comma,p=t.precision,d=t.type,v="$"===s?i[0]:"#"===s&&/[boxX]/.test(d)?"0"+d.toLowerCase():"",_="$"===s?i[1]:/[%p]/.test(d)?"%":"",g=Iv[d],y=!d||/[defgprs%]/.test(d);return p=null==p?d?6:12:/[gprs]/.test(d)?Math.max(1,Math.min(21,p)):Math.max(0,Math.min(20,p)),n.toString=function(){return t+""},n}function e(t,e){var r=n((t=lr(t),t.type="f",t)),i=3*Math.max(-8,Math.min(8,Math.floor(Lv(e)/3))),o=Math.pow(10,-i),u=Hv[8+i/3];return function(t){return r(o*t)+u}}var r=t.grouping&&t.thousands?qv(t.grouping,t.thousands):jv,i=t.currency,o=t.decimal,u=t.numerals?Uv(t.numerals):jv;return{format:n,formatPrefix:e}};pr({decimal:".",thousands:",",grouping:[3],currency:["$",""]});var Vv=function(t){return Math.max(0,-Lv(Math.abs(t)))},Wv=function(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(Lv(n)/3)))-Lv(Math.abs(t)))},$v=function(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,Lv(n)-Lv(t))+1},Zv=function(){return new dr};dr.prototype={constructor:dr,reset:function(){this.s=this.t=0},add:function(t){vr(N_,t,this.t),vr(this,N_.s,this.s),this.s?this.t+=N_.t:this.s=N_.t},valueOf:function(){return this.s}};var Gv,Jv,Qv,Kv,t_,n_,e_,r_,i_,o_,u_,a_,c_,s_,f_,l_,h_,p_,d_,v_,__,g_,y_,m_,x_,b_,w_,M_,T_,k_,S_,N_=new dr,E_=1e-6,A_=1e-12,C_=Math.PI,z_=C_/2,P_=C_/4,R_=2*C_,L_=180/C_,q_=C_/180,U_=Math.abs,D_=Math.atan,O_=Math.atan2,F_=Math.cos,I_=Math.ceil,Y_=Math.exp,B_=Math.log,j_=Math.pow,H_=Math.sin,X_=Math.sign||function(t){return t>0?1:t<0?-1:0},V_=Math.sqrt,W_=Math.tan,$_={Feature:function(t,n){xr(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++rE_?i_=90:ng<-E_&&(e_=-90),f_[0]=n_,f_[1]=r_}},rg=function(t){var n,e,r,i,o,u,a;if(i_=r_=-(n_=e_=1/0),s_=[],G_(t,eg),e=s_.length){for(s_.sort(Br),n=1,r=s_[0],o=[r];nYr(r[0],r[1])&&(r[1]=i[1]),Yr(i[0],r[1])>Yr(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(u=-(1/0),e=o.length-1,n=0,r=o[e];n<=e;r=i,++n)i=o[n],(a=Yr(r[1],i[0]))>u&&(u=a,n_=i[0],r_=r[1])}return s_=f_=null,n_===1/0||e_===1/0?[[NaN,NaN],[NaN,NaN]]:[[n_,e_],[r_,i_]]},ig={sphere:mr,point:Hr,lineStart:Vr,lineEnd:Zr,polygonStart:function(){ig.lineStart=Gr,ig.lineEnd=Jr},polygonEnd:function(){ig.lineStart=Vr,ig.lineEnd=Zr}},og=function(t){l_=h_=p_=d_=v_=__=g_=y_=m_=x_=b_=0,G_(t,ig);var n=m_,e=x_,r=b_,i=n*n+e*e+r*r;return i2?t[2]*q_:0),n.invert=function(n){return n=t.invert(n[0]*q_,n[1]*q_),n[0]*=L_,n[1]*=L_,n},n},xg=function(){function t(t,n){e.push(t=r(t,n)),t[0]*=L_,t[1]*=L_}function n(){var t=i.apply(this,arguments),n=o.apply(this,arguments)*q_,c=u.apply(this,arguments)*q_;return e=[],r=ni(-t[0]*q_,-t[1]*q_,0).invert,oi(a,n,c,1),t={type:"Polygon",coordinates:[e]},e=r=null,t}var e,r,i=ug([0,0]),o=ug(90),u=ug(6),a={point:t};return n.center=function(t){return arguments.length?(i="function"==typeof t?t:ug([+t[0],+t[1]]),n):i},n.radius=function(t){return arguments.length?(o="function"==typeof t?t:ug(+t),n):o},n.precision=function(t){return arguments.length?(u="function"==typeof t?t:ug(+t),n):u},n},bg=function(){var t,n=[];return{point:function(n,e){t.push([n,e])},lineStart:function(){n.push(t=[])},lineEnd:mr,rejoin:function(){n.length>1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}},wg=function(t,n,e,r,i,o){var u,a=t[0],c=t[1],s=n[0],f=n[1],l=0,h=1,p=s-a,d=f-c;if(u=e-a,p||!(u>0)){if(u/=p,p<0){if(u0){if(u>h)return;u>l&&(l=u)}if(u=i-a,p||!(u<0)){if(u/=p,p<0){if(u>h)return;u>l&&(l=u)}else if(p>0){if(u0)){if(u/=d,d<0){if(u0){if(u>h)return;u>l&&(l=u)}if(u=o-c,d||!(u<0)){if(u/=d,d<0){if(u>h)return;u>l&&(l=u)}else if(d>0){if(u0&&(t[0]=a+l*p,t[1]=c+l*d),h<1&&(n[0]=a+h*p,n[1]=c+h*d),!0}}}}},Mg=function(t,n){return U_(t[0]-n[0])=0;--o)i.point((f=s[o])[0],f[1]);else r(h.x,h.p.x,-1,i);h=h.p}h=h.o,s=h.z,p=!p}while(!h.v);i.lineEnd()}}},kg=1e9,Sg=-kg,Ng=function(){var t,n,e,r=0,i=0,o=960,u=500;return e={stream:function(e){return t&&n===e?t:t=si(r,i,o,u)(n=e)},extent:function(a){return arguments.length?(r=+a[0][0],i=+a[0][1],o=+a[1][0],u=+a[1][1],t=n=null,e):[[r,i],[o,u]]}}},Eg=Zv(),Ag=function(t,n){var e=n[0],r=n[1],i=[H_(e),-F_(e),0],o=0,u=0;Eg.reset();for(var a=0,c=t.length;a=0?1:-1,T=M*w,k=T>C_,S=d*x;if(Eg.add(O_(S*M*H_(T),v*b+S*F_(T))),o+=k?w+M*R_:w,k^h>=e^y>=e){var N=Cr(Er(l),Er(g));Rr(N);var E=Cr(i,N);Rr(E);var A=(k^w>=0?-1:1)*gr(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(u+=k^w>=0?1:-1)}}return(o<-E_||o0){for(x||(o.polygonStart(),x=!0),o.lineStart(),t=0;t1&&2&i&&u.push(u.pop().concat(u.shift())),d.push(u.filter(ji))}var p,d,v,_=n(o),g=i.invert(r[0],r[1]),y=bg(),m=n(y),x=!1,b={point:u,lineStart:c,lineEnd:s,polygonStart:function(){b.point=f,b.lineStart=l,b.lineEnd=h,d=[],p=[]},polygonEnd:function(){b.point=u,b.lineStart=c,b.lineEnd=s,d=hf(d);var t=Ag(p,g);d.length?(x||(o.polygonStart(),x=!0),Tg(d,Hi,t,e,o)):t&&(x||(o.polygonStart(),x=!0),o.lineStart(),e(null,null,1,o),o.lineEnd()),x&&(o.polygonEnd(),x=!1),d=p=null},sphere:function(){o.polygonStart(),o.lineStart(),e(null,null,1,o),o.lineEnd(),o.polygonEnd()}};return b}},dy=py(function(){return!0},Xi,Wi,[-C_,-z_]),vy=function(t,n){function e(e,r,i,o){oi(o,t,n,i,e,r)}function r(t,n){return F_(t)*F_(n)>a}function i(t){var n,e,i,a,f;return{lineStart:function(){a=i=!1,f=1},point:function(l,h){var p,d=[l,h],v=r(l,h),_=c?v?0:u(l,h):v?u(l+(l<0?C_:-C_),h):0;if(!n&&(a=i=v)&&t.lineStart(),v!==i&&(p=o(n,d),(Mg(n,p)||Mg(d,p))&&(d[0]+=E_,d[1]+=E_,v=r(d[0],d[1]))),v!==i)f=0,v?(t.lineStart(),p=o(d,n),t.point(p[0],p[1])):(p=o(n,d),t.point(p[0],p[1]),t.lineEnd()),n=p;else if(s&&n&&c^v){var g;_&e||!(g=o(d,n,!0))||(f=0,c?(t.lineStart(),t.point(g[0][0],g[0][1]),t.point(g[1][0],g[1][1]),t.lineEnd()):(t.point(g[1][0],g[1][1]),t.lineEnd(),t.lineStart(),t.point(g[0][0],g[0][1])))}!v||n&&Mg(n,d)||t.point(d[0],d[1]),n=d,i=v,e=_},lineEnd:function(){i&&t.lineEnd(),n=null},clean:function(){return f|(a&&i)<<1}}}function o(t,n,e){var r=Er(t),i=Er(n),o=[1,0,0],u=Cr(r,i),c=Ar(u,u),s=u[0],f=c-s*s;if(!f)return!e&&t;var l=a*c/f,h=-a*s/f,p=Cr(o,u),d=Pr(o,l);zr(d,Pr(u,h));var v=p,_=Ar(d,v),g=Ar(v,v),y=_*_-g*(Ar(d,d)-1);if(!(y<0)){var m=V_(y),x=Pr(v,(-_-m)/g);if(zr(x,d),x=Nr(x),!e)return x;var b,w=t[0],M=n[0],T=t[1],k=n[1];M0^x[1]<(U_(x[0]-w)C_^(w<=x[0]&&x[0]<=M)){var A=Pr(v,(-_+m)/g);return zr(A,d),[x,Nr(A)]}}}function u(n,e){var r=c?t:C_-t,i=0;return n<-r?i|=1:n>r&&(i|=2),e<-r?i|=4:e>r&&(i|=8),i}var a=F_(t),c=a>0,s=U_(a)>E_;return py(r,i,e,c?[0,-t]:[-C_,t-C_])},_y=function(t){return{stream:$i(t)}};Zi.prototype={constructor:Zi,point:function(t,n){this.stream.point(t,n)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var gy=16,yy=F_(30*q_),my=function(t,n){return+n?Ki(t,n):Qi(t)},xy=$i({point:function(t,n){this.stream.point(t*q_,n*q_)}}),by=function(){return eo(io).scale(155.424).center([0,33.6442])},wy=function(){return by().parallels([29.5,45.5]).scale(1070).translate([480,250]).rotate([96,0]).center([-.6,38.7])},My=function(){function t(t){var n=t[0],e=t[1];return a=null,i.point(n,e),a||(o.point(n,e),a)||(u.point(n,e),a)}function n(){return e=r=null,t}var e,r,i,o,u,a,c=wy(),s=by().rotate([154,0]).center([-2,58.5]).parallels([55,65]),f=by().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(t,n){a=[t,n]}};return t.invert=function(t){var n=c.scale(),e=c.translate(),r=(t[0]-e[0])/n,i=(t[1]-e[1])/n;return(i>=.12&&i<.234&&r>=-.425&&r<-.214?s:i>=.166&&i<.234&&r>=-.214&&r<-.115?f:c).invert(t)},t.stream=function(t){return e&&r===t?e:e=oo([c.stream(r=t),s.stream(t),f.stream(t)])},t.precision=function(t){return arguments.length?(c.precision(t),s.precision(t),f.precision(t),n()):c.precision()},t.scale=function(n){return arguments.length?(c.scale(n),s.scale(.35*n),f.scale(n),t.translate(c.translate())):c.scale()},t.translate=function(t){if(!arguments.length)return c.translate();var e=c.scale(),r=+t[0],a=+t[1];return i=c.translate(t).clipExtent([[r-.455*e,a-.238*e],[r+.455*e,a+.238*e]]).stream(l),o=s.translate([r-.307*e,a+.201*e]).clipExtent([[r-.425*e+E_,a+.12*e+E_],[r-.214*e-E_,a+.234*e-E_]]).stream(l),u=f.translate([r-.205*e,a+.212*e]).clipExtent([[r-.214*e+E_,a+.166*e+E_],[r-.115*e-E_,a+.234*e-E_]]).stream(l),n()},t.fitExtent=function(n,e){return Gi(t,n,e)},t.fitSize=function(n,e){return Ji(t,n,e)},t.scale(1070)},Ty=uo(function(t){return V_(2/(1+t))});Ty.invert=ao(function(t){return 2*gr(t/2)});var ky=function(){return to(Ty).scale(124.75).clipAngle(179.999)},Sy=uo(function(t){return(t=_r(t))&&t/H_(t)});Sy.invert=ao(function(t){return t});var Ny=function(){return to(Sy).scale(79.4188).clipAngle(179.999)};co.invert=function(t,n){return[t,2*D_(Y_(n))-z_]};var Ey=function(){return so(co).scale(961/R_)},Ay=function(){return eo(lo).scale(109.5).parallels([30,30])};ho.invert=ho;var Cy=function(){return to(ho).scale(152.63)},zy=function(){return eo(po).scale(131.154).center([0,13.9389])};vo.invert=ao(D_);var Py=function(){return to(vo).scale(144.049).clipAngle(60)},Ry=function(){function t(){return i=o=null,u}var n,e,r,i,o,u,a=1,c=0,s=0,f=1,l=1,h=Ig,p=null,d=Ig;return u={stream:function(t){return i&&o===t?i:i=h(d(o=t))},clipExtent:function(i){return arguments.length?(d=null==i?(p=n=e=r=null,Ig):si(p=+i[0][0],n=+i[0][1],e=+i[1][0],r=+i[1][1]),t()):null==p?null:[[p,n],[e,r]]},scale:function(n){return arguments.length?(h=_o((a=+n)*f,a*l,c,s),t()):a},translate:function(n){return arguments.length?(h=_o(a*f,a*l,c=+n[0],s=+n[1]),t()):[c,s]},reflectX:function(n){return arguments.length?(h=_o(a*(f=n?-1:1),a*l,c,s),t()):f<0},reflectY:function(n){return arguments.length?(h=_o(a*f,a*(l=n?-1:1),c,s),t()):l<0},fitExtent:function(t,n){return Gi(u,t,n)},fitSize:function(t,n){return Ji(u,t,n)}}};go.invert=ao(gr);var Ly=function(){return to(go).scale(249.5).clipAngle(90+E_)};yo.invert=ao(function(t){return 2*D_(t)});var qy=function(){return to(yo).scale(250).clipAngle(142)};mo.invert=function(t,n){return[-n,2*D_(Y_(t))-z_]};var Uy=function(){var t=so(mo),n=t.center,e=t.rotate;return t.center=function(t){return arguments.length?n([-t[1],t[0]]):(t=n(),[t[1],-t[0]])},t.rotate=function(t){return arguments.length?e([t[0],t[1],t.length>2?t[2]+90:90]):(t=e(),[t[0],t[1],t[2]-90])},e([0,0,90]).scale(159.155)},Dy=function(){function t(t){var o,u=0;t.eachAfter(function(t){var e=t.children;e?(t.x=bo(e),t.y=Mo(e)):(t.x=o?u+=n(t,o):0,t.y=0,o=t)});var a=ko(t),c=So(t),s=a.x-n(a,c)/2,f=c.x+n(c,a)/2;return t.eachAfter(i?function(n){n.x=(n.x-t.x)*e,n.y=(t.y-n.y)*r}:function(n){n.x=(n.x-s)/(f-s)*e,n.y=(1-(t.y?n.y/t.y:1))*r})}var n=xo,e=1,r=1,i=!1;return t.separation=function(e){return arguments.length?(n=e,t):n},t.size=function(n){return arguments.length?(i=!1,e=+n[0],r=+n[1],t):i?null:[e,r]},t.nodeSize=function(n){return arguments.length?(i=!0,e=+n[0],r=+n[1],t):i?[e,r]:null},t},Oy=function(){return this.eachAfter(No)},Fy=function(t){var n,e,r,i,o=this,u=[o];do for(n=u.reverse(),u=[];o=n.pop();)if(t(o),e=o.children)for(r=0,i=e.length;r=0;--e)i.push(n[e]);return this},Yy=function(t){for(var n,e,r,i=this,o=[i],u=[];i=o.pop();)if(u.push(i),n=i.children)for(e=0,r=n.length;e=0;)e+=r[i].value;n.value=e})},jy=function(t){return this.eachBefore(function(n){n.children&&n.children.sort(t)})},Hy=function(t){for(var n=this,e=Eo(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},Xy=function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},Vy=function(){var t=[];return this.each(function(n){t.push(n)}),t},Wy=function(){var t=[];return this.eachBefore(function(n){n.children||t.push(n)}),t},$y=function(){var t=this,n=[];return t.each(function(e){e!==t&&n.push({source:e.parent,target:e})}),n};Lo.prototype=Ao.prototype={constructor:Lo,count:Oy,each:Fy,eachAfter:Yy,eachBefore:Iy,sum:By,sort:jy,path:Hy,ancestors:Xy,descendants:Vy,leaves:Wy,links:$y,copy:Co};var Zy=function(t){for(var n,e=(t=t.slice()).length,r=null,i=r;e;){var o=new qo(t[e-1]);i=i?i.next=o:r=o,t[n]=t[--e]}return{head:r,tail:i}},Gy=function(t){return Do(Zy(t),[])},Jy=function(t){return Vo(t),t},Qy=function(t){return function(){return t}},Ky=function(){function t(t){return t.x=e/2,t.y=r/2,n?t.eachBefore(Jo(n)).eachAfter(Qo(i,.5)).eachBefore(Ko(1)):t.eachBefore(Jo(Go)).eachAfter(Qo(Zo,1)).eachAfter(Qo(i,t.r/Math.min(e,r))).eachBefore(Ko(Math.min(e,r)/(2*t.r))),t}var n=null,e=1,r=1,i=Zo;return t.radius=function(e){return arguments.length?(n=Wo(e),t):n},t.size=function(n){return arguments.length?(e=+n[0],r=+n[1],t):[e,r]},t.padding=function(n){return arguments.length?(i="function"==typeof n?n:Qy(+n),t):i},t},tm=function(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)},nm=function(t,n,e,r,i){for(var o,u=t.children,a=-1,c=u.length,s=t.value&&(r-n)/t.value;++a0)throw new Error("cycle");return o}var n=tu,e=nu;return t.id=function(e){return arguments.length?(n=$o(e),t):n},t.parentId=function(n){return arguments.length?(e=$o(n),t):e},t};cu.prototype=Object.create(Lo.prototype);var am=function(){function t(t){var r=su(t);if(r.eachAfter(n),r.parent.m=-r.z,r.eachBefore(e),c)t.eachBefore(i);else{var s=t,f=t,l=t;t.eachBefore(function(t){t.xf.x&&(f=t),t.depth>l.depth&&(l=t)});var h=s===f?1:o(s,f)/2,p=h-s.x,d=u/(f.x+h+p),v=a/(l.depth||1);t.eachBefore(function(t){t.x=(t.x+p)*d,t.y=t.depth*v})}return t}function n(t){var n=t.children,e=t.parent.children,i=t.i?e[t.i-1]:null;if(n){uu(t);var u=(n[0].z+n[n.length-1].z)/2;i?(t.z=i.z+o(t._,i._),t.m=t.z-u):t.z=u}else i&&(t.z=i.z+o(t._,i._));t.parent.A=r(t,i,t.parent.A||e[0])}function e(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function r(t,n,e){if(n){for(var r,i=t,u=t,a=n,c=i.parent.children[0],s=i.m,f=u.m,l=a.m,h=c.m;a=iu(a),i=ru(i),a&&i;)c=ru(c),u=iu(u),u.a=t,r=a.z+l-i.z-s+o(a._,i._),r>0&&(ou(au(a,t,e),t,r),s+=r,f+=r),l+=a.m,s+=i.m,h+=c.m,f+=u.m;a&&!iu(u)&&(u.t=a,u.m+=l-f),i&&!ru(c)&&(c.t=i,c.m+=s-h,e=t)}return e}function i(t){t.x*=u,t.y=t.depth*a}var o=eu,u=1,a=1,c=null;return t.separation=function(n){return arguments.length?(o=n,t):o},t.size=function(n){return arguments.length?(c=!1,u=+n[0],a=+n[1],t):c?null:[u,a]},t.nodeSize=function(n){return arguments.length?(c=!0,u=+n[0],a=+n[1],t):c?[u,a]:null},t},cm=function(t,n,e,r,i){for(var o,u=t.children,a=-1,c=u.length,s=t.value&&(i-e)/t.value;++a1?n:1)},e}(sm),lm=function(){function t(t){return t.x0=t.y0=0,t.x1=i,t.y1=o,t.eachBefore(n),u=[0],r&&t.eachBefore(tm),t}function n(t){var n=u[t.depth],r=t.x0+n,i=t.y0+n,o=t.x1-n,h=t.y1-n;o=n-1){var s=c[t];return s.x0=r,s.y0=i,s.x1=u,s.y1=a,void 0}for(var l=f[t],h=e/2+l,p=t+1,d=n-1;p>>1;f[v]u-r){var y=(i*g+a*_)/e;o(t,p,_,r,i,u,y),o(p,n,g,r,y,u,a)}else{var m=(r*g+u*_)/e;o(t,p,_,r,i,m,a),o(p,n,g,m,i,u,a)}}var u,a,c=t.children,s=c.length,f=new Array(s+1);for(f[0]=a=u=0;u1?n:1)},e}(sm),vm=function(t){for(var n,e=-1,r=t.length,i=t[r-1],o=0;++e=0;--n)s.push(t[r[o[n]][2]]);for(n=+a;na!=s>a&&u<(c-e)*(a-r)/(s-r)+e&&(f=!f),c=e,s=r;return f},xm=function(t){for(var n,e,r=-1,i=t.length,o=t[i-1],u=o[0],a=o[1],c=0;++r1);return t+n*i*Math.sqrt(-2*Math.log(r)/r)}},km=function(){var t=Tm.apply(this,arguments);return function(){return Math.exp(t())}},Sm=function(t){return function(){for(var n=0,e=0;e=200&&e<300||304===e){if(o)try{n=o.call(r,s)}catch(t){return void a.call("error",r,t)}else n=s;a.call("load",r,n)}else a.call("error",r,t)}var r,i,o,u,a=d("beforesend","progress","load","error"),c=Ye(),s=new XMLHttpRequest,f=null,l=null,h=0;if("undefined"==typeof XDomainRequest||"withCredentials"in s||!/^(http(s)?:)?\/\//.test(t)||(s=new XDomainRequest),"onload"in s?s.onload=s.onerror=s.ontimeout=e:s.onreadystatechange=function(t){s.readyState>3&&e(t)},s.onprogress=function(t){a.call("progress",r,t)},r={header:function(t,n){return t=(t+"").toLowerCase(),arguments.length<2?c.get(t):(null==n?c.remove(t):c.set(t,n+""),r)},mimeType:function(t){return arguments.length?(i=null==t?null:t+"",r):i},responseType:function(t){return arguments.length?(u=t,r):u},timeout:function(t){return arguments.length?(h=+t,r):h},user:function(t){return arguments.length<1?f:(f=null==t?null:t+"",r)},password:function(t){return arguments.length<1?l:(l=null==t?null:t+"",r)},response:function(t){return o=t,r},get:function(t,n){return r.send("GET",t,n)},post:function(t,n){return r.send("POST",t,n)},send:function(n,e,o){return s.open(n,t,!0,f,l),null==i||c.has("accept")||c.set("accept",i+",*/*"),s.setRequestHeader&&c.each(function(t,n){s.setRequestHeader(n,t)}),null!=i&&s.overrideMimeType&&s.overrideMimeType(i),null!=u&&(s.responseType=u),h>0&&(s.timeout=h),null==o&&"function"==typeof e&&(o=e,e=null),null!=o&&1===o.length&&(o=xu(o)),null!=o&&r.on("error",o).on("load",function(t){o(null,t)}),a.call("beforesend",r,s),s.send(null==e?null:e),r},abort:function(){return s.abort(),r},on:function(){var t=a.on.apply(a,arguments);return t===a?r:t}},null!=n){if("function"!=typeof n)throw new Error("invalid callback: "+n);return r.get(n)}return r},Cm=function(t,n){return function(e,r){var i=Am(e).mimeType(t).response(n);if(null!=r){if("function"!=typeof r)throw new Error("invalid callback: "+r);return i.get(r)}return i}},zm=Cm("text/html",function(t){return document.createRange().createContextualFragment(t.responseText)}),Pm=Cm("application/json",function(t){return JSON.parse(t.responseText)}),Rm=Cm("text/plain",function(t){return t.responseText}),Lm=Cm("application/xml",function(t){var n=t.responseXML;if(!n)throw new Error("parse error");return n}),qm=function(t,n){return function(e,r,i){arguments.length<3&&(i=r,r=null);var o=Am(e).mimeType(t);return o.row=function(t){return arguments.length?o.response(wu(n,r=t)):r},o.row(r),i?o.get(i):o}},Um=qm("text/csv",Qd),Dm=qm("text/tab-separated-values",rv),Om=Array.prototype,Fm=Om.map,Im=Om.slice,Ym={name:"implicit"},Bm=function(t){return function(){return t}},jm=function(t){return+t},Hm=[0,1],Xm=function(n,e,i){var o,u=n[0],a=n[n.length-1],c=r(u,a,null==e?10:e);switch(i=lr(null==i?",f":i),i.type){case"s":var s=Math.max(Math.abs(u),Math.abs(a));return null!=i.precision||isNaN(o=Wv(c,s))||(i.precision=o),t.formatPrefix(i,s);case"":case"e":case"g":case"p":case"r":null!=i.precision||isNaN(o=$v(c,Math.max(Math.abs(u),Math.abs(a))))||(i.precision=o-("e"===i.type));break;case"f":case"%":null!=i.precision||isNaN(o=Vv(c))||(i.precision=o-2*("%"===i.type))}return t.format(i)},Vm=function(t,n){t=t.slice();var e,r=0,i=t.length-1,o=t[r],u=t[i];return u0?t>1?Gu(function(n){n.setTime(Math.floor(n/t)*t)},function(n,e){n.setTime(+n+e*t)},function(n,e){return(e-n)/t}):Zm:null};var Gm=Zm.range,Jm=1e3,Qm=6e4,Km=36e5,tx=864e5,nx=6048e5,ex=Gu(function(t){t.setTime(Math.floor(t/Jm)*Jm)},function(t,n){t.setTime(+t+n*Jm)},function(t,n){return(n-t)/Jm},function(t){return t.getUTCSeconds()}),rx=ex.range,ix=Gu(function(t){t.setTime(Math.floor(t/Qm)*Qm)},function(t,n){t.setTime(+t+n*Qm)},function(t,n){return(n-t)/Qm},function(t){return t.getMinutes()}),ox=ix.range,ux=Gu(function(t){var n=t.getTimezoneOffset()*Qm%Km;n<0&&(n+=Km),t.setTime(Math.floor((+t-n)/Km)*Km+n)},function(t,n){t.setTime(+t+n*Km)},function(t,n){return(n-t)/Km},function(t){return t.getHours()}),ax=ux.range,cx=Gu(function(t){t.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Qm)/tx},function(t){return t.getDate()-1}),sx=cx.range,fx=Ju(0),lx=Ju(1),hx=Ju(2),px=Ju(3),dx=Ju(4),vx=Ju(5),_x=Ju(6),gx=fx.range,yx=lx.range,mx=hx.range,xx=px.range,bx=dx.range,wx=vx.range,Mx=_x.range,Tx=Gu(function(t){t.setDate(1),t.setHours(0,0,0,0)},function(t,n){t.setMonth(t.getMonth()+n)},function(t,n){return n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())},function(t){return t.getMonth()}),kx=Tx.range,Sx=Gu(function(t){t.setMonth(0,1),t.setHours(0,0,0,0)},function(t,n){t.setFullYear(t.getFullYear()+n)},function(t,n){return n.getFullYear()-t.getFullYear()},function(t){return t.getFullYear()});Sx.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Gu(function(n){n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)},function(n,e){n.setFullYear(n.getFullYear()+e*t)}):null};var Nx=Sx.range,Ex=Gu(function(t){t.setUTCSeconds(0,0)},function(t,n){t.setTime(+t+n*Qm)},function(t,n){return(n-t)/Qm},function(t){return t.getUTCMinutes()}),Ax=Ex.range,Cx=Gu(function(t){t.setUTCMinutes(0,0,0)},function(t,n){t.setTime(+t+n*Km)},function(t,n){return(n-t)/Km},function(t){return t.getUTCHours()}),zx=Cx.range,Px=Gu(function(t){t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+n)},function(t,n){return(n-t)/tx},function(t){return t.getUTCDate()-1}),Rx=Px.range,Lx=Qu(0),qx=Qu(1),Ux=Qu(2),Dx=Qu(3),Ox=Qu(4),Fx=Qu(5),Ix=Qu(6),Yx=Lx.range,Bx=qx.range,jx=Ux.range,Hx=Dx.range,Xx=Ox.range,Vx=Fx.range,Wx=Ix.range,$x=Gu(function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCMonth(t.getUTCMonth()+n)},function(t,n){return n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())},function(t){return t.getUTCMonth()}),Zx=$x.range,Gx=Gu(function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCFullYear(t.getUTCFullYear()+n)},function(t,n){return n.getUTCFullYear()-t.getUTCFullYear()},function(t){return t.getUTCFullYear()});Gx.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Gu(function(n){n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)},function(n,e){n.setUTCFullYear(n.getUTCFullYear()+e*t)}):null};var Jx,Qx=Gx.range,Kx={"-":"",_:" ",0:"0"},tb=/^\s*\d+/,nb=/^%/,eb=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;Ga({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var rb="%Y-%m-%dT%H:%M:%S.%LZ",ib=Date.prototype.toISOString?Ja:t.utcFormat(rb),ob=+new Date("2000-01-01T00:00:00.000Z")?Qa:t.utcParse(rb),ub=1e3,ab=60*ub,cb=60*ab,sb=24*cb,fb=7*sb,lb=30*sb,hb=365*sb,pb=function(){return nc(Sx,Tx,fx,cx,ux,ix,ex,Zm,t.timeFormat).domain([new Date(2e3,0,1),new Date(2e3,0,2)])},db=function(){return nc(Gx,$x,Lx,Px,Cx,Ex,ex,Zm,t.utcFormat).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)])},vb=function(t){return t.match(/.{6}/g).map(function(t){return"#"+t})},_b=vb("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"),gb=vb("393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6"),yb=vb("3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9"),mb=vb("1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5"),xb=Vh(Vt(300,.5,0),Vt(-240,.5,1)),bb=Vh(Vt(-100,.75,.35),Vt(80,1.5,.8)),wb=Vh(Vt(260,.75,.35),Vt(80,1.5,.8)),Mb=Vt(),Tb=function(t){(t<0||t>1)&&(t-=Math.floor(t));var n=Math.abs(t-.5);return Mb.h=360*t-100,Mb.s=1.5-1.5*n,Mb.l=.8-.9*n,Mb+""},kb=ec(vb("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),Sb=ec(vb("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),Nb=ec(vb("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),Eb=ec(vb("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921")),Ab=function(t){return function(){return t}},Cb=Math.abs,zb=Math.atan2,Pb=Math.cos,Rb=Math.max,Lb=Math.min,qb=Math.sin,Ub=Math.sqrt,Db=1e-12,Ob=Math.PI,Fb=Ob/2,Ib=2*Ob,Yb=function(){function t(){var t,s,f=+n.apply(this,arguments),l=+e.apply(this,arguments),h=o.apply(this,arguments)-Fb,p=u.apply(this,arguments)-Fb,d=Cb(p-h),v=p>h;if(c||(c=t=Le()),lDb)if(d>Ib-Db)c.moveTo(l*Pb(h),l*qb(h)),c.arc(0,0,l,h,p,!v),f>Db&&(c.moveTo(f*Pb(p),f*qb(p)),c.arc(0,0,f,p,h,v));else{var _,g,y=h,m=p,x=h,b=p,w=d,M=d,T=a.apply(this,arguments)/2,k=T>Db&&(i?+i.apply(this,arguments):Ub(f*f+l*l)),S=Lb(Cb(l-f)/2,+r.apply(this,arguments)),N=S,E=S;if(k>Db){var A=oc(k/f*qb(T)),C=oc(k/l*qb(T));(w-=2*A)>Db?(A*=v?1:-1,x+=A,b-=A):(w=0,x=b=(h+p)/2),(M-=2*C)>Db?(C*=v?1:-1,y+=C,m-=C):(M=0,y=m=(h+p)/2)}var z=l*Pb(y),P=l*qb(y),R=f*Pb(b),L=f*qb(b);if(S>Db){var q=l*Pb(m),U=l*qb(m),D=f*Pb(x),O=f*qb(x);if(dDb?lc(z,P,D,O,q,U,R,L):[R,L],I=z-F[0],Y=P-F[1],B=q-F[0],j=U-F[1],H=1/qb(ic((I*B+Y*j)/(Ub(I*I+Y*Y)*Ub(B*B+j*j)))/2),X=Ub(F[0]*F[0]+F[1]*F[1]);N=Lb(S,(f-X)/(H-1)),E=Lb(S,(l-X)/(H+1))}}M>Db?E>Db?(_=hc(D,O,z,P,l,E,v),g=hc(q,U,R,L,l,E,v),c.moveTo(_.cx+_.x01,_.cy+_.y01),EDb&&w>Db?N>Db?(_=hc(R,L,q,U,f,-N,v),g=hc(z,P,D,O,f,-N,v),c.lineTo(_.cx+_.x01,_.cy+_.y01),N=f;--l)s.point(_[l],g[l]);s.lineEnd(),s.areaEnd()}v&&(_[n]=+e(h,n,t),g[n]=+i(h,n,t),s.point(r?+r(h,n,t):_[n],o?+o(h,n,t):g[n]))}if(p)return s=null,p+""||null}function n(){return jb().defined(u).curve(c).context(a)}var e=dc,r=null,i=Ab(0),o=vc,u=Ab(!0),a=null,c=Bb,s=null;return t.x=function(n){return arguments.length?(e="function"==typeof n?n:Ab(+n),r=null,t):e},t.x0=function(n){return arguments.length?(e="function"==typeof n?n:Ab(+n),t):e},t.x1=function(n){return arguments.length?(r=null==n?null:"function"==typeof n?n:Ab(+n),t):r},t.y=function(n){return arguments.length?(i="function"==typeof n?n:Ab(+n),o=null,t):i},t.y0=function(n){return arguments.length?(i="function"==typeof n?n:Ab(+n),t):i},t.y1=function(n){return arguments.length?(o=null==n?null:"function"==typeof n?n:Ab(+n),t):o},t.lineX0=t.lineY0=function(){return n().x(e).y(i)},t.lineY1=function(){return n().x(e).y(o)},t.lineX1=function(){return n().x(r).y(i)},t.defined=function(n){return arguments.length?(u="function"==typeof n?n:Ab(!!n),t):u},t.curve=function(n){return arguments.length?(c=n,null!=a&&(s=c(a)),t):c},t.context=function(n){return arguments.length?(null==n?a=s=null:s=c(a=n),t):a},t},Xb=function(t,n){return nt?1:n>=t?0:NaN},Vb=function(t){return t},Wb=function(){function t(t){var a,c,s,f,l,h=t.length,p=0,d=new Array(h),v=new Array(h),_=+i.apply(this,arguments),g=Math.min(Ib,Math.max(-Ib,o.apply(this,arguments)-_)),y=Math.min(Math.abs(g)/h,u.apply(this,arguments)),m=y*(g<0?-1:1);for(a=0;a0&&(p+=l);for(null!=e?d.sort(function(t,n){return e(v[t],v[n])}):null!=r&&d.sort(function(n,e){return r(t[n],t[e])}),a=0,s=p?(g-h*m)/p:0;a0?l*s:0)+m,v[c]={data:t[c],index:a,value:l,startAngle:_,endAngle:f,padAngle:y};return v}var n=Vb,e=Xb,r=null,i=Ab(0),o=Ab(Ib),u=Ab(0);return t.value=function(e){return arguments.length?(n="function"==typeof e?e:Ab(+e),t):n},t.sortValues=function(n){return arguments.length?(e=n,r=null,t):e},t.sort=function(n){return arguments.length?(r=n,e=null,t):r},t.startAngle=function(n){return arguments.length?(i="function"==typeof n?n:Ab(+n),t):i},t.endAngle=function(n){return arguments.length?(o="function"==typeof n?n:Ab(+n),t):o},t.padAngle=function(n){return arguments.length?(u="function"==typeof n?n:Ab(+n),t):u},t},$b=gc(Bb);_c.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,n){this._curve.point(n*Math.sin(t),n*-Math.cos(t))}};var Zb=function(){return yc(jb().curve($b))},Gb=function(){var t=Hb().curve($b),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return yc(e())},delete t.lineX0,t.lineEndAngle=function(){return yc(r())},delete t.lineX1,t.lineInnerRadius=function(){return yc(i())},delete t.lineY0,t.lineOuterRadius=function(){return yc(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n(gc(t)):n()._curve},t},Jb={draw:function(t,n){var e=Math.sqrt(n/Ob);t.moveTo(e,0),t.arc(0,0,e,0,Ib)}},Qb={draw:function(t,n){var e=Math.sqrt(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}},Kb=Math.sqrt(1/3),tw=2*Kb,nw={draw:function(t,n){var e=Math.sqrt(n/tw),r=e*Kb;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}},ew=.8908130915292852,rw=Math.sin(Ob/10)/Math.sin(7*Ob/10),iw=Math.sin(Ib/10)*rw,ow=-Math.cos(Ib/10)*rw,uw={draw:function(t,n){var e=Math.sqrt(n*ew),r=iw*e,i=ow*e;t.moveTo(0,-e),t.lineTo(r,i);for(var o=1;o<5;++o){var u=Ib*o/5,a=Math.cos(u),c=Math.sin(u);t.lineTo(c*e,-a*e),t.lineTo(a*r-c*i,c*r+a*i)}t.closePath()}},aw={draw:function(t,n){var e=Math.sqrt(n),r=-e/2;t.rect(r,r,e,e)}},cw=Math.sqrt(3),sw={draw:function(t,n){var e=-Math.sqrt(n/(3*cw));t.moveTo(0,2*e),t.lineTo(-cw*e,-e),t.lineTo(cw*e,-e),t.closePath()}},fw=-.5,lw=Math.sqrt(3)/2,hw=1/Math.sqrt(12),pw=3*(hw/2+1),dw={draw:function(t,n){var e=Math.sqrt(n/pw),r=e/2,i=e*hw,o=r,u=e*hw+e,a=-o,c=u;t.moveTo(r,i),t.lineTo(o,u),t.lineTo(a,c),t.lineTo(fw*r-lw*i,lw*r+fw*i),t.lineTo(fw*o-lw*u,lw*o+fw*u),t.lineTo(fw*a-lw*c,lw*a+fw*c),t.lineTo(fw*r+lw*i,fw*i-lw*r),t.lineTo(fw*o+lw*u,fw*u-lw*o),t.lineTo(fw*a+lw*c,fw*c-lw*a),t.closePath()}},vw=[Jb,Qb,nw,aw,uw,sw,dw],_w=function(){function t(){var t;if(r||(r=t=Le()),n.apply(this,arguments).draw(r,+e.apply(this,arguments)),t)return r=null,t+""||null}var n=Ab(Jb),e=Ab(64),r=null;return t.type=function(e){return arguments.length?(n="function"==typeof e?e:Ab(e),t):n},t.size=function(n){return arguments.length?(e="function"==typeof n?n:Ab(+n),t):e},t.context=function(n){return arguments.length?(r=null==n?null:n,t):r},t},gw=function(){};xc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:mc(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:mc(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}};var yw=function(t){return new xc(t)};bc.prototype={areaStart:gw,areaEnd:gw,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:mc(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}};var mw=function(t){return new bc(t)};wc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var e=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break;case 3:this._point=4;default:mc(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}};var xw=function(t){return new wc(t)};Mc.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,n=this._y,e=t.length-1;if(e>0)for(var r,i=t[0],o=n[0],u=t[e]-i,a=n[e]-o,c=-1;++c<=e;)r=c/e,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*u),this._beta*n[c]+(1-this._beta)*(o+r*a));this._x=this._y=null,this._basis.lineEnd()},point:function(t,n){this._x.push(+t),this._y.push(+n)}};var bw=function t(n){function e(t){return 1===n?new xc(t):new Mc(t,n)}return e.beta=function(n){return t(+n)},e}(.85);kc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Tc(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2,this._x1=t,this._y1=n;break;case 2:this._point=3;default:Tc(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var ww=function t(n){function e(t){return new kc(t,n)}return e.tension=function(n){return t(+n)},e}(0);Sc.prototype={areaStart:gw,areaEnd:gw,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Tc(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Mw=function t(n){function e(t){return new Sc(t,n)}return e.tension=function(n){return t(+n)},e}(0);Nc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Tc(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Tw=function t(n){function e(t){return new Nc(t,n)}return e.tension=function(n){return t(+n)},e}(0);Ac.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3 +;default:Ec(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var kw=function t(n){function e(t){return n?new Ac(t,n):new kc(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);Cc.prototype={areaStart:gw,areaEnd:gw,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Ec(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Sw=function t(n){function e(t){return n?new Cc(t,n):new Sc(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);zc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Ec(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Nw=function t(n){function e(t){return n?new zc(t,n):new Nc(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);Pc.prototype={areaStart:gw,areaEnd:gw,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,n){t=+t,n=+n,this._point?this._context.lineTo(t,n):(this._point=1,this._context.moveTo(t,n))}};var Ew=function(t){return new Pc(t)};Dc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:Uc(this,this._t0,qc(this,this._t0))}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){var e=NaN;if(t=+t,n=+n,t!==this._x1||n!==this._y1){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,Uc(this,qc(this,e=Lc(this,t,n)),e);break;default:Uc(this,this._t0,e=Lc(this,t,n))}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n,this._t0=e}}},(Oc.prototype=Object.create(Dc.prototype)).point=function(t,n){Dc.prototype.point.call(this,n,t)},Fc.prototype={moveTo:function(t,n){this._context.moveTo(n,t)},closePath:function(){this._context.closePath()},lineTo:function(t,n){this._context.lineTo(n,t)},bezierCurveTo:function(t,n,e,r,i,o){this._context.bezierCurveTo(n,t,r,e,o,i)}},Bc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,n=this._y,e=t.length;if(e)if(this._line?this._context.lineTo(t[0],n[0]):this._context.moveTo(t[0],n[0]),2===e)this._context.lineTo(t[1],n[1]);else for(var r=jc(t),i=jc(n),o=0,u=1;u=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}};var Cw=function(t){return new Hc(t,.5)},zw=Array.prototype.slice,Pw=function(t,n){if((r=t.length)>1)for(var e,r,i=1,o=t[n[0]],u=o.length;i=0;)e[n]=n;return e},Lw=function(){function t(t){var o,u,a=n.apply(this,arguments),c=t.length,s=a.length,f=new Array(s);for(o=0;o0){for(var e,r,i,o=0,u=t[0].length;o0){for(var e,r=0,i=t[n[0]],o=i.length;r0&&(r=(e=t[n[0]]).length)>0){for(var e,r,i,o=0,u=1;u=a)return null;var c=t-i.site[0],s=n-i.site[1],f=c*c+s*s;do i=o.cells[r=u],u=null,i.halfedges.forEach(function(e){var r=o.edges[e],a=r.left;if(a!==i.site&&a||(a=r.right)){var c=t-a[0],s=n-a[1],l=c*c+s*s;le?(e+r)/2:Math.min(0,e)||Math.max(0,r),o>i?(i+o)/2:Math.min(0,i)||Math.max(0,o))}function o(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function u(t,n,e){t.on("start.zoom",function(){a(this,arguments).start()}).on("interrupt.zoom end.zoom",function(){a(this,arguments).end()}).tween("zoom",function(){var t=this,r=arguments,i=a(t,r),u=m.apply(t,r),c=e||o(u),s=Math.max(u[1][0]-u[0][0],u[1][1]-u[0][1]),f=t.__zoom,l="function"==typeof n?n.apply(t,r):n,h=N(f.invert(c).concat(s/f.k),l.invert(c).concat(s/l.k));return function(t){if(1===t)t=l;else{var n=h(t),e=s/n[2];t=new Es(e,c[0]-n[0]*e,c[1]-n[1]*e)}i.zoom(null,t)}})}function a(t,n){for(var e,r=0,i=A.length;r0?Ml(this).transition().duration(S).call(u,f,a):Ml(this).call(n.transform,f)}}function h(){if(y.apply(this,arguments)){var n,e,r,i,o=a(this,arguments),u=t.event.changedTouches,c=u.length;for(Cs(),e=0;e li { + position: relative; +} + +.fa-li { + position: absolute; + left: -2.143em; + width: 2.143em; + top: 0.143em; + text-align: center; +} +.fa-li.fa-lg { + left: -1.857em; +} + +.fa-border { + padding: .2em .25em .15em; + border: solid 0.08em #eeeeee; + border-radius: .1em; +} + +.fa-pull-left { + float: left; +} + +.fa-pull-right { + float: right; +} + +.fa.fa-pull-left { + margin-right: .3em; +} +.fa.fa-pull-right { + margin-left: .3em; +} + +/* Deprecated as of 4.4.0 */ +.pull-right { + float: right; +} + +.pull-left { + float: left; +} + +.fa.pull-left { + margin-right: .3em; +} +.fa.pull-right { + margin-left: .3em; +} + +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} + +.fa-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); +} + +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +.fa-rotate-90 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} + +.fa-rotate-180 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.fa-rotate-270 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); +} + +.fa-flip-horizontal { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0); + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} + +.fa-flip-vertical { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); +} + +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical { + filter: none; +} + +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} + +.fa-stack-1x, .fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} + +.fa-stack-1x { + line-height: inherit; +} + +.fa-stack-2x { + font-size: 2em; +} + +.fa-inverse { + color: white; +} + +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.fa-glass:before { + content: "\f000"; +} + +.fa-music:before { + content: "\f001"; +} + +.fa-search:before { + content: "\f002"; +} + +.fa-envelope-o:before { + content: "\f003"; +} + +.fa-heart:before { + content: "\f004"; +} + +.fa-star:before { + content: "\f005"; +} + +.fa-star-o:before { + content: "\f006"; +} + +.fa-user:before { + content: "\f007"; +} + +.fa-film:before { + content: "\f008"; +} + +.fa-th-large:before { + content: "\f009"; +} + +.fa-th:before { + content: "\f00a"; +} + +.fa-th-list:before { + content: "\f00b"; +} + +.fa-check:before { + content: "\f00c"; +} + +.fa-remove:before, +.fa-close:before, +.fa-times:before { + content: "\f00d"; +} + +.fa-search-plus:before { + content: "\f00e"; +} + +.fa-search-minus:before { + content: "\f010"; +} + +.fa-power-off:before { + content: "\f011"; +} + +.fa-signal:before { + content: "\f012"; +} + +.fa-gear:before, +.fa-cog:before { + content: "\f013"; +} + +.fa-trash-o:before { + content: "\f014"; +} + +.fa-home:before { + content: "\f015"; +} + +.fa-file-o:before { + content: "\f016"; +} + +.fa-clock-o:before { + content: "\f017"; +} + +.fa-road:before { + content: "\f018"; +} + +.fa-download:before { + content: "\f019"; +} + +.fa-arrow-circle-o-down:before { + content: "\f01a"; +} + +.fa-arrow-circle-o-up:before { + content: "\f01b"; +} + +.fa-inbox:before { + content: "\f01c"; +} + +.fa-play-circle-o:before { + content: "\f01d"; +} + +.fa-rotate-right:before, +.fa-repeat:before { + content: "\f01e"; +} + +.fa-refresh:before { + content: "\f021"; +} + +.fa-list-alt:before { + content: "\f022"; +} + +.fa-lock:before { + content: "\f023"; +} + +.fa-flag:before { + content: "\f024"; +} + +.fa-headphones:before { + content: "\f025"; +} + +.fa-volume-off:before { + content: "\f026"; +} + +.fa-volume-down:before { + content: "\f027"; +} + +.fa-volume-up:before { + content: "\f028"; +} + +.fa-qrcode:before { + content: "\f029"; +} + +.fa-barcode:before { + content: "\f02a"; +} + +.fa-tag:before { + content: "\f02b"; +} + +.fa-tags:before { + content: "\f02c"; +} + +.fa-book:before { + content: "\f02d"; +} + +.fa-bookmark:before { + content: "\f02e"; +} + +.fa-print:before { + content: "\f02f"; +} + +.fa-camera:before { + content: "\f030"; +} + +.fa-font:before { + content: "\f031"; +} + +.fa-bold:before { + content: "\f032"; +} + +.fa-italic:before { + content: "\f033"; +} + +.fa-text-height:before { + content: "\f034"; +} + +.fa-text-width:before { + content: "\f035"; +} + +.fa-align-left:before { + content: "\f036"; +} + +.fa-align-center:before { + content: "\f037"; +} + +.fa-align-right:before { + content: "\f038"; +} + +.fa-align-justify:before { + content: "\f039"; +} + +.fa-list:before { + content: "\f03a"; +} + +.fa-dedent:before, +.fa-outdent:before { + content: "\f03b"; +} + +.fa-indent:before { + content: "\f03c"; +} + +.fa-video-camera:before { + content: "\f03d"; +} + +.fa-photo:before, +.fa-image:before, +.fa-picture-o:before { + content: "\f03e"; +} + +.fa-pencil:before { + content: "\f040"; +} + +.fa-map-marker:before { + content: "\f041"; +} + +.fa-adjust:before { + content: "\f042"; +} + +.fa-tint:before { + content: "\f043"; +} + +.fa-edit:before, +.fa-pencil-square-o:before { + content: "\f044"; +} + +.fa-share-square-o:before { + content: "\f045"; +} + +.fa-check-square-o:before { + content: "\f046"; +} + +.fa-arrows:before { + content: "\f047"; +} + +.fa-step-backward:before { + content: "\f048"; +} + +.fa-fast-backward:before { + content: "\f049"; +} + +.fa-backward:before { + content: "\f04a"; +} + +.fa-play:before { + content: "\f04b"; +} + +.fa-pause:before { + content: "\f04c"; +} + +.fa-stop:before { + content: "\f04d"; +} + +.fa-forward:before { + content: "\f04e"; +} + +.fa-fast-forward:before { + content: "\f050"; +} + +.fa-step-forward:before { + content: "\f051"; +} + +.fa-eject:before { + content: "\f052"; +} + +.fa-chevron-left:before { + content: "\f053"; +} + +.fa-chevron-right:before { + content: "\f054"; +} + +.fa-plus-circle:before { + content: "\f055"; +} + +.fa-minus-circle:before { + content: "\f056"; +} + +.fa-times-circle:before { + content: "\f057"; +} + +.fa-check-circle:before { + content: "\f058"; +} + +.fa-question-circle:before { + content: "\f059"; +} + +.fa-info-circle:before { + content: "\f05a"; +} + +.fa-crosshairs:before { + content: "\f05b"; +} + +.fa-times-circle-o:before { + content: "\f05c"; +} + +.fa-check-circle-o:before { + content: "\f05d"; +} + +.fa-ban:before { + content: "\f05e"; +} + +.fa-arrow-left:before { + content: "\f060"; +} + +.fa-arrow-right:before { + content: "\f061"; +} + +.fa-arrow-up:before { + content: "\f062"; +} + +.fa-arrow-down:before { + content: "\f063"; +} + +.fa-mail-forward:before, +.fa-share:before { + content: "\f064"; +} + +.fa-expand:before { + content: "\f065"; +} + +.fa-compress:before { + content: "\f066"; +} + +.fa-plus:before { + content: "\f067"; +} + +.fa-minus:before { + content: "\f068"; +} + +.fa-asterisk:before { + content: "\f069"; +} + +.fa-exclamation-circle:before { + content: "\f06a"; +} + +.fa-gift:before { + content: "\f06b"; +} + +.fa-leaf:before { + content: "\f06c"; +} + +.fa-fire:before { + content: "\f06d"; +} + +.fa-eye:before { + content: "\f06e"; +} + +.fa-eye-slash:before { + content: "\f070"; +} + +.fa-warning:before, +.fa-exclamation-triangle:before { + content: "\f071"; +} + +.fa-plane:before { + content: "\f072"; +} + +.fa-calendar:before { + content: "\f073"; +} + +.fa-random:before { + content: "\f074"; +} + +.fa-comment:before { + content: "\f075"; +} + +.fa-magnet:before { + content: "\f076"; +} + +.fa-chevron-up:before { + content: "\f077"; +} + +.fa-chevron-down:before { + content: "\f078"; +} + +.fa-retweet:before { + content: "\f079"; +} + +.fa-shopping-cart:before { + content: "\f07a"; +} + +.fa-folder:before { + content: "\f07b"; +} + +.fa-folder-open:before { + content: "\f07c"; +} + +.fa-arrows-v:before { + content: "\f07d"; +} + +.fa-arrows-h:before { + content: "\f07e"; +} + +.fa-bar-chart-o:before, +.fa-bar-chart:before { + content: "\f080"; +} + +.fa-twitter-square:before { + content: "\f081"; +} + +.fa-facebook-square:before { + content: "\f082"; +} + +.fa-camera-retro:before { + content: "\f083"; +} + +.fa-key:before { + content: "\f084"; +} + +.fa-gears:before, +.fa-cogs:before { + content: "\f085"; +} + +.fa-comments:before { + content: "\f086"; +} + +.fa-thumbs-o-up:before { + content: "\f087"; +} + +.fa-thumbs-o-down:before { + content: "\f088"; +} + +.fa-star-half:before { + content: "\f089"; +} + +.fa-heart-o:before { + content: "\f08a"; +} + +.fa-sign-out:before { + content: "\f08b"; +} + +.fa-linkedin-square:before { + content: "\f08c"; +} + +.fa-thumb-tack:before { + content: "\f08d"; +} + +.fa-external-link:before { + content: "\f08e"; +} + +.fa-sign-in:before { + content: "\f090"; +} + +.fa-trophy:before { + content: "\f091"; +} + +.fa-github-square:before { + content: "\f092"; +} + +.fa-upload:before { + content: "\f093"; +} + +.fa-lemon-o:before { + content: "\f094"; +} + +.fa-phone:before { + content: "\f095"; +} + +.fa-square-o:before { + content: "\f096"; +} + +.fa-bookmark-o:before { + content: "\f097"; +} + +.fa-phone-square:before { + content: "\f098"; +} + +.fa-twitter:before { + content: "\f099"; +} + +.fa-facebook-f:before, +.fa-facebook:before { + content: "\f09a"; +} + +.fa-github:before { + content: "\f09b"; +} + +.fa-unlock:before { + content: "\f09c"; +} + +.fa-credit-card:before { + content: "\f09d"; +} + +.fa-feed:before, +.fa-rss:before { + content: "\f09e"; +} + +.fa-hdd-o:before { + content: "\f0a0"; +} + +.fa-bullhorn:before { + content: "\f0a1"; +} + +.fa-bell:before { + content: "\f0f3"; +} + +.fa-certificate:before { + content: "\f0a3"; +} + +.fa-hand-o-right:before { + content: "\f0a4"; +} + +.fa-hand-o-left:before { + content: "\f0a5"; +} + +.fa-hand-o-up:before { + content: "\f0a6"; +} + +.fa-hand-o-down:before { + content: "\f0a7"; +} + +.fa-arrow-circle-left:before { + content: "\f0a8"; +} + +.fa-arrow-circle-right:before { + content: "\f0a9"; +} + +.fa-arrow-circle-up:before { + content: "\f0aa"; +} + +.fa-arrow-circle-down:before { + content: "\f0ab"; +} + +.fa-globe:before { + content: "\f0ac"; +} + +.fa-wrench:before { + content: "\f0ad"; +} + +.fa-tasks:before { + content: "\f0ae"; +} + +.fa-filter:before { + content: "\f0b0"; +} + +.fa-briefcase:before { + content: "\f0b1"; +} + +.fa-arrows-alt:before { + content: "\f0b2"; +} + +.fa-group:before, +.fa-users:before { + content: "\f0c0"; +} + +.fa-chain:before, +.fa-link:before { + content: "\f0c1"; +} + +.fa-cloud:before { + content: "\f0c2"; +} + +.fa-flask:before { + content: "\f0c3"; +} + +.fa-cut:before, +.fa-scissors:before { + content: "\f0c4"; +} + +.fa-copy:before, +.fa-files-o:before { + content: "\f0c5"; +} + +.fa-paperclip:before { + content: "\f0c6"; +} + +.fa-save:before, +.fa-floppy-o:before { + content: "\f0c7"; +} + +.fa-square:before { + content: "\f0c8"; +} + +.fa-navicon:before, +.fa-reorder:before, +.fa-bars:before { + content: "\f0c9"; +} + +.fa-list-ul:before { + content: "\f0ca"; +} + +.fa-list-ol:before { + content: "\f0cb"; +} + +.fa-strikethrough:before { + content: "\f0cc"; +} + +.fa-underline:before { + content: "\f0cd"; +} + +.fa-table:before { + content: "\f0ce"; +} + +.fa-magic:before { + content: "\f0d0"; +} + +.fa-truck:before { + content: "\f0d1"; +} + +.fa-pinterest:before { + content: "\f0d2"; +} + +.fa-pinterest-square:before { + content: "\f0d3"; +} + +.fa-google-plus-square:before { + content: "\f0d4"; +} + +.fa-google-plus:before { + content: "\f0d5"; +} + +.fa-money:before { + content: "\f0d6"; +} + +.fa-caret-down:before { + content: "\f0d7"; +} + +.fa-caret-up:before { + content: "\f0d8"; +} + +.fa-caret-left:before { + content: "\f0d9"; +} + +.fa-caret-right:before { + content: "\f0da"; +} + +.fa-columns:before { + content: "\f0db"; +} + +.fa-unsorted:before, +.fa-sort:before { + content: "\f0dc"; +} + +.fa-sort-down:before, +.fa-sort-desc:before { + content: "\f0dd"; +} + +.fa-sort-up:before, +.fa-sort-asc:before { + content: "\f0de"; +} + +.fa-envelope:before { + content: "\f0e0"; +} + +.fa-linkedin:before { + content: "\f0e1"; +} + +.fa-rotate-left:before, +.fa-undo:before { + content: "\f0e2"; +} + +.fa-legal:before, +.fa-gavel:before { + content: "\f0e3"; +} + +.fa-dashboard:before, +.fa-tachometer:before { + content: "\f0e4"; +} + +.fa-comment-o:before { + content: "\f0e5"; +} + +.fa-comments-o:before { + content: "\f0e6"; +} + +.fa-flash:before, +.fa-bolt:before { + content: "\f0e7"; +} + +.fa-sitemap:before { + content: "\f0e8"; +} + +.fa-umbrella:before { + content: "\f0e9"; +} + +.fa-paste:before, +.fa-clipboard:before { + content: "\f0ea"; +} + +.fa-lightbulb-o:before { + content: "\f0eb"; +} + +.fa-exchange:before { + content: "\f0ec"; +} + +.fa-cloud-download:before { + content: "\f0ed"; +} + +.fa-cloud-upload:before { + content: "\f0ee"; +} + +.fa-user-md:before { + content: "\f0f0"; +} + +.fa-stethoscope:before { + content: "\f0f1"; +} + +.fa-suitcase:before { + content: "\f0f2"; +} + +.fa-bell-o:before { + content: "\f0a2"; +} + +.fa-coffee:before { + content: "\f0f4"; +} + +.fa-cutlery:before { + content: "\f0f5"; +} + +.fa-file-text-o:before { + content: "\f0f6"; +} + +.fa-building-o:before { + content: "\f0f7"; +} + +.fa-hospital-o:before { + content: "\f0f8"; +} + +.fa-ambulance:before { + content: "\f0f9"; +} + +.fa-medkit:before { + content: "\f0fa"; +} + +.fa-fighter-jet:before { + content: "\f0fb"; +} + +.fa-beer:before { + content: "\f0fc"; +} + +.fa-h-square:before { + content: "\f0fd"; +} + +.fa-plus-square:before { + content: "\f0fe"; +} + +.fa-angle-double-left:before { + content: "\f100"; +} + +.fa-angle-double-right:before { + content: "\f101"; +} + +.fa-angle-double-up:before { + content: "\f102"; +} + +.fa-angle-double-down:before { + content: "\f103"; +} + +.fa-angle-left:before { + content: "\f104"; +} + +.fa-angle-right:before { + content: "\f105"; +} + +.fa-angle-up:before { + content: "\f106"; +} + +.fa-angle-down:before { + content: "\f107"; +} + +.fa-desktop:before { + content: "\f108"; +} + +.fa-laptop:before { + content: "\f109"; +} + +.fa-tablet:before { + content: "\f10a"; +} + +.fa-mobile-phone:before, +.fa-mobile:before { + content: "\f10b"; +} + +.fa-circle-o:before { + content: "\f10c"; +} + +.fa-quote-left:before { + content: "\f10d"; +} + +.fa-quote-right:before { + content: "\f10e"; +} + +.fa-spinner:before { + content: "\f110"; +} + +.fa-circle:before { + content: "\f111"; +} + +.fa-mail-reply:before, +.fa-reply:before { + content: "\f112"; +} + +.fa-github-alt:before { + content: "\f113"; +} + +.fa-folder-o:before { + content: "\f114"; +} + +.fa-folder-open-o:before { + content: "\f115"; +} + +.fa-smile-o:before { + content: "\f118"; +} + +.fa-frown-o:before { + content: "\f119"; +} + +.fa-meh-o:before { + content: "\f11a"; +} + +.fa-gamepad:before { + content: "\f11b"; +} + +.fa-keyboard-o:before { + content: "\f11c"; +} + +.fa-flag-o:before { + content: "\f11d"; +} + +.fa-flag-checkered:before { + content: "\f11e"; +} + +.fa-terminal:before { + content: "\f120"; +} + +.fa-code:before { + content: "\f121"; +} + +.fa-mail-reply-all:before, +.fa-reply-all:before { + content: "\f122"; +} + +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "\f123"; +} + +.fa-location-arrow:before { + content: "\f124"; +} + +.fa-crop:before { + content: "\f125"; +} + +.fa-code-fork:before { + content: "\f126"; +} + +.fa-unlink:before, +.fa-chain-broken:before { + content: "\f127"; +} + +.fa-question:before { + content: "\f128"; +} + +.fa-info:before { + content: "\f129"; +} + +.fa-exclamation:before { + content: "\f12a"; +} + +.fa-superscript:before { + content: "\f12b"; +} + +.fa-subscript:before { + content: "\f12c"; +} + +.fa-eraser:before { + content: "\f12d"; +} + +.fa-puzzle-piece:before { + content: "\f12e"; +} + +.fa-microphone:before { + content: "\f130"; +} + +.fa-microphone-slash:before { + content: "\f131"; +} + +.fa-shield:before { + content: "\f132"; +} + +.fa-calendar-o:before { + content: "\f133"; +} + +.fa-fire-extinguisher:before { + content: "\f134"; +} + +.fa-rocket:before { + content: "\f135"; +} + +.fa-maxcdn:before { + content: "\f136"; +} + +.fa-chevron-circle-left:before { + content: "\f137"; +} + +.fa-chevron-circle-right:before { + content: "\f138"; +} + +.fa-chevron-circle-up:before { + content: "\f139"; +} + +.fa-chevron-circle-down:before { + content: "\f13a"; +} + +.fa-html5:before { + content: "\f13b"; +} + +.fa-css3:before { + content: "\f13c"; +} + +.fa-anchor:before { + content: "\f13d"; +} + +.fa-unlock-alt:before { + content: "\f13e"; +} + +.fa-bullseye:before { + content: "\f140"; +} + +.fa-ellipsis-h:before { + content: "\f141"; +} + +.fa-ellipsis-v:before { + content: "\f142"; +} + +.fa-rss-square:before { + content: "\f143"; +} + +.fa-play-circle:before { + content: "\f144"; +} + +.fa-ticket:before { + content: "\f145"; +} + +.fa-minus-square:before { + content: "\f146"; +} + +.fa-minus-square-o:before { + content: "\f147"; +} + +.fa-level-up:before { + content: "\f148"; +} + +.fa-level-down:before { + content: "\f149"; +} + +.fa-check-square:before { + content: "\f14a"; +} + +.fa-pencil-square:before { + content: "\f14b"; +} + +.fa-external-link-square:before { + content: "\f14c"; +} + +.fa-share-square:before { + content: "\f14d"; +} + +.fa-compass:before { + content: "\f14e"; +} + +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: "\f150"; +} + +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: "\f151"; +} + +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: "\f152"; +} + +.fa-euro:before, +.fa-eur:before { + content: "\f153"; +} + +.fa-gbp:before { + content: "\f154"; +} + +.fa-dollar:before, +.fa-usd:before { + content: "\f155"; +} + +.fa-rupee:before, +.fa-inr:before { + content: "\f156"; +} + +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: "\f157"; +} + +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: "\f158"; +} + +.fa-won:before, +.fa-krw:before { + content: "\f159"; +} + +.fa-bitcoin:before, +.fa-btc:before { + content: "\f15a"; +} + +.fa-file:before { + content: "\f15b"; +} + +.fa-file-text:before { + content: "\f15c"; +} + +.fa-sort-alpha-asc:before { + content: "\f15d"; +} + +.fa-sort-alpha-desc:before { + content: "\f15e"; +} + +.fa-sort-amount-asc:before { + content: "\f160"; +} + +.fa-sort-amount-desc:before { + content: "\f161"; +} + +.fa-sort-numeric-asc:before { + content: "\f162"; +} + +.fa-sort-numeric-desc:before { + content: "\f163"; +} + +.fa-thumbs-up:before { + content: "\f164"; +} + +.fa-thumbs-down:before { + content: "\f165"; +} + +.fa-youtube-square:before { + content: "\f166"; +} + +.fa-youtube:before { + content: "\f167"; +} + +.fa-xing:before { + content: "\f168"; +} + +.fa-xing-square:before { + content: "\f169"; +} + +.fa-youtube-play:before { + content: "\f16a"; +} + +.fa-dropbox:before { + content: "\f16b"; +} + +.fa-stack-overflow:before { + content: "\f16c"; +} + +.fa-instagram:before { + content: "\f16d"; +} + +.fa-flickr:before { + content: "\f16e"; +} + +.fa-adn:before { + content: "\f170"; +} + +.fa-bitbucket:before { + content: "\f171"; +} + +.fa-bitbucket-square:before { + content: "\f172"; +} + +.fa-tumblr:before { + content: "\f173"; +} + +.fa-tumblr-square:before { + content: "\f174"; +} + +.fa-long-arrow-down:before { + content: "\f175"; +} + +.fa-long-arrow-up:before { + content: "\f176"; +} + +.fa-long-arrow-left:before { + content: "\f177"; +} + +.fa-long-arrow-right:before { + content: "\f178"; +} + +.fa-apple:before { + content: "\f179"; +} + +.fa-windows:before { + content: "\f17a"; +} + +.fa-android:before { + content: "\f17b"; +} + +.fa-linux:before { + content: "\f17c"; +} + +.fa-dribbble:before { + content: "\f17d"; +} + +.fa-skype:before { + content: "\f17e"; +} + +.fa-foursquare:before { + content: "\f180"; +} + +.fa-trello:before { + content: "\f181"; +} + +.fa-female:before { + content: "\f182"; +} + +.fa-male:before { + content: "\f183"; +} + +.fa-gittip:before, +.fa-gratipay:before { + content: "\f184"; +} + +.fa-sun-o:before { + content: "\f185"; +} + +.fa-moon-o:before { + content: "\f186"; +} + +.fa-archive:before { + content: "\f187"; +} + +.fa-bug:before { + content: "\f188"; +} + +.fa-vk:before { + content: "\f189"; +} + +.fa-weibo:before { + content: "\f18a"; +} + +.fa-renren:before { + content: "\f18b"; +} + +.fa-pagelines:before { + content: "\f18c"; +} + +.fa-stack-exchange:before { + content: "\f18d"; +} + +.fa-arrow-circle-o-right:before { + content: "\f18e"; +} + +.fa-arrow-circle-o-left:before { + content: "\f190"; +} + +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: "\f191"; +} + +.fa-dot-circle-o:before { + content: "\f192"; +} + +.fa-wheelchair:before { + content: "\f193"; +} + +.fa-vimeo-square:before { + content: "\f194"; +} + +.fa-turkish-lira:before, +.fa-try:before { + content: "\f195"; +} + +.fa-plus-square-o:before { + content: "\f196"; +} + +.fa-space-shuttle:before { + content: "\f197"; +} + +.fa-slack:before { + content: "\f198"; +} + +.fa-envelope-square:before { + content: "\f199"; +} + +.fa-wordpress:before { + content: "\f19a"; +} + +.fa-openid:before { + content: "\f19b"; +} + +.fa-institution:before, +.fa-bank:before, +.fa-university:before { + content: "\f19c"; +} + +.fa-mortar-board:before, +.fa-graduation-cap:before { + content: "\f19d"; +} + +.fa-yahoo:before { + content: "\f19e"; +} + +.fa-google:before { + content: "\f1a0"; +} + +.fa-reddit:before { + content: "\f1a1"; +} + +.fa-reddit-square:before { + content: "\f1a2"; +} + +.fa-stumbleupon-circle:before { + content: "\f1a3"; +} + +.fa-stumbleupon:before { + content: "\f1a4"; +} + +.fa-delicious:before { + content: "\f1a5"; +} + +.fa-digg:before { + content: "\f1a6"; +} + +.fa-pied-piper:before { + content: "\f1a7"; +} + +.fa-pied-piper-alt:before { + content: "\f1a8"; +} + +.fa-drupal:before { + content: "\f1a9"; +} + +.fa-joomla:before { + content: "\f1aa"; +} + +.fa-language:before { + content: "\f1ab"; +} + +.fa-fax:before { + content: "\f1ac"; +} + +.fa-building:before { + content: "\f1ad"; +} + +.fa-child:before { + content: "\f1ae"; +} + +.fa-paw:before { + content: "\f1b0"; +} + +.fa-spoon:before { + content: "\f1b1"; +} + +.fa-cube:before { + content: "\f1b2"; +} + +.fa-cubes:before { + content: "\f1b3"; +} + +.fa-behance:before { + content: "\f1b4"; +} + +.fa-behance-square:before { + content: "\f1b5"; +} + +.fa-steam:before { + content: "\f1b6"; +} + +.fa-steam-square:before { + content: "\f1b7"; +} + +.fa-recycle:before { + content: "\f1b8"; +} + +.fa-automobile:before, +.fa-car:before { + content: "\f1b9"; +} + +.fa-cab:before, +.fa-taxi:before { + content: "\f1ba"; +} + +.fa-tree:before { + content: "\f1bb"; +} + +.fa-spotify:before { + content: "\f1bc"; +} + +.fa-deviantart:before { + content: "\f1bd"; +} + +.fa-soundcloud:before { + content: "\f1be"; +} + +.fa-database:before { + content: "\f1c0"; +} + +.fa-file-pdf-o:before { + content: "\f1c1"; +} + +.fa-file-word-o:before { + content: "\f1c2"; +} + +.fa-file-excel-o:before { + content: "\f1c3"; +} + +.fa-file-powerpoint-o:before { + content: "\f1c4"; +} + +.fa-file-photo-o:before, +.fa-file-picture-o:before, +.fa-file-image-o:before { + content: "\f1c5"; +} + +.fa-file-zip-o:before, +.fa-file-archive-o:before { + content: "\f1c6"; +} + +.fa-file-sound-o:before, +.fa-file-audio-o:before { + content: "\f1c7"; +} + +.fa-file-movie-o:before, +.fa-file-video-o:before { + content: "\f1c8"; +} + +.fa-file-code-o:before { + content: "\f1c9"; +} + +.fa-vine:before { + content: "\f1ca"; +} + +.fa-codepen:before { + content: "\f1cb"; +} + +.fa-jsfiddle:before { + content: "\f1cc"; +} + +.fa-life-bouy:before, +.fa-life-buoy:before, +.fa-life-saver:before, +.fa-support:before, +.fa-life-ring:before { + content: "\f1cd"; +} + +.fa-circle-o-notch:before { + content: "\f1ce"; +} + +.fa-ra:before, +.fa-rebel:before { + content: "\f1d0"; +} + +.fa-ge:before, +.fa-empire:before { + content: "\f1d1"; +} + +.fa-git-square:before { + content: "\f1d2"; +} + +.fa-git:before { + content: "\f1d3"; +} + +.fa-y-combinator-square:before, +.fa-yc-square:before, +.fa-hacker-news:before { + content: "\f1d4"; +} + +.fa-tencent-weibo:before { + content: "\f1d5"; +} + +.fa-qq:before { + content: "\f1d6"; +} + +.fa-wechat:before, +.fa-weixin:before { + content: "\f1d7"; +} + +.fa-send:before, +.fa-paper-plane:before { + content: "\f1d8"; +} + +.fa-send-o:before, +.fa-paper-plane-o:before { + content: "\f1d9"; +} + +.fa-history:before { + content: "\f1da"; +} + +.fa-circle-thin:before { + content: "\f1db"; +} + +.fa-header:before { + content: "\f1dc"; +} + +.fa-paragraph:before { + content: "\f1dd"; +} + +.fa-sliders:before { + content: "\f1de"; +} + +.fa-share-alt:before { + content: "\f1e0"; +} + +.fa-share-alt-square:before { + content: "\f1e1"; +} + +.fa-bomb:before { + content: "\f1e2"; +} + +.fa-soccer-ball-o:before, +.fa-futbol-o:before { + content: "\f1e3"; +} + +.fa-tty:before { + content: "\f1e4"; +} + +.fa-binoculars:before { + content: "\f1e5"; +} + +.fa-plug:before { + content: "\f1e6"; +} + +.fa-slideshare:before { + content: "\f1e7"; +} + +.fa-twitch:before { + content: "\f1e8"; +} + +.fa-yelp:before { + content: "\f1e9"; +} + +.fa-newspaper-o:before { + content: "\f1ea"; +} + +.fa-wifi:before { + content: "\f1eb"; +} + +.fa-calculator:before { + content: "\f1ec"; +} + +.fa-paypal:before { + content: "\f1ed"; +} + +.fa-google-wallet:before { + content: "\f1ee"; +} + +.fa-cc-visa:before { + content: "\f1f0"; +} + +.fa-cc-mastercard:before { + content: "\f1f1"; +} + +.fa-cc-discover:before { + content: "\f1f2"; +} + +.fa-cc-amex:before { + content: "\f1f3"; +} + +.fa-cc-paypal:before { + content: "\f1f4"; +} + +.fa-cc-stripe:before { + content: "\f1f5"; +} + +.fa-bell-slash:before { + content: "\f1f6"; +} + +.fa-bell-slash-o:before { + content: "\f1f7"; +} + +.fa-trash:before { + content: "\f1f8"; +} + +.fa-copyright:before { + content: "\f1f9"; +} + +.fa-at:before { + content: "\f1fa"; +} + +.fa-eyedropper:before { + content: "\f1fb"; +} + +.fa-paint-brush:before { + content: "\f1fc"; +} + +.fa-birthday-cake:before { + content: "\f1fd"; +} + +.fa-area-chart:before { + content: "\f1fe"; +} + +.fa-pie-chart:before { + content: "\f200"; +} + +.fa-line-chart:before { + content: "\f201"; +} + +.fa-lastfm:before { + content: "\f202"; +} + +.fa-lastfm-square:before { + content: "\f203"; +} + +.fa-toggle-off:before { + content: "\f204"; +} + +.fa-toggle-on:before { + content: "\f205"; +} + +.fa-bicycle:before { + content: "\f206"; +} + +.fa-bus:before { + content: "\f207"; +} + +.fa-ioxhost:before { + content: "\f208"; +} + +.fa-angellist:before { + content: "\f209"; +} + +.fa-cc:before { + content: "\f20a"; +} + +.fa-shekel:before, +.fa-sheqel:before, +.fa-ils:before { + content: "\f20b"; +} + +.fa-meanpath:before { + content: "\f20c"; +} + +.fa-buysellads:before { + content: "\f20d"; +} + +.fa-connectdevelop:before { + content: "\f20e"; +} + +.fa-dashcube:before { + content: "\f210"; +} + +.fa-forumbee:before { + content: "\f211"; +} + +.fa-leanpub:before { + content: "\f212"; +} + +.fa-sellsy:before { + content: "\f213"; +} + +.fa-shirtsinbulk:before { + content: "\f214"; +} + +.fa-simplybuilt:before { + content: "\f215"; +} + +.fa-skyatlas:before { + content: "\f216"; +} + +.fa-cart-plus:before { + content: "\f217"; +} + +.fa-cart-arrow-down:before { + content: "\f218"; +} + +.fa-diamond:before { + content: "\f219"; +} + +.fa-ship:before { + content: "\f21a"; +} + +.fa-user-secret:before { + content: "\f21b"; +} + +.fa-motorcycle:before { + content: "\f21c"; +} + +.fa-street-view:before { + content: "\f21d"; +} + +.fa-heartbeat:before { + content: "\f21e"; +} + +.fa-venus:before { + content: "\f221"; +} + +.fa-mars:before { + content: "\f222"; +} + +.fa-mercury:before { + content: "\f223"; +} + +.fa-intersex:before, +.fa-transgender:before { + content: "\f224"; +} + +.fa-transgender-alt:before { + content: "\f225"; +} + +.fa-venus-double:before { + content: "\f226"; +} + +.fa-mars-double:before { + content: "\f227"; +} + +.fa-venus-mars:before { + content: "\f228"; +} + +.fa-mars-stroke:before { + content: "\f229"; +} + +.fa-mars-stroke-v:before { + content: "\f22a"; +} + +.fa-mars-stroke-h:before { + content: "\f22b"; +} + +.fa-neuter:before { + content: "\f22c"; +} + +.fa-genderless:before { + content: "\f22d"; +} + +.fa-facebook-official:before { + content: "\f230"; +} + +.fa-pinterest-p:before { + content: "\f231"; +} + +.fa-whatsapp:before { + content: "\f232"; +} + +.fa-server:before { + content: "\f233"; +} + +.fa-user-plus:before { + content: "\f234"; +} + +.fa-user-times:before { + content: "\f235"; +} + +.fa-hotel:before, +.fa-bed:before { + content: "\f236"; +} + +.fa-viacoin:before { + content: "\f237"; +} + +.fa-train:before { + content: "\f238"; +} + +.fa-subway:before { + content: "\f239"; +} + +.fa-medium:before { + content: "\f23a"; +} + +.fa-yc:before, +.fa-y-combinator:before { + content: "\f23b"; +} + +.fa-optin-monster:before { + content: "\f23c"; +} + +.fa-opencart:before { + content: "\f23d"; +} + +.fa-expeditedssl:before { + content: "\f23e"; +} + +.fa-battery-4:before, +.fa-battery-full:before { + content: "\f240"; +} + +.fa-battery-3:before, +.fa-battery-three-quarters:before { + content: "\f241"; +} + +.fa-battery-2:before, +.fa-battery-half:before { + content: "\f242"; +} + +.fa-battery-1:before, +.fa-battery-quarter:before { + content: "\f243"; +} + +.fa-battery-0:before, +.fa-battery-empty:before { + content: "\f244"; +} + +.fa-mouse-pointer:before { + content: "\f245"; +} + +.fa-i-cursor:before { + content: "\f246"; +} + +.fa-object-group:before { + content: "\f247"; +} + +.fa-object-ungroup:before { + content: "\f248"; +} + +.fa-sticky-note:before { + content: "\f249"; +} + +.fa-sticky-note-o:before { + content: "\f24a"; +} + +.fa-cc-jcb:before { + content: "\f24b"; +} + +.fa-cc-diners-club:before { + content: "\f24c"; +} + +.fa-clone:before { + content: "\f24d"; +} + +.fa-balance-scale:before { + content: "\f24e"; +} + +.fa-hourglass-o:before { + content: "\f250"; +} + +.fa-hourglass-1:before, +.fa-hourglass-start:before { + content: "\f251"; +} + +.fa-hourglass-2:before, +.fa-hourglass-half:before { + content: "\f252"; +} + +.fa-hourglass-3:before, +.fa-hourglass-end:before { + content: "\f253"; +} + +.fa-hourglass:before { + content: "\f254"; +} + +.fa-hand-grab-o:before, +.fa-hand-rock-o:before { + content: "\f255"; +} + +.fa-hand-stop-o:before, +.fa-hand-paper-o:before { + content: "\f256"; +} + +.fa-hand-scissors-o:before { + content: "\f257"; +} + +.fa-hand-lizard-o:before { + content: "\f258"; +} + +.fa-hand-spock-o:before { + content: "\f259"; +} + +.fa-hand-pointer-o:before { + content: "\f25a"; +} + +.fa-hand-peace-o:before { + content: "\f25b"; +} + +.fa-trademark:before { + content: "\f25c"; +} + +.fa-registered:before { + content: "\f25d"; +} + +.fa-creative-commons:before { + content: "\f25e"; +} + +.fa-gg:before { + content: "\f260"; +} + +.fa-gg-circle:before { + content: "\f261"; +} + +.fa-tripadvisor:before { + content: "\f262"; +} + +.fa-odnoklassniki:before { + content: "\f263"; +} + +.fa-odnoklassniki-square:before { + content: "\f264"; +} + +.fa-get-pocket:before { + content: "\f265"; +} + +.fa-wikipedia-w:before { + content: "\f266"; +} + +.fa-safari:before { + content: "\f267"; +} + +.fa-chrome:before { + content: "\f268"; +} + +.fa-firefox:before { + content: "\f269"; +} + +.fa-opera:before { + content: "\f26a"; +} + +.fa-internet-explorer:before { + content: "\f26b"; +} + +.fa-tv:before, +.fa-television:before { + content: "\f26c"; +} + +.fa-contao:before { + content: "\f26d"; +} + +.fa-500px:before { + content: "\f26e"; +} + +.fa-amazon:before { + content: "\f270"; +} + +.fa-calendar-plus-o:before { + content: "\f271"; +} + +.fa-calendar-minus-o:before { + content: "\f272"; +} + +.fa-calendar-times-o:before { + content: "\f273"; +} + +.fa-calendar-check-o:before { + content: "\f274"; +} + +.fa-industry:before { + content: "\f275"; +} + +.fa-map-pin:before { + content: "\f276"; +} + +.fa-map-signs:before { + content: "\f277"; +} + +.fa-map-o:before { + content: "\f278"; +} + +.fa-map:before { + content: "\f279"; +} + +.fa-commenting:before { + content: "\f27a"; +} + +.fa-commenting-o:before { + content: "\f27b"; +} + +.fa-houzz:before { + content: "\f27c"; +} + +.fa-vimeo:before { + content: "\f27d"; +} + +.fa-black-tie:before { + content: "\f27e"; +} + +.fa-fonticons:before { + content: "\f280"; +} + +.fa-reddit-alien:before { + content: "\f281"; +} + +.fa-edge:before { + content: "\f282"; +} + +.fa-credit-card-alt:before { + content: "\f283"; +} + +.fa-codiepie:before { + content: "\f284"; +} + +.fa-modx:before { + content: "\f285"; +} + +.fa-fort-awesome:before { + content: "\f286"; +} + +.fa-usb:before { + content: "\f287"; +} + +.fa-product-hunt:before { + content: "\f288"; +} + +.fa-mixcloud:before { + content: "\f289"; +} + +.fa-scribd:before { + content: "\f28a"; +} + +.fa-pause-circle:before { + content: "\f28b"; +} + +.fa-pause-circle-o:before { + content: "\f28c"; +} + +.fa-stop-circle:before { + content: "\f28d"; +} + +.fa-stop-circle-o:before { + content: "\f28e"; +} + +.fa-shopping-bag:before { + content: "\f290"; +} + +.fa-shopping-basket:before { + content: "\f291"; +} + +.fa-hashtag:before { + content: "\f292"; +} + +.fa-bluetooth:before { + content: "\f293"; +} + +.fa-bluetooth-b:before { + content: "\f294"; +} + +.fa-percent:before { + content: "\f295"; +} + +/* including package baseapp */ +/* including package baseapp */ +/* including package rapture-theme */ +/** + * @class Ext.Component + */ +/** + * @class Ext.button.Button + */ +/** + * @class Ext.toolbar.Toolbar + */ +/** + * @class Ext.panel.Panel + */ +/** + * @class Ext.tip.ToolTip + */ +/** + * @class Ext.form.field.Base + */ +/** + * @class Ext.window.Window + */ +/** + * @class Ext.LoadMask + */ +/** + * @class Ext.tip.QuickTip + */ +/** + * @class Ext.form.Fieldset + */ +/* including package ext-theme-neptune */ +/** @class Global_CSS */ +/** @class Ext.button.Button */ +/** @class Ext.toolbar.Toolbar */ +/** @class Ext.panel.Panel */ +/** + * @var {boolean} + * True to include the "light" panel UI + */ +/** + * @var {boolean} + * True to include the "light-framed" panel UI + */ +/** @class Ext.tip.Tip */ +/** @class Ext.form.Labelable */ +/** @class Ext.form.field.Base */ +/** + * @var {color} $form-field-focus-border-color + * In the default neptune color scheme this is the same as $base-highlight-color + * but it does not change automatically when one changes the $base-color. This is because + * checkboxes and radio buttons have this focus color hard coded into their background + * images. If this color is changed, you should also modify checkbox and radio button + * background images to match + */ +/** @class Ext.window.Window */ +/** @class Ext.form.field.Display */ +/** @class Ext.ProgressBar */ +/** @class Ext.panel.Table */ +/** @class Ext.LoadMask */ +/** @class Ext.grid.header.Container */ +/** @class Ext.grid.column.Column */ +/** @class Ext.tree.Panel */ +/** @class Ext.form.field.Checkbox */ +/** @class Ext.menu.Menu */ +/** @class Ext.container.ButtonGroup */ +/** @class Ext.form.CheckboxGroup */ +/** @class Ext.form.FieldSet */ +/** @class Ext.form.field.Spinner */ +/** @class Ext.toolbar.Paging */ +/** @class Ext.view.BoundList */ +/** @class Ext.picker.Date */ +/** @class Ext.picker.Color */ +/** @class Ext.grid.column.Action */ +/** @class Ext.grid.feature.Grouping */ +/** @class Ext.grid.plugin.RowEditing */ +/** @class Ext.layout.container.Accordion */ +/** @class Ext.resizer.Splitter */ +/** @class Ext.layout.container.Border */ +/** @class Ext.panel.Tool */ +/** @class Ext.resizer.Resizer */ +/** @class Ext.tab.Tab */ +/** @class Ext.tab.Bar */ +/** @class Ext.selection.CheckboxModel */ +/* including package ext-theme-neutral */ +/** + * @class Global_CSS + */ +/** + * @var {color} $color + * The default text color to be used throughout the theme. + */ +/** + * @var {string} $font-family + * The default font-family to be used throughout the theme. + */ +/** + * @var {number} $font-size + * The default font-size to be used throughout the theme. + */ +/** + * @var {string} $base-gradient + * The base gradient to be used throughout the theme. + */ +/** + * @var {color} $base-color + * The base color to be used throughout the theme. + */ +/** + * @var {color} $neutral-color + * The neutral color to be used throughout the theme. + */ +/** + * @var {color} $body-background-color + * Background color to apply to the body element. If set to transparent or 'none' no + * background-color style will be set on the body element. + */ +/** @class Ext.button.Button */ +/** + * @var {number} + * The default width for a button's {@link #cfg-menu} arrow + */ +/** + * @var {number} + * The default height for a button's {@link #cfg-menu} arrow + */ +/** + * @var {number} + * The default width for a {@link Ext.button.Split Split Button}'s arrow + */ +/** + * @var {number} + * The default height for a {@link Ext.button.Split Split Button}'s arrow + */ +/** + * @var {number} + * The default space between a button's icon and text + */ +/** + * @var {number} + * The default border-radius for a small {@link #scale} button + */ +/** + * @var {number} + * The default border-width for a small {@link #scale} button + */ +/** + * @var {number} + * The default padding for a small {@link #scale} button + */ +/** + * @var {number} + * The default horizontal padding to add to the left and right of the text element for + * a small {@link #scale} button + */ +/** + * @var {number} + * The default font-size for a small {@link #scale} button + */ +/** + * @var {number} + * The default font-size for a small {@link #scale} button when the cursor is over the button + */ +/** + * @var {number} + * The default font-size for a small {@link #scale} button when the button is focused + */ +/** + * @var {number} + * The default font-size for a small {@link #scale} button when the button is pressed + */ +/** + * @var {number} + * The default font-size for a small {@link #scale} button when the button is disabled + */ +/** + * @var {string} + * The default font-weight for a small {@link #scale} button + */ +/** + * @var {string} + * The default font-weight for a small {@link #scale} button when the cursor is over the button + */ +/** + * @var {string} + * The default font-weight for a small {@link #scale} button when the button is focused + */ +/** + * @var {string} + * The default font-weight for a small {@link #scale} button when the button is pressed + */ +/** + * @var {string} + * The default font-weight for a small {@link #scale} button when the button is disabled + */ +/** + * @var {string} + * The default font-family for a small {@link #scale} button + */ +/** + * @var {string} + * The default font-family for a small {@link #scale} button when the cursor is over the button + */ +/** + * @var {string} + * The default font-family for a small {@link #scale} button when the button is focused + */ +/** + * @var {string} + * The default font-family for a small {@link #scale} button when the button is pressed + */ +/** + * @var {string} + * The default font-family for a small {@link #scale} button when the button is disabled + */ +/** + * @var {number} + * The default icon size for a small {@link #scale} button + */ +/** + * @var {number} + * The default width of a small {@link #scale} button's {@link #cfg-menu} arrow + */ +/** + * @var {number} + * The default height of a small {@link #scale} button's {@link #cfg-menu} arrow + */ +/** + * @var {number} + * The default width of a small {@link #scale} {@link Ext.button.Split Split Button}'s arrow + */ +/** + * @var {number} + * The default height of a small {@link #scale} {@link Ext.button.Split Split Button}'s arrow + */ +/** + * @var {number} + * The default border-radius for a medium {@link #scale} button + */ +/** + * @var {number} + * The default border-width for a medium {@link #scale} button + */ +/** + * @var {number} + * The default padding for a medium {@link #scale} button + */ +/** + * @var {number} + * The default horizontal padding to add to the left and right of the text element for + * a medium {@link #scale} button + */ +/** + * @var {number} + * The default font-size for a medium {@link #scale} button + */ +/** + * @var {number} + * The default font-size for a medium {@link #scale} button when the cursor is over the button + */ +/** + * @var {number} + * The default font-size for a medium {@link #scale} button when the button is focused + */ +/** + * @var {number} + * The default font-size for a medium {@link #scale} button when the button is pressed + */ +/** + * @var {number} + * The default font-size for a medium {@link #scale} button when the button is disabled + */ +/** + * @var {string} + * The default font-weight for a medium {@link #scale} button + */ +/** + * @var {string} + * The default font-weight for a medium {@link #scale} button when the cursor is over the button + */ +/** + * @var {string} + * The default font-weight for a medium {@link #scale} button when the button is focused + */ +/** + * @var {string} + * The default font-weight for a medium {@link #scale} button when the button is pressed + */ +/** + * @var {string} + * The default font-weight for a medium {@link #scale} button when the button is disabled + */ +/** + * @var {string} + * The default font-family for a medium {@link #scale} button + */ +/** + * @var {string} + * The default font-family for a medium {@link #scale} button when the cursor is over the button + */ +/** + * @var {string} + * The default font-family for a medium {@link #scale} button when the button is focused + */ +/** + * @var {string} + * The default font-family for a medium {@link #scale} button when the button is pressed + */ +/** + * @var {string} + * The default font-family for a medium {@link #scale} button when the button is disabled + */ +/** + * @var {number} + * The default icon size for a medium {@link #scale} button + */ +/** + * @var {number} + * The default width of a medium {@link #scale} button's {@link #cfg-menu} arrow + */ +/** + * @var {number} + * The default height of a medium {@link #scale} button's {@link #cfg-menu} arrow + */ +/** + * @var {number} + * The default width of a medium {@link #scale} {@link Ext.button.Split Split Button}'s arrow + */ +/** + * @var {number} + * The default height of a medium {@link #scale} {@link Ext.button.Split Split Button}'s arrow + */ +/** + * @var {number} + * The default border-radius for a large {@link #scale} button + */ +/** + * @var {number} + * The default border-width for a large {@link #scale} button + */ +/** + * @var {number} + * The default padding for a large {@link #scale} button + */ +/** + * @var {number} + * The default horizontal padding to add to the left and right of the text element for + * a large {@link #scale} button + */ +/** + * @var {number} + * The default font-size for a large {@link #scale} button + */ +/** + * @var {number} + * The default font-size for a large {@link #scale} button when the cursor is over the button + */ +/** + * @var {number} + * The default font-size for a large {@link #scale} button when the button is focused + */ +/** + * @var {number} + * The default font-size for a large {@link #scale} button when the button is pressed + */ +/** + * @var {number} + * The default font-size for a large {@link #scale} button when the button is disabled + */ +/** + * @var {string} + * The default font-weight for a large {@link #scale} button + */ +/** + * @var {string} + * The default font-weight for a large {@link #scale} button when the cursor is over the button + */ +/** + * @var {string} + * The default font-weight for a large {@link #scale} button when the button is focused + */ +/** + * @var {string} + * The default font-weight for a large {@link #scale} button when the button is pressed + */ +/** + * @var {string} + * The default font-weight for a large {@link #scale} button when the button is disabled + */ +/** + * @var {string} + * The default font-family for a large {@link #scale} button + */ +/** + * @var {string} + * The default font-family for a large {@link #scale} button when the cursor is over the button + */ +/** + * @var {string} + * The default font-family for a large {@link #scale} button when the button is focused + */ +/** + * @var {string} + * The default font-family for a large {@link #scale} button when the button is pressed + */ +/** + * @var {string} + * The default font-family for a large {@link #scale} button when the button is disabled + */ +/** + * @var {number} + * The default icon size for a large {@link #scale} button + */ +/** + * @var {number} + * The default width of a large {@link #scale} button's {@link #cfg-menu} arrow + */ +/** + * @var {number} + * The default height of a large {@link #scale} button's {@link #cfg-menu} arrow + */ +/** + * @var {number} + * The default width of a large {@link #scale} {@link Ext.button.Split Split Button}'s arrow + */ +/** + * @var {number} + * The default height of a large {@link #scale} {@link Ext.button.Split Split Button}'s arrow + */ +/** + * @var {color} + * The base color for the `default` button UI + */ +/** + * @var {color} + * The base color for the `default` button UI when the cursor is over the button + */ +/** + * @var {color} + * The base color for the `default` button UI when the button is focused + */ +/** + * @var {color} + * The base color for the `default` button UI when the button is pressed + */ +/** + * @var {color} + * The base color for the `default` button UI when the button is disabled + */ +/** + * @var {color} + * The border-color for the `default` button UI + */ +/** + * @var {color} + * The border-color for the `default` button UI when the cursor is over the button + */ +/** + * @var {color} + * The border-color for the `default` button UI when the button is focused + */ +/** + * @var {color} + * The border-color for the `default` button UI when the button is pressed + */ +/** + * @var {color} + * The border-color for the `default` button UI when the button is disabled + */ +/** + * @var {color} + * The background-color for the `default` button UI + */ +/** + * @var {color} + * The background-color for the `default` button UI when the cursor is over the button + */ +/** + * @var {color} + * The background-color for the `default` button UI when the button is focused + */ +/** + * @var {color} + * The background-color for the `default` button UI when the button is pressed + */ +/** + * @var {color} + * The background-color for the `default` button UI when the button is disabled + */ +/** + * @var {string/list} + * The background-gradient for the `default` button UI. Can be either the name of a + * predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + */ +/** + * @var {string/list} + * The background-gradient for the `default` button UI when the cursor is over the button. + * Can be either the name of a predefined gradient or a list of color stops. Used as the + * `$type` parameter for {@link Global_CSS#background-gradient}. + */ +/** + * @var {string/list} + * The background-gradient for the `default` button UI when the button is focused. Can be + * either the name of a predefined gradient or a list of color stops. Used as the `$type` + * parameter for {@link Global_CSS#background-gradient}. + */ +/** + * @var {string/list} + * The background-gradient for the `default` button UI when the button is pressed. Can be + * either the name of a predefined gradient or a list of color stops. Used as the `$type` + * parameter for {@link Global_CSS#background-gradient}. + */ +/** + * @var {string/list} + * The background-gradient for the `default` button UI when the button is disabled. Can be + * either the name of a predefined gradient or a list of color stops. Used as the `$type` + * parameter for {@link Global_CSS#background-gradient}. + */ +/** + * @var {color} + * The text color for the `default` button UI + */ +/** + * @var {color} + * The text color for the `default` button UI when the cursor is over the button + */ +/** + * @var {color} + * The text color for the `default` button UI when the button is focused + */ +/** + * @var {color} + * The text color for the `default` button UI when the button is pressed + */ +/** + * @var {color} + * The text color for the `default` button UI when the button is disabled + */ +/** + * @var {color} + * The color of the {@link #glyph} icon for the `default` button UI + */ +/** + * @var {color} + * The opacity of the {@link #glyph} icon for the `default` button UI + */ +/** + * @var {color} + * The border-color for the `default-toolbar` button UI + */ +/** + * @var {color} + * The border-color for the `default-toolbar` button UI when the cursor is over the button + */ +/** + * @var {color} + * The border-color for the `default-toolbar` button UI when the button is focused + */ +/** + * @var {color} + * The border-color for the `default-toolbar` button UI when the button is pressed + */ +/** + * @var {color} + * The border-color for the `default-toolbar` button UI when the button is disabled + */ +/** + * @var {color} + * The background-color for the `default-toolbar` button UI + */ +/** + * @var {color} + * The background-color for the `default-toolbar` button UI when the cursor is over the button + */ +/** + * @var {color} + * The background-color for the `default-toolbar` button UI when the button is focused + */ +/** + * @var {color} + * The background-color for the `default-toolbar` button UI when the button is pressed + */ +/** + * @var {color} + * The background-color for the `default-toolbar` button UI when the button is disabled + */ +/** + * @var {string/list} + * The background-gradient for the `default-toolbar` button UI. Can be either the name of + * a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + */ +/** + * @var {string/list} + * The background-gradient for the `default-toolbar` button UI when the cursor is over the + * button. Can be either the name of a predefined gradient or a list of color stops. Used + * as the `$type` parameter for {@link Global_CSS#background-gradient}. + */ +/** + * @var {string/list} + * The background-gradient for the `default-toolbar` button UI when the button is focused. + * Can be either the name of a predefined gradient or a list of color stops. Used as the + * `$type` parameter for {@link Global_CSS#background-gradient}. + */ +/** + * @var {string/list} + * The background-gradient for the `default-toolbar` button UI when the button is pressed. + * Can be either the name of a predefined gradient or a list of color stops. Used as the + * `$type` parameter for {@link Global_CSS#background-gradient}. + */ +/** + * @var {string/list} + * The background-gradient for the `default-toolbar` button UI when the button is disabled. + * Can be either the name of a predefined gradient or a list of color stops. Used as the + * `$type` parameter for {@link Global_CSS#background-gradient}. + */ +/** + * @var {color} + * The text color for the `default-toolbar` button UI + */ +/** + * @var {color} + * The text color for the `default-toolbar` button UI when the cursor is over the button + */ +/** + * @var {color} + * The text color for the `default-toolbar` button UI when the button is focused + */ +/** + * @var {color} + * The text color for the `default-toolbar` button UI when the button is pressed + */ +/** + * @var {color} + * The text color for the `default-toolbar` button UI when the button is disabled + */ +/** + * @var {color} + * The color of the {@link #glyph} icon for the `default-toolbar` button UI + */ +/** + * @var {color} + * The opacity of the {@link #glyph} icon for the `default-toolbar` button UI + */ +/** + * @var {boolean} $button-include-ui-menu-arrows + * True to use a different image url for the menu button arrows for each button UI + */ +/** + * @var {boolean} $button-include-ui-split-arrows + * True to use a different image url for the split button arrows for each button UI + */ +/** + * @var {boolean} $button-include-split-over-arrows + * True to include different split arrows for buttons' hover state. + */ +/** + * @var {boolean} $button-include-split-noline-arrows + * True to include "noline" split arrows for buttons in their default state. + */ +/** + * @var {boolean} $button-toolbar-include-split-noline-arrows + * True to include "noline" split arrows for toolbar buttons in their default state. + */ +/** + * @var {number} $button-opacity-disabled + * opacity to apply to the button's main element when the buton is disabled + */ +/** + * @var {number} $button-inner-opacity-disabled + * opacity to apply to the button's inner elements (icon and text) when the buton is disabled + */ +/** + * @var {number} $button-toolbar-opacity-disabled + * opacity to apply to the toolbar button's main element when the buton is disabled + */ +/** + * @var {number} $button-toolbar-inner-opacity-disabled + * opacity to apply to the toolbar button's inner elements (icon and text) when the buton is disabled + */ +/** + * @var {boolean} + * True to include the "default" button UI + */ +/** + * @var {boolean} + * True to include the "default" button UI for "small" scale buttons + */ +/** + * @var {boolean} + * True to include the "default" button UI for "medium" scale buttons + */ +/** + * @var {boolean} + * True to include the "default" button UI for "large" scale buttons + */ +/** + * @var {boolean} + * True to include the "default-toolbar" button UI + */ +/** + * @var {boolean} + * True to include the "default-toolbar" button UI for "small" scale buttons + */ +/** + * @var {boolean} + * True to include the "default-toolbar" button UI for "medium" scale buttons + */ +/** + * @var {boolean} + * True to include the "default-toolbar" button UI for "large" scale buttons + */ +/** + * @class Ext.toolbar.Toolbar + */ +/** + * @var {number} + * The default font-size of Toolbar text + */ +/** + * @var {color} + * The background-color of the Toolbar + */ +/** + * @var {string/list} + * The background-gradient of the Toolbar. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for {@link Global_CSS#background-gradient}. + */ +/** + * @var {number} + * The horizontal spacing of Toolbar items + */ +/** + * @var {number} + * The vertical spacing of Toolbar items + */ +/** + * @var {number} + * The horizontal spacing of {@link Ext.panel.Panel#fbar footer} Toolbar items + */ +/** + * @var {number} + * The vertical spacing of {@link Ext.panel.Panel#fbar footer} Toolbar items + */ +/** + * @var {color} + * The background-color of {@link Ext.panel.Panel#fbar footer} Toolbars + */ +/** + * @var {number} + * The border-width of {@link Ext.panel.Panel#fbar footer} Toolbars + */ +/** + * @var {number/list} + * The margin of {@link Ext.panel.Panel#fbar footer} Toolbars + */ +/** + * @var {color} + * The border-color of Toolbars + */ +/** + * @var {number} + * The border-width of Toolbars + */ +/** + * @var {string} + * The border-style of Toolbars + */ +/** + * @var {number} + * The width of Toolbar {@link Ext.toolbar.Spacer Spacers} + */ +/** + * @var {color} + * The main border-color of Toolbar {@link Ext.toolbar.Separator Separators} + */ +/** + * @var {color} + * The highlight border-color of Toolbar {@link Ext.toolbar.Separator Separators} + */ +/** + * @var {number/list} + * The margin of {@link Ext.toolbar.Separator Separators} on a horizontally oriented Toolbar + */ +/** + * @var {number} + * The height of {@link Ext.toolbar.Separator Separators} on a horizontally oriented Toolbar + */ +/** + * @var {string} + * The border-style of {@link Ext.toolbar.Separator Separators} on a horizontally oriented Toolbar + */ +/** + * @var {number} + * The border-width of {@link Ext.toolbar.Separator Separators} on a horizontally oriented Toolbar + */ +/** + * @var {number/list} + * The margin of {@link Ext.toolbar.Separator Separators} on a vertically oriented Toolbar + */ +/** + * @var {string} + * The border-style of {@link Ext.toolbar.Separator Separators} on a vertically oriented Toolbar + */ +/** + * @var {number} + * The border-width of {@link Ext.toolbar.Separator Separators} on a vertically oriented Toolbar + */ +/** + * @var {string} + * The default font-family of Toolbar text + */ +/** + * @var {number} + * The default font-size of Toolbar text + */ +/** + * @var {number} + * The default font-size of Toolbar text + */ +/** + * @var {number/list} + * The margin of Toolbar text + */ +/** + * @var {color} + * The text-color of Toolbar text + */ +/** + * @var {number/list} + * The padding of Toolbar text + */ +/** + * @var {number} + * The line-height of Toolbar text + */ +/** + * @var {number} + * The width of Toolbar scrollers + */ +/** + * @var {number} + * The height of Toolbar scrollers + */ +/** + * @var {color} + * The border-color of Toolbar scrollers + */ +/** + * @var {number} + * The border-width of Toolbar scrollers + */ +/** + * @var {string} + * The cursor of Toolbar scrollers + */ +/** + * @var {string} + * The cursor of disabled Toolbar scrollers + */ +/** + * @var {number} + * The opacity of disabled Toolbar scrollers + */ +/** + * @var {string} + * The sprite to use for {@link Ext.panel.Tool Tools} on a Toolbar + */ +/** + * @var {boolean} + * True to include the "default" toolbar UI + */ +/** @class Ext.panel.Header */ +/** + * @class Ext.panel.Panel + */ +/** + * @var {number} + * The default border-width of Panels + */ +/** + * @var {color} + * The base color of Panels + */ +/** + * @var {color} + * The default border-color of Panels + */ +/** + * @var {number} + * The maximum width a Panel's border can be before resizer handles are embedded + * into the borders using negative absolute positions. + * + * This defaults to 2, so that in the classic theme which uses 1 pixel borders, + * resize handles are in the content area within the border as they always have + * been. + * + * In the Neptune theme, the handles are embedded into the 5 pixel wide borders + * of any framed panel. + */ +/** + * @var {string} + * The default border-style of Panels + */ +/** + * @var {color} + * The default body background-color of Panels + */ +/** + * @var {color} + * The default color of text inside a Panel's body + */ +/** + * @var {color} + * The default border-color of the Panel body + */ +/** + * @var {number} + * The default border-width of the Panel body + */ +/** + * @var {number} + * The default font-size of the Panel body + */ +/** + * @var {string} + * The default font-weight of the Panel body + */ +/** + * @var {string} + * The default font-family of the Panel body + */ +/** + * @var {number} + * The space between the Panel {@link Ext.panel.Tool Tools} + */ +/** + * @var {string} + * The background sprite to use for Panel {@link Ext.panel.Tool Tools} + */ +/** + * @var {number} + * The border-width of Panel Headers + */ +/** + * @var {string} + * The border-style of Panel Headers + */ +/** + * @var {number/list} + * The padding of Panel Headers + */ +/** + * @var {number} + * The font-size of Panel Headers + */ +/** + * @var {number} + * The line-height of Panel Headers + */ +/** + * @var {string} + * The font-weight of Panel Headers + */ +/** + * @var {string} + * The font-family of Panel Headers + */ +/** + * @var {string} + * The text-transform of Panel Headers + */ +/** + * @var {number/list} + * The padding of the Panel Header's text element + */ +/** + * @var {string/list} + * The background-gradient of the Panel Header. Can be either the name of a predefined + * gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + */ +/** + * @var {color} + * The border-color of the Panel Header + */ +/** + * @var {color} + * The inner border-color of the Panel Header + */ +/** + * @var {number} + * The inner border-width of the Panel Header + */ +/** + * @var {color} + * The text color of the Panel Header + */ +/** + * @var {color} + * The background-color of the Panel Header + */ +/** + * @var {number} + * The width of the Panel Header icon + */ +/** + * @var {number} + * The height of the Panel Header icon + */ +/** + * @var {number} + * The space between the Panel Header icon and text + */ +/** + * @var {list} + * The background-position of the Panel Header icon + */ +/** + * @var {color} + * The color of the Panel Header glyph icon + */ +/** + * @var {number} + * The opacity of the Panel Header glyph icon + */ +/** + * @var {color} + * The base color of the framed Panels + */ +/** + * @var {number} + * The border-radius of framed Panels + */ +/** + * @var {number} + * The border-width of framed Panels + */ +/** + * @var {string} + * The border-style of framed Panels + */ +/** + * @var {number} + * The padding of framed Panels + */ +/** + * @var {color} + * The background-color of framed Panels + */ +/** + * @var {color} + * The border-color of framed Panels + */ +/** + * @var {number} + * The border-width of the body element of framed Panels + */ +/** + * @var {number} + * The border-width of framed Panel Headers + */ +/** + * @var {color} + * The inner border-color of framed Panel Headers + */ +/** + * @var {number} + * The inner border-width of framed Panel Headers + */ +/** + * @var {number/list} + * The padding of framed Panel Headers + */ +/** + * @var {number} + * The opacity of ghost Panels while dragging + */ +/** + * @var {string} + * The direction to strech the background-gradient of top docked Headers when slicing images + * for IE using Sencha Cmd + */ +/** + * @var {string} + * The direction to strech the background-gradient of bottom docked Headers when slicing images + * for IE using Sencha Cmd + */ +/** + * @var {string} + * The direction to strech the background-gradient of right docked Headers when slicing images + * for IE using Sencha Cmd + */ +/** + * @var {string} + * The direction to strech the background-gradient of left docked Headers when slicing images + * for IE using Sencha Cmd + */ +/** + * @var {boolean} + * True to include neptune style border management rules. + */ +/** + * @var {color} + * The color to apply to the border that wraps the body and docked items in a framed + * panel. The presence of the wrap border in a framed panel is controlled by the + * {@link #border} config. Only applicable when `$panel-include-border-management-rules` is + * `true`. + */ +/** + * @var {number} + * The width to apply to the border that wraps the body and docked items in a framed + * panel. The presence of the wrap border in a framed panel is controlled by the + * {@link #border} config. Only applicable when `$panel-include-border-management-rules` is + * `true`. + */ +/** + * @var {boolean} + * True to include the "default" panel UI + */ +/** + * @var {boolean} + * True to include the "default-framed" panel UI + */ +/** + * @class Ext.tip.Tip + */ +/** + * @var {color} + * The background-color of the Tip + */ +/** + * @var {string/list} + * The background-gradient of the Tip. Can be either the name of a predefined gradient or a + * list of color stops. Used as the `$type` parameter for {@link Global_CSS#background-gradient}. + */ +/** + * @var {color} + * The text color of the Tip body + */ +/** + * @var {number} + * The font-size of the Tip body + */ +/** + * @var {string} + * The font-weight of the Tip body + */ +/** + * @var {number/list} + * The padding of the Tip body + */ +/** + * @var {color} + * The text color of any anchor tags inside the Tip body + */ +/** + * @var {color} + * The text color of the Tip header + */ +/** + * @var {number} + * The font-size of the Tip header + */ +/** + * @var {string} + * The font-weight of the Tip header + */ +/** + * @var {number/list} + * The padding of the Tip header's body element + */ +/** + * @var {color} + * The border-color of the Tip + */ +/** + * @var {number} + * The border-width of the Tip + */ +/** + * @var {number} + * The border-radius of the Tip + */ +/** + * @var {color} + * The inner border-color of the form field error Tip + */ +/** + * @var {number} + * The inner border-width of the form field error Tip + */ +/** + * @var {color} + * The border-color of the form field error Tip + */ +/** + * @var {number} + * The border-radius of the form field error Tip + */ +/** + * @var {number} + * The border-width of the form field error Tip + */ +/** + * @var {color} + * The background-color of the form field error Tip + */ +/** + * @var {number/list} + * The padding of the form field error Tip's body element + */ +/** + * @var {color} + * The text color of the form field error Tip's body element + */ +/** + * @var {number} + * The font-size of the form field error Tip's body element + */ +/** + * @var {string} + * The font-weight of the form field error Tip's body element + */ +/** + * @var {color} + * The color of anchor tags in the form field error Tip's body element + */ +/** + * @var {number} + * The space between {@link Ext.panel.Tool Tools} in the header + */ +/** + * @var {string} + * The sprite to use for the header {@link Ext.panel.Tool Tools} + */ +/** + * @var {boolean} + * True to include the "default" tip UI + */ +/** + * @var {boolean} + * True to include the "form-invalid" tip UI + */ +/** + * @class Ext.form.Labelable + */ +/** + * @var {color} + * The text color of form field labels + */ +/** + * @var {string} + * The font-weight of form field labels + */ +/** + * @var {number} + * The font-size of form field labels + */ +/** + * @var {string} + * The font-family of form field labels + */ +/** + * @var {number} + * The line-height of form field labels + */ +/** + * @var {color} + * The text color of toolbar field labels + */ +/** + * @var {string} + * The font-weight of toolbar field labels + */ +/** + * @var {number} + * The font-size of toolbar field labels + */ +/** + * @var {string} + * The font-family of toolbar field labels + */ +/** + * @var {number} + * The line-height of toolbar field labels + */ +/** + * @var {number} + * Width for form error icons. + */ +/** + * @var {number} + * Height for form error icons. + */ +/** + * @var {number/list} + * Margin for error icons that are aligned to the side of the field + */ +/** + * @var {number} + * The space between the icon and the message for errors that display under the field + */ +/** + * @var {number/list} + * The padding on errors that display under the form field + */ +/** + * @var {color} + * The text color of form error messages + */ +/** + * @var {string} + * The font-weight of form error messages + */ +/** + * @var {number} + * The font-size of form error messages + */ +/** + * @var {string} + * The font-family of form error messages + */ +/** + * @var {number} + * The line-height of form error messages + */ +/** + * @var {number} + * The bottom margin to apply to form items when in auto, anchor, vbox, or table layout + */ +/** + * @class Ext.form.field.Base + */ +/** + * @var {number} $form-field-height + * Height for form fields. + */ +/** + * @var {number} $form-toolbar-field-height + * Height for form fields in toolbar. + */ +/** + * @var {number} $form-field-padding + * Padding around form fields. + */ +/** + * @var {number} $form-field-font-size + * Font size for form fields. + */ +/** + * @var {string} $form-field-font-family + * Font family for form fields. + */ +/** + * @var {string} $form-field-font-weight + * Font weight for form fields. + */ +/** + * @var {font} $form-field-font + * Font for form fields. + */ +/** + * @var {number} $form-toolbar-field-font-size + * Font size for toolbar form fields. + */ +/** + * @var {string} $form-toolbar-field-font-family + * Font family for toolbar form fields. + */ +/** + * @var {string} $form-toolbar-field-font-weight + * Font weight for toolbar form fields. + */ +/** + * @var {font} $form-toolbar-field-font + * Font for toolbar form fields. + */ +/** + * @var {color} $form-field-color + * Text color for form fields. + */ +/** + * @var {color} $form-field-empty-color + * Text color for empty form fields. + */ +/** + * @var {color} $form-field-border-color + * Border color for form fields. + */ +/** + * @var {number} $form-field-border-width + * Border width for form fields. + */ +/** + * @var {string} $form-field-border-style + * Border style for form fields. + */ +/** + * @var {color} $form-field-focus-border-color + * Border color for focused form fields. + */ +/** + * @var {color} $form-field-invalid-border-color + * Border color for invalid form fields. + */ +/** + * @var {color} $form-field-background-color + * Background color for form fields. + */ +/** + * @var {string} $form-field-background-image + * Background image for form fields. + */ +/** + * @var {color} $form-field-invalid-background-color + * Background color for invalid form fields. + */ +/** + * @var {string} $form-field-invalid-background-image + * Background image for invalid form fields. + */ +/** + * @var {string} $form-field-invalid-background-repeat + * Background repeat for invalid form fields. + */ +/** + * @var {string/list} $form-field-invalid-background-position + * Background position for invalid form fields. + */ +/** + * @var {number} $form-field-disabled-opacity + */ +/** + * @class Ext.FocusManager + */ +/** + * @var {color} + * The border-color of the focusFrame. See {@link #method-enable}. + */ +/** + * @var {color} + * The border-style of the focusFrame. See {@link #method-enable}. + */ +/** + * @var {color} + * The border-width of the focusFrame. See {@link #method-enable}. + */ +/** + * @class Ext.window.Window + */ +/** + * @var {color} + * The base color of Windows + */ +/** + * @var {number} + * The padding of Windows + */ +/** + * @var {number} + * The border-radius of Windows + */ +/** + * @var {number} + * The border-width of Windows + */ +/** + * @var {color} + * The border-color of Windows + */ +/** + * @var {color} + * The inner border-color of Windows + */ +/** + * @var {number} + * The inner border-width of Windows + */ +/** + * @var {color} + * The background-color of Windows + */ +/** + * @var {number} + * The body border-width of Windows + */ +/** + * @var {string} + * The body border-style of Windows + */ +/** + * @var {color} + * The body border-color of Windows + */ +/** + * @var {color} + * The body background-color of Windows + */ +/** + * @var {color} + * The body text color of Windows + */ +/** + * @var {number/list} + * The padding of Window Headers + */ +/** + * @var {number} + * The font-size of Window Headers + */ +/** + * @var {number} + * The line-height of Window Headers + */ +/** + * @var {color} + * The text color of Window Headers + */ +/** + * @var {color} + * The background-color of Window Headers + */ +/** + * @var {string} + * The font-weight of Window Headers + */ +/** + * @var {number} + * The space between the Window {@link Ext.panel.Tool Tools} + */ +/** + * @var {string} + * The background sprite to use for Window {@link Ext.panel.Tool Tools} + */ +/** + * @var {string} + * The font-family of Window Headers + */ +/** + * @var {number/list} + * The padding of the Window Header's text element + */ +/** + * @var {string} + * The text-transform of Window Headers + */ +/** + * @var {number} + * The width of the Window Header icon + */ +/** + * @var {number} + * The height of the Window Header icon + */ +/** + * @var {number} + * The space between the Window Header icon and text + */ +/** + * @var {list} + * The background-position of the Window Header icon + */ +/** + * @var {color} + * The color of the Window Header glyph icon + */ +/** + * @var {number} + * The opacity of the Window Header glyph icon + */ +/** + * @var {number} + * The border-width of Window Headers + */ +/** + * @var {color} + * The inner border-color of Window Headers + */ +/** + * @var {number} + * The inner border-width of Window Headers + */ +/** + * @var {boolean} $ui-force-header-border + * True to force the window header to have a border on the side facing the window body. + * Overrides dock layout's border management border removal rules. + */ +/** + * @var {number} + * The opacity of ghost Windows while dragging + */ +/** + * @var {boolean} + * True to include neptune style border management rules. + */ +/** + * @var {color} + * The color to apply to the border that wraps the body and docked items. The presence of + * the wrap border is controlled by the {@link #border} config. Only applicable when + * `$window-include-border-management-rules` is `true`. + */ +/** + * @var {number} + * The width to apply to the border that wraps the body and docked items. The presence of + * the wrap border is controlled by the {@link #border} config. Only applicable when + * `$window-include-border-management-rules` is `true`. + */ +/** + * @var {boolean} + * True to include the "default" window UI + */ +/** + * @var {number} + * The default font-size of the Window body + */ +/** + * @var {string} + * The default font-weight of the Window body + */ +/** + * @var {string} + * The default font-family of the Window body + */ +/** + * @class Ext.form.field.TextArea + */ +/** + * @var {number/string} + * The line-height to use for the TextArea's text + */ +/** + * @class Ext.form.field.Display + */ +/** + * @var {color} + * The text color of display fields + */ +/** + * @var {string} + * The font-weight of display fields + */ +/** + * @var {number} + * The font-size of display fields + */ +/** + * @var {string} + * The font-family of display fields + */ +/** + * @var {number} + * The line-height of display fields + */ +/** + * @var {string} + * The font-weight of toolbar display fields + */ +/** + * @var {number} + * The font-size of toolbar display fields + */ +/** + * @var {string} + * The font-family of toolbar display fields + */ +/** + * @var {number} + * The line-height of toolbar display fields + */ +/** + * @class Ext.ProgressBar + */ +/** + * @var {number} + * The height of the ProgressBar + */ +/** + * @var {color} + * The border-color of the ProgressBar + */ +/** + * @var {number} + * The border-width of the ProgressBar + */ +/** + * @var {number} + * The border-radius of the ProgressBar + */ +/** + * @var {color} + * The background-color of the ProgressBar + */ +/** + * @var {color} + * The background-color of the ProgressBar's moving element + */ +/** + * @var {string/list} + * The background-gradient of the ProgressBar's moving element. Can be either the name of + * a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + */ +/** + * @var {color} + * The color of the ProgressBar's text when in front of the ProgressBar's moving element + */ +/** + * @var {color} + * The color of the ProgressBar's text when the ProgressBar's 'moving element is not under it + */ +/** + * @var {string} + * The text-align of the ProgressBar's text + */ +/** + * @var {number} + * The font-size of the ProgressBar's text + */ +/** + * @var {string} + * The font-weight of the ProgressBar's text + */ +/** + * @var {string} + * The font-family of the ProgressBar's text + */ +/** + * @var {boolean} + * True to include the "default" ProgressBar UI + */ +/** + * @class Ext.window.MessageBox + */ +/** + * @var {color} + * The background-color of the MessageBox body + */ +/** + * @var {number} + * The border-width of the MessageBox body + */ +/** + * @var {color} + * The border-color of the MessageBox body + */ +/** + * @var {string} + * The border-style of the MessageBox body + */ +/** + * @var {list} + * The background-position of the MessageBox icon + */ +/** + * @class Ext.panel.Table + */ +/** + * @var {color} + * The color of the text in the grid cells + */ +/** + * @var {number} + * The font size of the text in the grid cells + */ +/** + * @var {number} + * The line-height of the text inside the grid cells. + */ +/** + * @var {string} + * The font-weight of the text in the grid cells + */ +/** + * @var {string} + * The font-family of the text in the grid cells + */ +/** + * @var {color} + * The background-color of the grid cells + */ +/** + * @var {color} + * The border-color of row/column borders. Can be specified as a single color, or as a list + * of colors containing the row border color followed by the column border color. + */ +/** + * @var {string} + * The border-style of the row/column borders. + */ +/** + * @var {number} + * The border-width of the row and column borders. + */ +/** + * @var {color} + * The background-color of "special" cells. Special cells are created by {@link + * Ext.grid.RowNumberer RowNumberer}, {@link Ext.selection.CheckboxModel Checkbox Selection + * Model} and {@link Ext.grid.plugin.RowExpander RowExpander}. + */ +/** + * @var {string} + * The background-gradient to use for "special" cells. Special cells are created by {@link + * Ext.grid.RowNumberer RowNumberer}, {@link Ext.selection.CheckboxModel Checkbox Selection + * Model} and {@link Ext.grid.plugin.RowExpander RowExpander}. + */ +/** + * @var {number} + * The border-width of "special" cells. Special cells are created by {@link + * Ext.grid.RowNumberer RowNumberer}, {@link Ext.selection.CheckboxModel Checkbox Selection + * Model} and {@link Ext.grid.plugin.RowExpander RowExpander}. + * Only applies to the vertical border, since the row border width is determined by + * {#$grid-row-cell-border-width}. + */ +/** + * @var {color} + * The border-color of "special" cells. Special cells are created by {@link + * Ext.grid.RowNumberer RowNumberer}, {@link Ext.selection.CheckboxModel Checkbox Selection + * Model} and {@link Ext.grid.plugin.RowExpander RowExpander}. + * Only applies to the vertical border, since the row border color is determined by + * {#$grid-row-cell-border-color}. + */ +/** + * @var {string} + * The border-style of "special" cells. Special cells are created by {@link + * Ext.grid.RowNumberer RowNumberer}, {@link Ext.selection.CheckboxModel Checkbox Selection + * Model} and {@link Ext.grid.plugin.RowExpander RowExpander}. + * Only applies to the vertical border, since the row border style is determined by + * {#$grid-row-cell-border-style}. + */ +/** + * @var {color} + * The border-color of "special" cells when the row is selected using a {@link + * Ext.selection.RowModel Row Selection Model}. Special cells are created by {@link + * Ext.grid.RowNumberer RowNumberer}, {@link Ext.selection.CheckboxModel Checkbox Selection + * Model} and {@link Ext.grid.plugin.RowExpander RowExpander}. + * Only applies to the vertical border, since the selected row border color is determined by + * {#$grid-row-cell-selected-border-color}. + */ +/** + * @var {color} + * The background-color of "special" cells when the row is hovered. Special cells are + * created by {@link Ext.grid.RowNumberer RowNumberer}, {@link Ext.selection.CheckboxModel + * Checkbox Selection Model} and {@link Ext.grid.plugin.RowExpander RowExpander}. + */ +/** + * @var {color} + * The background-color color of odd-numbered rows when the table view is configured with + * `{@link Ext.view.Table#stripeRows stripeRows}: true`. + */ +/** + * @var {string} + * The border-style of the hovered row + */ +/** + * @var {color} + * The text color of the hovered row + */ +/** + * @var {color} + * The background-color of the hovered row + */ +/** + * @var {color} + * The border-color of the hovered row + */ +/** + * @var {string} + * The border-style of the selected row + */ +/** + * @var {color} + * The text color of the selected row + */ +/** + * @var {color} + * The background-color of the selected row + */ +/** + * @var {color} + * The border-color of the selected row + */ +/** + * @var {color} + * The border-color of the focused row + */ +/** + * @var {string} + * The border-style of the focused row + */ +/** + * @var {color} + * The text color of the focused row + */ +/** + * @var {color} + * The background-color of the focused row + */ +/** + * @var {boolean} + * True to show the focus border when a row is focused even if the grid has no + * {@link Ext.panel.Table#rowLines rowLines}. + */ +/** + * @var {color} + * The text color of a selected cell when using a {@link Ext.selection.CellModel + * Cell Selection Model}. + */ +/** + * @var {color} + * The background-color of a selected cell when using a {@link Ext.selection.CellModel + * Cell Selection Model}. + */ +/** + * @var {number} + * The amount of padding to apply to the grid cell's inner div element + */ +/** + * @var {string} + * The type of text-overflow to use on the grid cell's inner div element + */ +/** + * @var {color} + * The border-color of the grid body + */ +/** + * @var {number} + * The border-width of the grid body border + */ +/** + * @var {string} + * The border-style of the grid body border + */ +/** + * @var {color} + * The background-color of the grid body + */ +/** + * @var {number} + * The amount of padding to apply to the grid body when the grid contains no data. + */ +/** + * @var {color} + * The text color of the {@link Ext.view.Table#emptyText emptyText} in the grid body when + * the grid contains no data. + */ +/** + * @var {color} + * The background color of the grid body when the grid contains no data. + */ +/** + * @var {number} + * The font-size of the {@link Ext.view.Table#emptyText emptyText} in the grid body when + * the grid contains no data. + */ +/** + * @var {number} + * The font-weight of the {@link Ext.view.Table#emptyText emptyText} in the grid body when + * the grid contains no data. + */ +/** + * @var {number} + * The font-family of the {@link Ext.view.Table#emptyText emptyText} in the grid body when + * the grid contains no data. + */ +/** + * @var {color} + * The color of the resize markers that display when dragging a column border to resize + * the column + */ +/** + * @class Ext.LoadMask + */ +/** + * @var {number} + * Opacity of the LoadMask + */ +/** + * @var {color} + * The background-color of the LoadMask + */ +/** + * @var {string} + * The type of cursor to dislay when the cursor is over the LoadMask + */ +/** + * @var {number/list} + * The padding to apply to the LoadMask's message element + */ +/** + * @var {string} + * The border-style of the LoadMask's message element + */ +/** + * @var {color} + * The border-color of the LoadMask's message element + */ +/** + * @var {number} + * The border-width of the LoadMask's message element + */ +/** + * @var {color} + * The background-color of the LoadMask's message element + */ +/** + * @var {string/list} + * The background-gradient of the LoadMask's message element. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + */ +/** + * @var {number/list} + * The padding of the message inner element + */ +/** + * @var {string} + * The icon to display in the message inner element + */ +/** + * @var {list} + * The background-position of the icon + */ +/** + * @var {string} + * The border-style of the message inner element + */ +/** + * @var {color} + * The border-color of the message inner element + */ +/** + * @var {number} + * The border-width of the message inner element + */ +/** + * @var {color} + * The background-color of the message inner element + */ +/** + * @var {color} + * The text color of the message inner element + */ +/** + * @var {number} + * The font-size of the message inner element + */ +/** + * @var {string} + * The font-weight of the message inner element + */ +/** + * @var {string} + * The font-family of the message inner element + */ +/** + * @var {number/list} + * The padding of the message element + */ +/** + * @var {number} + * The border-radius of the message element + */ +/** + * @class Ext.grid.header.DropZone + */ +/** + * @var {number} + * The size of the column move icon + */ +/** + * @class Ext.grid.header.Container + */ +/** + * @var {color} + * The background-color of grid headers + */ +/** + * @var {string/list} + * The background-gradient of grid headers. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for {@link Global_CSS#background-gradient}. + */ +/** + * @var {color} + * The border-color of grid headers + */ +/** + * @var {number} + * The border-width of grid headers + */ +/** + * @var {string} + * The border-style of grid headers + */ +/** + * @var {color} + * The background-color of grid headers when the cursor is over the header + */ +/** + * @var {string/list} + * The background-gradient of grid headers when the cursor is over the header. Can be + * either the name of a predefined gradient or a list of color stops. Used as the `$type` + * parameter for {@link Global_CSS#background-gradient}. + */ +/** + * @var {color} + * The background-color of a grid header when its menu is open + */ +/** + * @var {number/list} + * The padding to apply to grid headers + */ +/** + * @var {number} + * The height of grid header triggers + */ +/** + * @var {number} + * The width of grid header triggers + */ +/** + * @var {number} + * The width of the grid header sort icon + */ +/** + * @var {string} + * The type of cursor to display when the cursor is over a grid header trigger + */ +/** + * @var {number} + * The amount of space between the header trigger and text + */ +/** + * @var {list} + * The background-position of the header trigger + */ +/** + * @var {color} + * The background-color of the header trigger + */ +/** + * @var {color} + * The background-color of the header trigger when the menu is open + */ +/** + * @var {number} + * The space between the grid header sort icon and the grid header text + */ +/** + * @class Ext.grid.column.Column + */ +/** + * @var {string} + * The font-family of grid column headers + */ +/** + * @var {number} + * The font-size of grid column headers + */ +/** + * @var {string} + * The font-weight of grid column headers + */ +/** + * @var {number} + * The line-height of grid column headers + */ +/** + * @var {string} + * The text-overflow of grid column headers + */ +/** + * @var {color} + * The text color of grid column headers + */ +/** + * @var {number} + * The border-width of grid column headers + */ +/** + * @var {string} + * The border-style of grid column headers + */ +/** + * @class Ext.tree.Panel + */ +/** + * @var {number} $tree-elbow-width + * The width of the tree elbow/arrow icons + */ +/** + * @var {number} $tree-icon-width + * The width of the tree folder/leaf icons + */ +/** + * @var {number} $tree-elbow-spacing + * The amount of spacing between the tree elbows or arrows, and the checkbox or icon. + */ +/** + * @var {number} $tree-checkbox-spacing + * The amount of space (in pixels) between the tree checkbox and the folder/leaf icon + */ +/** + * @var {number} $tree-icon-spacing + * The amount of space (in pixels) between the folder/leaf icons and the text + */ +/** + * @var {string} $tree-expander-cursor + * The type of cursor to display when the mouse is over a tree expander (+, - or arrow icon) + */ +/** + * @var {number/list} + * The amount of padding to apply to the tree cell's inner div element + */ +/** + * @class Ext.form.field.Checkbox + */ +/** + * @var {number} + * The size of the checkbox + */ +/** + * @var {number} + * The space between the boxLabel and the checkbox. + */ +/** + * @class Ext.grid.feature.RowWrap + */ +/** + * @var {color} + * The border-color of wrapped rows + */ +/** + * @var {string} + * The border-style of wrapped rows + */ +/** + * @class Ext.grid.feature.RowBody + */ +/** + * @var {number} + * The font-size of the RowBody + */ +/** + * @var {number} + * The line-height of the RowBody + */ +/** + * @var {string} + * The font-family of the RowBody + */ +/** + * @var {number} + * The font-weight of the RowBody + */ +/** + * @var {number/list} + * The padding of the RowBody + */ +/** + * @class Ext.grid.plugin.RowExpander + */ +/** + * @var {number} + * The height of the RowExpander icon + */ +/** + * @var {number} + * The width of the RowExpander icon + */ +/** + * @var {number} + * The horizontal space before the RowExpander icon + */ +/** + * @var {number} + * The horizontal space after the RowExpander icon + */ +/** + * @var {string} + * The cursor for the RowExpander icon + */ +/** + * @class Ext.slider.Multi + */ +/** + * @var {number} + * The horizontal slider thumb width + */ +/** + * @var {number} + * The horizontal slider thumb height + */ +/** + * @var {number} + * The width of the horizontal slider end caps + */ +/** + * @var {number} + * The vertical slider thumb width + */ +/** + * @var {number} + * The vertical slider thumb height + */ +/** + * @var {number} + * The height of the vertical slider end caps + */ +/** + * @class Ext.menu.Menu + */ +/** + * @var {color} + * The background-color of the Menu + */ +/** + * @var {color} + * The border-color of {@link Ext.menu.Separator Menu Separators} + */ +/** + * @var {color} + * The background-color of {@link Ext.menu.Separator Menu Separators} + */ +/** + * @var {number} + * The size of {@link Ext.menu.Separator Menu Separators} + */ +/** + * @var {color} + * The border-color of the Menu + */ +/** + * @var {string} + * The border-style of the Menu + */ +/** + * @var {number} + * The border-width of the Menu + */ +/** + * @var {string} + * The font-family of {@link Ext.menu.Item Menu Items} + */ +/** + * @var {number} + * The font-size of {@link Ext.menu.Item Menu Items} + */ +/** + * @var {string} + * The font-weight of {@link Ext.menu.Item Menu Items} + */ +/** + * @var {number} + * The height of {@link Ext.menu.Item Menu Items} + */ +/** + * @var {number} + * The border-width of {@link Ext.menu.Item Menu Items} + */ +/** + * @var {string} + * The style of cursor to display when the cursor is over a {@link Ext.menu.Item Menu Item} + */ +/** + * @var {color} + * The background-color of the active {@link Ext.menu.Item Menu Item} + */ +/** + * @var {color} + * The border-color of the active {@link Ext.menu.Item Menu Item} + */ +/** + * @var {string/list} + * The background-gradient for {@link Ext.menu.Item Menu Items}. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + */ +/** + * @var {number} + * The border-radius of {@link Ext.menu.Item Menu Items} + */ +/** + * @var {number} + * The size of {@link Ext.menu.Item Menu Item} icons + */ +/** + * @var {list} + * The background-position of {@link Ext.menu.Item Menu Item} icons + */ +/** + * @var {number} + * vertical offset for menu item icons/checkboxes. By default the icons are roughly + * vertically centered, but it may be necessary in some cases to make minor adjustments + * to the vertical position. + */ +/** + * @var {number} + * vertical offset for menu item text. By default the text is given a line-height + * equal to the menu item's content-height, however, depending on the font this may not + * result in perfect vertical centering. Offset can be used to make small adjustments + * to the text's vertical position. + */ +/** + * @var {number/list} + * The space to the left and right of {@link Ext.menu.Item Menu Item} text. Can be specified + * as a number (e.g. 5px) or as a list with 2 items for different left/right values. e.g. + * + * $menu-item-text-horizontal-spacing: 4px 8px !default; // 4px of space to the left, and 8px to the right + */ +/** + * @var {number} + * The space to the left and right of {@link Ext.menu.Item Menu Item} icons. Can be specified + * as a number (e.g. 5px) or as a list with 2 items for different left/right values. e.g. + * + * $menu-item-icon-horizontal-spacing: 4px 8px !default; // 4px of space to the left, and 8px to the right + */ +/** + * @var {number} + * The space to the left and right of {@link Ext.menu.Item Menu Item} arrows. Can be specified + * as a number (e.g. 5px) or as a list with 2 items for different left/right values. e.g. + * + * $menu-item-arrow-horizontal-spacing: 4px 8px !default; // 4px of space to the left, and 8px to the right + */ +/** + * @var {number/list} + * The margin of {@link Ext.menu.Separator Menu Separators} + */ +/** + * @var {number} + * The height of {@link Ext.menu.Item Menu Item} arrows + */ +/** + * @var {number} + * The width of {@link Ext.menu.Item Menu Item} arrows + */ +/** + * @var {number} + * The opacity of disabled {@link Ext.menu.Item Menu Items} + */ +/** + * @var {number} + * The height of Menu crollers + */ +/** + * @var {number} + * The opacity of Menu crollers + */ +/** + * @var {number} + * The opacity of Menu crollers when hovered + */ +/** + * @var {number} + * The opacity of Menu crollers when pressed + */ +/** + * @var {number/list} + * The padding to apply to the Menu body element + */ +/** + * @var {color} + * The color of Menu Item text + */ +/** + * @var {number/list} + * The margin non-MenuItems placed in a Menu + */ +/** + * @var {color} $menu-glyph-color + * The color to use for menu icons configured using {@link Ext.menu.Item#glyph glyph} + */ +/** + * @var {number} $menu-glyph-opacity + * The opacity to use for menu icons configured using {@link Ext.menu.Item#glyph glyph} + */ +/** + * @class Ext.form.field.Trigger + */ +/** + * @var {number} + * The width of the Trigger field's trigger element + */ +/** + * @var {color} + * The color of the trigger field background + */ +/** + * @var {number/list} + * The width of the trigger's border + */ +/** + * @var {color} + * The color of the trigger's border + */ +/** + * @var {string} + * The style of the trigger's border + */ +/** + * @var {color} + * The color of the trigger's border when hovered + */ +/** + * @var {color} + * The color of the trigger's border when the field is focused + */ +/** + * @var {color} + * The color of the trigger's border when the field is focused and the trigger is hovered + */ +/** + * @class Ext.container.ButtonGroup + */ +/** + * @var {color} + * The background-color of the ButtonGroup + */ +/** + * @var {color} + * The border-color of the ButtonGroup + */ +/** + * @var {number} + * The border-radius of the ButtonGroup + */ +/** + * @var {number} + * The border-radius of framed ButtonGroups + */ +/** + * @var {number} + * The border-width of the ButtonGroup + */ +/** + * @var {number/list} + * The body padding of the ButtonGroup + */ +/** + * @var {number/list} + * The inner border-width of the ButtonGroup + */ +/** + * @var {color} + * The inner border-color of the ButtonGroup + */ +/** + * @var {number/list} + * The margin of the header element. Used to add space around the header. + */ +/** + * @var {number} + * The font-size of the header + */ +/** + * @var {number} + * The font-weight of the header + */ +/** + * @var {number} + * The font-family of the header + */ +/** + * @var {number} + * The line-height of the header + */ +/** + * @var {number} + * The text color of the header + */ +/** + * @var {number} + * The padding of the header + */ +/** + * @var {number} + * The background-color of the header + */ +/** + * @var {number} + * The border-spacing to use on the table layout element + */ +/** + * @var {number} + * The background-color of framed ButtonGroups + */ +/** + * @var {number} + * The border-width of framed ButtonGroups + */ +/** + * @var {string} + * Sprite image to use for header {@link Ext.panel.Tool Tools} + */ +/** + * @var {boolean} + * True to include the "default" button group UI + */ +/** + * @var {boolean} + * True to include the "default-framed" button group UI + */ +/** + * @class Ext.form.CheckboxGroup + */ +/** + * @var {number/list} + * The padding of the CheckboxGroup body element + */ +/** + * @var {color} + * The text color of the CheckboxGroup label + */ +/** + * @var {number} + * The padding of the CheckboxGroup label + */ +/** + * @var {number} + * The margin of the CheckboxGroup label + */ +/** + * @var {number} + * The border-width of the CheckboxGroup label + */ +/** + * @var {number} + * The border-style of the CheckboxGroup label + */ +/** + * @var {number} + * The border-color of the CheckboxGroup label + */ +/** + * @class Ext.form.FieldSet + */ +/** + * @var {number} + * The font-size of the FieldSet header + */ +/** + * @var {string} + * The font-weight of the FieldSet header + */ +/** + * @var {string} + * The font-family of the FieldSet header + */ +/** + * @var {number/string} + * The line-height of the FieldSet header + */ +/** + * @var {color} + * The text color of the FieldSet header + */ +/** + * @var {number} + * The border-width of the FieldSet + */ +/** + * @var {string} + * The border-style of the FieldSet + */ +/** + * @var {color} + * The border-color of the FieldSet + */ +/** + * @var {number} + * The border radius of FieldSet elements. + */ +/** + * @var {number/list} + * The FieldSet's padding + */ +/** + * @var {number/list} + * The FieldSet's margin + */ +/** + * @var {number/list} + * The padding to apply to the FieldSet's header + */ +/** + * @var {number/list} + * The margin to apply to the FieldSet's collapse tool + */ +/** + * @var {number/list} + * The padding to apply to the FieldSet's collapse tool + */ +/** + * @var {number/list} + * The margin to apply to the FieldSet's checkbox (for FieldSets that use + * {@link #checkboxToggle}) + */ +/** + * @var {number} + * The size of the FieldSet's collapse tool + */ +/** + * @var {string} $fieldset-collapse-tool-background-image + * The background-image to use for the collapse tool. If null the default tool + * sprite will be used. Defaults to null. + */ +/** + * @class Ext.form.field.Spinner + */ +/** + * @var {number} + * The height of the Spinner trigger buttons + */ +/** + * @var {number} + * The height of the Spinner trigger buttons when the Spinner is used on a + * {@link Ext.toolbar.Toolbar Toolbar} + */ +/** + * @class Ext.toolbar.Paging + */ +/** + * @var {boolean} + * True to include different icons when the paging toolbar buttons are disabled. + */ +/** + * @class Ext.view.BoundList + */ +/** + * @var {color} + * The background-color of the BoundList + */ +/** + * @var {color} + * The border-color of the BoundList + */ +/** + * @var {number} + * The border-width of the BoundList + */ +/** + * @var {string} + * The border-style of the BoundList + */ +/** + * @var {number} + * The height of BoundList items + */ +/** + * @var {string} + * The font family of the BoundList items + */ +/** + * @var {number} + * The font size of the BoundList items + */ +/** + * @var {string} + * The font-weight of the BoundList items + */ +/** + * @var {number/list} + * The padding of BoundList items + */ +/** + * @var {number} + * The border-width of BoundList items + */ +/** + * @var {string} + * The border-style of BoundList items + */ +/** + * @var {color} + * The border-color of BoundList items + */ +/** + * @var {color} + * The border-color of hovered BoundList items + */ +/** + * @var {color} + * The border-color of selected BoundList items + */ +/** + * @var {color} + * The background-color of hovered BoundList items + */ +/** + * @var {color} + * The background-color of selected BoundList items + */ +/** + * @class Ext.form.field.Radio + */ +/** + * @var {number} + * The size of the radio button + */ +/** + * @class Ext.picker.Date + */ +/** + * @var {number} + * The border-width of the DatePicker + */ +/** + * @var {string} + * The border-style of the DatePicker + */ +/** + * @var {color} + * The background-color of the DatePicker + */ +/** + * @var {string} + * The background-image of the DatePicker next arrow + */ +/** + * @var {string} + * The background-image of the DatePicker previous arrow + */ +/** + * @var {number} + * The width of DatePicker arrows + */ +/** + * @var {number} + * The height of DatePicker arrows + */ +/** + * @var {string} + * The type of cursor to display when the cursor is over a DatePicker arrow + */ +/** + * @var {number} + * The opacity of the DatePicker arrows + */ +/** + * @var {number} + * The opacity of the DatePicker arrows when hovered + */ +/** + * @var {string/list} + * The Date Picker header background gradient. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for {@link Global_CSS#background-gradient}. + */ +/** + * @var {number/list} + * The padding of the Date Picker header + */ +/** + * @var {color} + * The color of the Date Picker month button + */ +/** + * @var {number} + * The width of the arrow on the Date Picker month button + */ +/** + * @var {string} + * The background-image of the arrow on the Date Picker month button + */ +/** + * @var {boolean} + * True to render the month button as transparent + */ +/** + * @var {string} + * The text-align of the Date Picker header + */ +/** + * @var {number} + * The height of Date Picker items + */ +/** + * @var {number} + * The width of Date Picker items + */ +/** + * @var {number/list} + * The padding of Date Picker items + */ +/** + * @var {string} + * The font-family of Date Picker items + */ +/** + * @var {number} + * The font-size of Date Picker items + */ +/** + * @var {string} + * The font-weight of Date Picker items + */ +/** + * @var {string} + * The text-align of Date Picker items + */ +/** + * @var {color} + * The text color of Date Picker items + */ +/** + * @var {string} + * The type of cursor to display when the cursor is over a Date Picker item + */ +/** + * @var {string} + * The font-family of Date Picker column headers + */ +/** + * @var {number} + * The font-size of Date Picker column headers + */ +/** + * @var {string} + * The font-weight of Date Picker column headers + */ +/** + * @var {string/list} + * The background-gradient of Date Picker column headers. Can be either the name of a + * predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + */ +/** + * @var {string} + * The border-style of Date Picker column headers + */ +/** + * @var {number} + * The border-width of Date Picker column headers + */ +/** + * @var {string} + * The text-align of Date Picker column headers + */ +/** + * @var {number} + * The height of Date Picker column headers + */ +/** + * @var {number/list} + * The padding of Date Picker column headers + */ +/** + * @var {number} + * The border-width of Date Picker items + */ +/** + * @var {string} + * The border-style of Date Picker items + */ +/** + * @var {color} + * The border-color of Date Picker items + */ +/** + * @var {string} + * The border-style of today's date on the Date Picker + */ +/** + * @var {string} + * The border-style of the selected item + */ +/** + * @var {string} + * The font-weight of the selected item + */ +/** + * @var {color} + * The text color of the items in the previous and next months + */ +/** + * @var {string} + * The type of cursor to display when the cursor is over a disabled item + */ +/** + * @var {color} + * The text color of disabled Date Picker items + */ +/** + * @var {color} + * The background-color of disabled Date Picker items + */ +/** + * @var {color} + * The background-color of the Date Picker footer + */ +/** + * @var {string/list} + * The background-gradient of the Date Picker footer. Can be either the name of a + * predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + */ +/** + * @var {number/list} + * The border-width of the Date Picker footer + */ +/** + * @var {string} + * The border-style of the Date Picker footer + */ +/** + * @var {string} + * The text-align of the Date Picker footer + */ +/** + * @var {number/list} + * The padding of the Date Picker footer + */ +/** + * @var {number} + * The space between the footer buttons + */ +/** + * @var {color} + * The border-color of the Month Picker + */ +/** + * @var {number} + * The border-width of the Month Picker + */ +/** + * @var {string} + * The border-style of the Month Picker + */ +/** + * @var {color} + * The text color of Month Picker items + */ +/** + * @var {color} + * The text color of Month Picker items + */ +/** + * @var {color} + * The border-color of Month Picker items + */ +/** + * @var {string} + * The border-style of Month Picker items + */ +/** + * @var {string} + * The font-family of Month Picker items + */ +/** + * @var {number} + * The font-size of Month Picker items + */ +/** + * @var {string} + * The font-weight of Month Picker items + */ +/** + * @var {number/list} + * The margin of Month Picker items + */ +/** + * @var {string} + * The text-align of Month Picker items + */ +/** + * @var {number} + * The height of Month Picker items + */ +/** + * @var {string} + * The type of cursor to display when the cursor is over a Month Picker item + */ +/** + * @var {color} + * The background-color of hovered Month Picker items + */ +/** + * @var {color} + * The background-color of selected Month Picker items + */ +/** + * @var {string} + * The border-style of selected Month Picker items + */ +/** + * @var {color} + * The border-color of selected Month Picker items + */ +/** + * @var {number} + * The height of the Month Picker year navigation buttons + */ +/** + * @var {number} + * The width of the Month Picker year navigation buttons + */ +/** + * @var {string} + * The type of cursor to display when the cursor is over a Month Picker year navigation button + */ +/** + * @var {number} + * The opacity of the Month Picker year navigation buttons + */ +/** + * @var {number} + * The opacity of hovered Month Picker year navigation buttons + */ +/** + * @var {string} + * The background-image of the Month Picker next year navigation button + */ +/** + * @var {string} + * The background-image of the Month Picker previous year navigation button + */ +/** + * @var {list} + * The background-poisition of the Month Picker next year navigation button + */ +/** + * @var {list} + * The background-poisition of the hovered Month Picker next year navigation button + */ +/** + * @var {list} + * The background-poisition of the Month Picker previous year navigation button + */ +/** + * @var {list} + * The background-poisition of the hovered Month Picker previous year navigation button + */ +/** + * @var {string} + * The border-style of the Month Picker separator + */ +/** + * @var {number} + * The border-width of the Month Picker separator + */ +/** + * @var {color} + * The border-color of the Month Picker separator + */ +/** + * @var {number/list} + * The margin of Month Picker items when the datepicker does not have footer buttons + */ +/** + * @var {number} + * The height of Month Picker items when the datepicker does not have footer buttons + */ +/** + * @class Ext.picker.Color + */ +/** + * @var {color} + * The background-color of Color Pickers + */ +/** + * @var {color} + * The border-color of Color Pickers + */ +/** + * @var {number} + * The border-width of Color Pickers + */ +/** + * @var {string} + * The border-style of Color Pickers + */ +/** + * @var {number} + * The number of columns to display in the Color Picker + */ +/** + * @var {number} + * The number of rows to display in the Color Picker + */ +/** + * @var {number} + * The height of each Color Picker item + */ +/** + * @var {number} + * The width of each Color Picker item + */ +/** + * @var {number} + * The padding of each Color Picker item + */ +/** + * @var {string} + * The cursor to display when the mouse is over a Color Picker item + */ +/** + * @var {color} + * The border-color of Color Picker items + */ +/** + * @var {number} + * The border-width of Color Picker items + */ +/** + * @var {string} + * The border-style of Color Picker items + */ +/** + * @var {color} + * The border-color of hovered Color Picker items + */ +/** + * @var {color} + * The background-color of Color Picker items + */ +/** + * @var {color} + * The background-color of hovered Color Picker items + */ +/** + * @var {color} + * The border-color of the selected Color Picker item + */ +/** + * @var {color} + * The background-color of the selected Color Picker item + */ +/** + * @var {color} + * The inner border-color of Color Picker items + */ +/** + * @var {number} + * The inner border-width of Color Picker items + */ +/** + * @var {string} + * The inner border-style of Color Picker items + */ +/** + * @class Ext.form.field.HtmlEditor + */ +/** + * @var {number} + * The border-width of the HtmlEditor + */ +/** + * @var {color} + * The border-color of the HtmlEditor + */ +/** + * @var {color} + * The background-color of the HtmlEditor + */ +/** + * @var {number} + * The size of the HtmlEditor toolbar icons + */ +/** + * @var {number} + * The font-size of the HtmlEditor's font selection control + */ +/** + * @var {number} + * The font-family of the HtmlEditor's font selection control + */ +/** + * @class Ext.grid.column.Action + */ +/** + * @var {number} + * The height of action column icons + */ +/** + * @var {number} + * The width of action column icons + */ +/** + * @var {string} + * The type of cursor to display when the cursor is over an action column icon + */ +/** + * @var {number} + * The opacity of disabled action column icons + */ +/** + * @var {number} + * The amount of padding to add to the left and right of the action column cell + */ +/** + * @class Ext.grid.column.Check + */ +/** + * @var {number} + * Opacity of disabled CheckColumns + */ +/** + * @class Ext.grid.column.RowNumberer + */ +/** + * @var {number} + * The horizontal space before the number in the RowNumberer cell + */ +/** + * @var {number} + * The horizontal space after the number in the RowNumberer cell + */ +/** + * @class Ext.grid.feature.Grouping + */ +/** + * @var {color} + * The background color of group headers + */ +/** + * @var {number/list} + * The border-width of group headers + */ +/** + * @var {string} + * The border-style of group headers + */ +/** + * @var {color} + * The border-color of group headers + */ +/** + * @var {number/list} + * The padding of group headers + */ +/** + * @var {string} + * The cursor of group headers + */ +/** + * @var {color} + * The text color of group header titles + */ +/** + * @var {string} + * The font-family of group header titles + */ +/** + * @var {number} + * The font-size of group header titles + */ +/** + * @var {string} + * The font-weight of group header titles + */ +/** + * @var {number} + * The line-height of group header titles + */ +/** + * @var {number/list} + * The amount of padding to add to the group title element. This is typically used + * to reserve space for an icon by setting the amountof space to be reserved for the icon + * as the left value and setting the remaining sides to 0. + */ +/** + * @class Ext.grid.locking.Lockable + */ +/** + * @var {number} + * The width of the border between the locked views + */ +/** + * @var {string} + * The border-style of the border between the locked views + */ +/** + * @class Ext.grid.plugin.Editing + */ +/** + * @var {number} + * The height of grid editor text fields. Defaults to $form-field-height. If grid row + * height is smaller than $form-field-height, defaults to the grid row height. Grid row + * height is caluclated by adding $grid-row-cell-line-height to the top and bottom values of + * $grid-cell-inner-padding. + */ +/** + * @var {number/list} + * The padding of grid editor text fields. + */ +/** + * @var {number} + * The font size of the grid editor text + */ +/** + * @var {string} + * The font-weight of the grid editor text + */ +/** + * @var {string} + * The font-family of the grid editor text + */ +/** + * @class Ext.grid.plugin.RowEditing + */ +/** + * @var {color} + * The background-color of the RowEditor + */ +/** + * @var {color} + * The border-color of the RowEditor + */ +/** + * @var {number} + * The border-width of the RowEditor + */ +/** + * @var {number/list} + * The padding of the RowEditor + */ +/** + * @var {number} + * The amount of space in between the editor fields + */ +/** + * @var {number} + * The space between the RowEditor buttons + */ +/** + * @var {number} + * The border-radius of the RowEditor button container + */ +/** + * @var {number/list} + * The padding of the RowEditor button container + */ +/** + * @var {number/list} + * Padding to apply to the body element of the error tooltip + */ +/** + * @var {string} + * The list-style of the error tooltip's list items + */ +/** + * @var {number} + * Space to add before each list item on the error tooltip + */ +/** + * @class Ext.grid.property.Grid + */ +/** + * @var {string} + * The background-image of property grid cells + */ +/** + * @var {string} + * The background-position of property grid cells + */ +/** + * @var {number/string} + * The padding to add before the text of property grid cells to make room for the + * background-image. Only applies if $grid-property-cell-background-image is not null + */ +/** + * @class Ext.layout.container.Accordion + */ +/** + * @var {color} + * The text color of Accordion headers + */ +/** + * @var {color} + * The background-color of Accordion headers + */ +/** + * @var {number} + * The size of {@link Ext.panel.Tool Tools} in Accordion headers + */ +/** + * @var {number/list} + * The border-width of Accordion headers + */ +/** + * @var {number/list} + * The padding of Accordion headers + */ +/** + * @var {string} + * The font-weight of Accordion headers + */ +/** + * @var {string} + * The font-family of Accordion headers + */ +/** + * @var {string} + * The text-transform property of Accordion headers + */ +/** + * @var {number} + * The body border-width of Accordion layout element + */ +/** + * @var {color} + * The background-color of the Accordion layout element + */ +/** + * @var {color} + * The background-color of the Accordion layout element + */ +/** + * @var {number/list} + * The padding of the Accordion layout element + */ +/** + * @var {string} + * The sprite image to use for {@link Ext.panel.Tool Tools} in Accordion headers + */ +/** + * @class Ext.resizer.Splitter + */ +/** + * @var {number} + * The size of the Splitter + */ +/** + * @var {color} + * The background-color of the active Splitter (the Splitter currently being dragged) + */ +/** + * @var {number} + * The opacity of the active Splitter (the Splitter currently being dragged) + */ +/** + * @var {number} + * The opacity of the collapse tool on the active Splitter (the Splitter currently being dragged) + */ +/** + * @var {string} + * The the type of cursor to display when the cursor is over the collapse tool + */ +/** + * @var {number} + * The size of the collapse tool. This becomes the width of the collapse tool for + * horizontal splitters, and the height for vertical splitters. + */ +/** + * @class Ext.layout.container.Border + */ +/** + * @var {color} + * The background-color of the Border layout element + */ +/** + * @class Ext.panel.Tool + */ +/** + * @var {number} + * The size of Tools + */ +/** + * @var {boolean} + * True to change the background-position of the Tool on hover. Allows for a separate + * hover state icon in the sprite. + */ +/** + * @var {string} + * The cursor to display when the mouse cursor is over a Tool + */ +/** + * @var {number} + * The opacity of Tools + */ +/** + * @var {number} + * The opacity of hovered Tools + */ +/** + * @var {number} + * The opacity of pressed Tools + */ +/** + * @var {string} + * The sprite to use as the background-image for Tools + */ +/** + * @class Ext.tab.Tab + */ +/** + * @var {color} + * The base color of Tabs + */ +/** + * @var {color} + * The base color of hovered Tabs + */ +/** + * @var {color} + * The base color of the active Tabs + */ +/** + * @var {color} + * The base color of disabled Tabs + */ +/** + * @var {color} + * The text color of Tabs + */ +/** + * @var {color} + * The text color of hovered Tabs + */ +/** + * @var {color} + * The text color of the active Tab + */ +/** + * @var {color} + * The text color of disabled Tabs + */ +/** + * @var {number} + * The font-size of Tabs + */ +/** + * @var {number} + * The font-size of hovered Tabs + */ +/** + * @var {number} + * The font-size of the active Tab + */ +/** + * @var {number} + * The font-size of disabled Tabs + */ +/** + * @var {string} + * The font-family of Tabs + */ +/** + * @var {string} + * The font-family of hovered Tabs + */ +/** + * @var {string} + * The font-family of the active Tab + */ +/** + * @var {string} + * The font-family of disabled Tabs + */ +/** + * @var {string} + * The font-weight of Tabs + */ +/** + * @var {string} + * The font-weight of hovered Tabs + */ +/** + * @var {string} + * The font-weight of the active Tab + */ +/** + * @var {string} + * The font-weight of disabled Tabs + */ +/** + * @var {string} + * The Tab cursor + */ +/** + * @var {string} + * The cursor of disabled Tabs + */ +/** + * @var {string/list} + * The background-gradient for Tabs. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + */ +/** + * @var {string/list} + * The background-gradient for hovered Tabs. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + */ +/** + * @var {string/list} + * The background-gradient for the active Tab. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + */ +/** + * @var {string/list} + * The background-gradient for disabled Tabs. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + */ +/** + * @var {list} + * The border-radius of Tabs + */ +/** + * @var {number} + * The border-width of Tabs + */ +/** + * @var {number/list} + * The inner border-width of Tabs + */ +/** + * @var {color} + * The inner border-color of Tabs + */ +/** + * @var {color} + * The border-color of Tabs + */ +/** + * @var {color} + * The border-color of hovered Tabs + */ +/** + * @var {color} + * The border-color of the active Tab + */ +/** + * @var {color} + * The border-color of disabled Tabs + */ +/** + * @var {number/list} + * The padding of Tabs + */ +/** + * @var {number/list} + * The padding of the Tab's text element + */ +/** + * @var {number} + * The line-height of Tabs + */ +/** + * @var {number/list} + * The margin of Tabs. Typically used to add horizontal space between the tabs. + */ +/** + * @var {number} + * The width of the Tab close icon + */ +/** + * @var {number} + * The height of the Tab close icon + */ +/** + * @var {number} + * The distance to offset the Tab close icon from the top of the tab + */ +/** + * @var {number} + * The distance to offset the Tab close icon from the right of the tab + */ +/** + * @var {number} + * the space in between the text and the close button + */ +/** + * @var {number} + * The opacity of the Tab close icon + */ +/** + * @var {number} + * The opacity of the Tab close icon when hovered + */ +/** + * @var {number} + * The opacity of the Tab close icon when the Tab is disabled + */ +/** + * @var {boolean} + * True to change the x background-postition of the close icon background image on hover + * to allow for a horizontally aligned background image sprite + */ +/** + * @var {boolean} + * True to change the x background-postition of the close icon background image on click + * to allow for a horizontally aligned background image sprite + */ +/** + * @var {number} + * The width of Tab icons + */ +/** + * @var {number} + * The height of Tab icons + */ +/** + * @var {number} + * The space between the Tab icon and the Tab text + */ +/** + * @var {number} + * The background-position of Tab icons + */ +/** + * @var {color} + * The color of Tab glyph icons + */ +/** + * @var {color} + * The color of a Tab glyph icon when the Tab is hovered + */ +/** + * @var {color} + * The color of a Tab glyph icon when the Tab is active + */ +/** + * @var {color} + * The color of a Tab glyph icon when the Tab is disabled + */ +/** + * @var {number} + * The opacity of a Tab glyph icon + */ +/** + * @var {number} + * The opacity of a Tab glyph icon when the Tab is disabled + */ +/** + * @var {number} + * opacity to apply to the tab's main element when the tab is disabled + */ +/** + * @var {number} + * opacity to apply to the tab's text element when the tab is disabled + */ +/** + * @var {number} + * opacity to apply to the tab's icon element when the tab is disabled + */ +/** + * @var {string} + * Experimental - Has issues with IE + * The direction to rotate the contents of a left-aligned tab. `right` to rotate + * clockwise or `left` to rotate counterclockwise. Defaults to `left`. + */ +/** + * @var {string} + * Experimental - Has issues with IE + * The direction to rotate the contents of a right-aligned tab. `right` to rotate + * clockwise or `left` to rotate counterclockwise. Defaults to `right`. + */ +/** + * @var {boolean} + * True to include the "default" tab UI + */ +/** + * @class Ext.tab.Bar + */ +/** + * @var {number/list} + * The padding of the Tab Bar + */ +/** + * @var {number/list} + * The padding of the {@link Ext.tab.Panel#plain plain} Tab Bar + */ +/** + * @var {color} + * The base color of the Tab Bar + */ +/** + * @var {string/list} + * The background-gradient of the Tab Bar. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + */ +/** + * @var {color} + * The border-color of the Tab Bar + */ +/** + * @var {number/list} + * The border-width of the Tab Bar + */ +/** + * @var {number/list} + * The border-width of the {@link Ext.tab.Panel#plain plain} Tab Bar + */ +/** + * @var {number} + * The height of the Tab Bar strip + */ +/** + * @var {color} + * The border-color of the Tab Bar strip + */ +/** + * @var {color} + * The background-color of the Tab Bar strip + */ +/** + * @var {number/list} + * The border-width of the Tab Bar strip + */ +/** + * @var {number/list} + * The border-width of the {@link Ext.tab.Panel#plain plain} Tab Bar strip + */ +/** + * @var {number} + * The width of the Tab Bar scrollers + */ +/** + * @var {string} + * The cursor of the Tab Bar scrollers + */ +/** + * @var {string} + * The cursor of disabled Tab Bar scrollers + */ +/** + * @var {number} + * The opacity of Tab Bar scrollers + */ +/** + * @var {number} + * The opacity of hovered Tab Bar scrollers + */ +/** + * @var {number} + * The opacity of pressed Tab Bar scrollers + */ +/** + * @var {number} + * The opacity of disabled Tab Bar scrollers + */ +/** + * @var {boolean} + * true to change the x postition of the background image on hover to allow for a + * horizonatlly alined background image sprite + */ +/** + * @var {boolean} + * true to include separate scroller icons for "plain" tabbars + */ +/** + * @var {boolean} + * if true, the tabbar will use symmetrical scroller icons. Top and bottom tabbars + * will share icons, and Left and right will share icons. + */ +/** + * @var {boolean} + * True to include the "default" tabbar UI + */ +/** + * @class Ext.selection.CheckboxModel + */ +/** + * @var {number} + * The horizontal space before the checkbox + */ +/** + * @var {number} + * The horizontal space after the checkbox + */ +/* including package ext-theme-base */ +/** + * @class Global_CSS + */ +/** + * @var {string} $prefix + * The prefix to be applied to all CSS selectors. If this is changed, it must also be changed in your + * JavaScript application. + */ +/** + * @var {boolean/string} $relative-image-path-for-uis + * True to use a relative image path for all new UIs. If true, the path will be "../images/". + * It can also be a string of the path value. + * It defaults to false, which means it will look for the images in the ExtJS SDK folder. + */ +/** + * @var {boolean} $include-not-found-images + * True to include files which are not found when compiling your SASS + */ +/** + * @var {boolean} $include-ie + * True to include Internet Explorer specific rules for IE9 and lower. IE10 and up are + * considered to be "modern" browsers, and as such do not need any of the CSS hacks required + * for IE9 and below. Setting this property to false will result in a significantly smaller + * CSS file size, and may also result in a slight performance improvement, because the + * browser will have fewer rules to process. + */ +/** + * @var {boolean} $include-content-box + * True to include rules for browsers that do not support the border-box model + * (IE6 strict and IE7 strict) + */ +/** + * @var {boolean} $include-ff + * True to include Firefox specific rules + */ +/** + * @var {boolean} $include-opera + * True to include Opera specific rules + */ +/** + * @var {boolean} $include-webkit + * True to include Webkit specific rules + */ +/** + * @var {boolean} $include-safari + * True to include Safari specific rules + */ +/** + * @var {boolean} $include-chrome + * True to include Chrome specific rules + */ +/** + * @var {boolean} $include-slicer-border-radius + * True to include rules for rounded corners produced by the slicer. Enables emulation + * of CSS3 border-radius in browsers that do not support it. + */ +/** + * @var {boolean} $include-slicer-gradient + * True to include rules for background gradients produced by the slicer. Enables emulation + * of CSS3 background-gradient in browsers that do not support it. + */ +/** + * @var {number} $css-shadow-border-radius + * The border radius for CSS shadows + */ +/** + * @var {string} $image-extension + * default file extension to use for images (defaults to 'png'). + */ +/** + * @var {string} $slicer-image-extension + * default file extension to use for slicer images (defaults to 'gif'). + */ +/** + * Default search path for images + */ +/** + * @var {boolean} + * True to include the default UI for each component. + */ +/** + * @var {string} + * The base path relative to the CSS output directory to use for theme resources. For example + * if the theme's images live one directory up from the generated CSS output in a directory + * named 'foo/images/', you would need to set this variable to '../foo/' in order for the image + * paths in the CSS output to be generated correctly. By default this is the same as the + * CSS output directory. + */ +/* including package ext-theme-base */ +.x-dd-drag-proxy, +.x-dd-drag-current { + z-index: 1000000!important; + pointer-events: none; +} + +.x-body { + margin: 0; +} + +img { + border: 0; +} + +.x-border-box, +.x-border-box * { + box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -webkit-box-sizing: border-box; +} + +.x-rtl { + direction: rtl; +} + +.x-ltr { + direction: ltr; +} + +.x-clear { + overflow: hidden; + clear: both; + font-size: 0; + line-height: 0; + display: table; +} + +.x-strict .x-ie7 .x-clear { + height: 0; + width: 0; +} + +.x-layer { + position: absolute !important; + overflow: hidden; + zoom: 1; +} + +.x-fixed-layer { + position: fixed !important; + overflow: hidden; + zoom: 1; +} + +.x-shim { + position: absolute; + left: 0; + top: 0; + overflow: hidden; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-hide-display { + display: none !important; +} + +.x-hide-visibility { + visibility: hidden !important; +} + +.x-ie6 .x-item-disabled { + filter: none; +} + +.x-hidden, +.x-hide-offsets { + display: block !important; + visibility: hidden !important; + position: absolute !important; + top: -10000px !important; +} + +.x-hide-nosize { + height: 0 !important; + width: 0 !important; +} + +.x-hide-clip { + position: absolute!important; + clip: rect(0, 0, 0, 0); + clip: rect(0 0 0 0); +} + +.x-masked-relative { + position: relative; +} + +.x-ie-shadow { + background-color: #777; + display: none; + position: absolute; + overflow: hidden; + zoom: 1; +} + +.x-unselectable { + user-select: none; + -o-user-select: none; + -ms-user-select: none; + -moz-user-select: -moz-none; + -webkit-user-select: none; + cursor: default; +} + +.x-selectable { + cursor: auto; + -moz-user-select: text; + -webkit-user-select: text; + -ms-user-select: text; + user-select: text; + -o-user-select: text; +} + +.x-list-plain { + list-style-type: none; + margin: 0; + padding: 0; +} + +.x-table-plain { + border-collapse: collapse; + border-spacing: 0; + font-size: 1em; +} + +.x-frame-tl, +.x-frame-tr, +.x-frame-tc, +.x-frame-bl, +.x-frame-br, +.x-frame-bc { + overflow: hidden; + background-repeat: no-repeat; +} + +.x-frame-tc, +.x-frame-bc { + background-repeat: repeat-x; +} + +.x-frame-mc { + background-repeat: repeat-x; + overflow: hidden; +} + +.x-proxy-el { + position: absolute; + background: #b4b4b4; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); + opacity: 0.8; +} + +.x-css-shadow { + position: absolute; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + -ms-border-radius: 5px; + -o-border-radius: 5px; + border-radius: 5px; +} + +.x-item-disabled, +.x-item-disabled * { + cursor: default; +} + +.x-webkit *:focus { + outline: none !important; +} + +.x-box-item { + position: absolute !important; + left: 0; + top: 0; +} + +.x-docked { + position: absolute !important; + z-index: 1; +} + +.x-docked-vertical { + position: static; +} + +.x-docked-top { + border-bottom-width: 0 !important; +} + +.x-docked-bottom { + border-top-width: 0 !important; +} + +.x-docked-left { + border-right-width: 0 !important; +} + +.x-docked-right { + border-left-width: 0 !important; +} + +.x-docked-noborder-top { + border-top-width: 0 !important; +} + +.x-docked-noborder-right { + border-right-width: 0 !important; +} + +.x-docked-noborder-bottom { + border-bottom-width: 0 !important; +} + +.x-docked-noborder-left { + border-left-width: 0 !important; +} + +.x-noborder-l { + border-left-width: 0 !important; +} + +.x-noborder-b { + border-bottom-width: 0 !important; +} + +.x-noborder-bl { + border-bottom-width: 0 !important; + border-left-width: 0 !important; +} + +.x-noborder-r { + border-right-width: 0 !important; +} + +.x-noborder-rl { + border-right-width: 0 !important; + border-left-width: 0 !important; +} + +.x-noborder-rb { + border-right-width: 0 !important; + border-bottom-width: 0 !important; +} + +.x-noborder-rbl { + border-right-width: 0 !important; + border-bottom-width: 0 !important; + border-left-width: 0 !important; +} + +.x-noborder-t { + border-top-width: 0 !important; +} + +.x-noborder-tl { + border-top-width: 0 !important; + border-left-width: 0 !important; +} + +.x-noborder-tb { + border-top-width: 0 !important; + border-bottom-width: 0 !important; +} + +.x-noborder-tbl { + border-top-width: 0 !important; + border-bottom-width: 0 !important; + border-left-width: 0 !important; +} + +.x-noborder-tr { + border-top-width: 0 !important; + border-right-width: 0 !important; +} + +.x-noborder-trl { + border-top-width: 0 !important; + border-right-width: 0 !important; + border-left-width: 0 !important; +} + +.x-noborder-trb { + border-top-width: 0 !important; + border-right-width: 0 !important; + border-bottom-width: 0 !important; +} + +.x-noborder-trbl { + border-width: 0 !important; +} + +.x-btn { + display: inline-block; + position: relative; + zoom: 1; + *display: inline; + outline: 0; + cursor: pointer; + white-space: nowrap; + vertical-align: middle; + text-decoration: none; +} + +.x-btn-wrap { + position: relative; + display: block; +} + +.x-btn-button { + position: relative; + display: block; + text-decoration: none; + overflow: hidden; + zoom: 1; +} + +.x-btn-inner { + display: block; + white-space: nowrap; + overflow: hidden; + zoom: 1; +} + +.x-btn-icon-el { + top: 0; + right: 0; + bottom: 0; + left: 0; + position: absolute; + background-repeat: no-repeat; + text-align: center; +} + +.x-btn-inner-center { + text-align: center; +} + +.x-btn-inner-left { + text-align: left; +} + +.x-btn-inner-right { + text-align: right; +} + +.x-box-layout-ct { + overflow: hidden; + zoom: 1; +} + +.x-box-target { + position: absolute; + width: 20000px; + top: 0; + left: 0; + height: 1px; +} + +.x-box-inner { + overflow: hidden; + zoom: 1; + position: relative; + left: 0; + top: 0; +} + +.x-horizontal-box-overflow-body { + float: left; +} + +.x-box-scroller { + position: relative; + background-repeat: no-repeat; +} + +.x-box-scroller-left, +.x-box-scroller-right { + float: left; + height: 100%; + z-index: 5; +} + +.x-box-scroller-top .x-box-scroller, +.x-box-scroller-bottom .x-box-scroller { + line-height: 0; + font-size: 0; + background-position: center 0; +} + +.x-box-menu-after { + float: right; +} + +.x-toolbar-text { + white-space: nowrap; +} + +.x-toolbar-separator { + display: block; + font-size: 1px; + overflow: hidden; + cursor: default; + border: 0; + width: 0; + height: 0; + line-height: 0px; +} + +.x-quirks .x-ie .x-toolbar .x-toolbar-separator-horizontal { + width: 2px; +} + +.x-toolbar-scroller { + padding-left: 0; +} + +.x-toolbar-plain { + border: 0; +} + +.x-header-icon { + background-repeat: no-repeat; + background-position: 0 0; + vertical-align: middle; + text-align: center; +} + +.x-header-text-container { + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; +} + +.x-dd-drag-repair .x-dd-drag-ghost { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60); + opacity: 0.6; +} +.x-dd-drag-repair .x-dd-drop-icon { + display: none; +} + +.x-dd-drag-ghost { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=85); + opacity: 0.85; + padding: 5px; + padding-left: 20px; + white-space: nowrap; + color: #000; + font: normal 12px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border: 1px solid; + border-color: #ddd #bbb #bbb #ddd; + background-color: #fff; +} + +.x-dd-drop-icon { + position: absolute; + top: 3px; + left: 3px; + display: block; + width: 16px; + height: 16px; + background-color: transparent; + background-position: center; + background-repeat: no-repeat; + z-index: 1; +} + +.x-dd-drop-ok .x-dd-drop-icon { + background-image: url(images/dd/drop-yes.png); +} + +.x-dd-drop-ok-add .x-dd-drop-icon { + background-image: url(images/dd/drop-add.png); +} + +.x-dd-drop-nodrop div.x-dd-drop-icon { + background-image: url(images/dd/drop-no.png); +} + +.x-panel, +.x-plain { + overflow: hidden; + position: relative; +} + +.x-panel { + outline: none; +} + +.x-ie .x-panel-header, +.x-ie .x-panel-header-tl, +.x-ie .x-panel-header-tc, +.x-ie .x-panel-header-tr, +.x-ie .x-panel-header-ml, +.x-ie .x-panel-header-mc, +.x-ie .x-panel-header-mr, +.x-ie .x-panel-header-bl, +.x-ie .x-panel-header-bc, +.x-ie .x-panel-header-br { + zoom: 1; +} + +.x-ie8 td.x-frame-mc { + vertical-align: top; +} + +.x-panel-body { + overflow: hidden; + position: relative; +} + +.x-nlg .x-panel-header-vertical .x-frame-mc { + background-repeat: repeat-y; +} + +.x-panel-header-plain, +.x-panel-body-plain { + border: 0; + padding: 0; +} + +.x-tip { + position: absolute; + overflow: visible; + /*pointer needs to be able to stick out*/ +} + +.x-tip-body { + overflow: hidden; + position: relative; +} + +.x-tip-anchor { + position: absolute; + overflow: hidden; + border-style: solid; +} + +div.x-editor { + overflow: visible; +} + +.x-form-item-label { + display: block; +} + +.x-form-item-label-right { + text-align: right; +} + +.x-form-item-label-top { + display: block; + zoom: 1; +} + +.x-form-invalid-icon { + overflow: hidden; +} +.x-form-invalid-icon ul { + display: none; +} + +.x-window { + outline: none; + overflow: hidden; +} +.x-window .x-window-wrap { + position: relative; +} + +.x-window-body { + position: relative; + overflow: hidden; +} + +.x-form-textarea { + overflow: auto; + resize: none; +} +.x-safari.x-mac .x-form-textarea { + margin-bottom: -2px; +} + +.x-form-display-field-body { + vertical-align: top; +} + +.x-progress { + position: relative; + border-style: solid; + overflow: hidden; +} + +.x-progress-bar { + overflow: hidden; + position: absolute; + width: 0; + height: 100%; +} + +.x-progress-text { + overflow: hidden; + position: absolute; +} + +.x-message-box .x-form-display-field { + height: auto; +} + +.x-fit-item { + position: relative; +} + +.x-grid-row, +.x-grid-data-row { + outline: none; +} + +.x-grid-view { + overflow: hidden; + position: relative; +} + +.x-grid-table { + table-layout: fixed; + border-collapse: separate; +} + +.x-grid-td { + overflow: hidden; + border-width: 0; + vertical-align: top; +} + +.x-grid-cell-inner { + overflow: hidden; + white-space: nowrap; + zoom: 1; +} + +.x-grid-resize-marker { + position: absolute; + z-index: 5; + top: 0; +} + +.x-mask { + z-index: 100; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + zoom: 1; +} + +.x-mask-fixed { + position: fixed; +} + +.x-mask-shim { + z-index: 100; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.x-mask-msg { + z-index: 101; + position: absolute; +} + +.col-move-top, +.col-move-bottom { + position: absolute; + top: 0; + line-height: 0; + font-size: 0; + overflow: hidden; + z-index: 20000; + background: no-repeat center top transparent; +} + +.x-grid-header-ct { + cursor: default; +} + +.x-column-header { + position: absolute; + overflow: hidden; + background-repeat: repeat-x; +} + +.x-column-header-inner { + zoom: 1; + white-space: nowrap; + position: relative; + overflow: hidden; +} + +.x-column-header-text { + white-space: nowrap; + background-repeat: no-repeat; +} + +.x-column-header-trigger { + display: none; + height: 100%; + background-repeat: no-repeat; + position: absolute; + right: 0; + top: 0; + z-index: 2; +} + +.x-column-header-over .x-column-header-trigger, .x-column-header-open .x-column-header-trigger { + display: block; +} + +.x-column-header-align-right { + text-align: right; +} + +.x-column-header-align-left { + text-align: left; +} + +.x-column-header-align-center { + text-align: center; +} + +.x-autowidth-table .x-grid-table { + table-layout: auto; + width: auto !important; +} + +.x-tree-view { + overflow: hidden; +} + +.x-tree-elbow-img, +.x-tree-icon { + background-repeat: no-repeat; + background-position: 0 center; + vertical-align: top; +} + +.x-tree-checkbox { + border: 0; + padding: 0; + vertical-align: top; + position: relative; + background-color: transparent; +} + +.x-tree-animator-wrap { + overflow: hidden; +} + +.x-tree-node-text { + zoom: 1; +} + +.x-form-cb-wrap { + vertical-align: top; +} + +.x-form-cb-wrap-inner { + position: relative; + zoom: 1; +} + +.x-form-cb { + position: absolute; + left: 0; + right: auto; + vertical-align: top; + overflow: hidden; + padding: 0; + border: 0; +} +.x-form-cb::-moz-focus-inner { + padding: 0; + border: 0; +} + +/* allow for the component to be positioned after the label */ +.x-form-cb-after { + left: auto; + right: 0; +} + +/* some browsers like IE 10 need a block element to be able to measure +the height of a multi-line element */ +.x-form-cb-label { + display: inline-block; + zoom: 1; +} + +td.x-grid-rowwrap .x-grid-table { + border: 0; +} +td.x-grid-rowwrap .x-grid-cell { + border-bottom: 0; + background-color: transparent; +} + +.x-grid-rowbody { + zoom: 1; +} + +.x-grid-row-body-hidden { + display: none; +} + +.x-grid-row-expander { + font-size: 0; + line-height: 0; +} + +.x-slider { + outline: none; + zoom: 1; + position: relative; +} + +.x-slider-inner { + position: relative; + left: 0; + top: 0; + overflow: visible; + zoom: 1; +} +.x-slider-vert .x-slider-inner { + background: repeat-y 0 0; +} + +.x-slider-end { + zoom: 1; +} + +.x-slider-thumb { + position: absolute; + background: no-repeat 0 0; +} +.x-slider-horz .x-slider-thumb { + left: 0; +} +.x-slider-vert .x-slider-thumb { + bottom: 0; +} + +.x-menu { + outline: none; +} + +.x-menu-item { + white-space: nowrap; + overflow: hidden; +} + +.x-menu-item-cmp { + margin: 2px; +} +.x-menu-item-cmp .x-field-label-cell { + vertical-align: middle; +} + +.x-menu-icon-separator { + position: absolute; + top: 0px; + z-index: 0; + height: 100%; + overflow: hidden; +} +.x-menu-plain .x-menu-icon-separator { + display: none; +} + +.x-menu-item-link { + text-decoration: none; + outline: 0; + display: block; +} + +.x-menu-item-text { + display: inline-block; + zoom: 1; +} + +.x-menu-item-icon, +.x-menu-item-icon-right, +.x-menu-item-arrow { + font-size: 0; + position: absolute; + text-align: center; +} + +.x-form-trigger { + cursor: pointer; + overflow: hidden; + background-repeat: no-repeat; +} +.x-item-disabled .x-form-trigger { + cursor: default; +} + +.x-trigger-noedit { + cursor: default; +} + +.x-form-trigger-wrap { + vertical-align: top; + border-collapse: separate; +} + +.x-surface { + display: -moz-inline-stack; + display: inline-block; + vertical-align: middle; + *vertical-align: auto; + zoom: 1; + *display: inline; + overflow: hidden; +} + +.rvml { + behavior: url(#default#VML); +} + +.x-surface tspan { + user-select: none; + -o-user-select: none; + -ms-user-select: none; + -moz-user-select: -moz-none; + -webkit-user-select: none; + cursor: default; +} + +.x-vml-sprite { + position: absolute; + left: 0; + top: 0; + width: 1px; + height: 1px; +} + +.x-vml-group { + position: absolute; + left: 0; + top: 0; + width: 1000px; + height: 1000px; +} + +.x-vml-measure-span { + position: absolute; + left: -9999em; + top: -9999em; + padding: 0; + margin: 0; + display: inline; +} + +.x-vml-base { + position: relative; + top: 0; + left: 0; + overflow: hidden; + display: inline-block; +} + +.x-vml-base { + position: relative; + top: 0; + left: 0; + overflow: hidden; + display: inline-block; +} + +svg, vml { + overflow: hidden; +} + +.x-table-layout { + font-size: 1em; +} + +.x-btn-group { + position: relative; + overflow: hidden; +} + +.x-btn-group-body { + position: relative; + zoom: 1; +} +.x-btn-group-body .x-table-layout-cell { + vertical-align: top; +} + +.x-viewport, .x-viewport body { + margin: 0; + padding: 0; + border: 0 none; + overflow: hidden; + height: 100%; + position: static; +} + +.x-fieldset { + display: block; + /* preserve margins in IE */ + position: relative; +} + +.x-fieldset-header { + overflow: hidden; +} +.x-fieldset-header .x-form-item, +.x-fieldset-header .x-tool { + float: left; +} +.x-fieldset-header .x-fieldset-header-text { + float: left; +} +.x-fieldset-header .x-form-cb-wrap { + font-size: 0; + line-height: 0; + height: auto; +} +.x-fieldset-header .x-form-cb { + margin: 0; + position: static; +} + +.x-form-spinner-up, +.x-form-spinner-down { + font-size: 0; +} + +.x-strict .x-ie9 .x-boundlist-list-ct { + min-height: 0\%; +} + +.x-form-item { + vertical-align: top; + table-layout: fixed; +} + +.x-form-item-body { + position: relative; +} + +.x-form-form-item td { + border-top: 1px solid transparent; +} + +.x-datepicker { + position: relative; +} +.x-datepicker .x-monthpicker { + left: 0; + top: 0; + display: block; +} +.x-datepicker .x-monthpicker-months, +.x-datepicker .x-monthpicker-years { + height: 100%; +} + +.x-datepicker-inner { + table-layout: fixed; + width: 100%; + border-collapse: separate; +} + +.x-datepicker-cell { + padding: 0; +} + +.x-datepicker-header { + position: relative; + zoom: 1; +} + +.x-datepicker-arrow { + position: absolute; + outline: none; + font-size: 0; +} + +.x-datepicker-column-header { + padding: 0; +} + +.x-datepicker-date { + display: block; + zoom: 1; + text-decoration: none; +} + +.x-monthpicker { + display: table; +} + +.x-monthpicker-body { + height: 100%; + position: relative; +} + +.x-monthpicker-months, +.x-monthpicker-years { + float: left; +} + +.x-monthpicker-item { + float: left; +} + +.x-monthpicker-item-inner { + display: block; + text-decoration: none; +} + +.x-monthpicker-yearnav-button-ct { + float: left; + text-align: center; +} + +.x-monthpicker-yearnav-button { + display: inline-block; + outline: none; + font-size: 0; +} + +.x-monthpicker-buttons { + width: 100%; +} + +.x-datepicker .x-monthpicker-buttons { + position: absolute; + bottom: 0; +} + +.x-form-file-btn { + overflow: hidden; +} + +.x-form-file-input { + border: 0; + position: absolute; + cursor: pointer; + top: -2px; + right: -2px; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; + /* Yes, there's actually a good reason for this... + * If the configured buttonText is set to something longer than the default, + * then it will quickly exceed the width of the hidden file input's "Browse..." + * button, so part of the custom button's clickable area will be covered by + * the hidden file input's text box instead. This results in a text-selection + * mouse cursor over that part of the button, at least in Firefox, which is + * confusing to a user. Giving the hidden file input a huge font-size makes + * the native button part very large so it will cover the whole clickable area. + */ + font-size: 1000px; +} + +.x-form-item-hidden { + margin: 0; +} + +.x-color-picker-item { + float: left; + text-decoration: none; +} + +.x-color-picker-item-inner { + display: block; + font-size: 1px; +} + +.x-html-editor-tb .x-toolbar { + position: static !important; +} + +.x-htmleditor-iframe { + display: block; + overflow: auto; +} + +.x-grid-cell-inner-action-col { + line-height: 0; + font-size: 0; +} + +.x-grid-cell-inner-checkcolumn { + line-height: 0; + font-size: 0; +} + +.x-grid-group, +.x-grid-group-body, +.x-grid-group-hd { + zoom: 1; +} + +.x-group-hd-container { + overflow: hidden; +} + +.x-grid-group-hd { + white-space: nowrap; + outline: none; +} + +.x-grid-row-body-hidden, .x-grid-group-collapsed { + display: none; +} + +.x-grid-editor .x-form-cb-wrap { + text-align: center; +} + +.x-grid-editor .x-form-cb { + position: static; +} + +.x-grid-editor .x-form-display-field { + margin: 0; + white-space: nowrap; + overflow: hidden; +} +.x-grid-editor div.x-form-action-col-field { + line-height: 0; +} + +.x-grid-row-editor { + position: absolute; + overflow: visible; + z-index: 1; +} + +.x-grid-row-editor-buttons { + position: absolute; + white-space: nowrap; +} + +.x-abs-layout-ct { + position: relative; +} + +.x-abs-layout-item { + position: absolute !important; +} + +.x-splitter { + font-size: 1px; +} + +.x-splitter-horizontal { + cursor: e-resize; + cursor: row-resize; +} + +.x-splitter-vertical { + cursor: e-resize; + cursor: col-resize; +} + +.x-splitter-collapsed, +.x-splitter-horizontal-noresize, +.x-splitter-vertical-noresize { + cursor: default; +} + +.x-splitter-active { + z-index: 4; +} + +.x-collapse-el { + position: absolute; + background-repeat: no-repeat; +} + +.x-border-layout-ct { + overflow: hidden; + zoom: 1; +} + +.x-border-layout-ct { + position: relative; +} + +.x-border-region-slide-in { + z-index: 5; +} + +.x-region-collapsed-placeholder { + z-index: 4; +} + +.x-column { + float: left; +} + +.x-ie6 .x-column { + display: inline; + /*prevent IE6 double-margin bug*/ +} + +.x-quirks .x-ie .x-form-layout-table, .x-quirks .x-ie .x-form-layout-table tbody tr.x-form-item { + position: relative; +} + +.x-form-layout-table { + border-collapse: separate; + border-spacing: 0 2px; +} + +.x-ie6 .x-form-layout-table { + border-collapse: collapse; + border-spacing: 0; +} + +.x-resizable-overlay { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + display: none; + z-index: 200000; + background-color: #fff; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-resizable-wrapped { + box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -webkit-box-sizing: border-box; +} + +a.x-tab { + text-decoration: none; +} +a.x-tab .x-tab-inner-left { + text-align: left; +} +a.x-tab .x-tab-inner-right { + text-align: right; +} + +.x-tab-bar { + position: relative; +} + +.x-column-header-checkbox .x-column-header-text { + display: block; + background-repeat: no-repeat; + font-size: 0; +} + +.x-grid-cell-row-checker { + vertical-align: middle; + background-repeat: no-repeat; + font-size: 0; +} + +.x-tab { + outline: none; + display: block; + white-space: nowrap; + z-index: 1; +} + +.x-tab-active { + z-index: 3; +} + +.x-tab-wrap { + display: block; + position: relative; +} + +.x-tab-button { + zoom: 1; + display: block; +} + +.x-tab-inner { + display: block; + text-align: center; + white-space: nowrap; + text-overflow: ellipsis; + -o-text-overflow: ellipsis; + overflow: hidden; + zoom: 1; +} + +.x-btn-icon-el { + top: 0; + right: 0; + bottom: 0; + left: 0; + position: absolute; + background-repeat: no-repeat; + text-align: center; +} + +.x-tab-bar { + z-index: 1; +} + +.x-tab-bar-body { + z-index: 2; + position: relative; +} + +.x-tab-bar-strip { + position: absolute; + line-height: 0; + font-size: 0; + z-index: 1; +} + +.x-tab-bar-horizontal .x-tab-bar-strip { + width: 100%; + left: 0; +} + +.x-tab-bar-vertical .x-tab-bar-strip { + height: 100%; + top: 0; +} + +.x-tab-bar-strip-top { + bottom: 0; +} + +.x-tab-bar-strip-bottom { + top: 0; +} + +.x-tab-bar-strip-left { + right: 0; +} + +.x-tab-bar-strip-right { + left: 0; +} + +.x-tab-bar-plain { + background: transparent !important; +} + +.x-tab-icon-el { + position: absolute; + background-repeat: no-repeat; + top: 0; + left: 0; + right: auto; + bottom: 0; +} + +.x-tab-close-btn { + display: block; + position: absolute; + font-size: 0; + line-height: 0; + background: no-repeat; +} + +.x-tab-mc { + overflow: visible; +} + +/* including package ext-theme-neutral */ +.x-body { + color: #333333; + font-size: 13px; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + background: #f5f5f5; +} + +.x-animating-size, +.x-collapsed { + overflow: hidden!important; +} + +/** + * Creates a visual theme for a Button. This mixin is not {@link #scale} aware, and therefore + * does not provide defaults for most parameters, so it is advisable to use one of the + * following mixins instead when creating a custom buttonUI: + * + * #extjs-button-small-ui - creates a button UI for a small button + * #extjs-button-medium-ui - creates a button UI for a medium button + * #extjs-button-large-ui - creates a button UI for a large button + * #extjs-button-toolbar-small-ui - creates a button UI for a small toolbar button + * #extjs-button-toolbar-medium-ui - creates a button UI for a medium toolbar button + * #extjs-button-toolbar-large-ui - creates a button UI for a large toolbar button + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {number} [$border-radius=0px] + * The border-radius of the button + * + * @param {number} [$border-width=0px] + * The border-width of the button + * + * @param {color} $border-color + * The border-color of the button + * + * @param {color} $border-color-over + * The border-color of the button when the cursor is over the button + * + * @param {color} $border-color-focus + * The border-color of the button when focused + * + * @param {color} $border-color-pressed + * The border-color of the button when pressed + * + * @param {color} $border-color-disabled + * The border-color of the button when disabled + * + * @param {number} $padding + * The amount of padding inside the border of the button on all sides + * + * @param {number} $text-padding + * The amount of horizontal space to add to the left and right of the button text + * + * @param {color} $background-color + * The background-color of the button + * + * @param {color} $background-color-over + * The background-color of the button when the cursor is over the button + * + * @param {color} $background-color-focus + * The background-color of the button when focused + * + * @param {color} $background-color-pressed + * The background-color of the button when pressed + * + * @param {color} $background-color-disabled + * The background-color of the button when disabled + * + * @param {string/list} $background-gradient + * The background-gradient for the button. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for {@link Global_CSS#background-gradient}. + * + * @param {string} $background-gradient-over + * The background-gradient to use when the cursor is over the button. Can be either the + * name of a predefined gradient or a list of color stops. Used as the `$type` parameter + * for {@link Global_CSS#background-gradient}. + * + * @param {string} $background-gradient-focus + * The background-gradient to use when the the button is focused. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string} $background-gradient-pressed + * The background-gradient to use when the the button is pressed. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string} $background-gradient-disabled + * The background-gradient to use when the the button is disabled. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {color} $color + * The text color of the button + * + * @param {color} $color-over + * The text color of the button when the cursor is over the button + * + * @param {color} $color-focus + * The text color of the button when the button is focused + * + * @param {color} $color-pressed + * The text color of the button when the button is pressed + * + * @param {color} $color-disabled + * The text color of the button when the button is disabled + * + * @param {number} $font-size + * The font-size of the button + * + * @param {number} $font-size-over + * The font-size of the button when the cursor is over the button + * + * @param {number} $font-size-focus + * The font-size of the button when the button is focused + * + * @param {number} $font-size-pressed + * The font-size of the button when the button is pressed + * + * @param {number} $font-size-disabled + * The font-size of the button when the button is disabled + * + * @param {string} $font-weight + * The font-weight of the button + * + * @param {string} $font-weight-over + * The font-weight of the button when the cursor is over the button + * + * @param {string} $font-weight-focus + * The font-weight of the button when the button is focused + * + * @param {string} $font-weight-pressed + * The font-weight of the button when the button is pressed + * + * @param {string} $font-weight-disabled + * The font-weight of the button when the button is disabled + * + * @param {string} $font-family + * The font-family of the button + * + * @param {string} $font-family-over + * The font-family of the button when the cursor is over the button + * + * @param {string} $font-family-focus + * The font-family of the button when the button is focused + * + * @param {string} $font-family-pressed + * The font-family of the button when the button is pressed + * + * @param {string} $font-family-disabled + * The font-family of the button when the button is disabled + * + * @param {number} $icon-size + * The size of the button icon + * + * @param {color} $glyph-color + * The color of the button's {@link #glyph} icon + * + * @param {number} [$glyph-opacity=1] + * The opacity of the button's {@link #glyph} icon + * + * @param {number} $arrow-width + * The width of the button's {@link #cfg-menu} arrow + * + * @param {number} $arrow-height + * The height of the button's {@link #cfg-menu} arrow + * + * @param {number} $split-width + * The width of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {number} $split-height + * The height of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {boolean} [$include-ui-menu-arrows=$button-include-ui-menu-arrows] + * True to include the UI name in the file name of the {@link #cfg-menu} + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-ui-split-arrows=$button-include-ui-split-arrows] + * True to include the UI name in the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-split-noline-arrows=$button-include-split-noline-arrows] + * True to add a "-noline" suffix to the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Used for hiding the split line when toolbar buttons are in their default + * state. + * + * @param {boolean} [$include-split-over-arrows=$button-include-split-over-arrows] + * True to use a separate icon for {@link Ext.button.Split Split Button}s when the cursor + * is over the button. The over icon file name will have a "-o" suffix + * + * @param {number} [$opacity-disabled=1] + * The opacity of the button when it is disabled + * + * @param {number} [$inner-opacity-disabled=1] + * The opacity of the button's text and icon elements when when the button is disabled + * + * @member Ext.button.Button + */ +/** + * Creates a visual theme for a {@link #scale small} Button. + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {number} [$border-radius=$button-small-border-radius] + * The border-radius of the button + * + * @param {number} [$border-width=$button-small-border-width] + * The border-width of the button + * + * @param {color} [$border-color=$button-default-border-color] + * The border-color of the button + * + * @param {color} [$border-color-over=$button-default-border-color-over] + * The border-color of the button when the cursor is over the button + * + * @param {color} [$border-color-focus=$button-default-border-color-focus] + * The border-color of the button when focused + * + * @param {color} [$border-color-pressed=$button-default-border-color-pressed] + * The border-color of the button when pressed + * + * @param {color} [$border-color-disabled=$button-default-border-color-disabled] + * The border-color of the button when disabled + * + * @param {number} [$padding=$button-small-padding] + * The amount of padding inside the border of the button on all sides + * + * @param {number} [$text-padding=$button-small-text-padding] + * The amount of horizontal space to add to the left and right of the button text + * + * @param {color} [$background-color=$button-default-background-color] + * The background-color of the button + * + * @param {color} [$background-color-over=$button-default-background-color-over] + * The background-color of the button when the cursor is over the button + * + * @param {color} [$background-color-focus=$button-default-background-color-focus] + * The background-color of the button when focused + * + * @param {color} [$background-color-pressed=$button-default-background-color-pressed] + * The background-color of the button when pressed + * + * @param {color} [$background-color-disabled=$button-default-background-color-disabled] + * The background-color of the button when disabled + * + * @param {string/list} [$background-gradient=$button-default-background-gradient] + * The background-gradient for the button. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-over=$button-default-background-gradient-over] + * The background-gradient to use when the cursor is over the button. Can be either the + * name of a predefined gradient or a list of color stops. Used as the `$type` parameter + * for {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-focus=$button-default-background-gradient-focus] + * The background-gradient to use when the the button is focused. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-pressed=$button-default-background-gradient-pressed] + * The background-gradient to use when the the button is pressed. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-disabled=$button-default-background-gradient-disabled] + * The background-gradient to use when the the button is disabled. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {color} [$color=$button-default-color] + * The text color of the button + * + * @param {color} [$color-over=$button-default-color-over] + * The text color of the button when the cursor is over the button + * + * @param {color} [$color-focus=$button-default-color-focus] + * The text color of the button when the button is focused + * + * @param {color} [$color-pressed=$button-default-color-pressed] + * The text color of the button when the button is pressed + * + * @param {color} [$color-disabled=$button-default-color-disabled] + * The text color of the button when the button is disabled + * + * @param {number} [$font-size=$button-small-font-size] + * The font-size of the button + * + * @param {number} [$font-size-over=$button-small-font-size-over] + * The font-size of the button when the cursor is over the button + * + * @param {number} [$font-size-focus=$button-small-font-size-focus] + * The font-size of the button when the button is focused + * + * @param {number} [$font-size-pressed=$button-small-font-size-pressed] + * The font-size of the button when the button is pressed + * + * @param {number} [$font-size-disabled=$button-small-font-size-disabled] + * The font-size of the button when the button is disabled + * + * @param {string} [$font-weight=$button-small-font-weight] + * The font-weight of the button + * + * @param {string} [$font-weight-over=$button-small-font-weight-over] + * The font-weight of the button when the cursor is over the button + * + * @param {string} [$font-weight-focus=$button-small-font-weight-focus] + * The font-weight of the button when the button is focused + * + * @param {string} [$font-weight-pressed=$button-small-font-weight-pressed] + * The font-weight of the button when the button is pressed + * + * @param {string} [$font-weight-disabled=$button-small-font-weight-disabled] + * The font-weight of the button when the button is disabled + * + * @param {string} [$font-family=$button-small-font-family] + * The font-family of the button + * + * @param {string} [$font-family-over=$button-small-font-family-over] + * The font-family of the button when the cursor is over the button + * + * @param {string} [$font-family-focus=$button-small-font-family-focus] + * The font-family of the button when the button is focused + * + * @param {string} [$font-family-pressed=$button-small-font-family-pressed] + * The font-family of the button when the button is pressed + * + * @param {string} [$font-family-disabled=$button-small-font-family-disabled] + * The font-family of the button when the button is disabled + * + * @param {number} [$icon-size=$button-small-icon-size] + * The size of the button icon + * + * @param {color} [$glyph-color=$button-default-glyph-color] + * The color of the button's {@link #glyph} icon + * + * @param {number} [$glyph-opacity=$button-default-glyph-opacity] + * The opacity of the button's {@link #glyph} icon + * + * @param {number} [$arrow-width=$button-small-arrow-width] + * The width of the button's {@link #cfg-menu} arrow + * + * @param {number} [$arrow-height=$button-small-arrow-height] + * The height of the button's {@link #cfg-menu} arrow + * + * @param {number} [$split-width=$button-small-split-width] + * The width of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {number} [$split-height=$button-small-split-height] + * The height of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {boolean} [$include-ui-menu-arrows=$button-include-ui-menu-arrows] + * True to include the UI name in the file name of the {@link #cfg-menu} + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-ui-split-arrows=$button-include-ui-split-arrows] + * True to include the UI name in the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-split-noline-arrows=$button-include-split-noline-arrows] + * True to add a "-noline" suffix to the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Used for hiding the split line when toolbar buttons are in their default + * state. + * + * @param {boolean} [$include-split-over-arrows=$button-include-split-over-arrows] + * True to use a separate icon for {@link Ext.button.Split Split Button}s when the cursor + * is over the button. The over icon file name will have a "-o" suffix + * + * @param {number} [$opacity-disabled=$button-opacity-disabled] + * The opacity of the button when it is disabled + * + * @param {number} [$inner-opacity-disabled=$button-inner-opacity-disabled] + * The opacity of the button's text and icon elements when when the button is disabled + * + * @member Ext.button.Button + */ +/** + * Creates a visual theme for a {@link #scale small} toolbar Button. + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {number} [$border-radius=$button-small-border-radius] + * The border-radius of the button + * + * @param {number} [$border-width=$button-small-border-width] + * The border-width of the button + * + * @param {color} [$border-color=$button-toolbar-border-color] + * The border-color of the button + * + * @param {color} [$border-color-over=$button-toolbar-border-color-over] + * The border-color of the button when the cursor is over the button + * + * @param {color} [$border-color-focus=$button-toolbar-border-color-focus] + * The border-color of the button when focused + * + * @param {color} [$border-color-pressed=$button-toolbar-border-color-pressed] + * The border-color of the button when pressed + * + * @param {color} [$border-color-disabled=$button-toolbar-border-color-disabled] + * The border-color of the button when disabled + * + * @param {number} [$padding=$button-small-padding] + * The amount of padding inside the border of the button on all sides + * + * @param {number} [$text-padding=$button-small-text-padding] + * The amount of horizontal space to add to the left and right of the button text + * + * @param {color} [$background-color=$button-toolbar-background-color] + * The background-color of the button + * + * @param {color} [$background-color-over=$button-toolbar-background-color-over] + * The background-color of the button when the cursor is over the button + * + * @param {color} [$background-color-focus=$button-toolbar-background-color-focus] + * The background-color of the button when focused + * + * @param {color} [$background-color-pressed=$button-toolbar-background-color-pressed] + * The background-color of the button when pressed + * + * @param {color} [$background-color-disabled=$button-toolbar-background-color-disabled] + * The background-color of the button when disabled + * + * @param {string/list} [$background-gradient=$button-toolbar-background-gradient] + * The background-gradient for the button. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-over=$button-toolbar-background-gradient-over] + * The background-gradient to use when the cursor is over the button. Can be either the + * name of a predefined gradient or a list of color stops. Used as the `$type` parameter + * for {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-focus=$button-toolbar-background-gradient-focus] + * The background-gradient to use when the the button is focused. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-pressed=$button-toolbar-background-gradient-pressed] + * The background-gradient to use when the the button is pressed. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-disabled=$button-toolbar-background-gradient-disabled] + * The background-gradient to use when the the button is disabled. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {color} [$color=$button-toolbar-color] + * The text color of the button + * + * @param {color} [$color-over=$button-toolbar-color-over] + * The text color of the button when the cursor is over the button + * + * @param {color} [$color-focus=$button-toolbar-color-focus] + * The text color of the button when the button is focused + * + * @param {color} [$color-pressed=$button-toolbar-color-pressed] + * The text color of the button when the button is pressed + * + * @param {color} [$color-disabled=$button-toolbar-color-disabled] + * The text color of the button when the button is disabled + * + * @param {number} [$font-size=$button-small-font-size] + * The font-size of the button + * + * @param {number} [$font-size-over=$button-small-font-size-over] + * The font-size of the button when the cursor is over the button + * + * @param {number} [$font-size-focus=$button-small-font-size-focus] + * The font-size of the button when the button is focused + * + * @param {number} [$font-size-pressed=$button-small-font-size-pressed] + * The font-size of the button when the button is pressed + * + * @param {number} [$font-size-disabled=$button-small-font-size-disabled] + * The font-size of the button when the button is disabled + * + * @param {string} [$font-weight=$button-small-font-weight] + * The font-weight of the button + * + * @param {string} [$font-weight-over=$button-small-font-weight-over] + * The font-weight of the button when the cursor is over the button + * + * @param {string} [$font-weight-focus=$button-small-font-weight-focus] + * The font-weight of the button when the button is focused + * + * @param {string} [$font-weight-pressed=$button-small-font-weight-pressed] + * The font-weight of the button when the button is pressed + * + * @param {string} [$font-weight-disabled=$button-small-font-weight-disabled] + * The font-weight of the button when the button is disabled + * + * @param {string} [$font-family=$button-small-font-family] + * The font-family of the button + * + * @param {string} [$font-family-over=$button-small-font-family-over] + * The font-family of the button when the cursor is over the button + * + * @param {string} [$font-family-focus=$button-small-font-family-focus] + * The font-family of the button when the button is focused + * + * @param {string} [$font-family-pressed=$button-small-font-family-pressed] + * The font-family of the button when the button is pressed + * + * @param {string} [$font-family-disabled=$button-small-font-family-disabled] + * The font-family of the button when the button is disabled + * + * @param {number} [$icon-size=$button-small-icon-size] + * The size of the button icon + * + * @param {color} [$glyph-color=$button-toolbar-glyph-color] + * The color of the button's {@link #glyph} icon + * + * @param {number} [$glyph-opacity=$button-toolbar-glyph-opacity] + * The opacity of the button's {@link #glyph} icon + * + * @param {number} [$arrow-width=$button-small-arrow-width] + * The width of the button's {@link #cfg-menu} arrow + * + * @param {number} [$arrow-height=$button-small-arrow-height] + * The height of the button's {@link #cfg-menu} arrow + * + * @param {number} [$split-width=$button-small-split-width] + * The width of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {number} [$split-height=$button-small-split-height] + * The height of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {boolean} [$include-ui-menu-arrows=$button-include-ui-menu-arrows] + * True to include the UI name in the file name of the {@link #cfg-menu} + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-ui-split-arrows=$button-include-ui-split-arrows] + * True to include the UI name in the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-split-noline-arrows=$button-toolbar-include-split-noline-arrows] + * True to add a "-noline" suffix to the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Used for hiding the split line when toolbar buttons are in their default + * state. + * + * @param {boolean} [$include-split-over-arrows=$button-include-split-over-arrows] + * True to use a separate icon for {@link Ext.button.Split Split Button}s when the cursor + * is over the button. The over icon file name will have a "-o" suffix + * + * @param {number} [$opacity-disabled=$button-toolbar-opacity-disabled] + * The opacity of the button when it is disabled + * + * @param {number} [$inner-opacity-disabled=$button-toolbar-inner-opacity-disabled] + * The opacity of the button's text and icon elements when when the button is disabled + * + * @member Ext.button.Button + */ +/** + * Creates a visual theme for a {@link #scale medium} Button. + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {number} [$border-radius=$button-medium-border-radius] + * The border-radius of the button + * + * @param {number} [$border-width=$button-medium-border-width] + * The border-width of the button + * + * @param {color} [$border-color=$button-default-border-color] + * The border-color of the button + * + * @param {color} [$border-color-over=$button-default-border-color-over] + * The border-color of the button when the cursor is over the button + * + * @param {color} [$border-color-focus=$button-default-border-color-focus] + * The border-color of the button when focused + * + * @param {color} [$border-color-pressed=$button-default-border-color-pressed] + * The border-color of the button when pressed + * + * @param {color} [$border-color-disabled=$button-default-border-color-disabled] + * The border-color of the button when disabled + * + * @param {number} [$padding=$button-medium-padding] + * The amount of padding inside the border of the button on all sides + * + * @param {number} [$text-padding=$button-medium-text-padding] + * The amount of horizontal space to add to the left and right of the button text + * + * @param {color} [$background-color=$button-default-background-color] + * The background-color of the button + * + * @param {color} [$background-color-over=$button-default-background-color-over] + * The background-color of the button when the cursor is over the button + * + * @param {color} [$background-color-focus=$button-default-background-color-focus] + * The background-color of the button when focused + * + * @param {color} [$background-color-pressed=$button-default-background-color-pressed] + * The background-color of the button when pressed + * + * @param {color} [$background-color-disabled=$button-default-background-color-disabled] + * The background-color of the button when disabled + * + * @param {string/list} [$background-gradient=$button-default-background-gradient] + * The background-gradient for the button. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-over=$button-default-background-gradient-over] + * The background-gradient to use when the cursor is over the button. Can be either the + * name of a predefined gradient or a list of color stops. Used as the `$type` parameter + * for {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-focus=$button-default-background-gradient-focus] + * The background-gradient to use when the the button is focused. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-pressed=$button-default-background-gradient-pressed] + * The background-gradient to use when the the button is pressed. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-disabled=$button-default-background-gradient-disabled] + * The background-gradient to use when the the button is disabled. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {color} [$color=$button-default-color] + * The text color of the button + * + * @param {color} [$color-over=$button-default-color-over] + * The text color of the button when the cursor is over the button + * + * @param {color} [$color-focus=$button-default-color-focus] + * The text color of the button when the button is focused + * + * @param {color} [$color-pressed=$button-default-color-pressed] + * The text color of the button when the button is pressed + * + * @param {color} [$color-disabled=$button-default-color-disabled] + * The text color of the button when the button is disabled + * + * @param {number} [$font-size=$button-medium-font-size] + * The font-size of the button + * + * @param {number} [$font-size-over=$button-medium-font-size-over] + * The font-size of the button when the cursor is over the button + * + * @param {number} [$font-size-focus=$button-medium-font-size-focus] + * The font-size of the button when the button is focused + * + * @param {number} [$font-size-pressed=$button-medium-font-size-pressed] + * The font-size of the button when the button is pressed + * + * @param {number} [$font-size-disabled=$button-medium-font-size-disabled] + * The font-size of the button when the button is disabled + * + * @param {string} [$font-weight=$button-medium-font-weight] + * The font-weight of the button + * + * @param {string} [$font-weight-over=$button-medium-font-weight-over] + * The font-weight of the button when the cursor is over the button + * + * @param {string} [$font-weight-focus=$button-medium-font-weight-focus] + * The font-weight of the button when the button is focused + * + * @param {string} [$font-weight-pressed=$button-medium-font-weight-pressed] + * The font-weight of the button when the button is pressed + * + * @param {string} [$font-weight-disabled=$button-medium-font-weight-disabled] + * The font-weight of the button when the button is disabled + * + * @param {string} [$font-family=$button-medium-font-family] + * The font-family of the button + * + * @param {string} [$font-family-over=$button-medium-font-family-over] + * The font-family of the button when the cursor is over the button + * + * @param {string} [$font-family-focus=$button-medium-font-family-focus] + * The font-family of the button when the button is focused + * + * @param {string} [$font-family-pressed=$button-medium-font-family-pressed] + * The font-family of the button when the button is pressed + * + * @param {string} [$font-family-disabled=$button-medium-font-family-disabled] + * The font-family of the button when the button is disabled + * + * @param {number} [$icon-size=$button-medium-icon-size] + * The size of the button icon + * + * @param {color} [$glyph-color=$button-default-glyph-color] + * The color of the button's {@link #glyph} icon + * + * @param {number} [$glyph-opacity=$button-default-glyph-opacity] + * The opacity of the button's {@link #glyph} icon + * + * @param {number} [$arrow-width=$button-medium-arrow-width] + * The width of the button's {@link #cfg-menu} arrow + * + * @param {number} [$arrow-height=$button-medium-arrow-height] + * The height of the button's {@link #cfg-menu} arrow + * + * @param {number} [$split-width=$button-medium-split-width] + * The width of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {number} [$split-height=$button-medium-split-height] + * The height of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {boolean} [$include-ui-menu-arrows=$button-include-ui-menu-arrows] + * True to include the UI name in the file name of the {@link #cfg-menu} + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-ui-split-arrows=$button-include-ui-split-arrows] + * True to include the UI name in the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-split-noline-arrows=$button-include-split-noline-arrows] + * True to add a "-noline" suffix to the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Used for hiding the split line when toolbar buttons are in their default + * state. + * + * @param {boolean} [$include-split-over-arrows=$button-include-split-over-arrows] + * True to use a separate icon for {@link Ext.button.Split Split Button}s when the cursor + * is over the button. The over icon file name will have a "-o" suffix + * + * @param {number} [$opacity-disabled=$button-opacity-disabled] + * The opacity of the button when it is disabled + * + * @param {number} [$inner-opacity-disabled=$button-inner-opacity-disabled] + * The opacity of the button's text and icon elements when when the button is disabled + * + * @member Ext.button.Button + */ +/** + * Creates a visual theme for a {@link #scale medium} toolbar Button. + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {number} [$border-radius=$button-medium-border-radius] + * The border-radius of the button + * + * @param {number} [$border-width=$button-medium-border-width] + * The border-width of the button + * + * @param {color} [$border-color=$button-toolbar-border-color] + * The border-color of the button + * + * @param {color} [$border-color-over=$button-toolbar-border-color-over] + * The border-color of the button when the cursor is over the button + * + * @param {color} [$border-color-focus=$button-toolbar-border-color-focus] + * The border-color of the button when focused + * + * @param {color} [$border-color-pressed=$button-toolbar-border-color-pressed] + * The border-color of the button when pressed + * + * @param {color} [$border-color-disabled=$button-toolbar-border-color-disabled] + * The border-color of the button when disabled + * + * @param {number} [$padding=$button-medium-padding] + * The amount of padding inside the border of the button on all sides + * + * @param {number} [$text-padding=$button-medium-text-padding] + * The amount of horizontal space to add to the left and right of the button text + * + * @param {color} [$background-color=$button-toolbar-background-color] + * The background-color of the button + * + * @param {color} [$background-color-over=$button-toolbar-background-color-over] + * The background-color of the button when the cursor is over the button + * + * @param {color} [$background-color-focus=$button-toolbar-background-color-focus] + * The background-color of the button when focused + * + * @param {color} [$background-color-pressed=$button-toolbar-background-color-pressed] + * The background-color of the button when pressed + * + * @param {color} [$background-color-disabled=$button-toolbar-background-color-disabled] + * The background-color of the button when disabled + * + * @param {string/list} [$background-gradient=$button-toolbar-background-gradient] + * The background-gradient for the button. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-over=$button-toolbar-background-gradient-over] + * The background-gradient to use when the cursor is over the button. Can be either the + * name of a predefined gradient or a list of color stops. Used as the `$type` parameter + * for {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-focus=$button-toolbar-background-gradient-focus] + * The background-gradient to use when the the button is focused. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-pressed=$button-toolbar-background-gradient-pressed] + * The background-gradient to use when the the button is pressed. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-disabled=$button-toolbar-background-gradient-disabled] + * The background-gradient to use when the the button is disabled. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {color} [$color=$button-toolbar-color] + * The text color of the button + * + * @param {color} [$color-over=$button-toolbar-color-over] + * The text color of the button when the cursor is over the button + * + * @param {color} [$color-focus=$button-toolbar-color-focus] + * The text color of the button when the button is focused + * + * @param {color} [$color-pressed=$button-toolbar-color-pressed] + * The text color of the button when the button is pressed + * + * @param {color} [$color-disabled=$button-toolbar-color-disabled] + * The text color of the button when the button is disabled + * + * @param {number} [$font-size=$button-medium-font-size] + * The font-size of the button + * + * @param {number} [$font-size-over=$button-medium-font-size-over] + * The font-size of the button when the cursor is over the button + * + * @param {number} [$font-size-focus=$button-medium-font-size-focus] + * The font-size of the button when the button is focused + * + * @param {number} [$font-size-pressed=$button-medium-font-size-pressed] + * The font-size of the button when the button is pressed + * + * @param {number} [$font-size-disabled=$button-medium-font-size-disabled] + * The font-size of the button when the button is disabled + * + * @param {string} [$font-weight=$button-medium-font-weight] + * The font-weight of the button + * + * @param {string} [$font-weight-over=$button-medium-font-weight-over] + * The font-weight of the button when the cursor is over the button + * + * @param {string} [$font-weight-focus=$button-medium-font-weight-focus] + * The font-weight of the button when the button is focused + * + * @param {string} [$font-weight-pressed=$button-medium-font-weight-pressed] + * The font-weight of the button when the button is pressed + * + * @param {string} [$font-weight-disabled=$button-medium-font-weight-disabled] + * The font-weight of the button when the button is disabled + * + * @param {string} [$font-family=$button-medium-font-family] + * The font-family of the button + * + * @param {string} [$font-family-over=$button-medium-font-family-over] + * The font-family of the button when the cursor is over the button + * + * @param {string} [$font-family-focus=$button-medium-font-family-focus] + * The font-family of the button when the button is focused + * + * @param {string} [$font-family-pressed=$button-medium-font-family-pressed] + * The font-family of the button when the button is pressed + * + * @param {string} [$font-family-disabled=$button-medium-font-family-disabled] + * The font-family of the button when the button is disabled + * + * @param {number} [$icon-size=$button-medium-icon-size] + * The size of the button icon + * + * @param {color} [$glyph-color=$button-toolbar-glyph-color] + * The color of the button's {@link #glyph} icon + * + * @param {number} [$glyph-opacity=$button-toolbar-glyph-opacity] + * The opacity of the button's {@link #glyph} icon + * + * @param {number} [$arrow-width=$button-medium-arrow-width] + * The width of the button's {@link #cfg-menu} arrow + * + * @param {number} [$arrow-height=$button-medium-arrow-height] + * The height of the button's {@link #cfg-menu} arrow + * + * @param {number} [$split-width=$button-medium-split-width] + * The width of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {number} [$split-height=$button-medium-split-height] + * The height of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {boolean} [$include-ui-menu-arrows=$button-include-ui-menu-arrows] + * True to include the UI name in the file name of the {@link #cfg-menu} + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-ui-split-arrows=$button-include-ui-split-arrows] + * True to include the UI name in the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-split-noline-arrows=$button-toolbar-include-split-noline-arrows] + * True to add a "-noline" suffix to the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Used for hiding the split line when toolbar buttons are in their default + * state. + * + * @param {boolean} [$include-split-over-arrows=$button-include-split-over-arrows] + * True to use a separate icon for {@link Ext.button.Split Split Button}s when the cursor + * is over the button. The over icon file name will have a "-o" suffix + * + * @param {number} [$opacity-disabled=$button-toolbar-opacity-disabled] + * The opacity of the button when it is disabled + * + * @param {number} [$inner-opacity-disabled=$button-toolbar-inner-opacity-disabled] + * The opacity of the button's text and icon elements when when the button is disabled + * + * @member Ext.button.Button + */ +/** + * Creates a visual theme for a {@link #scale large} Button. + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {number} [$border-radius=$button-large-border-radius] + * The border-radius of the button + * + * @param {number} [$border-width=$button-large-border-width] + * The border-width of the button + * + * @param {color} [$border-color=$button-default-border-color] + * The border-color of the button + * + * @param {color} [$border-color-over=$button-default-border-color-over] + * The border-color of the button when the cursor is over the button + * + * @param {color} [$border-color-focus=$button-default-border-color-focus] + * The border-color of the button when focused + * + * @param {color} [$border-color-pressed=$button-default-border-color-pressed] + * The border-color of the button when pressed + * + * @param {color} [$border-color-disabled=$button-default-border-color-disabled] + * The border-color of the button when disabled + * + * @param {number} [$padding=$button-large-padding] + * The amount of padding inside the border of the button on all sides + * + * @param {number} [$text-padding=$button-large-text-padding] + * The amount of horizontal space to add to the left and right of the button text + * + * @param {color} [$background-color=$button-default-background-color] + * The background-color of the button + * + * @param {color} [$background-color-over=$button-default-background-color-over] + * The background-color of the button when the cursor is over the button + * + * @param {color} [$background-color-focus=$button-default-background-color-focus] + * The background-color of the button when focused + * + * @param {color} [$background-color-pressed=$button-default-background-color-pressed] + * The background-color of the button when pressed + * + * @param {color} [$background-color-disabled=$button-default-background-color-disabled] + * The background-color of the button when disabled + * + * @param {string/list} [$background-gradient=$button-default-background-gradient] + * The background-gradient for the button. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-over=$button-default-background-gradient-over] + * The background-gradient to use when the cursor is over the button. Can be either the + * name of a predefined gradient or a list of color stops. Used as the `$type` parameter + * for {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-focus=$button-default-background-gradient-focus] + * The background-gradient to use when the the button is focused. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-pressed=$button-default-background-gradient-pressed] + * The background-gradient to use when the the button is pressed. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-disabled=$button-default-background-gradient-disabled] + * The background-gradient to use when the the button is disabled. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {color} [$color=$button-default-color] + * The text color of the button + * + * @param {color} [$color-over=$button-default-color-over] + * The text color of the button when the cursor is over the button + * + * @param {color} [$color-focus=$button-default-color-focus] + * The text color of the button when the button is focused + * + * @param {color} [$color-pressed=$button-default-color-pressed] + * The text color of the button when the button is pressed + * + * @param {color} [$color-disabled=$button-default-color-disabled] + * The text color of the button when the button is disabled + * + * @param {number} [$font-size=$button-large-font-size] + * The font-size of the button + * + * @param {number} [$font-size-over=$button-large-font-size-over] + * The font-size of the button when the cursor is over the button + * + * @param {number} [$font-size-focus=$button-large-font-size-focus] + * The font-size of the button when the button is focused + * + * @param {number} [$font-size-pressed=$button-large-font-size-pressed] + * The font-size of the button when the button is pressed + * + * @param {number} [$font-size-disabled=$button-large-font-size-disabled] + * The font-size of the button when the button is disabled + * + * @param {string} [$font-weight=$button-large-font-weight] + * The font-weight of the button + * + * @param {string} [$font-weight-over=$button-large-font-weight-over] + * The font-weight of the button when the cursor is over the button + * + * @param {string} [$font-weight-focus=$button-large-font-weight-focus] + * The font-weight of the button when the button is focused + * + * @param {string} [$font-weight-pressed=$button-large-font-weight-pressed] + * The font-weight of the button when the button is pressed + * + * @param {string} [$font-weight-disabled=$button-large-font-weight-disabled] + * The font-weight of the button when the button is disabled + * + * @param {string} [$font-family=$button-large-font-family] + * The font-family of the button + * + * @param {string} [$font-family-over=$button-large-font-family-over] + * The font-family of the button when the cursor is over the button + * + * @param {string} [$font-family-focus=$button-large-font-family-focus] + * The font-family of the button when the button is focused + * + * @param {string} [$font-family-pressed=$button-large-font-family-pressed] + * The font-family of the button when the button is pressed + * + * @param {string} [$font-family-disabled=$button-large-font-family-disabled] + * The font-family of the button when the button is disabled + * + * @param {number} [$icon-size=$button-large-icon-size] + * The size of the button icon + * + * @param {color} [$glyph-color=$button-default-glyph-color] + * The color of the button's {@link #glyph} icon + * + * @param {number} [$glyph-opacity=$button-default-glyph-opacity] + * The opacity of the button's {@link #glyph} icon + * + * @param {number} [$arrow-width=$button-large-arrow-width] + * The width of the button's {@link #cfg-menu} arrow + * + * @param {number} [$arrow-height=$button-large-arrow-height] + * The height of the button's {@link #cfg-menu} arrow + * + * @param {number} [$split-width=$button-large-split-width] + * The width of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {number} [$split-height=$button-large-split-height] + * The height of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {boolean} [$include-ui-menu-arrows=$button-include-ui-menu-arrows] + * True to include the UI name in the file name of the {@link #cfg-menu} + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-ui-split-arrows=$button-include-ui-split-arrows] + * True to include the UI name in the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-split-noline-arrows=$button-include-split-noline-arrows] + * True to add a "-noline" suffix to the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Used for hiding the split line when toolbar buttons are in their default + * state. + * + * @param {boolean} [$include-split-over-arrows=$button-include-split-over-arrows] + * True to use a separate icon for {@link Ext.button.Split Split Button}s when the cursor + * is over the button. The over icon file name will have a "-o" suffix + * + * @param {number} [$opacity-disabled=$button-opacity-disabled] + * The opacity of the button when it is disabled + * + * @param {number} [$inner-opacity-disabled=$button-inner-opacity-disabled] + * The opacity of the button's text and icon elements when when the button is disabled + * + * @member Ext.button.Button + */ +/** + * Creates a visual theme for a {@link #scale large} toolbar Button. + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {number} [$border-radius=$button-large-border-radius] + * The border-radius of the button + * + * @param {number} [$border-width=$button-large-border-width] + * The border-width of the button + * + * @param {color} [$border-color=$button-toolbar-border-color] + * The border-color of the button + * + * @param {color} [$border-color-over=$button-toolbar-border-color-over] + * The border-color of the button when the cursor is over the button + * + * @param {color} [$border-color-focus=$button-toolbar-border-color-focus] + * The border-color of the button when focused + * + * @param {color} [$border-color-pressed=$button-toolbar-border-color-pressed] + * The border-color of the button when pressed + * + * @param {color} [$border-color-disabled=$button-toolbar-border-color-disabled] + * The border-color of the button when disabled + * + * @param {number} [$padding=$button-large-padding] + * The amount of padding inside the border of the button on all sides + * + * @param {number} [$text-padding=$button-large-text-padding] + * The amount of horizontal space to add to the left and right of the button text + * + * @param {color} [$background-color=$button-toolbar-background-color] + * The background-color of the button + * + * @param {color} [$background-color-over=$button-toolbar-background-color-over] + * The background-color of the button when the cursor is over the button + * + * @param {color} [$background-color-focus=$button-toolbar-background-color-focus] + * The background-color of the button when focused + * + * @param {color} [$background-color-pressed=$button-toolbar-background-color-pressed] + * The background-color of the button when pressed + * + * @param {color} [$background-color-disabled=$button-toolbar-background-color-disabled] + * The background-color of the button when disabled + * + * @param {string/list} [$background-gradient=$button-toolbar-background-gradient] + * The background-gradient for the button. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-over=$button-toolbar-background-gradient-over] + * The background-gradient to use when the cursor is over the button. Can be either the + * name of a predefined gradient or a list of color stops. Used as the `$type` parameter + * for {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-focus=$button-toolbar-background-gradient-focus] + * The background-gradient to use when the the button is focused. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-pressed=$button-toolbar-background-gradient-pressed] + * The background-gradient to use when the the button is pressed. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$background-gradient-disabled=$button-toolbar-background-gradient-disabled] + * The background-gradient to use when the the button is disabled. Can be either the name + * of a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {color} [$color=$button-toolbar-color] + * The text color of the button + * + * @param {color} [$color-over=$button-toolbar-color-over] + * The text color of the button when the cursor is over the button + * + * @param {color} [$color-focus=$button-toolbar-color-focus] + * The text color of the button when the button is focused + * + * @param {color} [$color-pressed=$button-toolbar-color-pressed] + * The text color of the button when the button is pressed + * + * @param {color} [$color-disabled=$button-toolbar-color-disabled] + * The text color of the button when the button is disabled + * + * @param {number} [$font-size=$button-large-font-size] + * The font-size of the button + * + * @param {number} [$font-size-over=$button-large-font-size-over] + * The font-size of the button when the cursor is over the button + * + * @param {number} [$font-size-focus=$button-large-font-size-focus] + * The font-size of the button when the button is focused + * + * @param {number} [$font-size-pressed=$button-large-font-size-pressed] + * The font-size of the button when the button is pressed + * + * @param {number} [$font-size-disabled=$button-large-font-size-disabled] + * The font-size of the button when the button is disabled + * + * @param {string} [$font-weight=$button-large-font-weight] + * The font-weight of the button + * + * @param {string} [$font-weight-over=$button-large-font-weight-over] + * The font-weight of the button when the cursor is over the button + * + * @param {string} [$font-weight-focus=$button-large-font-weight-focus] + * The font-weight of the button when the button is focused + * + * @param {string} [$font-weight-pressed=$button-large-font-weight-pressed] + * The font-weight of the button when the button is pressed + * + * @param {string} [$font-weight-disabled=$button-large-font-weight-disabled] + * The font-weight of the button when the button is disabled + * + * @param {string} [$font-family=$button-large-font-family] + * The font-family of the button + * + * @param {string} [$font-family-over=$button-large-font-family-over] + * The font-family of the button when the cursor is over the button + * + * @param {string} [$font-family-focus=$button-large-font-family-focus] + * The font-family of the button when the button is focused + * + * @param {string} [$font-family-pressed=$button-large-font-family-pressed] + * The font-family of the button when the button is pressed + * + * @param {string} [$font-family-disabled=$button-large-font-family-disabled] + * The font-family of the button when the button is disabled + * + * @param {number} [$icon-size=$button-large-icon-size] + * The size of the button icon + * + * @param {color} [$glyph-color=$button-toolbar-glyph-color] + * The color of the button's {@link #glyph} icon + * + * @param {number} [$glyph-opacity=$button-toolbar-glyph-opacity] + * The opacity of the button's {@link #glyph} icon + * + * @param {number} [$arrow-width=$button-large-arrow-width] + * The width of the button's {@link #cfg-menu} arrow + * + * @param {number} [$arrow-height=$button-large-arrow-height] + * The height of the button's {@link #cfg-menu} arrow + * + * @param {number} [$split-width=$button-large-split-width] + * The width of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {number} [$split-height=$button-large-split-height] + * The height of a {@link Ext.button.Split Split Button}'s arrow + * + * @param {boolean} [$include-ui-menu-arrows=$button-include-ui-menu-arrows] + * True to include the UI name in the file name of the {@link #cfg-menu} + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-ui-split-arrows=$button-include-ui-split-arrows] + * True to include the UI name in the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Set this to false to share the same arrow bewteen multiple UIs. + * + * @param {boolean} [$include-split-noline-arrows=$button-toolbar-include-split-noline-arrows] + * True to add a "-noline" suffix to the file name of the {@link Ext.button.Split Split Button}'s + * arrow icon. Used for hiding the split line when toolbar buttons are in their default + * state. + * + * @param {boolean} [$include-split-over-arrows=$button-include-split-over-arrows] + * True to use a separate icon for {@link Ext.button.Split Split Button}s when the cursor + * is over the button. The over icon file name will have a "-o" suffix + * + * @param {number} [$opacity-disabled=$button-toolbar-opacity-disabled] + * The opacity of the button when it is disabled + * + * @param {number} [$inner-opacity-disabled=$button-toolbar-inner-opacity-disabled] + * The opacity of the button's text and icon elements when when the button is disabled + * + * @member Ext.button.Button + */ +.x-btn-default-small { + border-color: #333333; +} + +.x-btn-default-small { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: #606060; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #6f6f6f), color-stop(50%, #606060), color-stop(51%, #5b5b5b), color-stop(100%, #606060)); + background-image: -webkit-linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); + background-image: -moz-linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); + background-image: -o-linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); + background-image: -ms-linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); + background-image: linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); +} + +.x-btn-default-small-mc { + background-image: url(images/btn/btn-default-small-fbg.gif); + background-position: 0 top; + background-color: #606060; +} + +.x-nlg .x-btn-default-small { + background-image: url(images/btn/btn-default-small-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-default-small { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-default-small-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-default-small-tl { + background-position: 0 -6px; +} + +.x-btn-default-small-tr { + background-position: right -9px; +} + +.x-btn-default-small-bl { + background-position: 0 -12px; +} + +.x-btn-default-small-br { + background-position: right -15px; +} + +.x-btn-default-small-ml { + background-position: 0 top; +} + +.x-btn-default-small-mr { + background-position: right top; +} + +.x-btn-default-small-tc { + background-position: 0 0; +} + +.x-btn-default-small-bc { + background-position: 0 -3px; +} + +.x-btn-default-small-tr, +.x-btn-default-small-br, +.x-btn-default-small-mr { + padding-right: 3px; +} + +.x-btn-default-small-tl, +.x-btn-default-small-bl, +.x-btn-default-small-ml { + padding-left: 3px; +} + +.x-btn-default-small-tc { + height: 3px; +} + +.x-btn-default-small-bc { + height: 3px; +} + +.x-btn-default-small-tl, +.x-btn-default-small-bl, +.x-btn-default-small-tr, +.x-btn-default-small-br, +.x-btn-default-small-tc, +.x-btn-default-small-bc, +.x-btn-default-small-ml, +.x-btn-default-small-mr { + zoom: 1; + background-image: url(images/btn/btn-default-small-corners.gif); +} + +.x-btn-default-small-ml, +.x-btn-default-small-mr { + zoom: 1; + background-image: url(images/btn/btn-default-small-sides.gif); +} + +.x-btn-default-small-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-default-small-tl, +.x-strict .x-ie7 .x-btn-default-small-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-default-small:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-small-fbg.gif), bg:url(images/btn/btn-default-small-bg.gif), corners:url(images/btn/btn-default-small-corners.gif), sides:url(images/btn/btn-default-small-sides.gif)"; +} + +/**/ +/* */ +.x-btn-default-small .x-btn-inner { + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: white; + padding: 0 5px; +} +.x-btn-default-small .x-btn-arrow { + background-image: url(images/button/default-small-arrow.png); +} +.x-btn-default-small .x-btn-arrow-right { + padding-right: 21px; +} +.x-btn-default-small .x-btn-arrow-bottom { + padding-bottom: 18px; +} +.x-btn-default-small .x-btn-glyph { + font-size: 16px; + line-height: 16px; + color: white; +} +.x-ie8m .x-btn-default-small .x-btn-glyph { + color: white; +} + +.x-btn-default-small-disabled { + background-image: none; + background-color: #444444; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #565656), color-stop(50%, #444444), color-stop(51%, #404040), color-stop(100%, #444444)); + background-image: -webkit-linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); + background-image: -moz-linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); + background-image: -o-linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); + background-image: -ms-linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); + background-image: linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); +} + +.x-btn-default-small-icon .x-btn-button, +.x-btn-default-small-noicon .x-btn-button { + height: 16px; +} +.x-btn-default-small-icon .x-btn-inner, +.x-btn-default-small-noicon .x-btn-inner { + line-height: 16px; +} + +.x-btn-default-small-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-small-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-small-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-default-small-icon .x-btn-inner { + width: 16px; + padding: 0; +} +.x-btn-default-small-icon .x-btn-icon-el { + width: 16px; + height: 16px; +} + +.x-btn-default-small-icon-text-left .x-btn-button { + height: 16px; +} +.x-btn-default-small-icon-text-left .x-btn-inner { + line-height: 16px; + padding-left: 21px; +} +.x-btn-default-small-icon-text-left .x-btn-icon-el { + width: 16px; + right: auto; +} +.x-ie6 .x-btn-default-small-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-small-icon-text-left .x-btn-icon-el { + height: 16px; +} + +.x-btn-default-small-icon-text-right .x-btn-button { + height: 16px; +} +.x-btn-default-small-icon-text-right .x-btn-inner { + line-height: 16px; + padding-right: 21px; +} +.x-btn-default-small-icon-text-right .x-btn-icon-el { + width: 16px; + left: auto; +} +.x-ie6 .x-btn-default-small-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-small-icon-text-right .x-btn-icon-el { + height: 16px; +} + +.x-btn-default-small-icon-text-top .x-btn-inner { + padding-top: 21px; +} +.x-btn-default-small-icon-text-top .x-btn-icon-el { + height: 16px; + bottom: auto; +} +.x-ie6 .x-btn-default-small-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-small-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-small-icon-text-bottom .x-btn-inner { + padding-bottom: 21px; +} +.x-btn-default-small-icon-text-bottom .x-btn-icon-el { + height: 16px; + top: auto; +} +.x-ie6 .x-btn-default-small-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-small-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-small-focus { + border-color: #96caee; + background-image: none; + background-color: #3e3e3e; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #515151), color-stop(50%, #3e3e3e), color-stop(51%, #3a3a3a), color-stop(100%, #3e3e3e)); + background-image: -webkit-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -moz-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -o-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -ms-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); +} + +.x-btn-default-small-over { + background-image: none; + background-color: #3e3e3e; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #515151), color-stop(50%, #3e3e3e), color-stop(51%, #3a3a3a), color-stop(100%, #3e3e3e)); + background-image: -webkit-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -moz-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -o-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -ms-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); +} + +.x-btn-default-small-menu-active, +.x-btn-default-small-pressed { + background-image: none; + background-color: #333333; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #333333), color-stop(50%, #303030), color-stop(51%, #333333), color-stop(100%, #474747)); + background-image: -webkit-linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); + background-image: -moz-linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); + background-image: -o-linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); + background-image: -ms-linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); + background-image: linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); +} + +.x-btn-default-small-focus .x-frame-tl, +.x-btn-default-small-focus .x-frame-bl, +.x-btn-default-small-focus .x-frame-tr, +.x-btn-default-small-focus .x-frame-br, +.x-btn-default-small-focus .x-frame-tc, +.x-btn-default-small-focus .x-frame-bc { + background-image: url(images/btn/btn-default-small-focus-corners.gif); +} +.x-btn-default-small-focus .x-frame-ml, +.x-btn-default-small-focus .x-frame-mr { + background-image: url(images/btn/btn-default-small-focus-sides.gif); +} +.x-btn-default-small-focus .x-frame-mc { + background-color: #3e3e3e; + background-image: url(images/btn/btn-default-small-focus-fbg.gif); +} + +.x-btn-default-small-over .x-frame-tl, +.x-btn-default-small-over .x-frame-bl, +.x-btn-default-small-over .x-frame-tr, +.x-btn-default-small-over .x-frame-br, +.x-btn-default-small-over .x-frame-tc, +.x-btn-default-small-over .x-frame-bc { + background-image: url(images/btn/btn-default-small-over-corners.gif); +} +.x-btn-default-small-over .x-frame-ml, +.x-btn-default-small-over .x-frame-mr { + background-image: url(images/btn/btn-default-small-over-sides.gif); +} +.x-btn-default-small-over .x-frame-mc { + background-color: #3e3e3e; + background-image: url(images/btn/btn-default-small-over-fbg.gif); +} + +.x-btn-default-small-menu-active .x-frame-tl, +.x-btn-default-small-menu-active .x-frame-bl, +.x-btn-default-small-menu-active .x-frame-tr, +.x-btn-default-small-menu-active .x-frame-br, +.x-btn-default-small-menu-active .x-frame-tc, +.x-btn-default-small-menu-active .x-frame-bc, +.x-btn-default-small-pressed .x-frame-tl, +.x-btn-default-small-pressed .x-frame-bl, +.x-btn-default-small-pressed .x-frame-tr, +.x-btn-default-small-pressed .x-frame-br, +.x-btn-default-small-pressed .x-frame-tc, +.x-btn-default-small-pressed .x-frame-bc { + background-image: url(images/btn/btn-default-small-pressed-corners.gif); +} +.x-btn-default-small-menu-active .x-frame-ml, +.x-btn-default-small-menu-active .x-frame-mr, +.x-btn-default-small-pressed .x-frame-ml, +.x-btn-default-small-pressed .x-frame-mr { + background-image: url(images/btn/btn-default-small-pressed-sides.gif); +} +.x-btn-default-small-menu-active .x-frame-mc, +.x-btn-default-small-pressed .x-frame-mc { + background-color: #333333; + background-image: url(images/btn/btn-default-small-pressed-fbg.gif); +} + +.x-btn-default-small-disabled .x-frame-tl, +.x-btn-default-small-disabled .x-frame-bl, +.x-btn-default-small-disabled .x-frame-tr, +.x-btn-default-small-disabled .x-frame-br, +.x-btn-default-small-disabled .x-frame-tc, +.x-btn-default-small-disabled .x-frame-bc { + background-image: url(images/btn/btn-default-small-disabled-corners.gif); +} +.x-btn-default-small-disabled .x-frame-ml, +.x-btn-default-small-disabled .x-frame-mr { + background-image: url(images/btn/btn-default-small-disabled-sides.gif); +} +.x-btn-default-small-disabled .x-frame-mc { + background-color: #444444; + background-image: url(images/btn/btn-default-small-disabled-fbg.gif); +} + +.x-nlg .x-btn-default-small-focus { + background-image: url(images/btn/btn-default-small-focus-bg.gif); +} + +.x-nlg .x-btn-default-small-over { + background-image: url(images/btn/btn-default-small-over-bg.gif); +} + +.x-nlg .x-btn-default-small-menu-active, +.x-nlg .x-btn-default-small-pressed { + background-image: url(images/btn/btn-default-small-pressed-bg.gif); +} + +.x-nlg .x-btn-default-small-disabled { + background-image: url(images/btn/btn-default-small-disabled-bg.gif); +} + +.x-nbr .x-btn-default-small { + background-image: none; +} + +.x-btn-default-small .x-btn-split-right { + background-image: url(images/button/default-small-s-arrow.png); + padding-right: 23px; +} +.x-btn-default-small .x-btn-split-bottom { + background-image: url(images/button/default-small-s-arrow-b.png); + padding-bottom: 20px; +} + +.x-btn-default-small-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-default-small-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-focus-corners.gif), sides:url(images/btn/btn-default-small-focus-sides.gif), frame-bg:url(images/btn/btn-default-small-focus-fbg.gif), bg:url(images/btn/btn-default-small-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-small-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-over-corners.gif), sides:url(images/btn/btn-default-small-over-sides.gif), frame-bg:url(images/btn/btn-default-small-over-fbg.gif), bg:url(images/btn/btn-default-small-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-small-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-pressed-corners.gif), sides:url(images/btn/btn-default-small-pressed-sides.gif), frame-bg:url(images/btn/btn-default-small-pressed-fbg.gif), bg:url(images/btn/btn-default-small-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-small-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-disabled-corners.gif), sides:url(images/btn/btn-default-small-disabled-sides.gif), frame-bg:url(images/btn/btn-default-small-disabled-fbg.gif), bg:url(images/btn/btn-default-small-disabled-bg.gif)"; +} + +/**/ +/* */ +.x-btn-default-medium { + border-color: #333333; +} + +.x-btn-default-medium { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: #606060; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #6f6f6f), color-stop(50%, #606060), color-stop(51%, #5b5b5b), color-stop(100%, #606060)); + background-image: -webkit-linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); + background-image: -moz-linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); + background-image: -o-linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); + background-image: -ms-linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); + background-image: linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); +} + +.x-btn-default-medium-mc { + background-image: url(images/btn/btn-default-medium-fbg.gif); + background-position: 0 top; + background-color: #606060; +} + +.x-nlg .x-btn-default-medium { + background-image: url(images/btn/btn-default-medium-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-default-medium { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-default-medium-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-default-medium-tl { + background-position: 0 -6px; +} + +.x-btn-default-medium-tr { + background-position: right -9px; +} + +.x-btn-default-medium-bl { + background-position: 0 -12px; +} + +.x-btn-default-medium-br { + background-position: right -15px; +} + +.x-btn-default-medium-ml { + background-position: 0 top; +} + +.x-btn-default-medium-mr { + background-position: right top; +} + +.x-btn-default-medium-tc { + background-position: 0 0; +} + +.x-btn-default-medium-bc { + background-position: 0 -3px; +} + +.x-btn-default-medium-tr, +.x-btn-default-medium-br, +.x-btn-default-medium-mr { + padding-right: 3px; +} + +.x-btn-default-medium-tl, +.x-btn-default-medium-bl, +.x-btn-default-medium-ml { + padding-left: 3px; +} + +.x-btn-default-medium-tc { + height: 3px; +} + +.x-btn-default-medium-bc { + height: 3px; +} + +.x-btn-default-medium-tl, +.x-btn-default-medium-bl, +.x-btn-default-medium-tr, +.x-btn-default-medium-br, +.x-btn-default-medium-tc, +.x-btn-default-medium-bc, +.x-btn-default-medium-ml, +.x-btn-default-medium-mr { + zoom: 1; + background-image: url(images/btn/btn-default-medium-corners.gif); +} + +.x-btn-default-medium-ml, +.x-btn-default-medium-mr { + zoom: 1; + background-image: url(images/btn/btn-default-medium-sides.gif); +} + +.x-btn-default-medium-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-default-medium-tl, +.x-strict .x-ie7 .x-btn-default-medium-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-default-medium:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-medium-fbg.gif), bg:url(images/btn/btn-default-medium-bg.gif), corners:url(images/btn/btn-default-medium-corners.gif), sides:url(images/btn/btn-default-medium-sides.gif)"; +} + +/**/ +/* */ +.x-btn-default-medium .x-btn-inner { + font-size: 14px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: white; + padding: 0 8px; +} +.x-btn-default-medium .x-btn-arrow { + background-image: url(images/button/default-medium-arrow.png); +} +.x-btn-default-medium .x-btn-arrow-right { + padding-right: 30px; +} +.x-btn-default-medium .x-btn-arrow-bottom { + padding-bottom: 26px; +} +.x-btn-default-medium .x-btn-glyph { + font-size: 24px; + line-height: 24px; + color: white; +} +.x-ie8m .x-btn-default-medium .x-btn-glyph { + color: white; +} + +.x-btn-default-medium-disabled { + background-image: none; + background-color: #444444; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #565656), color-stop(50%, #444444), color-stop(51%, #404040), color-stop(100%, #444444)); + background-image: -webkit-linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); + background-image: -moz-linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); + background-image: -o-linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); + background-image: -ms-linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); + background-image: linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); +} + +.x-btn-default-medium-icon .x-btn-button, +.x-btn-default-medium-noicon .x-btn-button { + height: 24px; +} +.x-btn-default-medium-icon .x-btn-inner, +.x-btn-default-medium-noicon .x-btn-inner { + line-height: 24px; +} + +.x-btn-default-medium-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-medium-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-medium-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-default-medium-icon .x-btn-inner { + width: 24px; + padding: 0; +} +.x-btn-default-medium-icon .x-btn-icon-el { + width: 24px; + height: 24px; +} + +.x-btn-default-medium-icon-text-left .x-btn-button { + height: 24px; +} +.x-btn-default-medium-icon-text-left .x-btn-inner { + line-height: 24px; + padding-left: 29px; +} +.x-btn-default-medium-icon-text-left .x-btn-icon-el { + width: 24px; + right: auto; +} +.x-ie6 .x-btn-default-medium-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-medium-icon-text-left .x-btn-icon-el { + height: 24px; +} + +.x-btn-default-medium-icon-text-right .x-btn-button { + height: 24px; +} +.x-btn-default-medium-icon-text-right .x-btn-inner { + line-height: 24px; + padding-right: 29px; +} +.x-btn-default-medium-icon-text-right .x-btn-icon-el { + width: 24px; + left: auto; +} +.x-ie6 .x-btn-default-medium-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-medium-icon-text-right .x-btn-icon-el { + height: 24px; +} + +.x-btn-default-medium-icon-text-top .x-btn-inner { + padding-top: 29px; +} +.x-btn-default-medium-icon-text-top .x-btn-icon-el { + height: 24px; + bottom: auto; +} +.x-ie6 .x-btn-default-medium-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-medium-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-medium-icon-text-bottom .x-btn-inner { + padding-bottom: 29px; +} +.x-btn-default-medium-icon-text-bottom .x-btn-icon-el { + height: 24px; + top: auto; +} +.x-ie6 .x-btn-default-medium-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-medium-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-medium-focus { + border-color: #96caee; + background-image: none; + background-color: #3e3e3e; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #515151), color-stop(50%, #3e3e3e), color-stop(51%, #3a3a3a), color-stop(100%, #3e3e3e)); + background-image: -webkit-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -moz-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -o-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -ms-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); +} + +.x-btn-default-medium-over { + background-image: none; + background-color: #3e3e3e; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #515151), color-stop(50%, #3e3e3e), color-stop(51%, #3a3a3a), color-stop(100%, #3e3e3e)); + background-image: -webkit-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -moz-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -o-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -ms-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); +} + +.x-btn-default-medium-menu-active, +.x-btn-default-medium-pressed { + background-image: none; + background-color: #333333; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #333333), color-stop(50%, #303030), color-stop(51%, #333333), color-stop(100%, #474747)); + background-image: -webkit-linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); + background-image: -moz-linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); + background-image: -o-linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); + background-image: -ms-linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); + background-image: linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); +} + +.x-btn-default-medium-focus .x-frame-tl, +.x-btn-default-medium-focus .x-frame-bl, +.x-btn-default-medium-focus .x-frame-tr, +.x-btn-default-medium-focus .x-frame-br, +.x-btn-default-medium-focus .x-frame-tc, +.x-btn-default-medium-focus .x-frame-bc { + background-image: url(images/btn/btn-default-medium-focus-corners.gif); +} +.x-btn-default-medium-focus .x-frame-ml, +.x-btn-default-medium-focus .x-frame-mr { + background-image: url(images/btn/btn-default-medium-focus-sides.gif); +} +.x-btn-default-medium-focus .x-frame-mc { + background-color: #3e3e3e; + background-image: url(images/btn/btn-default-medium-focus-fbg.gif); +} + +.x-btn-default-medium-over .x-frame-tl, +.x-btn-default-medium-over .x-frame-bl, +.x-btn-default-medium-over .x-frame-tr, +.x-btn-default-medium-over .x-frame-br, +.x-btn-default-medium-over .x-frame-tc, +.x-btn-default-medium-over .x-frame-bc { + background-image: url(images/btn/btn-default-medium-over-corners.gif); +} +.x-btn-default-medium-over .x-frame-ml, +.x-btn-default-medium-over .x-frame-mr { + background-image: url(images/btn/btn-default-medium-over-sides.gif); +} +.x-btn-default-medium-over .x-frame-mc { + background-color: #3e3e3e; + background-image: url(images/btn/btn-default-medium-over-fbg.gif); +} + +.x-btn-default-medium-menu-active .x-frame-tl, +.x-btn-default-medium-menu-active .x-frame-bl, +.x-btn-default-medium-menu-active .x-frame-tr, +.x-btn-default-medium-menu-active .x-frame-br, +.x-btn-default-medium-menu-active .x-frame-tc, +.x-btn-default-medium-menu-active .x-frame-bc, +.x-btn-default-medium-pressed .x-frame-tl, +.x-btn-default-medium-pressed .x-frame-bl, +.x-btn-default-medium-pressed .x-frame-tr, +.x-btn-default-medium-pressed .x-frame-br, +.x-btn-default-medium-pressed .x-frame-tc, +.x-btn-default-medium-pressed .x-frame-bc { + background-image: url(images/btn/btn-default-medium-pressed-corners.gif); +} +.x-btn-default-medium-menu-active .x-frame-ml, +.x-btn-default-medium-menu-active .x-frame-mr, +.x-btn-default-medium-pressed .x-frame-ml, +.x-btn-default-medium-pressed .x-frame-mr { + background-image: url(images/btn/btn-default-medium-pressed-sides.gif); +} +.x-btn-default-medium-menu-active .x-frame-mc, +.x-btn-default-medium-pressed .x-frame-mc { + background-color: #333333; + background-image: url(images/btn/btn-default-medium-pressed-fbg.gif); +} + +.x-btn-default-medium-disabled .x-frame-tl, +.x-btn-default-medium-disabled .x-frame-bl, +.x-btn-default-medium-disabled .x-frame-tr, +.x-btn-default-medium-disabled .x-frame-br, +.x-btn-default-medium-disabled .x-frame-tc, +.x-btn-default-medium-disabled .x-frame-bc { + background-image: url(images/btn/btn-default-medium-disabled-corners.gif); +} +.x-btn-default-medium-disabled .x-frame-ml, +.x-btn-default-medium-disabled .x-frame-mr { + background-image: url(images/btn/btn-default-medium-disabled-sides.gif); +} +.x-btn-default-medium-disabled .x-frame-mc { + background-color: #444444; + background-image: url(images/btn/btn-default-medium-disabled-fbg.gif); +} + +.x-nlg .x-btn-default-medium-focus { + background-image: url(images/btn/btn-default-medium-focus-bg.gif); +} + +.x-nlg .x-btn-default-medium-over { + background-image: url(images/btn/btn-default-medium-over-bg.gif); +} + +.x-nlg .x-btn-default-medium-menu-active, +.x-nlg .x-btn-default-medium-pressed { + background-image: url(images/btn/btn-default-medium-pressed-bg.gif); +} + +.x-nlg .x-btn-default-medium-disabled { + background-image: url(images/btn/btn-default-medium-disabled-bg.gif); +} + +.x-nbr .x-btn-default-medium { + background-image: none; +} + +.x-btn-default-medium .x-btn-split-right { + background-image: url(images/button/default-medium-s-arrow.png); + padding-right: 32px; +} +.x-btn-default-medium .x-btn-split-bottom { + background-image: url(images/button/default-medium-s-arrow-b.png); + padding-bottom: 28px; +} + +.x-btn-default-medium-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-default-medium-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-medium-focus-corners.gif), sides:url(images/btn/btn-default-medium-focus-sides.gif), frame-bg:url(images/btn/btn-default-medium-focus-fbg.gif), bg:url(images/btn/btn-default-medium-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-medium-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-medium-over-corners.gif), sides:url(images/btn/btn-default-medium-over-sides.gif), frame-bg:url(images/btn/btn-default-medium-over-fbg.gif), bg:url(images/btn/btn-default-medium-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-medium-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-medium-pressed-corners.gif), sides:url(images/btn/btn-default-medium-pressed-sides.gif), frame-bg:url(images/btn/btn-default-medium-pressed-fbg.gif), bg:url(images/btn/btn-default-medium-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-medium-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-medium-disabled-corners.gif), sides:url(images/btn/btn-default-medium-disabled-sides.gif), frame-bg:url(images/btn/btn-default-medium-disabled-fbg.gif), bg:url(images/btn/btn-default-medium-disabled-bg.gif)"; +} + +/**/ +/* */ +.x-btn-default-large { + border-color: #333333; +} + +.x-btn-default-large { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: #606060; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #6f6f6f), color-stop(50%, #606060), color-stop(51%, #5b5b5b), color-stop(100%, #606060)); + background-image: -webkit-linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); + background-image: -moz-linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); + background-image: -o-linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); + background-image: -ms-linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); + background-image: linear-gradient(top, #6f6f6f, #606060 50%, #5b5b5b 51%, #606060); +} + +.x-btn-default-large-mc { + background-image: url(images/btn/btn-default-large-fbg.gif); + background-position: 0 top; + background-color: #606060; +} + +.x-nlg .x-btn-default-large { + background-image: url(images/btn/btn-default-large-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-default-large { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-default-large-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-default-large-tl { + background-position: 0 -6px; +} + +.x-btn-default-large-tr { + background-position: right -9px; +} + +.x-btn-default-large-bl { + background-position: 0 -12px; +} + +.x-btn-default-large-br { + background-position: right -15px; +} + +.x-btn-default-large-ml { + background-position: 0 top; +} + +.x-btn-default-large-mr { + background-position: right top; +} + +.x-btn-default-large-tc { + background-position: 0 0; +} + +.x-btn-default-large-bc { + background-position: 0 -3px; +} + +.x-btn-default-large-tr, +.x-btn-default-large-br, +.x-btn-default-large-mr { + padding-right: 3px; +} + +.x-btn-default-large-tl, +.x-btn-default-large-bl, +.x-btn-default-large-ml { + padding-left: 3px; +} + +.x-btn-default-large-tc { + height: 3px; +} + +.x-btn-default-large-bc { + height: 3px; +} + +.x-btn-default-large-tl, +.x-btn-default-large-bl, +.x-btn-default-large-tr, +.x-btn-default-large-br, +.x-btn-default-large-tc, +.x-btn-default-large-bc, +.x-btn-default-large-ml, +.x-btn-default-large-mr { + zoom: 1; + background-image: url(images/btn/btn-default-large-corners.gif); +} + +.x-btn-default-large-ml, +.x-btn-default-large-mr { + zoom: 1; + background-image: url(images/btn/btn-default-large-sides.gif); +} + +.x-btn-default-large-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-default-large-tl, +.x-strict .x-ie7 .x-btn-default-large-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-default-large:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-large-fbg.gif), bg:url(images/btn/btn-default-large-bg.gif), corners:url(images/btn/btn-default-large-corners.gif), sides:url(images/btn/btn-default-large-sides.gif)"; +} + +/**/ +/* */ +.x-btn-default-large .x-btn-inner { + font-size: 16px; + font-weight: bold; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: white; + padding: 0 10px; +} +.x-btn-default-large .x-btn-arrow { + background-image: url(images/button/default-large-arrow.png); +} +.x-btn-default-large .x-btn-arrow-right { + padding-right: 36px; +} +.x-btn-default-large .x-btn-arrow-bottom { + padding-bottom: 32px; +} +.x-btn-default-large .x-btn-glyph { + font-size: 32px; + line-height: 32px; + color: white; +} +.x-ie8m .x-btn-default-large .x-btn-glyph { + color: white; +} + +.x-btn-default-large-disabled { + background-image: none; + background-color: #444444; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #565656), color-stop(50%, #444444), color-stop(51%, #404040), color-stop(100%, #444444)); + background-image: -webkit-linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); + background-image: -moz-linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); + background-image: -o-linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); + background-image: -ms-linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); + background-image: linear-gradient(top, #565656, #444444 50%, #404040 51%, #444444); +} + +.x-btn-default-large-icon .x-btn-button, +.x-btn-default-large-noicon .x-btn-button { + height: 32px; +} +.x-btn-default-large-icon .x-btn-inner, +.x-btn-default-large-noicon .x-btn-inner { + line-height: 32px; +} + +.x-btn-default-large-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-large-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-large-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-default-large-icon .x-btn-inner { + width: 32px; + padding: 0; +} +.x-btn-default-large-icon .x-btn-icon-el { + width: 32px; + height: 32px; +} + +.x-btn-default-large-icon-text-left .x-btn-button { + height: 32px; +} +.x-btn-default-large-icon-text-left .x-btn-inner { + line-height: 32px; + padding-left: 37px; +} +.x-btn-default-large-icon-text-left .x-btn-icon-el { + width: 32px; + right: auto; +} +.x-ie6 .x-btn-default-large-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-large-icon-text-left .x-btn-icon-el { + height: 32px; +} + +.x-btn-default-large-icon-text-right .x-btn-button { + height: 32px; +} +.x-btn-default-large-icon-text-right .x-btn-inner { + line-height: 32px; + padding-right: 37px; +} +.x-btn-default-large-icon-text-right .x-btn-icon-el { + width: 32px; + left: auto; +} +.x-ie6 .x-btn-default-large-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-large-icon-text-right .x-btn-icon-el { + height: 32px; +} + +.x-btn-default-large-icon-text-top .x-btn-inner { + padding-top: 37px; +} +.x-btn-default-large-icon-text-top .x-btn-icon-el { + height: 32px; + bottom: auto; +} +.x-ie6 .x-btn-default-large-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-large-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-large-icon-text-bottom .x-btn-inner { + padding-bottom: 37px; +} +.x-btn-default-large-icon-text-bottom .x-btn-icon-el { + height: 32px; + top: auto; +} +.x-ie6 .x-btn-default-large-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-large-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-large-focus { + border-color: #96caee; + background-image: none; + background-color: #3e3e3e; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #515151), color-stop(50%, #3e3e3e), color-stop(51%, #3a3a3a), color-stop(100%, #3e3e3e)); + background-image: -webkit-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -moz-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -o-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -ms-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); +} + +.x-btn-default-large-over { + background-image: none; + background-color: #3e3e3e; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #515151), color-stop(50%, #3e3e3e), color-stop(51%, #3a3a3a), color-stop(100%, #3e3e3e)); + background-image: -webkit-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -moz-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -o-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: -ms-linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); + background-image: linear-gradient(top, #515151, #3e3e3e 50%, #3a3a3a 51%, #3e3e3e); +} + +.x-btn-default-large-menu-active, +.x-btn-default-large-pressed { + background-image: none; + background-color: #333333; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #333333), color-stop(50%, #303030), color-stop(51%, #333333), color-stop(100%, #474747)); + background-image: -webkit-linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); + background-image: -moz-linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); + background-image: -o-linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); + background-image: -ms-linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); + background-image: linear-gradient(top, #333333, #303030 50%, #333333 51%, #474747); +} + +.x-btn-default-large-focus .x-frame-tl, +.x-btn-default-large-focus .x-frame-bl, +.x-btn-default-large-focus .x-frame-tr, +.x-btn-default-large-focus .x-frame-br, +.x-btn-default-large-focus .x-frame-tc, +.x-btn-default-large-focus .x-frame-bc { + background-image: url(images/btn/btn-default-large-focus-corners.gif); +} +.x-btn-default-large-focus .x-frame-ml, +.x-btn-default-large-focus .x-frame-mr { + background-image: url(images/btn/btn-default-large-focus-sides.gif); +} +.x-btn-default-large-focus .x-frame-mc { + background-color: #3e3e3e; + background-image: url(images/btn/btn-default-large-focus-fbg.gif); +} + +.x-btn-default-large-over .x-frame-tl, +.x-btn-default-large-over .x-frame-bl, +.x-btn-default-large-over .x-frame-tr, +.x-btn-default-large-over .x-frame-br, +.x-btn-default-large-over .x-frame-tc, +.x-btn-default-large-over .x-frame-bc { + background-image: url(images/btn/btn-default-large-over-corners.gif); +} +.x-btn-default-large-over .x-frame-ml, +.x-btn-default-large-over .x-frame-mr { + background-image: url(images/btn/btn-default-large-over-sides.gif); +} +.x-btn-default-large-over .x-frame-mc { + background-color: #3e3e3e; + background-image: url(images/btn/btn-default-large-over-fbg.gif); +} + +.x-btn-default-large-menu-active .x-frame-tl, +.x-btn-default-large-menu-active .x-frame-bl, +.x-btn-default-large-menu-active .x-frame-tr, +.x-btn-default-large-menu-active .x-frame-br, +.x-btn-default-large-menu-active .x-frame-tc, +.x-btn-default-large-menu-active .x-frame-bc, +.x-btn-default-large-pressed .x-frame-tl, +.x-btn-default-large-pressed .x-frame-bl, +.x-btn-default-large-pressed .x-frame-tr, +.x-btn-default-large-pressed .x-frame-br, +.x-btn-default-large-pressed .x-frame-tc, +.x-btn-default-large-pressed .x-frame-bc { + background-image: url(images/btn/btn-default-large-pressed-corners.gif); +} +.x-btn-default-large-menu-active .x-frame-ml, +.x-btn-default-large-menu-active .x-frame-mr, +.x-btn-default-large-pressed .x-frame-ml, +.x-btn-default-large-pressed .x-frame-mr { + background-image: url(images/btn/btn-default-large-pressed-sides.gif); +} +.x-btn-default-large-menu-active .x-frame-mc, +.x-btn-default-large-pressed .x-frame-mc { + background-color: #333333; + background-image: url(images/btn/btn-default-large-pressed-fbg.gif); +} + +.x-btn-default-large-disabled .x-frame-tl, +.x-btn-default-large-disabled .x-frame-bl, +.x-btn-default-large-disabled .x-frame-tr, +.x-btn-default-large-disabled .x-frame-br, +.x-btn-default-large-disabled .x-frame-tc, +.x-btn-default-large-disabled .x-frame-bc { + background-image: url(images/btn/btn-default-large-disabled-corners.gif); +} +.x-btn-default-large-disabled .x-frame-ml, +.x-btn-default-large-disabled .x-frame-mr { + background-image: url(images/btn/btn-default-large-disabled-sides.gif); +} +.x-btn-default-large-disabled .x-frame-mc { + background-color: #444444; + background-image: url(images/btn/btn-default-large-disabled-fbg.gif); +} + +.x-nlg .x-btn-default-large-focus { + background-image: url(images/btn/btn-default-large-focus-bg.gif); +} + +.x-nlg .x-btn-default-large-over { + background-image: url(images/btn/btn-default-large-over-bg.gif); +} + +.x-nlg .x-btn-default-large-menu-active, +.x-nlg .x-btn-default-large-pressed { + background-image: url(images/btn/btn-default-large-pressed-bg.gif); +} + +.x-nlg .x-btn-default-large-disabled { + background-image: url(images/btn/btn-default-large-disabled-bg.gif); +} + +.x-nbr .x-btn-default-large { + background-image: none; +} + +.x-btn-default-large .x-btn-split-right { + background-image: url(images/button/default-large-s-arrow.png); + padding-right: 38px; +} +.x-btn-default-large .x-btn-split-bottom { + background-image: url(images/button/default-large-s-arrow-b.png); + padding-bottom: 34px; +} + +.x-btn-default-large-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-default-large-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-large-focus-corners.gif), sides:url(images/btn/btn-default-large-focus-sides.gif), frame-bg:url(images/btn/btn-default-large-focus-fbg.gif), bg:url(images/btn/btn-default-large-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-large-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-large-over-corners.gif), sides:url(images/btn/btn-default-large-over-sides.gif), frame-bg:url(images/btn/btn-default-large-over-fbg.gif), bg:url(images/btn/btn-default-large-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-large-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-large-pressed-corners.gif), sides:url(images/btn/btn-default-large-pressed-sides.gif), frame-bg:url(images/btn/btn-default-large-pressed-fbg.gif), bg:url(images/btn/btn-default-large-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-large-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-large-disabled-corners.gif), sides:url(images/btn/btn-default-large-disabled-sides.gif), frame-bg:url(images/btn/btn-default-large-disabled-fbg.gif), bg:url(images/btn/btn-default-large-disabled-bg.gif)"; +} + +/**/ +/* */ +.x-btn-default-toolbar-small { + border-color: #e1e1e1; +} + +.x-btn-default-toolbar-small { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: #f5f5f5; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-default-toolbar-small-mc { + background-image: url(images/btn/btn-default-toolbar-small-fbg.gif); + background-position: 0 top; + background-color: #f5f5f5; +} + +.x-nlg .x-btn-default-toolbar-small { + background-image: url(images/btn/btn-default-toolbar-small-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-default-toolbar-small { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-default-toolbar-small-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-default-toolbar-small-tl { + background-position: 0 -6px; +} + +.x-btn-default-toolbar-small-tr { + background-position: right -9px; +} + +.x-btn-default-toolbar-small-bl { + background-position: 0 -12px; +} + +.x-btn-default-toolbar-small-br { + background-position: right -15px; +} + +.x-btn-default-toolbar-small-ml { + background-position: 0 top; +} + +.x-btn-default-toolbar-small-mr { + background-position: right top; +} + +.x-btn-default-toolbar-small-tc { + background-position: 0 0; +} + +.x-btn-default-toolbar-small-bc { + background-position: 0 -3px; +} + +.x-btn-default-toolbar-small-tr, +.x-btn-default-toolbar-small-br, +.x-btn-default-toolbar-small-mr { + padding-right: 3px; +} + +.x-btn-default-toolbar-small-tl, +.x-btn-default-toolbar-small-bl, +.x-btn-default-toolbar-small-ml { + padding-left: 3px; +} + +.x-btn-default-toolbar-small-tc { + height: 3px; +} + +.x-btn-default-toolbar-small-bc { + height: 3px; +} + +.x-btn-default-toolbar-small-tl, +.x-btn-default-toolbar-small-bl, +.x-btn-default-toolbar-small-tr, +.x-btn-default-toolbar-small-br, +.x-btn-default-toolbar-small-tc, +.x-btn-default-toolbar-small-bc, +.x-btn-default-toolbar-small-ml, +.x-btn-default-toolbar-small-mr { + zoom: 1; + background-image: url(images/btn/btn-default-toolbar-small-corners.gif); +} + +.x-btn-default-toolbar-small-ml, +.x-btn-default-toolbar-small-mr { + zoom: 1; + background-image: url(images/btn/btn-default-toolbar-small-sides.gif); +} + +.x-btn-default-toolbar-small-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-default-toolbar-small-tl, +.x-strict .x-ie7 .x-btn-default-toolbar-small-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-default-toolbar-small:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-toolbar-small-fbg.gif), bg:url(images/btn/btn-default-toolbar-small-bg.gif), corners:url(images/btn/btn-default-toolbar-small-corners.gif), sides:url(images/btn/btn-default-toolbar-small-sides.gif)"; +} + +/**/ +/* */ +.x-btn-default-toolbar-small .x-btn-inner { + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #333333; + padding: 0 5px; +} +.x-btn-default-toolbar-small .x-btn-arrow { + background-image: url(images/button/default-toolbar-small-arrow.png); +} +.x-btn-default-toolbar-small .x-btn-arrow-right { + padding-right: 21px; +} +.x-btn-default-toolbar-small .x-btn-arrow-bottom { + padding-bottom: 18px; +} +.x-btn-default-toolbar-small .x-btn-glyph { + font-size: 16px; + line-height: 16px; + color: #333333; +} +.x-ie8m .x-btn-default-toolbar-small .x-btn-glyph { + color: #333333; +} + +.x-btn-default-toolbar-small-disabled { + background-image: none; + background-color: #f5f5f5; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-default-toolbar-small-icon .x-btn-button, +.x-btn-default-toolbar-small-noicon .x-btn-button { + height: 16px; +} +.x-btn-default-toolbar-small-icon .x-btn-inner, +.x-btn-default-toolbar-small-noicon .x-btn-inner { + line-height: 16px; +} + +.x-btn-default-toolbar-small-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-toolbar-small-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-toolbar-small-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-default-toolbar-small-icon .x-btn-inner { + width: 16px; + padding: 0; +} +.x-btn-default-toolbar-small-icon .x-btn-icon-el { + width: 16px; + height: 16px; +} + +.x-btn-default-toolbar-small-icon-text-left .x-btn-button { + height: 16px; +} +.x-btn-default-toolbar-small-icon-text-left .x-btn-inner { + line-height: 16px; + padding-left: 21px; +} +.x-btn-default-toolbar-small-icon-text-left .x-btn-icon-el { + width: 16px; + right: auto; +} +.x-ie6 .x-btn-default-toolbar-small-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-toolbar-small-icon-text-left .x-btn-icon-el { + height: 16px; +} + +.x-btn-default-toolbar-small-icon-text-right .x-btn-button { + height: 16px; +} +.x-btn-default-toolbar-small-icon-text-right .x-btn-inner { + line-height: 16px; + padding-right: 21px; +} +.x-btn-default-toolbar-small-icon-text-right .x-btn-icon-el { + width: 16px; + left: auto; +} +.x-ie6 .x-btn-default-toolbar-small-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-toolbar-small-icon-text-right .x-btn-icon-el { + height: 16px; +} + +.x-btn-default-toolbar-small-icon-text-top .x-btn-inner { + padding-top: 21px; +} +.x-btn-default-toolbar-small-icon-text-top .x-btn-icon-el { + height: 16px; + bottom: auto; +} +.x-ie6 .x-btn-default-toolbar-small-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-toolbar-small-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-toolbar-small-icon-text-bottom .x-btn-inner { + padding-bottom: 21px; +} +.x-btn-default-toolbar-small-icon-text-bottom .x-btn-icon-el { + height: 16px; + top: auto; +} +.x-ie6 .x-btn-default-toolbar-small-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-toolbar-small-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-toolbar-small-focus { + border-color: #96caee; + background-image: none; + background-color: #ebebeb; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-default-toolbar-small-over { + border-color: #d8d8d8; + background-image: none; + background-color: #ebebeb; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #cbcbcb), color-stop(100%, #b8b8b8)); + background-image: -webkit-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -moz-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -o-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -ms-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: linear-gradient(top, #cbcbcb, #b8b8b8); +} + +.x-btn-default-toolbar-small-menu-active, +.x-btn-default-toolbar-small-pressed { + border-color: #cecece; + background-image: none; + background-color: #e1e1e1; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #919191), color-stop(100%, #808080)); + background-image: -webkit-linear-gradient(top, #919191, #808080); + background-image: -moz-linear-gradient(top, #919191, #808080); + background-image: -o-linear-gradient(top, #919191, #808080); + background-image: -ms-linear-gradient(top, #919191, #808080); + background-image: linear-gradient(top, #919191, #808080); +} +.x-btn-default-toolbar-small-menu-active .x-btn-inner, +.x-btn-default-toolbar-small-pressed .x-btn-inner { + color: white; +} + +.x-btn-default-toolbar-small-focus .x-frame-tl, +.x-btn-default-toolbar-small-focus .x-frame-bl, +.x-btn-default-toolbar-small-focus .x-frame-tr, +.x-btn-default-toolbar-small-focus .x-frame-br, +.x-btn-default-toolbar-small-focus .x-frame-tc, +.x-btn-default-toolbar-small-focus .x-frame-bc { + background-image: url(images/btn/btn-default-toolbar-small-focus-corners.gif); +} +.x-btn-default-toolbar-small-focus .x-frame-ml, +.x-btn-default-toolbar-small-focus .x-frame-mr { + background-image: url(images/btn/btn-default-toolbar-small-focus-sides.gif); +} +.x-btn-default-toolbar-small-focus .x-frame-mc { + background-color: #ebebeb; + background-image: url(images/btn/btn-default-toolbar-small-focus-fbg.gif); +} + +.x-btn-default-toolbar-small-over .x-frame-tl, +.x-btn-default-toolbar-small-over .x-frame-bl, +.x-btn-default-toolbar-small-over .x-frame-tr, +.x-btn-default-toolbar-small-over .x-frame-br, +.x-btn-default-toolbar-small-over .x-frame-tc, +.x-btn-default-toolbar-small-over .x-frame-bc { + background-image: url(images/btn/btn-default-toolbar-small-over-corners.gif); +} +.x-btn-default-toolbar-small-over .x-frame-ml, +.x-btn-default-toolbar-small-over .x-frame-mr { + background-image: url(images/btn/btn-default-toolbar-small-over-sides.gif); +} +.x-btn-default-toolbar-small-over .x-frame-mc { + background-color: #ebebeb; + background-image: url(images/btn/btn-default-toolbar-small-over-fbg.gif); +} + +.x-btn-default-toolbar-small-menu-active .x-frame-tl, +.x-btn-default-toolbar-small-menu-active .x-frame-bl, +.x-btn-default-toolbar-small-menu-active .x-frame-tr, +.x-btn-default-toolbar-small-menu-active .x-frame-br, +.x-btn-default-toolbar-small-menu-active .x-frame-tc, +.x-btn-default-toolbar-small-menu-active .x-frame-bc, +.x-btn-default-toolbar-small-pressed .x-frame-tl, +.x-btn-default-toolbar-small-pressed .x-frame-bl, +.x-btn-default-toolbar-small-pressed .x-frame-tr, +.x-btn-default-toolbar-small-pressed .x-frame-br, +.x-btn-default-toolbar-small-pressed .x-frame-tc, +.x-btn-default-toolbar-small-pressed .x-frame-bc { + background-image: url(images/btn/btn-default-toolbar-small-pressed-corners.gif); +} +.x-btn-default-toolbar-small-menu-active .x-frame-ml, +.x-btn-default-toolbar-small-menu-active .x-frame-mr, +.x-btn-default-toolbar-small-pressed .x-frame-ml, +.x-btn-default-toolbar-small-pressed .x-frame-mr { + background-image: url(images/btn/btn-default-toolbar-small-pressed-sides.gif); +} +.x-btn-default-toolbar-small-menu-active .x-frame-mc, +.x-btn-default-toolbar-small-pressed .x-frame-mc { + background-color: #e1e1e1; + background-image: url(images/btn/btn-default-toolbar-small-pressed-fbg.gif); +} + +.x-btn-default-toolbar-small-disabled .x-frame-tl, +.x-btn-default-toolbar-small-disabled .x-frame-bl, +.x-btn-default-toolbar-small-disabled .x-frame-tr, +.x-btn-default-toolbar-small-disabled .x-frame-br, +.x-btn-default-toolbar-small-disabled .x-frame-tc, +.x-btn-default-toolbar-small-disabled .x-frame-bc { + background-image: url(images/btn/btn-default-toolbar-small-disabled-corners.gif); +} +.x-btn-default-toolbar-small-disabled .x-frame-ml, +.x-btn-default-toolbar-small-disabled .x-frame-mr { + background-image: url(images/btn/btn-default-toolbar-small-disabled-sides.gif); +} +.x-btn-default-toolbar-small-disabled .x-frame-mc { + background-color: #f5f5f5; + background-image: url(images/btn/btn-default-toolbar-small-disabled-fbg.gif); +} + +.x-nlg .x-btn-default-toolbar-small-focus { + background-image: url(images/btn/btn-default-toolbar-small-focus-bg.gif); +} + +.x-nlg .x-btn-default-toolbar-small-over { + background-image: url(images/btn/btn-default-toolbar-small-over-bg.gif); +} + +.x-nlg .x-btn-default-toolbar-small-menu-active, +.x-nlg .x-btn-default-toolbar-small-pressed { + background-image: url(images/btn/btn-default-toolbar-small-pressed-bg.gif); +} + +.x-nlg .x-btn-default-toolbar-small-disabled { + background-image: url(images/btn/btn-default-toolbar-small-disabled-bg.gif); +} + +.x-nbr .x-btn-default-toolbar-small { + background-image: none; +} + +.x-btn-default-toolbar-small .x-btn-split-right { + background-image: url(images/button/default-toolbar-small-s-arrow.png); + padding-right: 23px; +} +.x-btn-default-toolbar-small .x-btn-split-bottom { + background-image: url(images/button/default-toolbar-small-s-arrow-b.png); + padding-bottom: 20px; +} + +.x-btn-default-toolbar-small-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-default-toolbar-small-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-small-focus-corners.gif), sides:url(images/btn/btn-default-toolbar-small-focus-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-small-focus-fbg.gif), bg:url(images/btn/btn-default-toolbar-small-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-toolbar-small-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-small-over-corners.gif), sides:url(images/btn/btn-default-toolbar-small-over-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-small-over-fbg.gif), bg:url(images/btn/btn-default-toolbar-small-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-toolbar-small-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-small-pressed-corners.gif), sides:url(images/btn/btn-default-toolbar-small-pressed-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-small-pressed-fbg.gif), bg:url(images/btn/btn-default-toolbar-small-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-toolbar-small-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-small-disabled-corners.gif), sides:url(images/btn/btn-default-toolbar-small-disabled-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-small-disabled-fbg.gif), bg:url(images/btn/btn-default-toolbar-small-disabled-bg.gif)"; +} + +/**/ +/* */ +.x-btn-default-toolbar-medium { + border-color: #e1e1e1; +} + +.x-btn-default-toolbar-medium { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: #f5f5f5; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-default-toolbar-medium-mc { + background-image: url(images/btn/btn-default-toolbar-medium-fbg.gif); + background-position: 0 top; + background-color: #f5f5f5; +} + +.x-nlg .x-btn-default-toolbar-medium { + background-image: url(images/btn/btn-default-toolbar-medium-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-default-toolbar-medium { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-default-toolbar-medium-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-default-toolbar-medium-tl { + background-position: 0 -6px; +} + +.x-btn-default-toolbar-medium-tr { + background-position: right -9px; +} + +.x-btn-default-toolbar-medium-bl { + background-position: 0 -12px; +} + +.x-btn-default-toolbar-medium-br { + background-position: right -15px; +} + +.x-btn-default-toolbar-medium-ml { + background-position: 0 top; +} + +.x-btn-default-toolbar-medium-mr { + background-position: right top; +} + +.x-btn-default-toolbar-medium-tc { + background-position: 0 0; +} + +.x-btn-default-toolbar-medium-bc { + background-position: 0 -3px; +} + +.x-btn-default-toolbar-medium-tr, +.x-btn-default-toolbar-medium-br, +.x-btn-default-toolbar-medium-mr { + padding-right: 3px; +} + +.x-btn-default-toolbar-medium-tl, +.x-btn-default-toolbar-medium-bl, +.x-btn-default-toolbar-medium-ml { + padding-left: 3px; +} + +.x-btn-default-toolbar-medium-tc { + height: 3px; +} + +.x-btn-default-toolbar-medium-bc { + height: 3px; +} + +.x-btn-default-toolbar-medium-tl, +.x-btn-default-toolbar-medium-bl, +.x-btn-default-toolbar-medium-tr, +.x-btn-default-toolbar-medium-br, +.x-btn-default-toolbar-medium-tc, +.x-btn-default-toolbar-medium-bc, +.x-btn-default-toolbar-medium-ml, +.x-btn-default-toolbar-medium-mr { + zoom: 1; + background-image: url(images/btn/btn-default-toolbar-medium-corners.gif); +} + +.x-btn-default-toolbar-medium-ml, +.x-btn-default-toolbar-medium-mr { + zoom: 1; + background-image: url(images/btn/btn-default-toolbar-medium-sides.gif); +} + +.x-btn-default-toolbar-medium-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-default-toolbar-medium-tl, +.x-strict .x-ie7 .x-btn-default-toolbar-medium-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-default-toolbar-medium:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-toolbar-medium-fbg.gif), bg:url(images/btn/btn-default-toolbar-medium-bg.gif), corners:url(images/btn/btn-default-toolbar-medium-corners.gif), sides:url(images/btn/btn-default-toolbar-medium-sides.gif)"; +} + +/**/ +/* */ +.x-btn-default-toolbar-medium .x-btn-inner { + font-size: 14px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #333333; + padding: 0 8px; +} +.x-btn-default-toolbar-medium .x-btn-arrow { + background-image: url(images/button/default-toolbar-medium-arrow.png); +} +.x-btn-default-toolbar-medium .x-btn-arrow-right { + padding-right: 30px; +} +.x-btn-default-toolbar-medium .x-btn-arrow-bottom { + padding-bottom: 26px; +} +.x-btn-default-toolbar-medium .x-btn-glyph { + font-size: 24px; + line-height: 24px; + color: #333333; +} +.x-ie8m .x-btn-default-toolbar-medium .x-btn-glyph { + color: #333333; +} + +.x-btn-default-toolbar-medium-disabled { + background-image: none; + background-color: #f5f5f5; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-default-toolbar-medium-icon .x-btn-button, +.x-btn-default-toolbar-medium-noicon .x-btn-button { + height: 24px; +} +.x-btn-default-toolbar-medium-icon .x-btn-inner, +.x-btn-default-toolbar-medium-noicon .x-btn-inner { + line-height: 24px; +} + +.x-btn-default-toolbar-medium-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-toolbar-medium-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-toolbar-medium-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-default-toolbar-medium-icon .x-btn-inner { + width: 24px; + padding: 0; +} +.x-btn-default-toolbar-medium-icon .x-btn-icon-el { + width: 24px; + height: 24px; +} + +.x-btn-default-toolbar-medium-icon-text-left .x-btn-button { + height: 24px; +} +.x-btn-default-toolbar-medium-icon-text-left .x-btn-inner { + line-height: 24px; + padding-left: 29px; +} +.x-btn-default-toolbar-medium-icon-text-left .x-btn-icon-el { + width: 24px; + right: auto; +} +.x-ie6 .x-btn-default-toolbar-medium-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-toolbar-medium-icon-text-left .x-btn-icon-el { + height: 24px; +} + +.x-btn-default-toolbar-medium-icon-text-right .x-btn-button { + height: 24px; +} +.x-btn-default-toolbar-medium-icon-text-right .x-btn-inner { + line-height: 24px; + padding-right: 29px; +} +.x-btn-default-toolbar-medium-icon-text-right .x-btn-icon-el { + width: 24px; + left: auto; +} +.x-ie6 .x-btn-default-toolbar-medium-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-toolbar-medium-icon-text-right .x-btn-icon-el { + height: 24px; +} + +.x-btn-default-toolbar-medium-icon-text-top .x-btn-inner { + padding-top: 29px; +} +.x-btn-default-toolbar-medium-icon-text-top .x-btn-icon-el { + height: 24px; + bottom: auto; +} +.x-ie6 .x-btn-default-toolbar-medium-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-toolbar-medium-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-toolbar-medium-icon-text-bottom .x-btn-inner { + padding-bottom: 29px; +} +.x-btn-default-toolbar-medium-icon-text-bottom .x-btn-icon-el { + height: 24px; + top: auto; +} +.x-ie6 .x-btn-default-toolbar-medium-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-toolbar-medium-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-toolbar-medium-focus { + border-color: #96caee; + background-image: none; + background-color: #ebebeb; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-default-toolbar-medium-over { + border-color: #d8d8d8; + background-image: none; + background-color: #ebebeb; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #cbcbcb), color-stop(100%, #b8b8b8)); + background-image: -webkit-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -moz-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -o-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -ms-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: linear-gradient(top, #cbcbcb, #b8b8b8); +} + +.x-btn-default-toolbar-medium-menu-active, +.x-btn-default-toolbar-medium-pressed { + border-color: #cecece; + background-image: none; + background-color: #e1e1e1; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #919191), color-stop(100%, #808080)); + background-image: -webkit-linear-gradient(top, #919191, #808080); + background-image: -moz-linear-gradient(top, #919191, #808080); + background-image: -o-linear-gradient(top, #919191, #808080); + background-image: -ms-linear-gradient(top, #919191, #808080); + background-image: linear-gradient(top, #919191, #808080); +} +.x-btn-default-toolbar-medium-menu-active .x-btn-inner, +.x-btn-default-toolbar-medium-pressed .x-btn-inner { + color: white; +} + +.x-btn-default-toolbar-medium-focus .x-frame-tl, +.x-btn-default-toolbar-medium-focus .x-frame-bl, +.x-btn-default-toolbar-medium-focus .x-frame-tr, +.x-btn-default-toolbar-medium-focus .x-frame-br, +.x-btn-default-toolbar-medium-focus .x-frame-tc, +.x-btn-default-toolbar-medium-focus .x-frame-bc { + background-image: url(images/btn/btn-default-toolbar-medium-focus-corners.gif); +} +.x-btn-default-toolbar-medium-focus .x-frame-ml, +.x-btn-default-toolbar-medium-focus .x-frame-mr { + background-image: url(images/btn/btn-default-toolbar-medium-focus-sides.gif); +} +.x-btn-default-toolbar-medium-focus .x-frame-mc { + background-color: #ebebeb; + background-image: url(images/btn/btn-default-toolbar-medium-focus-fbg.gif); +} + +.x-btn-default-toolbar-medium-over .x-frame-tl, +.x-btn-default-toolbar-medium-over .x-frame-bl, +.x-btn-default-toolbar-medium-over .x-frame-tr, +.x-btn-default-toolbar-medium-over .x-frame-br, +.x-btn-default-toolbar-medium-over .x-frame-tc, +.x-btn-default-toolbar-medium-over .x-frame-bc { + background-image: url(images/btn/btn-default-toolbar-medium-over-corners.gif); +} +.x-btn-default-toolbar-medium-over .x-frame-ml, +.x-btn-default-toolbar-medium-over .x-frame-mr { + background-image: url(images/btn/btn-default-toolbar-medium-over-sides.gif); +} +.x-btn-default-toolbar-medium-over .x-frame-mc { + background-color: #ebebeb; + background-image: url(images/btn/btn-default-toolbar-medium-over-fbg.gif); +} + +.x-btn-default-toolbar-medium-menu-active .x-frame-tl, +.x-btn-default-toolbar-medium-menu-active .x-frame-bl, +.x-btn-default-toolbar-medium-menu-active .x-frame-tr, +.x-btn-default-toolbar-medium-menu-active .x-frame-br, +.x-btn-default-toolbar-medium-menu-active .x-frame-tc, +.x-btn-default-toolbar-medium-menu-active .x-frame-bc, +.x-btn-default-toolbar-medium-pressed .x-frame-tl, +.x-btn-default-toolbar-medium-pressed .x-frame-bl, +.x-btn-default-toolbar-medium-pressed .x-frame-tr, +.x-btn-default-toolbar-medium-pressed .x-frame-br, +.x-btn-default-toolbar-medium-pressed .x-frame-tc, +.x-btn-default-toolbar-medium-pressed .x-frame-bc { + background-image: url(images/btn/btn-default-toolbar-medium-pressed-corners.gif); +} +.x-btn-default-toolbar-medium-menu-active .x-frame-ml, +.x-btn-default-toolbar-medium-menu-active .x-frame-mr, +.x-btn-default-toolbar-medium-pressed .x-frame-ml, +.x-btn-default-toolbar-medium-pressed .x-frame-mr { + background-image: url(images/btn/btn-default-toolbar-medium-pressed-sides.gif); +} +.x-btn-default-toolbar-medium-menu-active .x-frame-mc, +.x-btn-default-toolbar-medium-pressed .x-frame-mc { + background-color: #e1e1e1; + background-image: url(images/btn/btn-default-toolbar-medium-pressed-fbg.gif); +} + +.x-btn-default-toolbar-medium-disabled .x-frame-tl, +.x-btn-default-toolbar-medium-disabled .x-frame-bl, +.x-btn-default-toolbar-medium-disabled .x-frame-tr, +.x-btn-default-toolbar-medium-disabled .x-frame-br, +.x-btn-default-toolbar-medium-disabled .x-frame-tc, +.x-btn-default-toolbar-medium-disabled .x-frame-bc { + background-image: url(images/btn/btn-default-toolbar-medium-disabled-corners.gif); +} +.x-btn-default-toolbar-medium-disabled .x-frame-ml, +.x-btn-default-toolbar-medium-disabled .x-frame-mr { + background-image: url(images/btn/btn-default-toolbar-medium-disabled-sides.gif); +} +.x-btn-default-toolbar-medium-disabled .x-frame-mc { + background-color: #f5f5f5; + background-image: url(images/btn/btn-default-toolbar-medium-disabled-fbg.gif); +} + +.x-nlg .x-btn-default-toolbar-medium-focus { + background-image: url(images/btn/btn-default-toolbar-medium-focus-bg.gif); +} + +.x-nlg .x-btn-default-toolbar-medium-over { + background-image: url(images/btn/btn-default-toolbar-medium-over-bg.gif); +} + +.x-nlg .x-btn-default-toolbar-medium-menu-active, +.x-nlg .x-btn-default-toolbar-medium-pressed { + background-image: url(images/btn/btn-default-toolbar-medium-pressed-bg.gif); +} + +.x-nlg .x-btn-default-toolbar-medium-disabled { + background-image: url(images/btn/btn-default-toolbar-medium-disabled-bg.gif); +} + +.x-nbr .x-btn-default-toolbar-medium { + background-image: none; +} + +.x-btn-default-toolbar-medium .x-btn-split-right { + background-image: url(images/button/default-toolbar-medium-s-arrow.png); + padding-right: 32px; +} +.x-btn-default-toolbar-medium .x-btn-split-bottom { + background-image: url(images/button/default-toolbar-medium-s-arrow-b.png); + padding-bottom: 28px; +} + +.x-btn-default-toolbar-medium-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-default-toolbar-medium-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-medium-focus-corners.gif), sides:url(images/btn/btn-default-toolbar-medium-focus-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-medium-focus-fbg.gif), bg:url(images/btn/btn-default-toolbar-medium-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-toolbar-medium-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-medium-over-corners.gif), sides:url(images/btn/btn-default-toolbar-medium-over-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-medium-over-fbg.gif), bg:url(images/btn/btn-default-toolbar-medium-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-toolbar-medium-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-medium-pressed-corners.gif), sides:url(images/btn/btn-default-toolbar-medium-pressed-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-medium-pressed-fbg.gif), bg:url(images/btn/btn-default-toolbar-medium-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-toolbar-medium-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-medium-disabled-corners.gif), sides:url(images/btn/btn-default-toolbar-medium-disabled-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-medium-disabled-fbg.gif), bg:url(images/btn/btn-default-toolbar-medium-disabled-bg.gif)"; +} + +/**/ +/* */ +.x-btn-default-toolbar-large { + border-color: #e1e1e1; +} + +.x-btn-default-toolbar-large { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: #f5f5f5; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-default-toolbar-large-mc { + background-image: url(images/btn/btn-default-toolbar-large-fbg.gif); + background-position: 0 top; + background-color: #f5f5f5; +} + +.x-nlg .x-btn-default-toolbar-large { + background-image: url(images/btn/btn-default-toolbar-large-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-default-toolbar-large { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-default-toolbar-large-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-default-toolbar-large-tl { + background-position: 0 -6px; +} + +.x-btn-default-toolbar-large-tr { + background-position: right -9px; +} + +.x-btn-default-toolbar-large-bl { + background-position: 0 -12px; +} + +.x-btn-default-toolbar-large-br { + background-position: right -15px; +} + +.x-btn-default-toolbar-large-ml { + background-position: 0 top; +} + +.x-btn-default-toolbar-large-mr { + background-position: right top; +} + +.x-btn-default-toolbar-large-tc { + background-position: 0 0; +} + +.x-btn-default-toolbar-large-bc { + background-position: 0 -3px; +} + +.x-btn-default-toolbar-large-tr, +.x-btn-default-toolbar-large-br, +.x-btn-default-toolbar-large-mr { + padding-right: 3px; +} + +.x-btn-default-toolbar-large-tl, +.x-btn-default-toolbar-large-bl, +.x-btn-default-toolbar-large-ml { + padding-left: 3px; +} + +.x-btn-default-toolbar-large-tc { + height: 3px; +} + +.x-btn-default-toolbar-large-bc { + height: 3px; +} + +.x-btn-default-toolbar-large-tl, +.x-btn-default-toolbar-large-bl, +.x-btn-default-toolbar-large-tr, +.x-btn-default-toolbar-large-br, +.x-btn-default-toolbar-large-tc, +.x-btn-default-toolbar-large-bc, +.x-btn-default-toolbar-large-ml, +.x-btn-default-toolbar-large-mr { + zoom: 1; + background-image: url(images/btn/btn-default-toolbar-large-corners.gif); +} + +.x-btn-default-toolbar-large-ml, +.x-btn-default-toolbar-large-mr { + zoom: 1; + background-image: url(images/btn/btn-default-toolbar-large-sides.gif); +} + +.x-btn-default-toolbar-large-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-default-toolbar-large-tl, +.x-strict .x-ie7 .x-btn-default-toolbar-large-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-default-toolbar-large:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-toolbar-large-fbg.gif), bg:url(images/btn/btn-default-toolbar-large-bg.gif), corners:url(images/btn/btn-default-toolbar-large-corners.gif), sides:url(images/btn/btn-default-toolbar-large-sides.gif)"; +} + +/**/ +/* */ +.x-btn-default-toolbar-large .x-btn-inner { + font-size: 16px; + font-weight: bold; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #333333; + padding: 0 10px; +} +.x-btn-default-toolbar-large .x-btn-arrow { + background-image: url(images/button/default-toolbar-large-arrow.png); +} +.x-btn-default-toolbar-large .x-btn-arrow-right { + padding-right: 36px; +} +.x-btn-default-toolbar-large .x-btn-arrow-bottom { + padding-bottom: 32px; +} +.x-btn-default-toolbar-large .x-btn-glyph { + font-size: 32px; + line-height: 32px; + color: #333333; +} +.x-ie8m .x-btn-default-toolbar-large .x-btn-glyph { + color: #333333; +} + +.x-btn-default-toolbar-large-disabled { + background-image: none; + background-color: #f5f5f5; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-default-toolbar-large-icon .x-btn-button, +.x-btn-default-toolbar-large-noicon .x-btn-button { + height: 32px; +} +.x-btn-default-toolbar-large-icon .x-btn-inner, +.x-btn-default-toolbar-large-noicon .x-btn-inner { + line-height: 32px; +} + +.x-btn-default-toolbar-large-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-toolbar-large-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-toolbar-large-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-default-toolbar-large-icon .x-btn-inner { + width: 32px; + padding: 0; +} +.x-btn-default-toolbar-large-icon .x-btn-icon-el { + width: 32px; + height: 32px; +} + +.x-btn-default-toolbar-large-icon-text-left .x-btn-button { + height: 32px; +} +.x-btn-default-toolbar-large-icon-text-left .x-btn-inner { + line-height: 32px; + padding-left: 37px; +} +.x-btn-default-toolbar-large-icon-text-left .x-btn-icon-el { + width: 32px; + right: auto; +} +.x-ie6 .x-btn-default-toolbar-large-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-toolbar-large-icon-text-left .x-btn-icon-el { + height: 32px; +} + +.x-btn-default-toolbar-large-icon-text-right .x-btn-button { + height: 32px; +} +.x-btn-default-toolbar-large-icon-text-right .x-btn-inner { + line-height: 32px; + padding-right: 37px; +} +.x-btn-default-toolbar-large-icon-text-right .x-btn-icon-el { + width: 32px; + left: auto; +} +.x-ie6 .x-btn-default-toolbar-large-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-toolbar-large-icon-text-right .x-btn-icon-el { + height: 32px; +} + +.x-btn-default-toolbar-large-icon-text-top .x-btn-inner { + padding-top: 37px; +} +.x-btn-default-toolbar-large-icon-text-top .x-btn-icon-el { + height: 32px; + bottom: auto; +} +.x-ie6 .x-btn-default-toolbar-large-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-toolbar-large-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-toolbar-large-icon-text-bottom .x-btn-inner { + padding-bottom: 37px; +} +.x-btn-default-toolbar-large-icon-text-bottom .x-btn-icon-el { + height: 32px; + top: auto; +} +.x-ie6 .x-btn-default-toolbar-large-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-toolbar-large-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-toolbar-large-focus { + border-color: #96caee; + background-image: none; + background-color: #ebebeb; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-default-toolbar-large-over { + border-color: #d8d8d8; + background-image: none; + background-color: #ebebeb; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #cbcbcb), color-stop(100%, #b8b8b8)); + background-image: -webkit-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -moz-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -o-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -ms-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: linear-gradient(top, #cbcbcb, #b8b8b8); +} + +.x-btn-default-toolbar-large-menu-active, +.x-btn-default-toolbar-large-pressed { + border-color: #cecece; + background-image: none; + background-color: #e1e1e1; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #919191), color-stop(100%, #808080)); + background-image: -webkit-linear-gradient(top, #919191, #808080); + background-image: -moz-linear-gradient(top, #919191, #808080); + background-image: -o-linear-gradient(top, #919191, #808080); + background-image: -ms-linear-gradient(top, #919191, #808080); + background-image: linear-gradient(top, #919191, #808080); +} +.x-btn-default-toolbar-large-menu-active .x-btn-inner, +.x-btn-default-toolbar-large-pressed .x-btn-inner { + color: white; +} + +.x-btn-default-toolbar-large-focus .x-frame-tl, +.x-btn-default-toolbar-large-focus .x-frame-bl, +.x-btn-default-toolbar-large-focus .x-frame-tr, +.x-btn-default-toolbar-large-focus .x-frame-br, +.x-btn-default-toolbar-large-focus .x-frame-tc, +.x-btn-default-toolbar-large-focus .x-frame-bc { + background-image: url(images/btn/btn-default-toolbar-large-focus-corners.gif); +} +.x-btn-default-toolbar-large-focus .x-frame-ml, +.x-btn-default-toolbar-large-focus .x-frame-mr { + background-image: url(images/btn/btn-default-toolbar-large-focus-sides.gif); +} +.x-btn-default-toolbar-large-focus .x-frame-mc { + background-color: #ebebeb; + background-image: url(images/btn/btn-default-toolbar-large-focus-fbg.gif); +} + +.x-btn-default-toolbar-large-over .x-frame-tl, +.x-btn-default-toolbar-large-over .x-frame-bl, +.x-btn-default-toolbar-large-over .x-frame-tr, +.x-btn-default-toolbar-large-over .x-frame-br, +.x-btn-default-toolbar-large-over .x-frame-tc, +.x-btn-default-toolbar-large-over .x-frame-bc { + background-image: url(images/btn/btn-default-toolbar-large-over-corners.gif); +} +.x-btn-default-toolbar-large-over .x-frame-ml, +.x-btn-default-toolbar-large-over .x-frame-mr { + background-image: url(images/btn/btn-default-toolbar-large-over-sides.gif); +} +.x-btn-default-toolbar-large-over .x-frame-mc { + background-color: #ebebeb; + background-image: url(images/btn/btn-default-toolbar-large-over-fbg.gif); +} + +.x-btn-default-toolbar-large-menu-active .x-frame-tl, +.x-btn-default-toolbar-large-menu-active .x-frame-bl, +.x-btn-default-toolbar-large-menu-active .x-frame-tr, +.x-btn-default-toolbar-large-menu-active .x-frame-br, +.x-btn-default-toolbar-large-menu-active .x-frame-tc, +.x-btn-default-toolbar-large-menu-active .x-frame-bc, +.x-btn-default-toolbar-large-pressed .x-frame-tl, +.x-btn-default-toolbar-large-pressed .x-frame-bl, +.x-btn-default-toolbar-large-pressed .x-frame-tr, +.x-btn-default-toolbar-large-pressed .x-frame-br, +.x-btn-default-toolbar-large-pressed .x-frame-tc, +.x-btn-default-toolbar-large-pressed .x-frame-bc { + background-image: url(images/btn/btn-default-toolbar-large-pressed-corners.gif); +} +.x-btn-default-toolbar-large-menu-active .x-frame-ml, +.x-btn-default-toolbar-large-menu-active .x-frame-mr, +.x-btn-default-toolbar-large-pressed .x-frame-ml, +.x-btn-default-toolbar-large-pressed .x-frame-mr { + background-image: url(images/btn/btn-default-toolbar-large-pressed-sides.gif); +} +.x-btn-default-toolbar-large-menu-active .x-frame-mc, +.x-btn-default-toolbar-large-pressed .x-frame-mc { + background-color: #e1e1e1; + background-image: url(images/btn/btn-default-toolbar-large-pressed-fbg.gif); +} + +.x-btn-default-toolbar-large-disabled .x-frame-tl, +.x-btn-default-toolbar-large-disabled .x-frame-bl, +.x-btn-default-toolbar-large-disabled .x-frame-tr, +.x-btn-default-toolbar-large-disabled .x-frame-br, +.x-btn-default-toolbar-large-disabled .x-frame-tc, +.x-btn-default-toolbar-large-disabled .x-frame-bc { + background-image: url(images/btn/btn-default-toolbar-large-disabled-corners.gif); +} +.x-btn-default-toolbar-large-disabled .x-frame-ml, +.x-btn-default-toolbar-large-disabled .x-frame-mr { + background-image: url(images/btn/btn-default-toolbar-large-disabled-sides.gif); +} +.x-btn-default-toolbar-large-disabled .x-frame-mc { + background-color: #f5f5f5; + background-image: url(images/btn/btn-default-toolbar-large-disabled-fbg.gif); +} + +.x-nlg .x-btn-default-toolbar-large-focus { + background-image: url(images/btn/btn-default-toolbar-large-focus-bg.gif); +} + +.x-nlg .x-btn-default-toolbar-large-over { + background-image: url(images/btn/btn-default-toolbar-large-over-bg.gif); +} + +.x-nlg .x-btn-default-toolbar-large-menu-active, +.x-nlg .x-btn-default-toolbar-large-pressed { + background-image: url(images/btn/btn-default-toolbar-large-pressed-bg.gif); +} + +.x-nlg .x-btn-default-toolbar-large-disabled { + background-image: url(images/btn/btn-default-toolbar-large-disabled-bg.gif); +} + +.x-nbr .x-btn-default-toolbar-large { + background-image: none; +} + +.x-btn-default-toolbar-large .x-btn-split-right { + background-image: url(images/button/default-toolbar-large-s-arrow.png); + padding-right: 38px; +} +.x-btn-default-toolbar-large .x-btn-split-bottom { + background-image: url(images/button/default-toolbar-large-s-arrow-b.png); + padding-bottom: 34px; +} + +.x-btn-default-toolbar-large-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-default-toolbar-large-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-large-focus-corners.gif), sides:url(images/btn/btn-default-toolbar-large-focus-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-large-focus-fbg.gif), bg:url(images/btn/btn-default-toolbar-large-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-toolbar-large-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-large-over-corners.gif), sides:url(images/btn/btn-default-toolbar-large-over-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-large-over-fbg.gif), bg:url(images/btn/btn-default-toolbar-large-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-toolbar-large-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-large-pressed-corners.gif), sides:url(images/btn/btn-default-toolbar-large-pressed-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-large-pressed-fbg.gif), bg:url(images/btn/btn-default-toolbar-large-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-toolbar-large-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-large-disabled-corners.gif), sides:url(images/btn/btn-default-toolbar-large-disabled-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-large-disabled-fbg.gif), bg:url(images/btn/btn-default-toolbar-large-disabled-bg.gif)"; +} + +/**/ +/* */ +.x-btn-icon-text-left .x-btn-icon-el { + background-position: left center; +} + +.x-btn-icon-text-right .x-btn-icon-el { + background-position: right center; +} + +.x-btn-icon-text-top .x-btn-icon-el { + background-position: center top; +} + +.x-btn-icon-text-bottom .x-btn-icon-el { + background-position: center bottom; +} + +.x-btn-arrow-right { + background-position: right center; +} + +.x-btn-arrow-bottom { + background-position: center bottom; +} + +.x-btn-arrow { + background-repeat: no-repeat; +} + +.x-btn-split { + display: block; + background-repeat: no-repeat; +} + +.x-btn-split-right { + background-position: right center; +} + +.x-btn-split-bottom { + background-position: center bottom; +} + +.x-cycle-fixed-width .x-btn-inner { + text-align: inherit; +} + +/** + * Creates a visual theme for a Toolbar. + * @param {String} $ui + * The name of the UI + * + * @param {color} [$background-color=$toolbar-background-color] + * The background color of the toolbar + * + * @param {string/list} [$background-gradient=$toolbar-background-gradient] + * The background gradient of the toolbar + * + * @param {color} [$border-color=$toolbar-border-color] + * The border color of the toolbar + * + * @param {number} [$border-width=$toolbar-border-width] + * The border-width of the toolbar + * + * @param {string} [$scroller-cursor=$toolbar-scroller-cursor] + * The cursor of Toolbar scrollers + * + * @param {string} [$scroller-cursor-disabled=$toolbar-scroller-cursor-disabled] + * The cursor of disabled Toolbar scrollers + * + * @param {number} [$scroller-opacity-disabled=$toolbar-scroller-opacity-disabled] + * The opacity of disabled Toolbar scrollers + * + * @param {string} [$tool-background-image=$toolbar-tool-background-image] + * The sprite to use for {@link Ext.panel.Tool Tools} on a Toolbar + * + * @member Ext.toolbar.Toolbar + */ +.x-toolbar { + font-size: 13px; + border-style: solid; + padding: 6px 0 6px 8px; +} + +.x-toolbar-item { + margin: 0 8px 0 0; +} + +.x-toolbar-text { + margin: 0 6px 0 4px; + color: #303030; + line-height: 16px; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + font-weight: normal; +} + +.x-toolbar-separator-horizontal { + margin: 0 8px 0 0; + height: 14px; + border-style: solid; + border-width: 0 0 0 1px; + border-left-color: #e1e1e1; + border-right-color: white; +} + +.x-toolbar-footer { + background: transparent; + border: 0; + margin: 0; + padding: 6px 0 6px 6px; +} +.x-toolbar-footer .x-toolbar-item { + margin: 0 6px 0 0; +} + +.x-toolbar-spacer { + width: 2px; +} + +.x-toolbar-more-icon { + background-image: url(images/toolbar/more.png) !important; + background-position: center center !important; + background-repeat: no-repeat; +} + +.x-toolbar-default { + border-color: silver; + border-width: 1px; + background-image: none; + background-color: transparent; +} +.x-toolbar-default .x-box-scroller { + cursor: pointer; +} +.x-toolbar-default .x-box-scroller-disabled { + cursor: default; +} +.x-toolbar-default .x-tool-img { + background-image: url(images/tools/tool-sprites-dark.png); + background-color: transparent; +} + +.x-toolbar-scroll-left { + background-image: url(images/toolbar/scroll-left.png); + background-position: 0 0; + width: 16px; + height: 16px; + border-style: solid; + border-color: #8db2e3; + border-width: 0; + margin-top: 4px; +} + +.x-toolbar-scroll-left-hover { + background-position: 0 0; +} + +.x-toolbar-scroll-right { + background-image: url(images/toolbar/scroll-right.png); + width: 16px; + height: 16px; + border-style: solid; + border-color: #8db2e3; + border-width: 0; + margin-top: 4px; +} + +.x-toolbar-scroll-right-hover { + background-position: -16px 0; +} + +.x-toolbar .x-box-menu-after { + margin: 0 8px; +} + +.x-toolbar-vertical { + padding: 6px 8px 0 8px; +} +.x-toolbar-vertical .x-toolbar-item { + margin: 0 0 6px 0; +} +.x-toolbar-vertical .x-toolbar-text { + margin: 4px 0 6px 0; +} +.x-toolbar-vertical .x-toolbar-separator-vertical { + margin: 0 5px 6px; + border-style: solid none; + border-width: 1px 0 0; + border-top-color: #e1e1e1; + border-bottom-color: white; +} +.x-toolbar-vertical .x-box-menu-after { + margin: 6px 0; +} + +.x-header-draggable .x-header-body, +.x-header-ghost { + cursor: move; +} + +.x-header-text { + white-space: nowrap; +} + +/** + * Creates a visual theme for a Panel + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {color} [$ui-border-color=$panel-border-color] + * The border-color of the Panel + * + * @param {number} [$ui-border-radius=$panel-border-radius] + * The border-radius of the Panel + * + * @param {number} [$ui-border-width=$panel-border-width] + * The border-width of the Panel + * + * @param {number} [$ui-padding=$panel-padding] + * The padding of the Panel + * + * @param {color} [$ui-header-color=$panel-header-color] + * The text color of the Header + * + * @param {string} [$ui-header-font-family=$panel-header-font-family] + * The font-family of the Header + * + * @param {number} [$ui-header-font-size=$panel-header-font-size] + * The font-size of the Header + * + * @param {string} [$ui-header-font-weight=$panel-header-font-weight] + * The font-weight of the Header + * + * @param {number} [$ui-header-line-height=$panel-header-line-height] + * The line-height of the Header + * + * @param {color} [$ui-header-border-color=$panel-header-border-color] + * The border-color of the Header + * + * @param {number} [$ui-header-border-width=$panel-header-border-width] + * The border-width of the Header + * + * @param {string} [$ui-header-border-style=$panel-header-border-style] + * The border-style of the Header + * + * @param {color} [$ui-header-background-color=$panel-header-background-color] + * The background-color of the Header + * + * @param {string/list} [$ui-header-background-gradient=$panel-header-background-gradient] + * The background-gradient of the Header. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for {@link Global_CSS#background-gradient}. + * + * @param {color} [$ui-header-inner-border-color=$panel-header-inner-border-color] + * The inner border-color of the Header + * + * @param {number} [$ui-header-inner-border-width=$panel-header-inner-border-width] + * The inner border-width of the Header + * + * @param {number/list} [$ui-header-text-padding=$panel-header-text-padding] + * The padding of the Header's text element + * + * @param {string} [$ui-header-text-transform=$panel-header-text-transform] + * The text-transform of the Header + * + * @param {number/list} [$ui-header-padding=$panel-header-padding] + * The padding of the Header + * + * @param {number} [$ui-header-icon-width=$panel-header-icon-width] + * The width of the Header icon + * + * @param {number} [$ui-header-icon-height=$panel-header-icon-height] + * The height of the Header icon + * + * @param {number} [$ui-header-icon-spacing=$panel-header-icon-spacing] + * The space between the Header icon and text + * + * @param {list} [$ui-header-icon-background-position=$panel-header-icon-background-position] + * The background-position of the Header icon + * + * @param {color} [$ui-header-glyph-color=$panel-header-glyph-color] + * The color of the Header glyph icon + * + * @param {number} [$ui-header-glyph-opacity=$panel-header-glyph-opacity] + * The opacity of the Header glyph icon + * + * @param {number} [$ui-tool-spacing=$panel-tool-spacing] + * The space between the Panel {@link Ext.panel.Tool Tools} + * + * @param {string} [$ui-tool-background-image=$panel-tool-background-image] + * The background sprite to use for Panel {@link Ext.panel.Tool Tools} + * + * @param {color} [$ui-body-color=$panel-body-color] + * The color of text inside the Panel body + * + * @param {color} [$ui-body-border-color=$panel-body-border-color] + * The border-color of the Panel body + * + * @param {number} [$ui-body-border-width=$panel-body-border-width] + * The border-width of the Panel body + * + * @param {string} [$ui-body-border-style=$panel-body-border-style] + * The border-style of the Panel body + * + * @param {color} [$ui-body-background-color=$panel-body-background-color] + * The background-color of the Panel body + * + * @param {number} [$ui-body-font-size=$panel-body-font-size] + * The font-size of the Panel body + * + * @param {string} [$ui-body-font-weight=$panel-body-font-weight] + * The font-weight of the Panel body + * + * @param {string} [$ui-background-stretch-top=$panel-background-stretch-top] + * The direction to strech the background-gradient of top docked Headers when slicing images + * for IE using Sencha Cmd + * + * @param {string} [$ui-background-stretch-bottom=$panel-background-stretch-bottom] + * The direction to strech the background-gradient of bottom docked Headers when slicing images + * for IE using Sencha Cmd + * + * @param {string} [$ui-background-stretch-right=$panel-background-stretch-right] + * The direction to strech the background-gradient of right docked Headers when slicing images + * for IE using Sencha Cmd + * + * @param {string} [$ui-background-stretch-left=$panel-background-stretch-left] + * The direction to strech the background-gradient of left docked Headers when slicing images + * for IE using Sencha Cmd + * + * @param {boolean} [$ui-include-border-management-rules=$panel-include-border-management-rules] + * True to include neptune style border management rules. + * + * @param {color} [$ui-wrap-border-color=$panel-wrap-border-color] + * The color to apply to the border that wraps the body and docked items in a framed + * panel. The presence of the wrap border in a framed panel is controlled by the + * {@link #border} config. Only applicable when `$ui-include-border-management-rules` is + * `true`. + * + * @param {color} [$ui-wrap-border-width=$panel-wrap-border-width] + * The width to apply to the border that wraps the body and docked items in a framed + * panel. The presence of the wrap border in a framed panel is controlled by the + * {@link #border} config. Only applicable when `$ui-include-border-management-rules` is + * `true`. + * + * @member Ext.panel.Panel + */ +.x-panel-ghost { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +.x-panel-default { + border-color: #444444; + padding: 0; +} + +.x-panel-header-default { + font-size: 13px; + border: 1px solid #444444; +} +.x-panel-header-default .x-tool-img { + background-color: #444444; +} + +.x-panel-header-default-horizontal { + padding: 9px 9px 10px 9px; +} + +.x-panel-header-default-horizontal-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-default-vertical { + padding: 9px 9px 9px 10px; +} + +.x-panel-header-default-vertical-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-text-container-default { + color: white; + font-size: 13px; + font-weight: bold; + font-family: arial, helvetica, verdana, sans-serif; + line-height: 15px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-default { + background: transparent; + border-color: #444444; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-header-default { + background-image: none; + background-color: #444444; +} + +.x-panel-header-default-vertical { + background-image: none; + background-color: #444444; +} + +.x-panel .x-panel-header-default-collapsed-border-top { + border-bottom-width: 1px !important; +} +.x-panel .x-panel-header-default-collapsed-border-right { + border-left-width: 1px !important; +} +.x-panel .x-panel-header-default-collapsed-border-bottom { + border-top-width: 1px !important; +} +.x-panel .x-panel-header-default-collapsed-border-left { + border-right-width: 1px !important; +} + +/**/ +.x-panel-header-default-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-default-bottom:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-default-left:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +/**/ +.x-panel-header-default-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-panel-header-default-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-default-vertical .x-panel-header-text-container { + background-color: #444444; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#444444); +} + +.x-panel-header-default .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-default .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-default .x-panel-header-glyph { + color: #a1a1a1; +} + +.x-panel-header-default-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-default-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-default-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-default-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-default-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-default-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-default-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-default-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-default-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-default-outer-border-l { + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-outer-border-b { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-default-outer-border-bl { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-outer-border-r { + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-default-outer-border-rl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-outer-border-rb { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-default-outer-border-rbl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-outer-border-t { + border-top-color: #444444 !important; + border-top-width: 1px !important; +} + +.x-panel-default-outer-border-tl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-outer-border-tb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-default-outer-border-tbl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-outer-border-tr { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-default-outer-border-trl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-outer-border-trb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-default-outer-border-trbl { + border-color: #444444 !important; + border-width: 1px !important; +} + +.x-panel-default-framed { + border-color: #444444; + padding: 0; +} + +.x-panel-header-default-framed { + font-size: 13px; + border: 2px solid #444444; +} +.x-panel-header-default-framed .x-tool-img { + background-color: #444444; +} + +.x-panel-header-default-framed-horizontal { + padding: 5px; +} + +.x-panel-header-default-framed-horizontal-noborder { + padding: 7px 7px 5px 7px; +} + +.x-panel-header-default-framed-vertical { + padding: 5px 5px 5px 5px; +} + +.x-panel-header-default-framed-vertical-noborder { + padding: 7px 7px 7px 5px; +} + +.x-panel-header-text-container-default-framed { + color: white; + font-size: 13px; + font-weight: bold; + font-family: arial, helvetica, verdana, sans-serif; + line-height: 15px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-default-framed { + background: white; + border-color: #444444; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-default-framed { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 2px 2px 2px 2px; + border-width: 2px; + border-style: solid; + background-color: white; +} + +.x-panel-default-framed-mc { + background-color: white; +} + +.x-nbr .x-panel-default-framed { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-default-framed-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-2-2-2-2; +} + +.x-panel-default-framed-tl { + background-position: 0 -8px; +} + +.x-panel-default-framed-tr { + background-position: right -12px; +} + +.x-panel-default-framed-bl { + background-position: 0 -16px; +} + +.x-panel-default-framed-br { + background-position: right -20px; +} + +.x-panel-default-framed-ml { + background-position: 0 top; +} + +.x-panel-default-framed-mr { + background-position: right top; +} + +.x-panel-default-framed-tc { + background-position: 0 0; +} + +.x-panel-default-framed-bc { + background-position: 0 -4px; +} + +.x-panel-default-framed-tr, +.x-panel-default-framed-br, +.x-panel-default-framed-mr { + padding-right: 4px; +} + +.x-panel-default-framed-tl, +.x-panel-default-framed-bl, +.x-panel-default-framed-ml { + padding-left: 4px; +} + +.x-panel-default-framed-tc { + height: 4px; +} + +.x-panel-default-framed-bc { + height: 4px; +} + +.x-panel-default-framed-tl, +.x-panel-default-framed-bl, +.x-panel-default-framed-tr, +.x-panel-default-framed-br, +.x-panel-default-framed-tc, +.x-panel-default-framed-bc, +.x-panel-default-framed-ml, +.x-panel-default-framed-mr { + zoom: 1; + background-image: url(images/panel/panel-default-framed-corners.gif); +} + +.x-panel-default-framed-ml, +.x-panel-default-framed-mr { + zoom: 1; + background-image: url(images/panel/panel-default-framed-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-default-framed-mc { + padding: 0px 0px 0px 0px; +} + +.x-strict .x-ie7 .x-panel-default-framed-tl, +.x-strict .x-ie7 .x-panel-default-framed-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-default-framed:after { + display: none; + content: "x-slicer:corners:url(images/panel/panel-default-framed-corners.gif), sides:url(images/panel/panel-default-framed-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-default-framed-top { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 5px 5px 5px 5px; + border-width: 2px 2px 0 2px; + border-style: solid; + background-color: #444444; +} + +.x-panel-header-default-framed-top-mc { + background-color: #444444; +} + +.x-nbr .x-panel-header-default-framed-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-default-framed-top-frameInfo { + font-family: dh-4-4-0-0-2-2-0-2-5-5-5-5; +} + +.x-panel-header-default-framed-top-tl { + background-position: 0 -8px; +} + +.x-panel-header-default-framed-top-tr { + background-position: right -12px; +} + +.x-panel-header-default-framed-top-bl { + background-position: 0 -16px; +} + +.x-panel-header-default-framed-top-br { + background-position: right -20px; +} + +.x-panel-header-default-framed-top-ml { + background-position: 0 top; +} + +.x-panel-header-default-framed-top-mr { + background-position: right top; +} + +.x-panel-header-default-framed-top-tc { + background-position: 0 0; +} + +.x-panel-header-default-framed-top-bc { + background-position: 0 -4px; +} + +.x-panel-header-default-framed-top-tr, +.x-panel-header-default-framed-top-br, +.x-panel-header-default-framed-top-mr { + padding-right: 4px; +} + +.x-panel-header-default-framed-top-tl, +.x-panel-header-default-framed-top-bl, +.x-panel-header-default-framed-top-ml { + padding-left: 4px; +} + +.x-panel-header-default-framed-top-tc { + height: 4px; +} + +.x-panel-header-default-framed-top-bc { + height: 0; +} + +.x-panel-header-default-framed-top-tl, +.x-panel-header-default-framed-top-bl, +.x-panel-header-default-framed-top-tr, +.x-panel-header-default-framed-top-br, +.x-panel-header-default-framed-top-tc, +.x-panel-header-default-framed-top-bc, +.x-panel-header-default-framed-top-ml, +.x-panel-header-default-framed-top-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-top-corners.gif); +} + +.x-panel-header-default-framed-top-ml, +.x-panel-header-default-framed-top-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-top-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-default-framed-top-mc { + padding: 3px 3px 5px 3px; +} + +.x-strict .x-ie7 .x-panel-header-default-framed-top-tl, +.x-strict .x-ie7 .x-panel-header-default-framed-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-default-framed-top:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-default-framed-top-corners.gif), sides:url(images/panel-header/panel-header-default-framed-top-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-default-framed-right { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 5px 5px 5px 5px; + border-width: 2px 2px 2px 0; + border-style: solid; + background-color: #444444; +} + +.x-panel-header-default-framed-right-mc { + background-color: #444444; +} + +.x-nbr .x-panel-header-default-framed-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-default-framed-right-frameInfo { + font-family: dh-0-4-4-0-2-2-2-0-5-5-5-5; +} + +.x-panel-header-default-framed-right-tl { + background-position: 0 -8px; +} + +.x-panel-header-default-framed-right-tr { + background-position: right -12px; +} + +.x-panel-header-default-framed-right-bl { + background-position: 0 -16px; +} + +.x-panel-header-default-framed-right-br { + background-position: right -20px; +} + +.x-panel-header-default-framed-right-ml { + background-position: 0 right; +} + +.x-panel-header-default-framed-right-mr { + background-position: right right; +} + +.x-panel-header-default-framed-right-tc { + background-position: 0 0; +} + +.x-panel-header-default-framed-right-bc { + background-position: 0 -4px; +} + +.x-panel-header-default-framed-right-tr, +.x-panel-header-default-framed-right-br, +.x-panel-header-default-framed-right-mr { + padding-right: 4px; +} + +.x-panel-header-default-framed-right-tl, +.x-panel-header-default-framed-right-bl, +.x-panel-header-default-framed-right-ml { + padding-left: 0; +} + +.x-panel-header-default-framed-right-tc { + height: 4px; +} + +.x-panel-header-default-framed-right-bc { + height: 4px; +} + +.x-panel-header-default-framed-right-tl, +.x-panel-header-default-framed-right-bl, +.x-panel-header-default-framed-right-tr, +.x-panel-header-default-framed-right-br, +.x-panel-header-default-framed-right-tc, +.x-panel-header-default-framed-right-bc, +.x-panel-header-default-framed-right-ml, +.x-panel-header-default-framed-right-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-right-corners.gif); +} + +.x-panel-header-default-framed-right-ml, +.x-panel-header-default-framed-right-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-right-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-default-framed-right-mc { + padding: 3px 3px 3px 5px; +} + +.x-strict .x-ie7 .x-panel-header-default-framed-right-tl, +.x-strict .x-ie7 .x-panel-header-default-framed-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-default-framed-right:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-default-framed-right-corners.gif), sides:url(images/panel-header/panel-header-default-framed-right-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-default-framed-bottom { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 5px 5px 5px 5px; + border-width: 0 2px 2px 2px; + border-style: solid; + background-color: #444444; +} + +.x-panel-header-default-framed-bottom-mc { + background-color: #444444; +} + +.x-nbr .x-panel-header-default-framed-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-default-framed-bottom-frameInfo { + font-family: dh-0-0-4-4-0-2-2-2-5-5-5-5; +} + +.x-panel-header-default-framed-bottom-tl { + background-position: 0 -8px; +} + +.x-panel-header-default-framed-bottom-tr { + background-position: right -12px; +} + +.x-panel-header-default-framed-bottom-bl { + background-position: 0 -16px; +} + +.x-panel-header-default-framed-bottom-br { + background-position: right -20px; +} + +.x-panel-header-default-framed-bottom-ml { + background-position: 0 bottom; +} + +.x-panel-header-default-framed-bottom-mr { + background-position: right bottom; +} + +.x-panel-header-default-framed-bottom-tc { + background-position: 0 0; +} + +.x-panel-header-default-framed-bottom-bc { + background-position: 0 -4px; +} + +.x-panel-header-default-framed-bottom-tr, +.x-panel-header-default-framed-bottom-br, +.x-panel-header-default-framed-bottom-mr { + padding-right: 4px; +} + +.x-panel-header-default-framed-bottom-tl, +.x-panel-header-default-framed-bottom-bl, +.x-panel-header-default-framed-bottom-ml { + padding-left: 4px; +} + +.x-panel-header-default-framed-bottom-tc { + height: 0; +} + +.x-panel-header-default-framed-bottom-bc { + height: 4px; +} + +.x-panel-header-default-framed-bottom-tl, +.x-panel-header-default-framed-bottom-bl, +.x-panel-header-default-framed-bottom-tr, +.x-panel-header-default-framed-bottom-br, +.x-panel-header-default-framed-bottom-tc, +.x-panel-header-default-framed-bottom-bc, +.x-panel-header-default-framed-bottom-ml, +.x-panel-header-default-framed-bottom-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-bottom-corners.gif); +} + +.x-panel-header-default-framed-bottom-ml, +.x-panel-header-default-framed-bottom-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-default-framed-bottom-mc { + padding: 5px 3px 3px 3px; +} + +.x-strict .x-ie7 .x-panel-header-default-framed-bottom-tl, +.x-strict .x-ie7 .x-panel-header-default-framed-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-default-framed-bottom:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-default-framed-bottom-corners.gif), sides:url(images/panel-header/panel-header-default-framed-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-default-framed-left { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 5px 5px 5px 5px; + border-width: 2px 0 2px 2px; + border-style: solid; + background-color: #444444; +} + +.x-panel-header-default-framed-left-mc { + background-color: #444444; +} + +.x-nbr .x-panel-header-default-framed-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-default-framed-left-frameInfo { + font-family: dh-4-0-0-4-2-0-2-2-5-5-5-5; +} + +.x-panel-header-default-framed-left-tl { + background-position: 0 -8px; +} + +.x-panel-header-default-framed-left-tr { + background-position: right -12px; +} + +.x-panel-header-default-framed-left-bl { + background-position: 0 -16px; +} + +.x-panel-header-default-framed-left-br { + background-position: right -20px; +} + +.x-panel-header-default-framed-left-ml { + background-position: 0 left; +} + +.x-panel-header-default-framed-left-mr { + background-position: right left; +} + +.x-panel-header-default-framed-left-tc { + background-position: 0 0; +} + +.x-panel-header-default-framed-left-bc { + background-position: 0 -4px; +} + +.x-panel-header-default-framed-left-tr, +.x-panel-header-default-framed-left-br, +.x-panel-header-default-framed-left-mr { + padding-right: 0; +} + +.x-panel-header-default-framed-left-tl, +.x-panel-header-default-framed-left-bl, +.x-panel-header-default-framed-left-ml { + padding-left: 4px; +} + +.x-panel-header-default-framed-left-tc { + height: 4px; +} + +.x-panel-header-default-framed-left-bc { + height: 4px; +} + +.x-panel-header-default-framed-left-tl, +.x-panel-header-default-framed-left-bl, +.x-panel-header-default-framed-left-tr, +.x-panel-header-default-framed-left-br, +.x-panel-header-default-framed-left-tc, +.x-panel-header-default-framed-left-bc, +.x-panel-header-default-framed-left-ml, +.x-panel-header-default-framed-left-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-left-corners.gif); +} + +.x-panel-header-default-framed-left-ml, +.x-panel-header-default-framed-left-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-left-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-default-framed-left-mc { + padding: 3px 5px 3px 3px; +} + +.x-strict .x-ie7 .x-panel-header-default-framed-left-tl, +.x-strict .x-ie7 .x-panel-header-default-framed-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-default-framed-left:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-default-framed-left-corners.gif), sides:url(images/panel-header/panel-header-default-framed-left-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-default-framed-collapsed-top { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 5px 5px 5px 5px; + border-width: 2px; + border-style: solid; + background-color: #444444; +} + +.x-panel-header-default-framed-collapsed-top-mc { + background-color: #444444; +} + +.x-nbr .x-panel-header-default-framed-collapsed-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-default-framed-collapsed-top-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-5-5-5-5; +} + +.x-panel-header-default-framed-collapsed-top-tl { + background-position: 0 -8px; +} + +.x-panel-header-default-framed-collapsed-top-tr { + background-position: right -12px; +} + +.x-panel-header-default-framed-collapsed-top-bl { + background-position: 0 -16px; +} + +.x-panel-header-default-framed-collapsed-top-br { + background-position: right -20px; +} + +.x-panel-header-default-framed-collapsed-top-ml { + background-position: 0 top; +} + +.x-panel-header-default-framed-collapsed-top-mr { + background-position: right top; +} + +.x-panel-header-default-framed-collapsed-top-tc { + background-position: 0 0; +} + +.x-panel-header-default-framed-collapsed-top-bc { + background-position: 0 -4px; +} + +.x-panel-header-default-framed-collapsed-top-tr, +.x-panel-header-default-framed-collapsed-top-br, +.x-panel-header-default-framed-collapsed-top-mr { + padding-right: 4px; +} + +.x-panel-header-default-framed-collapsed-top-tl, +.x-panel-header-default-framed-collapsed-top-bl, +.x-panel-header-default-framed-collapsed-top-ml { + padding-left: 4px; +} + +.x-panel-header-default-framed-collapsed-top-tc { + height: 4px; +} + +.x-panel-header-default-framed-collapsed-top-bc { + height: 4px; +} + +.x-panel-header-default-framed-collapsed-top-tl, +.x-panel-header-default-framed-collapsed-top-bl, +.x-panel-header-default-framed-collapsed-top-tr, +.x-panel-header-default-framed-collapsed-top-br, +.x-panel-header-default-framed-collapsed-top-tc, +.x-panel-header-default-framed-collapsed-top-bc, +.x-panel-header-default-framed-collapsed-top-ml, +.x-panel-header-default-framed-collapsed-top-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-collapsed-top-corners.gif); +} + +.x-panel-header-default-framed-collapsed-top-ml, +.x-panel-header-default-framed-collapsed-top-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-collapsed-top-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-default-framed-collapsed-top-mc { + padding: 3px 3px 3px 3px; +} + +.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-top-tl, +.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-default-framed-collapsed-top:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-default-framed-collapsed-top-corners.gif), sides:url(images/panel-header/panel-header-default-framed-collapsed-top-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-default-framed-collapsed-right { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 5px 5px 5px 5px; + border-width: 2px; + border-style: solid; + background-color: #444444; +} + +.x-panel-header-default-framed-collapsed-right-mc { + background-color: #444444; +} + +.x-nbr .x-panel-header-default-framed-collapsed-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-default-framed-collapsed-right-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-5-5-5-5; +} + +.x-panel-header-default-framed-collapsed-right-tl { + background-position: 0 -8px; +} + +.x-panel-header-default-framed-collapsed-right-tr { + background-position: right -12px; +} + +.x-panel-header-default-framed-collapsed-right-bl { + background-position: 0 -16px; +} + +.x-panel-header-default-framed-collapsed-right-br { + background-position: right -20px; +} + +.x-panel-header-default-framed-collapsed-right-ml { + background-position: 0 right; +} + +.x-panel-header-default-framed-collapsed-right-mr { + background-position: right right; +} + +.x-panel-header-default-framed-collapsed-right-tc { + background-position: 0 0; +} + +.x-panel-header-default-framed-collapsed-right-bc { + background-position: 0 -4px; +} + +.x-panel-header-default-framed-collapsed-right-tr, +.x-panel-header-default-framed-collapsed-right-br, +.x-panel-header-default-framed-collapsed-right-mr { + padding-right: 4px; +} + +.x-panel-header-default-framed-collapsed-right-tl, +.x-panel-header-default-framed-collapsed-right-bl, +.x-panel-header-default-framed-collapsed-right-ml { + padding-left: 4px; +} + +.x-panel-header-default-framed-collapsed-right-tc { + height: 4px; +} + +.x-panel-header-default-framed-collapsed-right-bc { + height: 4px; +} + +.x-panel-header-default-framed-collapsed-right-tl, +.x-panel-header-default-framed-collapsed-right-bl, +.x-panel-header-default-framed-collapsed-right-tr, +.x-panel-header-default-framed-collapsed-right-br, +.x-panel-header-default-framed-collapsed-right-tc, +.x-panel-header-default-framed-collapsed-right-bc, +.x-panel-header-default-framed-collapsed-right-ml, +.x-panel-header-default-framed-collapsed-right-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-collapsed-right-corners.gif); +} + +.x-panel-header-default-framed-collapsed-right-ml, +.x-panel-header-default-framed-collapsed-right-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-collapsed-right-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-default-framed-collapsed-right-mc { + padding: 3px 3px 3px 3px; +} + +.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-right-tl, +.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-default-framed-collapsed-right:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-default-framed-collapsed-right-corners.gif), sides:url(images/panel-header/panel-header-default-framed-collapsed-right-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-default-framed-collapsed-bottom { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 5px 5px 5px 5px; + border-width: 2px; + border-style: solid; + background-color: #444444; +} + +.x-panel-header-default-framed-collapsed-bottom-mc { + background-color: #444444; +} + +.x-nbr .x-panel-header-default-framed-collapsed-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-default-framed-collapsed-bottom-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-5-5-5-5; +} + +.x-panel-header-default-framed-collapsed-bottom-tl { + background-position: 0 -8px; +} + +.x-panel-header-default-framed-collapsed-bottom-tr { + background-position: right -12px; +} + +.x-panel-header-default-framed-collapsed-bottom-bl { + background-position: 0 -16px; +} + +.x-panel-header-default-framed-collapsed-bottom-br { + background-position: right -20px; +} + +.x-panel-header-default-framed-collapsed-bottom-ml { + background-position: 0 bottom; +} + +.x-panel-header-default-framed-collapsed-bottom-mr { + background-position: right bottom; +} + +.x-panel-header-default-framed-collapsed-bottom-tc { + background-position: 0 0; +} + +.x-panel-header-default-framed-collapsed-bottom-bc { + background-position: 0 -4px; +} + +.x-panel-header-default-framed-collapsed-bottom-tr, +.x-panel-header-default-framed-collapsed-bottom-br, +.x-panel-header-default-framed-collapsed-bottom-mr { + padding-right: 4px; +} + +.x-panel-header-default-framed-collapsed-bottom-tl, +.x-panel-header-default-framed-collapsed-bottom-bl, +.x-panel-header-default-framed-collapsed-bottom-ml { + padding-left: 4px; +} + +.x-panel-header-default-framed-collapsed-bottom-tc { + height: 4px; +} + +.x-panel-header-default-framed-collapsed-bottom-bc { + height: 4px; +} + +.x-panel-header-default-framed-collapsed-bottom-tl, +.x-panel-header-default-framed-collapsed-bottom-bl, +.x-panel-header-default-framed-collapsed-bottom-tr, +.x-panel-header-default-framed-collapsed-bottom-br, +.x-panel-header-default-framed-collapsed-bottom-tc, +.x-panel-header-default-framed-collapsed-bottom-bc, +.x-panel-header-default-framed-collapsed-bottom-ml, +.x-panel-header-default-framed-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-collapsed-bottom-corners.gif); +} + +.x-panel-header-default-framed-collapsed-bottom-ml, +.x-panel-header-default-framed-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-collapsed-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-default-framed-collapsed-bottom-mc { + padding: 3px 3px 3px 3px; +} + +.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-bottom-tl, +.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-default-framed-collapsed-bottom:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-default-framed-collapsed-bottom-corners.gif), sides:url(images/panel-header/panel-header-default-framed-collapsed-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-default-framed-collapsed-left { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 5px 5px 5px 5px; + border-width: 2px; + border-style: solid; + background-color: #444444; +} + +.x-panel-header-default-framed-collapsed-left-mc { + background-color: #444444; +} + +.x-nbr .x-panel-header-default-framed-collapsed-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-default-framed-collapsed-left-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-5-5-5-5; +} + +.x-panel-header-default-framed-collapsed-left-tl { + background-position: 0 -8px; +} + +.x-panel-header-default-framed-collapsed-left-tr { + background-position: right -12px; +} + +.x-panel-header-default-framed-collapsed-left-bl { + background-position: 0 -16px; +} + +.x-panel-header-default-framed-collapsed-left-br { + background-position: right -20px; +} + +.x-panel-header-default-framed-collapsed-left-ml { + background-position: 0 left; +} + +.x-panel-header-default-framed-collapsed-left-mr { + background-position: right left; +} + +.x-panel-header-default-framed-collapsed-left-tc { + background-position: 0 0; +} + +.x-panel-header-default-framed-collapsed-left-bc { + background-position: 0 -4px; +} + +.x-panel-header-default-framed-collapsed-left-tr, +.x-panel-header-default-framed-collapsed-left-br, +.x-panel-header-default-framed-collapsed-left-mr { + padding-right: 4px; +} + +.x-panel-header-default-framed-collapsed-left-tl, +.x-panel-header-default-framed-collapsed-left-bl, +.x-panel-header-default-framed-collapsed-left-ml { + padding-left: 4px; +} + +.x-panel-header-default-framed-collapsed-left-tc { + height: 4px; +} + +.x-panel-header-default-framed-collapsed-left-bc { + height: 4px; +} + +.x-panel-header-default-framed-collapsed-left-tl, +.x-panel-header-default-framed-collapsed-left-bl, +.x-panel-header-default-framed-collapsed-left-tr, +.x-panel-header-default-framed-collapsed-left-br, +.x-panel-header-default-framed-collapsed-left-tc, +.x-panel-header-default-framed-collapsed-left-bc, +.x-panel-header-default-framed-collapsed-left-ml, +.x-panel-header-default-framed-collapsed-left-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-collapsed-left-corners.gif); +} + +.x-panel-header-default-framed-collapsed-left-ml, +.x-panel-header-default-framed-collapsed-left-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-default-framed-collapsed-left-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-default-framed-collapsed-left-mc { + padding: 3px 3px 3px 3px; +} + +.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-left-tl, +.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-default-framed-collapsed-left:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-default-framed-collapsed-left-corners.gif), sides:url(images/panel-header/panel-header-default-framed-collapsed-left-sides.gif)"; +} + +/**/ +/* */ +.x-panel .x-panel-header-default-framed-top { + border-bottom-width: 2px !important; +} +.x-panel .x-panel-header-default-framed-right { + border-left-width: 2px !important; +} +.x-panel .x-panel-header-default-framed-bottom { + border-top-width: 2px !important; +} +.x-panel .x-panel-header-default-framed-left { + border-right-width: 2px !important; +} + +.x-nbr .x-panel-header-default-framed-collapsed-top { + border-bottom-width: 0 !important; +} +.x-nbr .x-panel-header-default-framed-collapsed-right { + border-left-width: 0 !important; +} +.x-nbr .x-panel-header-default-framed-collapsed-bottom { + border-top-width: 0 !important; +} +.x-nbr .x-panel-header-default-framed-collapsed-left { + border-right-width: 0 !important; +} + +.x-panel-header-default-framed-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-default-framed-vertical .x-panel-header-text-container { + background-color: #444444; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#444444); +} + +.x-panel-header-default-framed .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-default-framed .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-default-framed .x-panel-header-glyph { + color: #a1a1a1; +} + +.x-panel-header-default-framed-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-default-framed-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-default-framed-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-default-framed-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-default-framed-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-default-framed-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-default-framed-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-default-framed-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-default-framed-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-default-framed-outer-border-l { + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-framed-outer-border-b { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-default-framed-outer-border-bl { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-framed-outer-border-r { + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-default-framed-outer-border-rl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-framed-outer-border-rb { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-default-framed-outer-border-rbl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-framed-outer-border-t { + border-top-color: #444444 !important; + border-top-width: 1px !important; +} + +.x-panel-default-framed-outer-border-tl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-framed-outer-border-tb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-default-framed-outer-border-tbl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-framed-outer-border-tr { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-default-framed-outer-border-trl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-default-framed-outer-border-trb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-default-framed-outer-border-trbl { + border-color: #444444 !important; + border-width: 1px !important; +} + +/** + * Creates a visual theme for a Ext.tip.Tip + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {color} [$ui-border-color=$tip-border-color] + * The border-color of the Tip + * + * @param {number} [$ui-border-width=$tip-border-width] + * The border-width of the Tip + * + * @param {number} [$ui-border-radius=$tip-border-radius] + * The border-radius of the Tip + * + * @param {color} [$ui-background-color=$tip-background-color] + * The background-color of the Tip + * + * @param {string/list} [$ui-background-gradient=$tip-background-gradient] + * The background-gradient of the Tip. Can be either the name of a predefined gradient or a + * list of color stops. Used as the `$type` parameter for {@link Global_CSS#background-gradient}. + * + * @param {number} [$ui-tool-spacing=$tip-tool-spacing] + * The space between {@link Ext.panel.Tool Tools} in the header + * + * @param {string} [$ui-tool-background-image=$tip-tool-background-image] + * The sprite to use for the header {@link Ext.panel.Tool Tools} + * + * @param {number/list} [$ui-header-body-padding=$tip-header-body-padding] + * The padding of the Tip header's body element + * + * @param {color} [$ui-header-color=$tip-header-color] + * The text color of the Tip header + * + * @param {number} [$ui-header-font-size=$tip-header-font-size] + * The font-size of the Tip header + * + * @param {string} [$ui-header-font-weight=$tip-header-font-weight] + * The font-weight of the Tip header + * + * @param {number/list} [$ui-body-padding=$tip-body-padding] + * The padding of the Tip body + * + * @param {color} [$ui-body-color=$tip-body-color] + * The text color of the Tip body + * + * @param {number} [$ui-body-font-size=$tip-body-font-size] + * The font-size of the Tip body + * + * @param {string} [$ui-body-font-weight=$tip-body-font-weight] + * The font-weight of the Tip body + * + * @param {color} [$ui-body-link-color=$tip-body-link-color] + * The text color of any anchor tags inside the Tip body + * + * @param {number} [$ui-inner-border-width=0] + * The inner border-width of the Tip + * + * @param {color} [$ui-inner-border-color=#fff] + * The inner border-color of the Tip + * + * @member Ext.tip.Tip + */ +.x-tip-anchor { + position: absolute; + overflow: hidden; + height: 10px; + width: 10px; + border-style: solid; + border-width: 5px; + border-color: #f5c649; + zoom: 1; +} +.x-content-box .x-tip-anchor { + height: 0; + width: 0; +} + +.x-tip-anchor-top { + border-top-color: transparent; + border-left-color: transparent; + border-right-color: transparent; + _border-top-color: pink; + _border-left-color: pink; + _border-right-color: pink; + _filter: chroma(color=pink); +} + +.x-tip-anchor-bottom { + border-bottom-color: transparent; + border-left-color: transparent; + border-right-color: transparent; + _border-bottom-color: pink; + _border-left-color: pink; + _border-right-color: pink; + _filter: chroma(color=pink); +} + +.x-tip-anchor-left { + border-top-color: transparent; + border-bottom-color: transparent; + border-left-color: transparent; + _border-top-color: pink; + _border-bottom-color: pink; + _border-left-color: pink; + _filter: chroma(color=pink); +} + +.x-tip-anchor-right { + border-top-color: transparent; + border-bottom-color: transparent; + border-right-color: transparent; + _border-top-color: pink; + _border-bottom-color: pink; + _border-right-color: pink; + _filter: chroma(color=pink); +} + +.x-tip-default { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 2px 2px 2px 2px; + border-width: 1px; + border-style: solid; + background-color: #fffaee; +} + +.x-tip-default-mc { + background-color: #fffaee; +} + +.x-nbr .x-tip-default { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-tip-default-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-2-2-2-2; +} + +.x-tip-default-tl { + background-position: 0 -6px; +} + +.x-tip-default-tr { + background-position: right -9px; +} + +.x-tip-default-bl { + background-position: 0 -12px; +} + +.x-tip-default-br { + background-position: right -15px; +} + +.x-tip-default-ml { + background-position: 0 top; +} + +.x-tip-default-mr { + background-position: right top; +} + +.x-tip-default-tc { + background-position: 0 0; +} + +.x-tip-default-bc { + background-position: 0 -3px; +} + +.x-tip-default-tr, +.x-tip-default-br, +.x-tip-default-mr { + padding-right: 3px; +} + +.x-tip-default-tl, +.x-tip-default-bl, +.x-tip-default-ml { + padding-left: 3px; +} + +.x-tip-default-tc { + height: 3px; +} + +.x-tip-default-bc { + height: 3px; +} + +.x-tip-default-tl, +.x-tip-default-bl, +.x-tip-default-tr, +.x-tip-default-br, +.x-tip-default-tc, +.x-tip-default-bc, +.x-tip-default-ml, +.x-tip-default-mr { + zoom: 1; + background-image: url(images/tip/tip-default-corners.gif); +} + +.x-tip-default-ml, +.x-tip-default-mr { + zoom: 1; + background-image: url(images/tip/tip-default-sides.gif); + background-repeat: repeat-y; +} + +.x-tip-default-mc { + padding: 0px 0px 0px 0px; +} + +.x-strict .x-ie7 .x-tip-default-tl, +.x-strict .x-ie7 .x-tip-default-bl { + position: relative; + right: 0; +} + +/**/ +.x-tip-default:after { + display: none; + content: "x-slicer:corners:url(images/tip/tip-default-corners.gif), sides:url(images/tip/tip-default-sides.gif)"; +} + +/**/ +/* */ +.x-tip-default { + border-color: #f5c649; +} +.x-tip-default .x-tool-img { + background-image: url(images/tools/tool-sprites-dark.png); + background-color: #fffaee; +} + +.x-tip-header-default .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-tip-header-default .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-tip-header-body-default { + padding: 3px 3px 0 3px; +} + +.x-tip-header-text-container-default { + color: black; + font-size: 13px; + font-weight: bold; +} + +.x-tip-body-default { + padding: 3px; + color: black; + font-size: 13px; + font-weight: normal; +} +.x-tip-body-default a { + color: #006bbf; +} + +.x-tip-form-invalid { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 2px 2px 2px 2px; + border-width: 1px; + border-style: solid; + background-color: #fffaee; +} + +.x-tip-form-invalid-mc { + background-color: #fffaee; +} + +.x-nbr .x-tip-form-invalid { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-tip-form-invalid-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-2-2-2-2; +} + +.x-tip-form-invalid-tl { + background-position: 0 -6px; +} + +.x-tip-form-invalid-tr { + background-position: right -9px; +} + +.x-tip-form-invalid-bl { + background-position: 0 -12px; +} + +.x-tip-form-invalid-br { + background-position: right -15px; +} + +.x-tip-form-invalid-ml { + background-position: 0 top; +} + +.x-tip-form-invalid-mr { + background-position: right top; +} + +.x-tip-form-invalid-tc { + background-position: 0 0; +} + +.x-tip-form-invalid-bc { + background-position: 0 -3px; +} + +.x-tip-form-invalid-tr, +.x-tip-form-invalid-br, +.x-tip-form-invalid-mr { + padding-right: 3px; +} + +.x-tip-form-invalid-tl, +.x-tip-form-invalid-bl, +.x-tip-form-invalid-ml { + padding-left: 3px; +} + +.x-tip-form-invalid-tc { + height: 3px; +} + +.x-tip-form-invalid-bc { + height: 3px; +} + +.x-tip-form-invalid-tl, +.x-tip-form-invalid-bl, +.x-tip-form-invalid-tr, +.x-tip-form-invalid-br, +.x-tip-form-invalid-tc, +.x-tip-form-invalid-bc, +.x-tip-form-invalid-ml, +.x-tip-form-invalid-mr { + zoom: 1; + background-image: url(images/tip/tip-form-invalid-corners.gif); +} + +.x-tip-form-invalid-ml, +.x-tip-form-invalid-mr { + zoom: 1; + background-image: url(images/tip/tip-form-invalid-sides.gif); + background-repeat: repeat-y; +} + +.x-tip-form-invalid-mc { + padding: 0px 0px 0px 0px; +} + +.x-strict .x-ie7 .x-tip-form-invalid-tl, +.x-strict .x-ie7 .x-tip-form-invalid-bl { + position: relative; + right: 0; +} + +/**/ +.x-tip-form-invalid:after { + display: none; + content: "x-slicer:corners:url(images/tip/tip-form-invalid-corners.gif), sides:url(images/tip/tip-form-invalid-sides.gif)"; +} + +/**/ +/* */ +.x-tip-form-invalid { + border-color: #f5c649; +} +.x-tip-form-invalid .x-tool-img { + background-image: url(images/tools/tool-sprites-dark.png); + background-color: #fffaee; +} + +.x-tip-header-form-invalid .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-tip-header-form-invalid .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-tip-header-body-form-invalid { + padding: 3px 3px 0 3px; +} + +.x-tip-header-text-container-form-invalid { + color: black; + font-size: 13px; + font-weight: bold; +} + +.x-tip-body-form-invalid { + padding: 3px 3px 3px 22px; + color: black; + font-size: 13px; + font-weight: normal; +} +.x-tip-body-form-invalid a { + color: #006bbf; +} + +.x-tip-body-form-invalid { + background: 1px 1px no-repeat; + background-image: url(images/form/exclamation.png); +} +.x-tip-body-form-invalid li { + margin-bottom: 4px; +} +.x-tip-body-form-invalid li.last { + margin-bottom: 0; +} + +.x-editor .x-form-item-body { + padding-bottom: 0; +} + +.x-form-invalid-under { + padding: 2px 2px 2px 20px; + color: #cf4c35; + font: normal 13px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 16px; + background: no-repeat 0 2px; + background-image: url(images/form/exclamation.png); +} + +div.x-lbl-top-err-icon { + margin-bottom: 4px; +} + +.x-form-invalid-icon { + width: 16px; + height: 16px; + margin: 0 5px; + background-image: url(images/form/exclamation.png); + background-repeat: no-repeat; +} + +.x-form-item-label { + color: #333333; + font: normal 13px/17px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + margin-top: 4px; +} + +.x-autocontainer-form-item, +.x-anchor-form-item, +.x-vbox-form-item, +.x-table-form-item { + margin-bottom: 5px; +} + +.x-ie6 .x-form-form-item td { + border-top-width: 0; +} +.x-ie6 td.x-form-item-pad { + height: 5px; +} + +.x-form-field { + color: #333333; +} + +.x-form-item, +.x-form-field { + font: normal 13px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.x-form-type-text textarea.x-form-invalid-field, .x-form-type-text input.x-form-invalid-field, +.x-form-type-password textarea.x-form-invalid-field, +.x-form-type-password input.x-form-invalid-field, +.x-form-type-number textarea.x-form-invalid-field, +.x-form-type-number input.x-form-invalid-field, +.x-form-type-email textarea.x-form-invalid-field, +.x-form-type-email input.x-form-invalid-field, +.x-form-type-search textarea.x-form-invalid-field, +.x-form-type-search input.x-form-invalid-field, +.x-form-type-tel textarea.x-form-invalid-field, +.x-form-type-tel input.x-form-invalid-field { + background-color: white; + border-color: #cf4c35; +} + +.x-item-disabled .x-form-item-label, +.x-item-disabled .x-form-field, +.x-item-disabled .x-form-display-field, +.x-item-disabled .x-form-cb-label, +.x-item-disabled .x-form-trigger { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30); + opacity: 0.3; +} + +.x-form-text { + color: #333333; + padding: 4px 6px 3px 6px; + background: white repeat-x 0 0; + border-width: 1px; + border-style: solid; + border-color: silver #d9d9d9 #d9d9d9; + height: 24px; + line-height: 15px; +} +.x-content-box .x-form-text { + height: 15px; +} + +.x-form-focus { + border-color: #96caee !important; +} + +.x-form-empty-field, +textarea.x-form-empty-field { + color: gray; +} + +.x-quirks .x-ie .x-form-text, +.x-ie7m .x-form-text { + margin-top: -1px; + margin-bottom: -1px; +} + +/** + * Creates a visual theme for a Window + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {number} [$ui-padding=$window-padding] + * The padding of the Window + * + * @param {number} [$ui-border-radius=$window-border-radius] + * The border-radius of the Window + * + * @param {color} [$ui-border-color=$window-border-color] + * The border-color of the Window + * + * @param {number} [$ui-border-width=$window-border-width] + * The border-width of the Window + * + * @param {color} [$ui-inner-border-color=$window-inner-border-color] + * The inner border-color of the Window + * + * @param {number} [$ui-inner-border-width=$window-inner-border-width] + * The inner border-width of the Window + * + * @param {color} [$ui-header-color=$window-header-color] + * The text color of the Header + * + * @param {color} [$ui-header-background-color=$window-header-background-color] + * The background-color of the Header + * + * @param {number/list} [$ui-header-padding=$window-header-padding] + * The padding of the Header + * + * @param {string} [$ui-header-font-family=$window-header-font-family] + * The font-family of the Header + * + * @param {number} [$ui-header-font-size=$window-header-font-size] + * The font-size of the Header + * + * @param {string} [$ui-header-font-weight=$window-header-font-weight] + * The font-weight of the Header + * + * @param {number} [$ui-header-line-height=$window-header-line-height] + * The line-height of the Header + * + * @param {number/list} [$ui-header-text-padding=$window-header-text-padding] + * The padding of the Header's text element + * + * @param {string} [$ui-header-text-transform=$window-header-text-transform] + * The text-transform of the Header + * + * @param {color} [$ui-header-border-color=$ui-border-color] + * The border-color of the Header + * + * @param {number} [$ui-header-border-width=$window-header-border-width] + * The border-width of the Header + * + * @param {color} [$ui-header-inner-border-color=$window-header-inner-border-color] + * The inner border-color of the Header + * + * @param {number} [$ui-header-inner-border-width=$window-header-inner-border-width] + * The inner border-width of the Header + * + * @param {number} [$ui-header-icon-width=$window-header-icon-width] + * The width of the Header icon + * + * @param {number} [$ui-header-icon-height=$window-header-icon-height] + * The height of the Header icon + * + * @param {number} [$ui-header-icon-spacing=$window-header-icon-spacing] + * The space between the Header icon and text + * + * @param {list} [$ui-header-icon-background-position=$window-header-icon-background-position] + * The background-position of the Header icon + * + * @param {color} [$ui-header-glyph-color=$window-header-glyph-color] + * The color of the Header glyph icon + * + * @param {number} [$ui-header-glyph-opacity=$window-header-glyph-opacity] + * The opacity of the Header glyph icon + * + * @param {number} [$ui-tool-spacing=$window-tool-spacing] + * The space between the {@link Ext.panel.Tool Tools} + * + * @param {string} [$ui-tool-background-image=$window-tool-background-image] + * The background sprite to use for {@link Ext.panel.Tool Tools} + * + * @param {color} [$ui-body-border-color=$window-body-border-color] + * The border-color of the Window body + * + * @param {color} [$ui-body-background-color=$window-body-background-color] + * The background-color of the Window body + * + * @param {number} [$ui-body-border-width=$window-body-border-width] + * The border-width of the Window body + * + * @param {string} [$ui-body-border-style=$window-body-border-style] + * The border-style of the Window body + * + * @param {color} [$ui-body-color=$window-body-color] + * The color of text inside the Window body + * + * @param {color} [$ui-background-color=$window-background-color] + * The background-color of the Window + * + * @param {boolean} [$ui-force-header-border=$window-force-header-border] + * True to force the window header to have a border on the side facing + * the window body. Overrides dock layout's border management border + * removal rules. + * + * @param {boolean} [$ui-include-border-management-rules=$window-include-border-management-rules] + * True to include neptune style border management rules. + * + * @param {color} [$ui-wrap-border-color=$window-wrap-border-color] + * The color to apply to the border that wraps the body and docked items. The presence of + * the wrap border is controlled by the {@link #border} config. Only applicable when + * `$ui-include-border-management-rules` is `true`. + * + * @param {color} [$ui-wrap-border-width=$window-wrap-border-width] + * The width to apply to the border that wraps the body and docked items. The presence of + * the wrap border is controlled by the {@link #border} config. Only applicable when + * `$ui-include-border-management-rules` is `true`. + * + * @member Ext.window.Window + */ +.x-window-ghost { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +.x-window-default { + border-color: #606060; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; +} + +.x-window-default { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 2px 2px 2px 2px; + border-width: 2px; + border-style: solid; + background-color: white; +} + +.x-window-default-mc { + background-color: white; +} + +.x-nbr .x-window-default { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-default-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-2-2-2-2; +} + +.x-window-default-tl { + background-position: 0 -8px; +} + +.x-window-default-tr { + background-position: right -12px; +} + +.x-window-default-bl { + background-position: 0 -16px; +} + +.x-window-default-br { + background-position: right -20px; +} + +.x-window-default-ml { + background-position: 0 top; +} + +.x-window-default-mr { + background-position: right top; +} + +.x-window-default-tc { + background-position: 0 0; +} + +.x-window-default-bc { + background-position: 0 -4px; +} + +.x-window-default-tr, +.x-window-default-br, +.x-window-default-mr { + padding-right: 4px; +} + +.x-window-default-tl, +.x-window-default-bl, +.x-window-default-ml { + padding-left: 4px; +} + +.x-window-default-tc { + height: 4px; +} + +.x-window-default-bc { + height: 4px; +} + +.x-window-default-tl, +.x-window-default-bl, +.x-window-default-tr, +.x-window-default-br, +.x-window-default-tc, +.x-window-default-bc, +.x-window-default-ml, +.x-window-default-mr { + zoom: 1; + background-image: url(images/window/window-default-corners.gif); +} + +.x-window-default-ml, +.x-window-default-mr { + zoom: 1; + background-image: url(images/window/window-default-sides.gif); + background-repeat: repeat-y; +} + +.x-window-default-mc { + padding: 0px 0px 0px 0px; +} + +.x-strict .x-ie7 .x-window-default-tl, +.x-strict .x-ie7 .x-window-default-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-default:after { + display: none; + content: "x-slicer:corners:url(images/window/window-default-corners.gif), sides:url(images/window/window-default-sides.gif)"; +} + +/**/ +/* */ +.x-window-body-default { + border-color: #606060; + border-width: 1px; + border-style: solid; + background: white; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.x-window-header-default { + font-size: 18px; + border-color: #606060; + zoom: 1; + background-color: #ebebeb; +} +.x-window-header-default .x-tool-img { + background-image: url(images/tools/tool-sprites-dark.png); + background-color: #ebebeb; +} + +.x-window-header-default-vertical .x-window-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-window-header-default-vertical .x-window-header-text-container { + background-color: #ebebeb; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#ebebeb); +} + +.x-window-header-text-container-default { + color: #333333; + font-weight: bold; + line-height: 22px; + font-family: arial, helvetica, verdana, sans-serif; + font-size: 18px; + padding: 1px 0 0; + text-transform: none; +} + +.x-window-header-default-top { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 10px 10px 8px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-default-top-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-default-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-default-top-frameInfo { + font-family: dh-4-4-0-0-0-0-0-0-10-10-8-10; +} + +.x-window-header-default-top-tl { + background-position: 0 -8px; +} + +.x-window-header-default-top-tr { + background-position: right -12px; +} + +.x-window-header-default-top-bl { + background-position: 0 -16px; +} + +.x-window-header-default-top-br { + background-position: right -20px; +} + +.x-window-header-default-top-ml { + background-position: 0 top; +} + +.x-window-header-default-top-mr { + background-position: right top; +} + +.x-window-header-default-top-tc { + background-position: 0 0; +} + +.x-window-header-default-top-bc { + background-position: 0 -4px; +} + +.x-window-header-default-top-tr, +.x-window-header-default-top-br, +.x-window-header-default-top-mr { + padding-right: 4px; +} + +.x-window-header-default-top-tl, +.x-window-header-default-top-bl, +.x-window-header-default-top-ml { + padding-left: 4px; +} + +.x-window-header-default-top-tc { + height: 4px; +} + +.x-window-header-default-top-bc { + height: 0; +} + +.x-window-header-default-top-tl, +.x-window-header-default-top-bl, +.x-window-header-default-top-tr, +.x-window-header-default-top-br, +.x-window-header-default-top-tc, +.x-window-header-default-top-bc, +.x-window-header-default-top-ml, +.x-window-header-default-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-top-corners.gif); +} + +.x-window-header-default-top-ml, +.x-window-header-default-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-default-top-mc { + padding: 6px 6px 8px 6px; +} + +.x-strict .x-ie7 .x-window-header-default-top-tl, +.x-strict .x-ie7 .x-window-header-default-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-default-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-default-top-corners.gif), sides:url(images/window-header/window-header-default-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-default-right { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 10px 10px 10px 8px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-default-right-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-default-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-default-right-frameInfo { + font-family: dh-0-4-4-0-0-0-0-0-10-10-10-8; +} + +.x-window-header-default-right-tl { + background-position: 0 -8px; +} + +.x-window-header-default-right-tr { + background-position: right -12px; +} + +.x-window-header-default-right-bl { + background-position: 0 -16px; +} + +.x-window-header-default-right-br { + background-position: right -20px; +} + +.x-window-header-default-right-ml { + background-position: 0 top; +} + +.x-window-header-default-right-mr { + background-position: right top; +} + +.x-window-header-default-right-tc { + background-position: 0 0; +} + +.x-window-header-default-right-bc { + background-position: 0 -4px; +} + +.x-window-header-default-right-tr, +.x-window-header-default-right-br, +.x-window-header-default-right-mr { + padding-right: 4px; +} + +.x-window-header-default-right-tl, +.x-window-header-default-right-bl, +.x-window-header-default-right-ml { + padding-left: 0; +} + +.x-window-header-default-right-tc { + height: 4px; +} + +.x-window-header-default-right-bc { + height: 4px; +} + +.x-window-header-default-right-tl, +.x-window-header-default-right-bl, +.x-window-header-default-right-tr, +.x-window-header-default-right-br, +.x-window-header-default-right-tc, +.x-window-header-default-right-bc, +.x-window-header-default-right-ml, +.x-window-header-default-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-right-corners.gif); +} + +.x-window-header-default-right-ml, +.x-window-header-default-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-default-right-mc { + padding: 6px 6px 6px 8px; +} + +.x-strict .x-ie7 .x-window-header-default-right-tl, +.x-strict .x-ie7 .x-window-header-default-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-default-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-default-right-corners.gif), sides:url(images/window-header/window-header-default-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-default-bottom { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 8px 10px 10px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-default-bottom-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-default-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-default-bottom-frameInfo { + font-family: dh-0-0-4-4-0-0-0-0-8-10-10-10; +} + +.x-window-header-default-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-default-bottom-tr { + background-position: right -12px; +} + +.x-window-header-default-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-default-bottom-br { + background-position: right -20px; +} + +.x-window-header-default-bottom-ml { + background-position: 0 top; +} + +.x-window-header-default-bottom-mr { + background-position: right top; +} + +.x-window-header-default-bottom-tc { + background-position: 0 0; +} + +.x-window-header-default-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-default-bottom-tr, +.x-window-header-default-bottom-br, +.x-window-header-default-bottom-mr { + padding-right: 4px; +} + +.x-window-header-default-bottom-tl, +.x-window-header-default-bottom-bl, +.x-window-header-default-bottom-ml { + padding-left: 4px; +} + +.x-window-header-default-bottom-tc { + height: 0; +} + +.x-window-header-default-bottom-bc { + height: 4px; +} + +.x-window-header-default-bottom-tl, +.x-window-header-default-bottom-bl, +.x-window-header-default-bottom-tr, +.x-window-header-default-bottom-br, +.x-window-header-default-bottom-tc, +.x-window-header-default-bottom-bc, +.x-window-header-default-bottom-ml, +.x-window-header-default-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-bottom-corners.gif); +} + +.x-window-header-default-bottom-ml, +.x-window-header-default-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-default-bottom-mc { + padding: 8px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-default-bottom-tl, +.x-strict .x-ie7 .x-window-header-default-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-default-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-default-bottom-corners.gif), sides:url(images/window-header/window-header-default-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-default-left { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 10px 8px 10px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-default-left-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-default-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-default-left-frameInfo { + font-family: dh-4-0-0-4-0-0-0-0-10-8-10-10; +} + +.x-window-header-default-left-tl { + background-position: 0 -8px; +} + +.x-window-header-default-left-tr { + background-position: right -12px; +} + +.x-window-header-default-left-bl { + background-position: 0 -16px; +} + +.x-window-header-default-left-br { + background-position: right -20px; +} + +.x-window-header-default-left-ml { + background-position: 0 top; +} + +.x-window-header-default-left-mr { + background-position: right top; +} + +.x-window-header-default-left-tc { + background-position: 0 0; +} + +.x-window-header-default-left-bc { + background-position: 0 -4px; +} + +.x-window-header-default-left-tr, +.x-window-header-default-left-br, +.x-window-header-default-left-mr { + padding-right: 0; +} + +.x-window-header-default-left-tl, +.x-window-header-default-left-bl, +.x-window-header-default-left-ml { + padding-left: 4px; +} + +.x-window-header-default-left-tc { + height: 4px; +} + +.x-window-header-default-left-bc { + height: 4px; +} + +.x-window-header-default-left-tl, +.x-window-header-default-left-bl, +.x-window-header-default-left-tr, +.x-window-header-default-left-br, +.x-window-header-default-left-tc, +.x-window-header-default-left-bc, +.x-window-header-default-left-ml, +.x-window-header-default-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-left-corners.gif); +} + +.x-window-header-default-left-ml, +.x-window-header-default-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-default-left-mc { + padding: 6px 8px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-default-left-tl, +.x-strict .x-ie7 .x-window-header-default-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-default-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-default-left-corners.gif), sides:url(images/window-header/window-header-default-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-default-collapsed-top { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-default-collapsed-top-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-default-collapsed-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-default-collapsed-top-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-default-collapsed-top-tl { + background-position: 0 -8px; +} + +.x-window-header-default-collapsed-top-tr { + background-position: right -12px; +} + +.x-window-header-default-collapsed-top-bl { + background-position: 0 -16px; +} + +.x-window-header-default-collapsed-top-br { + background-position: right -20px; +} + +.x-window-header-default-collapsed-top-ml { + background-position: 0 top; +} + +.x-window-header-default-collapsed-top-mr { + background-position: right top; +} + +.x-window-header-default-collapsed-top-tc { + background-position: 0 0; +} + +.x-window-header-default-collapsed-top-bc { + background-position: 0 -4px; +} + +.x-window-header-default-collapsed-top-tr, +.x-window-header-default-collapsed-top-br, +.x-window-header-default-collapsed-top-mr { + padding-right: 4px; +} + +.x-window-header-default-collapsed-top-tl, +.x-window-header-default-collapsed-top-bl, +.x-window-header-default-collapsed-top-ml { + padding-left: 4px; +} + +.x-window-header-default-collapsed-top-tc { + height: 4px; +} + +.x-window-header-default-collapsed-top-bc { + height: 4px; +} + +.x-window-header-default-collapsed-top-tl, +.x-window-header-default-collapsed-top-bl, +.x-window-header-default-collapsed-top-tr, +.x-window-header-default-collapsed-top-br, +.x-window-header-default-collapsed-top-tc, +.x-window-header-default-collapsed-top-bc, +.x-window-header-default-collapsed-top-ml, +.x-window-header-default-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-collapsed-top-corners.gif); +} + +.x-window-header-default-collapsed-top-ml, +.x-window-header-default-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-collapsed-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-default-collapsed-top-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-default-collapsed-top-tl, +.x-strict .x-ie7 .x-window-header-default-collapsed-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-default-collapsed-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-default-collapsed-top-corners.gif), sides:url(images/window-header/window-header-default-collapsed-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-default-collapsed-right { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-default-collapsed-right-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-default-collapsed-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-default-collapsed-right-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-default-collapsed-right-tl { + background-position: 0 -8px; +} + +.x-window-header-default-collapsed-right-tr { + background-position: right -12px; +} + +.x-window-header-default-collapsed-right-bl { + background-position: 0 -16px; +} + +.x-window-header-default-collapsed-right-br { + background-position: right -20px; +} + +.x-window-header-default-collapsed-right-ml { + background-position: 0 top; +} + +.x-window-header-default-collapsed-right-mr { + background-position: right top; +} + +.x-window-header-default-collapsed-right-tc { + background-position: 0 0; +} + +.x-window-header-default-collapsed-right-bc { + background-position: 0 -4px; +} + +.x-window-header-default-collapsed-right-tr, +.x-window-header-default-collapsed-right-br, +.x-window-header-default-collapsed-right-mr { + padding-right: 4px; +} + +.x-window-header-default-collapsed-right-tl, +.x-window-header-default-collapsed-right-bl, +.x-window-header-default-collapsed-right-ml { + padding-left: 4px; +} + +.x-window-header-default-collapsed-right-tc { + height: 4px; +} + +.x-window-header-default-collapsed-right-bc { + height: 4px; +} + +.x-window-header-default-collapsed-right-tl, +.x-window-header-default-collapsed-right-bl, +.x-window-header-default-collapsed-right-tr, +.x-window-header-default-collapsed-right-br, +.x-window-header-default-collapsed-right-tc, +.x-window-header-default-collapsed-right-bc, +.x-window-header-default-collapsed-right-ml, +.x-window-header-default-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-collapsed-right-corners.gif); +} + +.x-window-header-default-collapsed-right-ml, +.x-window-header-default-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-collapsed-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-default-collapsed-right-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-default-collapsed-right-tl, +.x-strict .x-ie7 .x-window-header-default-collapsed-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-default-collapsed-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-default-collapsed-right-corners.gif), sides:url(images/window-header/window-header-default-collapsed-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-default-collapsed-bottom { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-default-collapsed-bottom-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-default-collapsed-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-default-collapsed-bottom-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-default-collapsed-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-default-collapsed-bottom-tr { + background-position: right -12px; +} + +.x-window-header-default-collapsed-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-default-collapsed-bottom-br { + background-position: right -20px; +} + +.x-window-header-default-collapsed-bottom-ml { + background-position: 0 top; +} + +.x-window-header-default-collapsed-bottom-mr { + background-position: right top; +} + +.x-window-header-default-collapsed-bottom-tc { + background-position: 0 0; +} + +.x-window-header-default-collapsed-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-default-collapsed-bottom-tr, +.x-window-header-default-collapsed-bottom-br, +.x-window-header-default-collapsed-bottom-mr { + padding-right: 4px; +} + +.x-window-header-default-collapsed-bottom-tl, +.x-window-header-default-collapsed-bottom-bl, +.x-window-header-default-collapsed-bottom-ml { + padding-left: 4px; +} + +.x-window-header-default-collapsed-bottom-tc { + height: 4px; +} + +.x-window-header-default-collapsed-bottom-bc { + height: 4px; +} + +.x-window-header-default-collapsed-bottom-tl, +.x-window-header-default-collapsed-bottom-bl, +.x-window-header-default-collapsed-bottom-tr, +.x-window-header-default-collapsed-bottom-br, +.x-window-header-default-collapsed-bottom-tc, +.x-window-header-default-collapsed-bottom-bc, +.x-window-header-default-collapsed-bottom-ml, +.x-window-header-default-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-collapsed-bottom-corners.gif); +} + +.x-window-header-default-collapsed-bottom-ml, +.x-window-header-default-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-collapsed-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-default-collapsed-bottom-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-default-collapsed-bottom-tl, +.x-strict .x-ie7 .x-window-header-default-collapsed-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-default-collapsed-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-default-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-default-collapsed-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-default-collapsed-left { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-default-collapsed-left-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-default-collapsed-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-default-collapsed-left-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-default-collapsed-left-tl { + background-position: 0 -8px; +} + +.x-window-header-default-collapsed-left-tr { + background-position: right -12px; +} + +.x-window-header-default-collapsed-left-bl { + background-position: 0 -16px; +} + +.x-window-header-default-collapsed-left-br { + background-position: right -20px; +} + +.x-window-header-default-collapsed-left-ml { + background-position: 0 top; +} + +.x-window-header-default-collapsed-left-mr { + background-position: right top; +} + +.x-window-header-default-collapsed-left-tc { + background-position: 0 0; +} + +.x-window-header-default-collapsed-left-bc { + background-position: 0 -4px; +} + +.x-window-header-default-collapsed-left-tr, +.x-window-header-default-collapsed-left-br, +.x-window-header-default-collapsed-left-mr { + padding-right: 4px; +} + +.x-window-header-default-collapsed-left-tl, +.x-window-header-default-collapsed-left-bl, +.x-window-header-default-collapsed-left-ml { + padding-left: 4px; +} + +.x-window-header-default-collapsed-left-tc { + height: 4px; +} + +.x-window-header-default-collapsed-left-bc { + height: 4px; +} + +.x-window-header-default-collapsed-left-tl, +.x-window-header-default-collapsed-left-bl, +.x-window-header-default-collapsed-left-tr, +.x-window-header-default-collapsed-left-br, +.x-window-header-default-collapsed-left-tc, +.x-window-header-default-collapsed-left-bc, +.x-window-header-default-collapsed-left-ml, +.x-window-header-default-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-collapsed-left-corners.gif); +} + +.x-window-header-default-collapsed-left-ml, +.x-window-header-default-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-default-collapsed-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-default-collapsed-left-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-default-collapsed-left-tl, +.x-strict .x-ie7 .x-window-header-default-collapsed-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-default-collapsed-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-default-collapsed-left-corners.gif), sides:url(images/window-header/window-header-default-collapsed-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-default .x-window-header-icon { + width: 16px; + height: 16px; + color: #333333; + font-size: 16px; + line-height: 16px; + background-position: center center; +} +.x-window-header-default .x-window-header-glyph { + color: #333333; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-window-header-default .x-window-header-glyph { + color: #8f8f8f; +} + +.x-window-header-default-horizontal .x-window-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-window-header-default-horizontal .x-window-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-window-header-default-vertical .x-window-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-window-header-default-vertical .x-window-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-window-header-default-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-window-header-default-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-window-header-default-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-window-header-default-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-window-header-default { + border-width: 0 !important; +} + +.x-nbr .x-window-default-collapsed .x-window-header { + border-width: 0 !important; +} + +.x-window-default-outer-border-l { + border-left-color: #606060 !important; + border-left-width: 1px !important; +} + +.x-window-default-outer-border-b { + border-bottom-color: #606060 !important; + border-bottom-width: 1px !important; +} + +.x-window-default-outer-border-bl { + border-bottom-color: #606060 !important; + border-bottom-width: 1px !important; + border-left-color: #606060 !important; + border-left-width: 1px !important; +} + +.x-window-default-outer-border-r { + border-right-color: #606060 !important; + border-right-width: 1px !important; +} + +.x-window-default-outer-border-rl { + border-right-color: #606060 !important; + border-right-width: 1px !important; + border-left-color: #606060 !important; + border-left-width: 1px !important; +} + +.x-window-default-outer-border-rb { + border-right-color: #606060 !important; + border-right-width: 1px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 1px !important; +} + +.x-window-default-outer-border-rbl { + border-right-color: #606060 !important; + border-right-width: 1px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 1px !important; + border-left-color: #606060 !important; + border-left-width: 1px !important; +} + +.x-window-default-outer-border-t { + border-top-color: #606060 !important; + border-top-width: 1px !important; +} + +.x-window-default-outer-border-tl { + border-top-color: #606060 !important; + border-top-width: 1px !important; + border-left-color: #606060 !important; + border-left-width: 1px !important; +} + +.x-window-default-outer-border-tb { + border-top-color: #606060 !important; + border-top-width: 1px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 1px !important; +} + +.x-window-default-outer-border-tbl { + border-top-color: #606060 !important; + border-top-width: 1px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 1px !important; + border-left-color: #606060 !important; + border-left-width: 1px !important; +} + +.x-window-default-outer-border-tr { + border-top-color: #606060 !important; + border-top-width: 1px !important; + border-right-color: #606060 !important; + border-right-width: 1px !important; +} + +.x-window-default-outer-border-trl { + border-top-color: #606060 !important; + border-top-width: 1px !important; + border-right-color: #606060 !important; + border-right-width: 1px !important; + border-left-color: #606060 !important; + border-left-width: 1px !important; +} + +.x-window-default-outer-border-trb { + border-top-color: #606060 !important; + border-top-width: 1px !important; + border-right-color: #606060 !important; + border-right-width: 1px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 1px !important; +} + +.x-window-default-outer-border-trbl { + border-color: #606060 !important; + border-width: 1px !important; +} + +.x-window-body-plain { + background-color: transparent; +} + +.x-form-textarea { + line-height: normal; + height: auto; +} +.x-content-box .x-form-textarea { + height: auto; +} + +.x-form-display-field { + height: 24px; +} + +.x-form-display-field { + font: normal 13px/17px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #333333; + margin-top: 4px; +} + +/** + * Creates a visual theme for an Ext.ProgressBar + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {color} [$ui-border-color=$progress-border-color] + * The border-color of the ProgressBar + * + * @param {color} [$ui-background-color=$progress-background-color] + * The background-color of the ProgressBar + * + * @param {color} [$ui-bar-background-color=$progress-bar-background-color] + * The background-color of the ProgressBar's moving element + * + * @param {string/list} [$ui-bar-background-gradient=$progress-bar-background-gradient] + * The background-gradient of the ProgressBar's moving element. Can be either the name of + * a predefined gradient or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {color} [$ui-color-front=$progress-text-color-front] + * The color of the ProgressBar's text when in front of the ProgressBar's moving element + * + * @param {color} [$ui-color-back=$progress-text-color-back] + * The color of the ProgressBar's text when the ProgressBar's 'moving element is not under it + * + * @param {number} [$ui-height=$progress-height] + * The height of the ProgressBar + * + * @param {number} [$ui-border-width=$progress-border-width] + * The border-width of the ProgressBar + * + * @param {number} [$ui-border-radius=$progress-border-radius] + * The border-radius of the ProgressBar + * + * @param {string} [$ui-text-text-align=$progress-text-text-align] + * The text-align of the ProgressBar's text + * + * @param {number} [$ui-text-font-size=$progress-text-font-size] + * The font-size of the ProgressBar's text + * + * @param {string} [$ui-text-font-weight=$progress-text-font-weight] + * The font-weight of the ProgressBar's text + * + * @member Ext.ProgressBar + */ +.x-progress-default { + background-color: #f5f5f5; + border-width: 0; + height: 20px; + border-color: #444444; +} +.x-content-box .x-progress-default { + height: 20px; +} +.x-progress-default .x-progress-bar-default { + background-image: none; + background-color: #cdcdcd; +} +.x-progress-default .x-progress-text { + color: #666666; + font-weight: bold; + font-size: 13px; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + text-align: center; + line-height: 20px; +} +.x-progress-default .x-progress-text-back { + color: #666666; + line-height: 20px; +} + +/**/ +.x-progress-bar-default:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +.x-message-box .x-window-body { + background-color: white; + border-width: 0; +} + +.x-message-box-info, +.x-message-box-warning, +.x-message-box-question, +.x-message-box-error { + background-position: left top; + background-repeat: no-repeat; +} + +.x-message-box-info { + background-image: url(images/shared/icon-info.png); +} + +.x-message-box-warning { + background-image: url(images/shared/icon-warning.png); +} + +.x-message-box-question { + background-image: url(images/shared/icon-question.png); +} + +.x-message-box-error { + background-image: url(images/shared/icon-error.png); +} + +.x-grid-body { + background: transparent; + border-width: 1px; + border-style: solid; + border-color: silver; +} + +.x-grid-empty { + padding: 10px; + color: gray; + background-color: transparent; + font: normal 13px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.x-grid-cell { + color: "none"; + font: normal 13px/15px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + background-color: white; + border-color: #ededed; + border-style: solid; +} + +.x-grid-row-alt .x-grid-td { + background-color: #fafafa; +} +.x-grid-row-before-over .x-grid-td { + border-bottom-style: solid; + border-bottom-color: #e7e7e7; +} +.x-grid-row-over .x-grid-td { + border-bottom-style: solid; + border-bottom-color: #e7e7e7; +} +.x-grid-row-before-selected .x-grid-td { + border-bottom-style: solid; + border-bottom-color: #cdcdcd; +} +.x-grid-row-selected .x-grid-td { + border-bottom-style: solid; + border-bottom-color: #cdcdcd; +} +.x-grid-row-before-focused .x-grid-td { + border-bottom-style: solid; + border-bottom-color: #e7e7e7; +} +.x-grid-row-focused .x-grid-td { + background-color: #e7e7e7; +} +.x-grid-row-over .x-grid-td { + background-color: #e7e7e7; +} +.x-grid-row-selected .x-grid-td { + background-color: #cdcdcd; +} +.x-grid-row-focused .x-grid-td { + border-bottom-style: solid; + border-bottom-color: #e7e7e7; +} +.x-grid-with-row-lines .x-grid-row-focused-first .x-grid-td { + border-top: 1px solid #e7e7e7; +} +.x-grid-row-selected .x-grid-row-summary .x-grid-td { + border-bottom-color: #cdcdcd; + border-top-width: 0; +} +.x-grid-row-focused .x-grid-row-summary .x-grid-td { + border-bottom-color: #e7e7e7; + border-top-width: 0; +} + +.x-grid-with-row-lines .x-grid-td { + border-bottom-width: 1px; +} +.x-grid-with-row-lines .x-grid-table { + border-top: 1px solid white; +} +.x-grid-with-row-lines .x-grid-table-over-first { + border-top-style: solid; + border-top-color: #e7e7e7; +} +.x-grid-with-row-lines .x-grid-table-selected-first { + border-top-style: solid; + border-top-color: #cdcdcd; +} + +.x-grid-with-row-lines .x-grid-table-focused-first { + border-top-style: solid; + border-top-color: #e7e7e7; +} + +.x-grid-cell-inner { + text-overflow: ellipsis; + padding: 5px 10px 4px 10px; +} + +.x-grid-cell-special { + border-color: #ededed; + border-style: solid; + border-right-width: 1px 0; +} + +.x-grid-dirty-cell { + background: url(images/grid/dirty.png) no-repeat 0 0; +} + +.x-grid-row .x-grid-cell-selected { + color: "none"; + background-color: #cdcdcd; +} + +.x-grid-with-col-lines .x-grid-cell { + border-right-width: 1px; +} + +.x-grid-resize-marker { + width: 1px; + background-color: #0f0f0f; +} + +.x-mask { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30); + opacity: 0.3; + background: black; + cursor: default; +} + +.x-mask-msg { + padding: 8px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + background: #e5e5e5; +} + +.x-mask-msg-inner { + padding: 0; + background-color: transparent; + color: #666666; + font: normal 13px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.x-mask-msg-text { + padding: 21px 0 0; + background-image: url(images/loadmask/loading.gif); + background-repeat: no-repeat; + background-position: center 0; +} + +.col-move-top, +.col-move-bottom { + width: 9px; + height: 9px; +} + +.col-move-top { + background-image: url(images/grid/col-move-top.png); +} + +.col-move-bottom { + background-image: url(images/grid/col-move-bottom.png); +} + +.x-grid-header-ct { + border: 1px solid #444444; + border-bottom-color: #f5f5f5; + background-color: #f5f5f5; +} + +.x-accordion-item .x-grid-header-ct { + border-width: 0 0 1px !important; +} + +.x-accordion-item .x-grid-header-ct-hidden { + border: 0 !important; +} + +.x-grid-body { + border-top-color: silver; +} + +.x-hmenu-sort-asc { + background-image: url(images/grid/hmenu-asc.png); +} + +.x-hmenu-sort-desc { + background-image: url(images/grid/hmenu-desc.png); +} + +.x-cols-icon { + background-image: url(images/grid/columns.png); +} + +.x-column-header { + border-right: 1px solid silver; + color: #666666; + font: bold 13px/15px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + background-color: #f5f5f5; +} + +.x-group-sub-header { + background: transparent; + border-top: 1px solid silver; +} +.x-group-sub-header .x-column-header-inner { + padding: 6px 10px 7px 10px; +} + +.x-column-header-inner { + padding: 7px 10px 7px 10px; + text-overflow: ellipsis; +} + +.x-column-header-inner-empty { + text-overflow: clip; +} + +.x-column-header-over, +.x-column-header-sort-ASC, +.x-column-header-sort-DESC { + background-image: none; + background-color: #f1f1f1; +} + +.x-column-header-open { + background-color: #f1f1f1; +} +.x-column-header-open .x-column-header-trigger { + background-color: #e4e4e4; +} + +.x-column-header-trigger { + width: 18px; + cursor: pointer; + background-color: transparent; + background-position: center center; +} + +.x-column-header-align-right .x-column-header-text { + margin-right: 12px; +} + +.x-column-header-sort-ASC .x-column-header-text, +.x-column-header-sort-DESC .x-column-header-text { + padding-right: 17px; + background-position: right center; +} + +.x-column-header-sort-ASC .x-column-header-text { + background-image: url(images/grid/sort_asc.png); +} + +.x-column-header-sort-DESC .x-column-header-text { + background-image: url(images/grid/sort_desc.png); +} + +.x-tree-expander { + cursor: pointer; +} + +.x-tree-arrows .x-tree-expander { + background-image: url(images/tree/arrows.png); +} +.x-tree-arrows .x-tree-expander-over .x-tree-expander { + background-position: -32px center; +} +.x-tree-arrows .x-grid-tree-node-expanded .x-tree-expander { + background-position: -16px center; +} +.x-tree-arrows .x-grid-tree-node-expanded .x-tree-expander-over .x-tree-expander { + background-position: -48px center; +} + +.x-tree-lines .x-tree-elbow { + background-image: url(images/tree/elbow.png); +} +.x-tree-lines .x-tree-elbow-end { + background-image: url(images/tree/elbow-end.png); +} +.x-tree-lines .x-tree-elbow-plus { + background-image: url(images/tree/elbow-plus.png); +} +.x-tree-lines .x-tree-elbow-end-plus { + background-image: url(images/tree/elbow-end-plus.png); +} +.x-tree-lines .x-grid-tree-node-expanded .x-tree-elbow-plus { + background-image: url(images/tree/elbow-minus.png); +} +.x-tree-lines .x-grid-tree-node-expanded .x-tree-elbow-end-plus { + background-image: url(images/tree/elbow-end-minus.png); +} +.x-tree-lines .x-tree-elbow-line { + background-image: url(images/tree/elbow-line.png); +} + +.x-tree-no-lines .x-tree-expander { + background-image: url(images/tree/elbow-plus-nl.png); +} +.x-tree-no-lines .x-grid-tree-node-expanded .x-tree-expander { + background-image: url(images/tree/elbow-minus-nl.png); +} + +.x-tree-icon { + width: 16px; + height: 24px; +} + +.x-tree-elbow-img { + width: 18px; + height: 24px; + margin-right: 2px; +} + +.x-tree-icon, +.x-tree-elbow-img, +.x-tree-checkbox { + margin-top: -5px; + margin-bottom: -4px; +} + +.x-tree-icon-leaf { + background-image: url(images/tree/leaf.png); +} + +.x-tree-icon-parent { + background-image: url(images/tree/folder.png); +} + +.x-grid-tree-node-expanded .x-tree-icon-parent { + background-image: url(images/tree/folder-open.png); +} + +.x-tree-checkbox { + margin-right: 4px; + top: 5px; + width: 15px; + height: 15px; + background-image: url(images/form/checkbox.png); +} + +.x-tree-checkbox-checked { + background-position: 0 -15px; +} + +.x-grid-tree-loading .x-tree-icon { + background-image: url(images/tree/loading.gif); +} + +.x-grid-cell-inner-treecolumn { + *font-size: 1px; +} + +.x-tree-node-text { + *font-size: 13px; + padding-left: 4px; +} + +.x-grid-cell-inner-treecolumn { + padding: 5px 10px 4px 6px; +} + +.x-form-cb-wrap { + height: 24px; +} + +.x-form-cb { + margin-top: 5px; +} + +.x-form-checkbox { + width: 15px; + height: 15px; + background: url(images/form/checkbox.png) no-repeat; +} + +.x-form-cb-checked .x-form-checkbox { + background-position: 0 -15px; +} + +/* Focused */ +.x-form-checkbox-focus { + background-position: -15px 0; +} + +.x-form-cb-checked .x-form-checkbox-focus { + background-position: -15px -15px; +} + +/* boxLabel */ +.x-form-cb-label { + margin-top: 4px; + font: normal 13px/17px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.x-form-cb-label-before { + padding-right: 19px; +} + +.x-form-cb-label-after { + padding-left: 19px; +} + +.x-form-cb-wrap-inner-no-box-label .x-form-cb { + position: static; +} +.x-quirks .x-ie .x-form-cb-wrap-inner-no-box-label, .x-ie7m .x-form-cb-wrap-inner-no-box-label { + display: inline; + width: 15px; + zoom: 1; +} + +.x-grid-rowwrap { + border-color: #ededed; + border-style: solid; +} + +.x-grid-rowbody { + font: normal 13px/15px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 5px 10px 5px 10px; +} + +.x-grid-cell-inner-row-expander { + padding: 7px 6px 6px 6px; +} + +.x-grid-row-expander { + width: 11px; + height: 11px; + cursor: pointer; + background-image: url(images/grid/group-collapse.png); +} +.x-grid-row-collapsed .x-grid-row-expander { + background-image: url(images/grid/group-expand.png); +} + +/* Horizontal styles */ +.x-slider-horz { + padding-left: 7px; + background: no-repeat 0 -15px; +} +.x-slider-horz .x-slider-end { + padding-right: 7px; + background: no-repeat right -30px; +} + +.x-slider-horz .x-slider-inner { + height: 15px; +} + +.x-ie6 .x-form-item .x-slider-horz, +.x-ie7 .x-form-item .x-slider-horz, +.x-quirks .x-ie .x-form-item .x-slider-horz { + margin-top: 5px; +} + +.x-slider-horz .x-slider-thumb { + width: 15px; + height: 15px; + margin-left: -7px; + background-image: url(images/slider/slider-thumb.png); +} + +.x-slider-horz .x-slider-thumb-over { + background-position: -15px -15px; +} + +.x-slider-horz .x-slider-thumb-drag { + background-position: -30px -30px; +} + +/* Vertical styles */ +.x-slider-vert { + padding-top: 7px; + background: no-repeat -30px 0; +} + +.x-slider-vert .x-slider-end { + padding-bottom: 7px; + background: no-repeat -15px bottom; + width: 15px; +} + +.x-slider-vert .x-slider-inner { + width: 15px; +} + +.x-slider-vert .x-slider-thumb { + width: 15px; + height: 15px; + margin-bottom: -7px; + background-image: url(images/slider/slider-v-thumb.png); +} + +.x-slider-vert .x-slider-thumb-over { + background-position: -15px -15px; +} + +.x-slider-vert .x-slider-thumb-drag { + background-position: -30px -30px; +} + +.x-slider-horz, +.x-slider-horz .x-slider-end, +.x-slider-horz .x-slider-inner { + background-image: url(images/slider/slider-bg.png); +} + +.x-slider-vert, +.x-slider-vert .x-slider-end, +.x-slider-vert .x-slider-inner { + background-image: url(images/slider/slider-v-bg.png); +} + +.x-menu { + border-style: solid; + border-width: 1px; + border-color: #e1e1e1; +} + +.x-menu-body { + background: white; + padding: 0; +} + +.x-menu-icon-separator { + left: 26px; + border-left: solid 1px #e1e1e1; + background-color: white; + width: 1px; +} + +.x-menu-item { + cursor: pointer; +} + +.x-menu-item-text, +.x-menu-item-cmp { + margin: 0 5px 0 5px; +} + +.x-menu-item-indent { + margin-left: 32px; +} + +.x-menu-item-indent-no-separator { + margin-left: 26px; +} + +.x-menu-item-indent-right-icon { + margin-right: 31px; +} + +.x-menu-item-indent-right-arrow { + margin-right: 22px; +} + +.x-menu-item-active { + background-image: none; + background-color: #dedede; +} +.x-nlg .x-menu-item-active { + background: #dedede repeat-x left top; + background-image: url(images/menu/menu-item-active-bg.gif); +} + +.x-menu-item-icon { + width: 16px; + height: 16px; + top: 4px; + left: 5px; + background-position: center center; +} +.x-menu-item-active .x-menu-item-icon { + top: 4px; + left: 5px; +} + +.x-menu-item-glyph { + font-size: 16px; + line-height: 16px; + color: gray; + opacity: 0.5; +} +.x-ie8m .x-menu-item-glyph { + color: #bfbfbf; +} + +.x-menu-item-icon-right { + width: 16px; + height: 16px; + top: 4px; + right: 5px; + background-position: center center; +} +.x-menu-item-active .x-menu-item-icon-right { + top: 4px; + right: 5px; +} + +.x-menu-item-text { + font: normal 13px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 23px; + padding-top: 1px; + color: black; + cursor: pointer; +} + +.x-menu-item-checked .x-menu-item-checkbox { + background-image: url(images/menu/checked.png); +} +.x-menu-item-checked .x-menu-group-icon { + background-image: url(images/menu/group-checked.png); +} + +.x-menu-item-unchecked .x-menu-item-checkbox { + background-image: url(images/menu/unchecked.png); +} +.x-menu-item-unchecked .x-menu-group-icon { + background-image: none; +} + +.x-menu-item-separator { + height: 1px; + border-top: solid 1px #e1e1e1; + background-color: white; + margin: 2px 0; + padding: 0; +} + +.x-menu-item-arrow { + width: 12px; + height: 9px; + top: 8px; + right: 0; + background-image: url(images/menu/menu-parent.png); +} +.x-menu-item-active .x-menu-item-arrow { + top: 8px; + right: 0; +} + +.x-menu-item-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +.x-content-box .x-menu-icon-separator { + width: 0px; +} +.x-content-box .x-menu-item-separator { + height: 0px; +} + +.x-ie .x-menu-item-disabled .x-menu-item-icon { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} +.x-ie .x-menu-item-disabled .x-menu-item-text { + background-color: transparent; +} + +.x-menu-item .x-form-item-label { + font-size: 13px; + color: black; +} + +.x-menu-scroll-top { + height: 16px; + background-image: url(images/menu/scroll-top.png); +} + +.x-menu-scroll-bottom { + height: 16px; + background-image: url(images/menu/scroll-bottom.png); +} + +.x-menu-scroll-top, .x-menu-scroll-bottom { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; + background-color: white; +} + +.x-menu-scroll-top-hover, .x-menu-scroll-bottom-hover { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60); + opacity: 0.6; +} + +.x-menu-scroll-top-pressed, .x-menu-scroll-bottom-pressed { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70); + opacity: 0.7; +} + +/**/ +.x-menu-item-link:after { + display: none; + content: "x-slicer:bg:url(images/menu/menu-item-active-bg.gif), stretch:bottom"; +} + +/**/ +/* */ +.x-form-trigger { + background: url(images/form/trigger.png); + width: 22px; +} + +.x-trigger-cell { + background-color: white; + width: 22px; +} + +.x-form-trigger-over { + background-position: -22px 0; +} + +.x-form-trigger-wrap-focus .x-form-trigger { + background-position: -66px 0; +} + +.x-form-trigger-wrap-focus .x-form-trigger-over { + background-position: -88px 0; +} + +.x-form-trigger-click, +.x-form-trigger-wrap-focus .x-form-trigger-click { + background-position: -44px 0; +} + +.x-form-clear-trigger { + background-image: url(images/form/clear-trigger.png); +} + +.x-form-search-trigger { + background-image: url(images/form/search-trigger.png); +} + +.x-quirks .prefixie6 .x-form-trigger-input-cell { + height: 24px; +} +.x-quirks .prefixie6 .x-field-toolbar .x-form-trigger-input-cell { + height: 24px; +} + +/** + * Creates a visual theme for a ButtonGroup. + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {color} [$ui-background-color=$btn-group-background-color] + * The background-color of the button group + * + * @param {color} [$ui-border-color=$btn-group-border-color] + * The border-color of the button group + * + * @param {number} [$ui-border-width=$btn-group-border-width] + * The border-width of the button group + * + * @param {number} [$ui-border-radius=$btn-group-border-radius] + * The border-radius of the button group + * + * @param {color} [$ui-inner-border-color=$btn-group-inner-border-color] + * The inner border-color of the button group + * + * @param {color} [$ui-header-background-color=$btn-group-header-background-color] + * The background-color of the header + * + * @param {string} [$ui-header-font=$btn-group-header-font] + * The font of the header + * + * @param {color} [$ui-header-color=$btn-group-header-color] + * The text color of the header + * + * @param {number} [$ui-header-line-height=$btn-group-header-line-height] + * The line-height of the header + * + * @param {number} [$ui-header-padding=$btn-group-header-padding] + * The padding of the header + * + * @param {number} [$ui-body-padding=$btn-group-padding] + * The padding of the body element + * + * @param {string} [$ui-tool-background-image=$btn-group-tool-background-image] + * Sprite image to use for header {@link Ext.panel.Tool Tools} + * + * @member Ext.container.ButtonGroup + */ +.x-btn-group-default { + border-color: #e4e4e4; + -webkit-box-shadow: white 0 1px 0px 0 inset, white 0 -1px 0px 0 inset, white -1px 0 0px 0 inset, white 1px 0 0px 0 inset; + -moz-box-shadow: white 0 1px 0px 0 inset, white 0 -1px 0px 0 inset, white -1px 0 0px 0 inset, white 1px 0 0px 0 inset; + box-shadow: white 0 1px 0px 0 inset, white 0 -1px 0px 0 inset, white -1px 0 0px 0 inset, white 1px 0 0px 0 inset; +} + +.x-btn-group-header-default { + padding: 4px 5px; + line-height: 16px; + background: #e4e4e4; + -moz-border-radius-topleft: 0px; + -webkit-border-top-left-radius: 0px; + border-top-left-radius: 0px; + -moz-border-radius-topright: 0px; + -webkit-border-top-right-radius: 0px; + border-top-right-radius: 0px; +} +.x-btn-group-header-default .x-tool-img { + background-image: url(images/tools/tool-sprites-dark.png); + background-color: #e4e4e4; +} + +.x-btn-group-header-text-container-default { + font: normal 13px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 16px; + color: #666666; +} + +.x-btn-group-body-default { + padding: 0 1px; +} +.x-btn-group-body-default .x-table-layout { + border-spacing: 5px; +} + +.x-btn-group-default-framed { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 0px 1px 0px 1px; + border-width: 3px; + border-style: solid; + background-color: white; +} + +.x-btn-group-default-framed-mc { + background-color: white; +} + +.x-nbr .x-btn-group-default-framed { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-btn-group-default-framed-frameInfo { + font-family: dh-3-3-3-3-3-3-3-3-0-1-0-1; +} + +.x-btn-group-default-framed-tl { + background-position: 0 -6px; +} + +.x-btn-group-default-framed-tr { + background-position: right -9px; +} + +.x-btn-group-default-framed-bl { + background-position: 0 -12px; +} + +.x-btn-group-default-framed-br { + background-position: right -15px; +} + +.x-btn-group-default-framed-ml { + background-position: 0 top; +} + +.x-btn-group-default-framed-mr { + background-position: right top; +} + +.x-btn-group-default-framed-tc { + background-position: 0 0; +} + +.x-btn-group-default-framed-bc { + background-position: 0 -3px; +} + +.x-btn-group-default-framed-tr, +.x-btn-group-default-framed-br, +.x-btn-group-default-framed-mr { + padding-right: 3px; +} + +.x-btn-group-default-framed-tl, +.x-btn-group-default-framed-bl, +.x-btn-group-default-framed-ml { + padding-left: 3px; +} + +.x-btn-group-default-framed-tc { + height: 3px; +} + +.x-btn-group-default-framed-bc { + height: 3px; +} + +.x-btn-group-default-framed-tl, +.x-btn-group-default-framed-bl, +.x-btn-group-default-framed-tr, +.x-btn-group-default-framed-br, +.x-btn-group-default-framed-tc, +.x-btn-group-default-framed-bc, +.x-btn-group-default-framed-ml, +.x-btn-group-default-framed-mr { + zoom: 1; + background-image: url(images/btn-group/btn-group-default-framed-corners.gif); +} + +.x-btn-group-default-framed-ml, +.x-btn-group-default-framed-mr { + zoom: 1; + background-image: url(images/btn-group/btn-group-default-framed-sides.gif); + background-repeat: repeat-y; +} + +.x-btn-group-default-framed-mc { + padding: 0px 1px 0px 1px; +} + +.x-strict .x-ie7 .x-btn-group-default-framed-tl, +.x-strict .x-ie7 .x-btn-group-default-framed-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-group-default-framed:after { + display: none; + content: "x-slicer:corners:url(images/btn-group/btn-group-default-framed-corners.gif), sides:url(images/btn-group/btn-group-default-framed-sides.gif)"; +} + +/**/ +/* */ +.x-btn-group-default-framed-notitle { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 0px 1px 0px 1px; + border-width: 3px; + border-style: solid; + background-color: white; +} + +.x-btn-group-default-framed-notitle-mc { + background-color: white; +} + +.x-nbr .x-btn-group-default-framed-notitle { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-btn-group-default-framed-notitle-frameInfo { + font-family: dh-3-3-3-3-3-3-3-3-0-1-0-1; +} + +.x-btn-group-default-framed-notitle-tl { + background-position: 0 -6px; +} + +.x-btn-group-default-framed-notitle-tr { + background-position: right -9px; +} + +.x-btn-group-default-framed-notitle-bl { + background-position: 0 -12px; +} + +.x-btn-group-default-framed-notitle-br { + background-position: right -15px; +} + +.x-btn-group-default-framed-notitle-ml { + background-position: 0 top; +} + +.x-btn-group-default-framed-notitle-mr { + background-position: right top; +} + +.x-btn-group-default-framed-notitle-tc { + background-position: 0 0; +} + +.x-btn-group-default-framed-notitle-bc { + background-position: 0 -3px; +} + +.x-btn-group-default-framed-notitle-tr, +.x-btn-group-default-framed-notitle-br, +.x-btn-group-default-framed-notitle-mr { + padding-right: 3px; +} + +.x-btn-group-default-framed-notitle-tl, +.x-btn-group-default-framed-notitle-bl, +.x-btn-group-default-framed-notitle-ml { + padding-left: 3px; +} + +.x-btn-group-default-framed-notitle-tc { + height: 3px; +} + +.x-btn-group-default-framed-notitle-bc { + height: 3px; +} + +.x-btn-group-default-framed-notitle-tl, +.x-btn-group-default-framed-notitle-bl, +.x-btn-group-default-framed-notitle-tr, +.x-btn-group-default-framed-notitle-br, +.x-btn-group-default-framed-notitle-tc, +.x-btn-group-default-framed-notitle-bc, +.x-btn-group-default-framed-notitle-ml, +.x-btn-group-default-framed-notitle-mr { + zoom: 1; + background-image: url(images/btn-group/btn-group-default-framed-notitle-corners.gif); +} + +.x-btn-group-default-framed-notitle-ml, +.x-btn-group-default-framed-notitle-mr { + zoom: 1; + background-image: url(images/btn-group/btn-group-default-framed-notitle-sides.gif); + background-repeat: repeat-y; +} + +.x-btn-group-default-framed-notitle-mc { + padding: 0px 1px 0px 1px; +} + +.x-strict .x-ie7 .x-btn-group-default-framed-notitle-tl, +.x-strict .x-ie7 .x-btn-group-default-framed-notitle-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-group-default-framed-notitle:after { + display: none; + content: "x-slicer:corners:url(images/btn-group/btn-group-default-framed-notitle-corners.gif), sides:url(images/btn-group/btn-group-default-framed-notitle-sides.gif)"; +} + +/**/ +/* */ +.x-btn-group-default-framed { + border-color: #e4e4e4; + -webkit-box-shadow: white 0 1px 0px 0 inset, white 0 -1px 0px 0 inset, white -1px 0 0px 0 inset, white 1px 0 0px 0 inset; + -moz-box-shadow: white 0 1px 0px 0 inset, white 0 -1px 0px 0 inset, white -1px 0 0px 0 inset, white 1px 0 0px 0 inset; + box-shadow: white 0 1px 0px 0 inset, white 0 -1px 0px 0 inset, white -1px 0 0px 0 inset, white 1px 0 0px 0 inset; +} + +.x-btn-group-header-default-framed { + padding: 4px 5px; + line-height: 16px; + background: #e4e4e4; + -moz-border-radius-topleft: 3px; + -webkit-border-top-left-radius: 3px; + border-top-left-radius: 3px; + -moz-border-radius-topright: 3px; + -webkit-border-top-right-radius: 3px; + border-top-right-radius: 3px; +} +.x-btn-group-header-default-framed .x-tool-img { + background-image: url(images/tools/tool-sprites-dark.png); + background-color: #e4e4e4; +} + +.x-btn-group-header-text-container-default-framed { + font: normal 13px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 16px; + color: #666666; +} + +.x-btn-group-body-default-framed { + padding: 0 1px 0 1px; +} +.x-btn-group-body-default-framed .x-table-layout { + border-spacing: 5px; +} + +.x-form-checkboxgroup-body { + padding: 0 4px; +} + +.x-form-invalid .x-form-checkboxgroup-body { + border: 1px solid #cf4c35; +} + +.x-check-group-alt { + background: #f5f5f5; + border-top: 1px dotted #f5f5f5; + border-bottom: 1px dotted #f5f5f5; +} + +.x-form-check-group-label { + color: #333333; + padding: 2px; + margin: 0 30px 5px 0; + border-width: 0 0 1px 0; + border-style: solid; + border-color: #333333; +} + +.x-fieldset { + border: 0 solid #b5b8c8; + padding: 10px 0 25px 0px; + margin: 0 0 10px; + overflow: hidden; +} + +.x-ie8m .x-fieldset, +.x-quirks .x-ie .x-fieldset { + padding-top: 0; +} +.x-ie8m .x-fieldset .x-fieldset-body, +.x-quirks .x-ie .x-fieldset .x-fieldset-body { + padding-top: 10px; +} + +.x-fieldset-header-checkbox { + line-height: 16px; + margin: 1px 0 0; +} + +.x-fieldset-header { + padding: 0 8px 0 0; +} +.x-fieldset-header .x-tool { + margin-top: 1px; + padding: 0; +} + +.x-fieldset-header-text { + font: bold 13px/16px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: black; + padding: 1px 0; +} + +.x-fieldset-header-text-collapsible { + cursor: pointer; +} + +.x-fieldset-with-title .x-fieldset-header-checkbox, +.x-fieldset-with-title .x-tool { + margin: 1px 3px 0 0; +} + +.x-webkit .x-fieldset-header { + -webkit-padding-start: 0; + -webkit-padding-end: 8px; +} + +.x-opera .x-fieldset-with-legend { + margin-top: -0; +} +.x-opera.x-mac .x-fieldset-header-text { + padding: 2px 0 0; +} + +.x-strict .x-ie8 .x-fieldset-header { + margin-bottom: -0; +} +.x-strict .x-ie8 .x-fieldset-header .x-tool, +.x-strict .x-ie8 .x-fieldset-header .x-fieldset-header-text, +.x-strict .x-ie8 .x-fieldset-header .x-fieldset-header-checkbox { + position: relative; + top: -0; +} + +.x-quirks .x-ie .x-fieldset-header, +.x-ie8m .x-fieldset-header { + padding-left: -2; + padding-right: 6px; +} + +.x-fieldset-collapsed { + padding-bottom: 0 !important; + border-width: 1px 1px 0 1px !important; + border-left-color: transparent !important; + border-right-color: transparent !important; +} +.x-fieldset-collapsed .x-fieldset-body { + display: none; +} + +.x-ie6 .x-fieldset-collapsed { + border-width: 1px 0 0 0 !important; + padding-bottom: 0 !important; + margin-left: 1px; + margin-right: 1px; +} + +.x-ie .x-fieldset-bwrap { + zoom: 1; +} + +.x-fieldset .x-tool-toggle { + background-image: url(images/fieldset/collapse-tool.png); + background-position: 0 0; +} +.x-fieldset .x-tool-over .x-tool-toggle { + background-position: 0 -15px; +} + +.x-fieldset-collapsed .x-tool-toggle { + background-position: -15px 0; +} +.x-fieldset-collapsed .x-tool-over .x-tool-toggle { + background-position: -15px -15px; +} + +/* IE legend positioning bug */ +.x-ie .x-fieldset-noborder legend { + position: relative; + margin-bottom: 23px; +} + +.x-ie .x-fieldset-noborder legend span { + position: absolute; + left: 16px; +} + +x-fieldset-body { + overflow: hidden; +} + +div.x-form-spinner-up, +div.x-form-spinner-down { + background-image: url(images/form/spinner.png); + background-color: white; + width: 22px; + height: 11px; +} + +.x-form-spinner-down { + background-position: 0 -11px; +} + +.x-form-trigger-wrap-focus .x-form-spinner-down { + background-position: -66px -11px; +} + +.x-form-trigger-wrap .x-form-spinner-down-over { + background-position: -22px -11px; +} + +.x-form-trigger-wrap-focus .x-form-spinner-down-over { + background-position: -88px -11px; +} + +.x-form-trigger-wrap .x-form-spinner-down-click { + background-position: -44px -11px; +} + +.x-tbar-page-number { + width: 30px; +} + +.x-tbar-page-first { + background-image: url(images/grid/page-first.png); +} + +.x-tbar-page-prev { + background-image: url(images/grid/page-prev.png); +} + +.x-tbar-page-next { + background-image: url(images/grid/page-next.png); +} + +.x-tbar-page-last { + background-image: url(images/grid/page-last.png); +} + +.x-tbar-loading { + background-image: url(images/grid/refresh.png); +} + +.x-boundlist { + border-width: 1px; + border-style: solid; + border-color: #e1e1e1; + background: white; +} + +.x-strict .x-ie7m .x-boundlist-list-ct { + position: relative; +} + +.x-boundlist-item { + padding: 0 6px; + font: normal 13px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 22px; + cursor: pointer; + cursor: hand; + position: relative; + /*allow hover in IE on empty items*/ + zoom: 1; + border-width: 1px; + border-style: dotted; + border-color: white; +} + +.x-boundlist-selected { + background: #cdcdcd; + border-color: #cdcdcd; +} + +.x-boundlist-item-over { + background: #dedede; + border-color: #dedede; +} + +.x-boundlist-floating { + border-top-width: 0; +} + +.x-boundlist-above { + border-top-width: 1px; + border-bottom-width: 1px; +} + +.x-form-radio { + width: 15px; + height: 15px; + background: url(images/form/radio.png) no-repeat; +} + +.x-form-cb-checked .x-form-radio { + background-position: 0 -15px; +} + +.x-form-radio-focus { + background-position: -15px 0; +} + +.x-form-cb-checked .x-form-radio-focus { + background-position: -15px -15px; +} + +.x-datepicker { + border-width: 1px; + border-style: solid; + border-color: #e1e1e1; + background-color: white; + width: 212px; +} + +.x-datepicker-header { + padding: 4px 6px; + text-align: center; + background-image: none; + background-color: #f5f5f5; +} + +.x-datepicker-arrow { + width: 12px; + height: 12px; + top: 9px; + cursor: pointer; + background-color: #f5f5f5; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70); + opacity: 0.7; +} + +a.x-datepicker-arrow:hover { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); + opacity: 1; +} + +.x-datepicker-next { + right: 6px; + background-image: url(images/datepicker/arrow-right.png); +} + +.x-datepicker-prev { + left: 6px; + background-image: url(images/datepicker/arrow-left.png); +} + +.x-datepicker-month .x-btn, +.x-datepicker-month .x-btn .x-btn-tc, +.x-datepicker-month .x-btn .x-btn-tl, +.x-datepicker-month .x-btn .x-btn-tr, +.x-datepicker-month .x-btn .x-btn-mc, +.x-datepicker-month .x-btn .x-btn-ml, +.x-datepicker-month .x-btn .x-btn-mr, +.x-datepicker-month .x-btn .x-btn-bc, +.x-datepicker-month .x-btn .x-btn-bl, +.x-datepicker-month .x-btn .x-btn-br { + background: transparent; + border-width: 0 !important; +} +.x-datepicker-month .x-btn-inner { + color: #606060; +} +.x-datepicker-month .x-btn-split-right { + background-image: url(images/datepicker/month-arrow.png); + padding-right: 8px; +} + +.x-datepicker-column-header { + width: 30px; + color: #333333; + font: bold 13px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + text-align: right; + background-image: none; + background-color: white; +} + +.x-datepicker-column-header-inner { + line-height: 25px; + padding: 0 9px 0 0; +} + +.x-datepicker-cell { + text-align: right; + border-width: 1px; + border-style: solid; + border-color: white; +} + +.x-datepicker-date { + padding: 0 7px 0 0; + font: normal 13px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #333333; + cursor: pointer; + line-height: 23px; +} + +a.x-datepicker-date:hover { + color: #333333; + background-color: #eeeeee; +} + +.x-datepicker-selected { + border-style: solid; + border-color: #606060; +} +.x-datepicker-selected .x-datepicker-date { + background-color: #dedede; + font-weight: bold; +} + +.x-datepicker-today { + border-color: darkred; + border-style: solid; +} + +.x-datepicker-prevday .x-datepicker-date, +.x-datepicker-nextday .x-datepicker-date { + color: #f2f2f2; +} + +.x-datepicker-disabled a.x-datepicker-date { + background-color: #eeeeee; + cursor: default; + color: #b3b3b3; +} + +.x-datepicker-disabled a.x-datepicker-date:hover { + background-color: #eeeeee; +} + +.x-datepicker-footer, +.x-monthpicker-buttons { + padding: 3px 0; + background-image: none; + background-color: #f5f5f5; + text-align: center; +} +.x-datepicker-footer .x-btn, +.x-monthpicker-buttons .x-btn { + margin: 0 3px 0 2px; +} + +.x-monthpicker { + width: 212px; + border-width: 1px; + border-style: solid; + border-color: #e1e1e1; + background-color: white; +} + +.x-monthpicker-months { + border-width: 0 1px 0 0; + border-color: #e1e1e1; + border-style: solid; + width: 105px; +} +.x-monthpicker-months .x-monthpicker-item { + width: 52px; +} + +.x-monthpicker-years { + width: 105px; +} +.x-monthpicker-years .x-monthpicker-item { + width: 52px; +} + +.x-monthpicker-item { + margin: 5px 0 5px; + font: normal 13px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + text-align: center; +} + +.x-monthpicker-item-inner { + margin: 0 5px 0 5px; + color: #333333; + border-width: 1px; + border-style: solid; + border-color: white; + line-height: 22px; + cursor: pointer; +} + +a.x-monthpicker-item-inner:hover { + background-color: #eeeeee; +} + +.x-monthpicker-selected { + background-color: #dedede; + border-style: solid; + border-color: #606060; +} + +.x-monthpicker-yearnav { + height: 34px; +} + +.x-monthpicker-yearnav-button-ct { + width: 52px; +} + +.x-monthpicker-yearnav-button { + height: 12px; + width: 12px; + cursor: pointer; + margin-top: 11px; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70); + opacity: 0.7; + background-color: white; +} + +a.x-monthpicker-yearnav-button:hover { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); + opacity: 1; +} + +.x-monthpicker-yearnav-next { + background-image: url(images/datepicker/arrow-right.png); + background-position: 0 0; +} + +.x-monthpicker-yearnav-next-over { + background-position: 0 0; +} + +.x-monthpicker-yearnav-prev { + background-image: url(images/datepicker/arrow-left.png); + background-position: 0 0; +} + +.x-monthpicker-yearnav-prev-over { + background-position: 0 0; +} + +.x-monthpicker-small .x-monthpicker-item { + margin: 2px 0 2px; +} +.x-monthpicker-small .x-monthpicker-item-inner { + margin: 0 5px 0 5px; +} +.x-monthpicker-small .x-monthpicker-yearnav { + height: 28px; +} +.x-monthpicker-small .x-monthpicker-yearnav-button { + margin-top: 8px; +} + +/**/ +.x-datepicker-header:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-datepicker-footer:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +.x-form-date-trigger { + background-image: url(images/form/date-trigger.png); +} + +.x-form-file-wrap .x-form-text { + color: gray; +} + +.x-color-picker { + width: 192px; + height: 120px; + background-color: white; + border-color: white; + border-width: 0; + border-style: solid; +} + +.x-color-picker-item { + width: 24px; + height: 24px; + border-width: 1px; + border-color: white; + border-style: solid; + background-color: white; + cursor: pointer; + padding: 2px; +} +.x-content-box .x-color-picker-item { + width: 18px; + height: 18px; +} + +a.x-color-picker-item:hover { + border-color: #8bb8f3; + background-color: #e6e6e6; +} + +.x-color-picker-selected { + border-color: #8bb8f3; + background-color: #e6e6e6; +} + +.x-color-picker-item-inner { + line-height: 16px; + border-color: #e1e1e1; + border-width: 1px; + border-style: solid; +} + +.x-html-editor-tb .x-btn-text { + background: transparent no-repeat; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-bold, +.x-menu-item div.x-edit-bold { + background-position: 0 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-italic, +.x-menu-item div.x-edit-italic { + background-position: -16px 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-underline, +.x-menu-item div.x-edit-underline { + background-position: -32px 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-forecolor, +.x-menu-item div.x-edit-forecolor { + background-position: -160px 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-backcolor, +.x-menu-item div.x-edit-backcolor { + background-position: -176px 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-justifyleft, +.x-menu-item div.x-edit-justifyleft { + background-position: -112px 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-justifycenter, +.x-menu-item div.x-edit-justifycenter { + background-position: -128px 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-justifyright, +.x-menu-item div.x-edit-justifyright { + background-position: -144px 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-insertorderedlist, +.x-menu-item div.x-edit-insertorderedlist { + background-position: -80px 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-insertunorderedlist, +.x-menu-item div.x-edit-insertunorderedlist { + background-position: -96px 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-increasefontsize, +.x-menu-item div.x-edit-increasefontsize { + background-position: -48px 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-decreasefontsize, +.x-menu-item div.x-edit-decreasefontsize { + background-position: -64px 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-sourceedit, +.x-menu-item div.x-edit-sourceedit { + background-position: -192px 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tb .x-edit-createlink, +.x-menu-item div.x-edit-createlink { + background-position: -208px 0; + background-image: url(images/editor/tb-sprite.png); +} + +.x-html-editor-tip .x-tip-bd .x-tip-bd-inner { + padding: 5px; + padding-bottom: 1px; +} + +.x-html-editor-tb .x-font-select { + font-size: 13px; + font-family: inherit; +} + +.x-html-editor-wrap textarea { + font: normal 13px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + background-color: white; + resize: none; +} + +.x-grid-drop-indicator { + position: absolute; + height: 1px; + line-height: 0px; + background-color: #77BC71; + overflow: visible; + pointer-events: none; +} +.x-grid-drop-indicator .x-grid-drop-indicator-left { + position: absolute; + top: -8px; + left: -12px; + background-image: url(images/grid/dd-insert-arrow-right.png); + height: 16px; + width: 16px; +} +.x-grid-drop-indicator .x-grid-drop-indicator-right { + position: absolute; + top: -8px; + right: -11px; + background-image: url(images/grid/dd-insert-arrow-left.png); + height: 16px; + width: 16px; +} + +.x-ie6 .x-grid-drop-indicator-left { + background-image: url(images/grid/dd-insert-arrow-right.png); +} +.x-ie6 .x-grid-drop-indicator-right { + background-image: url(images/grid/dd-insert-arrow-left.png); +} + +.x-grid-cell-inner-action-col { + padding: 4px 4px 4px 4px; +} + +.x-action-col-cell .x-item-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30); + opacity: 0.3; +} + +.x-action-col-icon { + height: 16px; + width: 16px; + cursor: pointer; +} + +.x-grid-cell-inner-checkcolumn { + padding: 5px 10px 4px 10px; +} + +.x-grid-checkcolumn { + width: 15px; + height: 15px; + background: url(images/form/checkbox.png) 0 0 no-repeat; +} +.x-item-disabled .x-grid-checkcolumn { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30); + opacity: 0.3; +} + +.x-grid-checkcolumn-checked { + background-position: 0 -15px; +} + +.x-grid-cell-inner-row-numberer { + padding: 5px 5px 4px 3px; +} + +.x-grid-group-hd { + border-width: 0 0 1px 0; + border-style: solid; + border-color: silver; + padding: 8px 4px 8px 4px; + background: #f5f5f5; + cursor: pointer; +} + +.x-grid-group-hd-not-collapsible { + cursor: default; +} + +.x-grid-group-hd-collapsible .x-grid-group-title { + background-repeat: no-repeat; + background-position: left center; + background-image: url(images/grid/group-collapse.png); + padding: 0 0 0 17px; +} + +.x-grid-group-title { + color: #666666; + font: bold 13px/15px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.x-grid-group-hd-collapsed .x-grid-group-title { + background-image: url(images/grid/group-expand.png); +} + +.x-grid-group-collapsed .x-grid-group-title { + background-image: url(images/grid/group-expand.png); +} + +.x-group-by-icon { + background-image: url(images/grid/group-by.png); +} + +.x-show-groups-icon { + background-image: url(images/grid/group-by.png); +} + +.x-docked-summary { + background: transparent !important; +} +.x-docked-summary .x-grid-table { + width: 100%; + border: 0 none; +} + +.x-grid-row-summary .x-grid-cell, +.x-grid-row-summary .x-grid-rowwrap, +.x-grid-row-summary .x-grid-cell-rowbody { + border-color: #ededed; + background-color: transparent !important; + border-top-width: 0; + font: normal 13px/15px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.x-docked-summary-bottom .x-grid-row-summary .x-grid-cell { + border-bottom: 0 none; +} + +.x-docked-summary-top .x-grid-row-summary .x-grid-cell { + border-top: 0 none; +} + +.x-grid-with-row-lines .x-grid-table-summary { + border: 0; +} + +.x-grid-locked .x-grid-inner-locked { + border-width: 0 1px 0 0; + border-style: solid; +} + +.x-grid-locked-split .x-grid-inner-normal { + border-width: 0 0 0 1px; + border-style: solid; +} + +.x-grid-inner-locked .x-column-header-last, +.x-grid-inner-locked .x-grid-cell-last { + border-right-width: 0!important; +} + +.x-hmenu-lock { + background-image: url(images/grid/hmenu-lock.png); +} + +.x-hmenu-unlock { + background-image: url(images/grid/hmenu-unlock.png); +} + +.x-grid-editor .x-form-text { + font: normal 13px/15px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 4px 9px 3px 9px; +} +.x-gecko .x-grid-editor .x-form-text { + padding-left: 8px; + padding-right: 8px; +} +.x-grid-editor .x-form-display-field { + height: 24px; +} +.x-grid-editor .x-form-display-field { + font: normal 13px/15px "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 5px 10px 4px 10px; + text-overflow: ellipsis; +} +.x-grid-editor .x-form-action-col-field { + padding: 4px 4px 4px 4px; +} + +.x-tree-cell-editor .x-form-text { + padding-left: 3px; + padding-right: 3px; +} +.x-gecko .x-tree-cell-editor .x-form-text { + padding-left: 2px; + padding-right: 2px; +} + +.x-grid-row-editor .x-field { + margin: 0 3px 0 2px; +} +.x-grid-row-editor .x-form-display-field { + padding: 5px 7px 4px 8px; +} +.x-grid-row-editor .x-form-action-col-field { + padding: 4px 1px 4px 2px; +} +.x-grid-row-editor .x-form-text { + padding: 4px 6px 3px 7px; +} +.x-gecko .x-grid-row-editor .x-form-text { + padding-left: 6px; + padding-right: 5px; +} +.x-grid-row-editor .x-panel-body { + border-top: 1px solid #e1e1e1 !important; + border-bottom: 1px solid #e1e1e1 !important; + padding: 5px 0 5px 0; + background-color: #e4e4e4; +} +.x-grid-with-col-lines .x-grid-row-editor .x-form-cb { + margin-right: 1px; +} + +.x-grid-row-editor-buttons-default-bottom { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 5px; + -webkit-border-bottom-right-radius: 5px; + border-bottom-right-radius: 5px; + -moz-border-radius-bottomleft: 5px; + -webkit-border-bottom-left-radius: 5px; + border-bottom-left-radius: 5px; + padding: 5px 5px 5px 5px; + border-width: 0 1px 1px 1px; + border-style: solid; + background-color: #e4e4e4; +} + +.x-grid-row-editor-buttons-default-bottom-mc { + background-color: #e4e4e4; +} + +.x-nbr .x-grid-row-editor-buttons-default-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-grid-row-editor-buttons-default-bottom-frameInfo { + font-family: th-0-0-5-5-0-1-1-1-5-5-5-5; +} + +.x-grid-row-editor-buttons-default-bottom-tl { + background-position: 0 -10px; +} + +.x-grid-row-editor-buttons-default-bottom-tr { + background-position: right -15px; +} + +.x-grid-row-editor-buttons-default-bottom-bl { + background-position: 0 -20px; +} + +.x-grid-row-editor-buttons-default-bottom-br { + background-position: right -25px; +} + +.x-grid-row-editor-buttons-default-bottom-ml { + background-position: 0 top; +} + +.x-grid-row-editor-buttons-default-bottom-mr { + background-position: right top; +} + +.x-grid-row-editor-buttons-default-bottom-tc { + background-position: 0 0; +} + +.x-grid-row-editor-buttons-default-bottom-bc { + background-position: 0 -5px; +} + +.x-grid-row-editor-buttons-default-bottom-tr, +.x-grid-row-editor-buttons-default-bottom-br, +.x-grid-row-editor-buttons-default-bottom-mr { + padding-right: 5px; +} + +.x-grid-row-editor-buttons-default-bottom-tl, +.x-grid-row-editor-buttons-default-bottom-bl, +.x-grid-row-editor-buttons-default-bottom-ml { + padding-left: 5px; +} + +.x-grid-row-editor-buttons-default-bottom-tc { + height: 0; +} + +.x-grid-row-editor-buttons-default-bottom-bc { + height: 5px; +} + +.x-grid-row-editor-buttons-default-bottom-tl, +.x-grid-row-editor-buttons-default-bottom-bl, +.x-grid-row-editor-buttons-default-bottom-tr, +.x-grid-row-editor-buttons-default-bottom-br, +.x-grid-row-editor-buttons-default-bottom-tc, +.x-grid-row-editor-buttons-default-bottom-bc, +.x-grid-row-editor-buttons-default-bottom-ml, +.x-grid-row-editor-buttons-default-bottom-mr { + zoom: 1; + background-image: url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-corners.gif); +} + +.x-grid-row-editor-buttons-default-bottom-ml, +.x-grid-row-editor-buttons-default-bottom-mr { + zoom: 1; + background-image: url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-grid-row-editor-buttons-default-bottom-mc { + padding: 5px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-grid-row-editor-buttons-default-bottom-tl, +.x-strict .x-ie7 .x-grid-row-editor-buttons-default-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-grid-row-editor-buttons-default-bottom:after { + display: none; + content: "x-slicer:corners:url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-corners.gif), sides:url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-grid-row-editor-buttons-default-top { + -moz-border-radius-topleft: 5px; + -webkit-border-top-left-radius: 5px; + border-top-left-radius: 5px; + -moz-border-radius-topright: 5px; + -webkit-border-top-right-radius: 5px; + border-top-right-radius: 5px; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 5px 5px 5px 5px; + border-width: 1px 1px 0 1px; + border-style: solid; + background-color: #e4e4e4; +} + +.x-grid-row-editor-buttons-default-top-mc { + background-color: #e4e4e4; +} + +.x-nbr .x-grid-row-editor-buttons-default-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-grid-row-editor-buttons-default-top-frameInfo { + font-family: th-5-5-0-0-1-1-0-1-5-5-5-5; +} + +.x-grid-row-editor-buttons-default-top-tl { + background-position: 0 -10px; +} + +.x-grid-row-editor-buttons-default-top-tr { + background-position: right -15px; +} + +.x-grid-row-editor-buttons-default-top-bl { + background-position: 0 -20px; +} + +.x-grid-row-editor-buttons-default-top-br { + background-position: right -25px; +} + +.x-grid-row-editor-buttons-default-top-ml { + background-position: 0 top; +} + +.x-grid-row-editor-buttons-default-top-mr { + background-position: right top; +} + +.x-grid-row-editor-buttons-default-top-tc { + background-position: 0 0; +} + +.x-grid-row-editor-buttons-default-top-bc { + background-position: 0 -5px; +} + +.x-grid-row-editor-buttons-default-top-tr, +.x-grid-row-editor-buttons-default-top-br, +.x-grid-row-editor-buttons-default-top-mr { + padding-right: 5px; +} + +.x-grid-row-editor-buttons-default-top-tl, +.x-grid-row-editor-buttons-default-top-bl, +.x-grid-row-editor-buttons-default-top-ml { + padding-left: 5px; +} + +.x-grid-row-editor-buttons-default-top-tc { + height: 5px; +} + +.x-grid-row-editor-buttons-default-top-bc { + height: 0; +} + +.x-grid-row-editor-buttons-default-top-tl, +.x-grid-row-editor-buttons-default-top-bl, +.x-grid-row-editor-buttons-default-top-tr, +.x-grid-row-editor-buttons-default-top-br, +.x-grid-row-editor-buttons-default-top-tc, +.x-grid-row-editor-buttons-default-top-bc, +.x-grid-row-editor-buttons-default-top-ml, +.x-grid-row-editor-buttons-default-top-mr { + zoom: 1; + background-image: url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-corners.gif); +} + +.x-grid-row-editor-buttons-default-top-ml, +.x-grid-row-editor-buttons-default-top-mr { + zoom: 1; + background-image: url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-sides.gif); + background-repeat: repeat-y; +} + +.x-grid-row-editor-buttons-default-top-mc { + padding: 1px 1px 5px 1px; +} + +.x-strict .x-ie7 .x-grid-row-editor-buttons-default-top-tl, +.x-strict .x-ie7 .x-grid-row-editor-buttons-default-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-grid-row-editor-buttons-default-top:after { + display: none; + content: "x-slicer:corners:url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-corners.gif), sides:url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-sides.gif)"; +} + +/**/ +/* */ +.x-grid-row-editor-buttons { + border-color: #e1e1e1; +} + +.x-row-editor-update-button { + margin-right: 3px; +} + +.x-row-editor-cancel-button { + margin-left: 2px; +} + +.x-grid-row-editor-errors .x-tip-body { + padding: 5px; +} + +.x-grid-row-editor-errors-item { + list-style: disc; + margin-left: 15px; +} + +.x-accordion-layout-ct { + background-color: white; + padding: 5px 5px 0; +} + +.x-accordion-hd .x-panel-header-text-container { + color: #666666; + font-weight: bold; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + text-transform: none; +} + +.x-accordion-item { + margin: 0 0 5px; +} +.x-accordion-item .x-accordion-hd { + background: #e4e4e4; + border-top-color: white; + padding: 8px 10px; +} +.x-accordion-item .x-accordion-hd-sibling-expanded { + border-top-color: #444444; +} +.x-accordion-item .x-accordion-hd-last-collapsed { + border-bottom-color: #e4e4e4; +} +.x-accordion-item .x-accordion-body { + border-width: 0; +} + +.x-accordion-hd .x-tool-collapse-top, +.x-accordion-hd .x-tool-collapse-bottom { + background-position: 0 -272px; +} +.x-accordion-hd .x-tool-expand-top, +.x-accordion-hd .x-tool-expand-bottom { + background-position: 0 -256px; +} +.x-accordion-hd .x-tool-img { + background-image: url(images/tools/tool-sprites-dark.png); + background-color: #e4e4e4; +} + +.x-collapse-el { + cursor: pointer; +} + +.x-layout-split-left, +.x-layout-split-right { + top: 50%; + margin-top: -24px; + width: 8px; + height: 48px; +} + +.x-layout-split-top, +.x-layout-split-bottom { + left: 50%; + width: 48px; + height: 8px; + margin-left: -24px; +} + +.x-layout-split-left { + background-image: url(images/util/splitter/mini-left.png); +} + +.x-layout-split-right { + background-image: url(images/util/splitter/mini-right.png); +} + +.x-layout-split-top { + background-image: url(images/util/splitter/mini-top.png); +} + +.x-layout-split-bottom { + background-image: url(images/util/splitter/mini-bottom.png); +} + +.x-splitter-collapsed .x-layout-split-left { + background-image: url(images/util/splitter/mini-right.png); +} +.x-splitter-collapsed .x-layout-split-right { + background-image: url(images/util/splitter/mini-left.png); +} +.x-splitter-collapsed .x-layout-split-top { + background-image: url(images/util/splitter/mini-bottom.png); +} +.x-splitter-collapsed .x-layout-split-bottom { + background-image: url(images/util/splitter/mini-top.png); +} + +.x-splitter-active { + background-color: #b4b4b4; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); + opacity: 0.8; +} +.x-splitter-active .x-collapse-el { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30); + opacity: 0.3; +} + +.x-border-layout-ct { + background-color: #606060; +} + +.x-tool { + cursor: pointer; +} + +.x-tool-img { + overflow: hidden; + width: 16px; + height: 16px; + background-image: url(images/tools/tool-sprites.png); + margin: 0; +} +.x-tool .x-tool-img { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} +.x-tool-over .x-tool-img { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60); + opacity: 0.6; +} +.x-tool-pressed .x-tool-img { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70); + opacity: 0.7; +} + +.x-tool-placeholder { + visibility: hidden; +} + +.x-tool-close { + background-position: 0 0; +} + +.x-tool-minimize { + background-position: 0 -16px; +} + +.x-tool-maximize { + background-position: 0 -32px; +} + +.x-tool-restore { + background-position: 0 -48px; +} + +.x-tool-toggle { + background-position: 0 -64px; +} +.x-panel-collapsed .x-tool-toggle { + background-position: 0 -80px; +} + +.x-tool-gear { + background-position: 0 -96px; +} + +.x-tool-prev { + background-position: 0 -112px; +} + +.x-tool-next { + background-position: 0 -128px; +} + +.x-tool-pin { + background-position: 0 -144px; +} + +.x-tool-unpin { + background-position: 0 -160px; +} + +.x-tool-right { + background-position: 0 -176px; +} + +.x-tool-left { + background-position: 0 -192px; +} + +.x-tool-down { + background-position: 0 -208px; +} + +.x-tool-up { + background-position: 0 -224px; +} + +.x-tool-refresh { + background-position: 0 -240px; +} + +.x-tool-plus { + background-position: 0 -256px; +} + +.x-tool-minus { + background-position: 0 -272px; +} + +.x-tool-search { + background-position: 0 -288px; +} + +.x-tool-save { + background-position: 0 -304px; +} + +.x-tool-help { + background-position: 0 -320px; +} + +.x-tool-print { + background-position: 0 -336px; +} + +.x-tool-expand { + background-position: 0 -352px; +} + +.x-tool-collapse { + background-position: 0 -368px; +} + +.x-tool-resize { + background-position: 0 -384px; +} + +.x-tool-move { + background-position: 0 -400px; +} + +.x-tool-expand-bottom, +.x-tool-collapse-bottom { + background-position: 0 -208px; +} + +.x-tool-expand-top, +.x-tool-collapse-top { + background-position: 0 -224px; +} + +.x-tool-expand-left, +.x-tool-collapse-left { + background-position: 0 -192px; +} + +.x-tool-expand-right, +.x-tool-collapse-right { + background-position: 0 -176px; +} + +.x-resizable-handle { + position: absolute; + z-index: 100; + font-size: 1px; + line-height: 2px; + overflow: hidden; + zoom: 1; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; + background-color: #fff; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + -ms-border-radius: 2px; + -o-border-radius: 2px; + border-radius: 2px; +} + +.x-collapsed .x-resizable-handle { + display: none; +} + +.x-resizable-handle-north { + cursor: n-resize; +} + +.x-resizable-handle-south { + cursor: s-resize; +} + +.x-resizable-handle-east { + cursor: e-resize; +} + +.x-resizable-handle-west { + cursor: w-resize; +} + +.x-resizable-handle-southeast { + cursor: se-resize; +} + +.x-resizable-handle-northwest { + cursor: nw-resize; +} + +.x-resizable-handle-northeast { + cursor: ne-resize; +} + +.x-resizable-handle-southwest { + cursor: sw-resize; +} + +.x-resizable-handle-east { + width: 2px; + height: 100%; + right: 0; + top: 0; +} + +.x-resizable-handle-south { + width: 100%; + height: 2px; + left: 0; + bottom: 0; +} + +.x-resizable-handle-west { + width: 2px; + height: 100%; + left: 0; + top: 0; +} + +.x-resizable-handle-north { + width: 100%; + height: 2px; + left: 0; + top: 0; +} + +.x-resizable-handle-southeast { + width: 2px; + height: 2px; + right: 0; + bottom: 0; + z-index: 101; +} + +.x-resizable-handle-northwest { + width: 2px; + height: 2px; + left: 0; + top: 0; + z-index: 101; +} + +.x-resizable-handle-northeast { + width: 2px; + height: 2px; + right: 0; + top: 0; + z-index: 101; +} + +.x-resizable-handle-southwest { + width: 2px; + height: 2px; + left: 0; + bottom: 0; + z-index: 101; +} + +.x-window .x-window-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-window-collapsed .x-window-handle { + display: none; +} + +.x-resizable-proxy { + border: 1px dashed #3b5a82; + position: absolute; + overflow: hidden; + z-index: 50000; +} + +.x-resizable-handle-over, +.x-resizable-pinned .x-resizable-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); + opacity: 1; +} + +.x-resizable-handle-east-over, +.x-resizable-handle-west-over { + background-image: url(images/sizer/e-handle.png); +} + +.x-resizable-handle-south-over, +.x-resizable-handle-north-over { + background-image: url(images/sizer/s-handle.png); +} + +.x-resizable-handle-southeast-over { + background-position: top left; + background-image: url(images/sizer/se-handle.png); +} + +.x-resizable-handle-northwest-over { + background-position: bottom right; + background-image: url(images/sizer/nw-handle.png); +} + +.x-resizable-handle-northeast-over { + background-position: bottom left; + background-image: url(images/sizer/ne-handle.png); +} + +.x-resizable-handle-southwest-over { + background-position: top right; + background-image: url(images/sizer/sw-handle.png); +} + +.x-resizable-pinned .x-resizable-handle-east, +.x-resizable-pinned .x-resizable-handle-west { + background-image: url(images/sizer/e-handle.png); +} +.x-resizable-pinned .x-resizable-handle-south, +.x-resizable-pinned .x-resizable-handle-north { + background-image: url(images/sizer/s-handle.png); +} +.x-resizable-pinned .x-resizable-handle-southeast { + background-position: top left; + background-image: url(images/sizer/se-handle.png); +} +.x-resizable-pinned .x-resizable-handle-northwest { + background-position: bottom right; + background-image: url(images/sizer/nw-handle.png); +} +.x-resizable-pinned .x-resizable-handle-northeast { + background-position: bottom left; + background-image: url(images/sizer/ne-handle.png); +} +.x-resizable-pinned .x-resizable-handle-southwest { + background-position: top right; + background-image: url(images/sizer/sw-handle.png); +} + +/** + * Creates a visual theme for a Tab + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {color} [$ui-background-color=$tab-base-color] + * The background-color of Tabs + * + * @param {color} [$ui-background-color-over=$tab-base-color-over] + * The background-color of hovered Tabs + * + * @param {color} [$ui-background-color-active=$tab-base-color-active] + * The background-color of the active Tab + * + * @param {color} [$ui-background-color-disabled=$tab-base-color-disabled] + * The background-color of disabled Tabs + * + * @param {list} [$ui-border-radius=$tab-border-radius] + * The border-radius of Tabs + * + * @param {number} [$ui-border-width=$tab-border-width] + * The border-width of Tabs + * + * @param {number/list} [$ui-margin=$tab-margin] + * The border-width of Tabs + * + * @param {number/list} [$ui-padding=$tab-padding] + * The padding of Tabs + * + * @param {number/list} [$ui-text-padding=$tab-text-padding] + * The padding of the Tab's text element + * + * @param {color} [$ui-border-color=$tab-border-color] + * The border-color of Tabs + * + * @param {color} [$ui-border-color-over=$tab-border-color-over] + * The border-color of hovered Tabs + * + * @param {color} [$ui-border-color-active=$tab-border-color-active] + * The border-color of the active Tab + * + * @param {color} [$ui-border-color-disabled=$tab-border-color-disabled] + * The border-color of disabled Tabs + * + * @param {string} [$ui-cursor=$tab-cursor] + * The Tab cursor + * + * @param {string} [$ui-cursor-disabled=$tab-cursor-disabled] + * The cursor of disabled Tabs + * + * @param {number} [$ui-font-size=$tab-font-size] + * The font-size of Tabs + * + * @param {number} [$ui-font-size-over=$tab-font-size-over] + * The font-size of hovered Tabs + * + * @param {number} [$ui-font-size-active=$tab-font-size-active] + * The font-size of the active Tab + * + * @param {number} [$ui-font-size-disabled=$tab-font-size-disabled] + * The font-size of disabled Tabs + * + * @param {string} [$ui-font-weight=$tab-font-weight] + * The font-weight of Tabs + * + * @param {string} [$ui-font-weight-over=$tab-font-weight-over] + * The font-weight of hovered Tabs + * + * @param {string} [$ui-font-weight-active=$tab-font-weight-active] + * The font-weight of the active Tab + * + * @param {string} [$ui-font-weight-disabled=$tab-font-weight-disabled] + * The font-weight of disabled Tabs + * + * @param {string} [$ui-font-family=$tab-font-family] + * The font-family of Tabs + * + * @param {string} [$ui-font-family-over=$tab-font-family-over] + * The font-family of hovered Tabs + * + * @param {string} [$ui-font-family-active=$tab-font-family-active] + * The font-family of the active Tab + * + * @param {string} [$ui-font-family-disabled=$tab-font-family-disabled] + * The font-family of disabled Tabs + * + * @param {number} [$ui-line-height=$tab-line-height] + * The line-height of Tabs + * + * @param {color} [$ui-color=$tab-color] + * The text color of Tabs + * + * @param {color} [$ui-color-over=$tab-color-over] + * The text color of hovered Tabs + * + * @param {color} [$ui-color-active=$tab-color-active] + * The text color of the active Tab + * + * @param {color} [$ui-color-disabled=$tab-color-disabled] + * The text color of disabled Tabs + * + * @param {string/list} [$ui-background-gradient=$tab-background-gradient] + * The background-gradient for Tabs. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$ui-background-gradient-over=$tab-background-gradient-over] + * The background-gradient for hovered Tabs. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$ui-background-gradient-active=$tab-background-gradient-active] + * The background-gradient for the active Tab. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$ui-background-gradient-disabled=$tab-background-gradient-disabled] + * The background-gradient for disabled Tabs. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {number} [$ui-inner-border-width=$tab-inner-border-width] + * The inner border-width of Tabs + * + * @param {color} [$ui-inner-border-color=$tab-inner-border-color] + * The inner border-color of Tabs + * + * @param {number} [$ui-icon-width=$tab-icon-width] + * The width of the Tab close icon + * + * @param {number} [$ui-icon-height=$tab-icon-height] + * The height of the Tab close icon + * + * @param {number} [$ui-icon-spacing=$tab-icon-spacing] + * the space in between the text and the close button + * + * @param {list} [$ui-icon-background-position=$tab-icon-background-position] + * The background-position of Tab icons + * + * @param {color} [$ui-glyph-color=$tab-glyph-color] + * The color of Tab glyph icons + * + * @param {color} [$ui-glyph-color-over=$tab-glyph-color-over] + * The color of a Tab glyph icon when the Tab is hovered + * + * @param {color} [$ui-glyph-color-active=$tab-glyph-color-active] + * The color of a Tab glyph icon when the Tab is active + * + * @param {color} [$ui-glyph-color-disabled=$tab-glyph-color-disabled] + * The color of a Tab glyph icon when the Tab is disabled + * + * @param {number} [$ui-glyph-opacity=$tab-glyph-opacity] + * The opacity of a Tab glyph icon + * + * @param {number} [$ui-glyph-opacity-disabled=$tab-glyph-opacity-disabled] + * The opacity of a Tab glyph icon when the Tab is disabled + * + * @param {number} [$ui-opacity-disabled=$tab-opacity-disabled] + * opacity to apply to the tab's main element when the tab is disabled + * + * @param {number} [$ui-text-opacity-disabled=$tab-text-opacity-disabled] + * opacity to apply to the tab's text element when the tab is disabled + * + * @param {number} [$ui-icon-opacity-disabled=$tab-icon-opacity-disabled] + * opacity to apply to the tab's icon element when the tab is disabled + * + * @param {number} [$ui-closable-icon-width=$tab-closable-icon-width] + * The width of the Tab close icon + * + * @param {number} [$ui-closable-icon-height=$tab-closable-icon-height] + * The height of the Tab close icon + * + * @param {number} [$ui-closable-icon-top=$tab-closable-icon-top] + * The distance to offset the Tab close icon from the top of the tab + * + * @param {number} [$ui-closable-icon-right=$tab-closable-icon-right] + * The distance to offset the Tab close icon from the right of the tab + * + * @param {number} [$ui-closable-icon-spacing=$tab-closable-icon-spacing] + * The space in between the text and the close button + * + * @param {color} [$ui-border-bottom-color=$tabbar-strip-border-color] + * The bottom border color of inactive tabs. + * + * @member Ext.tab.Tab + */ +/** + * Creates a visual theme for a Tab Bar + * + * Note: When creating a tab bar UI with the extjs-tab-bar-ui mixin, + * you will need to create a corresponding tab-ui of the same name. + * This will ensure that the tabs render properly in your theme. + * Not creating a matching tab theme may result in unpredictable + * tab rendering. + * + * See `Ext.tab.Tab-css_mixin-extjs-tab-ui` + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {number} [$ui-strip-height=$tabbar-strip-height] + * The height of the Tab Bar strip + * + * @param {number/list} [$ui-strip-border-width=$tabbar-strip-border-width] + * The border-width of the Tab Bar strip + * + * @param {number/list} [$ui-strip-plain-border-width=$tabbar-strip-plain-border-width] + * The border-width of the {@link Ext.tab.Panel#plain plain} Tab Bar strip + * + * @param {color} [$ui-strip-border-color=$tabbar-strip-border-color] + * The border-color of the Tab Bar strip + * + * @param {color} [$ui-strip-background-color=$tabbar-strip-background-color] + * The background-color of the Tab Bar strip + * + * @param {number/list} [$ui-border-width=$tabbar-border-width] + * The border-width of the Tab Bar + * + * @param {color} [$ui-border-color=$tabbar-border-color] + * The border-color of the Tab Bar + * + * @param {number/list} [$ui-padding=$tabbar-padding] + * The padding of the Tab Bar + * + * @param {color} [$ui-background-color=$tabbar-background-color] + * The background color of the Tab Bar + * + * @param {string/list} [$ui-background-gradient=$tabbar-background-gradient] + * The background-gradient of the Tab Bar. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {number} [$ui-scroller-width=$tabbar-scroller-width] + * The width of the Tab Bar scrollers + * + * @param {string} [$ui-scroller-cursor=$tabbar-scroller-cursor] + * The cursor of the Tab Bar scrollers + * + * @param {string} [$ui-scroller-cursor-disabled=$tabbar-scroller-cursor-disabled] + * The cursor of disabled Tab Bar scrollers + * + * @param {number} [$ui-scroller-opacity=$tabbar-scroller-opacity] + * The opacity of Tab Bar scrollers + * + * @param {number} [$ui-scroller-opacity-over=$tabbar-scroller-opacity-over] + * The opacity of hovered Tab Bar scrollers + * + * @param {number} [$ui-scroller-opacity-pressed=$tabbar-scroller-opacity-pressed] + * The opacity of pressed Tab Bar scrollers + * + * @param {number} [$ui-scroller-opacity-disabled=$tabbar-scroller-opacity-disabled] + * The opacity of disabled Tab Bar scrollers + * + * @param {number} [$ui-tab-height] + * The height of tabs that will be used in this tabbar UI. The tabbar body is given + * a fixed height to leave room for the tabs, and so that the tabbar does not collapse + * when it does not contain any tabs. + * + * @member Ext.tab.Bar + */ +/** + * Creates a visual theme for a Tab Panel + * + * @param {string} $ui + * The name of the UI being created. Can not included spaces or special punctuation + * (used in CSS class names). + * + * @param {color} [$ui-tab-background-color=$tab-base-color] + * The background-color of Tabs + * + * @param {color} [$ui-tab-background-color-over=$tab-base-color-over] + * The background-color of hovered Tabs + * + * @param {color} [$ui-tab-background-color-active=$tab-base-color-active] + * The background-color of the active Tab + * + * @param {color} [$ui-tab-background-color-disabled=$tab-base-color-disabled] + * The background-color of disabled Tabs + * + * @param {list} [$ui-tab-border-radius=$tab-border-radius] + * The border-radius of Tabs + * + * @param {number} [$ui-tab-border-width=$tab-border-width] + * The border-width of Tabs + * + * @param {number/list} [$ui-tab-margin=$tab-margin] + * The border-width of Tabs + * + * @param {number/list} [$ui-tab-padding=$tab-padding] + * The padding of Tabs + * + * @param {number/list} [$ui-tab-text-padding=$tab-text-padding] + * The padding of the Tab's text element + * + * @param {color} [$ui-tab-border-color=$tab-border-color] + * The border-color of Tabs + * + * @param {color} [$ui-tab-border-color-over=$tab-border-color-over] + * The border-color of hovered Tabs + * + * @param {color} [$ui-tab-border-color-active=$tab-border-color-active] + * The border-color of the active Tab + * + * @param {color} [$ui-tab-border-color-disabled=$tab-border-color-disabled] + * The border-color of disabled Tabs + * + * @param {string} [$ui-tab-cursor=$tab-cursor] + * The Tab cursor + * + * @param {string} [$ui-tab-cursor-disabled=$tab-cursor-disabled] + * The cursor of disabled Tabs + * + * @param {number} [$ui-tab-font-size=$tab-font-size] + * The font-size of Tabs + * + * @param {number} [$ui-tab-font-size-over=$tab-font-size-over] + * The font-size of hovered Tabs + * + * @param {number} [$ui-tab-font-size-active=$tab-font-size-active] + * The font-size of the active Tab + * + * @param {number} [$ui-tab-font-size-disabled=$tab-font-size-disabled] + * The font-size of disabled Tabs + * + * @param {string} [$ui-tab-font-weight=$tab-font-weight] + * The font-weight of Tabs + * + * @param {string} [$ui-tab-font-weight-over=$tab-font-weight-over] + * The font-weight of hovered Tabs + * + * @param {string} [$ui-tab-font-weight-active=$tab-font-weight-active] + * The font-weight of the active Tab + * + * @param {string} [$ui-tab-font-weight-disabled=$tab-font-weight-disabled] + * The font-weight of disabled Tabs + * + * @param {string} [$ui-tab-font-family=$tab-font-family] + * The font-family of Tabs + * + * @param {string} [$ui-tab-font-family-over=$tab-font-family-over] + * The font-family of hovered Tabs + * + * @param {string} [$ui-tab-font-family-active=$tab-font-family-active] + * The font-family of the active Tab + * + * @param {string} [$ui-tab-font-family-disabled=$tab-font-family-disabled] + * The font-family of disabled Tabs + * + * @param {number} [$ui-tab-line-height=$tab-line-height] + * The line-height of Tabs + * + * @param {color} [$ui-tab-color=$tab-color] + * The text color of Tabs + * + * @param {color} [$ui-tab-color-over=$tab-color-over] + * The text color of hovered Tabs + * + * @param {color} [$ui-tab-color-active=$tab-color-active] + * The text color of the active Tab + * + * @param {color} [$ui-tab-color-disabled=$tab-color-disabled] + * The text color of disabled Tabs + * + * @param {string/list} [$ui-tab-background-gradient=$tab-background-gradient] + * The background-gradient for Tabs. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$ui-tab-background-gradient-over=$tab-background-gradient-over] + * The background-gradient for hovered Tabs. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$ui-tab-background-gradient-active=$tab-background-gradient-active] + * The background-gradient for the active Tab. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {string/list} [$ui-tab-background-gradient-disabled=$tab-background-gradient-disabled] + * The background-gradient for disabled Tabs. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {number} [$ui-tab-inner-border-width=$tab-inner-border-width] + * The inner border-width of Tabs + * + * @param {color} [$ui-tab-inner-border-color=$tab-inner-border-color] + * The inner border-color of Tabs + * + * @param {number} [$ui-tab-icon-width=$tab-icon-width] + * The width of the Tab close icon + * + * @param {number} [$ui-tab-icon-height=$tab-icon-height] + * The height of the Tab close icon + * + * @param {number} [$ui-tab-icon-spacing=$tab-icon-spacing] + * the space in between the text and the close button + * + * @param {list} [$ui-tab-icon-background-position=$tab-icon-background-position] + * The background-position of Tab icons + * + * @param {color} [$ui-tab-glyph-color=$tab-glyph-color] + * The color of Tab glyph icons + * + * @param {color} [$ui-tab-glyph-color-over=$tab-glyph-color-over] + * The color of a Tab glyph icon when the Tab is hovered + * + * @param {color} [$ui-tab-glyph-color-active=$tab-glyph-color-active] + * The color of a Tab glyph icon when the Tab is active + * + * @param {color} [$ui-tab-glyph-color-disabled=$tab-glyph-color-disabled] + * The color of a Tab glyph icon when the Tab is disabled + * + * @param {number} [$ui-tab-glyph-opacity=$tab-glyph-opacity] + * The opacity of a Tab glyph icon + * + * @param {number} [$ui-tab-glyph-opacity-disabled=$tab-glyph-opacity-disabled] + * The opacity of a Tab glyph icon when the Tab is disabled + * + * @param {number} [$ui-tab-opacity-disabled=$tab-opacity-disabled] + * opacity to apply to the tab's main element when the tab is disabled + * + * @param {number} [$ui-tab-text-opacity-disabled=$tab-text-opacity-disabled] + * opacity to apply to the tab's text element when the tab is disabled + * + * @param {number} [$ui-tab-icon-opacity-disabled=$tab-icon-opacity-disabled] + * opacity to apply to the tab's icon element when the tab is disabled + * + * @param {number} [$ui-strip-height=$tabbar-strip-height] + * The height of the Tab Bar strip + * + * @param {number/list} [$ui-strip-border-width=$tabbar-strip-border-width] + * The border-width of the Tab Bar strip + * + * @param {number/list} [$ui-strip-plain-border-width=$tabbar-strip-plain-border-width] + * The border-width of the {@link Ext.tab.Panel#plain plain} Tab Bar strip + * + * @param {color} [$ui-strip-border-color=$tabbar-strip-border-color] + * The border-color of the Tab Bar strip + * + * @param {color} [$ui-strip-background-color=$tabbar-strip-background-color] + * The background-color of the Tab Bar strip + * + * @param {number/list} [$ui-bar-border-width=$tabbar-border-width] + * The border-width of the Tab Bar + * + * @param {color} [$ui-bar-border-color=$tabbar-border-color] + * The border-color of the Tab Bar + * + * @param {number/list} [$ui-bar-padding=$tabbar-padding] + * The padding of the Tab Bar + * + * @param {color} [$ui-bar-background-color=$tabbar-background-color] + * The background color of the Tab Bar + * + * @param {string/list} [$ui-bar-background-gradient=$tabbar-background-gradient] + * The background-gradient of the Tab Bar. Can be either the name of a predefined gradient + * or a list of color stops. Used as the `$type` parameter for + * {@link Global_CSS#background-gradient}. + * + * @param {number} [$ui-bar-scroller-width=$tabbar-scroller-width] + * The width of the Tab Bar scrollers + * + * @param {string} [$ui-bar-scroller-cursor=$tabbar-scroller-cursor] + * The cursor of the Tab Bar scrollers + * + * @param {string} [$ui-bar-scroller-cursor-disabled=$tabbar-scroller-cursor-disabled] + * The cursor of disabled Tab Bar scrollers + * + * @param {number} [$ui-bar-scroller-opacity=$tabbar-scroller-opacity] + * The opacity of Tab Bar scrollers + * + * @param {number} [$ui-bar-scroller-opacity-over=$tabbar-scroller-opacity-over] + * The opacity of hovered Tab Bar scrollers + * + * @param {number} [$ui-bar-scroller-opacity-pressed=$tabbar-scroller-opacity-pressed] + * The opacity of pressed Tab Bar scrollers + * + * @param {number} [$ui-bar-scroller-opacity-disabled=$tabbar-scroller-opacity-disabled] + * The opacity of disabled Tab Bar scrollers + * + * @param {number} [$ui-tab-closable-icon-width=$tab-closable-icon-width] + * The width of the Tab close icon + * + * @param {number} [$ui-tab-closable-icon-height=$tab-closable-icon-height] + * The height of the Tab close icon + * + * @param {number} [$ui-tab-closable-icon-top=$tab-closable-icon-top] + * The distance to offset the Tab close icon from the top of the tab + * + * @param {number} [$ui-tab-closable-icon-right=$tab-closable-icon-right] + * The distance to offset the Tab close icon from the right of the tab + * + * @param {number} [$ui-tab-closable-icon-spacing=$tab-closable-icon-spacing] + * the space in between the text and the close button + * + * @member Ext.tab.Panel + */ +.x-tab-default-top { + -moz-border-radius-topleft: 3px; + -webkit-border-top-left-radius: 3px; + border-top-left-radius: 3px; + -moz-border-radius-topright: 3px; + -webkit-border-top-right-radius: 3px; + border-top-right-radius: 3px; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 8px 12px 7px 12px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #6f6f6f; +} + +.x-tab-default-top-mc { + background-color: #6f6f6f; +} + +.x-nbr .x-tab-default-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-tab-default-top-frameInfo { + font-family: th-3-3-0-0-0-0-0-0-8-12-7-12; +} + +.x-tab-default-top-tl { + background-position: 0 -6px; +} + +.x-tab-default-top-tr { + background-position: right -9px; +} + +.x-tab-default-top-bl { + background-position: 0 -12px; +} + +.x-tab-default-top-br { + background-position: right -15px; +} + +.x-tab-default-top-ml { + background-position: 0 top; +} + +.x-tab-default-top-mr { + background-position: right top; +} + +.x-tab-default-top-tc { + background-position: 0 0; +} + +.x-tab-default-top-bc { + background-position: 0 -3px; +} + +.x-tab-default-top-tr, +.x-tab-default-top-br, +.x-tab-default-top-mr { + padding-right: 3px; +} + +.x-tab-default-top-tl, +.x-tab-default-top-bl, +.x-tab-default-top-ml { + padding-left: 3px; +} + +.x-tab-default-top-tc { + height: 3px; +} + +.x-tab-default-top-bc { + height: 0; +} + +.x-tab-default-top-tl, +.x-tab-default-top-bl, +.x-tab-default-top-tr, +.x-tab-default-top-br, +.x-tab-default-top-tc, +.x-tab-default-top-bc, +.x-tab-default-top-ml, +.x-tab-default-top-mr { + zoom: 1; + background-image: url(images/tab/tab-default-top-corners.gif); +} + +.x-tab-default-top-ml, +.x-tab-default-top-mr { + zoom: 1; + background-image: url(images/tab/tab-default-top-sides.gif); + background-repeat: repeat-y; +} + +.x-tab-default-top-mc { + padding: 5px 9px 7px 9px; +} + +.x-strict .x-ie7 .x-tab-default-top-tl, +.x-strict .x-ie7 .x-tab-default-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-tab-default-top:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-default-top-corners.gif), sides:url(images/tab/tab-default-top-sides.gif)"; +} + +/**/ +/* */ +.x-tab-default-bottom { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 3px; + -webkit-border-bottom-right-radius: 3px; + border-bottom-right-radius: 3px; + -moz-border-radius-bottomleft: 3px; + -webkit-border-bottom-left-radius: 3px; + border-bottom-left-radius: 3px; + padding: 8px 12px 7px 12px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #6f6f6f; +} + +.x-tab-default-bottom-mc { + background-color: #6f6f6f; +} + +.x-nbr .x-tab-default-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-tab-default-bottom-frameInfo { + font-family: th-0-0-3-3-0-0-0-0-8-12-7-12; +} + +.x-tab-default-bottom-tl { + background-position: 0 -6px; +} + +.x-tab-default-bottom-tr { + background-position: right -9px; +} + +.x-tab-default-bottom-bl { + background-position: 0 -12px; +} + +.x-tab-default-bottom-br { + background-position: right -15px; +} + +.x-tab-default-bottom-ml { + background-position: 0 top; +} + +.x-tab-default-bottom-mr { + background-position: right top; +} + +.x-tab-default-bottom-tc { + background-position: 0 0; +} + +.x-tab-default-bottom-bc { + background-position: 0 -3px; +} + +.x-tab-default-bottom-tr, +.x-tab-default-bottom-br, +.x-tab-default-bottom-mr { + padding-right: 3px; +} + +.x-tab-default-bottom-tl, +.x-tab-default-bottom-bl, +.x-tab-default-bottom-ml { + padding-left: 3px; +} + +.x-tab-default-bottom-tc { + height: 0; +} + +.x-tab-default-bottom-bc { + height: 3px; +} + +.x-tab-default-bottom-tl, +.x-tab-default-bottom-bl, +.x-tab-default-bottom-tr, +.x-tab-default-bottom-br, +.x-tab-default-bottom-tc, +.x-tab-default-bottom-bc, +.x-tab-default-bottom-ml, +.x-tab-default-bottom-mr { + zoom: 1; + background-image: url(images/tab/tab-default-bottom-corners.gif); +} + +.x-tab-default-bottom-ml, +.x-tab-default-bottom-mr { + zoom: 1; + background-image: url(images/tab/tab-default-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-tab-default-bottom-mc { + padding: 8px 9px 4px 9px; +} + +.x-strict .x-ie7 .x-tab-default-bottom-tl, +.x-strict .x-ie7 .x-tab-default-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-tab-default-bottom:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-default-bottom-corners.gif), sides:url(images/tab/tab-default-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-tab-default-left { + -moz-border-radius-topleft: 3px; + -webkit-border-top-left-radius: 3px; + border-top-left-radius: 3px; + -moz-border-radius-topright: 3px; + -webkit-border-top-right-radius: 3px; + border-top-right-radius: 3px; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 8px 12px 7px 12px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #6f6f6f; +} + +.x-tab-default-left-mc { + background-color: #6f6f6f; +} + +.x-nbr .x-tab-default-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-tab-default-left-frameInfo { + font-family: th-3-3-0-0-0-0-0-0-8-12-7-12; +} + +.x-tab-default-left-tl { + background-position: 0 -6px; +} + +.x-tab-default-left-tr { + background-position: right -9px; +} + +.x-tab-default-left-bl { + background-position: 0 -12px; +} + +.x-tab-default-left-br { + background-position: right -15px; +} + +.x-tab-default-left-ml { + background-position: 0 top; +} + +.x-tab-default-left-mr { + background-position: right top; +} + +.x-tab-default-left-tc { + background-position: 0 0; +} + +.x-tab-default-left-bc { + background-position: 0 -3px; +} + +.x-tab-default-left-tr, +.x-tab-default-left-br, +.x-tab-default-left-mr { + padding-right: 3px; +} + +.x-tab-default-left-tl, +.x-tab-default-left-bl, +.x-tab-default-left-ml { + padding-left: 3px; +} + +.x-tab-default-left-tc { + height: 3px; +} + +.x-tab-default-left-bc { + height: 0; +} + +.x-tab-default-left-tl, +.x-tab-default-left-bl, +.x-tab-default-left-tr, +.x-tab-default-left-br, +.x-tab-default-left-tc, +.x-tab-default-left-bc, +.x-tab-default-left-ml, +.x-tab-default-left-mr { + zoom: 1; + background-image: url(images/tab/tab-default-top-corners.gif); +} + +.x-tab-default-left-ml, +.x-tab-default-left-mr { + zoom: 1; + background-image: url(images/tab/tab-default-top-sides.gif); + background-repeat: repeat-y; +} + +.x-tab-default-left-mc { + padding: 5px 9px 7px 9px; +} + +.x-strict .x-ie7 .x-tab-default-left-tl, +.x-strict .x-ie7 .x-tab-default-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-tab-default-left:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-default-top-corners.gif), sides:url(images/tab/tab-default-top-sides.gif)"; +} + +/**/ +/* */ +.x-tab-default-right { + -moz-border-radius-topleft: 3px; + -webkit-border-top-left-radius: 3px; + border-top-left-radius: 3px; + -moz-border-radius-topright: 3px; + -webkit-border-top-right-radius: 3px; + border-top-right-radius: 3px; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 8px 12px 7px 12px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #6f6f6f; +} + +.x-tab-default-right-mc { + background-color: #6f6f6f; +} + +.x-nbr .x-tab-default-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-tab-default-right-frameInfo { + font-family: th-3-3-0-0-0-0-0-0-8-12-7-12; +} + +.x-tab-default-right-tl { + background-position: 0 -6px; +} + +.x-tab-default-right-tr { + background-position: right -9px; +} + +.x-tab-default-right-bl { + background-position: 0 -12px; +} + +.x-tab-default-right-br { + background-position: right -15px; +} + +.x-tab-default-right-ml { + background-position: 0 top; +} + +.x-tab-default-right-mr { + background-position: right top; +} + +.x-tab-default-right-tc { + background-position: 0 0; +} + +.x-tab-default-right-bc { + background-position: 0 -3px; +} + +.x-tab-default-right-tr, +.x-tab-default-right-br, +.x-tab-default-right-mr { + padding-right: 3px; +} + +.x-tab-default-right-tl, +.x-tab-default-right-bl, +.x-tab-default-right-ml { + padding-left: 3px; +} + +.x-tab-default-right-tc { + height: 3px; +} + +.x-tab-default-right-bc { + height: 0; +} + +.x-tab-default-right-tl, +.x-tab-default-right-bl, +.x-tab-default-right-tr, +.x-tab-default-right-br, +.x-tab-default-right-tc, +.x-tab-default-right-bc, +.x-tab-default-right-ml, +.x-tab-default-right-mr { + zoom: 1; + background-image: url(images/tab/tab-default-top-corners.gif); +} + +.x-tab-default-right-ml, +.x-tab-default-right-mr { + zoom: 1; + background-image: url(images/tab/tab-default-top-sides.gif); + background-repeat: repeat-y; +} + +.x-tab-default-right-mc { + padding: 5px 9px 7px 9px; +} + +.x-strict .x-ie7 .x-tab-default-right-tl, +.x-strict .x-ie7 .x-tab-default-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-tab-default-right:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-default-top-corners.gif), sides:url(images/tab/tab-default-top-sides.gif)"; +} + +/**/ +/* */ +.x-tab-default { + border-color: #444444; + margin: 0 1px 0 0; + cursor: pointer; +} +.x-tab-default .x-tab-inner { + font-size: 13px; + font-weight: bold; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: white; + line-height: 16px; +} +.x-tab-default .x-tab-icon-el { + width: 16px; + height: 16px; + line-height: 16px; + background-position: center center; +} +.x-tab-default .x-tab-glyph { + font-size: 16px; + color: white; + opacity: 0.5; +} +.x-ie8m .x-tab-default .x-tab-glyph { + color: #b7b7b7; +} +.x-strict .x-ie9 .x-tab-bar-vertical .x-tab-default { + padding-left: 0; +} +.x-strict .x-ie9 .x-tab-bar-vertical .x-tab-default .x-tab-button { + padding-left: 12px; +} +.x-strict .x-ie9 .x-tab-bar-vertical .x-tab-default .x-tab-icon-el { + left: 12px; +} + +.x-tab-default-icon .x-tab-inner { + width: 16px; +} + +.x-tab-default-left { + margin: 0 0 0 1px; +} + +.x-tab-default-top, +.x-tab-default-left, +.x-tab-default-right { + border-bottom: 0 solid #444444; +} + +.x-tab-default-bottom { + border-top: 0 solid #444444; +} + +.x-tab-default-left { + -webkit-transform: rotate(270deg); + -webkit-transform-origin: 100% 0; + -moz-transform: rotate(270deg); + -moz-transform-origin: 100% 0; + -o-transform: rotate(270deg); + -o-transform-origin: 100% 0; + transform: rotate(270deg); + transform-origin: 100% 0; +} +.x-ie9m .x-tab-default-left { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); +} + +.x-tab-default-right { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-tab-default-right { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); +} + +.x-tab-default-icon-text-left .x-tab-inner { + padding-left: 22px; +} + +.x-tab-default-over { + background-color: #7f7f7f; +} +.x-tab-default-over .x-tab-glyph { + color: white; +} +.x-ie8m .x-tab-default-over .x-tab-glyph { + color: #bfbfbf; +} + +.x-tab-default-active { + background-color: #bdbdbd; +} +.x-tab-default-active .x-tab-inner { + color: #444444; +} +.x-tab-default-active .x-tab-glyph { + color: #444444; +} +.x-ie8m .x-tab-default-active .x-tab-glyph { + color: gray; +} + +.x-tab-default-top-active, +.x-tab-default-left-active, +.x-tab-default-right-active { + border-bottom: 0 solid #bdbdbd; +} + +.x-tab-default-bottom-active { + border-top: 0 solid #bdbdbd; +} + +.x-tab-default-disabled { + cursor: default; +} +.x-tab-default-disabled .x-tab-inner { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30); + opacity: 0.3; +} +.x-tab-default-disabled .x-tab-icon-el { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} +.x-tab-default-disabled .x-tab-glyph { + color: white; + opacity: 0.3; + filter: none; +} +.x-ie8m .x-tab-default-disabled .x-tab-glyph { + color: #9a9a9a; +} + +.x-tab-default-top-disabled, +.x-tab-default-left-disabled, +.x-tab-default-right-disabled { + border-color: #444444 #444444 #444444; +} + +.x-tab-default-bottom-disabled { + border-color: #444444 #444444 #444444 #444444; +} + +.x-nbr .x-tab-default { + background-image: none; +} + +.x-tab-default-top-over .x-frame-tl, +.x-tab-default-top-over .x-frame-bl, +.x-tab-default-top-over .x-frame-tr, +.x-tab-default-top-over .x-frame-br, +.x-tab-default-top-over .x-frame-tc, +.x-tab-default-top-over .x-frame-bc, +.x-tab-default-left-over .x-frame-tl, +.x-tab-default-left-over .x-frame-bl, +.x-tab-default-left-over .x-frame-tr, +.x-tab-default-left-over .x-frame-br, +.x-tab-default-left-over .x-frame-tc, +.x-tab-default-left-over .x-frame-bc, +.x-tab-default-right-over .x-frame-tl, +.x-tab-default-right-over .x-frame-bl, +.x-tab-default-right-over .x-frame-tr, +.x-tab-default-right-over .x-frame-br, +.x-tab-default-right-over .x-frame-tc, +.x-tab-default-right-over .x-frame-bc { + background-image: url(images/tab/tab-default-top-over-corners.gif); +} +.x-tab-default-top-over .x-frame-ml, +.x-tab-default-top-over .x-frame-mr, +.x-tab-default-left-over .x-frame-ml, +.x-tab-default-left-over .x-frame-mr, +.x-tab-default-right-over .x-frame-ml, +.x-tab-default-right-over .x-frame-mr { + background-image: url(images/tab/tab-default-top-over-sides.gif); +} +.x-tab-default-top-over .x-frame-mc, +.x-tab-default-left-over .x-frame-mc, +.x-tab-default-right-over .x-frame-mc { + background-color: #7f7f7f; +} + +.x-tab-default-bottom-over .x-frame-tl, +.x-tab-default-bottom-over .x-frame-bl, +.x-tab-default-bottom-over .x-frame-tr, +.x-tab-default-bottom-over .x-frame-br, +.x-tab-default-bottom-over .x-frame-tc, +.x-tab-default-bottom-over .x-frame-bc { + background-image: url(images/tab/tab-default-bottom-over-corners.gif); +} +.x-tab-default-bottom-over .x-frame-ml, +.x-tab-default-bottom-over .x-frame-mr { + background-image: url(images/tab/tab-default-bottom-over-sides.gif); +} +.x-tab-default-bottom-over .x-frame-mc { + background-color: #7f7f7f; +} + +.x-tab-default-top-active .x-frame-tl, +.x-tab-default-top-active .x-frame-bl, +.x-tab-default-top-active .x-frame-tr, +.x-tab-default-top-active .x-frame-br, +.x-tab-default-top-active .x-frame-tc, +.x-tab-default-top-active .x-frame-bc, +.x-tab-default-left-active .x-frame-tl, +.x-tab-default-left-active .x-frame-bl, +.x-tab-default-left-active .x-frame-tr, +.x-tab-default-left-active .x-frame-br, +.x-tab-default-left-active .x-frame-tc, +.x-tab-default-left-active .x-frame-bc, +.x-tab-default-right-active .x-frame-tl, +.x-tab-default-right-active .x-frame-bl, +.x-tab-default-right-active .x-frame-tr, +.x-tab-default-right-active .x-frame-br, +.x-tab-default-right-active .x-frame-tc, +.x-tab-default-right-active .x-frame-bc { + background-image: url(images/tab/tab-default-top-active-corners.gif); +} +.x-tab-default-top-active .x-frame-ml, +.x-tab-default-top-active .x-frame-mr, +.x-tab-default-left-active .x-frame-ml, +.x-tab-default-left-active .x-frame-mr, +.x-tab-default-right-active .x-frame-ml, +.x-tab-default-right-active .x-frame-mr { + background-image: url(images/tab/tab-default-top-active-sides.gif); +} +.x-tab-default-top-active .x-frame-mc, +.x-tab-default-left-active .x-frame-mc, +.x-tab-default-right-active .x-frame-mc { + background-color: #bdbdbd; +} + +.x-tab-default-bottom-active .x-frame-tl, +.x-tab-default-bottom-active .x-frame-bl, +.x-tab-default-bottom-active .x-frame-tr, +.x-tab-default-bottom-active .x-frame-br, +.x-tab-default-bottom-active .x-frame-tc, +.x-tab-default-bottom-active .x-frame-bc { + background-image: url(images/tab/tab-default-bottom-active-corners.gif); +} +.x-tab-default-bottom-active .x-frame-ml, +.x-tab-default-bottom-active .x-frame-mr { + background-image: url(images/tab/tab-default-bottom-active-sides.gif); +} +.x-tab-default-bottom-active .x-frame-mc { + background-color: #bdbdbd; +} + +.x-tab-default-top-disabled .x-frame-tl, +.x-tab-default-top-disabled .x-frame-bl, +.x-tab-default-top-disabled .x-frame-tr, +.x-tab-default-top-disabled .x-frame-br, +.x-tab-default-top-disabled .x-frame-tc, +.x-tab-default-top-disabled .x-frame-bc, +.x-tab-default-left-disabled .x-frame-tl, +.x-tab-default-left-disabled .x-frame-bl, +.x-tab-default-left-disabled .x-frame-tr, +.x-tab-default-left-disabled .x-frame-br, +.x-tab-default-left-disabled .x-frame-tc, +.x-tab-default-left-disabled .x-frame-bc, +.x-tab-default-right-disabled .x-frame-tl, +.x-tab-default-right-disabled .x-frame-bl, +.x-tab-default-right-disabled .x-frame-tr, +.x-tab-default-right-disabled .x-frame-br, +.x-tab-default-right-disabled .x-frame-tc, +.x-tab-default-right-disabled .x-frame-bc { + background-image: url(images/tab/tab-default-top-disabled-corners.gif); +} +.x-tab-default-top-disabled .x-frame-ml, +.x-tab-default-top-disabled .x-frame-mr, +.x-tab-default-left-disabled .x-frame-ml, +.x-tab-default-left-disabled .x-frame-mr, +.x-tab-default-right-disabled .x-frame-ml, +.x-tab-default-right-disabled .x-frame-mr { + background-image: url(images/tab/tab-default-top-disabled-sides.gif); +} +.x-tab-default-top-disabled .x-frame-mc, +.x-tab-default-left-disabled .x-frame-mc, +.x-tab-default-right-disabled .x-frame-mc { + background-color: #6f6f6f; +} + +.x-tab-default-bottom-disabled .x-frame-tl, +.x-tab-default-bottom-disabled .x-frame-bl, +.x-tab-default-bottom-disabled .x-frame-tr, +.x-tab-default-bottom-disabled .x-frame-br, +.x-tab-default-bottom-disabled .x-frame-tc, +.x-tab-default-bottom-disabled .x-frame-bc { + background-image: url(images/tab/tab-default-bottom-disabled-corners.gif); +} +.x-tab-default-bottom-disabled .x-frame-ml, +.x-tab-default-bottom-disabled .x-frame-mr { + background-image: url(images/tab/tab-default-bottom-disabled-sides.gif); +} +.x-tab-default-bottom-disabled .x-frame-mc { + background-color: #6f6f6f; +} + +.x-tab-default .x-tab-close-btn { + width: 12px; + height: 12px; + background-image: url(images/tab/tab-default-close.png); +} +.x-tab-default .x-tab-close-btn-over { + background-position: -12px 0; +} + +.x-tab-default .x-tab-close-btn { + top: 2px; + right: 2px; +} + +.x-tab-default-disabled .x-tab-close-btn { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30); + opacity: 0.3; + background-position: 0 0; +} + +.x-tab-default-pressed .x-tab-close-btn { + background-position: -24px 0; +} + +.x-tab-default-closable .x-tab-wrap { + padding-right: 15px; +} + +/**/ +.x-tab-default-top-over:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-default-top-over-corners.gif), sides:url(images/tab/tab-default-top-over-sides.gif), stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-tab-default-bottom-over:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-default-bottom-over-corners.gif), sides:url(images/tab/tab-default-bottom-over-sides.gif), stretch:top"; +} + +/**/ +/* */ +/**/ +.x-tab-default-top-active:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-default-top-active-corners.gif), sides:url(images/tab/tab-default-top-active-sides.gif), stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-tab-default-bottom-active:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-default-bottom-active-corners.gif), sides:url(images/tab/tab-default-bottom-active-sides.gif), stretch:top"; +} + +/**/ +/* */ +/**/ +.x-tab-default-top-disabled:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-default-top-disabled-corners.gif), sides:url(images/tab/tab-default-top-disabled-sides.gif), stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-tab-default-bottom-disabled:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-default-bottom-disabled-corners.gif), sides:url(images/tab/tab-default-bottom-disabled-sides.gif), stretch:top"; +} + +/**/ +/* */ +.x-tab-bar-default-top { + padding: 0; +} + +.x-tab-bar-default-bottom { + padding: 0 0 0 0; +} + +.x-tab-bar-default-left { + padding: 0 0 0 0; +} + +.x-tab-bar-default-right { + padding: 0 0 0 0; +} + +.x-tab-bar-default-horizontal { + height: 36px; +} +.x-content-box .x-tab-bar-default-horizontal { + height: 36px; +} + +.x-tab-bar-default-vertical { + width: 36px; +} +.x-content-box .x-tab-bar-default-vertical { + width: 36px; +} + +.x-tab-bar-body-default-top { + padding-bottom: 5px; +} + +.x-tab-bar-body-default-bottom { + padding-top: 5px; +} + +.x-tab-bar-body-default-left { + padding-right: 5px; +} + +.x-tab-bar-body-default-right { + padding-left: 5px; +} + +.x-tab-bar-strip-default { + border-style: solid; + border-color: #444444; + background-color: #bdbdbd; +} + +.x-content-box .x-tab-bar-strip-default-horizontal { + height: 5px; +} + +.x-content-box .x-tab-bar-strip-default-vertical { + width: 5px; +} + +.x-tab-bar-strip-default-top { + border-width: 0 0 0 0; + height: 5px; +} +.x-tab-bar-plain .x-tab-bar-strip-default-top { + border-width: 0 0 0 0; +} + +.x-tab-bar-strip-default-bottom { + border-width: 0 0 0 0; + height: 5px; +} +.x-tab-bar-plain .x-tab-bar-strip-default-bottom { + border-width: 0 0 0 0; +} + +.x-tab-bar-strip-default-left { + border-width: 0 0 0 0; + width: 5px; +} +.x-tab-bar-plain .x-tab-bar-strip-default-left { + border-width: 0 0 0 0; +} + +.x-tab-bar-strip-default-right { + border-width: 0 0 0 0; + width: 5px; +} +.x-tab-bar-plain .x-tab-bar-strip-default-right { + border-width: 0 0 0 0; +} + +.x-tab-bar-default { + background-color: #444444; +} + +.x-tab-bar-default .x-box-scroller { + cursor: pointer; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; + background-color: #444444; +} +.x-tab-bar-default .x-box-scroller-plain .x-box-scroller { + background-color: transparent; +} +.x-ie8m .x-tab-bar-default .x-box-scroller-plain .x-box-scroller { + background-color: #fff; +} +.x-tab-bar-default .x-box-scroller-hover { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60); + opacity: 0.6; +} +.x-tab-bar-default .x-box-scroller-pressed { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70); + opacity: 0.7; +} +.x-tab-bar-default .x-tabbar-scroll-left, +.x-tab-bar-default .x-tabbar-scroll-right { + height: 31px; + width: 24px; +} +.x-tab-bar-default .x-tabbar-scroll-top, +.x-tab-bar-default .x-tabbar-scroll-bottom { + width: 31px; + height: 24px; +} + +.x-tab-bar-default-bottom .x-box-scroller { + margin-top: 0; +} +.x-tab-bar-default-right .x-box-scroller { + margin-left: 0; +} + +.x-tab-bar-default .x-tabbar-scroll-left { + background-image: url(images/tab-bar/default-scroll-left.png); +} +.x-tab-bar-default .x-tabbar-scroll-right { + background-image: url(images/tab-bar/default-scroll-right.png); +} +.x-tab-bar-default .x-tabbar-scroll-top { + background-image: url(images/tab-bar/default-scroll-top.png); +} +.x-tab-bar-default .x-tabbar-scroll-bottom { + background-image: url(images/tab-bar/default-scroll-bottom.png); +} + +.x-tab-bar-default .x-box-scroller-plain .x-tabbar-scroll-left { + background-image: url(images/tab-bar/default-plain-scroll-left.png); +} +.x-tab-bar-default .x-box-scroller-plain .x-tabbar-scroll-right { + background-image: url(images/tab-bar/default-plain-scroll-right.png); +} +.x-tab-bar-default .x-box-scroller-plain .x-tabbar-scroll-top { + background-image: url(images/tab-bar/default-plain-scroll-top.png); +} +.x-tab-bar-default .x-box-scroller-plain .x-tabbar-scroll-bottom { + background-image: url(images/tab-bar/default-plain-scroll-bottom.png); +} + +.x-tab-bar-default .x-box-scroller-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=25); + opacity: 0.25; + cursor: default; +} + +/**/ +.x-tab-bar-default-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-tab-bar-default-bottom:after { + display: none; + content: "x-slicer:stretch:top"; +} + +/**/ +/* */ +/**/ +.x-tab-bar-default-left:after { + display: none; + content: "x-slicer:stretch:right"; +} + +/**/ +/* */ +/**/ +.x-tab-bar-default-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-tab-bar-plain { + border-width: 0; + padding: 0; + height: 36px; +} + +.x-column-header-checkbox { + border-color: #f5f5f5; +} + +.x-grid-row-checker, +.x-column-header-checkbox .x-column-header-text { + height: 15px; + width: 15px; + background-image: url(images/form/checkbox.png); + line-height: 15px; +} + +.x-column-header-checkbox .x-column-header-inner { + padding: 7px 4px 7px 4px; +} + +.x-grid-cell-row-checker .x-grid-cell-inner { + padding: 5px 4px 4px 4px; +} + +.x-grid-hd-checker-on .x-column-header-text, +.x-grid-row-selected .x-grid-row-checker, +.x-grid-row-checked .x-grid-row-checker { + background-position: 0 -15px; +} + +.x-tree-drop-ok-append .x-dd-drop-icon { + background-image: url(images/tree/drop-append.png); +} + +.x-tree-drop-ok-above .x-dd-drop-icon { + background-image: url(images/tree/drop-above.png); +} + +.x-tree-drop-ok-below .x-dd-drop-icon { + background-image: url(images/tree/drop-below.png); +} + +.x-tree-drop-ok-between .x-dd-drop-icon { + background-image: url(images/tree/drop-between.png); +} + +.x-tree-ddindicator { + height: 1px; + border-width: 1px 0px 0px; + border-style: dotted; + border-color: green; +} + +/* including package ext-theme-neptune */ +.x-btn-plain-toolbar-small { + border-color: transparent; +} + +.x-btn-plain-toolbar-small { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: transparent; +} + +.x-btn-plain-toolbar-small-mc { + background-image: url(images/btn/btn-plain-toolbar-small-fbg.gif); + background-position: 0 top; + background-color: transparent; +} + +.x-nlg .x-btn-plain-toolbar-small { + background-image: url(images/btn/btn-plain-toolbar-small-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-plain-toolbar-small { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-plain-toolbar-small-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-plain-toolbar-small-tl { + background-position: 0 -6px; +} + +.x-btn-plain-toolbar-small-tr { + background-position: right -9px; +} + +.x-btn-plain-toolbar-small-bl { + background-position: 0 -12px; +} + +.x-btn-plain-toolbar-small-br { + background-position: right -15px; +} + +.x-btn-plain-toolbar-small-ml { + background-position: 0 top; +} + +.x-btn-plain-toolbar-small-mr { + background-position: right top; +} + +.x-btn-plain-toolbar-small-tc { + background-position: 0 0; +} + +.x-btn-plain-toolbar-small-bc { + background-position: 0 -3px; +} + +.x-btn-plain-toolbar-small-tr, +.x-btn-plain-toolbar-small-br, +.x-btn-plain-toolbar-small-mr { + padding-right: 3px; +} + +.x-btn-plain-toolbar-small-tl, +.x-btn-plain-toolbar-small-bl, +.x-btn-plain-toolbar-small-ml { + padding-left: 3px; +} + +.x-btn-plain-toolbar-small-tc { + height: 3px; +} + +.x-btn-plain-toolbar-small-bc { + height: 3px; +} + +.x-btn-plain-toolbar-small-tl, +.x-btn-plain-toolbar-small-bl, +.x-btn-plain-toolbar-small-tr, +.x-btn-plain-toolbar-small-br, +.x-btn-plain-toolbar-small-tc, +.x-btn-plain-toolbar-small-bc, +.x-btn-plain-toolbar-small-ml, +.x-btn-plain-toolbar-small-mr { + zoom: 1; +} + +.x-btn-plain-toolbar-small-ml, +.x-btn-plain-toolbar-small-mr { + zoom: 1; +} + +.x-btn-plain-toolbar-small-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-plain-toolbar-small-tl, +.x-strict .x-ie7 .x-btn-plain-toolbar-small-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-plain-toolbar-small:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-plain-toolbar-small-fbg.gif), bg:url(images/btn/btn-plain-toolbar-small-bg.gif)"; +} + +/**/ +/* */ +.x-btn-plain-toolbar-small .x-btn-inner { + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #333333; + padding: 0 5px; +} +.x-btn-plain-toolbar-small .x-btn-arrow { + background-image: url(images/button/plain-toolbar-small-arrow.png); +} +.x-btn-plain-toolbar-small .x-btn-arrow-right { + padding-right: 21px; +} +.x-btn-plain-toolbar-small .x-btn-arrow-bottom { + padding-bottom: 18px; +} +.x-btn-plain-toolbar-small .x-btn-glyph { + font-size: 16px; + line-height: 16px; + color: #333333; +} +.x-ie8m .x-btn-plain-toolbar-small .x-btn-glyph { + color: #333333; +} + +.x-btn-plain-toolbar-small-disabled { + background-image: none; + background-color: transparent; +} + +.x-btn-plain-toolbar-small-icon .x-btn-button, +.x-btn-plain-toolbar-small-noicon .x-btn-button { + height: 16px; +} +.x-btn-plain-toolbar-small-icon .x-btn-inner, +.x-btn-plain-toolbar-small-noicon .x-btn-inner { + line-height: 16px; +} + +.x-btn-plain-toolbar-small-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-plain-toolbar-small-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-plain-toolbar-small-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-plain-toolbar-small-icon .x-btn-inner { + width: 16px; + padding: 0; +} +.x-btn-plain-toolbar-small-icon .x-btn-icon-el { + width: 16px; + height: 16px; +} + +.x-btn-plain-toolbar-small-icon-text-left .x-btn-button { + height: 16px; +} +.x-btn-plain-toolbar-small-icon-text-left .x-btn-inner { + line-height: 16px; + padding-left: 21px; +} +.x-btn-plain-toolbar-small-icon-text-left .x-btn-icon-el { + width: 16px; + right: auto; +} +.x-ie6 .x-btn-plain-toolbar-small-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-plain-toolbar-small-icon-text-left .x-btn-icon-el { + height: 16px; +} + +.x-btn-plain-toolbar-small-icon-text-right .x-btn-button { + height: 16px; +} +.x-btn-plain-toolbar-small-icon-text-right .x-btn-inner { + line-height: 16px; + padding-right: 21px; +} +.x-btn-plain-toolbar-small-icon-text-right .x-btn-icon-el { + width: 16px; + left: auto; +} +.x-ie6 .x-btn-plain-toolbar-small-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-plain-toolbar-small-icon-text-right .x-btn-icon-el { + height: 16px; +} + +.x-btn-plain-toolbar-small-icon-text-top .x-btn-inner { + padding-top: 21px; +} +.x-btn-plain-toolbar-small-icon-text-top .x-btn-icon-el { + height: 16px; + bottom: auto; +} +.x-ie6 .x-btn-plain-toolbar-small-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-plain-toolbar-small-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-plain-toolbar-small-icon-text-bottom .x-btn-inner { + padding-bottom: 21px; +} +.x-btn-plain-toolbar-small-icon-text-bottom .x-btn-icon-el { + height: 16px; + top: auto; +} +.x-ie6 .x-btn-plain-toolbar-small-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-plain-toolbar-small-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-plain-toolbar-small-focus { + border-color: #96caee; + background-image: none; + background-color: #ebebeb; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-plain-toolbar-small-over { + border-color: #d8d8d8; + background-image: none; + background-color: #ebebeb; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #cbcbcb), color-stop(100%, #b8b8b8)); + background-image: -webkit-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -moz-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -o-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -ms-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: linear-gradient(top, #cbcbcb, #b8b8b8); +} + +.x-btn-plain-toolbar-small-menu-active, +.x-btn-plain-toolbar-small-pressed { + border-color: #cecece; + background-image: none; + background-color: #e1e1e1; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #919191), color-stop(100%, #808080)); + background-image: -webkit-linear-gradient(top, #919191, #808080); + background-image: -moz-linear-gradient(top, #919191, #808080); + background-image: -o-linear-gradient(top, #919191, #808080); + background-image: -ms-linear-gradient(top, #919191, #808080); + background-image: linear-gradient(top, #919191, #808080); +} +.x-btn-plain-toolbar-small-menu-active .x-btn-inner, +.x-btn-plain-toolbar-small-pressed .x-btn-inner { + color: white; +} + +.x-btn-plain-toolbar-small-focus .x-frame-tl, +.x-btn-plain-toolbar-small-focus .x-frame-bl, +.x-btn-plain-toolbar-small-focus .x-frame-tr, +.x-btn-plain-toolbar-small-focus .x-frame-br, +.x-btn-plain-toolbar-small-focus .x-frame-tc, +.x-btn-plain-toolbar-small-focus .x-frame-bc { + background-image: url(images/btn/btn-plain-toolbar-small-focus-corners.gif); +} +.x-btn-plain-toolbar-small-focus .x-frame-ml, +.x-btn-plain-toolbar-small-focus .x-frame-mr { + background-image: url(images/btn/btn-plain-toolbar-small-focus-sides.gif); +} +.x-btn-plain-toolbar-small-focus .x-frame-mc { + background-color: #ebebeb; + background-image: url(images/btn/btn-plain-toolbar-small-focus-fbg.gif); +} + +.x-btn-plain-toolbar-small-over .x-frame-tl, +.x-btn-plain-toolbar-small-over .x-frame-bl, +.x-btn-plain-toolbar-small-over .x-frame-tr, +.x-btn-plain-toolbar-small-over .x-frame-br, +.x-btn-plain-toolbar-small-over .x-frame-tc, +.x-btn-plain-toolbar-small-over .x-frame-bc { + background-image: url(images/btn/btn-plain-toolbar-small-over-corners.gif); +} +.x-btn-plain-toolbar-small-over .x-frame-ml, +.x-btn-plain-toolbar-small-over .x-frame-mr { + background-image: url(images/btn/btn-plain-toolbar-small-over-sides.gif); +} +.x-btn-plain-toolbar-small-over .x-frame-mc { + background-color: #ebebeb; + background-image: url(images/btn/btn-plain-toolbar-small-over-fbg.gif); +} + +.x-btn-plain-toolbar-small-menu-active .x-frame-tl, +.x-btn-plain-toolbar-small-menu-active .x-frame-bl, +.x-btn-plain-toolbar-small-menu-active .x-frame-tr, +.x-btn-plain-toolbar-small-menu-active .x-frame-br, +.x-btn-plain-toolbar-small-menu-active .x-frame-tc, +.x-btn-plain-toolbar-small-menu-active .x-frame-bc, +.x-btn-plain-toolbar-small-pressed .x-frame-tl, +.x-btn-plain-toolbar-small-pressed .x-frame-bl, +.x-btn-plain-toolbar-small-pressed .x-frame-tr, +.x-btn-plain-toolbar-small-pressed .x-frame-br, +.x-btn-plain-toolbar-small-pressed .x-frame-tc, +.x-btn-plain-toolbar-small-pressed .x-frame-bc { + background-image: url(images/btn/btn-plain-toolbar-small-pressed-corners.gif); +} +.x-btn-plain-toolbar-small-menu-active .x-frame-ml, +.x-btn-plain-toolbar-small-menu-active .x-frame-mr, +.x-btn-plain-toolbar-small-pressed .x-frame-ml, +.x-btn-plain-toolbar-small-pressed .x-frame-mr { + background-image: url(images/btn/btn-plain-toolbar-small-pressed-sides.gif); +} +.x-btn-plain-toolbar-small-menu-active .x-frame-mc, +.x-btn-plain-toolbar-small-pressed .x-frame-mc { + background-color: #e1e1e1; + background-image: url(images/btn/btn-plain-toolbar-small-pressed-fbg.gif); +} + +.x-btn-plain-toolbar-small-disabled .x-frame-tl, +.x-btn-plain-toolbar-small-disabled .x-frame-bl, +.x-btn-plain-toolbar-small-disabled .x-frame-tr, +.x-btn-plain-toolbar-small-disabled .x-frame-br, +.x-btn-plain-toolbar-small-disabled .x-frame-tc, +.x-btn-plain-toolbar-small-disabled .x-frame-bc { + background-image: url(images/btn/btn-plain-toolbar-small-disabled-corners.gif); +} +.x-btn-plain-toolbar-small-disabled .x-frame-ml, +.x-btn-plain-toolbar-small-disabled .x-frame-mr { + background-image: url(images/btn/btn-plain-toolbar-small-disabled-sides.gif); +} +.x-btn-plain-toolbar-small-disabled .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-plain-toolbar-small-disabled-fbg.gif); +} + +.x-nlg .x-btn-plain-toolbar-small-focus { + background-image: url(images/btn/btn-plain-toolbar-small-focus-bg.gif); +} + +.x-nlg .x-btn-plain-toolbar-small-over { + background-image: url(images/btn/btn-plain-toolbar-small-over-bg.gif); +} + +.x-nlg .x-btn-plain-toolbar-small-menu-active, +.x-nlg .x-btn-plain-toolbar-small-pressed { + background-image: url(images/btn/btn-plain-toolbar-small-pressed-bg.gif); +} + +.x-nlg .x-btn-plain-toolbar-small-disabled { + background-image: url(images/btn/btn-plain-toolbar-small-disabled-bg.gif); +} + +.x-nbr .x-btn-plain-toolbar-small { + background-image: none; +} + +.x-btn-plain-toolbar-small .x-btn-split-right { + background-image: url(images/button/plain-toolbar-small-s-arrow.png); + padding-right: 23px; +} +.x-btn-plain-toolbar-small .x-btn-split-bottom { + background-image: url(images/button/plain-toolbar-small-s-arrow-b.png); + padding-bottom: 20px; +} + +.x-btn-plain-toolbar-small-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-plain-toolbar-small-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-small-focus-corners.gif), sides:url(images/btn/btn-plain-toolbar-small-focus-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-small-focus-fbg.gif), bg:url(images/btn/btn-plain-toolbar-small-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-plain-toolbar-small-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-small-over-corners.gif), sides:url(images/btn/btn-plain-toolbar-small-over-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-small-over-fbg.gif), bg:url(images/btn/btn-plain-toolbar-small-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-plain-toolbar-small-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-small-pressed-corners.gif), sides:url(images/btn/btn-plain-toolbar-small-pressed-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-small-pressed-fbg.gif), bg:url(images/btn/btn-plain-toolbar-small-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-plain-toolbar-small-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-small-disabled-corners.gif), sides:url(images/btn/btn-plain-toolbar-small-disabled-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-small-disabled-fbg.gif), bg:url(images/btn/btn-plain-toolbar-small-disabled-bg.gif)"; +} + +/**/ +/* */ +.x-btn-plain-toolbar-medium { + border-color: transparent; +} + +.x-btn-plain-toolbar-medium { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: transparent; +} + +.x-btn-plain-toolbar-medium-mc { + background-image: url(images/btn/btn-plain-toolbar-medium-fbg.gif); + background-position: 0 top; + background-color: transparent; +} + +.x-nlg .x-btn-plain-toolbar-medium { + background-image: url(images/btn/btn-plain-toolbar-medium-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-plain-toolbar-medium { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-plain-toolbar-medium-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-plain-toolbar-medium-tl { + background-position: 0 -6px; +} + +.x-btn-plain-toolbar-medium-tr { + background-position: right -9px; +} + +.x-btn-plain-toolbar-medium-bl { + background-position: 0 -12px; +} + +.x-btn-plain-toolbar-medium-br { + background-position: right -15px; +} + +.x-btn-plain-toolbar-medium-ml { + background-position: 0 top; +} + +.x-btn-plain-toolbar-medium-mr { + background-position: right top; +} + +.x-btn-plain-toolbar-medium-tc { + background-position: 0 0; +} + +.x-btn-plain-toolbar-medium-bc { + background-position: 0 -3px; +} + +.x-btn-plain-toolbar-medium-tr, +.x-btn-plain-toolbar-medium-br, +.x-btn-plain-toolbar-medium-mr { + padding-right: 3px; +} + +.x-btn-plain-toolbar-medium-tl, +.x-btn-plain-toolbar-medium-bl, +.x-btn-plain-toolbar-medium-ml { + padding-left: 3px; +} + +.x-btn-plain-toolbar-medium-tc { + height: 3px; +} + +.x-btn-plain-toolbar-medium-bc { + height: 3px; +} + +.x-btn-plain-toolbar-medium-tl, +.x-btn-plain-toolbar-medium-bl, +.x-btn-plain-toolbar-medium-tr, +.x-btn-plain-toolbar-medium-br, +.x-btn-plain-toolbar-medium-tc, +.x-btn-plain-toolbar-medium-bc, +.x-btn-plain-toolbar-medium-ml, +.x-btn-plain-toolbar-medium-mr { + zoom: 1; +} + +.x-btn-plain-toolbar-medium-ml, +.x-btn-plain-toolbar-medium-mr { + zoom: 1; +} + +.x-btn-plain-toolbar-medium-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-plain-toolbar-medium-tl, +.x-strict .x-ie7 .x-btn-plain-toolbar-medium-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-plain-toolbar-medium:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-plain-toolbar-medium-fbg.gif), bg:url(images/btn/btn-plain-toolbar-medium-bg.gif)"; +} + +/**/ +/* */ +.x-btn-plain-toolbar-medium .x-btn-inner { + font-size: 14px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #333333; + padding: 0 8px; +} +.x-btn-plain-toolbar-medium .x-btn-arrow { + background-image: url(images/button/plain-toolbar-medium-arrow.png); +} +.x-btn-plain-toolbar-medium .x-btn-arrow-right { + padding-right: 30px; +} +.x-btn-plain-toolbar-medium .x-btn-arrow-bottom { + padding-bottom: 26px; +} +.x-btn-plain-toolbar-medium .x-btn-glyph { + font-size: 24px; + line-height: 24px; + color: #333333; +} +.x-ie8m .x-btn-plain-toolbar-medium .x-btn-glyph { + color: #333333; +} + +.x-btn-plain-toolbar-medium-disabled { + background-image: none; + background-color: transparent; +} + +.x-btn-plain-toolbar-medium-icon .x-btn-button, +.x-btn-plain-toolbar-medium-noicon .x-btn-button { + height: 24px; +} +.x-btn-plain-toolbar-medium-icon .x-btn-inner, +.x-btn-plain-toolbar-medium-noicon .x-btn-inner { + line-height: 24px; +} + +.x-btn-plain-toolbar-medium-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-plain-toolbar-medium-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-plain-toolbar-medium-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-plain-toolbar-medium-icon .x-btn-inner { + width: 24px; + padding: 0; +} +.x-btn-plain-toolbar-medium-icon .x-btn-icon-el { + width: 24px; + height: 24px; +} + +.x-btn-plain-toolbar-medium-icon-text-left .x-btn-button { + height: 24px; +} +.x-btn-plain-toolbar-medium-icon-text-left .x-btn-inner { + line-height: 24px; + padding-left: 29px; +} +.x-btn-plain-toolbar-medium-icon-text-left .x-btn-icon-el { + width: 24px; + right: auto; +} +.x-ie6 .x-btn-plain-toolbar-medium-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-plain-toolbar-medium-icon-text-left .x-btn-icon-el { + height: 24px; +} + +.x-btn-plain-toolbar-medium-icon-text-right .x-btn-button { + height: 24px; +} +.x-btn-plain-toolbar-medium-icon-text-right .x-btn-inner { + line-height: 24px; + padding-right: 29px; +} +.x-btn-plain-toolbar-medium-icon-text-right .x-btn-icon-el { + width: 24px; + left: auto; +} +.x-ie6 .x-btn-plain-toolbar-medium-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-plain-toolbar-medium-icon-text-right .x-btn-icon-el { + height: 24px; +} + +.x-btn-plain-toolbar-medium-icon-text-top .x-btn-inner { + padding-top: 29px; +} +.x-btn-plain-toolbar-medium-icon-text-top .x-btn-icon-el { + height: 24px; + bottom: auto; +} +.x-ie6 .x-btn-plain-toolbar-medium-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-plain-toolbar-medium-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-plain-toolbar-medium-icon-text-bottom .x-btn-inner { + padding-bottom: 29px; +} +.x-btn-plain-toolbar-medium-icon-text-bottom .x-btn-icon-el { + height: 24px; + top: auto; +} +.x-ie6 .x-btn-plain-toolbar-medium-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-plain-toolbar-medium-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-plain-toolbar-medium-focus { + border-color: #96caee; + background-image: none; + background-color: #ebebeb; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-plain-toolbar-medium-over { + border-color: #d8d8d8; + background-image: none; + background-color: #ebebeb; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #cbcbcb), color-stop(100%, #b8b8b8)); + background-image: -webkit-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -moz-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -o-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -ms-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: linear-gradient(top, #cbcbcb, #b8b8b8); +} + +.x-btn-plain-toolbar-medium-menu-active, +.x-btn-plain-toolbar-medium-pressed { + border-color: #cecece; + background-image: none; + background-color: #e1e1e1; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #919191), color-stop(100%, #808080)); + background-image: -webkit-linear-gradient(top, #919191, #808080); + background-image: -moz-linear-gradient(top, #919191, #808080); + background-image: -o-linear-gradient(top, #919191, #808080); + background-image: -ms-linear-gradient(top, #919191, #808080); + background-image: linear-gradient(top, #919191, #808080); +} +.x-btn-plain-toolbar-medium-menu-active .x-btn-inner, +.x-btn-plain-toolbar-medium-pressed .x-btn-inner { + color: white; +} + +.x-btn-plain-toolbar-medium-focus .x-frame-tl, +.x-btn-plain-toolbar-medium-focus .x-frame-bl, +.x-btn-plain-toolbar-medium-focus .x-frame-tr, +.x-btn-plain-toolbar-medium-focus .x-frame-br, +.x-btn-plain-toolbar-medium-focus .x-frame-tc, +.x-btn-plain-toolbar-medium-focus .x-frame-bc { + background-image: url(images/btn/btn-plain-toolbar-medium-focus-corners.gif); +} +.x-btn-plain-toolbar-medium-focus .x-frame-ml, +.x-btn-plain-toolbar-medium-focus .x-frame-mr { + background-image: url(images/btn/btn-plain-toolbar-medium-focus-sides.gif); +} +.x-btn-plain-toolbar-medium-focus .x-frame-mc { + background-color: #ebebeb; + background-image: url(images/btn/btn-plain-toolbar-medium-focus-fbg.gif); +} + +.x-btn-plain-toolbar-medium-over .x-frame-tl, +.x-btn-plain-toolbar-medium-over .x-frame-bl, +.x-btn-plain-toolbar-medium-over .x-frame-tr, +.x-btn-plain-toolbar-medium-over .x-frame-br, +.x-btn-plain-toolbar-medium-over .x-frame-tc, +.x-btn-plain-toolbar-medium-over .x-frame-bc { + background-image: url(images/btn/btn-plain-toolbar-medium-over-corners.gif); +} +.x-btn-plain-toolbar-medium-over .x-frame-ml, +.x-btn-plain-toolbar-medium-over .x-frame-mr { + background-image: url(images/btn/btn-plain-toolbar-medium-over-sides.gif); +} +.x-btn-plain-toolbar-medium-over .x-frame-mc { + background-color: #ebebeb; + background-image: url(images/btn/btn-plain-toolbar-medium-over-fbg.gif); +} + +.x-btn-plain-toolbar-medium-menu-active .x-frame-tl, +.x-btn-plain-toolbar-medium-menu-active .x-frame-bl, +.x-btn-plain-toolbar-medium-menu-active .x-frame-tr, +.x-btn-plain-toolbar-medium-menu-active .x-frame-br, +.x-btn-plain-toolbar-medium-menu-active .x-frame-tc, +.x-btn-plain-toolbar-medium-menu-active .x-frame-bc, +.x-btn-plain-toolbar-medium-pressed .x-frame-tl, +.x-btn-plain-toolbar-medium-pressed .x-frame-bl, +.x-btn-plain-toolbar-medium-pressed .x-frame-tr, +.x-btn-plain-toolbar-medium-pressed .x-frame-br, +.x-btn-plain-toolbar-medium-pressed .x-frame-tc, +.x-btn-plain-toolbar-medium-pressed .x-frame-bc { + background-image: url(images/btn/btn-plain-toolbar-medium-pressed-corners.gif); +} +.x-btn-plain-toolbar-medium-menu-active .x-frame-ml, +.x-btn-plain-toolbar-medium-menu-active .x-frame-mr, +.x-btn-plain-toolbar-medium-pressed .x-frame-ml, +.x-btn-plain-toolbar-medium-pressed .x-frame-mr { + background-image: url(images/btn/btn-plain-toolbar-medium-pressed-sides.gif); +} +.x-btn-plain-toolbar-medium-menu-active .x-frame-mc, +.x-btn-plain-toolbar-medium-pressed .x-frame-mc { + background-color: #e1e1e1; + background-image: url(images/btn/btn-plain-toolbar-medium-pressed-fbg.gif); +} + +.x-btn-plain-toolbar-medium-disabled .x-frame-tl, +.x-btn-plain-toolbar-medium-disabled .x-frame-bl, +.x-btn-plain-toolbar-medium-disabled .x-frame-tr, +.x-btn-plain-toolbar-medium-disabled .x-frame-br, +.x-btn-plain-toolbar-medium-disabled .x-frame-tc, +.x-btn-plain-toolbar-medium-disabled .x-frame-bc { + background-image: url(images/btn/btn-plain-toolbar-medium-disabled-corners.gif); +} +.x-btn-plain-toolbar-medium-disabled .x-frame-ml, +.x-btn-plain-toolbar-medium-disabled .x-frame-mr { + background-image: url(images/btn/btn-plain-toolbar-medium-disabled-sides.gif); +} +.x-btn-plain-toolbar-medium-disabled .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-plain-toolbar-medium-disabled-fbg.gif); +} + +.x-nlg .x-btn-plain-toolbar-medium-focus { + background-image: url(images/btn/btn-plain-toolbar-medium-focus-bg.gif); +} + +.x-nlg .x-btn-plain-toolbar-medium-over { + background-image: url(images/btn/btn-plain-toolbar-medium-over-bg.gif); +} + +.x-nlg .x-btn-plain-toolbar-medium-menu-active, +.x-nlg .x-btn-plain-toolbar-medium-pressed { + background-image: url(images/btn/btn-plain-toolbar-medium-pressed-bg.gif); +} + +.x-nlg .x-btn-plain-toolbar-medium-disabled { + background-image: url(images/btn/btn-plain-toolbar-medium-disabled-bg.gif); +} + +.x-nbr .x-btn-plain-toolbar-medium { + background-image: none; +} + +.x-btn-plain-toolbar-medium .x-btn-split-right { + background-image: url(images/button/plain-toolbar-medium-s-arrow.png); + padding-right: 32px; +} +.x-btn-plain-toolbar-medium .x-btn-split-bottom { + background-image: url(images/button/plain-toolbar-medium-s-arrow-b.png); + padding-bottom: 28px; +} + +.x-btn-plain-toolbar-medium-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-plain-toolbar-medium-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-medium-focus-corners.gif), sides:url(images/btn/btn-plain-toolbar-medium-focus-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-medium-focus-fbg.gif), bg:url(images/btn/btn-plain-toolbar-medium-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-plain-toolbar-medium-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-medium-over-corners.gif), sides:url(images/btn/btn-plain-toolbar-medium-over-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-medium-over-fbg.gif), bg:url(images/btn/btn-plain-toolbar-medium-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-plain-toolbar-medium-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-medium-pressed-corners.gif), sides:url(images/btn/btn-plain-toolbar-medium-pressed-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-medium-pressed-fbg.gif), bg:url(images/btn/btn-plain-toolbar-medium-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-plain-toolbar-medium-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-medium-disabled-corners.gif), sides:url(images/btn/btn-plain-toolbar-medium-disabled-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-medium-disabled-fbg.gif), bg:url(images/btn/btn-plain-toolbar-medium-disabled-bg.gif)"; +} + +/**/ +/* */ +.x-btn-plain-toolbar-large { + border-color: transparent; +} + +.x-btn-plain-toolbar-large { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: transparent; +} + +.x-btn-plain-toolbar-large-mc { + background-image: url(images/btn/btn-plain-toolbar-large-fbg.gif); + background-position: 0 top; + background-color: transparent; +} + +.x-nlg .x-btn-plain-toolbar-large { + background-image: url(images/btn/btn-plain-toolbar-large-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-plain-toolbar-large { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-plain-toolbar-large-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-plain-toolbar-large-tl { + background-position: 0 -6px; +} + +.x-btn-plain-toolbar-large-tr { + background-position: right -9px; +} + +.x-btn-plain-toolbar-large-bl { + background-position: 0 -12px; +} + +.x-btn-plain-toolbar-large-br { + background-position: right -15px; +} + +.x-btn-plain-toolbar-large-ml { + background-position: 0 top; +} + +.x-btn-plain-toolbar-large-mr { + background-position: right top; +} + +.x-btn-plain-toolbar-large-tc { + background-position: 0 0; +} + +.x-btn-plain-toolbar-large-bc { + background-position: 0 -3px; +} + +.x-btn-plain-toolbar-large-tr, +.x-btn-plain-toolbar-large-br, +.x-btn-plain-toolbar-large-mr { + padding-right: 3px; +} + +.x-btn-plain-toolbar-large-tl, +.x-btn-plain-toolbar-large-bl, +.x-btn-plain-toolbar-large-ml { + padding-left: 3px; +} + +.x-btn-plain-toolbar-large-tc { + height: 3px; +} + +.x-btn-plain-toolbar-large-bc { + height: 3px; +} + +.x-btn-plain-toolbar-large-tl, +.x-btn-plain-toolbar-large-bl, +.x-btn-plain-toolbar-large-tr, +.x-btn-plain-toolbar-large-br, +.x-btn-plain-toolbar-large-tc, +.x-btn-plain-toolbar-large-bc, +.x-btn-plain-toolbar-large-ml, +.x-btn-plain-toolbar-large-mr { + zoom: 1; +} + +.x-btn-plain-toolbar-large-ml, +.x-btn-plain-toolbar-large-mr { + zoom: 1; +} + +.x-btn-plain-toolbar-large-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-plain-toolbar-large-tl, +.x-strict .x-ie7 .x-btn-plain-toolbar-large-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-plain-toolbar-large:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-plain-toolbar-large-fbg.gif), bg:url(images/btn/btn-plain-toolbar-large-bg.gif)"; +} + +/**/ +/* */ +.x-btn-plain-toolbar-large .x-btn-inner { + font-size: 16px; + font-weight: bold; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #333333; + padding: 0 10px; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-debug_02.css b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-debug_02.css new file mode 100644 index 0000000000000000000000000000000000000000..997370454aa2f56b98c744b5b4be41acc30e4fe4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-debug_02.css @@ -0,0 +1,15698 @@ + background-image: url(images/button/plain-toolbar-large-arrow.png); +} +.x-btn-plain-toolbar-large .x-btn-arrow-right { + padding-right: 36px; +} +.x-btn-plain-toolbar-large .x-btn-arrow-bottom { + padding-bottom: 32px; +} +.x-btn-plain-toolbar-large .x-btn-glyph { + font-size: 32px; + line-height: 32px; + color: #333333; +} +.x-ie8m .x-btn-plain-toolbar-large .x-btn-glyph { + color: #333333; +} + +.x-btn-plain-toolbar-large-disabled { + background-image: none; + background-color: transparent; +} + +.x-btn-plain-toolbar-large-icon .x-btn-button, +.x-btn-plain-toolbar-large-noicon .x-btn-button { + height: 32px; +} +.x-btn-plain-toolbar-large-icon .x-btn-inner, +.x-btn-plain-toolbar-large-noicon .x-btn-inner { + line-height: 32px; +} + +.x-btn-plain-toolbar-large-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-plain-toolbar-large-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-plain-toolbar-large-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-plain-toolbar-large-icon .x-btn-inner { + width: 32px; + padding: 0; +} +.x-btn-plain-toolbar-large-icon .x-btn-icon-el { + width: 32px; + height: 32px; +} + +.x-btn-plain-toolbar-large-icon-text-left .x-btn-button { + height: 32px; +} +.x-btn-plain-toolbar-large-icon-text-left .x-btn-inner { + line-height: 32px; + padding-left: 37px; +} +.x-btn-plain-toolbar-large-icon-text-left .x-btn-icon-el { + width: 32px; + right: auto; +} +.x-ie6 .x-btn-plain-toolbar-large-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-plain-toolbar-large-icon-text-left .x-btn-icon-el { + height: 32px; +} + +.x-btn-plain-toolbar-large-icon-text-right .x-btn-button { + height: 32px; +} +.x-btn-plain-toolbar-large-icon-text-right .x-btn-inner { + line-height: 32px; + padding-right: 37px; +} +.x-btn-plain-toolbar-large-icon-text-right .x-btn-icon-el { + width: 32px; + left: auto; +} +.x-ie6 .x-btn-plain-toolbar-large-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-plain-toolbar-large-icon-text-right .x-btn-icon-el { + height: 32px; +} + +.x-btn-plain-toolbar-large-icon-text-top .x-btn-inner { + padding-top: 37px; +} +.x-btn-plain-toolbar-large-icon-text-top .x-btn-icon-el { + height: 32px; + bottom: auto; +} +.x-ie6 .x-btn-plain-toolbar-large-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-plain-toolbar-large-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-plain-toolbar-large-icon-text-bottom .x-btn-inner { + padding-bottom: 37px; +} +.x-btn-plain-toolbar-large-icon-text-bottom .x-btn-icon-el { + height: 32px; + top: auto; +} +.x-ie6 .x-btn-plain-toolbar-large-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-plain-toolbar-large-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-plain-toolbar-large-focus { + border-color: #96caee; + background-image: none; + background-color: #ebebeb; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-plain-toolbar-large-over { + border-color: #d8d8d8; + background-image: none; + background-color: #ebebeb; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #cbcbcb), color-stop(100%, #b8b8b8)); + background-image: -webkit-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -moz-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -o-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -ms-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: linear-gradient(top, #cbcbcb, #b8b8b8); +} + +.x-btn-plain-toolbar-large-menu-active, +.x-btn-plain-toolbar-large-pressed { + border-color: #cecece; + background-image: none; + background-color: #e1e1e1; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #919191), color-stop(100%, #808080)); + background-image: -webkit-linear-gradient(top, #919191, #808080); + background-image: -moz-linear-gradient(top, #919191, #808080); + background-image: -o-linear-gradient(top, #919191, #808080); + background-image: -ms-linear-gradient(top, #919191, #808080); + background-image: linear-gradient(top, #919191, #808080); +} +.x-btn-plain-toolbar-large-menu-active .x-btn-inner, +.x-btn-plain-toolbar-large-pressed .x-btn-inner { + color: white; +} + +.x-btn-plain-toolbar-large-focus .x-frame-tl, +.x-btn-plain-toolbar-large-focus .x-frame-bl, +.x-btn-plain-toolbar-large-focus .x-frame-tr, +.x-btn-plain-toolbar-large-focus .x-frame-br, +.x-btn-plain-toolbar-large-focus .x-frame-tc, +.x-btn-plain-toolbar-large-focus .x-frame-bc { + background-image: url(images/btn/btn-plain-toolbar-large-focus-corners.gif); +} +.x-btn-plain-toolbar-large-focus .x-frame-ml, +.x-btn-plain-toolbar-large-focus .x-frame-mr { + background-image: url(images/btn/btn-plain-toolbar-large-focus-sides.gif); +} +.x-btn-plain-toolbar-large-focus .x-frame-mc { + background-color: #ebebeb; + background-image: url(images/btn/btn-plain-toolbar-large-focus-fbg.gif); +} + +.x-btn-plain-toolbar-large-over .x-frame-tl, +.x-btn-plain-toolbar-large-over .x-frame-bl, +.x-btn-plain-toolbar-large-over .x-frame-tr, +.x-btn-plain-toolbar-large-over .x-frame-br, +.x-btn-plain-toolbar-large-over .x-frame-tc, +.x-btn-plain-toolbar-large-over .x-frame-bc { + background-image: url(images/btn/btn-plain-toolbar-large-over-corners.gif); +} +.x-btn-plain-toolbar-large-over .x-frame-ml, +.x-btn-plain-toolbar-large-over .x-frame-mr { + background-image: url(images/btn/btn-plain-toolbar-large-over-sides.gif); +} +.x-btn-plain-toolbar-large-over .x-frame-mc { + background-color: #ebebeb; + background-image: url(images/btn/btn-plain-toolbar-large-over-fbg.gif); +} + +.x-btn-plain-toolbar-large-menu-active .x-frame-tl, +.x-btn-plain-toolbar-large-menu-active .x-frame-bl, +.x-btn-plain-toolbar-large-menu-active .x-frame-tr, +.x-btn-plain-toolbar-large-menu-active .x-frame-br, +.x-btn-plain-toolbar-large-menu-active .x-frame-tc, +.x-btn-plain-toolbar-large-menu-active .x-frame-bc, +.x-btn-plain-toolbar-large-pressed .x-frame-tl, +.x-btn-plain-toolbar-large-pressed .x-frame-bl, +.x-btn-plain-toolbar-large-pressed .x-frame-tr, +.x-btn-plain-toolbar-large-pressed .x-frame-br, +.x-btn-plain-toolbar-large-pressed .x-frame-tc, +.x-btn-plain-toolbar-large-pressed .x-frame-bc { + background-image: url(images/btn/btn-plain-toolbar-large-pressed-corners.gif); +} +.x-btn-plain-toolbar-large-menu-active .x-frame-ml, +.x-btn-plain-toolbar-large-menu-active .x-frame-mr, +.x-btn-plain-toolbar-large-pressed .x-frame-ml, +.x-btn-plain-toolbar-large-pressed .x-frame-mr { + background-image: url(images/btn/btn-plain-toolbar-large-pressed-sides.gif); +} +.x-btn-plain-toolbar-large-menu-active .x-frame-mc, +.x-btn-plain-toolbar-large-pressed .x-frame-mc { + background-color: #e1e1e1; + background-image: url(images/btn/btn-plain-toolbar-large-pressed-fbg.gif); +} + +.x-btn-plain-toolbar-large-disabled .x-frame-tl, +.x-btn-plain-toolbar-large-disabled .x-frame-bl, +.x-btn-plain-toolbar-large-disabled .x-frame-tr, +.x-btn-plain-toolbar-large-disabled .x-frame-br, +.x-btn-plain-toolbar-large-disabled .x-frame-tc, +.x-btn-plain-toolbar-large-disabled .x-frame-bc { + background-image: url(images/btn/btn-plain-toolbar-large-disabled-corners.gif); +} +.x-btn-plain-toolbar-large-disabled .x-frame-ml, +.x-btn-plain-toolbar-large-disabled .x-frame-mr { + background-image: url(images/btn/btn-plain-toolbar-large-disabled-sides.gif); +} +.x-btn-plain-toolbar-large-disabled .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-plain-toolbar-large-disabled-fbg.gif); +} + +.x-nlg .x-btn-plain-toolbar-large-focus { + background-image: url(images/btn/btn-plain-toolbar-large-focus-bg.gif); +} + +.x-nlg .x-btn-plain-toolbar-large-over { + background-image: url(images/btn/btn-plain-toolbar-large-over-bg.gif); +} + +.x-nlg .x-btn-plain-toolbar-large-menu-active, +.x-nlg .x-btn-plain-toolbar-large-pressed { + background-image: url(images/btn/btn-plain-toolbar-large-pressed-bg.gif); +} + +.x-nlg .x-btn-plain-toolbar-large-disabled { + background-image: url(images/btn/btn-plain-toolbar-large-disabled-bg.gif); +} + +.x-nbr .x-btn-plain-toolbar-large { + background-image: none; +} + +.x-btn-plain-toolbar-large .x-btn-split-right { + background-image: url(images/button/plain-toolbar-large-s-arrow.png); + padding-right: 38px; +} +.x-btn-plain-toolbar-large .x-btn-split-bottom { + background-image: url(images/button/plain-toolbar-large-s-arrow-b.png); + padding-bottom: 34px; +} + +.x-btn-plain-toolbar-large-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-plain-toolbar-large-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-large-focus-corners.gif), sides:url(images/btn/btn-plain-toolbar-large-focus-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-large-focus-fbg.gif), bg:url(images/btn/btn-plain-toolbar-large-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-plain-toolbar-large-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-large-over-corners.gif), sides:url(images/btn/btn-plain-toolbar-large-over-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-large-over-fbg.gif), bg:url(images/btn/btn-plain-toolbar-large-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-plain-toolbar-large-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-large-pressed-corners.gif), sides:url(images/btn/btn-plain-toolbar-large-pressed-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-large-pressed-fbg.gif), bg:url(images/btn/btn-plain-toolbar-large-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-plain-toolbar-large-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-large-disabled-corners.gif), sides:url(images/btn/btn-plain-toolbar-large-disabled-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-large-disabled-fbg.gif), bg:url(images/btn/btn-plain-toolbar-large-disabled-bg.gif)"; +} + +/**/ +/* */ +.x-btn-plain-toolbar-small-disabled .x-btn-icon-el, +.x-btn-plain-toolbar-medium-disabled .x-btn-icon-el, +.x-btn-plain-toolbar-large-disabled .x-btn-icon-el { + background-color: transparent; +} +.x-strict .x-ie8 .x-btn-plain-toolbar-small-disabled .x-btn-icon-el, .x-strict .x-ie8 +.x-btn-plain-toolbar-medium-disabled .x-btn-icon-el, .x-strict .x-ie8 +.x-btn-plain-toolbar-large-disabled .x-btn-icon-el { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +.x-toolbar-default .x-toolbar-scroll-left { + margin-right: 4px; +} +.x-toolbar-default .x-toolbar-scroll-right { + margin-left: 4px; +} +.x-toolbar-default .x-toolbar-scroll-left, .x-toolbar-default .x-toolbar-scroll-right { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60); + opacity: 0.6; +} +.x-toolbar-default .x-toolbar-scroll-left-hover, .x-toolbar-default .x-toolbar-scroll-right-hover { + background-position: 0 0; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); + opacity: 0.8; +} +.x-toolbar-default .x-toolbar-scroll-left-pressed, .x-toolbar-default .x-toolbar-scroll-right-pressed { + background-position: 0 0; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); + opacity: 1; +} +.x-toolbar-default .x-box-scroller-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=25); + opacity: 0.25; +} +.x-toolbar-default .x-box-scroller { + background-color: transparent; +} + +.x-toolbar-scroller { + padding: 6px 4px 6px 4px; +} + +.x-toolbar-vertical-scroller { + padding: 3px 8px 3px 8px; +} + +.x-panel-light { + border-color: #e4e4e4; + padding: 0; +} + +.x-panel-header-light { + font-size: 13px; + border: 1px solid #e4e4e4; +} +.x-panel-header-light .x-tool-img { + background-image: url(images/tools/tool-sprites-dark.png); + background-color: #e4e4e4; +} + +.x-panel-header-light-horizontal { + padding: 9px 9px 10px 9px; +} + +.x-panel-header-light-horizontal-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-light-vertical { + padding: 9px 9px 9px 10px; +} + +.x-panel-header-light-vertical-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-text-container-light { + color: #666666; + font-size: 13px; + font-weight: bold; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 15px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-light { + background: transparent; + border-color: #e4e4e4; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-header-light { + background-image: none; + background-color: #e4e4e4; +} + +.x-panel-header-light-vertical { + background-image: none; + background-color: #e4e4e4; +} + +.x-panel .x-panel-header-light-collapsed-border-top { + border-bottom-width: 1px !important; +} +.x-panel .x-panel-header-light-collapsed-border-right { + border-left-width: 1px !important; +} +.x-panel .x-panel-header-light-collapsed-border-bottom { + border-top-width: 1px !important; +} +.x-panel .x-panel-header-light-collapsed-border-left { + border-right-width: 1px !important; +} + +/**/ +.x-panel-header-light-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-light-bottom:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-light-left:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +/**/ +.x-panel-header-light-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-panel-header-light-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-light-vertical .x-panel-header-text-container { + background-color: #e4e4e4; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#e4e4e4); +} + +.x-panel-header-light .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-light .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-light .x-panel-header-glyph { + color: #f1f1f1; +} + +.x-panel-header-light-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-light-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-light-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-light-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-light-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-light-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-light-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-light-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-light-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-light-outer-border-l { + border-left-color: #e4e4e4 !important; + border-left-width: 1px !important; +} + +.x-panel-light-outer-border-b { + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 1px !important; +} + +.x-panel-light-outer-border-bl { + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 1px !important; + border-left-color: #e4e4e4 !important; + border-left-width: 1px !important; +} + +.x-panel-light-outer-border-r { + border-right-color: #e4e4e4 !important; + border-right-width: 1px !important; +} + +.x-panel-light-outer-border-rl { + border-right-color: #e4e4e4 !important; + border-right-width: 1px !important; + border-left-color: #e4e4e4 !important; + border-left-width: 1px !important; +} + +.x-panel-light-outer-border-rb { + border-right-color: #e4e4e4 !important; + border-right-width: 1px !important; + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 1px !important; +} + +.x-panel-light-outer-border-rbl { + border-right-color: #e4e4e4 !important; + border-right-width: 1px !important; + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 1px !important; + border-left-color: #e4e4e4 !important; + border-left-width: 1px !important; +} + +.x-panel-light-outer-border-t { + border-top-color: #e4e4e4 !important; + border-top-width: 1px !important; +} + +.x-panel-light-outer-border-tl { + border-top-color: #e4e4e4 !important; + border-top-width: 1px !important; + border-left-color: #e4e4e4 !important; + border-left-width: 1px !important; +} + +.x-panel-light-outer-border-tb { + border-top-color: #e4e4e4 !important; + border-top-width: 1px !important; + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 1px !important; +} + +.x-panel-light-outer-border-tbl { + border-top-color: #e4e4e4 !important; + border-top-width: 1px !important; + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 1px !important; + border-left-color: #e4e4e4 !important; + border-left-width: 1px !important; +} + +.x-panel-light-outer-border-tr { + border-top-color: #e4e4e4 !important; + border-top-width: 1px !important; + border-right-color: #e4e4e4 !important; + border-right-width: 1px !important; +} + +.x-panel-light-outer-border-trl { + border-top-color: #e4e4e4 !important; + border-top-width: 1px !important; + border-right-color: #e4e4e4 !important; + border-right-width: 1px !important; + border-left-color: #e4e4e4 !important; + border-left-width: 1px !important; +} + +.x-panel-light-outer-border-trb { + border-top-color: #e4e4e4 !important; + border-top-width: 1px !important; + border-right-color: #e4e4e4 !important; + border-right-width: 1px !important; + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 1px !important; +} + +.x-panel-light-outer-border-trbl { + border-color: #e4e4e4 !important; + border-width: 1px !important; +} + +.x-panel-light-framed { + border-color: #e4e4e4; + padding: 0; +} + +.x-panel-header-light-framed { + font-size: 13px; + border: 2px solid #e4e4e4; +} +.x-panel-header-light-framed .x-tool-img { + background-image: url(images/tools/tool-sprites-dark.png); + background-color: #e4e4e4; +} + +.x-panel-header-light-framed-horizontal { + padding: 5px; +} + +.x-panel-header-light-framed-horizontal-noborder { + padding: 7px 7px 5px 7px; +} + +.x-panel-header-light-framed-vertical { + padding: 5px 5px 5px 5px; +} + +.x-panel-header-light-framed-vertical-noborder { + padding: 7px 7px 7px 5px; +} + +.x-panel-header-text-container-light-framed { + color: #666666; + font-size: 13px; + font-weight: bold; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 15px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-light-framed { + background: white; + border-color: #e4e4e4; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-light-framed { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 2px 2px 2px 2px; + border-width: 2px; + border-style: solid; + background-color: white; +} + +.x-panel-light-framed-mc { + background-color: white; +} + +.x-nbr .x-panel-light-framed { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-light-framed-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-2-2-2-2; +} + +.x-panel-light-framed-tl { + background-position: 0 -8px; +} + +.x-panel-light-framed-tr { + background-position: right -12px; +} + +.x-panel-light-framed-bl { + background-position: 0 -16px; +} + +.x-panel-light-framed-br { + background-position: right -20px; +} + +.x-panel-light-framed-ml { + background-position: 0 top; +} + +.x-panel-light-framed-mr { + background-position: right top; +} + +.x-panel-light-framed-tc { + background-position: 0 0; +} + +.x-panel-light-framed-bc { + background-position: 0 -4px; +} + +.x-panel-light-framed-tr, +.x-panel-light-framed-br, +.x-panel-light-framed-mr { + padding-right: 4px; +} + +.x-panel-light-framed-tl, +.x-panel-light-framed-bl, +.x-panel-light-framed-ml { + padding-left: 4px; +} + +.x-panel-light-framed-tc { + height: 4px; +} + +.x-panel-light-framed-bc { + height: 4px; +} + +.x-panel-light-framed-tl, +.x-panel-light-framed-bl, +.x-panel-light-framed-tr, +.x-panel-light-framed-br, +.x-panel-light-framed-tc, +.x-panel-light-framed-bc, +.x-panel-light-framed-ml, +.x-panel-light-framed-mr { + zoom: 1; + background-image: url(images/panel/panel-light-framed-corners.gif); +} + +.x-panel-light-framed-ml, +.x-panel-light-framed-mr { + zoom: 1; + background-image: url(images/panel/panel-light-framed-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-light-framed-mc { + padding: 0px 0px 0px 0px; +} + +.x-strict .x-ie7 .x-panel-light-framed-tl, +.x-strict .x-ie7 .x-panel-light-framed-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-light-framed:after { + display: none; + content: "x-slicer:corners:url(images/panel/panel-light-framed-corners.gif), sides:url(images/panel/panel-light-framed-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-light-framed-top { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 5px 5px 5px 5px; + border-width: 2px 2px 0 2px; + border-style: solid; + background-color: #e4e4e4; +} + +.x-panel-header-light-framed-top-mc { + background-color: #e4e4e4; +} + +.x-nbr .x-panel-header-light-framed-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-light-framed-top-frameInfo { + font-family: dh-4-4-0-0-2-2-0-2-5-5-5-5; +} + +.x-panel-header-light-framed-top-tl { + background-position: 0 -8px; +} + +.x-panel-header-light-framed-top-tr { + background-position: right -12px; +} + +.x-panel-header-light-framed-top-bl { + background-position: 0 -16px; +} + +.x-panel-header-light-framed-top-br { + background-position: right -20px; +} + +.x-panel-header-light-framed-top-ml { + background-position: 0 top; +} + +.x-panel-header-light-framed-top-mr { + background-position: right top; +} + +.x-panel-header-light-framed-top-tc { + background-position: 0 0; +} + +.x-panel-header-light-framed-top-bc { + background-position: 0 -4px; +} + +.x-panel-header-light-framed-top-tr, +.x-panel-header-light-framed-top-br, +.x-panel-header-light-framed-top-mr { + padding-right: 4px; +} + +.x-panel-header-light-framed-top-tl, +.x-panel-header-light-framed-top-bl, +.x-panel-header-light-framed-top-ml { + padding-left: 4px; +} + +.x-panel-header-light-framed-top-tc { + height: 4px; +} + +.x-panel-header-light-framed-top-bc { + height: 0; +} + +.x-panel-header-light-framed-top-tl, +.x-panel-header-light-framed-top-bl, +.x-panel-header-light-framed-top-tr, +.x-panel-header-light-framed-top-br, +.x-panel-header-light-framed-top-tc, +.x-panel-header-light-framed-top-bc, +.x-panel-header-light-framed-top-ml, +.x-panel-header-light-framed-top-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-top-corners.gif); +} + +.x-panel-header-light-framed-top-ml, +.x-panel-header-light-framed-top-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-top-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-light-framed-top-mc { + padding: 3px 3px 5px 3px; +} + +.x-strict .x-ie7 .x-panel-header-light-framed-top-tl, +.x-strict .x-ie7 .x-panel-header-light-framed-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-light-framed-top:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-light-framed-top-corners.gif), sides:url(images/panel-header/panel-header-light-framed-top-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-light-framed-right { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 5px 5px 5px 5px; + border-width: 2px 2px 2px 0; + border-style: solid; + background-color: #e4e4e4; +} + +.x-panel-header-light-framed-right-mc { + background-color: #e4e4e4; +} + +.x-nbr .x-panel-header-light-framed-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-light-framed-right-frameInfo { + font-family: dh-0-4-4-0-2-2-2-0-5-5-5-5; +} + +.x-panel-header-light-framed-right-tl { + background-position: 0 -8px; +} + +.x-panel-header-light-framed-right-tr { + background-position: right -12px; +} + +.x-panel-header-light-framed-right-bl { + background-position: 0 -16px; +} + +.x-panel-header-light-framed-right-br { + background-position: right -20px; +} + +.x-panel-header-light-framed-right-ml { + background-position: 0 right; +} + +.x-panel-header-light-framed-right-mr { + background-position: right right; +} + +.x-panel-header-light-framed-right-tc { + background-position: 0 0; +} + +.x-panel-header-light-framed-right-bc { + background-position: 0 -4px; +} + +.x-panel-header-light-framed-right-tr, +.x-panel-header-light-framed-right-br, +.x-panel-header-light-framed-right-mr { + padding-right: 4px; +} + +.x-panel-header-light-framed-right-tl, +.x-panel-header-light-framed-right-bl, +.x-panel-header-light-framed-right-ml { + padding-left: 0; +} + +.x-panel-header-light-framed-right-tc { + height: 4px; +} + +.x-panel-header-light-framed-right-bc { + height: 4px; +} + +.x-panel-header-light-framed-right-tl, +.x-panel-header-light-framed-right-bl, +.x-panel-header-light-framed-right-tr, +.x-panel-header-light-framed-right-br, +.x-panel-header-light-framed-right-tc, +.x-panel-header-light-framed-right-bc, +.x-panel-header-light-framed-right-ml, +.x-panel-header-light-framed-right-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-right-corners.gif); +} + +.x-panel-header-light-framed-right-ml, +.x-panel-header-light-framed-right-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-right-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-light-framed-right-mc { + padding: 3px 3px 3px 5px; +} + +.x-strict .x-ie7 .x-panel-header-light-framed-right-tl, +.x-strict .x-ie7 .x-panel-header-light-framed-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-light-framed-right:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-light-framed-right-corners.gif), sides:url(images/panel-header/panel-header-light-framed-right-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-light-framed-bottom { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 5px 5px 5px 5px; + border-width: 0 2px 2px 2px; + border-style: solid; + background-color: #e4e4e4; +} + +.x-panel-header-light-framed-bottom-mc { + background-color: #e4e4e4; +} + +.x-nbr .x-panel-header-light-framed-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-light-framed-bottom-frameInfo { + font-family: dh-0-0-4-4-0-2-2-2-5-5-5-5; +} + +.x-panel-header-light-framed-bottom-tl { + background-position: 0 -8px; +} + +.x-panel-header-light-framed-bottom-tr { + background-position: right -12px; +} + +.x-panel-header-light-framed-bottom-bl { + background-position: 0 -16px; +} + +.x-panel-header-light-framed-bottom-br { + background-position: right -20px; +} + +.x-panel-header-light-framed-bottom-ml { + background-position: 0 bottom; +} + +.x-panel-header-light-framed-bottom-mr { + background-position: right bottom; +} + +.x-panel-header-light-framed-bottom-tc { + background-position: 0 0; +} + +.x-panel-header-light-framed-bottom-bc { + background-position: 0 -4px; +} + +.x-panel-header-light-framed-bottom-tr, +.x-panel-header-light-framed-bottom-br, +.x-panel-header-light-framed-bottom-mr { + padding-right: 4px; +} + +.x-panel-header-light-framed-bottom-tl, +.x-panel-header-light-framed-bottom-bl, +.x-panel-header-light-framed-bottom-ml { + padding-left: 4px; +} + +.x-panel-header-light-framed-bottom-tc { + height: 0; +} + +.x-panel-header-light-framed-bottom-bc { + height: 4px; +} + +.x-panel-header-light-framed-bottom-tl, +.x-panel-header-light-framed-bottom-bl, +.x-panel-header-light-framed-bottom-tr, +.x-panel-header-light-framed-bottom-br, +.x-panel-header-light-framed-bottom-tc, +.x-panel-header-light-framed-bottom-bc, +.x-panel-header-light-framed-bottom-ml, +.x-panel-header-light-framed-bottom-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-bottom-corners.gif); +} + +.x-panel-header-light-framed-bottom-ml, +.x-panel-header-light-framed-bottom-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-light-framed-bottom-mc { + padding: 5px 3px 3px 3px; +} + +.x-strict .x-ie7 .x-panel-header-light-framed-bottom-tl, +.x-strict .x-ie7 .x-panel-header-light-framed-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-light-framed-bottom:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-light-framed-bottom-corners.gif), sides:url(images/panel-header/panel-header-light-framed-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-light-framed-left { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 5px 5px 5px 5px; + border-width: 2px 0 2px 2px; + border-style: solid; + background-color: #e4e4e4; +} + +.x-panel-header-light-framed-left-mc { + background-color: #e4e4e4; +} + +.x-nbr .x-panel-header-light-framed-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-light-framed-left-frameInfo { + font-family: dh-4-0-0-4-2-0-2-2-5-5-5-5; +} + +.x-panel-header-light-framed-left-tl { + background-position: 0 -8px; +} + +.x-panel-header-light-framed-left-tr { + background-position: right -12px; +} + +.x-panel-header-light-framed-left-bl { + background-position: 0 -16px; +} + +.x-panel-header-light-framed-left-br { + background-position: right -20px; +} + +.x-panel-header-light-framed-left-ml { + background-position: 0 left; +} + +.x-panel-header-light-framed-left-mr { + background-position: right left; +} + +.x-panel-header-light-framed-left-tc { + background-position: 0 0; +} + +.x-panel-header-light-framed-left-bc { + background-position: 0 -4px; +} + +.x-panel-header-light-framed-left-tr, +.x-panel-header-light-framed-left-br, +.x-panel-header-light-framed-left-mr { + padding-right: 0; +} + +.x-panel-header-light-framed-left-tl, +.x-panel-header-light-framed-left-bl, +.x-panel-header-light-framed-left-ml { + padding-left: 4px; +} + +.x-panel-header-light-framed-left-tc { + height: 4px; +} + +.x-panel-header-light-framed-left-bc { + height: 4px; +} + +.x-panel-header-light-framed-left-tl, +.x-panel-header-light-framed-left-bl, +.x-panel-header-light-framed-left-tr, +.x-panel-header-light-framed-left-br, +.x-panel-header-light-framed-left-tc, +.x-panel-header-light-framed-left-bc, +.x-panel-header-light-framed-left-ml, +.x-panel-header-light-framed-left-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-left-corners.gif); +} + +.x-panel-header-light-framed-left-ml, +.x-panel-header-light-framed-left-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-left-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-light-framed-left-mc { + padding: 3px 5px 3px 3px; +} + +.x-strict .x-ie7 .x-panel-header-light-framed-left-tl, +.x-strict .x-ie7 .x-panel-header-light-framed-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-light-framed-left:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-light-framed-left-corners.gif), sides:url(images/panel-header/panel-header-light-framed-left-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-light-framed-collapsed-top { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 5px 5px 5px 5px; + border-width: 2px; + border-style: solid; + background-color: #e4e4e4; +} + +.x-panel-header-light-framed-collapsed-top-mc { + background-color: #e4e4e4; +} + +.x-nbr .x-panel-header-light-framed-collapsed-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-light-framed-collapsed-top-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-5-5-5-5; +} + +.x-panel-header-light-framed-collapsed-top-tl { + background-position: 0 -8px; +} + +.x-panel-header-light-framed-collapsed-top-tr { + background-position: right -12px; +} + +.x-panel-header-light-framed-collapsed-top-bl { + background-position: 0 -16px; +} + +.x-panel-header-light-framed-collapsed-top-br { + background-position: right -20px; +} + +.x-panel-header-light-framed-collapsed-top-ml { + background-position: 0 top; +} + +.x-panel-header-light-framed-collapsed-top-mr { + background-position: right top; +} + +.x-panel-header-light-framed-collapsed-top-tc { + background-position: 0 0; +} + +.x-panel-header-light-framed-collapsed-top-bc { + background-position: 0 -4px; +} + +.x-panel-header-light-framed-collapsed-top-tr, +.x-panel-header-light-framed-collapsed-top-br, +.x-panel-header-light-framed-collapsed-top-mr { + padding-right: 4px; +} + +.x-panel-header-light-framed-collapsed-top-tl, +.x-panel-header-light-framed-collapsed-top-bl, +.x-panel-header-light-framed-collapsed-top-ml { + padding-left: 4px; +} + +.x-panel-header-light-framed-collapsed-top-tc { + height: 4px; +} + +.x-panel-header-light-framed-collapsed-top-bc { + height: 4px; +} + +.x-panel-header-light-framed-collapsed-top-tl, +.x-panel-header-light-framed-collapsed-top-bl, +.x-panel-header-light-framed-collapsed-top-tr, +.x-panel-header-light-framed-collapsed-top-br, +.x-panel-header-light-framed-collapsed-top-tc, +.x-panel-header-light-framed-collapsed-top-bc, +.x-panel-header-light-framed-collapsed-top-ml, +.x-panel-header-light-framed-collapsed-top-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-collapsed-top-corners.gif); +} + +.x-panel-header-light-framed-collapsed-top-ml, +.x-panel-header-light-framed-collapsed-top-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-collapsed-top-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-light-framed-collapsed-top-mc { + padding: 3px 3px 3px 3px; +} + +.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-top-tl, +.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-light-framed-collapsed-top:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-light-framed-collapsed-top-corners.gif), sides:url(images/panel-header/panel-header-light-framed-collapsed-top-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-light-framed-collapsed-right { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 5px 5px 5px 5px; + border-width: 2px; + border-style: solid; + background-color: #e4e4e4; +} + +.x-panel-header-light-framed-collapsed-right-mc { + background-color: #e4e4e4; +} + +.x-nbr .x-panel-header-light-framed-collapsed-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-light-framed-collapsed-right-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-5-5-5-5; +} + +.x-panel-header-light-framed-collapsed-right-tl { + background-position: 0 -8px; +} + +.x-panel-header-light-framed-collapsed-right-tr { + background-position: right -12px; +} + +.x-panel-header-light-framed-collapsed-right-bl { + background-position: 0 -16px; +} + +.x-panel-header-light-framed-collapsed-right-br { + background-position: right -20px; +} + +.x-panel-header-light-framed-collapsed-right-ml { + background-position: 0 right; +} + +.x-panel-header-light-framed-collapsed-right-mr { + background-position: right right; +} + +.x-panel-header-light-framed-collapsed-right-tc { + background-position: 0 0; +} + +.x-panel-header-light-framed-collapsed-right-bc { + background-position: 0 -4px; +} + +.x-panel-header-light-framed-collapsed-right-tr, +.x-panel-header-light-framed-collapsed-right-br, +.x-panel-header-light-framed-collapsed-right-mr { + padding-right: 4px; +} + +.x-panel-header-light-framed-collapsed-right-tl, +.x-panel-header-light-framed-collapsed-right-bl, +.x-panel-header-light-framed-collapsed-right-ml { + padding-left: 4px; +} + +.x-panel-header-light-framed-collapsed-right-tc { + height: 4px; +} + +.x-panel-header-light-framed-collapsed-right-bc { + height: 4px; +} + +.x-panel-header-light-framed-collapsed-right-tl, +.x-panel-header-light-framed-collapsed-right-bl, +.x-panel-header-light-framed-collapsed-right-tr, +.x-panel-header-light-framed-collapsed-right-br, +.x-panel-header-light-framed-collapsed-right-tc, +.x-panel-header-light-framed-collapsed-right-bc, +.x-panel-header-light-framed-collapsed-right-ml, +.x-panel-header-light-framed-collapsed-right-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-collapsed-right-corners.gif); +} + +.x-panel-header-light-framed-collapsed-right-ml, +.x-panel-header-light-framed-collapsed-right-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-collapsed-right-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-light-framed-collapsed-right-mc { + padding: 3px 3px 3px 3px; +} + +.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-right-tl, +.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-light-framed-collapsed-right:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-light-framed-collapsed-right-corners.gif), sides:url(images/panel-header/panel-header-light-framed-collapsed-right-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-light-framed-collapsed-bottom { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 5px 5px 5px 5px; + border-width: 2px; + border-style: solid; + background-color: #e4e4e4; +} + +.x-panel-header-light-framed-collapsed-bottom-mc { + background-color: #e4e4e4; +} + +.x-nbr .x-panel-header-light-framed-collapsed-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-light-framed-collapsed-bottom-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-5-5-5-5; +} + +.x-panel-header-light-framed-collapsed-bottom-tl { + background-position: 0 -8px; +} + +.x-panel-header-light-framed-collapsed-bottom-tr { + background-position: right -12px; +} + +.x-panel-header-light-framed-collapsed-bottom-bl { + background-position: 0 -16px; +} + +.x-panel-header-light-framed-collapsed-bottom-br { + background-position: right -20px; +} + +.x-panel-header-light-framed-collapsed-bottom-ml { + background-position: 0 bottom; +} + +.x-panel-header-light-framed-collapsed-bottom-mr { + background-position: right bottom; +} + +.x-panel-header-light-framed-collapsed-bottom-tc { + background-position: 0 0; +} + +.x-panel-header-light-framed-collapsed-bottom-bc { + background-position: 0 -4px; +} + +.x-panel-header-light-framed-collapsed-bottom-tr, +.x-panel-header-light-framed-collapsed-bottom-br, +.x-panel-header-light-framed-collapsed-bottom-mr { + padding-right: 4px; +} + +.x-panel-header-light-framed-collapsed-bottom-tl, +.x-panel-header-light-framed-collapsed-bottom-bl, +.x-panel-header-light-framed-collapsed-bottom-ml { + padding-left: 4px; +} + +.x-panel-header-light-framed-collapsed-bottom-tc { + height: 4px; +} + +.x-panel-header-light-framed-collapsed-bottom-bc { + height: 4px; +} + +.x-panel-header-light-framed-collapsed-bottom-tl, +.x-panel-header-light-framed-collapsed-bottom-bl, +.x-panel-header-light-framed-collapsed-bottom-tr, +.x-panel-header-light-framed-collapsed-bottom-br, +.x-panel-header-light-framed-collapsed-bottom-tc, +.x-panel-header-light-framed-collapsed-bottom-bc, +.x-panel-header-light-framed-collapsed-bottom-ml, +.x-panel-header-light-framed-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-collapsed-bottom-corners.gif); +} + +.x-panel-header-light-framed-collapsed-bottom-ml, +.x-panel-header-light-framed-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-collapsed-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-light-framed-collapsed-bottom-mc { + padding: 3px 3px 3px 3px; +} + +.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-bottom-tl, +.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-light-framed-collapsed-bottom:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-light-framed-collapsed-bottom-corners.gif), sides:url(images/panel-header/panel-header-light-framed-collapsed-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-panel-header-light-framed-collapsed-left { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 5px 5px 5px 5px; + border-width: 2px; + border-style: solid; + background-color: #e4e4e4; +} + +.x-panel-header-light-framed-collapsed-left-mc { + background-color: #e4e4e4; +} + +.x-nbr .x-panel-header-light-framed-collapsed-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-panel-header-light-framed-collapsed-left-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-5-5-5-5; +} + +.x-panel-header-light-framed-collapsed-left-tl { + background-position: 0 -8px; +} + +.x-panel-header-light-framed-collapsed-left-tr { + background-position: right -12px; +} + +.x-panel-header-light-framed-collapsed-left-bl { + background-position: 0 -16px; +} + +.x-panel-header-light-framed-collapsed-left-br { + background-position: right -20px; +} + +.x-panel-header-light-framed-collapsed-left-ml { + background-position: 0 left; +} + +.x-panel-header-light-framed-collapsed-left-mr { + background-position: right left; +} + +.x-panel-header-light-framed-collapsed-left-tc { + background-position: 0 0; +} + +.x-panel-header-light-framed-collapsed-left-bc { + background-position: 0 -4px; +} + +.x-panel-header-light-framed-collapsed-left-tr, +.x-panel-header-light-framed-collapsed-left-br, +.x-panel-header-light-framed-collapsed-left-mr { + padding-right: 4px; +} + +.x-panel-header-light-framed-collapsed-left-tl, +.x-panel-header-light-framed-collapsed-left-bl, +.x-panel-header-light-framed-collapsed-left-ml { + padding-left: 4px; +} + +.x-panel-header-light-framed-collapsed-left-tc { + height: 4px; +} + +.x-panel-header-light-framed-collapsed-left-bc { + height: 4px; +} + +.x-panel-header-light-framed-collapsed-left-tl, +.x-panel-header-light-framed-collapsed-left-bl, +.x-panel-header-light-framed-collapsed-left-tr, +.x-panel-header-light-framed-collapsed-left-br, +.x-panel-header-light-framed-collapsed-left-tc, +.x-panel-header-light-framed-collapsed-left-bc, +.x-panel-header-light-framed-collapsed-left-ml, +.x-panel-header-light-framed-collapsed-left-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-collapsed-left-corners.gif); +} + +.x-panel-header-light-framed-collapsed-left-ml, +.x-panel-header-light-framed-collapsed-left-mr { + zoom: 1; + background-image: url(images/panel-header/panel-header-light-framed-collapsed-left-sides.gif); + background-repeat: repeat-y; +} + +.x-panel-header-light-framed-collapsed-left-mc { + padding: 3px 3px 3px 3px; +} + +.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-left-tl, +.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-panel-header-light-framed-collapsed-left:after { + display: none; + content: "x-slicer:corners:url(images/panel-header/panel-header-light-framed-collapsed-left-corners.gif), sides:url(images/panel-header/panel-header-light-framed-collapsed-left-sides.gif)"; +} + +/**/ +/* */ +.x-panel .x-panel-header-light-framed-top { + border-bottom-width: 2px !important; +} +.x-panel .x-panel-header-light-framed-right { + border-left-width: 2px !important; +} +.x-panel .x-panel-header-light-framed-bottom { + border-top-width: 2px !important; +} +.x-panel .x-panel-header-light-framed-left { + border-right-width: 2px !important; +} + +.x-nbr .x-panel-header-light-framed-collapsed-top { + border-bottom-width: 0 !important; +} +.x-nbr .x-panel-header-light-framed-collapsed-right { + border-left-width: 0 !important; +} +.x-nbr .x-panel-header-light-framed-collapsed-bottom { + border-top-width: 0 !important; +} +.x-nbr .x-panel-header-light-framed-collapsed-left { + border-right-width: 0 !important; +} + +.x-panel-header-light-framed-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-light-framed-vertical .x-panel-header-text-container { + background-color: #e4e4e4; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#e4e4e4); +} + +.x-panel-header-light-framed .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-light-framed .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-light-framed .x-panel-header-glyph { + color: #f1f1f1; +} + +.x-panel-header-light-framed-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-light-framed-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-light-framed-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-light-framed-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-light-framed-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-light-framed-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-light-framed-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-light-framed-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-light-framed-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-light-framed-outer-border-l { + border-left-color: #e4e4e4 !important; + border-left-width: 2px !important; +} + +.x-panel-light-framed-outer-border-b { + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 2px !important; +} + +.x-panel-light-framed-outer-border-bl { + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 2px !important; + border-left-color: #e4e4e4 !important; + border-left-width: 2px !important; +} + +.x-panel-light-framed-outer-border-r { + border-right-color: #e4e4e4 !important; + border-right-width: 2px !important; +} + +.x-panel-light-framed-outer-border-rl { + border-right-color: #e4e4e4 !important; + border-right-width: 2px !important; + border-left-color: #e4e4e4 !important; + border-left-width: 2px !important; +} + +.x-panel-light-framed-outer-border-rb { + border-right-color: #e4e4e4 !important; + border-right-width: 2px !important; + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 2px !important; +} + +.x-panel-light-framed-outer-border-rbl { + border-right-color: #e4e4e4 !important; + border-right-width: 2px !important; + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 2px !important; + border-left-color: #e4e4e4 !important; + border-left-width: 2px !important; +} + +.x-panel-light-framed-outer-border-t { + border-top-color: #e4e4e4 !important; + border-top-width: 2px !important; +} + +.x-panel-light-framed-outer-border-tl { + border-top-color: #e4e4e4 !important; + border-top-width: 2px !important; + border-left-color: #e4e4e4 !important; + border-left-width: 2px !important; +} + +.x-panel-light-framed-outer-border-tb { + border-top-color: #e4e4e4 !important; + border-top-width: 2px !important; + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 2px !important; +} + +.x-panel-light-framed-outer-border-tbl { + border-top-color: #e4e4e4 !important; + border-top-width: 2px !important; + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 2px !important; + border-left-color: #e4e4e4 !important; + border-left-width: 2px !important; +} + +.x-panel-light-framed-outer-border-tr { + border-top-color: #e4e4e4 !important; + border-top-width: 2px !important; + border-right-color: #e4e4e4 !important; + border-right-width: 2px !important; +} + +.x-panel-light-framed-outer-border-trl { + border-top-color: #e4e4e4 !important; + border-top-width: 2px !important; + border-right-color: #e4e4e4 !important; + border-right-width: 2px !important; + border-left-color: #e4e4e4 !important; + border-left-width: 2px !important; +} + +.x-panel-light-framed-outer-border-trb { + border-top-color: #e4e4e4 !important; + border-top-width: 2px !important; + border-right-color: #e4e4e4 !important; + border-right-width: 2px !important; + border-bottom-color: #e4e4e4 !important; + border-bottom-width: 2px !important; +} + +.x-panel-light-framed-outer-border-trbl { + border-color: #e4e4e4 !important; + border-width: 2px !important; +} + +.x-grid-header-ct { + border: 1px solid silver; +} + +.x-column-header-trigger { + background-image: url(images/grid/hd-pop.png); + border-left: 1px solid silver; +} + +.x-column-header-last { + border-right-width: 0; +} +.x-column-header-last .x-column-header-over .x-column-header-trigger { + border-right: 1px solid silver; +} + +.x-form-trigger { + height: 22px; +} + +.x-form-trigger-wrap { + border: 1px solid; + border-color: silver #d9d9d9 #d9d9d9; +} +.x-form-trigger-wrap .x-form-text { + border-width: 0; + height: 22px; +} +.x-content-box .x-form-trigger-wrap .x-form-text { + height: 15px; +} +.x-form-trigger-wrap-focus .x-form-trigger-wrap { + border-color: #96caee !important; +} +.x-form-invalid .x-form-trigger-wrap { + border-color: #cf4c35; +} + +.x-form-file-wrap .x-form-trigger-wrap { + border: 0; +} + +.x-form-file-wrap .x-form-trigger-wrap .x-form-text { + border: 1px solid; + border-color: silver #d9d9d9 #d9d9d9; + height: 24px; +} +.x-content-box .x-form-file-wrap .x-form-trigger-wrap .x-form-text { + height: 15px; +} + +.x-html-editor-container { + border: 1px solid; + border-color: silver #d9d9d9 #d9d9d9; +} + +.x-accordion-hd .x-tool-img { + background-image: url(images/tools/tool-sprites-dark.png); +} + +.x-accordion-item .x-accordion-hd-over { + background-color: #ebebeb; +} + +.x-resizable-handle { + background-color: #444444; + background-repeat: no-repeat; +} + +.x-resizable-handle-east-over, +.x-resizable-handle-west-over { + background-position: center; +} + +.x-resizable-handle-south-over, +.x-resizable-handle-north-over { + background-position: center; +} + +.x-resizable-handle-southeast-over { + background-position: -2px -2px; +} + +.x-resizable-handle-northwest-over { + background-position: 2px 2px; +} + +.x-resizable-handle-northeast-over { + background-position: -2px 2px; +} + +.x-resizable-handle-southwest-over { + background-position: 2px -2px; +} + +.x-resizable-pinned .x-resizable-handle-east, +.x-resizable-pinned .x-resizable-handle-west { + background-position: center; +} +.x-resizable-pinned .x-resizable-handle-south, +.x-resizable-pinned .x-resizable-handle-north { + background-position: center; +} +.x-resizable-pinned .x-resizable-handle-southeast { + background-position: -2px -2px; +} +.x-resizable-pinned .x-resizable-handle-northwest { + background-position: 2px 2px; +} +.x-resizable-pinned .x-resizable-handle-northeast { + background-position: -2px 2px; +} +.x-resizable-pinned .x-resizable-handle-southwest { + background-position: 2px -2px; +} + +/* including package rapture-theme */ +/** + * @class Ext.container.Container + */ +/** + * Styles a scrollable container which contains multiple nx-subsection containers + * + * cls: 'nx-inset' + */ +.nx-inset, .nx-coreui-component-details { + padding: 12px 12px 12px 12px; +} + +/** + * Styles a subsection inside an nx-inset + * + * cls: 'nx-subsection' + */ +.nx-subsection { + padding: 12px 12px 12px 12px; + background-color: white; + border-top: 1px solid #dddddd !important; + border-right: 1px solid #dddddd !important; + border-bottom: 1px solid #dddddd !important; + border-left: 1px solid #dddddd !important; +} + +/** + * Styles a section as an important message + * cls: 'nx-form-important-msg' + */ +.nx-form-important-msg { + font-size: 10px; + font-weight: bold; +} + +/** + * @class Ext.button.Button + */ +/** + * ui: 'default' + * + * This is not namespaced, because 'default' is the theme a component + * receives when 'ui' is not set. + */ +.x-btn-default-small { + border-color: #333333; +} + +.x-btn-default-small { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: #606060; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-default-small-mc { + background-image: url(images/btn/btn-default-small-fbg.gif); + background-position: 0 top; + background-color: #606060; +} + +.x-nlg .x-btn-default-small { + background-image: url(images/btn/btn-default-small-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-default-small { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-default-small-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-default-small-tl { + background-position: 0 -6px; +} + +.x-btn-default-small-tr { + background-position: right -9px; +} + +.x-btn-default-small-bl { + background-position: 0 -12px; +} + +.x-btn-default-small-br { + background-position: right -15px; +} + +.x-btn-default-small-ml { + background-position: 0 top; +} + +.x-btn-default-small-mr { + background-position: right top; +} + +.x-btn-default-small-tc { + background-position: 0 0; +} + +.x-btn-default-small-bc { + background-position: 0 -3px; +} + +.x-btn-default-small-tr, +.x-btn-default-small-br, +.x-btn-default-small-mr { + padding-right: 3px; +} + +.x-btn-default-small-tl, +.x-btn-default-small-bl, +.x-btn-default-small-ml { + padding-left: 3px; +} + +.x-btn-default-small-tc { + height: 3px; +} + +.x-btn-default-small-bc { + height: 3px; +} + +.x-btn-default-small-tl, +.x-btn-default-small-bl, +.x-btn-default-small-tr, +.x-btn-default-small-br, +.x-btn-default-small-tc, +.x-btn-default-small-bc, +.x-btn-default-small-ml, +.x-btn-default-small-mr { + zoom: 1; + background-image: url(images/btn/btn-default-small-corners.gif); +} + +.x-btn-default-small-ml, +.x-btn-default-small-mr { + zoom: 1; + background-image: url(images/btn/btn-default-small-sides.gif); +} + +.x-btn-default-small-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-default-small-tl, +.x-strict .x-ie7 .x-btn-default-small-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-default-small:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-small-fbg.gif), bg:url(images/btn/btn-default-small-bg.gif), corners:url(images/btn/btn-default-small-corners.gif), sides:url(images/btn/btn-default-small-sides.gif), stretch:bottom, frame-bg:url(images/btn/btn-default-small-fbg.gif), bg:url(images/btn/btn-default-small-bg.gif), corners:url(images/btn/btn-default-small-corners.gif), sides:url(images/btn/btn-default-small-sides.gif)"; +} + +/**/ +/* */ +.x-btn-default-small .x-btn-inner { + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #333333; + padding: 0 5px; +} +.x-btn-default-small .x-btn-arrow { + background-image: url(images/button/default-small-arrow.png); +} +.x-btn-default-small .x-btn-arrow-right { + padding-right: 21px; +} +.x-btn-default-small .x-btn-arrow-bottom { + padding-bottom: 18px; +} +.x-btn-default-small .x-btn-glyph { + font-size: 16px; + line-height: 16px; + color: #333333; +} +.x-ie8m .x-btn-default-small .x-btn-glyph { + color: #333333; +} + +.x-btn-default-small-disabled { + background-image: none; + background-color: #444444; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} +.x-btn-default-small-disabled .x-btn-inner { + color: #777777; +} + +.x-btn-default-small-icon .x-btn-button, +.x-btn-default-small-noicon .x-btn-button { + height: 16px; +} +.x-btn-default-small-icon .x-btn-inner, +.x-btn-default-small-noicon .x-btn-inner { + line-height: 16px; +} + +.x-btn-default-small-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-small-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-default-small-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-default-small-icon .x-btn-inner { + width: 16px; + padding: 0; +} +.x-btn-default-small-icon .x-btn-icon-el { + width: 16px; + height: 16px; +} + +.x-btn-default-small-icon-text-left .x-btn-button { + height: 16px; +} +.x-btn-default-small-icon-text-left .x-btn-inner { + line-height: 16px; + padding-left: 21px; +} +.x-btn-default-small-icon-text-left .x-btn-icon-el { + width: 16px; + right: auto; +} +.x-ie6 .x-btn-default-small-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-small-icon-text-left .x-btn-icon-el { + height: 16px; +} + +.x-btn-default-small-icon-text-right .x-btn-button { + height: 16px; +} +.x-btn-default-small-icon-text-right .x-btn-inner { + line-height: 16px; + padding-right: 21px; +} +.x-btn-default-small-icon-text-right .x-btn-icon-el { + width: 16px; + left: auto; +} +.x-ie6 .x-btn-default-small-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-small-icon-text-right .x-btn-icon-el { + height: 16px; +} + +.x-btn-default-small-icon-text-top .x-btn-inner { + padding-top: 21px; +} +.x-btn-default-small-icon-text-top .x-btn-icon-el { + height: 16px; + bottom: auto; +} +.x-ie6 .x-btn-default-small-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-small-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-small-icon-text-bottom .x-btn-inner { + padding-bottom: 21px; +} +.x-btn-default-small-icon-text-bottom .x-btn-icon-el { + height: 16px; + top: auto; +} +.x-ie6 .x-btn-default-small-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-default-small-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-default-small-focus { + border-color: #96caee; + background-image: none; + background-color: #3e3e3e; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-default-small-over { + background-image: none; + background-color: #3e3e3e; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #cbcbcb), color-stop(100%, #b8b8b8)); + background-image: -webkit-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -moz-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -o-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -ms-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: linear-gradient(top, #cbcbcb, #b8b8b8); +} + +.x-btn-default-small-menu-active, +.x-btn-default-small-pressed { + background-image: none; + background-color: #333333; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #919191), color-stop(100%, #808080)); + background-image: -webkit-linear-gradient(top, #919191, #808080); + background-image: -moz-linear-gradient(top, #919191, #808080); + background-image: -o-linear-gradient(top, #919191, #808080); + background-image: -ms-linear-gradient(top, #919191, #808080); + background-image: linear-gradient(top, #919191, #808080); +} +.x-btn-default-small-menu-active .x-btn-inner, +.x-btn-default-small-pressed .x-btn-inner { + color: white; +} + +.x-btn-default-small-focus .x-frame-tl, +.x-btn-default-small-focus .x-frame-bl, +.x-btn-default-small-focus .x-frame-tr, +.x-btn-default-small-focus .x-frame-br, +.x-btn-default-small-focus .x-frame-tc, +.x-btn-default-small-focus .x-frame-bc { + background-image: url(images/btn/btn-default-small-focus-corners.gif); +} +.x-btn-default-small-focus .x-frame-ml, +.x-btn-default-small-focus .x-frame-mr { + background-image: url(images/btn/btn-default-small-focus-sides.gif); +} +.x-btn-default-small-focus .x-frame-mc { + background-color: #3e3e3e; + background-image: url(images/btn/btn-default-small-focus-fbg.gif); +} + +.x-btn-default-small-over .x-frame-tl, +.x-btn-default-small-over .x-frame-bl, +.x-btn-default-small-over .x-frame-tr, +.x-btn-default-small-over .x-frame-br, +.x-btn-default-small-over .x-frame-tc, +.x-btn-default-small-over .x-frame-bc { + background-image: url(images/btn/btn-default-small-over-corners.gif); +} +.x-btn-default-small-over .x-frame-ml, +.x-btn-default-small-over .x-frame-mr { + background-image: url(images/btn/btn-default-small-over-sides.gif); +} +.x-btn-default-small-over .x-frame-mc { + background-color: #3e3e3e; + background-image: url(images/btn/btn-default-small-over-fbg.gif); +} + +.x-btn-default-small-menu-active .x-frame-tl, +.x-btn-default-small-menu-active .x-frame-bl, +.x-btn-default-small-menu-active .x-frame-tr, +.x-btn-default-small-menu-active .x-frame-br, +.x-btn-default-small-menu-active .x-frame-tc, +.x-btn-default-small-menu-active .x-frame-bc, +.x-btn-default-small-pressed .x-frame-tl, +.x-btn-default-small-pressed .x-frame-bl, +.x-btn-default-small-pressed .x-frame-tr, +.x-btn-default-small-pressed .x-frame-br, +.x-btn-default-small-pressed .x-frame-tc, +.x-btn-default-small-pressed .x-frame-bc { + background-image: url(images/btn/btn-default-small-pressed-corners.gif); +} +.x-btn-default-small-menu-active .x-frame-ml, +.x-btn-default-small-menu-active .x-frame-mr, +.x-btn-default-small-pressed .x-frame-ml, +.x-btn-default-small-pressed .x-frame-mr { + background-image: url(images/btn/btn-default-small-pressed-sides.gif); +} +.x-btn-default-small-menu-active .x-frame-mc, +.x-btn-default-small-pressed .x-frame-mc { + background-color: #333333; + background-image: url(images/btn/btn-default-small-pressed-fbg.gif); +} + +.x-btn-default-small-disabled .x-frame-tl, +.x-btn-default-small-disabled .x-frame-bl, +.x-btn-default-small-disabled .x-frame-tr, +.x-btn-default-small-disabled .x-frame-br, +.x-btn-default-small-disabled .x-frame-tc, +.x-btn-default-small-disabled .x-frame-bc { + background-image: url(images/btn/btn-default-small-disabled-corners.gif); +} +.x-btn-default-small-disabled .x-frame-ml, +.x-btn-default-small-disabled .x-frame-mr { + background-image: url(images/btn/btn-default-small-disabled-sides.gif); +} +.x-btn-default-small-disabled .x-frame-mc { + background-color: #444444; + background-image: url(images/btn/btn-default-small-disabled-fbg.gif); +} + +.x-nlg .x-btn-default-small-focus { + background-image: url(images/btn/btn-default-small-focus-bg.gif); +} + +.x-nlg .x-btn-default-small-over { + background-image: url(images/btn/btn-default-small-over-bg.gif); +} + +.x-nlg .x-btn-default-small-menu-active, +.x-nlg .x-btn-default-small-pressed { + background-image: url(images/btn/btn-default-small-pressed-bg.gif); +} + +.x-nlg .x-btn-default-small-disabled { + background-image: url(images/btn/btn-default-small-disabled-bg.gif); +} + +.x-nbr .x-btn-default-small { + background-image: none; +} + +.x-btn-default-small .x-btn-split-right { + background-image: url(images/button/default-small-s-arrow.png); + padding-right: 23px; +} +.x-btn-default-small .x-btn-split-bottom { + background-image: url(images/button/default-small-s-arrow-b.png); + padding-bottom: 20px; +} + +.x-btn-default-small-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-default-small-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-focus-corners.gif), sides:url(images/btn/btn-default-small-focus-sides.gif), frame-bg:url(images/btn/btn-default-small-focus-fbg.gif), bg:url(images/btn/btn-default-small-focus-bg.gif), stretch:bottom, corners:url(images/btn/btn-default-small-focus-corners.gif), sides:url(images/btn/btn-default-small-focus-sides.gif), frame-bg:url(images/btn/btn-default-small-focus-fbg.gif), bg:url(images/btn/btn-default-small-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-small-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-over-corners.gif), sides:url(images/btn/btn-default-small-over-sides.gif), frame-bg:url(images/btn/btn-default-small-over-fbg.gif), bg:url(images/btn/btn-default-small-over-bg.gif), stretch:bottom, corners:url(images/btn/btn-default-small-over-corners.gif), sides:url(images/btn/btn-default-small-over-sides.gif), frame-bg:url(images/btn/btn-default-small-over-fbg.gif), bg:url(images/btn/btn-default-small-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-small-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-pressed-corners.gif), sides:url(images/btn/btn-default-small-pressed-sides.gif), frame-bg:url(images/btn/btn-default-small-pressed-fbg.gif), bg:url(images/btn/btn-default-small-pressed-bg.gif), stretch:bottom, corners:url(images/btn/btn-default-small-pressed-corners.gif), sides:url(images/btn/btn-default-small-pressed-sides.gif), frame-bg:url(images/btn/btn-default-small-pressed-fbg.gif), bg:url(images/btn/btn-default-small-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-default-small-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-disabled-corners.gif), sides:url(images/btn/btn-default-small-disabled-sides.gif), frame-bg:url(images/btn/btn-default-small-disabled-fbg.gif), bg:url(images/btn/btn-default-small-disabled-bg.gif), stretch:bottom, corners:url(images/btn/btn-default-small-disabled-corners.gif), sides:url(images/btn/btn-default-small-disabled-sides.gif), frame-bg:url(images/btn/btn-default-small-disabled-fbg.gif), bg:url(images/btn/btn-default-small-disabled-bg.gif)"; +} + +/**/ +/* */ +/** + * ui: 'nx-plain' + */ +.x-btn-nx-plain-small { + border-color: #dddddd; +} + +.x-btn-nx-plain-small { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: white; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-nx-plain-small-mc { + background-image: url(images/btn/btn-nx-plain-small-fbg.gif); + background-position: 0 top; + background-color: white; +} + +.x-nlg .x-btn-nx-plain-small { + background-image: url(images/btn/btn-nx-plain-small-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-nx-plain-small { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-nx-plain-small-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-nx-plain-small-tl { + background-position: 0 -6px; +} + +.x-btn-nx-plain-small-tr { + background-position: right -9px; +} + +.x-btn-nx-plain-small-bl { + background-position: 0 -12px; +} + +.x-btn-nx-plain-small-br { + background-position: right -15px; +} + +.x-btn-nx-plain-small-ml { + background-position: 0 top; +} + +.x-btn-nx-plain-small-mr { + background-position: right top; +} + +.x-btn-nx-plain-small-tc { + background-position: 0 0; +} + +.x-btn-nx-plain-small-bc { + background-position: 0 -3px; +} + +.x-btn-nx-plain-small-tr, +.x-btn-nx-plain-small-br, +.x-btn-nx-plain-small-mr { + padding-right: 3px; +} + +.x-btn-nx-plain-small-tl, +.x-btn-nx-plain-small-bl, +.x-btn-nx-plain-small-ml { + padding-left: 3px; +} + +.x-btn-nx-plain-small-tc { + height: 3px; +} + +.x-btn-nx-plain-small-bc { + height: 3px; +} + +.x-btn-nx-plain-small-tl, +.x-btn-nx-plain-small-bl, +.x-btn-nx-plain-small-tr, +.x-btn-nx-plain-small-br, +.x-btn-nx-plain-small-tc, +.x-btn-nx-plain-small-bc, +.x-btn-nx-plain-small-ml, +.x-btn-nx-plain-small-mr { + zoom: 1; + background-image: url(images/btn/btn-nx-plain-small-corners.gif); +} + +.x-btn-nx-plain-small-ml, +.x-btn-nx-plain-small-mr { + zoom: 1; + background-image: url(images/btn/btn-nx-plain-small-sides.gif); +} + +.x-btn-nx-plain-small-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-nx-plain-small-tl, +.x-strict .x-ie7 .x-btn-nx-plain-small-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-nx-plain-small:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-plain-small-fbg.gif), bg:url(images/btn/btn-nx-plain-small-bg.gif), corners:url(images/btn/btn-nx-plain-small-corners.gif), sides:url(images/btn/btn-nx-plain-small-sides.gif)"; +} + +/**/ +/* */ +.x-btn-nx-plain-small .x-btn-inner { + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #333333; + padding: 0 5px; +} +.x-btn-nx-plain-small .x-btn-arrow { + background-image: url(images/button/nx-plain-small-arrow.png); +} +.x-btn-nx-plain-small .x-btn-arrow-right { + padding-right: 21px; +} +.x-btn-nx-plain-small .x-btn-arrow-bottom { + padding-bottom: 18px; +} +.x-btn-nx-plain-small .x-btn-glyph { + font-size: 16px; + line-height: 16px; + color: #333333; +} +.x-ie8m .x-btn-nx-plain-small .x-btn-glyph { + color: #333333; +} + +.x-btn-nx-plain-small-disabled { + background-image: none; + background-color: white; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} +.x-btn-nx-plain-small-disabled .x-btn-inner { + color: #777777; +} + +.x-btn-nx-plain-small-icon .x-btn-button, +.x-btn-nx-plain-small-noicon .x-btn-button { + height: 16px; +} +.x-btn-nx-plain-small-icon .x-btn-inner, +.x-btn-nx-plain-small-noicon .x-btn-inner { + line-height: 16px; +} + +.x-btn-nx-plain-small-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-plain-small-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-plain-small-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-nx-plain-small-icon .x-btn-inner { + width: 16px; + padding: 0; +} +.x-btn-nx-plain-small-icon .x-btn-icon-el { + width: 16px; + height: 16px; +} + +.x-btn-nx-plain-small-icon-text-left .x-btn-button { + height: 16px; +} +.x-btn-nx-plain-small-icon-text-left .x-btn-inner { + line-height: 16px; + padding-left: 21px; +} +.x-btn-nx-plain-small-icon-text-left .x-btn-icon-el { + width: 16px; + right: auto; +} +.x-ie6 .x-btn-nx-plain-small-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-plain-small-icon-text-left .x-btn-icon-el { + height: 16px; +} + +.x-btn-nx-plain-small-icon-text-right .x-btn-button { + height: 16px; +} +.x-btn-nx-plain-small-icon-text-right .x-btn-inner { + line-height: 16px; + padding-right: 21px; +} +.x-btn-nx-plain-small-icon-text-right .x-btn-icon-el { + width: 16px; + left: auto; +} +.x-ie6 .x-btn-nx-plain-small-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-plain-small-icon-text-right .x-btn-icon-el { + height: 16px; +} + +.x-btn-nx-plain-small-icon-text-top .x-btn-inner { + padding-top: 21px; +} +.x-btn-nx-plain-small-icon-text-top .x-btn-icon-el { + height: 16px; + bottom: auto; +} +.x-ie6 .x-btn-nx-plain-small-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-plain-small-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-plain-small-icon-text-bottom .x-btn-inner { + padding-bottom: 21px; +} +.x-btn-nx-plain-small-icon-text-bottom .x-btn-icon-el { + height: 16px; + top: auto; +} +.x-ie6 .x-btn-nx-plain-small-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-plain-small-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-plain-small-focus { + border-color: #cbcbcb; + background-image: none; + background-color: white; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); +} + +.x-btn-nx-plain-small-over { + background-image: none; + background-color: #cbcbcb; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #cbcbcb), color-stop(100%, #b8b8b8)); + background-image: -webkit-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -moz-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -o-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: -ms-linear-gradient(top, #cbcbcb, #b8b8b8); + background-image: linear-gradient(top, #cbcbcb, #b8b8b8); +} + +.x-btn-nx-plain-small-menu-active, +.x-btn-nx-plain-small-pressed { + background-image: none; + background-color: #919191; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #919191), color-stop(100%, #808080)); + background-image: -webkit-linear-gradient(top, #919191, #808080); + background-image: -moz-linear-gradient(top, #919191, #808080); + background-image: -o-linear-gradient(top, #919191, #808080); + background-image: -ms-linear-gradient(top, #919191, #808080); + background-image: linear-gradient(top, #919191, #808080); +} +.x-btn-nx-plain-small-menu-active .x-btn-inner, +.x-btn-nx-plain-small-pressed .x-btn-inner { + color: white; +} + +.x-btn-nx-plain-small-focus .x-frame-tl, +.x-btn-nx-plain-small-focus .x-frame-bl, +.x-btn-nx-plain-small-focus .x-frame-tr, +.x-btn-nx-plain-small-focus .x-frame-br, +.x-btn-nx-plain-small-focus .x-frame-tc, +.x-btn-nx-plain-small-focus .x-frame-bc { + background-image: url(images/btn/btn-nx-plain-small-focus-corners.gif); +} +.x-btn-nx-plain-small-focus .x-frame-ml, +.x-btn-nx-plain-small-focus .x-frame-mr { + background-image: url(images/btn/btn-nx-plain-small-focus-sides.gif); +} +.x-btn-nx-plain-small-focus .x-frame-mc { + background-color: white; + background-image: url(images/btn/btn-nx-plain-small-focus-fbg.gif); +} + +.x-btn-nx-plain-small-over .x-frame-tl, +.x-btn-nx-plain-small-over .x-frame-bl, +.x-btn-nx-plain-small-over .x-frame-tr, +.x-btn-nx-plain-small-over .x-frame-br, +.x-btn-nx-plain-small-over .x-frame-tc, +.x-btn-nx-plain-small-over .x-frame-bc { + background-image: url(images/btn/btn-nx-plain-small-over-corners.gif); +} +.x-btn-nx-plain-small-over .x-frame-ml, +.x-btn-nx-plain-small-over .x-frame-mr { + background-image: url(images/btn/btn-nx-plain-small-over-sides.gif); +} +.x-btn-nx-plain-small-over .x-frame-mc { + background-color: #cbcbcb; + background-image: url(images/btn/btn-nx-plain-small-over-fbg.gif); +} + +.x-btn-nx-plain-small-menu-active .x-frame-tl, +.x-btn-nx-plain-small-menu-active .x-frame-bl, +.x-btn-nx-plain-small-menu-active .x-frame-tr, +.x-btn-nx-plain-small-menu-active .x-frame-br, +.x-btn-nx-plain-small-menu-active .x-frame-tc, +.x-btn-nx-plain-small-menu-active .x-frame-bc, +.x-btn-nx-plain-small-pressed .x-frame-tl, +.x-btn-nx-plain-small-pressed .x-frame-bl, +.x-btn-nx-plain-small-pressed .x-frame-tr, +.x-btn-nx-plain-small-pressed .x-frame-br, +.x-btn-nx-plain-small-pressed .x-frame-tc, +.x-btn-nx-plain-small-pressed .x-frame-bc { + background-image: url(images/btn/btn-nx-plain-small-pressed-corners.gif); +} +.x-btn-nx-plain-small-menu-active .x-frame-ml, +.x-btn-nx-plain-small-menu-active .x-frame-mr, +.x-btn-nx-plain-small-pressed .x-frame-ml, +.x-btn-nx-plain-small-pressed .x-frame-mr { + background-image: url(images/btn/btn-nx-plain-small-pressed-sides.gif); +} +.x-btn-nx-plain-small-menu-active .x-frame-mc, +.x-btn-nx-plain-small-pressed .x-frame-mc { + background-color: #919191; + background-image: url(images/btn/btn-nx-plain-small-pressed-fbg.gif); +} + +.x-btn-nx-plain-small-disabled .x-frame-tl, +.x-btn-nx-plain-small-disabled .x-frame-bl, +.x-btn-nx-plain-small-disabled .x-frame-tr, +.x-btn-nx-plain-small-disabled .x-frame-br, +.x-btn-nx-plain-small-disabled .x-frame-tc, +.x-btn-nx-plain-small-disabled .x-frame-bc { + background-image: url(images/btn/btn-nx-plain-small-disabled-corners.gif); +} +.x-btn-nx-plain-small-disabled .x-frame-ml, +.x-btn-nx-plain-small-disabled .x-frame-mr { + background-image: url(images/btn/btn-nx-plain-small-disabled-sides.gif); +} +.x-btn-nx-plain-small-disabled .x-frame-mc { + background-color: white; + background-image: url(images/btn/btn-nx-plain-small-disabled-fbg.gif); +} + +.x-nlg .x-btn-nx-plain-small-focus { + background-image: url(images/btn/btn-nx-plain-small-focus-bg.gif); +} + +.x-nlg .x-btn-nx-plain-small-over { + background-image: url(images/btn/btn-nx-plain-small-over-bg.gif); +} + +.x-nlg .x-btn-nx-plain-small-menu-active, +.x-nlg .x-btn-nx-plain-small-pressed { + background-image: url(images/btn/btn-nx-plain-small-pressed-bg.gif); +} + +.x-nlg .x-btn-nx-plain-small-disabled { + background-image: url(images/btn/btn-nx-plain-small-disabled-bg.gif); +} + +.x-nbr .x-btn-nx-plain-small { + background-image: none; +} + +.x-btn-nx-plain-small .x-btn-split-right { + background-image: url(images/button/nx-plain-small-s-arrow.png); + padding-right: 23px; +} +.x-btn-nx-plain-small .x-btn-split-bottom { + background-image: url(images/button/nx-plain-small-s-arrow-b.png); + padding-bottom: 20px; +} + +.x-btn-nx-plain-small-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-nx-plain-small-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-plain-small-focus-corners.gif), sides:url(images/btn/btn-nx-plain-small-focus-sides.gif), frame-bg:url(images/btn/btn-nx-plain-small-focus-fbg.gif), bg:url(images/btn/btn-nx-plain-small-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-plain-small-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-plain-small-over-corners.gif), sides:url(images/btn/btn-nx-plain-small-over-sides.gif), frame-bg:url(images/btn/btn-nx-plain-small-over-fbg.gif), bg:url(images/btn/btn-nx-plain-small-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-plain-small-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-plain-small-pressed-corners.gif), sides:url(images/btn/btn-nx-plain-small-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-plain-small-pressed-fbg.gif), bg:url(images/btn/btn-nx-plain-small-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-plain-small-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-plain-small-disabled-corners.gif), sides:url(images/btn/btn-nx-plain-small-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-plain-small-disabled-fbg.gif), bg:url(images/btn/btn-nx-plain-small-disabled-bg.gif)"; +} + +/**/ +/* */ +/** + * ui: 'nx-primary' + */ +.x-btn-nx-primary-small { + border-color: #333333; +} + +.x-btn-nx-primary-small { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: #197ac5; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #197ac5), color-stop(100%, #0161ad)); + background-image: -webkit-linear-gradient(top, #197ac5, #0161ad); + background-image: -moz-linear-gradient(top, #197ac5, #0161ad); + background-image: -o-linear-gradient(top, #197ac5, #0161ad); + background-image: -ms-linear-gradient(top, #197ac5, #0161ad); + background-image: linear-gradient(top, #197ac5, #0161ad); +} + +.x-btn-nx-primary-small-mc { + background-image: url(images/btn/btn-nx-primary-small-fbg.gif); + background-position: 0 top; + background-color: #197ac5; +} + +.x-nlg .x-btn-nx-primary-small { + background-image: url(images/btn/btn-nx-primary-small-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-nx-primary-small { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-nx-primary-small-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-nx-primary-small-tl { + background-position: 0 -6px; +} + +.x-btn-nx-primary-small-tr { + background-position: right -9px; +} + +.x-btn-nx-primary-small-bl { + background-position: 0 -12px; +} + +.x-btn-nx-primary-small-br { + background-position: right -15px; +} + +.x-btn-nx-primary-small-ml { + background-position: 0 top; +} + +.x-btn-nx-primary-small-mr { + background-position: right top; +} + +.x-btn-nx-primary-small-tc { + background-position: 0 0; +} + +.x-btn-nx-primary-small-bc { + background-position: 0 -3px; +} + +.x-btn-nx-primary-small-tr, +.x-btn-nx-primary-small-br, +.x-btn-nx-primary-small-mr { + padding-right: 3px; +} + +.x-btn-nx-primary-small-tl, +.x-btn-nx-primary-small-bl, +.x-btn-nx-primary-small-ml { + padding-left: 3px; +} + +.x-btn-nx-primary-small-tc { + height: 3px; +} + +.x-btn-nx-primary-small-bc { + height: 3px; +} + +.x-btn-nx-primary-small-tl, +.x-btn-nx-primary-small-bl, +.x-btn-nx-primary-small-tr, +.x-btn-nx-primary-small-br, +.x-btn-nx-primary-small-tc, +.x-btn-nx-primary-small-bc, +.x-btn-nx-primary-small-ml, +.x-btn-nx-primary-small-mr { + zoom: 1; + background-image: url(images/btn/btn-nx-primary-small-corners.gif); +} + +.x-btn-nx-primary-small-ml, +.x-btn-nx-primary-small-mr { + zoom: 1; + background-image: url(images/btn/btn-nx-primary-small-sides.gif); +} + +.x-btn-nx-primary-small-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-nx-primary-small-tl, +.x-strict .x-ie7 .x-btn-nx-primary-small-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-nx-primary-small:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-primary-small-fbg.gif), bg:url(images/btn/btn-nx-primary-small-bg.gif), corners:url(images/btn/btn-nx-primary-small-corners.gif), sides:url(images/btn/btn-nx-primary-small-sides.gif)"; +} + +/**/ +/* */ +.x-btn-nx-primary-small .x-btn-inner { + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: white; + padding: 0 5px; +} +.x-btn-nx-primary-small .x-btn-arrow { + background-image: url(images/button/nx-primary-small-arrow.png); +} +.x-btn-nx-primary-small .x-btn-arrow-right { + padding-right: 21px; +} +.x-btn-nx-primary-small .x-btn-arrow-bottom { + padding-bottom: 18px; +} +.x-btn-nx-primary-small .x-btn-glyph { + font-size: 16px; + line-height: 16px; + color: white; +} +.x-ie8m .x-btn-nx-primary-small .x-btn-glyph { + color: white; +} + +.x-btn-nx-primary-small-disabled { + background-image: none; + background-color: #197ac5; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #197ac5), color-stop(100%, #0161ad)); + background-image: -webkit-linear-gradient(top, #197ac5, #0161ad); + background-image: -moz-linear-gradient(top, #197ac5, #0161ad); + background-image: -o-linear-gradient(top, #197ac5, #0161ad); + background-image: -ms-linear-gradient(top, #197ac5, #0161ad); + background-image: linear-gradient(top, #197ac5, #0161ad); +} + +.x-btn-nx-primary-small-icon .x-btn-button, +.x-btn-nx-primary-small-noicon .x-btn-button { + height: 16px; +} +.x-btn-nx-primary-small-icon .x-btn-inner, +.x-btn-nx-primary-small-noicon .x-btn-inner { + line-height: 16px; +} + +.x-btn-nx-primary-small-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-primary-small-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-primary-small-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-nx-primary-small-icon .x-btn-inner { + width: 16px; + padding: 0; +} +.x-btn-nx-primary-small-icon .x-btn-icon-el { + width: 16px; + height: 16px; +} + +.x-btn-nx-primary-small-icon-text-left .x-btn-button { + height: 16px; +} +.x-btn-nx-primary-small-icon-text-left .x-btn-inner { + line-height: 16px; + padding-left: 21px; +} +.x-btn-nx-primary-small-icon-text-left .x-btn-icon-el { + width: 16px; + right: auto; +} +.x-ie6 .x-btn-nx-primary-small-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-primary-small-icon-text-left .x-btn-icon-el { + height: 16px; +} + +.x-btn-nx-primary-small-icon-text-right .x-btn-button { + height: 16px; +} +.x-btn-nx-primary-small-icon-text-right .x-btn-inner { + line-height: 16px; + padding-right: 21px; +} +.x-btn-nx-primary-small-icon-text-right .x-btn-icon-el { + width: 16px; + left: auto; +} +.x-ie6 .x-btn-nx-primary-small-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-primary-small-icon-text-right .x-btn-icon-el { + height: 16px; +} + +.x-btn-nx-primary-small-icon-text-top .x-btn-inner { + padding-top: 21px; +} +.x-btn-nx-primary-small-icon-text-top .x-btn-icon-el { + height: 16px; + bottom: auto; +} +.x-ie6 .x-btn-nx-primary-small-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-primary-small-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-primary-small-icon-text-bottom .x-btn-inner { + padding-bottom: 21px; +} +.x-btn-nx-primary-small-icon-text-bottom .x-btn-icon-el { + height: 16px; + top: auto; +} +.x-ie6 .x-btn-nx-primary-small-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-primary-small-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-primary-small-focus { + border-color: #96caee; + background-image: none; + background-color: #197ac5; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #197ac5), color-stop(100%, #0161ad)); + background-image: -webkit-linear-gradient(top, #197ac5, #0161ad); + background-image: -moz-linear-gradient(top, #197ac5, #0161ad); + background-image: -o-linear-gradient(top, #197ac5, #0161ad); + background-image: -ms-linear-gradient(top, #197ac5, #0161ad); + background-image: linear-gradient(top, #197ac5, #0161ad); +} + +.x-btn-nx-primary-small-over { + background-image: none; + background-color: #14629e; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #14629e), color-stop(100%, #014e8a)); + background-image: -webkit-linear-gradient(top, #14629e, #014e8a); + background-image: -moz-linear-gradient(top, #14629e, #014e8a); + background-image: -o-linear-gradient(top, #14629e, #014e8a); + background-image: -ms-linear-gradient(top, #14629e, #014e8a); + background-image: linear-gradient(top, #14629e, #014e8a); +} + +.x-btn-nx-primary-small-menu-active, +.x-btn-nx-primary-small-pressed { + background-image: none; + background-color: #0f4976; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #0f4976), color-stop(100%, #013a68)); + background-image: -webkit-linear-gradient(top, #0f4976, #013a68); + background-image: -moz-linear-gradient(top, #0f4976, #013a68); + background-image: -o-linear-gradient(top, #0f4976, #013a68); + background-image: -ms-linear-gradient(top, #0f4976, #013a68); + background-image: linear-gradient(top, #0f4976, #013a68); +} + +.x-btn-nx-primary-small-focus .x-frame-tl, +.x-btn-nx-primary-small-focus .x-frame-bl, +.x-btn-nx-primary-small-focus .x-frame-tr, +.x-btn-nx-primary-small-focus .x-frame-br, +.x-btn-nx-primary-small-focus .x-frame-tc, +.x-btn-nx-primary-small-focus .x-frame-bc { + background-image: url(images/btn/btn-nx-primary-small-focus-corners.gif); +} +.x-btn-nx-primary-small-focus .x-frame-ml, +.x-btn-nx-primary-small-focus .x-frame-mr { + background-image: url(images/btn/btn-nx-primary-small-focus-sides.gif); +} +.x-btn-nx-primary-small-focus .x-frame-mc { + background-color: #197ac5; + background-image: url(images/btn/btn-nx-primary-small-focus-fbg.gif); +} + +.x-btn-nx-primary-small-over .x-frame-tl, +.x-btn-nx-primary-small-over .x-frame-bl, +.x-btn-nx-primary-small-over .x-frame-tr, +.x-btn-nx-primary-small-over .x-frame-br, +.x-btn-nx-primary-small-over .x-frame-tc, +.x-btn-nx-primary-small-over .x-frame-bc { + background-image: url(images/btn/btn-nx-primary-small-over-corners.gif); +} +.x-btn-nx-primary-small-over .x-frame-ml, +.x-btn-nx-primary-small-over .x-frame-mr { + background-image: url(images/btn/btn-nx-primary-small-over-sides.gif); +} +.x-btn-nx-primary-small-over .x-frame-mc { + background-color: #14629e; + background-image: url(images/btn/btn-nx-primary-small-over-fbg.gif); +} + +.x-btn-nx-primary-small-menu-active .x-frame-tl, +.x-btn-nx-primary-small-menu-active .x-frame-bl, +.x-btn-nx-primary-small-menu-active .x-frame-tr, +.x-btn-nx-primary-small-menu-active .x-frame-br, +.x-btn-nx-primary-small-menu-active .x-frame-tc, +.x-btn-nx-primary-small-menu-active .x-frame-bc, +.x-btn-nx-primary-small-pressed .x-frame-tl, +.x-btn-nx-primary-small-pressed .x-frame-bl, +.x-btn-nx-primary-small-pressed .x-frame-tr, +.x-btn-nx-primary-small-pressed .x-frame-br, +.x-btn-nx-primary-small-pressed .x-frame-tc, +.x-btn-nx-primary-small-pressed .x-frame-bc { + background-image: url(images/btn/btn-nx-primary-small-pressed-corners.gif); +} +.x-btn-nx-primary-small-menu-active .x-frame-ml, +.x-btn-nx-primary-small-menu-active .x-frame-mr, +.x-btn-nx-primary-small-pressed .x-frame-ml, +.x-btn-nx-primary-small-pressed .x-frame-mr { + background-image: url(images/btn/btn-nx-primary-small-pressed-sides.gif); +} +.x-btn-nx-primary-small-menu-active .x-frame-mc, +.x-btn-nx-primary-small-pressed .x-frame-mc { + background-color: #0f4976; + background-image: url(images/btn/btn-nx-primary-small-pressed-fbg.gif); +} + +.x-btn-nx-primary-small-disabled .x-frame-tl, +.x-btn-nx-primary-small-disabled .x-frame-bl, +.x-btn-nx-primary-small-disabled .x-frame-tr, +.x-btn-nx-primary-small-disabled .x-frame-br, +.x-btn-nx-primary-small-disabled .x-frame-tc, +.x-btn-nx-primary-small-disabled .x-frame-bc { + background-image: url(images/btn/btn-nx-primary-small-disabled-corners.gif); +} +.x-btn-nx-primary-small-disabled .x-frame-ml, +.x-btn-nx-primary-small-disabled .x-frame-mr { + background-image: url(images/btn/btn-nx-primary-small-disabled-sides.gif); +} +.x-btn-nx-primary-small-disabled .x-frame-mc { + background-color: #197ac5; + background-image: url(images/btn/btn-nx-primary-small-disabled-fbg.gif); +} + +.x-nlg .x-btn-nx-primary-small-focus { + background-image: url(images/btn/btn-nx-primary-small-focus-bg.gif); +} + +.x-nlg .x-btn-nx-primary-small-over { + background-image: url(images/btn/btn-nx-primary-small-over-bg.gif); +} + +.x-nlg .x-btn-nx-primary-small-menu-active, +.x-nlg .x-btn-nx-primary-small-pressed { + background-image: url(images/btn/btn-nx-primary-small-pressed-bg.gif); +} + +.x-nlg .x-btn-nx-primary-small-disabled { + background-image: url(images/btn/btn-nx-primary-small-disabled-bg.gif); +} + +.x-nbr .x-btn-nx-primary-small { + background-image: none; +} + +.x-btn-nx-primary-small .x-btn-split-right { + background-image: url(images/button/nx-primary-small-s-arrow.png); + padding-right: 23px; +} +.x-btn-nx-primary-small .x-btn-split-bottom { + background-image: url(images/button/nx-primary-small-s-arrow-b.png); + padding-bottom: 20px; +} + +.x-btn-nx-primary-small-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-nx-primary-small-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-primary-small-focus-corners.gif), sides:url(images/btn/btn-nx-primary-small-focus-sides.gif), frame-bg:url(images/btn/btn-nx-primary-small-focus-fbg.gif), bg:url(images/btn/btn-nx-primary-small-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-primary-small-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-primary-small-over-corners.gif), sides:url(images/btn/btn-nx-primary-small-over-sides.gif), frame-bg:url(images/btn/btn-nx-primary-small-over-fbg.gif), bg:url(images/btn/btn-nx-primary-small-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-primary-small-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-primary-small-pressed-corners.gif), sides:url(images/btn/btn-nx-primary-small-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-primary-small-pressed-fbg.gif), bg:url(images/btn/btn-nx-primary-small-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-primary-small-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-primary-small-disabled-corners.gif), sides:url(images/btn/btn-nx-primary-small-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-primary-small-disabled-fbg.gif), bg:url(images/btn/btn-nx-primary-small-disabled-bg.gif)"; +} + +/**/ +/* */ +/** + * ui: 'nx-danger' + */ +.x-btn-nx-danger-small { + border-color: #333333; +} + +.x-btn-nx-danger-small { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: #de3d63; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #de3d63), color-stop(100%, #c6254b)); + background-image: -webkit-linear-gradient(top, #de3d63, #c6254b); + background-image: -moz-linear-gradient(top, #de3d63, #c6254b); + background-image: -o-linear-gradient(top, #de3d63, #c6254b); + background-image: -ms-linear-gradient(top, #de3d63, #c6254b); + background-image: linear-gradient(top, #de3d63, #c6254b); +} + +.x-btn-nx-danger-small-mc { + background-image: url(images/btn/btn-nx-danger-small-fbg.gif); + background-position: 0 top; + background-color: #de3d63; +} + +.x-nlg .x-btn-nx-danger-small { + background-image: url(images/btn/btn-nx-danger-small-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-nx-danger-small { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-nx-danger-small-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-nx-danger-small-tl { + background-position: 0 -6px; +} + +.x-btn-nx-danger-small-tr { + background-position: right -9px; +} + +.x-btn-nx-danger-small-bl { + background-position: 0 -12px; +} + +.x-btn-nx-danger-small-br { + background-position: right -15px; +} + +.x-btn-nx-danger-small-ml { + background-position: 0 top; +} + +.x-btn-nx-danger-small-mr { + background-position: right top; +} + +.x-btn-nx-danger-small-tc { + background-position: 0 0; +} + +.x-btn-nx-danger-small-bc { + background-position: 0 -3px; +} + +.x-btn-nx-danger-small-tr, +.x-btn-nx-danger-small-br, +.x-btn-nx-danger-small-mr { + padding-right: 3px; +} + +.x-btn-nx-danger-small-tl, +.x-btn-nx-danger-small-bl, +.x-btn-nx-danger-small-ml { + padding-left: 3px; +} + +.x-btn-nx-danger-small-tc { + height: 3px; +} + +.x-btn-nx-danger-small-bc { + height: 3px; +} + +.x-btn-nx-danger-small-tl, +.x-btn-nx-danger-small-bl, +.x-btn-nx-danger-small-tr, +.x-btn-nx-danger-small-br, +.x-btn-nx-danger-small-tc, +.x-btn-nx-danger-small-bc, +.x-btn-nx-danger-small-ml, +.x-btn-nx-danger-small-mr { + zoom: 1; + background-image: url(images/btn/btn-nx-danger-small-corners.gif); +} + +.x-btn-nx-danger-small-ml, +.x-btn-nx-danger-small-mr { + zoom: 1; + background-image: url(images/btn/btn-nx-danger-small-sides.gif); +} + +.x-btn-nx-danger-small-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-nx-danger-small-tl, +.x-strict .x-ie7 .x-btn-nx-danger-small-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-nx-danger-small:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-danger-small-fbg.gif), bg:url(images/btn/btn-nx-danger-small-bg.gif), corners:url(images/btn/btn-nx-danger-small-corners.gif), sides:url(images/btn/btn-nx-danger-small-sides.gif)"; +} + +/**/ +/* */ +.x-btn-nx-danger-small .x-btn-inner { + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: white; + padding: 0 5px; +} +.x-btn-nx-danger-small .x-btn-arrow { + background-image: url(images/button/nx-danger-small-arrow.png); +} +.x-btn-nx-danger-small .x-btn-arrow-right { + padding-right: 21px; +} +.x-btn-nx-danger-small .x-btn-arrow-bottom { + padding-bottom: 18px; +} +.x-btn-nx-danger-small .x-btn-glyph { + font-size: 16px; + line-height: 16px; + color: white; +} +.x-ie8m .x-btn-nx-danger-small .x-btn-glyph { + color: white; +} + +.x-btn-nx-danger-small-disabled { + background-image: none; + background-color: #de3d63; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #de3d63), color-stop(100%, #c6254b)); + background-image: -webkit-linear-gradient(top, #de3d63, #c6254b); + background-image: -moz-linear-gradient(top, #de3d63, #c6254b); + background-image: -o-linear-gradient(top, #de3d63, #c6254b); + background-image: -ms-linear-gradient(top, #de3d63, #c6254b); + background-image: linear-gradient(top, #de3d63, #c6254b); +} + +.x-btn-nx-danger-small-icon .x-btn-button, +.x-btn-nx-danger-small-noicon .x-btn-button { + height: 16px; +} +.x-btn-nx-danger-small-icon .x-btn-inner, +.x-btn-nx-danger-small-noicon .x-btn-inner { + line-height: 16px; +} + +.x-btn-nx-danger-small-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-danger-small-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-danger-small-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-nx-danger-small-icon .x-btn-inner { + width: 16px; + padding: 0; +} +.x-btn-nx-danger-small-icon .x-btn-icon-el { + width: 16px; + height: 16px; +} + +.x-btn-nx-danger-small-icon-text-left .x-btn-button { + height: 16px; +} +.x-btn-nx-danger-small-icon-text-left .x-btn-inner { + line-height: 16px; + padding-left: 21px; +} +.x-btn-nx-danger-small-icon-text-left .x-btn-icon-el { + width: 16px; + right: auto; +} +.x-ie6 .x-btn-nx-danger-small-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-danger-small-icon-text-left .x-btn-icon-el { + height: 16px; +} + +.x-btn-nx-danger-small-icon-text-right .x-btn-button { + height: 16px; +} +.x-btn-nx-danger-small-icon-text-right .x-btn-inner { + line-height: 16px; + padding-right: 21px; +} +.x-btn-nx-danger-small-icon-text-right .x-btn-icon-el { + width: 16px; + left: auto; +} +.x-ie6 .x-btn-nx-danger-small-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-danger-small-icon-text-right .x-btn-icon-el { + height: 16px; +} + +.x-btn-nx-danger-small-icon-text-top .x-btn-inner { + padding-top: 21px; +} +.x-btn-nx-danger-small-icon-text-top .x-btn-icon-el { + height: 16px; + bottom: auto; +} +.x-ie6 .x-btn-nx-danger-small-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-danger-small-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-danger-small-icon-text-bottom .x-btn-inner { + padding-bottom: 21px; +} +.x-btn-nx-danger-small-icon-text-bottom .x-btn-icon-el { + height: 16px; + top: auto; +} +.x-ie6 .x-btn-nx-danger-small-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-danger-small-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-danger-small-focus { + border-color: #96caee; + background-image: none; + background-color: #de3d63; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #de3d63), color-stop(100%, #c6254b)); + background-image: -webkit-linear-gradient(top, #de3d63, #c6254b); + background-image: -moz-linear-gradient(top, #de3d63, #c6254b); + background-image: -o-linear-gradient(top, #de3d63, #c6254b); + background-image: -ms-linear-gradient(top, #de3d63, #c6254b); + background-image: linear-gradient(top, #de3d63, #c6254b); +} + +.x-btn-nx-danger-small-over { + background-image: none; + background-color: #b2314f; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b2314f), color-stop(100%, #9e1e3c)); + background-image: -webkit-linear-gradient(top, #b2314f, #9e1e3c); + background-image: -moz-linear-gradient(top, #b2314f, #9e1e3c); + background-image: -o-linear-gradient(top, #b2314f, #9e1e3c); + background-image: -ms-linear-gradient(top, #b2314f, #9e1e3c); + background-image: linear-gradient(top, #b2314f, #9e1e3c); +} + +.x-btn-nx-danger-small-menu-active, +.x-btn-nx-danger-small-pressed { + background-image: none; + background-color: #85253b; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #85253b), color-stop(100%, #77162d)); + background-image: -webkit-linear-gradient(top, #85253b, #77162d); + background-image: -moz-linear-gradient(top, #85253b, #77162d); + background-image: -o-linear-gradient(top, #85253b, #77162d); + background-image: -ms-linear-gradient(top, #85253b, #77162d); + background-image: linear-gradient(top, #85253b, #77162d); +} + +.x-btn-nx-danger-small-focus .x-frame-tl, +.x-btn-nx-danger-small-focus .x-frame-bl, +.x-btn-nx-danger-small-focus .x-frame-tr, +.x-btn-nx-danger-small-focus .x-frame-br, +.x-btn-nx-danger-small-focus .x-frame-tc, +.x-btn-nx-danger-small-focus .x-frame-bc { + background-image: url(images/btn/btn-nx-danger-small-focus-corners.gif); +} +.x-btn-nx-danger-small-focus .x-frame-ml, +.x-btn-nx-danger-small-focus .x-frame-mr { + background-image: url(images/btn/btn-nx-danger-small-focus-sides.gif); +} +.x-btn-nx-danger-small-focus .x-frame-mc { + background-color: #de3d63; + background-image: url(images/btn/btn-nx-danger-small-focus-fbg.gif); +} + +.x-btn-nx-danger-small-over .x-frame-tl, +.x-btn-nx-danger-small-over .x-frame-bl, +.x-btn-nx-danger-small-over .x-frame-tr, +.x-btn-nx-danger-small-over .x-frame-br, +.x-btn-nx-danger-small-over .x-frame-tc, +.x-btn-nx-danger-small-over .x-frame-bc { + background-image: url(images/btn/btn-nx-danger-small-over-corners.gif); +} +.x-btn-nx-danger-small-over .x-frame-ml, +.x-btn-nx-danger-small-over .x-frame-mr { + background-image: url(images/btn/btn-nx-danger-small-over-sides.gif); +} +.x-btn-nx-danger-small-over .x-frame-mc { + background-color: #b2314f; + background-image: url(images/btn/btn-nx-danger-small-over-fbg.gif); +} + +.x-btn-nx-danger-small-menu-active .x-frame-tl, +.x-btn-nx-danger-small-menu-active .x-frame-bl, +.x-btn-nx-danger-small-menu-active .x-frame-tr, +.x-btn-nx-danger-small-menu-active .x-frame-br, +.x-btn-nx-danger-small-menu-active .x-frame-tc, +.x-btn-nx-danger-small-menu-active .x-frame-bc, +.x-btn-nx-danger-small-pressed .x-frame-tl, +.x-btn-nx-danger-small-pressed .x-frame-bl, +.x-btn-nx-danger-small-pressed .x-frame-tr, +.x-btn-nx-danger-small-pressed .x-frame-br, +.x-btn-nx-danger-small-pressed .x-frame-tc, +.x-btn-nx-danger-small-pressed .x-frame-bc { + background-image: url(images/btn/btn-nx-danger-small-pressed-corners.gif); +} +.x-btn-nx-danger-small-menu-active .x-frame-ml, +.x-btn-nx-danger-small-menu-active .x-frame-mr, +.x-btn-nx-danger-small-pressed .x-frame-ml, +.x-btn-nx-danger-small-pressed .x-frame-mr { + background-image: url(images/btn/btn-nx-danger-small-pressed-sides.gif); +} +.x-btn-nx-danger-small-menu-active .x-frame-mc, +.x-btn-nx-danger-small-pressed .x-frame-mc { + background-color: #85253b; + background-image: url(images/btn/btn-nx-danger-small-pressed-fbg.gif); +} + +.x-btn-nx-danger-small-disabled .x-frame-tl, +.x-btn-nx-danger-small-disabled .x-frame-bl, +.x-btn-nx-danger-small-disabled .x-frame-tr, +.x-btn-nx-danger-small-disabled .x-frame-br, +.x-btn-nx-danger-small-disabled .x-frame-tc, +.x-btn-nx-danger-small-disabled .x-frame-bc { + background-image: url(images/btn/btn-nx-danger-small-disabled-corners.gif); +} +.x-btn-nx-danger-small-disabled .x-frame-ml, +.x-btn-nx-danger-small-disabled .x-frame-mr { + background-image: url(images/btn/btn-nx-danger-small-disabled-sides.gif); +} +.x-btn-nx-danger-small-disabled .x-frame-mc { + background-color: #de3d63; + background-image: url(images/btn/btn-nx-danger-small-disabled-fbg.gif); +} + +.x-nlg .x-btn-nx-danger-small-focus { + background-image: url(images/btn/btn-nx-danger-small-focus-bg.gif); +} + +.x-nlg .x-btn-nx-danger-small-over { + background-image: url(images/btn/btn-nx-danger-small-over-bg.gif); +} + +.x-nlg .x-btn-nx-danger-small-menu-active, +.x-nlg .x-btn-nx-danger-small-pressed { + background-image: url(images/btn/btn-nx-danger-small-pressed-bg.gif); +} + +.x-nlg .x-btn-nx-danger-small-disabled { + background-image: url(images/btn/btn-nx-danger-small-disabled-bg.gif); +} + +.x-nbr .x-btn-nx-danger-small { + background-image: none; +} + +.x-btn-nx-danger-small .x-btn-split-right { + background-image: url(images/button/nx-danger-small-s-arrow.png); + padding-right: 23px; +} +.x-btn-nx-danger-small .x-btn-split-bottom { + background-image: url(images/button/nx-danger-small-s-arrow-b.png); + padding-bottom: 20px; +} + +.x-btn-nx-danger-small-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-nx-danger-small-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-danger-small-focus-corners.gif), sides:url(images/btn/btn-nx-danger-small-focus-sides.gif), frame-bg:url(images/btn/btn-nx-danger-small-focus-fbg.gif), bg:url(images/btn/btn-nx-danger-small-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-danger-small-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-danger-small-over-corners.gif), sides:url(images/btn/btn-nx-danger-small-over-sides.gif), frame-bg:url(images/btn/btn-nx-danger-small-over-fbg.gif), bg:url(images/btn/btn-nx-danger-small-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-danger-small-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-danger-small-pressed-corners.gif), sides:url(images/btn/btn-nx-danger-small-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-danger-small-pressed-fbg.gif), bg:url(images/btn/btn-nx-danger-small-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-danger-small-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-danger-small-disabled-corners.gif), sides:url(images/btn/btn-nx-danger-small-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-danger-small-disabled-fbg.gif), bg:url(images/btn/btn-nx-danger-small-disabled-bg.gif)"; +} + +/**/ +/* */ +/** + * ui: 'nx-warning' + */ +.x-btn-nx-warning-small { + border-color: #333333; +} + +.x-btn-nx-warning-small { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: #f39244; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #f39244), color-stop(100%, #da792b)); + background-image: -webkit-linear-gradient(top, #f39244, #da792b); + background-image: -moz-linear-gradient(top, #f39244, #da792b); + background-image: -o-linear-gradient(top, #f39244, #da792b); + background-image: -ms-linear-gradient(top, #f39244, #da792b); + background-image: linear-gradient(top, #f39244, #da792b); +} + +.x-btn-nx-warning-small-mc { + background-image: url(images/btn/btn-nx-warning-small-fbg.gif); + background-position: 0 top; + background-color: #f39244; +} + +.x-nlg .x-btn-nx-warning-small { + background-image: url(images/btn/btn-nx-warning-small-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-nx-warning-small { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-nx-warning-small-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-nx-warning-small-tl { + background-position: 0 -6px; +} + +.x-btn-nx-warning-small-tr { + background-position: right -9px; +} + +.x-btn-nx-warning-small-bl { + background-position: 0 -12px; +} + +.x-btn-nx-warning-small-br { + background-position: right -15px; +} + +.x-btn-nx-warning-small-ml { + background-position: 0 top; +} + +.x-btn-nx-warning-small-mr { + background-position: right top; +} + +.x-btn-nx-warning-small-tc { + background-position: 0 0; +} + +.x-btn-nx-warning-small-bc { + background-position: 0 -3px; +} + +.x-btn-nx-warning-small-tr, +.x-btn-nx-warning-small-br, +.x-btn-nx-warning-small-mr { + padding-right: 3px; +} + +.x-btn-nx-warning-small-tl, +.x-btn-nx-warning-small-bl, +.x-btn-nx-warning-small-ml { + padding-left: 3px; +} + +.x-btn-nx-warning-small-tc { + height: 3px; +} + +.x-btn-nx-warning-small-bc { + height: 3px; +} + +.x-btn-nx-warning-small-tl, +.x-btn-nx-warning-small-bl, +.x-btn-nx-warning-small-tr, +.x-btn-nx-warning-small-br, +.x-btn-nx-warning-small-tc, +.x-btn-nx-warning-small-bc, +.x-btn-nx-warning-small-ml, +.x-btn-nx-warning-small-mr { + zoom: 1; + background-image: url(images/btn/btn-nx-warning-small-corners.gif); +} + +.x-btn-nx-warning-small-ml, +.x-btn-nx-warning-small-mr { + zoom: 1; + background-image: url(images/btn/btn-nx-warning-small-sides.gif); +} + +.x-btn-nx-warning-small-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-nx-warning-small-tl, +.x-strict .x-ie7 .x-btn-nx-warning-small-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-nx-warning-small:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-warning-small-fbg.gif), bg:url(images/btn/btn-nx-warning-small-bg.gif), corners:url(images/btn/btn-nx-warning-small-corners.gif), sides:url(images/btn/btn-nx-warning-small-sides.gif)"; +} + +/**/ +/* */ +.x-btn-nx-warning-small .x-btn-inner { + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: white; + padding: 0 5px; +} +.x-btn-nx-warning-small .x-btn-arrow { + background-image: url(images/button/nx-warning-small-arrow.png); +} +.x-btn-nx-warning-small .x-btn-arrow-right { + padding-right: 21px; +} +.x-btn-nx-warning-small .x-btn-arrow-bottom { + padding-bottom: 18px; +} +.x-btn-nx-warning-small .x-btn-glyph { + font-size: 16px; + line-height: 16px; + color: white; +} +.x-ie8m .x-btn-nx-warning-small .x-btn-glyph { + color: white; +} + +.x-btn-nx-warning-small-disabled { + background-image: none; + background-color: #f39244; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #f39244), color-stop(100%, #da792b)); + background-image: -webkit-linear-gradient(top, #f39244, #da792b); + background-image: -moz-linear-gradient(top, #f39244, #da792b); + background-image: -o-linear-gradient(top, #f39244, #da792b); + background-image: -ms-linear-gradient(top, #f39244, #da792b); + background-image: linear-gradient(top, #f39244, #da792b); +} + +.x-btn-nx-warning-small-icon .x-btn-button, +.x-btn-nx-warning-small-noicon .x-btn-button { + height: 16px; +} +.x-btn-nx-warning-small-icon .x-btn-inner, +.x-btn-nx-warning-small-noicon .x-btn-inner { + line-height: 16px; +} + +.x-btn-nx-warning-small-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-warning-small-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-warning-small-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-nx-warning-small-icon .x-btn-inner { + width: 16px; + padding: 0; +} +.x-btn-nx-warning-small-icon .x-btn-icon-el { + width: 16px; + height: 16px; +} + +.x-btn-nx-warning-small-icon-text-left .x-btn-button { + height: 16px; +} +.x-btn-nx-warning-small-icon-text-left .x-btn-inner { + line-height: 16px; + padding-left: 21px; +} +.x-btn-nx-warning-small-icon-text-left .x-btn-icon-el { + width: 16px; + right: auto; +} +.x-ie6 .x-btn-nx-warning-small-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-warning-small-icon-text-left .x-btn-icon-el { + height: 16px; +} + +.x-btn-nx-warning-small-icon-text-right .x-btn-button { + height: 16px; +} +.x-btn-nx-warning-small-icon-text-right .x-btn-inner { + line-height: 16px; + padding-right: 21px; +} +.x-btn-nx-warning-small-icon-text-right .x-btn-icon-el { + width: 16px; + left: auto; +} +.x-ie6 .x-btn-nx-warning-small-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-warning-small-icon-text-right .x-btn-icon-el { + height: 16px; +} + +.x-btn-nx-warning-small-icon-text-top .x-btn-inner { + padding-top: 21px; +} +.x-btn-nx-warning-small-icon-text-top .x-btn-icon-el { + height: 16px; + bottom: auto; +} +.x-ie6 .x-btn-nx-warning-small-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-warning-small-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-warning-small-icon-text-bottom .x-btn-inner { + padding-bottom: 21px; +} +.x-btn-nx-warning-small-icon-text-bottom .x-btn-icon-el { + height: 16px; + top: auto; +} +.x-ie6 .x-btn-nx-warning-small-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-warning-small-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-warning-small-focus { + border-color: #96caee; + background-image: none; + background-color: #f39244; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #f39244), color-stop(100%, #da792b)); + background-image: -webkit-linear-gradient(top, #f39244, #da792b); + background-image: -moz-linear-gradient(top, #f39244, #da792b); + background-image: -o-linear-gradient(top, #f39244, #da792b); + background-image: -ms-linear-gradient(top, #f39244, #da792b); + background-image: linear-gradient(top, #f39244, #da792b); +} + +.x-btn-nx-warning-small-over { + background-image: none; + background-color: #c17536; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #c17536), color-stop(100%, #ae6122)); + background-image: -webkit-linear-gradient(top, #c17536, #ae6122); + background-image: -moz-linear-gradient(top, #c17536, #ae6122); + background-image: -o-linear-gradient(top, #c17536, #ae6122); + background-image: -ms-linear-gradient(top, #c17536, #ae6122); + background-image: linear-gradient(top, #c17536, #ae6122); +} + +.x-btn-nx-warning-small-menu-active, +.x-btn-nx-warning-small-pressed { + background-image: none; + background-color: #925829; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #925829), color-stop(100%, #83491a)); + background-image: -webkit-linear-gradient(top, #925829, #83491a); + background-image: -moz-linear-gradient(top, #925829, #83491a); + background-image: -o-linear-gradient(top, #925829, #83491a); + background-image: -ms-linear-gradient(top, #925829, #83491a); + background-image: linear-gradient(top, #925829, #83491a); +} + +.x-btn-nx-warning-small-focus .x-frame-tl, +.x-btn-nx-warning-small-focus .x-frame-bl, +.x-btn-nx-warning-small-focus .x-frame-tr, +.x-btn-nx-warning-small-focus .x-frame-br, +.x-btn-nx-warning-small-focus .x-frame-tc, +.x-btn-nx-warning-small-focus .x-frame-bc { + background-image: url(images/btn/btn-nx-warning-small-focus-corners.gif); +} +.x-btn-nx-warning-small-focus .x-frame-ml, +.x-btn-nx-warning-small-focus .x-frame-mr { + background-image: url(images/btn/btn-nx-warning-small-focus-sides.gif); +} +.x-btn-nx-warning-small-focus .x-frame-mc { + background-color: #f39244; + background-image: url(images/btn/btn-nx-warning-small-focus-fbg.gif); +} + +.x-btn-nx-warning-small-over .x-frame-tl, +.x-btn-nx-warning-small-over .x-frame-bl, +.x-btn-nx-warning-small-over .x-frame-tr, +.x-btn-nx-warning-small-over .x-frame-br, +.x-btn-nx-warning-small-over .x-frame-tc, +.x-btn-nx-warning-small-over .x-frame-bc { + background-image: url(images/btn/btn-nx-warning-small-over-corners.gif); +} +.x-btn-nx-warning-small-over .x-frame-ml, +.x-btn-nx-warning-small-over .x-frame-mr { + background-image: url(images/btn/btn-nx-warning-small-over-sides.gif); +} +.x-btn-nx-warning-small-over .x-frame-mc { + background-color: #c17536; + background-image: url(images/btn/btn-nx-warning-small-over-fbg.gif); +} + +.x-btn-nx-warning-small-menu-active .x-frame-tl, +.x-btn-nx-warning-small-menu-active .x-frame-bl, +.x-btn-nx-warning-small-menu-active .x-frame-tr, +.x-btn-nx-warning-small-menu-active .x-frame-br, +.x-btn-nx-warning-small-menu-active .x-frame-tc, +.x-btn-nx-warning-small-menu-active .x-frame-bc, +.x-btn-nx-warning-small-pressed .x-frame-tl, +.x-btn-nx-warning-small-pressed .x-frame-bl, +.x-btn-nx-warning-small-pressed .x-frame-tr, +.x-btn-nx-warning-small-pressed .x-frame-br, +.x-btn-nx-warning-small-pressed .x-frame-tc, +.x-btn-nx-warning-small-pressed .x-frame-bc { + background-image: url(images/btn/btn-nx-warning-small-pressed-corners.gif); +} +.x-btn-nx-warning-small-menu-active .x-frame-ml, +.x-btn-nx-warning-small-menu-active .x-frame-mr, +.x-btn-nx-warning-small-pressed .x-frame-ml, +.x-btn-nx-warning-small-pressed .x-frame-mr { + background-image: url(images/btn/btn-nx-warning-small-pressed-sides.gif); +} +.x-btn-nx-warning-small-menu-active .x-frame-mc, +.x-btn-nx-warning-small-pressed .x-frame-mc { + background-color: #925829; + background-image: url(images/btn/btn-nx-warning-small-pressed-fbg.gif); +} + +.x-btn-nx-warning-small-disabled .x-frame-tl, +.x-btn-nx-warning-small-disabled .x-frame-bl, +.x-btn-nx-warning-small-disabled .x-frame-tr, +.x-btn-nx-warning-small-disabled .x-frame-br, +.x-btn-nx-warning-small-disabled .x-frame-tc, +.x-btn-nx-warning-small-disabled .x-frame-bc { + background-image: url(images/btn/btn-nx-warning-small-disabled-corners.gif); +} +.x-btn-nx-warning-small-disabled .x-frame-ml, +.x-btn-nx-warning-small-disabled .x-frame-mr { + background-image: url(images/btn/btn-nx-warning-small-disabled-sides.gif); +} +.x-btn-nx-warning-small-disabled .x-frame-mc { + background-color: #f39244; + background-image: url(images/btn/btn-nx-warning-small-disabled-fbg.gif); +} + +.x-nlg .x-btn-nx-warning-small-focus { + background-image: url(images/btn/btn-nx-warning-small-focus-bg.gif); +} + +.x-nlg .x-btn-nx-warning-small-over { + background-image: url(images/btn/btn-nx-warning-small-over-bg.gif); +} + +.x-nlg .x-btn-nx-warning-small-menu-active, +.x-nlg .x-btn-nx-warning-small-pressed { + background-image: url(images/btn/btn-nx-warning-small-pressed-bg.gif); +} + +.x-nlg .x-btn-nx-warning-small-disabled { + background-image: url(images/btn/btn-nx-warning-small-disabled-bg.gif); +} + +.x-nbr .x-btn-nx-warning-small { + background-image: none; +} + +.x-btn-nx-warning-small .x-btn-split-right { + background-image: url(images/button/nx-warning-small-s-arrow.png); + padding-right: 23px; +} +.x-btn-nx-warning-small .x-btn-split-bottom { + background-image: url(images/button/nx-warning-small-s-arrow-b.png); + padding-bottom: 20px; +} + +.x-btn-nx-warning-small-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-nx-warning-small-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-warning-small-focus-corners.gif), sides:url(images/btn/btn-nx-warning-small-focus-sides.gif), frame-bg:url(images/btn/btn-nx-warning-small-focus-fbg.gif), bg:url(images/btn/btn-nx-warning-small-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-warning-small-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-warning-small-over-corners.gif), sides:url(images/btn/btn-nx-warning-small-over-sides.gif), frame-bg:url(images/btn/btn-nx-warning-small-over-fbg.gif), bg:url(images/btn/btn-nx-warning-small-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-warning-small-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-warning-small-pressed-corners.gif), sides:url(images/btn/btn-nx-warning-small-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-warning-small-pressed-fbg.gif), bg:url(images/btn/btn-nx-warning-small-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-warning-small-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-warning-small-disabled-corners.gif), sides:url(images/btn/btn-nx-warning-small-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-warning-small-disabled-fbg.gif), bg:url(images/btn/btn-nx-warning-small-disabled-bg.gif)"; +} + +/**/ +/* */ +/** + * ui: 'nx-success' + */ +.x-btn-nx-success-small { + border-color: #333333; +} + +.x-btn-nx-success-small { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: #23a156; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #23a156), color-stop(100%, #0b893d)); + background-image: -webkit-linear-gradient(top, #23a156, #0b893d); + background-image: -moz-linear-gradient(top, #23a156, #0b893d); + background-image: -o-linear-gradient(top, #23a156, #0b893d); + background-image: -ms-linear-gradient(top, #23a156, #0b893d); + background-image: linear-gradient(top, #23a156, #0b893d); +} + +.x-btn-nx-success-small-mc { + background-image: url(images/btn/btn-nx-success-small-fbg.gif); + background-position: 0 top; + background-color: #23a156; +} + +.x-nlg .x-btn-nx-success-small { + background-image: url(images/btn/btn-nx-success-small-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-nx-success-small { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-nx-success-small-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-nx-success-small-tl { + background-position: 0 -6px; +} + +.x-btn-nx-success-small-tr { + background-position: right -9px; +} + +.x-btn-nx-success-small-bl { + background-position: 0 -12px; +} + +.x-btn-nx-success-small-br { + background-position: right -15px; +} + +.x-btn-nx-success-small-ml { + background-position: 0 top; +} + +.x-btn-nx-success-small-mr { + background-position: right top; +} + +.x-btn-nx-success-small-tc { + background-position: 0 0; +} + +.x-btn-nx-success-small-bc { + background-position: 0 -3px; +} + +.x-btn-nx-success-small-tr, +.x-btn-nx-success-small-br, +.x-btn-nx-success-small-mr { + padding-right: 3px; +} + +.x-btn-nx-success-small-tl, +.x-btn-nx-success-small-bl, +.x-btn-nx-success-small-ml { + padding-left: 3px; +} + +.x-btn-nx-success-small-tc { + height: 3px; +} + +.x-btn-nx-success-small-bc { + height: 3px; +} + +.x-btn-nx-success-small-tl, +.x-btn-nx-success-small-bl, +.x-btn-nx-success-small-tr, +.x-btn-nx-success-small-br, +.x-btn-nx-success-small-tc, +.x-btn-nx-success-small-bc, +.x-btn-nx-success-small-ml, +.x-btn-nx-success-small-mr { + zoom: 1; + background-image: url(images/btn/btn-nx-success-small-corners.gif); +} + +.x-btn-nx-success-small-ml, +.x-btn-nx-success-small-mr { + zoom: 1; + background-image: url(images/btn/btn-nx-success-small-sides.gif); +} + +.x-btn-nx-success-small-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-nx-success-small-tl, +.x-strict .x-ie7 .x-btn-nx-success-small-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-nx-success-small:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-success-small-fbg.gif), bg:url(images/btn/btn-nx-success-small-bg.gif), corners:url(images/btn/btn-nx-success-small-corners.gif), sides:url(images/btn/btn-nx-success-small-sides.gif)"; +} + +/**/ +/* */ +.x-btn-nx-success-small .x-btn-inner { + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: white; + padding: 0 5px; +} +.x-btn-nx-success-small .x-btn-arrow { + background-image: url(images/button/nx-success-small-arrow.png); +} +.x-btn-nx-success-small .x-btn-arrow-right { + padding-right: 21px; +} +.x-btn-nx-success-small .x-btn-arrow-bottom { + padding-bottom: 18px; +} +.x-btn-nx-success-small .x-btn-glyph { + font-size: 16px; + line-height: 16px; + color: white; +} +.x-ie8m .x-btn-nx-success-small .x-btn-glyph { + color: white; +} + +.x-btn-nx-success-small-disabled { + background-image: none; + background-color: #23a156; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #23a156), color-stop(100%, #0b893d)); + background-image: -webkit-linear-gradient(top, #23a156, #0b893d); + background-image: -moz-linear-gradient(top, #23a156, #0b893d); + background-image: -o-linear-gradient(top, #23a156, #0b893d); + background-image: -ms-linear-gradient(top, #23a156, #0b893d); + background-image: linear-gradient(top, #23a156, #0b893d); +} + +.x-btn-nx-success-small-icon .x-btn-button, +.x-btn-nx-success-small-noicon .x-btn-button { + height: 16px; +} +.x-btn-nx-success-small-icon .x-btn-inner, +.x-btn-nx-success-small-noicon .x-btn-inner { + line-height: 16px; +} + +.x-btn-nx-success-small-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-success-small-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-success-small-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-nx-success-small-icon .x-btn-inner { + width: 16px; + padding: 0; +} +.x-btn-nx-success-small-icon .x-btn-icon-el { + width: 16px; + height: 16px; +} + +.x-btn-nx-success-small-icon-text-left .x-btn-button { + height: 16px; +} +.x-btn-nx-success-small-icon-text-left .x-btn-inner { + line-height: 16px; + padding-left: 21px; +} +.x-btn-nx-success-small-icon-text-left .x-btn-icon-el { + width: 16px; + right: auto; +} +.x-ie6 .x-btn-nx-success-small-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-success-small-icon-text-left .x-btn-icon-el { + height: 16px; +} + +.x-btn-nx-success-small-icon-text-right .x-btn-button { + height: 16px; +} +.x-btn-nx-success-small-icon-text-right .x-btn-inner { + line-height: 16px; + padding-right: 21px; +} +.x-btn-nx-success-small-icon-text-right .x-btn-icon-el { + width: 16px; + left: auto; +} +.x-ie6 .x-btn-nx-success-small-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-success-small-icon-text-right .x-btn-icon-el { + height: 16px; +} + +.x-btn-nx-success-small-icon-text-top .x-btn-inner { + padding-top: 21px; +} +.x-btn-nx-success-small-icon-text-top .x-btn-icon-el { + height: 16px; + bottom: auto; +} +.x-ie6 .x-btn-nx-success-small-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-success-small-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-success-small-icon-text-bottom .x-btn-inner { + padding-bottom: 21px; +} +.x-btn-nx-success-small-icon-text-bottom .x-btn-icon-el { + height: 16px; + top: auto; +} +.x-ie6 .x-btn-nx-success-small-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-success-small-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-success-small-focus { + border-color: #96caee; + background-image: none; + background-color: #23a156; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #23a156), color-stop(100%, #0b893d)); + background-image: -webkit-linear-gradient(top, #23a156, #0b893d); + background-image: -moz-linear-gradient(top, #23a156, #0b893d); + background-image: -o-linear-gradient(top, #23a156, #0b893d); + background-image: -ms-linear-gradient(top, #23a156, #0b893d); + background-image: linear-gradient(top, #23a156, #0b893d); +} + +.x-btn-nx-success-small-over { + background-image: none; + background-color: #1c8145; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #1c8145), color-stop(100%, #096e31)); + background-image: -webkit-linear-gradient(top, #1c8145, #096e31); + background-image: -moz-linear-gradient(top, #1c8145, #096e31); + background-image: -o-linear-gradient(top, #1c8145, #096e31); + background-image: -ms-linear-gradient(top, #1c8145, #096e31); + background-image: linear-gradient(top, #1c8145, #096e31); +} + +.x-btn-nx-success-small-menu-active, +.x-btn-nx-success-small-pressed { + background-image: none; + background-color: #156134; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #156134), color-stop(100%, #0c4f26)); + background-image: -webkit-linear-gradient(top, #156134, #0c4f26); + background-image: -moz-linear-gradient(top, #156134, #0c4f26); + background-image: -o-linear-gradient(top, #156134, #0c4f26); + background-image: -ms-linear-gradient(top, #156134, #0c4f26); + background-image: linear-gradient(top, #156134, #0c4f26); +} + +.x-btn-nx-success-small-focus .x-frame-tl, +.x-btn-nx-success-small-focus .x-frame-bl, +.x-btn-nx-success-small-focus .x-frame-tr, +.x-btn-nx-success-small-focus .x-frame-br, +.x-btn-nx-success-small-focus .x-frame-tc, +.x-btn-nx-success-small-focus .x-frame-bc { + background-image: url(images/btn/btn-nx-success-small-focus-corners.gif); +} +.x-btn-nx-success-small-focus .x-frame-ml, +.x-btn-nx-success-small-focus .x-frame-mr { + background-image: url(images/btn/btn-nx-success-small-focus-sides.gif); +} +.x-btn-nx-success-small-focus .x-frame-mc { + background-color: #23a156; + background-image: url(images/btn/btn-nx-success-small-focus-fbg.gif); +} + +.x-btn-nx-success-small-over .x-frame-tl, +.x-btn-nx-success-small-over .x-frame-bl, +.x-btn-nx-success-small-over .x-frame-tr, +.x-btn-nx-success-small-over .x-frame-br, +.x-btn-nx-success-small-over .x-frame-tc, +.x-btn-nx-success-small-over .x-frame-bc { + background-image: url(images/btn/btn-nx-success-small-over-corners.gif); +} +.x-btn-nx-success-small-over .x-frame-ml, +.x-btn-nx-success-small-over .x-frame-mr { + background-image: url(images/btn/btn-nx-success-small-over-sides.gif); +} +.x-btn-nx-success-small-over .x-frame-mc { + background-color: #1c8145; + background-image: url(images/btn/btn-nx-success-small-over-fbg.gif); +} + +.x-btn-nx-success-small-menu-active .x-frame-tl, +.x-btn-nx-success-small-menu-active .x-frame-bl, +.x-btn-nx-success-small-menu-active .x-frame-tr, +.x-btn-nx-success-small-menu-active .x-frame-br, +.x-btn-nx-success-small-menu-active .x-frame-tc, +.x-btn-nx-success-small-menu-active .x-frame-bc, +.x-btn-nx-success-small-pressed .x-frame-tl, +.x-btn-nx-success-small-pressed .x-frame-bl, +.x-btn-nx-success-small-pressed .x-frame-tr, +.x-btn-nx-success-small-pressed .x-frame-br, +.x-btn-nx-success-small-pressed .x-frame-tc, +.x-btn-nx-success-small-pressed .x-frame-bc { + background-image: url(images/btn/btn-nx-success-small-pressed-corners.gif); +} +.x-btn-nx-success-small-menu-active .x-frame-ml, +.x-btn-nx-success-small-menu-active .x-frame-mr, +.x-btn-nx-success-small-pressed .x-frame-ml, +.x-btn-nx-success-small-pressed .x-frame-mr { + background-image: url(images/btn/btn-nx-success-small-pressed-sides.gif); +} +.x-btn-nx-success-small-menu-active .x-frame-mc, +.x-btn-nx-success-small-pressed .x-frame-mc { + background-color: #156134; + background-image: url(images/btn/btn-nx-success-small-pressed-fbg.gif); +} + +.x-btn-nx-success-small-disabled .x-frame-tl, +.x-btn-nx-success-small-disabled .x-frame-bl, +.x-btn-nx-success-small-disabled .x-frame-tr, +.x-btn-nx-success-small-disabled .x-frame-br, +.x-btn-nx-success-small-disabled .x-frame-tc, +.x-btn-nx-success-small-disabled .x-frame-bc { + background-image: url(images/btn/btn-nx-success-small-disabled-corners.gif); +} +.x-btn-nx-success-small-disabled .x-frame-ml, +.x-btn-nx-success-small-disabled .x-frame-mr { + background-image: url(images/btn/btn-nx-success-small-disabled-sides.gif); +} +.x-btn-nx-success-small-disabled .x-frame-mc { + background-color: #23a156; + background-image: url(images/btn/btn-nx-success-small-disabled-fbg.gif); +} + +.x-nlg .x-btn-nx-success-small-focus { + background-image: url(images/btn/btn-nx-success-small-focus-bg.gif); +} + +.x-nlg .x-btn-nx-success-small-over { + background-image: url(images/btn/btn-nx-success-small-over-bg.gif); +} + +.x-nlg .x-btn-nx-success-small-menu-active, +.x-nlg .x-btn-nx-success-small-pressed { + background-image: url(images/btn/btn-nx-success-small-pressed-bg.gif); +} + +.x-nlg .x-btn-nx-success-small-disabled { + background-image: url(images/btn/btn-nx-success-small-disabled-bg.gif); +} + +.x-nbr .x-btn-nx-success-small { + background-image: none; +} + +.x-btn-nx-success-small .x-btn-split-right { + background-image: url(images/button/nx-success-small-s-arrow.png); + padding-right: 23px; +} +.x-btn-nx-success-small .x-btn-split-bottom { + background-image: url(images/button/nx-success-small-s-arrow-b.png); + padding-bottom: 20px; +} + +.x-btn-nx-success-small-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-nx-success-small-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-success-small-focus-corners.gif), sides:url(images/btn/btn-nx-success-small-focus-sides.gif), frame-bg:url(images/btn/btn-nx-success-small-focus-fbg.gif), bg:url(images/btn/btn-nx-success-small-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-success-small-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-success-small-over-corners.gif), sides:url(images/btn/btn-nx-success-small-over-sides.gif), frame-bg:url(images/btn/btn-nx-success-small-over-fbg.gif), bg:url(images/btn/btn-nx-success-small-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-success-small-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-success-small-pressed-corners.gif), sides:url(images/btn/btn-nx-success-small-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-success-small-pressed-fbg.gif), bg:url(images/btn/btn-nx-success-small-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-success-small-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-success-small-disabled-corners.gif), sides:url(images/btn/btn-nx-success-small-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-success-small-disabled-fbg.gif), bg:url(images/btn/btn-nx-success-small-disabled-bg.gif)"; +} + +/**/ +/* */ +.x-btn-default-small .x-btn-glyph, +.x-btn-plain-small .x-btn-glyph, +.x-btn-primary-small .x-btn-glyph, +.x-btn-danger-small .x-btn-glyph, +.x-btn-warning-small .x-btn-glyph, +.x-btn-success-small .x-btn-glyph { + font-size: 13px; +} + +/** + * @class Ext.panel.Panel + */ +/** + * ui: 'nx-inset' + */ +.x-panel-nx-inset { + border-color: #444444; + padding: 12px 12px 0 12px; +} + +.x-panel-header-nx-inset { + font-size: 13px; + border: 1px solid #444444; +} +.x-panel-header-nx-inset .x-tool-img { + background-color: #444444; +} + +.x-panel-header-nx-inset-horizontal { + padding: 9px 9px 10px 9px; +} + +.x-panel-header-nx-inset-horizontal-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-nx-inset-vertical { + padding: 9px 9px 9px 10px; +} + +.x-panel-header-nx-inset-vertical-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-text-container-nx-inset { + color: white; + font-size: 13px; + font-weight: bold; + font-family: arial, helvetica, verdana, sans-serif; + line-height: 15px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-nx-inset { + background: transparent; + border-color: #444444; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-header-nx-inset { + background-image: none; + background-color: #444444; +} + +.x-panel-header-nx-inset-vertical { + background-image: none; + background-color: #444444; +} + +.x-panel .x-panel-header-nx-inset-collapsed-border-top { + border-bottom-width: 1px !important; +} +.x-panel .x-panel-header-nx-inset-collapsed-border-right { + border-left-width: 1px !important; +} +.x-panel .x-panel-header-nx-inset-collapsed-border-bottom { + border-top-width: 1px !important; +} +.x-panel .x-panel-header-nx-inset-collapsed-border-left { + border-right-width: 1px !important; +} + +/**/ +.x-panel-header-nx-inset-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-inset-bottom:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-inset-left:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-inset-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-panel-header-nx-inset-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-nx-inset-vertical .x-panel-header-text-container { + background-color: #444444; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#444444); +} + +.x-panel-header-nx-inset .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-nx-inset .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-nx-inset .x-panel-header-glyph { + color: #a1a1a1; +} + +.x-panel-header-nx-inset-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-nx-inset-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-nx-inset-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-nx-inset-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-nx-inset-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-nx-inset-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-nx-inset-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-nx-inset-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-nx-inset-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-nx-inset-outer-border-l { + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-b { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-bl { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-r { + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-rl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-rb { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-rbl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-t { + border-top-color: #444444 !important; + border-top-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-tl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-tb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-tbl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-tr { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-trl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-trb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-inset-outer-border-trbl { + border-color: #444444 !important; + border-width: 1px !important; +} + +.x-panel-nx-inset .x-panel-body .x-toolbar { + padding: 6px 0 6px 0; +} +.x-panel-nx-inset .x-panel-nx-subsection-framed { + margin: 0 0 10px 0; +} + +/** + * Style for the global "license warning" header + */ +.x-panel-nx-license-warning, .x-panel-nx-database-freeze-warning, .x-panel-nx-file-descriptor-warning { + border-color: #444444; + padding: 0; +} + +.x-panel-header-nx-license-warning { + font-size: 13px; + border: 1px solid #444444; +} +.x-panel-header-nx-license-warning .x-tool-img { + background-color: #444444; +} + +.x-panel-header-nx-license-warning-horizontal { + padding: 9px 9px 10px 9px; +} + +.x-panel-header-nx-license-warning-horizontal-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-nx-license-warning-vertical { + padding: 9px 9px 9px 10px; +} + +.x-panel-header-nx-license-warning-vertical-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-text-container-nx-license-warning { + color: white; + font-size: 13px; + font-weight: bold; + font-family: arial, helvetica, verdana, sans-serif; + line-height: 15px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-nx-license-warning { + background: transparent; + border-color: #444444; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-header-nx-license-warning { + background-image: none; + background-color: #444444; +} + +.x-panel-header-nx-license-warning-vertical { + background-image: none; + background-color: #444444; +} + +.x-panel .x-panel-header-nx-license-warning-collapsed-border-top { + border-bottom-width: 1px !important; +} +.x-panel .x-panel-header-nx-license-warning-collapsed-border-right { + border-left-width: 1px !important; +} +.x-panel .x-panel-header-nx-license-warning-collapsed-border-bottom { + border-top-width: 1px !important; +} +.x-panel .x-panel-header-nx-license-warning-collapsed-border-left { + border-right-width: 1px !important; +} + +/**/ +.x-panel-header-nx-license-warning-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-license-warning-bottom:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-license-warning-left:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-license-warning-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-panel-header-nx-license-warning-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-nx-license-warning-vertical .x-panel-header-text-container { + background-color: #444444; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#444444); +} + +.x-panel-header-nx-license-warning .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-nx-license-warning .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-nx-license-warning .x-panel-header-glyph { + color: #a1a1a1; +} + +.x-panel-header-nx-license-warning-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-nx-license-warning-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-nx-license-warning-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-nx-license-warning-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-nx-license-warning-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-nx-license-warning-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-nx-license-warning-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-nx-license-warning-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-nx-license-warning-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-nx-license-warning-outer-border-l { + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-b { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-bl { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-r { + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-rl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-rb { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-rbl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-t { + border-top-color: #444444 !important; + border-top-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-tl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-tb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-tbl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-tr { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-trl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-trb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-license-warning-outer-border-trbl { + border-color: #444444 !important; + border-width: 1px !important; +} + +.x-panel-nx-license-warning .x-panel-header, .x-panel-nx-database-freeze-warning .x-panel-header, .x-panel-nx-file-descriptor-warning .x-panel-header { + padding: 5px 16px; +} + +.x-panel-nx-license-warning, .x-panel-nx-database-freeze-warning, .x-panel-nx-file-descriptor-warning { + color: white; +} + +.x-panel-nx-license-warning a, .x-panel-nx-database-freeze-warning a, .x-panel-nx-file-descriptor-warning a, +.x-panel-nx-license-warning a:visited, +.x-panel-nx-database-freeze-warning a:visited, +.x-panel-nx-file-descriptor-warning a:visited, +.x-panel-nx-license-warning a:active, +.x-panel-nx-database-freeze-warning a:active, +.x-panel-nx-file-descriptor-warning a:active { + color: #dddddd; +} + +.x-panel-nx-license-warning a:hover, .x-panel-nx-database-freeze-warning a:hover, .x-panel-nx-file-descriptor-warning a:hover { + color: white; + text-decoration: underline; +} + +/** + * Style for the global "node frozen warning" header + */ +.x-panel-nx-database-freeze-warning { + background-color: #953ea9; +} + +.x-panel-header-text-container-nx-database-freeze-warning { + margin-left: 6px; + font-weight: bold; +} + +/** + * Style for the global "file descriptor warning" header + */ +.x-panel-nx-file-descriptor-warning { + background-color: #953ea9; +} + +.x-panel-header-text-container-nx-file-descriptor-warning { + margin-left: 6px; + font-weight: bold; +} + +/** +* Style for the "transparent" inter-page panels +* +* ui: 'subsection' +*/ +.x-panel-nx-subsection { + border-color: #444444; + padding: 0; +} + +.x-panel-header-nx-subsection { + font-size: 18px; + border: 1px solid #444444; +} +.x-panel-header-nx-subsection .x-tool-img { + background-color: white; +} + +.x-panel-header-nx-subsection-horizontal { + padding: 9px 9px 10px 9px; +} + +.x-panel-header-nx-subsection-horizontal-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-nx-subsection-vertical { + padding: 9px 9px 9px 10px; +} + +.x-panel-header-nx-subsection-vertical-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-text-container-nx-subsection { + color: #333333; + font-size: 18px; + font-weight: bold; + font-family: arial, helvetica, verdana, sans-serif; + line-height: 22px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-nx-subsection { + background: transparent; + border-color: #444444; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-header-nx-subsection { + background-image: none; + background-color: white; +} + +.x-panel-header-nx-subsection-vertical { + background-image: none; + background-color: white; +} + +.x-panel .x-panel-header-nx-subsection-collapsed-border-top { + border-bottom-width: 1px !important; +} +.x-panel .x-panel-header-nx-subsection-collapsed-border-right { + border-left-width: 1px !important; +} +.x-panel .x-panel-header-nx-subsection-collapsed-border-bottom { + border-top-width: 1px !important; +} +.x-panel .x-panel-header-nx-subsection-collapsed-border-left { + border-right-width: 1px !important; +} + +/**/ +.x-panel-header-nx-subsection-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-subsection-bottom:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-subsection-left:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-subsection-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-panel-header-nx-subsection-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-nx-subsection-vertical .x-panel-header-text-container { + background-color: white; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=white); +} + +.x-panel-header-nx-subsection .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-nx-subsection .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-nx-subsection .x-panel-header-glyph { + color: white; +} + +.x-panel-header-nx-subsection-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-nx-subsection-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-nx-subsection-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-nx-subsection-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-nx-subsection-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-nx-subsection-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-nx-subsection-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-nx-subsection-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-nx-subsection-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-nx-subsection-outer-border-l { + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-b { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-bl { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-r { + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-rl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-rb { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-rbl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-t { + border-top-color: #444444 !important; + border-top-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-tl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-tb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-tbl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-tr { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-trl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-trb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-subsection-outer-border-trbl { + border-color: #444444 !important; + border-width: 1px !important; +} + +.x-panel-nx-subsection-framed { + border-color: #444444; + padding: 0; +} + +.x-panel-header-nx-subsection-framed { + font-size: 18px; + border: 1px solid #444444; +} +.x-panel-header-nx-subsection-framed .x-tool-img { + background-color: white; +} + +.x-panel-header-nx-subsection-framed-horizontal { + padding: 9px 9px 10px 9px; +} + +.x-panel-header-nx-subsection-framed-horizontal-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-nx-subsection-framed-vertical { + padding: 9px 9px 9px 10px; +} + +.x-panel-header-nx-subsection-framed-vertical-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-text-container-nx-subsection-framed { + color: #333333; + font-size: 18px; + font-weight: bold; + font-family: arial, helvetica, verdana, sans-serif; + line-height: 22px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-nx-subsection-framed { + background: white; + border-color: #444444; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-header-nx-subsection-framed { + background-image: none; + background-color: white; +} + +.x-panel-header-nx-subsection-framed-vertical { + background-image: none; + background-color: white; +} + +.x-panel .x-panel-header-nx-subsection-framed-collapsed-border-top { + border-bottom-width: 1px !important; +} +.x-panel .x-panel-header-nx-subsection-framed-collapsed-border-right { + border-left-width: 1px !important; +} +.x-panel .x-panel-header-nx-subsection-framed-collapsed-border-bottom { + border-top-width: 1px !important; +} +.x-panel .x-panel-header-nx-subsection-framed-collapsed-border-left { + border-right-width: 1px !important; +} + +/**/ +.x-panel-header-nx-subsection-framed-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-subsection-framed-bottom:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-subsection-framed-left:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-subsection-framed-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-panel-header-nx-subsection-framed-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-nx-subsection-framed-vertical .x-panel-header-text-container { + background-color: white; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=white); +} + +.x-panel-header-nx-subsection-framed .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-nx-subsection-framed .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-nx-subsection-framed .x-panel-header-glyph { + color: white; +} + +.x-panel-header-nx-subsection-framed-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-nx-subsection-framed-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-nx-subsection-framed-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-nx-subsection-framed-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-nx-subsection-framed-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-nx-subsection-framed-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-nx-subsection-framed-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-nx-subsection-framed-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-nx-subsection-framed-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-nx-subsection-framed-outer-border-l { + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-b { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-bl { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-r { + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-rl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-rb { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-rbl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-t { + border-top-color: #444444 !important; + border-top-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-tl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-tb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-tbl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-tr { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-trl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-trb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-subsection-framed-outer-border-trbl { + border-color: #444444 !important; + border-width: 1px !important; +} + +.x-panel-nx-subsection > .x-panel-header { + padding: 10px 0 5px 0 !important; +} + +.x-panel-nx-subsection-framed { + border: #dddddd 1px solid; +} + +.x-panel-nx-subsection-framed > .x-panel-header { + padding: 10px 9px 5px 9px !important; +} + +.x-panel-nx-subsection-framed > .x-panel-body { + padding: 9px 9px 10px 9px !important; +} + +/** + * cls: 'nx-hr' + */ +.nx-hr { + border-top: 1px solid #dddddd !important; +} + +/** + * Style for the 'too many results' panel on the search page + */ +.x-panel-nx-info-message { + border-color: #444444; + padding: 0; +} + +.x-panel-header-nx-info-message { + font-size: 13px; + border: 1px solid #444444; +} +.x-panel-header-nx-info-message .x-tool-img { + background-color: white; +} + +.x-panel-header-nx-info-message-horizontal { + padding: 9px 9px 10px 9px; +} + +.x-panel-header-nx-info-message-horizontal-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-nx-info-message-vertical { + padding: 9px 9px 9px 10px; +} + +.x-panel-header-nx-info-message-vertical-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-text-container-nx-info-message { + color: #333333; + font-size: 13px; + font-weight: bold; + font-family: arial, helvetica, verdana, sans-serif; + line-height: 15px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-nx-info-message { + background: transparent; + border-color: #444444; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-header-nx-info-message { + background-image: none; + background-color: white; +} + +.x-panel-header-nx-info-message-vertical { + background-image: none; + background-color: white; +} + +.x-panel .x-panel-header-nx-info-message-collapsed-border-top { + border-bottom-width: 1px !important; +} +.x-panel .x-panel-header-nx-info-message-collapsed-border-right { + border-left-width: 1px !important; +} +.x-panel .x-panel-header-nx-info-message-collapsed-border-bottom { + border-top-width: 1px !important; +} +.x-panel .x-panel-header-nx-info-message-collapsed-border-left { + border-right-width: 1px !important; +} + +/**/ +.x-panel-header-nx-info-message-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-info-message-bottom:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-info-message-left:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-info-message-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-panel-header-nx-info-message-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-nx-info-message-vertical .x-panel-header-text-container { + background-color: white; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=white); +} + +.x-panel-header-nx-info-message .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-nx-info-message .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-nx-info-message .x-panel-header-glyph { + color: white; +} + +.x-panel-header-nx-info-message-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-nx-info-message-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-nx-info-message-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-nx-info-message-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-nx-info-message-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-nx-info-message-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-nx-info-message-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-nx-info-message-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-nx-info-message-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-nx-info-message-outer-border-l { + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-b { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-bl { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-r { + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-rl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-rb { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-rbl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-t { + border-top-color: #444444 !important; + border-top-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-tl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-tb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-tbl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-tr { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-trl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-trb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-info-message-outer-border-trbl { + border-color: #444444 !important; + border-width: 1px !important; +} + +.x-panel-nx-info-message .x-header { + padding: 0 10px 10px 10px; +} + +.nx-search-result-list { + border-top: 1px solid #dddddd; +} + +.nx-repeated-row { + margin-bottom: 5px; +} + +/** + * @class Ext.form.field.Base + */ +/** + * Style for read-only fields + * + * cls: 'x-form-readonly' + */ +.x-form-field[readonly].x-form-field:not([role="combobox"]) { + color: #777777; + background-color: #ebebeb; + opacity: 1; +} + +/** + * editable = false on a combo sets readonly already, so use a custom class to achieve the same effect + */ +.nx-combo-disabled input { + color: #777777; + background-color: #ebebeb; + opacity: 1; +} + +.x-form-fieldcontainer .x-mask { + background-color: #dddddd; + opacity: 0.6; +} + +/** + * Styles which allow for horizontal alignment of form fields + */ +.nx-float-left { + position: relative; + float: left !important; + margin-right: 5px; +} + +.nx-interstitial-label { + font-size: 13px; + padding-top: 5px; +} + +.nx-clear-both { + clear: both; +} + +/** + * Styling for the boxlabel of form fields + */ +.x-field .nx-boxlabel { + font-size: 10px; +} + +/** + * IE10 adds a second 'x' icon to text fields. Hide this. + */ +::-ms-clear { + width: 0; + height: 0; +} + +/** + * @class Ext.window.Window + */ +/** + * ui: 'nx-inset' + */ +.x-window-nx-inset { + border-color: #606060; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; +} + +.x-window-nx-inset { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 12px 12px 2px 12px; + border-width: 2px; + border-style: solid; + background-color: white; +} + +.x-window-nx-inset-mc { + background-color: white; +} + +.x-nbr .x-window-nx-inset { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-nx-inset-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-12-12-2-12; +} + +.x-window-nx-inset-tl { + background-position: 0 -8px; +} + +.x-window-nx-inset-tr { + background-position: right -12px; +} + +.x-window-nx-inset-bl { + background-position: 0 -16px; +} + +.x-window-nx-inset-br { + background-position: right -20px; +} + +.x-window-nx-inset-ml { + background-position: 0 top; +} + +.x-window-nx-inset-mr { + background-position: right top; +} + +.x-window-nx-inset-tc { + background-position: 0 0; +} + +.x-window-nx-inset-bc { + background-position: 0 -4px; +} + +.x-window-nx-inset-tr, +.x-window-nx-inset-br, +.x-window-nx-inset-mr { + padding-right: 4px; +} + +.x-window-nx-inset-tl, +.x-window-nx-inset-bl, +.x-window-nx-inset-ml { + padding-left: 4px; +} + +.x-window-nx-inset-tc { + height: 4px; +} + +.x-window-nx-inset-bc { + height: 4px; +} + +.x-window-nx-inset-tl, +.x-window-nx-inset-bl, +.x-window-nx-inset-tr, +.x-window-nx-inset-br, +.x-window-nx-inset-tc, +.x-window-nx-inset-bc, +.x-window-nx-inset-ml, +.x-window-nx-inset-mr { + zoom: 1; + background-image: url(images/window/window-nx-inset-corners.gif); +} + +.x-window-nx-inset-ml, +.x-window-nx-inset-mr { + zoom: 1; + background-image: url(images/window/window-nx-inset-sides.gif); + background-repeat: repeat-y; +} + +.x-window-nx-inset-mc { + padding: 10px 10px 0px 10px; +} + +.x-strict .x-ie7 .x-window-nx-inset-tl, +.x-strict .x-ie7 .x-window-nx-inset-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-nx-inset:after { + display: none; + content: "x-slicer:corners:url(images/window/window-nx-inset-corners.gif), sides:url(images/window/window-nx-inset-sides.gif)"; +} + +/**/ +/* */ +.x-window-body-nx-inset { + border-color: #606060; + border-width: 1px; + border-style: solid; + background: white; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.x-window-header-nx-inset { + font-size: 18px; + border-color: #606060; + zoom: 1; + background-color: #ebebeb; +} +.x-window-header-nx-inset .x-tool-img { + background-image: url(images/tools/tool-sprites-dark.png); + background-color: #ebebeb; +} + +.x-window-header-nx-inset-vertical .x-window-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-window-header-nx-inset-vertical .x-window-header-text-container { + background-color: #ebebeb; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#ebebeb); +} + +.x-window-header-text-container-nx-inset { + color: #333333; + font-weight: bold; + line-height: 22px; + font-family: arial, helvetica, verdana, sans-serif; + font-size: 18px; + padding: 1px 0 0; + text-transform: none; +} + +.x-window-header-nx-inset-top { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 10px 10px 6px 14px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-inset-top-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-inset-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-inset-top-frameInfo { + font-family: dh-4-4-0-0-0-0-0-0-10-10-6-14; +} + +.x-window-header-nx-inset-top-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-inset-top-tr { + background-position: right -12px; +} + +.x-window-header-nx-inset-top-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-inset-top-br { + background-position: right -20px; +} + +.x-window-header-nx-inset-top-ml { + background-position: 0 top; +} + +.x-window-header-nx-inset-top-mr { + background-position: right top; +} + +.x-window-header-nx-inset-top-tc { + background-position: 0 0; +} + +.x-window-header-nx-inset-top-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-inset-top-tr, +.x-window-header-nx-inset-top-br, +.x-window-header-nx-inset-top-mr { + padding-right: 4px; +} + +.x-window-header-nx-inset-top-tl, +.x-window-header-nx-inset-top-bl, +.x-window-header-nx-inset-top-ml { + padding-left: 4px; +} + +.x-window-header-nx-inset-top-tc { + height: 4px; +} + +.x-window-header-nx-inset-top-bc { + height: 0; +} + +.x-window-header-nx-inset-top-tl, +.x-window-header-nx-inset-top-bl, +.x-window-header-nx-inset-top-tr, +.x-window-header-nx-inset-top-br, +.x-window-header-nx-inset-top-tc, +.x-window-header-nx-inset-top-bc, +.x-window-header-nx-inset-top-ml, +.x-window-header-nx-inset-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-top-corners.gif); +} + +.x-window-header-nx-inset-top-ml, +.x-window-header-nx-inset-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-inset-top-mc { + padding: 6px 6px 6px 10px; +} + +.x-strict .x-ie7 .x-window-header-nx-inset-top-tl, +.x-strict .x-ie7 .x-window-header-nx-inset-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-inset-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-inset-top-corners.gif), sides:url(images/window-header/window-header-nx-inset-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-inset-right { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 14px 10px 10px 6px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-inset-right-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-inset-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-inset-right-frameInfo { + font-family: dh-0-4-4-0-0-0-0-0-14-10-10-6; +} + +.x-window-header-nx-inset-right-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-inset-right-tr { + background-position: right -12px; +} + +.x-window-header-nx-inset-right-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-inset-right-br { + background-position: right -20px; +} + +.x-window-header-nx-inset-right-ml { + background-position: 0 top; +} + +.x-window-header-nx-inset-right-mr { + background-position: right top; +} + +.x-window-header-nx-inset-right-tc { + background-position: 0 0; +} + +.x-window-header-nx-inset-right-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-inset-right-tr, +.x-window-header-nx-inset-right-br, +.x-window-header-nx-inset-right-mr { + padding-right: 4px; +} + +.x-window-header-nx-inset-right-tl, +.x-window-header-nx-inset-right-bl, +.x-window-header-nx-inset-right-ml { + padding-left: 0; +} + +.x-window-header-nx-inset-right-tc { + height: 4px; +} + +.x-window-header-nx-inset-right-bc { + height: 4px; +} + +.x-window-header-nx-inset-right-tl, +.x-window-header-nx-inset-right-bl, +.x-window-header-nx-inset-right-tr, +.x-window-header-nx-inset-right-br, +.x-window-header-nx-inset-right-tc, +.x-window-header-nx-inset-right-bc, +.x-window-header-nx-inset-right-ml, +.x-window-header-nx-inset-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-right-corners.gif); +} + +.x-window-header-nx-inset-right-ml, +.x-window-header-nx-inset-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-inset-right-mc { + padding: 10px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-inset-right-tl, +.x-strict .x-ie7 .x-window-header-nx-inset-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-inset-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-inset-right-corners.gif), sides:url(images/window-header/window-header-nx-inset-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-inset-bottom { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 6px 14px 10px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-inset-bottom-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-inset-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-inset-bottom-frameInfo { + font-family: dh-0-0-4-4-0-0-0-0-6-14-10-10; +} + +.x-window-header-nx-inset-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-inset-bottom-tr { + background-position: right -12px; +} + +.x-window-header-nx-inset-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-inset-bottom-br { + background-position: right -20px; +} + +.x-window-header-nx-inset-bottom-ml { + background-position: 0 top; +} + +.x-window-header-nx-inset-bottom-mr { + background-position: right top; +} + +.x-window-header-nx-inset-bottom-tc { + background-position: 0 0; +} + +.x-window-header-nx-inset-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-inset-bottom-tr, +.x-window-header-nx-inset-bottom-br, +.x-window-header-nx-inset-bottom-mr { + padding-right: 4px; +} + +.x-window-header-nx-inset-bottom-tl, +.x-window-header-nx-inset-bottom-bl, +.x-window-header-nx-inset-bottom-ml { + padding-left: 4px; +} + +.x-window-header-nx-inset-bottom-tc { + height: 0; +} + +.x-window-header-nx-inset-bottom-bc { + height: 4px; +} + +.x-window-header-nx-inset-bottom-tl, +.x-window-header-nx-inset-bottom-bl, +.x-window-header-nx-inset-bottom-tr, +.x-window-header-nx-inset-bottom-br, +.x-window-header-nx-inset-bottom-tc, +.x-window-header-nx-inset-bottom-bc, +.x-window-header-nx-inset-bottom-ml, +.x-window-header-nx-inset-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-bottom-corners.gif); +} + +.x-window-header-nx-inset-bottom-ml, +.x-window-header-nx-inset-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-inset-bottom-mc { + padding: 6px 10px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-inset-bottom-tl, +.x-strict .x-ie7 .x-window-header-nx-inset-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-inset-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-inset-bottom-corners.gif), sides:url(images/window-header/window-header-nx-inset-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-inset-left { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 10px 6px 14px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-inset-left-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-inset-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-inset-left-frameInfo { + font-family: dh-4-0-0-4-0-0-0-0-10-6-14-10; +} + +.x-window-header-nx-inset-left-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-inset-left-tr { + background-position: right -12px; +} + +.x-window-header-nx-inset-left-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-inset-left-br { + background-position: right -20px; +} + +.x-window-header-nx-inset-left-ml { + background-position: 0 top; +} + +.x-window-header-nx-inset-left-mr { + background-position: right top; +} + +.x-window-header-nx-inset-left-tc { + background-position: 0 0; +} + +.x-window-header-nx-inset-left-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-inset-left-tr, +.x-window-header-nx-inset-left-br, +.x-window-header-nx-inset-left-mr { + padding-right: 0; +} + +.x-window-header-nx-inset-left-tl, +.x-window-header-nx-inset-left-bl, +.x-window-header-nx-inset-left-ml { + padding-left: 4px; +} + +.x-window-header-nx-inset-left-tc { + height: 4px; +} + +.x-window-header-nx-inset-left-bc { + height: 4px; +} + +.x-window-header-nx-inset-left-tl, +.x-window-header-nx-inset-left-bl, +.x-window-header-nx-inset-left-tr, +.x-window-header-nx-inset-left-br, +.x-window-header-nx-inset-left-tc, +.x-window-header-nx-inset-left-bc, +.x-window-header-nx-inset-left-ml, +.x-window-header-nx-inset-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-left-corners.gif); +} + +.x-window-header-nx-inset-left-ml, +.x-window-header-nx-inset-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-inset-left-mc { + padding: 6px 6px 10px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-inset-left-tl, +.x-strict .x-ie7 .x-window-header-nx-inset-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-inset-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-inset-left-corners.gif), sides:url(images/window-header/window-header-nx-inset-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-inset-collapsed-top { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 8px 14px; + border-width: 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-inset-collapsed-top-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-inset-collapsed-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-inset-collapsed-top-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-8-14; +} + +.x-window-header-nx-inset-collapsed-top-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-inset-collapsed-top-tr { + background-position: right -12px; +} + +.x-window-header-nx-inset-collapsed-top-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-inset-collapsed-top-br { + background-position: right -20px; +} + +.x-window-header-nx-inset-collapsed-top-ml { + background-position: 0 top; +} + +.x-window-header-nx-inset-collapsed-top-mr { + background-position: right top; +} + +.x-window-header-nx-inset-collapsed-top-tc { + background-position: 0 0; +} + +.x-window-header-nx-inset-collapsed-top-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-inset-collapsed-top-tr, +.x-window-header-nx-inset-collapsed-top-br, +.x-window-header-nx-inset-collapsed-top-mr { + padding-right: 4px; +} + +.x-window-header-nx-inset-collapsed-top-tl, +.x-window-header-nx-inset-collapsed-top-bl, +.x-window-header-nx-inset-collapsed-top-ml { + padding-left: 4px; +} + +.x-window-header-nx-inset-collapsed-top-tc { + height: 4px; +} + +.x-window-header-nx-inset-collapsed-top-bc { + height: 4px; +} + +.x-window-header-nx-inset-collapsed-top-tl, +.x-window-header-nx-inset-collapsed-top-bl, +.x-window-header-nx-inset-collapsed-top-tr, +.x-window-header-nx-inset-collapsed-top-br, +.x-window-header-nx-inset-collapsed-top-tc, +.x-window-header-nx-inset-collapsed-top-bc, +.x-window-header-nx-inset-collapsed-top-ml, +.x-window-header-nx-inset-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-collapsed-top-corners.gif); +} + +.x-window-header-nx-inset-collapsed-top-ml, +.x-window-header-nx-inset-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-collapsed-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-inset-collapsed-top-mc { + padding: 6px 6px 4px 10px; +} + +.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-top-tl, +.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-inset-collapsed-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-inset-collapsed-top-corners.gif), sides:url(images/window-header/window-header-nx-inset-collapsed-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-inset-collapsed-right { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 14px 10px 10px 8px; + border-width: 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-inset-collapsed-right-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-inset-collapsed-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-inset-collapsed-right-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-14-10-10-8; +} + +.x-window-header-nx-inset-collapsed-right-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-inset-collapsed-right-tr { + background-position: right -12px; +} + +.x-window-header-nx-inset-collapsed-right-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-inset-collapsed-right-br { + background-position: right -20px; +} + +.x-window-header-nx-inset-collapsed-right-ml { + background-position: 0 top; +} + +.x-window-header-nx-inset-collapsed-right-mr { + background-position: right top; +} + +.x-window-header-nx-inset-collapsed-right-tc { + background-position: 0 0; +} + +.x-window-header-nx-inset-collapsed-right-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-inset-collapsed-right-tr, +.x-window-header-nx-inset-collapsed-right-br, +.x-window-header-nx-inset-collapsed-right-mr { + padding-right: 4px; +} + +.x-window-header-nx-inset-collapsed-right-tl, +.x-window-header-nx-inset-collapsed-right-bl, +.x-window-header-nx-inset-collapsed-right-ml { + padding-left: 4px; +} + +.x-window-header-nx-inset-collapsed-right-tc { + height: 4px; +} + +.x-window-header-nx-inset-collapsed-right-bc { + height: 4px; +} + +.x-window-header-nx-inset-collapsed-right-tl, +.x-window-header-nx-inset-collapsed-right-bl, +.x-window-header-nx-inset-collapsed-right-tr, +.x-window-header-nx-inset-collapsed-right-br, +.x-window-header-nx-inset-collapsed-right-tc, +.x-window-header-nx-inset-collapsed-right-bc, +.x-window-header-nx-inset-collapsed-right-ml, +.x-window-header-nx-inset-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-collapsed-right-corners.gif); +} + +.x-window-header-nx-inset-collapsed-right-ml, +.x-window-header-nx-inset-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-collapsed-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-inset-collapsed-right-mc { + padding: 10px 6px 6px 4px; +} + +.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-right-tl, +.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-inset-collapsed-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-inset-collapsed-right-corners.gif), sides:url(images/window-header/window-header-nx-inset-collapsed-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-inset-collapsed-bottom { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 8px 14px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-inset-collapsed-bottom-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-inset-collapsed-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-inset-collapsed-bottom-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-8-14-10-10; +} + +.x-window-header-nx-inset-collapsed-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-inset-collapsed-bottom-tr { + background-position: right -12px; +} + +.x-window-header-nx-inset-collapsed-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-inset-collapsed-bottom-br { + background-position: right -20px; +} + +.x-window-header-nx-inset-collapsed-bottom-ml { + background-position: 0 top; +} + +.x-window-header-nx-inset-collapsed-bottom-mr { + background-position: right top; +} + +.x-window-header-nx-inset-collapsed-bottom-tc { + background-position: 0 0; +} + +.x-window-header-nx-inset-collapsed-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-inset-collapsed-bottom-tr, +.x-window-header-nx-inset-collapsed-bottom-br, +.x-window-header-nx-inset-collapsed-bottom-mr { + padding-right: 4px; +} + +.x-window-header-nx-inset-collapsed-bottom-tl, +.x-window-header-nx-inset-collapsed-bottom-bl, +.x-window-header-nx-inset-collapsed-bottom-ml { + padding-left: 4px; +} + +.x-window-header-nx-inset-collapsed-bottom-tc { + height: 4px; +} + +.x-window-header-nx-inset-collapsed-bottom-bc { + height: 4px; +} + +.x-window-header-nx-inset-collapsed-bottom-tl, +.x-window-header-nx-inset-collapsed-bottom-bl, +.x-window-header-nx-inset-collapsed-bottom-tr, +.x-window-header-nx-inset-collapsed-bottom-br, +.x-window-header-nx-inset-collapsed-bottom-tc, +.x-window-header-nx-inset-collapsed-bottom-bc, +.x-window-header-nx-inset-collapsed-bottom-ml, +.x-window-header-nx-inset-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-collapsed-bottom-corners.gif); +} + +.x-window-header-nx-inset-collapsed-bottom-ml, +.x-window-header-nx-inset-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-collapsed-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-inset-collapsed-bottom-mc { + padding: 4px 10px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-bottom-tl, +.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-inset-collapsed-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-inset-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-nx-inset-collapsed-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-inset-collapsed-left { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 8px 14px 10px; + border-width: 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-inset-collapsed-left-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-inset-collapsed-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-inset-collapsed-left-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-8-14-10; +} + +.x-window-header-nx-inset-collapsed-left-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-inset-collapsed-left-tr { + background-position: right -12px; +} + +.x-window-header-nx-inset-collapsed-left-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-inset-collapsed-left-br { + background-position: right -20px; +} + +.x-window-header-nx-inset-collapsed-left-ml { + background-position: 0 top; +} + +.x-window-header-nx-inset-collapsed-left-mr { + background-position: right top; +} + +.x-window-header-nx-inset-collapsed-left-tc { + background-position: 0 0; +} + +.x-window-header-nx-inset-collapsed-left-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-inset-collapsed-left-tr, +.x-window-header-nx-inset-collapsed-left-br, +.x-window-header-nx-inset-collapsed-left-mr { + padding-right: 4px; +} + +.x-window-header-nx-inset-collapsed-left-tl, +.x-window-header-nx-inset-collapsed-left-bl, +.x-window-header-nx-inset-collapsed-left-ml { + padding-left: 4px; +} + +.x-window-header-nx-inset-collapsed-left-tc { + height: 4px; +} + +.x-window-header-nx-inset-collapsed-left-bc { + height: 4px; +} + +.x-window-header-nx-inset-collapsed-left-tl, +.x-window-header-nx-inset-collapsed-left-bl, +.x-window-header-nx-inset-collapsed-left-tr, +.x-window-header-nx-inset-collapsed-left-br, +.x-window-header-nx-inset-collapsed-left-tc, +.x-window-header-nx-inset-collapsed-left-bc, +.x-window-header-nx-inset-collapsed-left-ml, +.x-window-header-nx-inset-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-collapsed-left-corners.gif); +} + +.x-window-header-nx-inset-collapsed-left-ml, +.x-window-header-nx-inset-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-inset-collapsed-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-inset-collapsed-left-mc { + padding: 6px 4px 10px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-left-tl, +.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-inset-collapsed-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-inset-collapsed-left-corners.gif), sides:url(images/window-header/window-header-nx-inset-collapsed-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-inset .x-window-header-icon { + width: 16px; + height: 16px; + color: #333333; + font-size: 16px; + line-height: 16px; + background-position: center center; +} +.x-window-header-nx-inset .x-window-header-glyph { + color: #333333; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-window-header-nx-inset .x-window-header-glyph { + color: #8f8f8f; +} + +.x-window-header-nx-inset-horizontal .x-window-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-window-header-nx-inset-horizontal .x-window-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-window-header-nx-inset-vertical .x-window-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-window-header-nx-inset-vertical .x-window-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-window-header-nx-inset-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-window-header-nx-inset-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-window-header-nx-inset-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-window-header-nx-inset-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-window-header-nx-inset { + border-width: 0 !important; +} + +.x-nbr .x-window-nx-inset-collapsed .x-window-header { + border-width: 0 !important; +} + +.x-window-nx-inset-outer-border-l { + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-inset-outer-border-b { + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-inset-outer-border-bl { + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-inset-outer-border-r { + border-right-color: #606060 !important; + border-right-width: 2px !important; +} + +.x-window-nx-inset-outer-border-rl { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-inset-outer-border-rb { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-inset-outer-border-rbl { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-inset-outer-border-t { + border-top-color: #606060 !important; + border-top-width: 2px !important; +} + +.x-window-nx-inset-outer-border-tl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-inset-outer-border-tb { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-inset-outer-border-tbl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-inset-outer-border-tr { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; +} + +.x-window-nx-inset-outer-border-trl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-inset-outer-border-trb { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-inset-outer-border-trbl { + border-color: #606060 !important; + border-width: 2px !important; +} + +.x-window-nx-message-default { + border-color: #606060; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; +} + +.x-window-nx-message-default { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 2px; + border-style: solid; + background-color: white; +} + +.x-window-nx-message-default-mc { + background-color: white; +} + +.x-nbr .x-window-nx-message-default { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-nx-message-default-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-10-10-10-10; +} + +.x-window-nx-message-default-tl { + background-position: 0 -8px; +} + +.x-window-nx-message-default-tr { + background-position: right -12px; +} + +.x-window-nx-message-default-bl { + background-position: 0 -16px; +} + +.x-window-nx-message-default-br { + background-position: right -20px; +} + +.x-window-nx-message-default-ml { + background-position: 0 top; +} + +.x-window-nx-message-default-mr { + background-position: right top; +} + +.x-window-nx-message-default-tc { + background-position: 0 0; +} + +.x-window-nx-message-default-bc { + background-position: 0 -4px; +} + +.x-window-nx-message-default-tr, +.x-window-nx-message-default-br, +.x-window-nx-message-default-mr { + padding-right: 4px; +} + +.x-window-nx-message-default-tl, +.x-window-nx-message-default-bl, +.x-window-nx-message-default-ml { + padding-left: 4px; +} + +.x-window-nx-message-default-tc { + height: 4px; +} + +.x-window-nx-message-default-bc { + height: 4px; +} + +.x-window-nx-message-default-tl, +.x-window-nx-message-default-bl, +.x-window-nx-message-default-tr, +.x-window-nx-message-default-br, +.x-window-nx-message-default-tc, +.x-window-nx-message-default-bc, +.x-window-nx-message-default-ml, +.x-window-nx-message-default-mr { + zoom: 1; + background-image: url(images/window/window-nx-message-default-corners.gif); +} + +.x-window-nx-message-default-ml, +.x-window-nx-message-default-mr { + zoom: 1; + background-image: url(images/window/window-nx-message-default-sides.gif); + background-repeat: repeat-y; +} + +.x-window-nx-message-default-mc { + padding: 8px 8px 8px 8px; +} + +.x-strict .x-ie7 .x-window-nx-message-default-tl, +.x-strict .x-ie7 .x-window-nx-message-default-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-nx-message-default:after { + display: none; + content: "x-slicer:corners:url(images/window/window-nx-message-default-corners.gif), sides:url(images/window/window-nx-message-default-sides.gif)"; +} + +/**/ +/* */ +.x-window-body-nx-message-default { + border-color: #606060; + border-width: 1px; + border-style: solid; + background: white; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.x-window-header-nx-message-default { + font-size: 13px; + border-color: #606060; + zoom: 1; + background-color: #ebebeb; +} +.x-window-header-nx-message-default .x-tool-img { + background-image: url(images/tools/tool-sprites-dark.png); + background-color: #ebebeb; +} + +.x-window-header-nx-message-default-vertical .x-window-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-window-header-nx-message-default-vertical .x-window-header-text-container { + background-color: #ebebeb; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#ebebeb); +} + +.x-window-header-text-container-nx-message-default { + color: #333333; + font-weight: bold; + line-height: 15px; + font-family: arial, helvetica, verdana, sans-serif; + font-size: 13px; + padding: 1px 0 0; + text-transform: none; +} + +.x-window-header-nx-message-default-top { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 10px 10px 8px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-message-default-top-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-message-default-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-default-top-frameInfo { + font-family: dh-4-4-0-0-0-0-0-0-10-10-8-10; +} + +.x-window-header-nx-message-default-top-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-default-top-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-default-top-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-default-top-br { + background-position: right -20px; +} + +.x-window-header-nx-message-default-top-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-default-top-mr { + background-position: right top; +} + +.x-window-header-nx-message-default-top-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-default-top-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-default-top-tr, +.x-window-header-nx-message-default-top-br, +.x-window-header-nx-message-default-top-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-default-top-tl, +.x-window-header-nx-message-default-top-bl, +.x-window-header-nx-message-default-top-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-default-top-tc { + height: 4px; +} + +.x-window-header-nx-message-default-top-bc { + height: 0; +} + +.x-window-header-nx-message-default-top-tl, +.x-window-header-nx-message-default-top-bl, +.x-window-header-nx-message-default-top-tr, +.x-window-header-nx-message-default-top-br, +.x-window-header-nx-message-default-top-tc, +.x-window-header-nx-message-default-top-bc, +.x-window-header-nx-message-default-top-ml, +.x-window-header-nx-message-default-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-top-corners.gif); +} + +.x-window-header-nx-message-default-top-ml, +.x-window-header-nx-message-default-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-default-top-mc { + padding: 6px 6px 8px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-default-top-tl, +.x-strict .x-ie7 .x-window-header-nx-message-default-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-default-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-default-top-corners.gif), sides:url(images/window-header/window-header-nx-message-default-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-default-right { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 10px 10px 10px 8px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-message-default-right-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-message-default-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-default-right-frameInfo { + font-family: dh-0-4-4-0-0-0-0-0-10-10-10-8; +} + +.x-window-header-nx-message-default-right-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-default-right-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-default-right-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-default-right-br { + background-position: right -20px; +} + +.x-window-header-nx-message-default-right-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-default-right-mr { + background-position: right top; +} + +.x-window-header-nx-message-default-right-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-default-right-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-default-right-tr, +.x-window-header-nx-message-default-right-br, +.x-window-header-nx-message-default-right-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-default-right-tl, +.x-window-header-nx-message-default-right-bl, +.x-window-header-nx-message-default-right-ml { + padding-left: 0; +} + +.x-window-header-nx-message-default-right-tc { + height: 4px; +} + +.x-window-header-nx-message-default-right-bc { + height: 4px; +} + +.x-window-header-nx-message-default-right-tl, +.x-window-header-nx-message-default-right-bl, +.x-window-header-nx-message-default-right-tr, +.x-window-header-nx-message-default-right-br, +.x-window-header-nx-message-default-right-tc, +.x-window-header-nx-message-default-right-bc, +.x-window-header-nx-message-default-right-ml, +.x-window-header-nx-message-default-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-right-corners.gif); +} + +.x-window-header-nx-message-default-right-ml, +.x-window-header-nx-message-default-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-default-right-mc { + padding: 6px 6px 6px 8px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-default-right-tl, +.x-strict .x-ie7 .x-window-header-nx-message-default-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-default-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-default-right-corners.gif), sides:url(images/window-header/window-header-nx-message-default-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-default-bottom { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 8px 10px 10px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-message-default-bottom-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-message-default-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-default-bottom-frameInfo { + font-family: dh-0-0-4-4-0-0-0-0-8-10-10-10; +} + +.x-window-header-nx-message-default-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-default-bottom-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-default-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-default-bottom-br { + background-position: right -20px; +} + +.x-window-header-nx-message-default-bottom-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-default-bottom-mr { + background-position: right top; +} + +.x-window-header-nx-message-default-bottom-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-default-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-default-bottom-tr, +.x-window-header-nx-message-default-bottom-br, +.x-window-header-nx-message-default-bottom-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-default-bottom-tl, +.x-window-header-nx-message-default-bottom-bl, +.x-window-header-nx-message-default-bottom-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-default-bottom-tc { + height: 0; +} + +.x-window-header-nx-message-default-bottom-bc { + height: 4px; +} + +.x-window-header-nx-message-default-bottom-tl, +.x-window-header-nx-message-default-bottom-bl, +.x-window-header-nx-message-default-bottom-tr, +.x-window-header-nx-message-default-bottom-br, +.x-window-header-nx-message-default-bottom-tc, +.x-window-header-nx-message-default-bottom-bc, +.x-window-header-nx-message-default-bottom-ml, +.x-window-header-nx-message-default-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-bottom-corners.gif); +} + +.x-window-header-nx-message-default-bottom-ml, +.x-window-header-nx-message-default-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-default-bottom-mc { + padding: 8px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-default-bottom-tl, +.x-strict .x-ie7 .x-window-header-nx-message-default-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-default-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-default-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-default-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-default-left { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 10px 8px 10px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-message-default-left-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-message-default-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-default-left-frameInfo { + font-family: dh-4-0-0-4-0-0-0-0-10-8-10-10; +} + +.x-window-header-nx-message-default-left-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-default-left-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-default-left-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-default-left-br { + background-position: right -20px; +} + +.x-window-header-nx-message-default-left-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-default-left-mr { + background-position: right top; +} + +.x-window-header-nx-message-default-left-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-default-left-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-default-left-tr, +.x-window-header-nx-message-default-left-br, +.x-window-header-nx-message-default-left-mr { + padding-right: 0; +} + +.x-window-header-nx-message-default-left-tl, +.x-window-header-nx-message-default-left-bl, +.x-window-header-nx-message-default-left-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-default-left-tc { + height: 4px; +} + +.x-window-header-nx-message-default-left-bc { + height: 4px; +} + +.x-window-header-nx-message-default-left-tl, +.x-window-header-nx-message-default-left-bl, +.x-window-header-nx-message-default-left-tr, +.x-window-header-nx-message-default-left-br, +.x-window-header-nx-message-default-left-tc, +.x-window-header-nx-message-default-left-bc, +.x-window-header-nx-message-default-left-ml, +.x-window-header-nx-message-default-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-left-corners.gif); +} + +.x-window-header-nx-message-default-left-ml, +.x-window-header-nx-message-default-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-default-left-mc { + padding: 6px 8px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-default-left-tl, +.x-strict .x-ie7 .x-window-header-nx-message-default-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-default-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-default-left-corners.gif), sides:url(images/window-header/window-header-nx-message-default-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-default-collapsed-top { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-message-default-collapsed-top-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-message-default-collapsed-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-default-collapsed-top-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-default-collapsed-top-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-default-collapsed-top-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-default-collapsed-top-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-default-collapsed-top-br { + background-position: right -20px; +} + +.x-window-header-nx-message-default-collapsed-top-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-default-collapsed-top-mr { + background-position: right top; +} + +.x-window-header-nx-message-default-collapsed-top-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-default-collapsed-top-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-default-collapsed-top-tr, +.x-window-header-nx-message-default-collapsed-top-br, +.x-window-header-nx-message-default-collapsed-top-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-default-collapsed-top-tl, +.x-window-header-nx-message-default-collapsed-top-bl, +.x-window-header-nx-message-default-collapsed-top-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-default-collapsed-top-tc { + height: 4px; +} + +.x-window-header-nx-message-default-collapsed-top-bc { + height: 4px; +} + +.x-window-header-nx-message-default-collapsed-top-tl, +.x-window-header-nx-message-default-collapsed-top-bl, +.x-window-header-nx-message-default-collapsed-top-tr, +.x-window-header-nx-message-default-collapsed-top-br, +.x-window-header-nx-message-default-collapsed-top-tc, +.x-window-header-nx-message-default-collapsed-top-bc, +.x-window-header-nx-message-default-collapsed-top-ml, +.x-window-header-nx-message-default-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-collapsed-top-corners.gif); +} + +.x-window-header-nx-message-default-collapsed-top-ml, +.x-window-header-nx-message-default-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-collapsed-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-default-collapsed-top-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-top-tl, +.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-default-collapsed-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-default-collapsed-top-corners.gif), sides:url(images/window-header/window-header-nx-message-default-collapsed-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-default-collapsed-right { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-message-default-collapsed-right-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-message-default-collapsed-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-default-collapsed-right-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-default-collapsed-right-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-default-collapsed-right-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-default-collapsed-right-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-default-collapsed-right-br { + background-position: right -20px; +} + +.x-window-header-nx-message-default-collapsed-right-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-default-collapsed-right-mr { + background-position: right top; +} + +.x-window-header-nx-message-default-collapsed-right-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-default-collapsed-right-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-default-collapsed-right-tr, +.x-window-header-nx-message-default-collapsed-right-br, +.x-window-header-nx-message-default-collapsed-right-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-default-collapsed-right-tl, +.x-window-header-nx-message-default-collapsed-right-bl, +.x-window-header-nx-message-default-collapsed-right-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-default-collapsed-right-tc { + height: 4px; +} + +.x-window-header-nx-message-default-collapsed-right-bc { + height: 4px; +} + +.x-window-header-nx-message-default-collapsed-right-tl, +.x-window-header-nx-message-default-collapsed-right-bl, +.x-window-header-nx-message-default-collapsed-right-tr, +.x-window-header-nx-message-default-collapsed-right-br, +.x-window-header-nx-message-default-collapsed-right-tc, +.x-window-header-nx-message-default-collapsed-right-bc, +.x-window-header-nx-message-default-collapsed-right-ml, +.x-window-header-nx-message-default-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-collapsed-right-corners.gif); +} + +.x-window-header-nx-message-default-collapsed-right-ml, +.x-window-header-nx-message-default-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-collapsed-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-default-collapsed-right-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-right-tl, +.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-default-collapsed-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-default-collapsed-right-corners.gif), sides:url(images/window-header/window-header-nx-message-default-collapsed-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-default-collapsed-bottom { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-message-default-collapsed-bottom-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-message-default-collapsed-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-default-collapsed-bottom-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-default-collapsed-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-default-collapsed-bottom-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-default-collapsed-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-default-collapsed-bottom-br { + background-position: right -20px; +} + +.x-window-header-nx-message-default-collapsed-bottom-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-default-collapsed-bottom-mr { + background-position: right top; +} + +.x-window-header-nx-message-default-collapsed-bottom-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-default-collapsed-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-default-collapsed-bottom-tr, +.x-window-header-nx-message-default-collapsed-bottom-br, +.x-window-header-nx-message-default-collapsed-bottom-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-default-collapsed-bottom-tl, +.x-window-header-nx-message-default-collapsed-bottom-bl, +.x-window-header-nx-message-default-collapsed-bottom-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-default-collapsed-bottom-tc { + height: 4px; +} + +.x-window-header-nx-message-default-collapsed-bottom-bc { + height: 4px; +} + +.x-window-header-nx-message-default-collapsed-bottom-tl, +.x-window-header-nx-message-default-collapsed-bottom-bl, +.x-window-header-nx-message-default-collapsed-bottom-tr, +.x-window-header-nx-message-default-collapsed-bottom-br, +.x-window-header-nx-message-default-collapsed-bottom-tc, +.x-window-header-nx-message-default-collapsed-bottom-bc, +.x-window-header-nx-message-default-collapsed-bottom-ml, +.x-window-header-nx-message-default-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-collapsed-bottom-corners.gif); +} + +.x-window-header-nx-message-default-collapsed-bottom-ml, +.x-window-header-nx-message-default-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-collapsed-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-default-collapsed-bottom-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-bottom-tl, +.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-default-collapsed-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-default-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-default-collapsed-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-default-collapsed-left { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #ebebeb; +} + +.x-window-header-nx-message-default-collapsed-left-mc { + background-color: #ebebeb; +} + +.x-nbr .x-window-header-nx-message-default-collapsed-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-default-collapsed-left-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-default-collapsed-left-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-default-collapsed-left-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-default-collapsed-left-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-default-collapsed-left-br { + background-position: right -20px; +} + +.x-window-header-nx-message-default-collapsed-left-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-default-collapsed-left-mr { + background-position: right top; +} + +.x-window-header-nx-message-default-collapsed-left-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-default-collapsed-left-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-default-collapsed-left-tr, +.x-window-header-nx-message-default-collapsed-left-br, +.x-window-header-nx-message-default-collapsed-left-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-default-collapsed-left-tl, +.x-window-header-nx-message-default-collapsed-left-bl, +.x-window-header-nx-message-default-collapsed-left-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-default-collapsed-left-tc { + height: 4px; +} + +.x-window-header-nx-message-default-collapsed-left-bc { + height: 4px; +} + +.x-window-header-nx-message-default-collapsed-left-tl, +.x-window-header-nx-message-default-collapsed-left-bl, +.x-window-header-nx-message-default-collapsed-left-tr, +.x-window-header-nx-message-default-collapsed-left-br, +.x-window-header-nx-message-default-collapsed-left-tc, +.x-window-header-nx-message-default-collapsed-left-bc, +.x-window-header-nx-message-default-collapsed-left-ml, +.x-window-header-nx-message-default-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-collapsed-left-corners.gif); +} + +.x-window-header-nx-message-default-collapsed-left-ml, +.x-window-header-nx-message-default-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-default-collapsed-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-default-collapsed-left-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-left-tl, +.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-default-collapsed-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-default-collapsed-left-corners.gif), sides:url(images/window-header/window-header-nx-message-default-collapsed-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-default .x-window-header-icon { + width: 16px; + height: 16px; + color: #333333; + font-size: 16px; + line-height: 16px; + background-position: center center; +} +.x-window-header-nx-message-default .x-window-header-glyph { + color: #333333; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-window-header-nx-message-default .x-window-header-glyph { + color: #8f8f8f; +} + +.x-window-header-nx-message-default-horizontal .x-window-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-window-header-nx-message-default-horizontal .x-window-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-window-header-nx-message-default-vertical .x-window-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-window-header-nx-message-default-vertical .x-window-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-window-header-nx-message-default-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-window-header-nx-message-default-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-window-header-nx-message-default-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-window-header-nx-message-default-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-window-header-nx-message-default { + border-width: 0 !important; +} + +.x-nbr .x-window-nx-message-default-collapsed .x-window-header { + border-width: 0 !important; +} + +.x-window-nx-message-default-outer-border-l { + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-b { + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-bl { + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-r { + border-right-color: #606060 !important; + border-right-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-rl { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-rb { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-rbl { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-t { + border-top-color: #606060 !important; + border-top-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-tl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-tb { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-tbl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-tr { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-trl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-trb { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-default-outer-border-trbl { + border-color: #606060 !important; + border-width: 2px !important; +} + +.x-window-nx-message-primary { + border-color: #606060; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; +} + +.x-window-nx-message-primary { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 2px; + border-style: solid; + background-color: white; +} + +.x-window-nx-message-primary-mc { + background-color: white; +} + +.x-nbr .x-window-nx-message-primary { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-nx-message-primary-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-10-10-10-10; +} + +.x-window-nx-message-primary-tl { + background-position: 0 -8px; +} + +.x-window-nx-message-primary-tr { + background-position: right -12px; +} + +.x-window-nx-message-primary-bl { + background-position: 0 -16px; +} + +.x-window-nx-message-primary-br { + background-position: right -20px; +} + +.x-window-nx-message-primary-ml { + background-position: 0 top; +} + +.x-window-nx-message-primary-mr { + background-position: right top; +} + +.x-window-nx-message-primary-tc { + background-position: 0 0; +} + +.x-window-nx-message-primary-bc { + background-position: 0 -4px; +} + +.x-window-nx-message-primary-tr, +.x-window-nx-message-primary-br, +.x-window-nx-message-primary-mr { + padding-right: 4px; +} + +.x-window-nx-message-primary-tl, +.x-window-nx-message-primary-bl, +.x-window-nx-message-primary-ml { + padding-left: 4px; +} + +.x-window-nx-message-primary-tc { + height: 4px; +} + +.x-window-nx-message-primary-bc { + height: 4px; +} + +.x-window-nx-message-primary-tl, +.x-window-nx-message-primary-bl, +.x-window-nx-message-primary-tr, +.x-window-nx-message-primary-br, +.x-window-nx-message-primary-tc, +.x-window-nx-message-primary-bc, +.x-window-nx-message-primary-ml, +.x-window-nx-message-primary-mr { + zoom: 1; + background-image: url(images/window/window-nx-message-primary-corners.gif); +} + +.x-window-nx-message-primary-ml, +.x-window-nx-message-primary-mr { + zoom: 1; + background-image: url(images/window/window-nx-message-primary-sides.gif); + background-repeat: repeat-y; +} + +.x-window-nx-message-primary-mc { + padding: 8px 8px 8px 8px; +} + +.x-strict .x-ie7 .x-window-nx-message-primary-tl, +.x-strict .x-ie7 .x-window-nx-message-primary-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-nx-message-primary:after { + display: none; + content: "x-slicer:corners:url(images/window/window-nx-message-primary-corners.gif), sides:url(images/window/window-nx-message-primary-sides.gif)"; +} + +/**/ +/* */ +.x-window-body-nx-message-primary { + border-color: #606060; + border-width: 1px; + border-style: solid; + background: white; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.x-window-header-nx-message-primary { + font-size: 13px; + border-color: #606060; + zoom: 1; + background-color: #0047b2; +} +.x-window-header-nx-message-primary .x-tool-img { + background-color: #0047b2; +} + +.x-window-header-nx-message-primary-vertical .x-window-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-window-header-nx-message-primary-vertical .x-window-header-text-container { + background-color: #0047b2; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#0047b2); +} + +.x-window-header-text-container-nx-message-primary { + color: white; + font-weight: bold; + line-height: 15px; + font-family: arial, helvetica, verdana, sans-serif; + font-size: 13px; + padding: 1px 0 0; + text-transform: none; +} + +.x-window-header-nx-message-primary-top { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 10px 10px 8px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #0047b2; +} + +.x-window-header-nx-message-primary-top-mc { + background-color: #0047b2; +} + +.x-nbr .x-window-header-nx-message-primary-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-primary-top-frameInfo { + font-family: dh-4-4-0-0-0-0-0-0-10-10-8-10; +} + +.x-window-header-nx-message-primary-top-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-primary-top-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-primary-top-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-primary-top-br { + background-position: right -20px; +} + +.x-window-header-nx-message-primary-top-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-primary-top-mr { + background-position: right top; +} + +.x-window-header-nx-message-primary-top-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-primary-top-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-primary-top-tr, +.x-window-header-nx-message-primary-top-br, +.x-window-header-nx-message-primary-top-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-primary-top-tl, +.x-window-header-nx-message-primary-top-bl, +.x-window-header-nx-message-primary-top-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-primary-top-tc { + height: 4px; +} + +.x-window-header-nx-message-primary-top-bc { + height: 0; +} + +.x-window-header-nx-message-primary-top-tl, +.x-window-header-nx-message-primary-top-bl, +.x-window-header-nx-message-primary-top-tr, +.x-window-header-nx-message-primary-top-br, +.x-window-header-nx-message-primary-top-tc, +.x-window-header-nx-message-primary-top-bc, +.x-window-header-nx-message-primary-top-ml, +.x-window-header-nx-message-primary-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-top-corners.gif); +} + +.x-window-header-nx-message-primary-top-ml, +.x-window-header-nx-message-primary-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-primary-top-mc { + padding: 6px 6px 8px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-primary-top-tl, +.x-strict .x-ie7 .x-window-header-nx-message-primary-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-primary-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-primary-top-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-primary-right { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 10px 10px 10px 8px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #0047b2; +} + +.x-window-header-nx-message-primary-right-mc { + background-color: #0047b2; +} + +.x-nbr .x-window-header-nx-message-primary-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-primary-right-frameInfo { + font-family: dh-0-4-4-0-0-0-0-0-10-10-10-8; +} + +.x-window-header-nx-message-primary-right-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-primary-right-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-primary-right-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-primary-right-br { + background-position: right -20px; +} + +.x-window-header-nx-message-primary-right-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-primary-right-mr { + background-position: right top; +} + +.x-window-header-nx-message-primary-right-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-primary-right-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-primary-right-tr, +.x-window-header-nx-message-primary-right-br, +.x-window-header-nx-message-primary-right-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-primary-right-tl, +.x-window-header-nx-message-primary-right-bl, +.x-window-header-nx-message-primary-right-ml { + padding-left: 0; +} + +.x-window-header-nx-message-primary-right-tc { + height: 4px; +} + +.x-window-header-nx-message-primary-right-bc { + height: 4px; +} + +.x-window-header-nx-message-primary-right-tl, +.x-window-header-nx-message-primary-right-bl, +.x-window-header-nx-message-primary-right-tr, +.x-window-header-nx-message-primary-right-br, +.x-window-header-nx-message-primary-right-tc, +.x-window-header-nx-message-primary-right-bc, +.x-window-header-nx-message-primary-right-ml, +.x-window-header-nx-message-primary-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-right-corners.gif); +} + +.x-window-header-nx-message-primary-right-ml, +.x-window-header-nx-message-primary-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-primary-right-mc { + padding: 6px 6px 6px 8px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-primary-right-tl, +.x-strict .x-ie7 .x-window-header-nx-message-primary-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-primary-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-primary-right-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-primary-bottom { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 8px 10px 10px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #0047b2; +} + +.x-window-header-nx-message-primary-bottom-mc { + background-color: #0047b2; +} + +.x-nbr .x-window-header-nx-message-primary-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-primary-bottom-frameInfo { + font-family: dh-0-0-4-4-0-0-0-0-8-10-10-10; +} + +.x-window-header-nx-message-primary-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-primary-bottom-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-primary-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-primary-bottom-br { + background-position: right -20px; +} + +.x-window-header-nx-message-primary-bottom-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-primary-bottom-mr { + background-position: right top; +} + +.x-window-header-nx-message-primary-bottom-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-primary-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-primary-bottom-tr, +.x-window-header-nx-message-primary-bottom-br, +.x-window-header-nx-message-primary-bottom-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-primary-bottom-tl, +.x-window-header-nx-message-primary-bottom-bl, +.x-window-header-nx-message-primary-bottom-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-primary-bottom-tc { + height: 0; +} + +.x-window-header-nx-message-primary-bottom-bc { + height: 4px; +} + +.x-window-header-nx-message-primary-bottom-tl, +.x-window-header-nx-message-primary-bottom-bl, +.x-window-header-nx-message-primary-bottom-tr, +.x-window-header-nx-message-primary-bottom-br, +.x-window-header-nx-message-primary-bottom-tc, +.x-window-header-nx-message-primary-bottom-bc, +.x-window-header-nx-message-primary-bottom-ml, +.x-window-header-nx-message-primary-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-bottom-corners.gif); +} + +.x-window-header-nx-message-primary-bottom-ml, +.x-window-header-nx-message-primary-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-primary-bottom-mc { + padding: 8px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-primary-bottom-tl, +.x-strict .x-ie7 .x-window-header-nx-message-primary-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-primary-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-primary-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-primary-left { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 10px 8px 10px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #0047b2; +} + +.x-window-header-nx-message-primary-left-mc { + background-color: #0047b2; +} + +.x-nbr .x-window-header-nx-message-primary-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-primary-left-frameInfo { + font-family: dh-4-0-0-4-0-0-0-0-10-8-10-10; +} + +.x-window-header-nx-message-primary-left-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-primary-left-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-primary-left-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-primary-left-br { + background-position: right -20px; +} + +.x-window-header-nx-message-primary-left-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-primary-left-mr { + background-position: right top; +} + +.x-window-header-nx-message-primary-left-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-primary-left-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-primary-left-tr, +.x-window-header-nx-message-primary-left-br, +.x-window-header-nx-message-primary-left-mr { + padding-right: 0; +} + +.x-window-header-nx-message-primary-left-tl, +.x-window-header-nx-message-primary-left-bl, +.x-window-header-nx-message-primary-left-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-primary-left-tc { + height: 4px; +} + +.x-window-header-nx-message-primary-left-bc { + height: 4px; +} + +.x-window-header-nx-message-primary-left-tl, +.x-window-header-nx-message-primary-left-bl, +.x-window-header-nx-message-primary-left-tr, +.x-window-header-nx-message-primary-left-br, +.x-window-header-nx-message-primary-left-tc, +.x-window-header-nx-message-primary-left-bc, +.x-window-header-nx-message-primary-left-ml, +.x-window-header-nx-message-primary-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-left-corners.gif); +} + +.x-window-header-nx-message-primary-left-ml, +.x-window-header-nx-message-primary-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-primary-left-mc { + padding: 6px 8px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-primary-left-tl, +.x-strict .x-ie7 .x-window-header-nx-message-primary-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-primary-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-primary-left-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-primary-collapsed-top { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #0047b2; +} + +.x-window-header-nx-message-primary-collapsed-top-mc { + background-color: #0047b2; +} + +.x-nbr .x-window-header-nx-message-primary-collapsed-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-primary-collapsed-top-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-primary-collapsed-top-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-primary-collapsed-top-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-primary-collapsed-top-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-primary-collapsed-top-br { + background-position: right -20px; +} + +.x-window-header-nx-message-primary-collapsed-top-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-primary-collapsed-top-mr { + background-position: right top; +} + +.x-window-header-nx-message-primary-collapsed-top-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-primary-collapsed-top-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-primary-collapsed-top-tr, +.x-window-header-nx-message-primary-collapsed-top-br, +.x-window-header-nx-message-primary-collapsed-top-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-primary-collapsed-top-tl, +.x-window-header-nx-message-primary-collapsed-top-bl, +.x-window-header-nx-message-primary-collapsed-top-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-primary-collapsed-top-tc { + height: 4px; +} + +.x-window-header-nx-message-primary-collapsed-top-bc { + height: 4px; +} + +.x-window-header-nx-message-primary-collapsed-top-tl, +.x-window-header-nx-message-primary-collapsed-top-bl, +.x-window-header-nx-message-primary-collapsed-top-tr, +.x-window-header-nx-message-primary-collapsed-top-br, +.x-window-header-nx-message-primary-collapsed-top-tc, +.x-window-header-nx-message-primary-collapsed-top-bc, +.x-window-header-nx-message-primary-collapsed-top-ml, +.x-window-header-nx-message-primary-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-collapsed-top-corners.gif); +} + +.x-window-header-nx-message-primary-collapsed-top-ml, +.x-window-header-nx-message-primary-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-collapsed-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-primary-collapsed-top-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-top-tl, +.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-primary-collapsed-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-primary-collapsed-top-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-collapsed-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-primary-collapsed-right { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #0047b2; +} + +.x-window-header-nx-message-primary-collapsed-right-mc { + background-color: #0047b2; +} + +.x-nbr .x-window-header-nx-message-primary-collapsed-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-primary-collapsed-right-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-primary-collapsed-right-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-primary-collapsed-right-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-primary-collapsed-right-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-primary-collapsed-right-br { + background-position: right -20px; +} + +.x-window-header-nx-message-primary-collapsed-right-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-primary-collapsed-right-mr { + background-position: right top; +} + +.x-window-header-nx-message-primary-collapsed-right-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-primary-collapsed-right-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-primary-collapsed-right-tr, +.x-window-header-nx-message-primary-collapsed-right-br, +.x-window-header-nx-message-primary-collapsed-right-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-primary-collapsed-right-tl, +.x-window-header-nx-message-primary-collapsed-right-bl, +.x-window-header-nx-message-primary-collapsed-right-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-primary-collapsed-right-tc { + height: 4px; +} + +.x-window-header-nx-message-primary-collapsed-right-bc { + height: 4px; +} + +.x-window-header-nx-message-primary-collapsed-right-tl, +.x-window-header-nx-message-primary-collapsed-right-bl, +.x-window-header-nx-message-primary-collapsed-right-tr, +.x-window-header-nx-message-primary-collapsed-right-br, +.x-window-header-nx-message-primary-collapsed-right-tc, +.x-window-header-nx-message-primary-collapsed-right-bc, +.x-window-header-nx-message-primary-collapsed-right-ml, +.x-window-header-nx-message-primary-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-collapsed-right-corners.gif); +} + +.x-window-header-nx-message-primary-collapsed-right-ml, +.x-window-header-nx-message-primary-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-collapsed-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-primary-collapsed-right-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-right-tl, +.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-primary-collapsed-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-primary-collapsed-right-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-collapsed-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-primary-collapsed-bottom { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #0047b2; +} + +.x-window-header-nx-message-primary-collapsed-bottom-mc { + background-color: #0047b2; +} + +.x-nbr .x-window-header-nx-message-primary-collapsed-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-primary-collapsed-bottom-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-primary-collapsed-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-primary-collapsed-bottom-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-primary-collapsed-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-primary-collapsed-bottom-br { + background-position: right -20px; +} + +.x-window-header-nx-message-primary-collapsed-bottom-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-primary-collapsed-bottom-mr { + background-position: right top; +} + +.x-window-header-nx-message-primary-collapsed-bottom-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-primary-collapsed-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-primary-collapsed-bottom-tr, +.x-window-header-nx-message-primary-collapsed-bottom-br, +.x-window-header-nx-message-primary-collapsed-bottom-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-primary-collapsed-bottom-tl, +.x-window-header-nx-message-primary-collapsed-bottom-bl, +.x-window-header-nx-message-primary-collapsed-bottom-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-primary-collapsed-bottom-tc { + height: 4px; +} + +.x-window-header-nx-message-primary-collapsed-bottom-bc { + height: 4px; +} + +.x-window-header-nx-message-primary-collapsed-bottom-tl, +.x-window-header-nx-message-primary-collapsed-bottom-bl, +.x-window-header-nx-message-primary-collapsed-bottom-tr, +.x-window-header-nx-message-primary-collapsed-bottom-br, +.x-window-header-nx-message-primary-collapsed-bottom-tc, +.x-window-header-nx-message-primary-collapsed-bottom-bc, +.x-window-header-nx-message-primary-collapsed-bottom-ml, +.x-window-header-nx-message-primary-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-collapsed-bottom-corners.gif); +} + +.x-window-header-nx-message-primary-collapsed-bottom-ml, +.x-window-header-nx-message-primary-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-collapsed-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-primary-collapsed-bottom-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-bottom-tl, +.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-primary-collapsed-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-primary-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-collapsed-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-primary-collapsed-left { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #0047b2; +} + +.x-window-header-nx-message-primary-collapsed-left-mc { + background-color: #0047b2; +} + +.x-nbr .x-window-header-nx-message-primary-collapsed-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-primary-collapsed-left-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-primary-collapsed-left-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-primary-collapsed-left-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-primary-collapsed-left-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-primary-collapsed-left-br { + background-position: right -20px; +} + +.x-window-header-nx-message-primary-collapsed-left-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-primary-collapsed-left-mr { + background-position: right top; +} + +.x-window-header-nx-message-primary-collapsed-left-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-primary-collapsed-left-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-primary-collapsed-left-tr, +.x-window-header-nx-message-primary-collapsed-left-br, +.x-window-header-nx-message-primary-collapsed-left-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-primary-collapsed-left-tl, +.x-window-header-nx-message-primary-collapsed-left-bl, +.x-window-header-nx-message-primary-collapsed-left-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-primary-collapsed-left-tc { + height: 4px; +} + +.x-window-header-nx-message-primary-collapsed-left-bc { + height: 4px; +} + +.x-window-header-nx-message-primary-collapsed-left-tl, +.x-window-header-nx-message-primary-collapsed-left-bl, +.x-window-header-nx-message-primary-collapsed-left-tr, +.x-window-header-nx-message-primary-collapsed-left-br, +.x-window-header-nx-message-primary-collapsed-left-tc, +.x-window-header-nx-message-primary-collapsed-left-bc, +.x-window-header-nx-message-primary-collapsed-left-ml, +.x-window-header-nx-message-primary-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-collapsed-left-corners.gif); +} + +.x-window-header-nx-message-primary-collapsed-left-ml, +.x-window-header-nx-message-primary-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-primary-collapsed-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-primary-collapsed-left-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-left-tl, +.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-primary-collapsed-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-primary-collapsed-left-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-collapsed-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-primary .x-window-header-icon { + width: 16px; + height: 16px; + color: #333333; + font-size: 16px; + line-height: 16px; + background-position: center center; +} +.x-window-header-nx-message-primary .x-window-header-glyph { + color: #333333; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-window-header-nx-message-primary .x-window-header-glyph { + color: #193d72; +} + +.x-window-header-nx-message-primary-horizontal .x-window-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-window-header-nx-message-primary-horizontal .x-window-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-window-header-nx-message-primary-vertical .x-window-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-window-header-nx-message-primary-vertical .x-window-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-window-header-nx-message-primary-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-window-header-nx-message-primary-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-window-header-nx-message-primary-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-window-header-nx-message-primary-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-window-header-nx-message-primary { + border-width: 0 !important; +} + +.x-nbr .x-window-nx-message-primary-collapsed .x-window-header { + border-width: 0 !important; +} + +.x-window-nx-message-primary-outer-border-l { + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-b { + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-bl { + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-r { + border-right-color: #606060 !important; + border-right-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-rl { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-rb { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-rbl { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-t { + border-top-color: #606060 !important; + border-top-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-tl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-tb { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-tbl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-tr { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-trl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-trb { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-primary-outer-border-trbl { + border-color: #606060 !important; + border-width: 2px !important; +} + +.x-window-nx-message-danger { + border-color: #606060; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; +} + +.x-window-nx-message-danger { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 2px; + border-style: solid; + background-color: white; +} + +.x-window-nx-message-danger-mc { + background-color: white; +} + +.x-nbr .x-window-nx-message-danger { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-nx-message-danger-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-10-10-10-10; +} + +.x-window-nx-message-danger-tl { + background-position: 0 -8px; +} + +.x-window-nx-message-danger-tr { + background-position: right -12px; +} + +.x-window-nx-message-danger-bl { + background-position: 0 -16px; +} + +.x-window-nx-message-danger-br { + background-position: right -20px; +} + +.x-window-nx-message-danger-ml { + background-position: 0 top; +} + +.x-window-nx-message-danger-mr { + background-position: right top; +} + +.x-window-nx-message-danger-tc { + background-position: 0 0; +} + +.x-window-nx-message-danger-bc { + background-position: 0 -4px; +} + +.x-window-nx-message-danger-tr, +.x-window-nx-message-danger-br, +.x-window-nx-message-danger-mr { + padding-right: 4px; +} + +.x-window-nx-message-danger-tl, +.x-window-nx-message-danger-bl, +.x-window-nx-message-danger-ml { + padding-left: 4px; +} + +.x-window-nx-message-danger-tc { + height: 4px; +} + +.x-window-nx-message-danger-bc { + height: 4px; +} + +.x-window-nx-message-danger-tl, +.x-window-nx-message-danger-bl, +.x-window-nx-message-danger-tr, +.x-window-nx-message-danger-br, +.x-window-nx-message-danger-tc, +.x-window-nx-message-danger-bc, +.x-window-nx-message-danger-ml, +.x-window-nx-message-danger-mr { + zoom: 1; + background-image: url(images/window/window-nx-message-danger-corners.gif); +} + +.x-window-nx-message-danger-ml, +.x-window-nx-message-danger-mr { + zoom: 1; + background-image: url(images/window/window-nx-message-danger-sides.gif); + background-repeat: repeat-y; +} + +.x-window-nx-message-danger-mc { + padding: 8px 8px 8px 8px; +} + +.x-strict .x-ie7 .x-window-nx-message-danger-tl, +.x-strict .x-ie7 .x-window-nx-message-danger-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-nx-message-danger:after { + display: none; + content: "x-slicer:corners:url(images/window/window-nx-message-danger-corners.gif), sides:url(images/window/window-nx-message-danger-sides.gif)"; +} + +/**/ +/* */ +.x-window-body-nx-message-danger { + border-color: #606060; + border-width: 1px; + border-style: solid; + background: white; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.x-window-header-nx-message-danger { + font-size: 13px; + border-color: #606060; + zoom: 1; + background-color: #db2852; +} +.x-window-header-nx-message-danger .x-tool-img { + background-color: #db2852; +} + +.x-window-header-nx-message-danger-vertical .x-window-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-window-header-nx-message-danger-vertical .x-window-header-text-container { + background-color: #db2852; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#db2852); +} + +.x-window-header-text-container-nx-message-danger { + color: white; + font-weight: bold; + line-height: 15px; + font-family: arial, helvetica, verdana, sans-serif; + font-size: 13px; + padding: 1px 0 0; + text-transform: none; +} + +.x-window-header-nx-message-danger-top { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 10px 10px 8px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #db2852; +} + +.x-window-header-nx-message-danger-top-mc { + background-color: #db2852; +} + +.x-nbr .x-window-header-nx-message-danger-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-danger-top-frameInfo { + font-family: dh-4-4-0-0-0-0-0-0-10-10-8-10; +} + +.x-window-header-nx-message-danger-top-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-danger-top-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-danger-top-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-danger-top-br { + background-position: right -20px; +} + +.x-window-header-nx-message-danger-top-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-danger-top-mr { + background-position: right top; +} + +.x-window-header-nx-message-danger-top-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-danger-top-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-danger-top-tr, +.x-window-header-nx-message-danger-top-br, +.x-window-header-nx-message-danger-top-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-danger-top-tl, +.x-window-header-nx-message-danger-top-bl, +.x-window-header-nx-message-danger-top-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-danger-top-tc { + height: 4px; +} + +.x-window-header-nx-message-danger-top-bc { + height: 0; +} + +.x-window-header-nx-message-danger-top-tl, +.x-window-header-nx-message-danger-top-bl, +.x-window-header-nx-message-danger-top-tr, +.x-window-header-nx-message-danger-top-br, +.x-window-header-nx-message-danger-top-tc, +.x-window-header-nx-message-danger-top-bc, +.x-window-header-nx-message-danger-top-ml, +.x-window-header-nx-message-danger-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-top-corners.gif); +} + +.x-window-header-nx-message-danger-top-ml, +.x-window-header-nx-message-danger-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-danger-top-mc { + padding: 6px 6px 8px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-danger-top-tl, +.x-strict .x-ie7 .x-window-header-nx-message-danger-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-danger-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-danger-top-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-danger-right { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 10px 10px 10px 8px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #db2852; +} + +.x-window-header-nx-message-danger-right-mc { + background-color: #db2852; +} + +.x-nbr .x-window-header-nx-message-danger-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-danger-right-frameInfo { + font-family: dh-0-4-4-0-0-0-0-0-10-10-10-8; +} + +.x-window-header-nx-message-danger-right-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-danger-right-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-danger-right-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-danger-right-br { + background-position: right -20px; +} + +.x-window-header-nx-message-danger-right-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-danger-right-mr { + background-position: right top; +} + +.x-window-header-nx-message-danger-right-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-danger-right-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-danger-right-tr, +.x-window-header-nx-message-danger-right-br, +.x-window-header-nx-message-danger-right-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-danger-right-tl, +.x-window-header-nx-message-danger-right-bl, +.x-window-header-nx-message-danger-right-ml { + padding-left: 0; +} + +.x-window-header-nx-message-danger-right-tc { + height: 4px; +} + +.x-window-header-nx-message-danger-right-bc { + height: 4px; +} + +.x-window-header-nx-message-danger-right-tl, +.x-window-header-nx-message-danger-right-bl, +.x-window-header-nx-message-danger-right-tr, +.x-window-header-nx-message-danger-right-br, +.x-window-header-nx-message-danger-right-tc, +.x-window-header-nx-message-danger-right-bc, +.x-window-header-nx-message-danger-right-ml, +.x-window-header-nx-message-danger-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-right-corners.gif); +} + +.x-window-header-nx-message-danger-right-ml, +.x-window-header-nx-message-danger-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-danger-right-mc { + padding: 6px 6px 6px 8px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-danger-right-tl, +.x-strict .x-ie7 .x-window-header-nx-message-danger-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-danger-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-danger-right-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-danger-bottom { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 8px 10px 10px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #db2852; +} + +.x-window-header-nx-message-danger-bottom-mc { + background-color: #db2852; +} + +.x-nbr .x-window-header-nx-message-danger-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-danger-bottom-frameInfo { + font-family: dh-0-0-4-4-0-0-0-0-8-10-10-10; +} + +.x-window-header-nx-message-danger-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-danger-bottom-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-danger-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-danger-bottom-br { + background-position: right -20px; +} + +.x-window-header-nx-message-danger-bottom-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-danger-bottom-mr { + background-position: right top; +} + +.x-window-header-nx-message-danger-bottom-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-danger-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-danger-bottom-tr, +.x-window-header-nx-message-danger-bottom-br, +.x-window-header-nx-message-danger-bottom-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-danger-bottom-tl, +.x-window-header-nx-message-danger-bottom-bl, +.x-window-header-nx-message-danger-bottom-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-danger-bottom-tc { + height: 0; +} + +.x-window-header-nx-message-danger-bottom-bc { + height: 4px; +} + +.x-window-header-nx-message-danger-bottom-tl, +.x-window-header-nx-message-danger-bottom-bl, +.x-window-header-nx-message-danger-bottom-tr, +.x-window-header-nx-message-danger-bottom-br, +.x-window-header-nx-message-danger-bottom-tc, +.x-window-header-nx-message-danger-bottom-bc, +.x-window-header-nx-message-danger-bottom-ml, +.x-window-header-nx-message-danger-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-bottom-corners.gif); +} + +.x-window-header-nx-message-danger-bottom-ml, +.x-window-header-nx-message-danger-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-danger-bottom-mc { + padding: 8px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-danger-bottom-tl, +.x-strict .x-ie7 .x-window-header-nx-message-danger-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-danger-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-danger-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-danger-left { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 10px 8px 10px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #db2852; +} + +.x-window-header-nx-message-danger-left-mc { + background-color: #db2852; +} + +.x-nbr .x-window-header-nx-message-danger-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-danger-left-frameInfo { + font-family: dh-4-0-0-4-0-0-0-0-10-8-10-10; +} + +.x-window-header-nx-message-danger-left-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-danger-left-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-danger-left-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-danger-left-br { + background-position: right -20px; +} + +.x-window-header-nx-message-danger-left-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-danger-left-mr { + background-position: right top; +} + +.x-window-header-nx-message-danger-left-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-danger-left-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-danger-left-tr, +.x-window-header-nx-message-danger-left-br, +.x-window-header-nx-message-danger-left-mr { + padding-right: 0; +} + +.x-window-header-nx-message-danger-left-tl, +.x-window-header-nx-message-danger-left-bl, +.x-window-header-nx-message-danger-left-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-danger-left-tc { + height: 4px; +} + +.x-window-header-nx-message-danger-left-bc { + height: 4px; +} + +.x-window-header-nx-message-danger-left-tl, +.x-window-header-nx-message-danger-left-bl, +.x-window-header-nx-message-danger-left-tr, +.x-window-header-nx-message-danger-left-br, +.x-window-header-nx-message-danger-left-tc, +.x-window-header-nx-message-danger-left-bc, +.x-window-header-nx-message-danger-left-ml, +.x-window-header-nx-message-danger-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-left-corners.gif); +} + +.x-window-header-nx-message-danger-left-ml, +.x-window-header-nx-message-danger-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-danger-left-mc { + padding: 6px 8px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-danger-left-tl, +.x-strict .x-ie7 .x-window-header-nx-message-danger-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-danger-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-danger-left-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-danger-collapsed-top { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #db2852; +} + +.x-window-header-nx-message-danger-collapsed-top-mc { + background-color: #db2852; +} + +.x-nbr .x-window-header-nx-message-danger-collapsed-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-danger-collapsed-top-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-danger-collapsed-top-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-danger-collapsed-top-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-danger-collapsed-top-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-danger-collapsed-top-br { + background-position: right -20px; +} + +.x-window-header-nx-message-danger-collapsed-top-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-danger-collapsed-top-mr { + background-position: right top; +} + +.x-window-header-nx-message-danger-collapsed-top-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-danger-collapsed-top-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-danger-collapsed-top-tr, +.x-window-header-nx-message-danger-collapsed-top-br, +.x-window-header-nx-message-danger-collapsed-top-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-danger-collapsed-top-tl, +.x-window-header-nx-message-danger-collapsed-top-bl, +.x-window-header-nx-message-danger-collapsed-top-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-danger-collapsed-top-tc { + height: 4px; +} + +.x-window-header-nx-message-danger-collapsed-top-bc { + height: 4px; +} + +.x-window-header-nx-message-danger-collapsed-top-tl, +.x-window-header-nx-message-danger-collapsed-top-bl, +.x-window-header-nx-message-danger-collapsed-top-tr, +.x-window-header-nx-message-danger-collapsed-top-br, +.x-window-header-nx-message-danger-collapsed-top-tc, +.x-window-header-nx-message-danger-collapsed-top-bc, +.x-window-header-nx-message-danger-collapsed-top-ml, +.x-window-header-nx-message-danger-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-collapsed-top-corners.gif); +} + +.x-window-header-nx-message-danger-collapsed-top-ml, +.x-window-header-nx-message-danger-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-collapsed-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-danger-collapsed-top-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-top-tl, +.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-danger-collapsed-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-danger-collapsed-top-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-collapsed-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-danger-collapsed-right { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #db2852; +} + +.x-window-header-nx-message-danger-collapsed-right-mc { + background-color: #db2852; +} + +.x-nbr .x-window-header-nx-message-danger-collapsed-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-danger-collapsed-right-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-danger-collapsed-right-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-danger-collapsed-right-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-danger-collapsed-right-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-danger-collapsed-right-br { + background-position: right -20px; +} + +.x-window-header-nx-message-danger-collapsed-right-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-danger-collapsed-right-mr { + background-position: right top; +} + +.x-window-header-nx-message-danger-collapsed-right-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-danger-collapsed-right-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-danger-collapsed-right-tr, +.x-window-header-nx-message-danger-collapsed-right-br, +.x-window-header-nx-message-danger-collapsed-right-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-danger-collapsed-right-tl, +.x-window-header-nx-message-danger-collapsed-right-bl, +.x-window-header-nx-message-danger-collapsed-right-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-danger-collapsed-right-tc { + height: 4px; +} + +.x-window-header-nx-message-danger-collapsed-right-bc { + height: 4px; +} + +.x-window-header-nx-message-danger-collapsed-right-tl, +.x-window-header-nx-message-danger-collapsed-right-bl, +.x-window-header-nx-message-danger-collapsed-right-tr, +.x-window-header-nx-message-danger-collapsed-right-br, +.x-window-header-nx-message-danger-collapsed-right-tc, +.x-window-header-nx-message-danger-collapsed-right-bc, +.x-window-header-nx-message-danger-collapsed-right-ml, +.x-window-header-nx-message-danger-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-collapsed-right-corners.gif); +} + +.x-window-header-nx-message-danger-collapsed-right-ml, +.x-window-header-nx-message-danger-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-collapsed-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-danger-collapsed-right-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-right-tl, +.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-danger-collapsed-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-danger-collapsed-right-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-collapsed-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-danger-collapsed-bottom { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #db2852; +} + +.x-window-header-nx-message-danger-collapsed-bottom-mc { + background-color: #db2852; +} + +.x-nbr .x-window-header-nx-message-danger-collapsed-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-danger-collapsed-bottom-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-danger-collapsed-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-danger-collapsed-bottom-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-danger-collapsed-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-danger-collapsed-bottom-br { + background-position: right -20px; +} + +.x-window-header-nx-message-danger-collapsed-bottom-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-danger-collapsed-bottom-mr { + background-position: right top; +} + +.x-window-header-nx-message-danger-collapsed-bottom-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-danger-collapsed-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-danger-collapsed-bottom-tr, +.x-window-header-nx-message-danger-collapsed-bottom-br, +.x-window-header-nx-message-danger-collapsed-bottom-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-danger-collapsed-bottom-tl, +.x-window-header-nx-message-danger-collapsed-bottom-bl, +.x-window-header-nx-message-danger-collapsed-bottom-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-danger-collapsed-bottom-tc { + height: 4px; +} + +.x-window-header-nx-message-danger-collapsed-bottom-bc { + height: 4px; +} + +.x-window-header-nx-message-danger-collapsed-bottom-tl, +.x-window-header-nx-message-danger-collapsed-bottom-bl, +.x-window-header-nx-message-danger-collapsed-bottom-tr, +.x-window-header-nx-message-danger-collapsed-bottom-br, +.x-window-header-nx-message-danger-collapsed-bottom-tc, +.x-window-header-nx-message-danger-collapsed-bottom-bc, +.x-window-header-nx-message-danger-collapsed-bottom-ml, +.x-window-header-nx-message-danger-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-collapsed-bottom-corners.gif); +} + +.x-window-header-nx-message-danger-collapsed-bottom-ml, +.x-window-header-nx-message-danger-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-collapsed-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-danger-collapsed-bottom-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-bottom-tl, +.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-danger-collapsed-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-danger-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-collapsed-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-danger-collapsed-left { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #db2852; +} + +.x-window-header-nx-message-danger-collapsed-left-mc { + background-color: #db2852; +} + +.x-nbr .x-window-header-nx-message-danger-collapsed-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-danger-collapsed-left-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-danger-collapsed-left-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-danger-collapsed-left-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-danger-collapsed-left-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-danger-collapsed-left-br { + background-position: right -20px; +} + +.x-window-header-nx-message-danger-collapsed-left-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-danger-collapsed-left-mr { + background-position: right top; +} + +.x-window-header-nx-message-danger-collapsed-left-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-danger-collapsed-left-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-danger-collapsed-left-tr, +.x-window-header-nx-message-danger-collapsed-left-br, +.x-window-header-nx-message-danger-collapsed-left-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-danger-collapsed-left-tl, +.x-window-header-nx-message-danger-collapsed-left-bl, +.x-window-header-nx-message-danger-collapsed-left-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-danger-collapsed-left-tc { + height: 4px; +} + +.x-window-header-nx-message-danger-collapsed-left-bc { + height: 4px; +} + +.x-window-header-nx-message-danger-collapsed-left-tl, +.x-window-header-nx-message-danger-collapsed-left-bl, +.x-window-header-nx-message-danger-collapsed-left-tr, +.x-window-header-nx-message-danger-collapsed-left-br, +.x-window-header-nx-message-danger-collapsed-left-tc, +.x-window-header-nx-message-danger-collapsed-left-bc, +.x-window-header-nx-message-danger-collapsed-left-ml, +.x-window-header-nx-message-danger-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-collapsed-left-corners.gif); +} + +.x-window-header-nx-message-danger-collapsed-left-ml, +.x-window-header-nx-message-danger-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-danger-collapsed-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-danger-collapsed-left-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-left-tl, +.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-danger-collapsed-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-danger-collapsed-left-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-collapsed-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-danger .x-window-header-icon { + width: 16px; + height: 16px; + color: #333333; + font-size: 16px; + line-height: 16px; + background-position: center center; +} +.x-window-header-nx-message-danger .x-window-header-glyph { + color: #333333; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-window-header-nx-message-danger .x-window-header-glyph { + color: #872d42; +} + +.x-window-header-nx-message-danger-horizontal .x-window-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-window-header-nx-message-danger-horizontal .x-window-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-window-header-nx-message-danger-vertical .x-window-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-window-header-nx-message-danger-vertical .x-window-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-window-header-nx-message-danger-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-window-header-nx-message-danger-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-window-header-nx-message-danger-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-window-header-nx-message-danger-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-window-header-nx-message-danger { + border-width: 0 !important; +} + +.x-nbr .x-window-nx-message-danger-collapsed .x-window-header { + border-width: 0 !important; +} + +.x-window-nx-message-danger-outer-border-l { + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-b { + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-bl { + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-r { + border-right-color: #606060 !important; + border-right-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-rl { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-rb { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-rbl { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-t { + border-top-color: #606060 !important; + border-top-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-tl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-tb { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-tbl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-tr { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-trl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-trb { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-danger-outer-border-trbl { + border-color: #606060 !important; + border-width: 2px !important; +} + +.x-window-nx-message-warning { + border-color: #606060; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; +} + +.x-window-nx-message-warning { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 2px; + border-style: solid; + background-color: white; +} + +.x-window-nx-message-warning-mc { + background-color: white; +} + +.x-nbr .x-window-nx-message-warning { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-nx-message-warning-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-10-10-10-10; +} + +.x-window-nx-message-warning-tl { + background-position: 0 -8px; +} + +.x-window-nx-message-warning-tr { + background-position: right -12px; +} + +.x-window-nx-message-warning-bl { + background-position: 0 -16px; +} + +.x-window-nx-message-warning-br { + background-position: right -20px; +} + +.x-window-nx-message-warning-ml { + background-position: 0 top; +} + +.x-window-nx-message-warning-mr { + background-position: right top; +} + +.x-window-nx-message-warning-tc { + background-position: 0 0; +} + +.x-window-nx-message-warning-bc { + background-position: 0 -4px; +} + +.x-window-nx-message-warning-tr, +.x-window-nx-message-warning-br, +.x-window-nx-message-warning-mr { + padding-right: 4px; +} + +.x-window-nx-message-warning-tl, +.x-window-nx-message-warning-bl, +.x-window-nx-message-warning-ml { + padding-left: 4px; +} + +.x-window-nx-message-warning-tc { + height: 4px; +} + +.x-window-nx-message-warning-bc { + height: 4px; +} + +.x-window-nx-message-warning-tl, +.x-window-nx-message-warning-bl, +.x-window-nx-message-warning-tr, +.x-window-nx-message-warning-br, +.x-window-nx-message-warning-tc, +.x-window-nx-message-warning-bc, +.x-window-nx-message-warning-ml, +.x-window-nx-message-warning-mr { + zoom: 1; + background-image: url(images/window/window-nx-message-warning-corners.gif); +} + +.x-window-nx-message-warning-ml, +.x-window-nx-message-warning-mr { + zoom: 1; + background-image: url(images/window/window-nx-message-warning-sides.gif); + background-repeat: repeat-y; +} + +.x-window-nx-message-warning-mc { + padding: 8px 8px 8px 8px; +} + +.x-strict .x-ie7 .x-window-nx-message-warning-tl, +.x-strict .x-ie7 .x-window-nx-message-warning-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-nx-message-warning:after { + display: none; + content: "x-slicer:corners:url(images/window/window-nx-message-warning-corners.gif), sides:url(images/window/window-nx-message-warning-sides.gif)"; +} + +/**/ +/* */ +.x-window-body-nx-message-warning { + border-color: #606060; + border-width: 1px; + border-style: solid; + background: white; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.x-window-header-nx-message-warning { + font-size: 13px; + border-color: #606060; + zoom: 1; + background-color: #f2862f; +} +.x-window-header-nx-message-warning .x-tool-img { + background-color: #f2862f; +} + +.x-window-header-nx-message-warning-vertical .x-window-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-window-header-nx-message-warning-vertical .x-window-header-text-container { + background-color: #f2862f; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#f2862f); +} + +.x-window-header-text-container-nx-message-warning { + color: white; + font-weight: bold; + line-height: 15px; + font-family: arial, helvetica, verdana, sans-serif; + font-size: 13px; + padding: 1px 0 0; + text-transform: none; +} + +.x-window-header-nx-message-warning-top { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 10px 10px 8px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #f2862f; +} + +.x-window-header-nx-message-warning-top-mc { + background-color: #f2862f; +} + +.x-nbr .x-window-header-nx-message-warning-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-warning-top-frameInfo { + font-family: dh-4-4-0-0-0-0-0-0-10-10-8-10; +} + +.x-window-header-nx-message-warning-top-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-warning-top-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-warning-top-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-warning-top-br { + background-position: right -20px; +} + +.x-window-header-nx-message-warning-top-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-warning-top-mr { + background-position: right top; +} + +.x-window-header-nx-message-warning-top-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-warning-top-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-warning-top-tr, +.x-window-header-nx-message-warning-top-br, +.x-window-header-nx-message-warning-top-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-warning-top-tl, +.x-window-header-nx-message-warning-top-bl, +.x-window-header-nx-message-warning-top-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-warning-top-tc { + height: 4px; +} + +.x-window-header-nx-message-warning-top-bc { + height: 0; +} + +.x-window-header-nx-message-warning-top-tl, +.x-window-header-nx-message-warning-top-bl, +.x-window-header-nx-message-warning-top-tr, +.x-window-header-nx-message-warning-top-br, +.x-window-header-nx-message-warning-top-tc, +.x-window-header-nx-message-warning-top-bc, +.x-window-header-nx-message-warning-top-ml, +.x-window-header-nx-message-warning-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-top-corners.gif); +} + +.x-window-header-nx-message-warning-top-ml, +.x-window-header-nx-message-warning-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-warning-top-mc { + padding: 6px 6px 8px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-warning-top-tl, +.x-strict .x-ie7 .x-window-header-nx-message-warning-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-warning-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-warning-top-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-warning-right { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 10px 10px 10px 8px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #f2862f; +} + +.x-window-header-nx-message-warning-right-mc { + background-color: #f2862f; +} + +.x-nbr .x-window-header-nx-message-warning-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-warning-right-frameInfo { + font-family: dh-0-4-4-0-0-0-0-0-10-10-10-8; +} + +.x-window-header-nx-message-warning-right-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-warning-right-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-warning-right-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-warning-right-br { + background-position: right -20px; +} + +.x-window-header-nx-message-warning-right-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-warning-right-mr { + background-position: right top; +} + +.x-window-header-nx-message-warning-right-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-warning-right-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-warning-right-tr, +.x-window-header-nx-message-warning-right-br, +.x-window-header-nx-message-warning-right-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-warning-right-tl, +.x-window-header-nx-message-warning-right-bl, +.x-window-header-nx-message-warning-right-ml { + padding-left: 0; +} + +.x-window-header-nx-message-warning-right-tc { + height: 4px; +} + +.x-window-header-nx-message-warning-right-bc { + height: 4px; +} + +.x-window-header-nx-message-warning-right-tl, +.x-window-header-nx-message-warning-right-bl, +.x-window-header-nx-message-warning-right-tr, +.x-window-header-nx-message-warning-right-br, +.x-window-header-nx-message-warning-right-tc, +.x-window-header-nx-message-warning-right-bc, +.x-window-header-nx-message-warning-right-ml, +.x-window-header-nx-message-warning-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-right-corners.gif); +} + +.x-window-header-nx-message-warning-right-ml, +.x-window-header-nx-message-warning-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-warning-right-mc { + padding: 6px 6px 6px 8px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-warning-right-tl, +.x-strict .x-ie7 .x-window-header-nx-message-warning-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-warning-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-warning-right-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-warning-bottom { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 8px 10px 10px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #f2862f; +} + +.x-window-header-nx-message-warning-bottom-mc { + background-color: #f2862f; +} + +.x-nbr .x-window-header-nx-message-warning-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-warning-bottom-frameInfo { + font-family: dh-0-0-4-4-0-0-0-0-8-10-10-10; +} + +.x-window-header-nx-message-warning-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-warning-bottom-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-warning-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-warning-bottom-br { + background-position: right -20px; +} + +.x-window-header-nx-message-warning-bottom-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-warning-bottom-mr { + background-position: right top; +} + +.x-window-header-nx-message-warning-bottom-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-warning-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-warning-bottom-tr, +.x-window-header-nx-message-warning-bottom-br, +.x-window-header-nx-message-warning-bottom-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-warning-bottom-tl, +.x-window-header-nx-message-warning-bottom-bl, +.x-window-header-nx-message-warning-bottom-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-warning-bottom-tc { + height: 0; +} + +.x-window-header-nx-message-warning-bottom-bc { + height: 4px; +} + +.x-window-header-nx-message-warning-bottom-tl, +.x-window-header-nx-message-warning-bottom-bl, +.x-window-header-nx-message-warning-bottom-tr, +.x-window-header-nx-message-warning-bottom-br, +.x-window-header-nx-message-warning-bottom-tc, +.x-window-header-nx-message-warning-bottom-bc, +.x-window-header-nx-message-warning-bottom-ml, +.x-window-header-nx-message-warning-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-bottom-corners.gif); +} + +.x-window-header-nx-message-warning-bottom-ml, +.x-window-header-nx-message-warning-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-warning-bottom-mc { + padding: 8px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-warning-bottom-tl, +.x-strict .x-ie7 .x-window-header-nx-message-warning-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-warning-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-warning-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-warning-left { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 10px 8px 10px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #f2862f; +} + +.x-window-header-nx-message-warning-left-mc { + background-color: #f2862f; +} + +.x-nbr .x-window-header-nx-message-warning-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-warning-left-frameInfo { + font-family: dh-4-0-0-4-0-0-0-0-10-8-10-10; +} + +.x-window-header-nx-message-warning-left-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-warning-left-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-warning-left-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-warning-left-br { + background-position: right -20px; +} + +.x-window-header-nx-message-warning-left-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-warning-left-mr { + background-position: right top; +} + +.x-window-header-nx-message-warning-left-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-warning-left-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-warning-left-tr, +.x-window-header-nx-message-warning-left-br, +.x-window-header-nx-message-warning-left-mr { + padding-right: 0; +} + +.x-window-header-nx-message-warning-left-tl, +.x-window-header-nx-message-warning-left-bl, +.x-window-header-nx-message-warning-left-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-warning-left-tc { + height: 4px; +} + +.x-window-header-nx-message-warning-left-bc { + height: 4px; +} + +.x-window-header-nx-message-warning-left-tl, +.x-window-header-nx-message-warning-left-bl, +.x-window-header-nx-message-warning-left-tr, +.x-window-header-nx-message-warning-left-br, +.x-window-header-nx-message-warning-left-tc, +.x-window-header-nx-message-warning-left-bc, +.x-window-header-nx-message-warning-left-ml, +.x-window-header-nx-message-warning-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-left-corners.gif); +} + +.x-window-header-nx-message-warning-left-ml, +.x-window-header-nx-message-warning-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-warning-left-mc { + padding: 6px 8px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-warning-left-tl, +.x-strict .x-ie7 .x-window-header-nx-message-warning-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-warning-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-warning-left-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-warning-collapsed-top { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #f2862f; +} + +.x-window-header-nx-message-warning-collapsed-top-mc { + background-color: #f2862f; +} + +.x-nbr .x-window-header-nx-message-warning-collapsed-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-warning-collapsed-top-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-warning-collapsed-top-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-warning-collapsed-top-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-warning-collapsed-top-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-warning-collapsed-top-br { + background-position: right -20px; +} + +.x-window-header-nx-message-warning-collapsed-top-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-warning-collapsed-top-mr { + background-position: right top; +} + +.x-window-header-nx-message-warning-collapsed-top-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-warning-collapsed-top-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-warning-collapsed-top-tr, +.x-window-header-nx-message-warning-collapsed-top-br, +.x-window-header-nx-message-warning-collapsed-top-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-warning-collapsed-top-tl, +.x-window-header-nx-message-warning-collapsed-top-bl, +.x-window-header-nx-message-warning-collapsed-top-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-warning-collapsed-top-tc { + height: 4px; +} + +.x-window-header-nx-message-warning-collapsed-top-bc { + height: 4px; +} + +.x-window-header-nx-message-warning-collapsed-top-tl, +.x-window-header-nx-message-warning-collapsed-top-bl, +.x-window-header-nx-message-warning-collapsed-top-tr, +.x-window-header-nx-message-warning-collapsed-top-br, +.x-window-header-nx-message-warning-collapsed-top-tc, +.x-window-header-nx-message-warning-collapsed-top-bc, +.x-window-header-nx-message-warning-collapsed-top-ml, +.x-window-header-nx-message-warning-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-collapsed-top-corners.gif); +} + +.x-window-header-nx-message-warning-collapsed-top-ml, +.x-window-header-nx-message-warning-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-collapsed-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-warning-collapsed-top-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-top-tl, +.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-warning-collapsed-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-warning-collapsed-top-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-collapsed-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-warning-collapsed-right { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #f2862f; +} + +.x-window-header-nx-message-warning-collapsed-right-mc { + background-color: #f2862f; +} + +.x-nbr .x-window-header-nx-message-warning-collapsed-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-warning-collapsed-right-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-warning-collapsed-right-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-warning-collapsed-right-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-warning-collapsed-right-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-warning-collapsed-right-br { + background-position: right -20px; +} + +.x-window-header-nx-message-warning-collapsed-right-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-warning-collapsed-right-mr { + background-position: right top; +} + +.x-window-header-nx-message-warning-collapsed-right-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-warning-collapsed-right-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-warning-collapsed-right-tr, +.x-window-header-nx-message-warning-collapsed-right-br, +.x-window-header-nx-message-warning-collapsed-right-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-warning-collapsed-right-tl, +.x-window-header-nx-message-warning-collapsed-right-bl, +.x-window-header-nx-message-warning-collapsed-right-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-warning-collapsed-right-tc { + height: 4px; +} + +.x-window-header-nx-message-warning-collapsed-right-bc { + height: 4px; +} + +.x-window-header-nx-message-warning-collapsed-right-tl, +.x-window-header-nx-message-warning-collapsed-right-bl, +.x-window-header-nx-message-warning-collapsed-right-tr, +.x-window-header-nx-message-warning-collapsed-right-br, +.x-window-header-nx-message-warning-collapsed-right-tc, +.x-window-header-nx-message-warning-collapsed-right-bc, +.x-window-header-nx-message-warning-collapsed-right-ml, +.x-window-header-nx-message-warning-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-collapsed-right-corners.gif); +} + +.x-window-header-nx-message-warning-collapsed-right-ml, +.x-window-header-nx-message-warning-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-collapsed-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-warning-collapsed-right-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-right-tl, +.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-warning-collapsed-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-warning-collapsed-right-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-collapsed-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-warning-collapsed-bottom { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #f2862f; +} + +.x-window-header-nx-message-warning-collapsed-bottom-mc { + background-color: #f2862f; +} + +.x-nbr .x-window-header-nx-message-warning-collapsed-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-warning-collapsed-bottom-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-warning-collapsed-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-warning-collapsed-bottom-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-warning-collapsed-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-warning-collapsed-bottom-br { + background-position: right -20px; +} + +.x-window-header-nx-message-warning-collapsed-bottom-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-warning-collapsed-bottom-mr { + background-position: right top; +} + +.x-window-header-nx-message-warning-collapsed-bottom-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-warning-collapsed-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-warning-collapsed-bottom-tr, +.x-window-header-nx-message-warning-collapsed-bottom-br, +.x-window-header-nx-message-warning-collapsed-bottom-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-warning-collapsed-bottom-tl, +.x-window-header-nx-message-warning-collapsed-bottom-bl, +.x-window-header-nx-message-warning-collapsed-bottom-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-warning-collapsed-bottom-tc { + height: 4px; +} + +.x-window-header-nx-message-warning-collapsed-bottom-bc { + height: 4px; +} + +.x-window-header-nx-message-warning-collapsed-bottom-tl, +.x-window-header-nx-message-warning-collapsed-bottom-bl, +.x-window-header-nx-message-warning-collapsed-bottom-tr, +.x-window-header-nx-message-warning-collapsed-bottom-br, +.x-window-header-nx-message-warning-collapsed-bottom-tc, +.x-window-header-nx-message-warning-collapsed-bottom-bc, +.x-window-header-nx-message-warning-collapsed-bottom-ml, +.x-window-header-nx-message-warning-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-collapsed-bottom-corners.gif); +} + +.x-window-header-nx-message-warning-collapsed-bottom-ml, +.x-window-header-nx-message-warning-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-collapsed-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-warning-collapsed-bottom-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-bottom-tl, +.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-warning-collapsed-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-warning-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-collapsed-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-warning-collapsed-left { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #f2862f; +} + +.x-window-header-nx-message-warning-collapsed-left-mc { + background-color: #f2862f; +} + +.x-nbr .x-window-header-nx-message-warning-collapsed-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-warning-collapsed-left-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-warning-collapsed-left-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-warning-collapsed-left-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-warning-collapsed-left-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-warning-collapsed-left-br { + background-position: right -20px; +} + +.x-window-header-nx-message-warning-collapsed-left-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-warning-collapsed-left-mr { + background-position: right top; +} + +.x-window-header-nx-message-warning-collapsed-left-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-warning-collapsed-left-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-warning-collapsed-left-tr, +.x-window-header-nx-message-warning-collapsed-left-br, +.x-window-header-nx-message-warning-collapsed-left-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-warning-collapsed-left-tl, +.x-window-header-nx-message-warning-collapsed-left-bl, +.x-window-header-nx-message-warning-collapsed-left-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-warning-collapsed-left-tc { + height: 4px; +} + +.x-window-header-nx-message-warning-collapsed-left-bc { + height: 4px; +} + +.x-window-header-nx-message-warning-collapsed-left-tl, +.x-window-header-nx-message-warning-collapsed-left-bl, +.x-window-header-nx-message-warning-collapsed-left-tr, +.x-window-header-nx-message-warning-collapsed-left-br, +.x-window-header-nx-message-warning-collapsed-left-tc, +.x-window-header-nx-message-warning-collapsed-left-bc, +.x-window-header-nx-message-warning-collapsed-left-ml, +.x-window-header-nx-message-warning-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-collapsed-left-corners.gif); +} + +.x-window-header-nx-message-warning-collapsed-left-ml, +.x-window-header-nx-message-warning-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-warning-collapsed-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-warning-collapsed-left-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-left-tl, +.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-warning-collapsed-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-warning-collapsed-left-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-collapsed-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-warning .x-window-header-icon { + width: 16px; + height: 16px; + color: #333333; + font-size: 16px; + line-height: 16px; + background-position: center center; +} +.x-window-header-nx-message-warning .x-window-header-glyph { + color: #333333; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-window-header-nx-message-warning .x-window-header-glyph { + color: #925c31; +} + +.x-window-header-nx-message-warning-horizontal .x-window-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-window-header-nx-message-warning-horizontal .x-window-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-window-header-nx-message-warning-vertical .x-window-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-window-header-nx-message-warning-vertical .x-window-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-window-header-nx-message-warning-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-window-header-nx-message-warning-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-window-header-nx-message-warning-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-window-header-nx-message-warning-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-window-header-nx-message-warning { + border-width: 0 !important; +} + +.x-nbr .x-window-nx-message-warning-collapsed .x-window-header { + border-width: 0 !important; +} + +.x-window-nx-message-warning-outer-border-l { + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-b { + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-bl { + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-r { + border-right-color: #606060 !important; + border-right-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-rl { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-rb { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-rbl { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-t { + border-top-color: #606060 !important; + border-top-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-tl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-tb { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-tbl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-tr { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-trl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-trb { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-warning-outer-border-trbl { + border-color: #606060 !important; + border-width: 2px !important; +} + +.x-window-nx-message-success { + border-color: #606060; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; +} + +.x-window-nx-message-success { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 2px; + border-style: solid; + background-color: white; +} + +.x-window-nx-message-success-mc { + background-color: white; +} + +.x-nbr .x-window-nx-message-success { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-nx-message-success-frameInfo { + font-family: dh-4-4-4-4-2-2-2-2-10-10-10-10; +} + +.x-window-nx-message-success-tl { + background-position: 0 -8px; +} + +.x-window-nx-message-success-tr { + background-position: right -12px; +} + +.x-window-nx-message-success-bl { + background-position: 0 -16px; +} + +.x-window-nx-message-success-br { + background-position: right -20px; +} + +.x-window-nx-message-success-ml { + background-position: 0 top; +} + +.x-window-nx-message-success-mr { + background-position: right top; +} + +.x-window-nx-message-success-tc { + background-position: 0 0; +} + +.x-window-nx-message-success-bc { + background-position: 0 -4px; +} + +.x-window-nx-message-success-tr, +.x-window-nx-message-success-br, +.x-window-nx-message-success-mr { + padding-right: 4px; +} + +.x-window-nx-message-success-tl, +.x-window-nx-message-success-bl, +.x-window-nx-message-success-ml { + padding-left: 4px; +} + +.x-window-nx-message-success-tc { + height: 4px; +} + +.x-window-nx-message-success-bc { + height: 4px; +} + +.x-window-nx-message-success-tl, +.x-window-nx-message-success-bl, +.x-window-nx-message-success-tr, +.x-window-nx-message-success-br, +.x-window-nx-message-success-tc, +.x-window-nx-message-success-bc, +.x-window-nx-message-success-ml, +.x-window-nx-message-success-mr { + zoom: 1; + background-image: url(images/window/window-nx-message-success-corners.gif); +} + +.x-window-nx-message-success-ml, +.x-window-nx-message-success-mr { + zoom: 1; + background-image: url(images/window/window-nx-message-success-sides.gif); + background-repeat: repeat-y; +} + +.x-window-nx-message-success-mc { + padding: 8px 8px 8px 8px; +} + +.x-strict .x-ie7 .x-window-nx-message-success-tl, +.x-strict .x-ie7 .x-window-nx-message-success-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-nx-message-success:after { + display: none; + content: "x-slicer:corners:url(images/window/window-nx-message-success-corners.gif), sides:url(images/window/window-nx-message-success-sides.gif)"; +} + +/**/ +/* */ +.x-window-body-nx-message-success { + border-color: #606060; + border-width: 1px; + border-style: solid; + background: white; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.x-window-header-nx-message-success { + font-size: 13px; + border-color: #606060; + zoom: 1; + background-color: #0b9743; +} +.x-window-header-nx-message-success .x-tool-img { + background-color: #0b9743; +} + +.x-window-header-nx-message-success-vertical .x-window-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-window-header-nx-message-success-vertical .x-window-header-text-container { + background-color: #0b9743; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#0b9743); +} + +.x-window-header-text-container-nx-message-success { + color: white; + font-weight: bold; + line-height: 15px; + font-family: arial, helvetica, verdana, sans-serif; + font-size: 13px; + padding: 1px 0 0; + text-transform: none; +} + +.x-window-header-nx-message-success-top { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 10px 10px 8px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #0b9743; +} + +.x-window-header-nx-message-success-top-mc { + background-color: #0b9743; +} + +.x-nbr .x-window-header-nx-message-success-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-success-top-frameInfo { + font-family: dh-4-4-0-0-0-0-0-0-10-10-8-10; +} + +.x-window-header-nx-message-success-top-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-success-top-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-success-top-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-success-top-br { + background-position: right -20px; +} + +.x-window-header-nx-message-success-top-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-success-top-mr { + background-position: right top; +} + +.x-window-header-nx-message-success-top-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-success-top-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-success-top-tr, +.x-window-header-nx-message-success-top-br, +.x-window-header-nx-message-success-top-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-success-top-tl, +.x-window-header-nx-message-success-top-bl, +.x-window-header-nx-message-success-top-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-success-top-tc { + height: 4px; +} + +.x-window-header-nx-message-success-top-bc { + height: 0; +} + +.x-window-header-nx-message-success-top-tl, +.x-window-header-nx-message-success-top-bl, +.x-window-header-nx-message-success-top-tr, +.x-window-header-nx-message-success-top-br, +.x-window-header-nx-message-success-top-tc, +.x-window-header-nx-message-success-top-bc, +.x-window-header-nx-message-success-top-ml, +.x-window-header-nx-message-success-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-top-corners.gif); +} + +.x-window-header-nx-message-success-top-ml, +.x-window-header-nx-message-success-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-success-top-mc { + padding: 6px 6px 8px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-success-top-tl, +.x-strict .x-ie7 .x-window-header-nx-message-success-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-success-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-success-top-corners.gif), sides:url(images/window-header/window-header-nx-message-success-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-success-right { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + padding: 10px 10px 10px 8px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #0b9743; +} + +.x-window-header-nx-message-success-right-mc { + background-color: #0b9743; +} + +.x-nbr .x-window-header-nx-message-success-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-success-right-frameInfo { + font-family: dh-0-4-4-0-0-0-0-0-10-10-10-8; +} + +.x-window-header-nx-message-success-right-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-success-right-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-success-right-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-success-right-br { + background-position: right -20px; +} + +.x-window-header-nx-message-success-right-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-success-right-mr { + background-position: right top; +} + +.x-window-header-nx-message-success-right-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-success-right-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-success-right-tr, +.x-window-header-nx-message-success-right-br, +.x-window-header-nx-message-success-right-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-success-right-tl, +.x-window-header-nx-message-success-right-bl, +.x-window-header-nx-message-success-right-ml { + padding-left: 0; +} + +.x-window-header-nx-message-success-right-tc { + height: 4px; +} + +.x-window-header-nx-message-success-right-bc { + height: 4px; +} + +.x-window-header-nx-message-success-right-tl, +.x-window-header-nx-message-success-right-bl, +.x-window-header-nx-message-success-right-tr, +.x-window-header-nx-message-success-right-br, +.x-window-header-nx-message-success-right-tc, +.x-window-header-nx-message-success-right-bc, +.x-window-header-nx-message-success-right-ml, +.x-window-header-nx-message-success-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-right-corners.gif); +} + +.x-window-header-nx-message-success-right-ml, +.x-window-header-nx-message-success-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-success-right-mc { + padding: 6px 6px 6px 8px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-success-right-tl, +.x-strict .x-ie7 .x-window-header-nx-message-success-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-success-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-success-right-corners.gif), sides:url(images/window-header/window-header-nx-message-success-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-success-bottom { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 8px 10px 10px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #0b9743; +} + +.x-window-header-nx-message-success-bottom-mc { + background-color: #0b9743; +} + +.x-nbr .x-window-header-nx-message-success-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-success-bottom-frameInfo { + font-family: dh-0-0-4-4-0-0-0-0-8-10-10-10; +} + +.x-window-header-nx-message-success-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-success-bottom-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-success-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-success-bottom-br { + background-position: right -20px; +} + +.x-window-header-nx-message-success-bottom-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-success-bottom-mr { + background-position: right top; +} + +.x-window-header-nx-message-success-bottom-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-success-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-success-bottom-tr, +.x-window-header-nx-message-success-bottom-br, +.x-window-header-nx-message-success-bottom-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-success-bottom-tl, +.x-window-header-nx-message-success-bottom-bl, +.x-window-header-nx-message-success-bottom-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-success-bottom-tc { + height: 0; +} + +.x-window-header-nx-message-success-bottom-bc { + height: 4px; +} + +.x-window-header-nx-message-success-bottom-tl, +.x-window-header-nx-message-success-bottom-bl, +.x-window-header-nx-message-success-bottom-tr, +.x-window-header-nx-message-success-bottom-br, +.x-window-header-nx-message-success-bottom-tc, +.x-window-header-nx-message-success-bottom-bc, +.x-window-header-nx-message-success-bottom-ml, +.x-window-header-nx-message-success-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-bottom-corners.gif); +} + +.x-window-header-nx-message-success-bottom-ml, +.x-window-header-nx-message-success-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-success-bottom-mc { + padding: 8px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-success-bottom-tl, +.x-strict .x-ie7 .x-window-header-nx-message-success-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-success-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-success-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-success-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-success-left { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 0; + -webkit-border-top-right-radius: 0; + border-top-right-radius: 0; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 10px 8px 10px 10px; + border-width: 0 0 0 0; + border-style: solid; + background-color: #0b9743; +} + +.x-window-header-nx-message-success-left-mc { + background-color: #0b9743; +} + +.x-nbr .x-window-header-nx-message-success-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-success-left-frameInfo { + font-family: dh-4-0-0-4-0-0-0-0-10-8-10-10; +} + +.x-window-header-nx-message-success-left-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-success-left-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-success-left-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-success-left-br { + background-position: right -20px; +} + +.x-window-header-nx-message-success-left-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-success-left-mr { + background-position: right top; +} + +.x-window-header-nx-message-success-left-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-success-left-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-success-left-tr, +.x-window-header-nx-message-success-left-br, +.x-window-header-nx-message-success-left-mr { + padding-right: 0; +} + +.x-window-header-nx-message-success-left-tl, +.x-window-header-nx-message-success-left-bl, +.x-window-header-nx-message-success-left-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-success-left-tc { + height: 4px; +} + +.x-window-header-nx-message-success-left-bc { + height: 4px; +} + +.x-window-header-nx-message-success-left-tl, +.x-window-header-nx-message-success-left-bl, +.x-window-header-nx-message-success-left-tr, +.x-window-header-nx-message-success-left-br, +.x-window-header-nx-message-success-left-tc, +.x-window-header-nx-message-success-left-bc, +.x-window-header-nx-message-success-left-ml, +.x-window-header-nx-message-success-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-left-corners.gif); +} + +.x-window-header-nx-message-success-left-ml, +.x-window-header-nx-message-success-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-success-left-mc { + padding: 6px 8px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-success-left-tl, +.x-strict .x-ie7 .x-window-header-nx-message-success-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-success-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-success-left-corners.gif), sides:url(images/window-header/window-header-nx-message-success-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-success-collapsed-top { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #0b9743; +} + +.x-window-header-nx-message-success-collapsed-top-mc { + background-color: #0b9743; +} + +.x-nbr .x-window-header-nx-message-success-collapsed-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-success-collapsed-top-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-success-collapsed-top-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-success-collapsed-top-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-success-collapsed-top-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-success-collapsed-top-br { + background-position: right -20px; +} + +.x-window-header-nx-message-success-collapsed-top-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-success-collapsed-top-mr { + background-position: right top; +} + +.x-window-header-nx-message-success-collapsed-top-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-success-collapsed-top-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-success-collapsed-top-tr, +.x-window-header-nx-message-success-collapsed-top-br, +.x-window-header-nx-message-success-collapsed-top-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-success-collapsed-top-tl, +.x-window-header-nx-message-success-collapsed-top-bl, +.x-window-header-nx-message-success-collapsed-top-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-success-collapsed-top-tc { + height: 4px; +} + +.x-window-header-nx-message-success-collapsed-top-bc { + height: 4px; +} + +.x-window-header-nx-message-success-collapsed-top-tl, +.x-window-header-nx-message-success-collapsed-top-bl, +.x-window-header-nx-message-success-collapsed-top-tr, +.x-window-header-nx-message-success-collapsed-top-br, +.x-window-header-nx-message-success-collapsed-top-tc, +.x-window-header-nx-message-success-collapsed-top-bc, +.x-window-header-nx-message-success-collapsed-top-ml, +.x-window-header-nx-message-success-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-collapsed-top-corners.gif); +} + +.x-window-header-nx-message-success-collapsed-top-ml, +.x-window-header-nx-message-success-collapsed-top-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-collapsed-top-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-success-collapsed-top-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-top-tl, +.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-success-collapsed-top:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-success-collapsed-top-corners.gif), sides:url(images/window-header/window-header-nx-message-success-collapsed-top-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-success-collapsed-right { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #0b9743; +} + +.x-window-header-nx-message-success-collapsed-right-mc { + background-color: #0b9743; +} + +.x-nbr .x-window-header-nx-message-success-collapsed-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-success-collapsed-right-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-success-collapsed-right-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-success-collapsed-right-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-success-collapsed-right-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-success-collapsed-right-br { + background-position: right -20px; +} + +.x-window-header-nx-message-success-collapsed-right-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-success-collapsed-right-mr { + background-position: right top; +} + +.x-window-header-nx-message-success-collapsed-right-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-success-collapsed-right-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-success-collapsed-right-tr, +.x-window-header-nx-message-success-collapsed-right-br, +.x-window-header-nx-message-success-collapsed-right-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-success-collapsed-right-tl, +.x-window-header-nx-message-success-collapsed-right-bl, +.x-window-header-nx-message-success-collapsed-right-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-success-collapsed-right-tc { + height: 4px; +} + +.x-window-header-nx-message-success-collapsed-right-bc { + height: 4px; +} + +.x-window-header-nx-message-success-collapsed-right-tl, +.x-window-header-nx-message-success-collapsed-right-bl, +.x-window-header-nx-message-success-collapsed-right-tr, +.x-window-header-nx-message-success-collapsed-right-br, +.x-window-header-nx-message-success-collapsed-right-tc, +.x-window-header-nx-message-success-collapsed-right-bc, +.x-window-header-nx-message-success-collapsed-right-ml, +.x-window-header-nx-message-success-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-collapsed-right-corners.gif); +} + +.x-window-header-nx-message-success-collapsed-right-ml, +.x-window-header-nx-message-success-collapsed-right-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-collapsed-right-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-success-collapsed-right-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-right-tl, +.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-success-collapsed-right:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-success-collapsed-right-corners.gif), sides:url(images/window-header/window-header-nx-message-success-collapsed-right-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-success-collapsed-bottom { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #0b9743; +} + +.x-window-header-nx-message-success-collapsed-bottom-mc { + background-color: #0b9743; +} + +.x-nbr .x-window-header-nx-message-success-collapsed-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-success-collapsed-bottom-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-success-collapsed-bottom-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-success-collapsed-bottom-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-success-collapsed-bottom-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-success-collapsed-bottom-br { + background-position: right -20px; +} + +.x-window-header-nx-message-success-collapsed-bottom-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-success-collapsed-bottom-mr { + background-position: right top; +} + +.x-window-header-nx-message-success-collapsed-bottom-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-success-collapsed-bottom-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-success-collapsed-bottom-tr, +.x-window-header-nx-message-success-collapsed-bottom-br, +.x-window-header-nx-message-success-collapsed-bottom-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-success-collapsed-bottom-tl, +.x-window-header-nx-message-success-collapsed-bottom-bl, +.x-window-header-nx-message-success-collapsed-bottom-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-success-collapsed-bottom-tc { + height: 4px; +} + +.x-window-header-nx-message-success-collapsed-bottom-bc { + height: 4px; +} + +.x-window-header-nx-message-success-collapsed-bottom-tl, +.x-window-header-nx-message-success-collapsed-bottom-bl, +.x-window-header-nx-message-success-collapsed-bottom-tr, +.x-window-header-nx-message-success-collapsed-bottom-br, +.x-window-header-nx-message-success-collapsed-bottom-tc, +.x-window-header-nx-message-success-collapsed-bottom-bc, +.x-window-header-nx-message-success-collapsed-bottom-ml, +.x-window-header-nx-message-success-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-collapsed-bottom-corners.gif); +} + +.x-window-header-nx-message-success-collapsed-bottom-ml, +.x-window-header-nx-message-success-collapsed-bottom-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-collapsed-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-success-collapsed-bottom-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-bottom-tl, +.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-success-collapsed-bottom:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-success-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-success-collapsed-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-success-collapsed-left { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + padding: 10px 10px 10px 10px; + border-width: 0; + border-style: solid; + background-color: #0b9743; +} + +.x-window-header-nx-message-success-collapsed-left-mc { + background-color: #0b9743; +} + +.x-nbr .x-window-header-nx-message-success-collapsed-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-window-header-nx-message-success-collapsed-left-frameInfo { + font-family: dh-4-4-4-4-0-0-0-0-10-10-10-10; +} + +.x-window-header-nx-message-success-collapsed-left-tl { + background-position: 0 -8px; +} + +.x-window-header-nx-message-success-collapsed-left-tr { + background-position: right -12px; +} + +.x-window-header-nx-message-success-collapsed-left-bl { + background-position: 0 -16px; +} + +.x-window-header-nx-message-success-collapsed-left-br { + background-position: right -20px; +} + +.x-window-header-nx-message-success-collapsed-left-ml { + background-position: 0 top; +} + +.x-window-header-nx-message-success-collapsed-left-mr { + background-position: right top; +} + +.x-window-header-nx-message-success-collapsed-left-tc { + background-position: 0 0; +} + +.x-window-header-nx-message-success-collapsed-left-bc { + background-position: 0 -4px; +} + +.x-window-header-nx-message-success-collapsed-left-tr, +.x-window-header-nx-message-success-collapsed-left-br, +.x-window-header-nx-message-success-collapsed-left-mr { + padding-right: 4px; +} + +.x-window-header-nx-message-success-collapsed-left-tl, +.x-window-header-nx-message-success-collapsed-left-bl, +.x-window-header-nx-message-success-collapsed-left-ml { + padding-left: 4px; +} + +.x-window-header-nx-message-success-collapsed-left-tc { + height: 4px; +} + +.x-window-header-nx-message-success-collapsed-left-bc { + height: 4px; +} + +.x-window-header-nx-message-success-collapsed-left-tl, +.x-window-header-nx-message-success-collapsed-left-bl, +.x-window-header-nx-message-success-collapsed-left-tr, +.x-window-header-nx-message-success-collapsed-left-br, +.x-window-header-nx-message-success-collapsed-left-tc, +.x-window-header-nx-message-success-collapsed-left-bc, +.x-window-header-nx-message-success-collapsed-left-ml, +.x-window-header-nx-message-success-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-collapsed-left-corners.gif); +} + +.x-window-header-nx-message-success-collapsed-left-ml, +.x-window-header-nx-message-success-collapsed-left-mr { + zoom: 1; + background-image: url(images/window-header/window-header-nx-message-success-collapsed-left-sides.gif); + background-repeat: repeat-y; +} + +.x-window-header-nx-message-success-collapsed-left-mc { + padding: 6px 6px 6px 6px; +} + +.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-left-tl, +.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-window-header-nx-message-success-collapsed-left:after { + display: none; + content: "x-slicer:corners:url(images/window-header/window-header-nx-message-success-collapsed-left-corners.gif), sides:url(images/window-header/window-header-nx-message-success-collapsed-left-sides.gif)"; +} + +/**/ +/* */ +.x-window-header-nx-message-success .x-window-header-icon { + width: 16px; + height: 16px; + color: #333333; + font-size: 16px; + line-height: 16px; + background-position: center center; +} +.x-window-header-nx-message-success .x-window-header-glyph { + color: #333333; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-window-header-nx-message-success .x-window-header-glyph { + color: #1f653b; +} + +.x-window-header-nx-message-success-horizontal .x-window-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-window-header-nx-message-success-horizontal .x-window-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-window-header-nx-message-success-vertical .x-window-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-window-header-nx-message-success-vertical .x-window-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-window-header-nx-message-success-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-window-header-nx-message-success-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-window-header-nx-message-success-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-window-header-nx-message-success-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-window-header-nx-message-success { + border-width: 0 !important; +} + +.x-nbr .x-window-nx-message-success-collapsed .x-window-header { + border-width: 0 !important; +} + +.x-window-nx-message-success-outer-border-l { + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-b { + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-bl { + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-r { + border-right-color: #606060 !important; + border-right-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-rl { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-rb { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-rbl { + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-t { + border-top-color: #606060 !important; + border-top-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-tl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-tb { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-tbl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-tr { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-trl { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-left-color: #606060 !important; + border-left-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-trb { + border-top-color: #606060 !important; + border-top-width: 2px !important; + border-right-color: #606060 !important; + border-right-width: 2px !important; + border-bottom-color: #606060 !important; + border-bottom-width: 2px !important; +} + +.x-window-nx-message-success-outer-border-trbl { + border-color: #606060 !important; + border-width: 2px !important; +} + +.fixed-modal { + top: 0 !important; +} + +.x-window-body .x-toolbar-footer { + padding-left: 0 !important; +} + +/** + * @class Ext.grid.column.Column + */ +.nx-middle-align { + vertical-align: middle; +} + +/** + * @class Ext.ux.IFrame + */ +.nx-iframe-full { + background-color: white; +} + +/** + * @class Ext.grid.Panel + */ +/** + * ui: 'borderless' + */ +.x-panel-borderless { + border-color: #444444; + padding: 5px 0 0 0; +} + +.x-panel-header-borderless { + font-size: 13px; + border: 1px solid #444444; +} +.x-panel-header-borderless .x-tool-img { + background-color: #444444; +} + +.x-panel-header-borderless-horizontal { + padding: 9px 9px 10px 9px; +} + +.x-panel-header-borderless-horizontal-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-borderless-vertical { + padding: 9px 9px 9px 10px; +} + +.x-panel-header-borderless-vertical-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-text-container-borderless { + color: white; + font-size: 13px; + font-weight: bold; + font-family: arial, helvetica, verdana, sans-serif; + line-height: 15px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-borderless { + background: transparent; + border-color: #444444; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 0; + border-style: solid; +} + +.x-panel-header-borderless { + background-image: none; + background-color: #444444; +} + +.x-panel-header-borderless-vertical { + background-image: none; + background-color: #444444; +} + +.x-panel .x-panel-header-borderless-collapsed-border-top { + border-bottom-width: 1px !important; +} +.x-panel .x-panel-header-borderless-collapsed-border-right { + border-left-width: 1px !important; +} +.x-panel .x-panel-header-borderless-collapsed-border-bottom { + border-top-width: 1px !important; +} +.x-panel .x-panel-header-borderless-collapsed-border-left { + border-right-width: 1px !important; +} + +/**/ +.x-panel-header-borderless-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-borderless-bottom:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-borderless-left:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +/**/ +.x-panel-header-borderless-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-panel-header-borderless-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-borderless-vertical .x-panel-header-text-container { + background-color: #444444; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#444444); +} + +.x-panel-header-borderless .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-borderless .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-borderless .x-panel-header-glyph { + color: #a1a1a1; +} + +.x-panel-header-borderless-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-borderless-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-borderless-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-borderless-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-borderless-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-borderless-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-borderless-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-borderless-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-borderless-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-borderless-outer-border-l { + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-borderless-outer-border-b { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-borderless-outer-border-bl { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-borderless-outer-border-r { + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-borderless-outer-border-rl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-borderless-outer-border-rb { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-borderless-outer-border-rbl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-borderless-outer-border-t { + border-top-color: #444444 !important; + border-top-width: 1px !important; +} + +.x-panel-borderless-outer-border-tl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-borderless-outer-border-tb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-borderless-outer-border-tbl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-borderless-outer-border-tr { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-borderless-outer-border-trl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-borderless-outer-border-trb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-borderless-outer-border-trbl { + border-color: #444444 !important; + border-width: 1px !important; +} + +/** + * cls: 'nx-rowexpander' + */ +.nx-rowexpander { + padding: 5px; +} +.nx-rowexpander tbody td:first-child { + padding-right: 5px; + font-weight: bold; +} + +/** + * cls: 'nx-disabled-row' + */ +.nx-disabled-row td { + color: #777777; +} +.nx-disabled-row td button { + color: #777777; +} + +/** + * @class Ext.draw.Text + */ +.nx-table-header-label { + font-weight: bold; +} + +/** + * @class Ext.form.CheckboxGroup + */ +.x-form-checkboxgroup-body { + padding: 0; +} + +/** + * @class Ext.form.Fieldset + */ +fieldset.nx-form-section { + margin-bottom: 0; + padding-bottom: 20px; +} +fieldset.nx-form-section .x-fieldset-body { + padding-left: 10px; +} +fieldset.nx-form-section.nx-no-title { + padding-top: 0; +} + +fieldset.nx-form-section > legend > span > div > .x-fieldset-header-text { + font-size: 18px; + line-height: 22px; +} + +/** +* @class Ext.ux.form.MultiSelect +*/ +.nx-multiselect .x-panel-header { + background-color: transparent; + border: none; + padding-left: 0; + padding-bottom: 9px; + padding-right: 0; +} +.nx-multiselect .x-panel-header .x-header-text { + color: #333333; + font-weight: normal; +} +.nx-multiselect .x-form-item .x-form-trigger-wrap { + margin-bottom: 9px; +} +.nx-multiselect .x-panel-body { + border: 1px #dddddd solid !important; +} + +.x-form-fieldcontainer.nx-invalid .nx-multiselect:nth-child(3) .x-panel-body { + border: 1px #db2852 solid !important; +} + +.x-form-multiselect-body .x-boundlist .x-mask { + background: none; +} + +/** +* @class Ext.ux.form.ItemSelector +*/ +.nx-itemselector-disabled .x-form-item-label { + opacity: 1; +} + +.x-form-itemselector-body .x-form-item { + margin: 0; +} + +/** + * @class Ext.form.Label + */ +.nx-monospace-field textarea, .nx-monospace-field input { + font-family: "Courier New", Courier, monospace; + font-size: 13px; +} + +/** + * @class Ext.tab.Panel + */ +/** + * ui: 'light' + */ +.x-panel-nx-light { + border-color: #444444; + padding: 0; +} + +.x-panel-header-nx-light { + font-size: 16px; + border: 1px solid #444444; +} +.x-panel-header-nx-light .x-tool-img { + background-color: #f4f4f4; +} + +.x-panel-header-nx-light-horizontal { + padding: 9px 9px 10px 9px; +} + +.x-panel-header-nx-light-horizontal-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-nx-light-vertical { + padding: 9px 9px 9px 10px; +} + +.x-panel-header-nx-light-vertical-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-text-container-nx-light { + color: #333333; + font-size: 16px; + font-weight: bold; + font-family: arial, helvetica, verdana, sans-serif; + line-height: 20px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-nx-light { + background: transparent; + border-color: #444444; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-header-nx-light { + background-image: none; + background-color: #f4f4f4; +} + +.x-panel-header-nx-light-vertical { + background-image: none; + background-color: #f4f4f4; +} + +.x-panel .x-panel-header-nx-light-collapsed-border-top { + border-bottom-width: 1px !important; +} +.x-panel .x-panel-header-nx-light-collapsed-border-right { + border-left-width: 1px !important; +} +.x-panel .x-panel-header-nx-light-collapsed-border-bottom { + border-top-width: 1px !important; +} +.x-panel .x-panel-header-nx-light-collapsed-border-left { + border-right-width: 1px !important; +} + +/**/ +.x-panel-header-nx-light-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-light-bottom:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-light-left:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-light-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-panel-header-nx-light-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-nx-light-vertical .x-panel-header-text-container { + background-color: #f4f4f4; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#f4f4f4); +} + +.x-panel-header-nx-light .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-nx-light .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-nx-light .x-panel-header-glyph { + color: #f9f9f9; +} + +.x-panel-header-nx-light-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-nx-light-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-nx-light-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-nx-light-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-nx-light-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-nx-light-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-nx-light-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-nx-light-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-nx-light-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-nx-light-outer-border-l { + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-light-outer-border-b { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-light-outer-border-bl { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-light-outer-border-r { + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-light-outer-border-rl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-light-outer-border-rb { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-light-outer-border-rbl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-light-outer-border-t { + border-top-color: #444444 !important; + border-top-width: 1px !important; +} + +.x-panel-nx-light-outer-border-tl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-light-outer-border-tb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-light-outer-border-tbl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-light-outer-border-tr { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-light-outer-border-trl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-light-outer-border-trb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-light-outer-border-trbl { + border-color: #444444 !important; + border-width: 1px !important; +} + +.x-tab-nx-light-top { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + -ms-border-radius: 6px; + -o-border-radius: 6px; + border-radius: 6px; + padding: 5px 12px 6px 12px; + border-width: 1px 1px 0 1px; + border-style: solid; + background-color: #f4f4f4; +} + +.x-tab-nx-light-top-mc { + background-color: #f4f4f4; +} + +.x-nbr .x-tab-nx-light-top { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-tab-nx-light-top-frameInfo { + font-family: th-6-6-6-6-1-1-0-1-5-12-6-12; +} + +.x-tab-nx-light-top-tl { + background-position: 0 -12px; +} + +.x-tab-nx-light-top-tr { + background-position: right -18px; +} + +.x-tab-nx-light-top-bl { + background-position: 0 -24px; +} + +.x-tab-nx-light-top-br { + background-position: right -30px; +} + +.x-tab-nx-light-top-ml { + background-position: 0 top; +} + +.x-tab-nx-light-top-mr { + background-position: right top; +} + +.x-tab-nx-light-top-tc { + background-position: 0 0; +} + +.x-tab-nx-light-top-bc { + background-position: 0 -6px; +} + +.x-tab-nx-light-top-tr, +.x-tab-nx-light-top-br, +.x-tab-nx-light-top-mr { + padding-right: 6px; +} + +.x-tab-nx-light-top-tl, +.x-tab-nx-light-top-bl, +.x-tab-nx-light-top-ml { + padding-left: 6px; +} + +.x-tab-nx-light-top-tc { + height: 6px; +} + +.x-tab-nx-light-top-bc { + height: 6px; +} + +.x-tab-nx-light-top-tl, +.x-tab-nx-light-top-bl, +.x-tab-nx-light-top-tr, +.x-tab-nx-light-top-br, +.x-tab-nx-light-top-tc, +.x-tab-nx-light-top-bc, +.x-tab-nx-light-top-ml, +.x-tab-nx-light-top-mr { + zoom: 1; + background-image: url(images/tab/tab-nx-light-top-corners.gif); +} + +.x-tab-nx-light-top-ml, +.x-tab-nx-light-top-mr { + zoom: 1; + background-image: url(images/tab/tab-nx-light-top-sides.gif); + background-repeat: repeat-y; +} + +.x-tab-nx-light-top-mc { + padding: 0px 7px 0px 7px; +} + +.x-strict .x-ie7 .x-tab-nx-light-top-tl, +.x-strict .x-ie7 .x-tab-nx-light-top-bl { + position: relative; + right: 0; +} + +/**/ +.x-tab-nx-light-top:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-nx-light-top-corners.gif), sides:url(images/tab/tab-nx-light-top-sides.gif)"; +} + +/**/ +/* */ +.x-tab-nx-light-bottom { + -moz-border-radius-topleft: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-topright: 6px; + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; + -moz-border-radius-bottomright: 6px; + -webkit-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + -moz-border-radius-bottomleft: 6px; + -webkit-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + padding: 6px 12px 5px 12px; + border-width: 0 1px 1px 1px; + border-style: solid; + background-color: #f4f4f4; +} + +.x-tab-nx-light-bottom-mc { + background-color: #f4f4f4; +} + +.x-nbr .x-tab-nx-light-bottom { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-tab-nx-light-bottom-frameInfo { + font-family: th-6-6-6-6-0-1-1-1-6-12-5-12; +} + +.x-tab-nx-light-bottom-tl { + background-position: 0 -12px; +} + +.x-tab-nx-light-bottom-tr { + background-position: right -18px; +} + +.x-tab-nx-light-bottom-bl { + background-position: 0 -24px; +} + +.x-tab-nx-light-bottom-br { + background-position: right -30px; +} + +.x-tab-nx-light-bottom-ml { + background-position: 0 top; +} + +.x-tab-nx-light-bottom-mr { + background-position: right top; +} + +.x-tab-nx-light-bottom-tc { + background-position: 0 0; +} + +.x-tab-nx-light-bottom-bc { + background-position: 0 -6px; +} + +.x-tab-nx-light-bottom-tr, +.x-tab-nx-light-bottom-br, +.x-tab-nx-light-bottom-mr { + padding-right: 6px; +} + +.x-tab-nx-light-bottom-tl, +.x-tab-nx-light-bottom-bl, +.x-tab-nx-light-bottom-ml { + padding-left: 6px; +} + +.x-tab-nx-light-bottom-tc { + height: 6px; +} + +.x-tab-nx-light-bottom-bc { + height: 6px; +} + +.x-tab-nx-light-bottom-tl, +.x-tab-nx-light-bottom-bl, +.x-tab-nx-light-bottom-tr, +.x-tab-nx-light-bottom-br, +.x-tab-nx-light-bottom-tc, +.x-tab-nx-light-bottom-bc, +.x-tab-nx-light-bottom-ml, +.x-tab-nx-light-bottom-mr { + zoom: 1; + background-image: url(images/tab/tab-nx-light-bottom-corners.gif); +} + +.x-tab-nx-light-bottom-ml, +.x-tab-nx-light-bottom-mr { + zoom: 1; + background-image: url(images/tab/tab-nx-light-bottom-sides.gif); + background-repeat: repeat-y; +} + +.x-tab-nx-light-bottom-mc { + padding: 0px 7px 0px 7px; +} + +.x-strict .x-ie7 .x-tab-nx-light-bottom-tl, +.x-strict .x-ie7 .x-tab-nx-light-bottom-bl { + position: relative; + right: 0; +} + +/**/ +.x-tab-nx-light-bottom:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-nx-light-bottom-corners.gif), sides:url(images/tab/tab-nx-light-bottom-sides.gif)"; +} + +/**/ +/* */ +.x-tab-nx-light-left { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + -ms-border-radius: 6px; + -o-border-radius: 6px; + border-radius: 6px; + padding: 5px 12px 6px 12px; + border-width: 1px 1px 0 1px; + border-style: solid; + background-color: #f4f4f4; +} + +.x-tab-nx-light-left-mc { + background-color: #f4f4f4; +} + +.x-nbr .x-tab-nx-light-left { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-tab-nx-light-left-frameInfo { + font-family: th-6-6-6-6-1-1-0-1-5-12-6-12; +} + +.x-tab-nx-light-left-tl { + background-position: 0 -12px; +} + +.x-tab-nx-light-left-tr { + background-position: right -18px; +} + +.x-tab-nx-light-left-bl { + background-position: 0 -24px; +} + +.x-tab-nx-light-left-br { + background-position: right -30px; +} + +.x-tab-nx-light-left-ml { + background-position: 0 top; +} + +.x-tab-nx-light-left-mr { + background-position: right top; +} + +.x-tab-nx-light-left-tc { + background-position: 0 0; +} + +.x-tab-nx-light-left-bc { + background-position: 0 -6px; +} + +.x-tab-nx-light-left-tr, +.x-tab-nx-light-left-br, +.x-tab-nx-light-left-mr { + padding-right: 6px; +} + +.x-tab-nx-light-left-tl, +.x-tab-nx-light-left-bl, +.x-tab-nx-light-left-ml { + padding-left: 6px; +} + +.x-tab-nx-light-left-tc { + height: 6px; +} + +.x-tab-nx-light-left-bc { + height: 6px; +} + +.x-tab-nx-light-left-tl, +.x-tab-nx-light-left-bl, +.x-tab-nx-light-left-tr, +.x-tab-nx-light-left-br, +.x-tab-nx-light-left-tc, +.x-tab-nx-light-left-bc, +.x-tab-nx-light-left-ml, +.x-tab-nx-light-left-mr { + zoom: 1; + background-image: url(images/tab/tab-nx-light-top-corners.gif); +} + +.x-tab-nx-light-left-ml, +.x-tab-nx-light-left-mr { + zoom: 1; + background-image: url(images/tab/tab-nx-light-top-sides.gif); + background-repeat: repeat-y; +} + +.x-tab-nx-light-left-mc { + padding: 0px 7px 0px 7px; +} + +.x-strict .x-ie7 .x-tab-nx-light-left-tl, +.x-strict .x-ie7 .x-tab-nx-light-left-bl { + position: relative; + right: 0; +} + +/**/ +.x-tab-nx-light-left:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-nx-light-top-corners.gif), sides:url(images/tab/tab-nx-light-top-sides.gif)"; +} + +/**/ +/* */ +.x-tab-nx-light-right { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + -ms-border-radius: 6px; + -o-border-radius: 6px; + border-radius: 6px; + padding: 5px 12px 6px 12px; + border-width: 1px 1px 0 1px; + border-style: solid; + background-color: #f4f4f4; +} + +.x-tab-nx-light-right-mc { + background-color: #f4f4f4; +} + +.x-nbr .x-tab-nx-light-right { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; +} + +.x-tab-nx-light-right-frameInfo { + font-family: th-6-6-6-6-1-1-0-1-5-12-6-12; +} + +.x-tab-nx-light-right-tl { + background-position: 0 -12px; +} + +.x-tab-nx-light-right-tr { + background-position: right -18px; +} + +.x-tab-nx-light-right-bl { + background-position: 0 -24px; +} + +.x-tab-nx-light-right-br { + background-position: right -30px; +} + +.x-tab-nx-light-right-ml { + background-position: 0 top; +} + +.x-tab-nx-light-right-mr { + background-position: right top; +} + +.x-tab-nx-light-right-tc { + background-position: 0 0; +} + +.x-tab-nx-light-right-bc { + background-position: 0 -6px; +} + +.x-tab-nx-light-right-tr, +.x-tab-nx-light-right-br, +.x-tab-nx-light-right-mr { + padding-right: 6px; +} + +.x-tab-nx-light-right-tl, +.x-tab-nx-light-right-bl, +.x-tab-nx-light-right-ml { + padding-left: 6px; +} + +.x-tab-nx-light-right-tc { + height: 6px; +} + +.x-tab-nx-light-right-bc { + height: 6px; +} + +.x-tab-nx-light-right-tl, +.x-tab-nx-light-right-bl, +.x-tab-nx-light-right-tr, +.x-tab-nx-light-right-br, +.x-tab-nx-light-right-tc, +.x-tab-nx-light-right-bc, +.x-tab-nx-light-right-ml, +.x-tab-nx-light-right-mr { + zoom: 1; + background-image: url(images/tab/tab-nx-light-top-corners.gif); +} + +.x-tab-nx-light-right-ml, +.x-tab-nx-light-right-mr { + zoom: 1; + background-image: url(images/tab/tab-nx-light-top-sides.gif); + background-repeat: repeat-y; +} + +.x-tab-nx-light-right-mc { + padding: 0px 7px 0px 7px; +} + +.x-strict .x-ie7 .x-tab-nx-light-right-tl, +.x-strict .x-ie7 .x-tab-nx-light-right-bl { + position: relative; + right: 0; +} + +/**/ +.x-tab-nx-light-right:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-nx-light-top-corners.gif), sides:url(images/tab/tab-nx-light-top-sides.gif)"; +} + +/**/ +/* */ +.x-tab-nx-light { + border-color: #f4f4f4; + margin: 0 2px; + cursor: pointer; +} +.x-tab-nx-light .x-tab-inner { + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #006bbf; + line-height: 16px; +} +.x-tab-nx-light .x-tab-icon-el { + width: 16px; + height: 16px; + line-height: 16px; + background-position: center center; +} +.x-tab-nx-light .x-tab-glyph { + font-size: 16px; + color: white; + opacity: 0.5; +} +.x-ie8m .x-tab-nx-light .x-tab-glyph { + color: #f9f9f9; +} +.x-strict .x-ie9 .x-tab-bar-vertical .x-tab-nx-light { + padding-left: 0; +} +.x-strict .x-ie9 .x-tab-bar-vertical .x-tab-nx-light .x-tab-button { + padding-left: 12px; +} +.x-strict .x-ie9 .x-tab-bar-vertical .x-tab-nx-light .x-tab-icon-el { + left: 12px; +} + +.x-tab-nx-light-icon .x-tab-inner { + width: 16px; +} + +.x-tab-nx-light-left { + margin: 0 2px 0 2px; +} + +.x-tab-nx-light-top, +.x-tab-nx-light-left, +.x-tab-nx-light-right { + border-bottom: 1px solid #444444; +} + +.x-tab-nx-light-bottom { + border-top: 1px solid #444444; +} + +.x-tab-nx-light-left { + -webkit-transform: rotate(270deg); + -webkit-transform-origin: 100% 0; + -moz-transform: rotate(270deg); + -moz-transform-origin: 100% 0; + -o-transform: rotate(270deg); + -o-transform-origin: 100% 0; + transform: rotate(270deg); + transform-origin: 100% 0; +} +.x-ie9m .x-tab-nx-light-left { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); +} + +.x-tab-nx-light-right { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-tab-nx-light-right { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); +} + +.x-tab-nx-light-icon-text-left .x-tab-inner { + padding-left: 22px; +} + +.x-tab-nx-light-over { + border-color: #dddddd; + background-color: #ebebeb; +} +.x-tab-nx-light-over .x-tab-glyph { + color: white; +} +.x-ie8m .x-tab-nx-light-over .x-tab-glyph { + color: #f5f5f5; +} + +.x-tab-nx-light-active { + border-color: white; + background-color: white; +} +.x-tab-nx-light-active .x-tab-inner { + color: #333333; +} +.x-tab-nx-light-active .x-tab-glyph { + color: #444444; +} +.x-ie8m .x-tab-nx-light-active .x-tab-glyph { + color: #a1a1a1; +} + +.x-tab-nx-light-top-active, +.x-tab-nx-light-left-active, +.x-tab-nx-light-right-active { + border-bottom: 1px solid white; +} + +.x-tab-nx-light-bottom-active { + border-top: 1px solid white; +} + +.x-tab-nx-light-disabled { + cursor: default; +} +.x-tab-nx-light-disabled .x-tab-inner { + color: #333333; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30); + opacity: 0.3; +} +.x-tab-nx-light-disabled .x-tab-icon-el { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} +.x-tab-nx-light-disabled .x-tab-glyph { + color: white; + opacity: 0.3; + filter: none; +} +.x-ie8m .x-tab-nx-light-disabled .x-tab-glyph { + color: #f7f7f7; +} + +.x-tab-nx-light-top-disabled, +.x-tab-nx-light-left-disabled, +.x-tab-nx-light-right-disabled { + border-color: #f4f4f4 #f4f4f4 #444444; +} + +.x-tab-nx-light-bottom-disabled { + border-color: #444444 #f4f4f4 #f4f4f4 #f4f4f4; +} + +.x-nbr .x-tab-nx-light { + background-image: none; +} + +.x-tab-nx-light-top-over .x-frame-tl, +.x-tab-nx-light-top-over .x-frame-bl, +.x-tab-nx-light-top-over .x-frame-tr, +.x-tab-nx-light-top-over .x-frame-br, +.x-tab-nx-light-top-over .x-frame-tc, +.x-tab-nx-light-top-over .x-frame-bc, +.x-tab-nx-light-left-over .x-frame-tl, +.x-tab-nx-light-left-over .x-frame-bl, +.x-tab-nx-light-left-over .x-frame-tr, +.x-tab-nx-light-left-over .x-frame-br, +.x-tab-nx-light-left-over .x-frame-tc, +.x-tab-nx-light-left-over .x-frame-bc, +.x-tab-nx-light-right-over .x-frame-tl, +.x-tab-nx-light-right-over .x-frame-bl, +.x-tab-nx-light-right-over .x-frame-tr, +.x-tab-nx-light-right-over .x-frame-br, +.x-tab-nx-light-right-over .x-frame-tc, +.x-tab-nx-light-right-over .x-frame-bc { + background-image: url(images/tab/tab-nx-light-top-over-corners.gif); +} +.x-tab-nx-light-top-over .x-frame-ml, +.x-tab-nx-light-top-over .x-frame-mr, +.x-tab-nx-light-left-over .x-frame-ml, +.x-tab-nx-light-left-over .x-frame-mr, +.x-tab-nx-light-right-over .x-frame-ml, +.x-tab-nx-light-right-over .x-frame-mr { + background-image: url(images/tab/tab-nx-light-top-over-sides.gif); +} +.x-tab-nx-light-top-over .x-frame-mc, +.x-tab-nx-light-left-over .x-frame-mc, +.x-tab-nx-light-right-over .x-frame-mc { + background-color: #ebebeb; +} + +.x-tab-nx-light-bottom-over .x-frame-tl, +.x-tab-nx-light-bottom-over .x-frame-bl, +.x-tab-nx-light-bottom-over .x-frame-tr, +.x-tab-nx-light-bottom-over .x-frame-br, +.x-tab-nx-light-bottom-over .x-frame-tc, +.x-tab-nx-light-bottom-over .x-frame-bc { + background-image: url(images/tab/tab-nx-light-bottom-over-corners.gif); +} +.x-tab-nx-light-bottom-over .x-frame-ml, +.x-tab-nx-light-bottom-over .x-frame-mr { + background-image: url(images/tab/tab-nx-light-bottom-over-sides.gif); +} +.x-tab-nx-light-bottom-over .x-frame-mc { + background-color: #ebebeb; +} + +.x-tab-nx-light-top-active .x-frame-tl, +.x-tab-nx-light-top-active .x-frame-bl, +.x-tab-nx-light-top-active .x-frame-tr, +.x-tab-nx-light-top-active .x-frame-br, +.x-tab-nx-light-top-active .x-frame-tc, +.x-tab-nx-light-top-active .x-frame-bc, +.x-tab-nx-light-left-active .x-frame-tl, +.x-tab-nx-light-left-active .x-frame-bl, +.x-tab-nx-light-left-active .x-frame-tr, +.x-tab-nx-light-left-active .x-frame-br, +.x-tab-nx-light-left-active .x-frame-tc, +.x-tab-nx-light-left-active .x-frame-bc, +.x-tab-nx-light-right-active .x-frame-tl, +.x-tab-nx-light-right-active .x-frame-bl, +.x-tab-nx-light-right-active .x-frame-tr, +.x-tab-nx-light-right-active .x-frame-br, +.x-tab-nx-light-right-active .x-frame-tc, +.x-tab-nx-light-right-active .x-frame-bc { + background-image: url(images/tab/tab-nx-light-top-active-corners.gif); +} +.x-tab-nx-light-top-active .x-frame-ml, +.x-tab-nx-light-top-active .x-frame-mr, +.x-tab-nx-light-left-active .x-frame-ml, +.x-tab-nx-light-left-active .x-frame-mr, +.x-tab-nx-light-right-active .x-frame-ml, +.x-tab-nx-light-right-active .x-frame-mr { + background-image: url(images/tab/tab-nx-light-top-active-sides.gif); +} +.x-tab-nx-light-top-active .x-frame-mc, +.x-tab-nx-light-left-active .x-frame-mc, +.x-tab-nx-light-right-active .x-frame-mc { + background-color: white; +} + +.x-tab-nx-light-bottom-active .x-frame-tl, +.x-tab-nx-light-bottom-active .x-frame-bl, +.x-tab-nx-light-bottom-active .x-frame-tr, +.x-tab-nx-light-bottom-active .x-frame-br, +.x-tab-nx-light-bottom-active .x-frame-tc, +.x-tab-nx-light-bottom-active .x-frame-bc { + background-image: url(images/tab/tab-nx-light-bottom-active-corners.gif); +} +.x-tab-nx-light-bottom-active .x-frame-ml, +.x-tab-nx-light-bottom-active .x-frame-mr { + background-image: url(images/tab/tab-nx-light-bottom-active-sides.gif); +} +.x-tab-nx-light-bottom-active .x-frame-mc { + background-color: white; +} + +.x-tab-nx-light-top-disabled .x-frame-tl, +.x-tab-nx-light-top-disabled .x-frame-bl, +.x-tab-nx-light-top-disabled .x-frame-tr, +.x-tab-nx-light-top-disabled .x-frame-br, +.x-tab-nx-light-top-disabled .x-frame-tc, +.x-tab-nx-light-top-disabled .x-frame-bc, +.x-tab-nx-light-left-disabled .x-frame-tl, +.x-tab-nx-light-left-disabled .x-frame-bl, +.x-tab-nx-light-left-disabled .x-frame-tr, +.x-tab-nx-light-left-disabled .x-frame-br, +.x-tab-nx-light-left-disabled .x-frame-tc, +.x-tab-nx-light-left-disabled .x-frame-bc, +.x-tab-nx-light-right-disabled .x-frame-tl, +.x-tab-nx-light-right-disabled .x-frame-bl, +.x-tab-nx-light-right-disabled .x-frame-tr, +.x-tab-nx-light-right-disabled .x-frame-br, +.x-tab-nx-light-right-disabled .x-frame-tc, +.x-tab-nx-light-right-disabled .x-frame-bc { + background-image: url(images/tab/tab-nx-light-top-disabled-corners.gif); +} +.x-tab-nx-light-top-disabled .x-frame-ml, +.x-tab-nx-light-top-disabled .x-frame-mr, +.x-tab-nx-light-left-disabled .x-frame-ml, +.x-tab-nx-light-left-disabled .x-frame-mr, +.x-tab-nx-light-right-disabled .x-frame-ml, +.x-tab-nx-light-right-disabled .x-frame-mr { + background-image: url(images/tab/tab-nx-light-top-disabled-sides.gif); +} +.x-tab-nx-light-top-disabled .x-frame-mc, +.x-tab-nx-light-left-disabled .x-frame-mc, +.x-tab-nx-light-right-disabled .x-frame-mc { + background-color: #f4f4f4; +} + +.x-tab-nx-light-bottom-disabled .x-frame-tl, +.x-tab-nx-light-bottom-disabled .x-frame-bl, +.x-tab-nx-light-bottom-disabled .x-frame-tr, +.x-tab-nx-light-bottom-disabled .x-frame-br, +.x-tab-nx-light-bottom-disabled .x-frame-tc, +.x-tab-nx-light-bottom-disabled .x-frame-bc { + background-image: url(images/tab/tab-nx-light-bottom-disabled-corners.gif); +} +.x-tab-nx-light-bottom-disabled .x-frame-ml, +.x-tab-nx-light-bottom-disabled .x-frame-mr { + background-image: url(images/tab/tab-nx-light-bottom-disabled-sides.gif); +} +.x-tab-nx-light-bottom-disabled .x-frame-mc { + background-color: #f4f4f4; +} + +.x-nbr .x-tab-nx-light-top, +.x-nbr .x-tab-nx-light-left, +.x-nbr .x-tab-nx-light-right { + border-bottom-width: 1px !important; +} +.x-nbr .x-tab-nx-light-bottom { + border-top-width: 1px !important; +} + +.x-tab-nx-light .x-tab-close-btn { + width: 12px; + height: 12px; + background-image: url(images/tab/tab-nx-light-close.png); +} +.x-tab-nx-light .x-tab-close-btn-over { + background-position: -12px 0; +} + +.x-tab-nx-light .x-tab-close-btn { + top: 2px; + right: 2px; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-debug_03.css b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-debug_03.css new file mode 100644 index 0000000000000000000000000000000000000000..f0225ab3eb7fafe6e6848ae74a6138507b6d3bcc --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-debug_03.css @@ -0,0 +1,3811 @@ +.x-tab-nx-light-disabled .x-tab-close-btn { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30); + opacity: 0.3; + background-position: 0 0; +} + +.x-tab-nx-light-pressed .x-tab-close-btn { + background-position: -24px 0; +} + +.x-tab-nx-light-closable .x-tab-wrap { + padding-right: 15px; +} + +/**/ +.x-tab-nx-light-top-over:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-nx-light-top-over-corners.gif), sides:url(images/tab/tab-nx-light-top-over-sides.gif), stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-tab-nx-light-bottom-over:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-nx-light-bottom-over-corners.gif), sides:url(images/tab/tab-nx-light-bottom-over-sides.gif), stretch:top"; +} + +/**/ +/* */ +/**/ +.x-tab-nx-light-top-active:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-nx-light-top-active-corners.gif), sides:url(images/tab/tab-nx-light-top-active-sides.gif), stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-tab-nx-light-bottom-active:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-nx-light-bottom-active-corners.gif), sides:url(images/tab/tab-nx-light-bottom-active-sides.gif), stretch:top"; +} + +/**/ +/* */ +/**/ +.x-tab-nx-light-top-disabled:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-nx-light-top-disabled-corners.gif), sides:url(images/tab/tab-nx-light-top-disabled-sides.gif), stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-tab-nx-light-bottom-disabled:after { + display: none; + content: "x-slicer:corners:url(images/tab/tab-nx-light-bottom-disabled-corners.gif), sides:url(images/tab/tab-nx-light-bottom-disabled-sides.gif), stretch:top"; +} + +/**/ +/* */ +.x-tab-bar-nx-light-top { + padding: 5px 0 2px 0; +} + +.x-tab-bar-nx-light-bottom { + padding: 2px 0 5px 0; +} + +.x-tab-bar-nx-light-left { + padding: 0 2px 0 5px; +} + +.x-tab-bar-nx-light-right { + padding: 0 5px 0 2px; +} + +.x-tab-bar-nx-light-horizontal { + height: 39px; +} +.x-content-box .x-tab-bar-nx-light-horizontal { + height: 32px; +} + +.x-tab-bar-nx-light-vertical { + width: 39px; +} +.x-content-box .x-tab-bar-nx-light-vertical { + width: 32px; +} + +.x-tab-bar-body-nx-light-top { + padding-bottom: 0; +} + +.x-tab-bar-body-nx-light-bottom { + padding-top: 0; +} + +.x-tab-bar-body-nx-light-left { + padding-right: 0; +} + +.x-tab-bar-body-nx-light-right { + padding-left: 0; +} + +.x-tab-bar-strip-nx-light { + border-style: solid; + border-color: #cbcbcb; + background-color: #f4f4f4; +} + +.x-content-box .x-tab-bar-strip-nx-light-horizontal { + height: -1px; +} + +.x-content-box .x-tab-bar-strip-nx-light-vertical { + width: -1px; +} + +.x-tab-bar-strip-nx-light-top { + border-width: 1px; + height: 1px; +} +.x-tab-bar-plain .x-tab-bar-strip-nx-light-top { + border-width: 1px; +} + +.x-tab-bar-strip-nx-light-bottom { + border-width: 1px 1px 1px 1px; + height: 1px; +} +.x-tab-bar-plain .x-tab-bar-strip-nx-light-bottom { + border-width: 1px 1px 1px 1px; +} + +.x-tab-bar-strip-nx-light-left { + border-width: 1px 1px 1px 1px; + width: 1px; +} +.x-tab-bar-plain .x-tab-bar-strip-nx-light-left { + border-width: 1px 1px 1px 1px; +} + +.x-tab-bar-strip-nx-light-right { + border-width: 1px 1px 1px 1px; + width: 1px; +} +.x-tab-bar-plain .x-tab-bar-strip-nx-light-right { + border-width: 1px 1px 1px 1px; +} + +.x-tab-bar-nx-light { + background-color: #f4f4f4; +} + +.x-tab-bar-nx-light .x-box-scroller { + cursor: pointer; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; + background-color: #f4f4f4; +} +.x-tab-bar-nx-light .x-box-scroller-plain .x-box-scroller { + background-color: transparent; +} +.x-ie8m .x-tab-bar-nx-light .x-box-scroller-plain .x-box-scroller { + background-color: #fff; +} +.x-tab-bar-nx-light .x-box-scroller-hover { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60); + opacity: 0.6; +} +.x-tab-bar-nx-light .x-box-scroller-pressed { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70); + opacity: 0.7; +} +.x-tab-bar-nx-light .x-tabbar-scroll-left, +.x-tab-bar-nx-light .x-tabbar-scroll-right { + height: 31px; + width: 24px; +} +.x-tab-bar-nx-light .x-tabbar-scroll-top, +.x-tab-bar-nx-light .x-tabbar-scroll-bottom { + width: 31px; + height: 24px; +} + +.x-tab-bar-nx-light-bottom .x-box-scroller { + margin-top: 1px; +} +.x-tab-bar-nx-light-right .x-box-scroller { + margin-left: 1px; +} + +.x-tab-bar-nx-light .x-tabbar-scroll-left { + background-image: url(images/tab-bar/nx-light-scroll-left.png); +} +.x-tab-bar-nx-light .x-tabbar-scroll-right { + background-image: url(images/tab-bar/nx-light-scroll-right.png); +} +.x-tab-bar-nx-light .x-tabbar-scroll-top { + background-image: url(images/tab-bar/nx-light-scroll-top.png); +} +.x-tab-bar-nx-light .x-tabbar-scroll-bottom { + background-image: url(images/tab-bar/nx-light-scroll-bottom.png); +} + +.x-tab-bar-nx-light .x-box-scroller-plain .x-tabbar-scroll-left { + background-image: url(images/tab-bar/nx-light-plain-scroll-left.png); +} +.x-tab-bar-nx-light .x-box-scroller-plain .x-tabbar-scroll-right { + background-image: url(images/tab-bar/nx-light-plain-scroll-right.png); +} +.x-tab-bar-nx-light .x-box-scroller-plain .x-tabbar-scroll-top { + background-image: url(images/tab-bar/nx-light-plain-scroll-top.png); +} +.x-tab-bar-nx-light .x-box-scroller-plain .x-tabbar-scroll-bottom { + background-image: url(images/tab-bar/nx-light-plain-scroll-bottom.png); +} + +.x-tab-bar-nx-light .x-box-scroller-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=25); + opacity: 0.25; + cursor: default; +} + +/**/ +.x-tab-bar-nx-light-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-tab-bar-nx-light-bottom:after { + display: none; + content: "x-slicer:stretch:top"; +} + +/**/ +/* */ +/**/ +.x-tab-bar-nx-light-left:after { + display: none; + content: "x-slicer:stretch:right"; +} + +/**/ +/* */ +/**/ +.x-tab-bar-nx-light-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-tab-bar-body-nx-light-top { + padding-left: 5px; +} + +.x-tab-bar-strip-nx-light-top { + border-bottom: 1px solid #dddddd; + border-top: none; +} + +.x-tab-nx-light-top, +.x-tab-nx-light-top-disabled { + border-bottom: 1px solid #f4f4f4; +} + +.x-tab-nx-light-top-active, +.x-tab-nx-light-top-active.x-tab-nx-light-top-over { + border-bottom: 1px solid #f4f4f4; +} + +.x-tab-nx-light-top-over { + border-bottom: 1px solid #dddddd; +} + +.x-tab-bar + .x-panel-body { + z-index: 0; +} + +/* including package baseapp */ +/** + * @class NX.ext.form.OptionalFieldSet + */ +.nx-optionalfieldset { + margin: 10px 25px 0 0; +} +.nx-optionalfieldset .x-fieldset-body { + background-color: #f4f4f4; + border: 1px solid #cbcbcb; + padding: 5px 20px 10px 20px; + position: relative; + margin-left: 25px; + width: auto !important; +} +.nx-optionalfieldset .nx-optionalfieldset .x-fieldset-body { + background-color: rgba(0, 0, 0, 0.05); +} + +.nx-mask-without-spinner .x-mask-msg-text { + background: transparent !important; + padding: 5px !important; +} + +/** + * @class NX.view.AboutWindow + */ +.nx-aboutwindow .x-window { + padding: 0; +} +.nx-aboutwindow .summary { + background-color: #f4f4f4; +} +.nx-aboutwindow .logo { + margin: 10px; +} + +/** + * @class NX.view.Authenticate + */ +.nx-authenticate .message { + margin-bottom: 10px; +} + +/** + * @class NX.view.Unlicensed + */ +.nx-unlicensed { + background-color: white; +} +.nx-unlicensed .title { + color: #333333; + font-size: 20px; + font-weight: bold; + text-align: center; + padding: 20px; +} +.nx-unlicensed .description { + font-size: 16px; +} + +/** + * @class NX.view.ExpireSession + */ +.nx-expire-session #expire { + color: #db2852; + font-size: 20px; + margin: 12px; +} + +/** + * @class NX.view.UnsupportedBrowser + */ +.nx-unsupported-browser { + background-color: white; +} +.nx-unsupported-browser .title { + color: #333333; + font-size: 20px; + font-weight: bold; + text-align: center; + padding: 20px; +} +.nx-unsupported-browser .description { + font-size: 16px; +} +.nx-unsupported-browser .icons { + padding: 20px 0 50px 0; +} + +/** + * @class NX.view.header.Mode + */ +.nx-modebutton { + padding: 6px 0 0 0; + color: white; +} +.nx-modebutton .x-btn-glyph { + font-size: 24px; +} +.nx-modebutton.x-icon-text-left .x-btn-inner { + line-height: 25px !important; + font-size: 15px; + padding-left: 36px; + padding-right: 10px; +} +.nx-modebutton.x-icon-text-left .x-btn-glyph { + text-align: left; + padding-left: 10px; +} + +.nx-modebutton + .nx-caret { + border-left: 5px solid transparent; + border-right: 5px solid transparent; +} + +.nx-modebutton:hover, +.nx-modebutton:focus { + background-color: #dddddd; +} +.nx-modebutton:hover .x-btn-inner, +.nx-modebutton:focus .x-btn-inner { + color: #006bbf; +} +.nx-modebutton:hover .x-btn-glyph, +.nx-modebutton:focus .x-btn-glyph { + color: #006bbf; +} + +.nx-modebutton.x-pressed { + background-color: #006bbf; +} +.nx-modebutton.x-pressed .x-btn-inner { + color: white; +} +.nx-modebutton.x-pressed .x-btn-glyph { + color: white; +} + +.nx-modebutton.x-pressed + .nx-caret { + border-bottom: 5px solid white; +} + +/** + * @class NX.view.header.QuickSearch + */ +/** + * Style for the main header search field. + */ +.nx-quicksearch .x-form-trigger-wrap { + border-radius: 11px; + padding-left: 3px; + padding-right: 0px; + background-color: #444444; + border-color: #333333; +} +.nx-quicksearch .x-form-trigger-wrap input.x-form-field, +.nx-quicksearch .x-form-trigger-wrap .x-trigger-cell, +.nx-quicksearch .x-form-trigger-wrap .x-form-trigger, +.nx-quicksearch .x-form-trigger-wrap .x-form-trigger-input-cell { + background-color: rgba(0, 0, 0, 0); +} +.nx-quicksearch .x-form-trigger-wrap input.x-form-field, +.nx-quicksearch .x-form-trigger-wrap .x-trigger-cell, +.nx-quicksearch .x-form-trigger-wrap .x-form-trigger, +.nx-quicksearch .x-form-trigger-wrap .x-form-text { + color: white; +} + +/** + * @class NX.view.header.Panel + */ +.nx-header-panel .x-toolbar { + background-color: black; + padding: 0 0 0 16px; +} +.nx-header-panel .productname { + color: white; + font-family: 'Proxima Nova Thin', 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-size: 20px; + line-height: 1.2em; +} +.nx-header-panel .productspec { + color: white; + font-family: 'Proxima Nova Semibold', 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-size: 11px; + line-height: 1.1em; + letter-spacing: 0.05em; +} + +.x-btn-nx-header-toolbar-medium { + border-color: #e1e1e1; +} + +.x-btn-nx-header-toolbar-medium { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 0; + border-style: solid; + background-image: none; + background-color: transparent; +} + +.x-btn-nx-header-toolbar-medium-mc { + background-image: url(images/btn/btn-nx-header-toolbar-medium-fbg.gif); + background-position: 0 top; + background-color: transparent; +} + +.x-nlg .x-btn-nx-header-toolbar-medium { + background-image: url(images/btn/btn-nx-header-toolbar-medium-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-nx-header-toolbar-medium { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-nx-header-toolbar-medium-frameInfo { + font-family: th-3-3-3-3-0-0-0-0-3-3-3-3; +} + +.x-btn-nx-header-toolbar-medium-tl { + background-position: 0 -6px; +} + +.x-btn-nx-header-toolbar-medium-tr { + background-position: right -9px; +} + +.x-btn-nx-header-toolbar-medium-bl { + background-position: 0 -12px; +} + +.x-btn-nx-header-toolbar-medium-br { + background-position: right -15px; +} + +.x-btn-nx-header-toolbar-medium-ml { + background-position: 0 top; +} + +.x-btn-nx-header-toolbar-medium-mr { + background-position: right top; +} + +.x-btn-nx-header-toolbar-medium-tc { + background-position: 0 0; +} + +.x-btn-nx-header-toolbar-medium-bc { + background-position: 0 -3px; +} + +.x-btn-nx-header-toolbar-medium-tr, +.x-btn-nx-header-toolbar-medium-br, +.x-btn-nx-header-toolbar-medium-mr { + padding-right: 3px; +} + +.x-btn-nx-header-toolbar-medium-tl, +.x-btn-nx-header-toolbar-medium-bl, +.x-btn-nx-header-toolbar-medium-ml { + padding-left: 3px; +} + +.x-btn-nx-header-toolbar-medium-tc { + height: 3px; +} + +.x-btn-nx-header-toolbar-medium-bc { + height: 3px; +} + +.x-btn-nx-header-toolbar-medium-tl, +.x-btn-nx-header-toolbar-medium-bl, +.x-btn-nx-header-toolbar-medium-tr, +.x-btn-nx-header-toolbar-medium-br, +.x-btn-nx-header-toolbar-medium-tc, +.x-btn-nx-header-toolbar-medium-bc, +.x-btn-nx-header-toolbar-medium-ml, +.x-btn-nx-header-toolbar-medium-mr { + zoom: 1; +} + +.x-btn-nx-header-toolbar-medium-ml, +.x-btn-nx-header-toolbar-medium-mr { + zoom: 1; +} + +.x-btn-nx-header-toolbar-medium-mc { + padding: 0px 0px 0px 0px; +} + +.x-strict .x-ie7 .x-btn-nx-header-toolbar-medium-tl, +.x-strict .x-ie7 .x-btn-nx-header-toolbar-medium-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-nx-header-toolbar-medium:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-header-toolbar-medium-fbg.gif), bg:url(images/btn/btn-nx-header-toolbar-medium-bg.gif)"; +} + +/**/ +/* */ +.x-btn-nx-header-toolbar-medium .x-btn-inner { + font-size: 14px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: white; + padding: 0 8px; +} +.x-btn-nx-header-toolbar-medium .x-btn-arrow { + background-image: url(images/button/nx-header-toolbar-medium-arrow.png); +} +.x-btn-nx-header-toolbar-medium .x-btn-arrow-right { + padding-right: 30px; +} +.x-btn-nx-header-toolbar-medium .x-btn-arrow-bottom { + padding-bottom: 26px; +} +.x-btn-nx-header-toolbar-medium .x-btn-glyph { + font-size: 24px; + line-height: 24px; + color: white; +} +.x-ie8m .x-btn-nx-header-toolbar-medium .x-btn-glyph { + color: white; +} + +.x-btn-nx-header-toolbar-medium-disabled { + background-image: none; + background-color: transparent; +} + +.x-btn-nx-header-toolbar-medium-icon .x-btn-button, +.x-btn-nx-header-toolbar-medium-noicon .x-btn-button { + height: 24px; +} +.x-btn-nx-header-toolbar-medium-icon .x-btn-inner, +.x-btn-nx-header-toolbar-medium-noicon .x-btn-inner { + line-height: 24px; +} + +.x-btn-nx-header-toolbar-medium-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-header-toolbar-medium-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-header-toolbar-medium-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-nx-header-toolbar-medium-icon .x-btn-inner { + width: 24px; + padding: 0; +} +.x-btn-nx-header-toolbar-medium-icon .x-btn-icon-el { + width: 24px; + height: 24px; +} + +.x-btn-nx-header-toolbar-medium-icon-text-left .x-btn-button { + height: 24px; +} +.x-btn-nx-header-toolbar-medium-icon-text-left .x-btn-inner { + line-height: 24px; + padding-left: 29px; +} +.x-btn-nx-header-toolbar-medium-icon-text-left .x-btn-icon-el { + width: 24px; + right: auto; +} +.x-ie6 .x-btn-nx-header-toolbar-medium-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-header-toolbar-medium-icon-text-left .x-btn-icon-el { + height: 24px; +} + +.x-btn-nx-header-toolbar-medium-icon-text-right .x-btn-button { + height: 24px; +} +.x-btn-nx-header-toolbar-medium-icon-text-right .x-btn-inner { + line-height: 24px; + padding-right: 29px; +} +.x-btn-nx-header-toolbar-medium-icon-text-right .x-btn-icon-el { + width: 24px; + left: auto; +} +.x-ie6 .x-btn-nx-header-toolbar-medium-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-header-toolbar-medium-icon-text-right .x-btn-icon-el { + height: 24px; +} + +.x-btn-nx-header-toolbar-medium-icon-text-top .x-btn-inner { + padding-top: 29px; +} +.x-btn-nx-header-toolbar-medium-icon-text-top .x-btn-icon-el { + height: 24px; + bottom: auto; +} +.x-ie6 .x-btn-nx-header-toolbar-medium-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-header-toolbar-medium-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-header-toolbar-medium-icon-text-bottom .x-btn-inner { + padding-bottom: 29px; +} +.x-btn-nx-header-toolbar-medium-icon-text-bottom .x-btn-icon-el { + height: 24px; + top: auto; +} +.x-ie6 .x-btn-nx-header-toolbar-medium-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-header-toolbar-medium-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-header-toolbar-medium-focus { + border-color: #96caee; + background-image: none; + background-color: #1c8145; +} + +.x-btn-nx-header-toolbar-medium-over { + border-color: #d8d8d8; + background-image: none; + background-color: #1c8145; +} + +.x-btn-nx-header-toolbar-medium-menu-active, +.x-btn-nx-header-toolbar-medium-pressed { + border-color: #cecece; + background-image: none; + background-color: #096e31; +} + +.x-btn-nx-header-toolbar-medium-focus .x-frame-tl, +.x-btn-nx-header-toolbar-medium-focus .x-frame-bl, +.x-btn-nx-header-toolbar-medium-focus .x-frame-tr, +.x-btn-nx-header-toolbar-medium-focus .x-frame-br, +.x-btn-nx-header-toolbar-medium-focus .x-frame-tc, +.x-btn-nx-header-toolbar-medium-focus .x-frame-bc { + background-image: url(images/btn/btn-nx-header-toolbar-medium-focus-corners.gif); +} +.x-btn-nx-header-toolbar-medium-focus .x-frame-ml, +.x-btn-nx-header-toolbar-medium-focus .x-frame-mr { + background-image: url(images/btn/btn-nx-header-toolbar-medium-focus-sides.gif); +} +.x-btn-nx-header-toolbar-medium-focus .x-frame-mc { + background-color: #1c8145; + background-image: url(images/btn/btn-nx-header-toolbar-medium-focus-fbg.gif); +} + +.x-btn-nx-header-toolbar-medium-over .x-frame-tl, +.x-btn-nx-header-toolbar-medium-over .x-frame-bl, +.x-btn-nx-header-toolbar-medium-over .x-frame-tr, +.x-btn-nx-header-toolbar-medium-over .x-frame-br, +.x-btn-nx-header-toolbar-medium-over .x-frame-tc, +.x-btn-nx-header-toolbar-medium-over .x-frame-bc { + background-image: url(images/btn/btn-nx-header-toolbar-medium-over-corners.gif); +} +.x-btn-nx-header-toolbar-medium-over .x-frame-ml, +.x-btn-nx-header-toolbar-medium-over .x-frame-mr { + background-image: url(images/btn/btn-nx-header-toolbar-medium-over-sides.gif); +} +.x-btn-nx-header-toolbar-medium-over .x-frame-mc { + background-color: #1c8145; + background-image: url(images/btn/btn-nx-header-toolbar-medium-over-fbg.gif); +} + +.x-btn-nx-header-toolbar-medium-menu-active .x-frame-tl, +.x-btn-nx-header-toolbar-medium-menu-active .x-frame-bl, +.x-btn-nx-header-toolbar-medium-menu-active .x-frame-tr, +.x-btn-nx-header-toolbar-medium-menu-active .x-frame-br, +.x-btn-nx-header-toolbar-medium-menu-active .x-frame-tc, +.x-btn-nx-header-toolbar-medium-menu-active .x-frame-bc, +.x-btn-nx-header-toolbar-medium-pressed .x-frame-tl, +.x-btn-nx-header-toolbar-medium-pressed .x-frame-bl, +.x-btn-nx-header-toolbar-medium-pressed .x-frame-tr, +.x-btn-nx-header-toolbar-medium-pressed .x-frame-br, +.x-btn-nx-header-toolbar-medium-pressed .x-frame-tc, +.x-btn-nx-header-toolbar-medium-pressed .x-frame-bc { + background-image: url(images/btn/btn-nx-header-toolbar-medium-pressed-corners.gif); +} +.x-btn-nx-header-toolbar-medium-menu-active .x-frame-ml, +.x-btn-nx-header-toolbar-medium-menu-active .x-frame-mr, +.x-btn-nx-header-toolbar-medium-pressed .x-frame-ml, +.x-btn-nx-header-toolbar-medium-pressed .x-frame-mr { + background-image: url(images/btn/btn-nx-header-toolbar-medium-pressed-sides.gif); +} +.x-btn-nx-header-toolbar-medium-menu-active .x-frame-mc, +.x-btn-nx-header-toolbar-medium-pressed .x-frame-mc { + background-color: #096e31; + background-image: url(images/btn/btn-nx-header-toolbar-medium-pressed-fbg.gif); +} + +.x-btn-nx-header-toolbar-medium-disabled .x-frame-tl, +.x-btn-nx-header-toolbar-medium-disabled .x-frame-bl, +.x-btn-nx-header-toolbar-medium-disabled .x-frame-tr, +.x-btn-nx-header-toolbar-medium-disabled .x-frame-br, +.x-btn-nx-header-toolbar-medium-disabled .x-frame-tc, +.x-btn-nx-header-toolbar-medium-disabled .x-frame-bc { + background-image: url(images/btn/btn-nx-header-toolbar-medium-disabled-corners.gif); +} +.x-btn-nx-header-toolbar-medium-disabled .x-frame-ml, +.x-btn-nx-header-toolbar-medium-disabled .x-frame-mr { + background-image: url(images/btn/btn-nx-header-toolbar-medium-disabled-sides.gif); +} +.x-btn-nx-header-toolbar-medium-disabled .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-nx-header-toolbar-medium-disabled-fbg.gif); +} + +.x-nlg .x-btn-nx-header-toolbar-medium-focus { + background-image: url(images/btn/btn-nx-header-toolbar-medium-focus-bg.gif); +} + +.x-nlg .x-btn-nx-header-toolbar-medium-over { + background-image: url(images/btn/btn-nx-header-toolbar-medium-over-bg.gif); +} + +.x-nlg .x-btn-nx-header-toolbar-medium-menu-active, +.x-nlg .x-btn-nx-header-toolbar-medium-pressed { + background-image: url(images/btn/btn-nx-header-toolbar-medium-pressed-bg.gif); +} + +.x-nlg .x-btn-nx-header-toolbar-medium-disabled { + background-image: url(images/btn/btn-nx-header-toolbar-medium-disabled-bg.gif); +} + +.x-nbr .x-btn-nx-header-toolbar-medium { + background-image: none; +} + +.x-btn-nx-header-toolbar-medium .x-btn-split-right { + background-image: url(images/button/nx-header-toolbar-medium-s-arrow.png); + padding-right: 32px; +} +.x-btn-nx-header-toolbar-medium .x-btn-split-bottom { + background-image: url(images/button/nx-header-toolbar-medium-s-arrow-b.png); + padding-bottom: 28px; +} + +.x-btn-nx-header-toolbar-medium-disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); + opacity: 0.5; +} + +/**/ +.x-btn-nx-header-toolbar-medium-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-header-toolbar-medium-focus-corners.gif), sides:url(images/btn/btn-nx-header-toolbar-medium-focus-sides.gif), frame-bg:url(images/btn/btn-nx-header-toolbar-medium-focus-fbg.gif), bg:url(images/btn/btn-nx-header-toolbar-medium-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-header-toolbar-medium-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-header-toolbar-medium-over-corners.gif), sides:url(images/btn/btn-nx-header-toolbar-medium-over-sides.gif), frame-bg:url(images/btn/btn-nx-header-toolbar-medium-over-fbg.gif), bg:url(images/btn/btn-nx-header-toolbar-medium-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-header-toolbar-medium-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-header-toolbar-medium-pressed-corners.gif), sides:url(images/btn/btn-nx-header-toolbar-medium-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-header-toolbar-medium-pressed-fbg.gif), bg:url(images/btn/btn-nx-header-toolbar-medium-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-header-toolbar-medium-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-header-toolbar-medium-disabled-corners.gif), sides:url(images/btn/btn-nx-header-toolbar-medium-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-header-toolbar-medium-disabled-fbg.gif), bg:url(images/btn/btn-nx-header-toolbar-medium-disabled-bg.gif)"; +} + +/**/ +/* */ +.x-btn-nx-header-toolbar-medium { + height: 39px; + border-radius: 0; + padding: 3px 6px; +} +.x-btn-nx-header-toolbar-medium .x-btn-wrap { + margin-top: 4px; +} + +/** + * @class NX.view.footer.Panel + */ +.nx-footer { + background-color: #444444; +} +.nx-footer .copyright { + color: #cbcbcb; + font-size: 8px; + text-align: right; + padding: 1px 2px 0 0; +} + +/** + * @class NX.view.feature.Menu + */ +.x-panel-nx-feature-menu { + border-color: #444444; + padding: 0; +} + +.x-panel-header-nx-feature-menu { + font-size: 13px; + border: 1px solid #444444; +} +.x-panel-header-nx-feature-menu .x-tool-img { + background-color: #444444; +} + +.x-panel-header-nx-feature-menu-horizontal { + padding: 4px; +} + +.x-panel-header-nx-feature-menu-horizontal-noborder { + padding: 5px 5px 4px 5px; +} + +.x-panel-header-nx-feature-menu-vertical { + padding: 4px 4px 4px 4px; +} + +.x-panel-header-nx-feature-menu-vertical-noborder { + padding: 5px 5px 5px 4px; +} + +.x-panel-header-text-container-nx-feature-menu { + color: white; + font-size: 13px; + font-weight: bold; + font-family: arial, helvetica, verdana, sans-serif; + line-height: 15px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-nx-feature-menu { + background: #dddddd; + border-color: #444444; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-header-nx-feature-menu { + background-image: none; + background-color: #444444; +} + +.x-panel-header-nx-feature-menu-vertical { + background-image: none; + background-color: #444444; +} + +.x-panel .x-panel-header-nx-feature-menu-collapsed-border-top { + border-bottom-width: 1px !important; +} +.x-panel .x-panel-header-nx-feature-menu-collapsed-border-right { + border-left-width: 1px !important; +} +.x-panel .x-panel-header-nx-feature-menu-collapsed-border-bottom { + border-top-width: 1px !important; +} +.x-panel .x-panel-header-nx-feature-menu-collapsed-border-left { + border-right-width: 1px !important; +} + +/**/ +.x-panel-header-nx-feature-menu-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-feature-menu-bottom:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-feature-menu-left:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-feature-menu-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-panel-header-nx-feature-menu-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-nx-feature-menu-vertical .x-panel-header-text-container { + background-color: #444444; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#444444); +} + +.x-panel-header-nx-feature-menu .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-nx-feature-menu .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-nx-feature-menu .x-panel-header-glyph { + color: #a1a1a1; +} + +.x-panel-header-nx-feature-menu-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-nx-feature-menu-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-nx-feature-menu-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-nx-feature-menu-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-nx-feature-menu-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-nx-feature-menu-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-nx-feature-menu-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-nx-feature-menu-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-nx-feature-menu-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-nx-feature-menu-outer-border-l { + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-b { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-bl { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-r { + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-rl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-rb { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-rbl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-t { + border-top-color: #444444 !important; + border-top-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-tl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-tb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-tbl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-tr { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-trl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-trb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-feature-menu-outer-border-trbl { + border-color: #444444 !important; + border-width: 1px !important; +} + +.x-panel-nx-feature-menu { + border-width: 0 2px 0 0; + border-color: #cbcbcb; + border-style: solid; +} +.x-panel-nx-feature-menu .x-grid-header-ct { + border: none; +} +.x-panel-nx-feature-menu .x-panel-body-nx-feature-menu .x-grid-cell { + background-color: #dddddd; +} +.x-panel-nx-feature-menu .x-panel-body-nx-feature-menu .x-grid-row-over .x-grid-td { + background-color: #ebebeb; +} +.x-panel-nx-feature-menu .x-panel-body-nx-feature-menu .x-grid-row-selected .x-grid-td { + color: white; + background-color: #2476c3; +} + +/** + * @class NX.view.feature.Content + */ +.x-panel-nx-feature-content { + border-color: #444444; + padding: 0; +} + +.x-panel-header-nx-feature-content { + font-size: 13px; + border: 1px solid #444444; +} +.x-panel-header-nx-feature-content .x-tool-img { + background-image: url(images/tools/tool-sprites-dark.png); + background-color: white; +} + +.x-panel-header-nx-feature-content-horizontal { + padding: 10px 8px 10px 10px; +} + +.x-panel-header-nx-feature-content-horizontal-noborder { + padding: 11px 9px 10px 11px; +} + +.x-panel-header-nx-feature-content-vertical { + padding: 10px 10px 8px 10px; +} + +.x-panel-header-nx-feature-content-vertical-noborder { + padding: 11px 11px 9px 10px; +} + +.x-panel-header-text-container-nx-feature-content { + color: white; + font-size: 13px; + font-weight: bold; + font-family: arial, helvetica, verdana, sans-serif; + line-height: 15px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-nx-feature-content { + background: #f4f4f4; + border-color: #444444; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-header-nx-feature-content { + background-image: none; + background-color: white; +} + +.x-panel-header-nx-feature-content-vertical { + background-image: none; + background-color: white; +} + +.x-panel .x-panel-header-nx-feature-content-collapsed-border-top { + border-bottom-width: 1px !important; +} +.x-panel .x-panel-header-nx-feature-content-collapsed-border-right { + border-left-width: 1px !important; +} +.x-panel .x-panel-header-nx-feature-content-collapsed-border-bottom { + border-top-width: 1px !important; +} +.x-panel .x-panel-header-nx-feature-content-collapsed-border-left { + border-right-width: 1px !important; +} + +/**/ +.x-panel-header-nx-feature-content-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-feature-content-bottom:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-feature-content-left:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-feature-content-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-panel-header-nx-feature-content-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-nx-feature-content-vertical .x-panel-header-text-container { + background-color: white; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=white); +} + +.x-panel-header-nx-feature-content .x-panel-header-icon { + width: 32px; + height: 32px; + background-position: center center; +} +.x-panel-header-nx-feature-content .x-panel-header-glyph { + color: white; + font-size: 32px; + line-height: 32px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-nx-feature-content .x-panel-header-glyph { + color: white; +} + +.x-panel-header-nx-feature-content-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-nx-feature-content-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-nx-feature-content-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-nx-feature-content-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-nx-feature-content-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-nx-feature-content-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-nx-feature-content-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-nx-feature-content-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-nx-feature-content-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-nx-feature-content-outer-border-l { + border-left-color: #444444 !important; + border-left-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-b { + border-bottom-color: #444444 !important; + border-bottom-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-bl { + border-bottom-color: #444444 !important; + border-bottom-width: 0 !important; + border-left-color: #444444 !important; + border-left-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-r { + border-right-color: #444444 !important; + border-right-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-rl { + border-right-color: #444444 !important; + border-right-width: 0 !important; + border-left-color: #444444 !important; + border-left-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-rb { + border-right-color: #444444 !important; + border-right-width: 0 !important; + border-bottom-color: #444444 !important; + border-bottom-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-rbl { + border-right-color: #444444 !important; + border-right-width: 0 !important; + border-bottom-color: #444444 !important; + border-bottom-width: 0 !important; + border-left-color: #444444 !important; + border-left-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-t { + border-top-color: #444444 !important; + border-top-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-tl { + border-top-color: #444444 !important; + border-top-width: 0 !important; + border-left-color: #444444 !important; + border-left-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-tb { + border-top-color: #444444 !important; + border-top-width: 0 !important; + border-bottom-color: #444444 !important; + border-bottom-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-tbl { + border-top-color: #444444 !important; + border-top-width: 0 !important; + border-bottom-color: #444444 !important; + border-bottom-width: 0 !important; + border-left-color: #444444 !important; + border-left-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-tr { + border-top-color: #444444 !important; + border-top-width: 0 !important; + border-right-color: #444444 !important; + border-right-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-trl { + border-top-color: #444444 !important; + border-top-width: 0 !important; + border-right-color: #444444 !important; + border-right-width: 0 !important; + border-left-color: #444444 !important; + border-left-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-trb { + border-top-color: #444444 !important; + border-top-width: 0 !important; + border-right-color: #444444 !important; + border-right-width: 0 !important; + border-bottom-color: #444444 !important; + border-bottom-width: 0 !important; +} + +.x-panel-nx-feature-content-outer-border-trbl { + border-color: #444444 !important; + border-width: 0 !important; +} + +.nx-feature-content .nx-feature-group { + border-top: 1px #dddddd solid !important; + margin: 0 !important; +} + +.nx-feature-content .x-panel-body .x-toolbar { + padding: 12px 8px 0 0; + /*.x-toolbar-item { + margin: 100px 8px 0 0; + }*/ +} +.nx-feature-content .x-panel-body .x-toolbar.x-toolbar-footer { + background-color: white; +} + +.nx-feature-content .x-panel-body .x-panel-nx-subsection-framed > div + .x-toolbar, +.nx-feature-content .x-panel-body .nx-actions.x-toolbar { + padding: 0 8px 0 0; +} +.nx-feature-content .x-panel-body .x-panel-nx-subsection-framed > div + .x-toolbar .x-toolbar-item, +.nx-feature-content .x-panel-body .nx-actions.x-toolbar .x-toolbar-item { + margin: 6px 0 6px 8px; +} + +.nx-feature-name { + font-size: 26px; + font-weight: bold; +} + +.nx-feature-description { + font-size: 13px; + font-weight: normal; + top: 12px !important; + padding-left: 7px; +} + +/** + * @class NX.view.feature.Group + */ +.nx-feature-group { + margin-top: 16px; + margin-left: 16px; +} +.nx-feature-group .item-wrap { + border: 1px solid rgba(0, 0, 0, 0); + float: left; + width: 200px; + height: 42px; + margin: 5px; + padding: 5px; + cursor: pointer; +} +.nx-feature-group .x-item-over { + border: 1px solid #cbcbcb; + background: #ebebeb; + border-radius: 5px; + -moz-border-radius: 5px; +} +.nx-feature-group .x-item-selected { + border: 1px solid gray; + background: #cbcbcb; + border-radius: 5px; + -moz-border-radius: 5px; +} +.nx-feature-group img { + height: 32px; + width: 32px; + margin-right: 5px; +} + +/** + * @class NX.view.feature.NotFound + */ +.nx-feature-notfound { + background-color: white; +} +.nx-feature-notfound .title { + color: #333333; + font-size: 20px; + font-weight: bold; + text-align: center; + padding: 20px; +} +.nx-feature-notfound .description { + font-size: 16px; +} + +/** + * @class NX.view.feature.NotVisible + */ +.nx-feature-notvisible { + background-color: #f4f4f4; +} +.nx-feature-notvisible .title { + color: #333333; + font-size: 20px; + font-weight: bold; + text-align: center; + padding: 20px; +} +.nx-feature-notvisible .description { + font-size: 16px; +} + +/** + * @class NX.view.dev.Panel + */ +/** + * ui: 'nx-developer' + */ +.x-panel-nx-developer { + border-color: #444444; + padding: 0; +} + +.x-panel-header-nx-developer { + font-size: 13px; + border: 1px solid #444444; +} +.x-panel-header-nx-developer .x-tool-img { + background-color: #014e8a; +} + +.x-panel-header-nx-developer-horizontal { + padding: 4px; +} + +.x-panel-header-nx-developer-horizontal-noborder { + padding: 5px 5px 4px 5px; +} + +.x-panel-header-nx-developer-vertical { + padding: 4px 4px 4px 4px; +} + +.x-panel-header-nx-developer-vertical-noborder { + padding: 5px 5px 5px 4px; +} + +.x-panel-header-text-container-nx-developer { + color: white; + font-size: 13px; + font-weight: bold; + font-family: arial, helvetica, verdana, sans-serif; + line-height: 15px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-nx-developer { + background: white; + border-color: #444444; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-header-nx-developer { + background-image: none; + background-color: #014e8a; +} + +.x-panel-header-nx-developer-vertical { + background-image: none; + background-color: #014e8a; +} + +.x-panel .x-panel-header-nx-developer-collapsed-border-top { + border-bottom-width: 1px !important; +} +.x-panel .x-panel-header-nx-developer-collapsed-border-right { + border-left-width: 1px !important; +} +.x-panel .x-panel-header-nx-developer-collapsed-border-bottom { + border-top-width: 1px !important; +} +.x-panel .x-panel-header-nx-developer-collapsed-border-left { + border-right-width: 1px !important; +} + +/**/ +.x-panel-header-nx-developer-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-developer-bottom:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-developer-left:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-developer-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-panel-header-nx-developer-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-nx-developer-vertical .x-panel-header-text-container { + background-color: #014e8a; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#014e8a); +} + +.x-panel-header-nx-developer .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-nx-developer .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-nx-developer .x-panel-header-glyph { + color: #80a6c4; +} + +.x-panel-header-nx-developer-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-nx-developer-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-nx-developer-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-nx-developer-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-nx-developer-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-nx-developer-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-nx-developer-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-nx-developer-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-nx-developer-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-nx-developer-outer-border-l { + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-b { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-bl { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-r { + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-rl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-rb { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-rbl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-t { + border-top-color: #444444 !important; + border-top-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-tl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-tb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-tbl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-tr { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-trl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-trb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-developer-outer-border-trbl { + border-color: #444444 !important; + border-width: 1px !important; +} + +.x-panel-nx-developer { + /* + * Meta styles for the visual style sheet + */ + /* + * Visual style sheet fonts + */ + /* + * Visual style sheet type styles + */ + /* + * Visual style sheet colors + */ +} +.x-panel-nx-developer .x-panel-body .x-tabpanel-child .x-box-inner { + overflow-y: auto; +} +.x-panel-nx-developer .nx-hbox { + float: left; + margin-right: 20px; +} +.x-panel-nx-developer .nx-vbox { + margin-bottom: 20px; +} +.x-panel-nx-developer .nx-section-header { + font-size: 13px; + padding-bottom: 5px; + text-transform: uppercase; +} +.x-panel-nx-developer table thead th { + font-size: 13px; + text-transform: uppercase; + text-align: left; +} +.x-panel-nx-developer table tbody tr .nx-color { + height: 10px; + width: 10px; +} +.x-panel-nx-developer .nx-proxima-nova-regular { + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; +} +.x-panel-nx-developer .nx-proxima-nova-bold { + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: bold; +} +.x-panel-nx-developer .nx-courier-new-regular { + font-family: "Courier New", Courier, monospace; +} +.x-panel-nx-developer .nx-sample-h1 { + font-size: 20px; + font-weight: 100; +} +.x-panel-nx-developer .nx-sample-h2 { + font-size: 26px; + font-weight: bold; +} +.x-panel-nx-developer .nx-sample-h3 { + font-size: 22px; + font-weight: bold; +} +.x-panel-nx-developer .nx-sample-h4 { + font-size: 18px; + font-weight: bold; +} +.x-panel-nx-developer .nx-sample-h5 { + font-size: 13px; + font-weight: bold; +} +.x-panel-nx-developer .nx-sample-body { + font-size: 13px; + font-weight: normal; +} +.x-panel-nx-developer .nx-sample-code { + font-size: 13px; + font-weight: normal; + font-family: "Courier New", Courier, monospace; +} +.x-panel-nx-developer .nx-sample-utility { + font-size: 10px; + font-weight: normal; +} +.x-panel-nx-developer .nx-color { + border: 1px #dddddd solid; + margin: 0 3px 3px 0 !important; + height: 40px; + width: 80px; +} +.x-panel-nx-developer .nx-color.black { + background-color: black; +} +.x-panel-nx-developer .nx-color.night-rider { + background-color: #333333; +} +.x-panel-nx-developer .nx-color.charcoal { + background-color: #444444; +} +.x-panel-nx-developer .nx-color.dark-gray { + background-color: #777777; +} +.x-panel-nx-developer .nx-color.gray { + background-color: gray; +} +.x-panel-nx-developer .nx-color.light-gray { + background-color: #cbcbcb; +} +.x-panel-nx-developer .nx-color.gainsboro { + background-color: #dddddd; +} +.x-panel-nx-developer .nx-color.smoke { + background-color: #ebebeb; +} +.x-panel-nx-developer .nx-color.light-smoke { + background-color: #f4f4f4; +} +.x-panel-nx-developer .nx-color.cerise { + background-color: #db2852; +} +.x-panel-nx-developer .nx-color.sun { + background-color: #f2862f; +} +.x-panel-nx-developer .nx-color.energy-yellow { + background-color: #f5c649; +} +.x-panel-nx-developer .nx-color.cobalt { + background-color: #0047b2; +} +.x-panel-nx-developer .nx-color.cerulean-blue { + background-color: #2476c3; +} +.x-panel-nx-developer .nx-color.citrus { + background-color: #84c900; +} +.x-panel-nx-developer .nx-color.free-speech-red { + background-color: #c70000; +} +.x-panel-nx-developer .nx-color.energy-yellow { + background-color: #f5c649; +} +.x-panel-nx-developer .nx-color.floral-white { + background-color: #fffaee; +} +.x-panel-nx-developer .nx-color.pigment-green { + background-color: #0b9743; +} +.x-panel-nx-developer .nx-color.madang { + background-color: #b6e9ab; +} +.x-panel-nx-developer .nx-color.venetian-red { + background-color: #bc0430; +} +.x-panel-nx-developer .nx-color.beauty-bush { + background-color: #edb2af; +} +.x-panel-nx-developer .nx-color.navy-blue { + background-color: #006bbf; +} +.x-panel-nx-developer .nx-color.cornflower { + background-color: #96caee; +} +.x-panel-nx-developer .nx-color.affair { + background-color: #875393; +} +.x-panel-nx-developer .nx-color.east-side { + background-color: #b087b9; +} +.x-panel-nx-developer .nx-color.blue-chalk { + background-color: #dac5df; +} +.x-panel-nx-developer .nx-color.white { + background-color: white; +} +.x-panel-nx-developer .nx-color.light-gainsboro { + background-color: #e6e6e6; +} +.x-panel-nx-developer .nx-color.light-gray { + background-color: #cbcbcb; +} +.x-panel-nx-developer .nx-color.silver { + background-color: #b8b8b8; +} +.x-panel-nx-developer .nx-color.suva-gray { + background-color: #919191; +} +.x-panel-nx-developer .nx-color.gray { + background-color: gray; +} +.x-panel-nx-developer .nx-color.denim { + background-color: #197ac5; +} +.x-panel-nx-developer .nx-color.light-cobalt { + background-color: #0161ad; +} +.x-panel-nx-developer .nx-color.dark-denim { + background-color: #014e8a; +} +.x-panel-nx-developer .nx-color.smalt { + background-color: #0f4976; +} +.x-panel-nx-developer .nx-color.dark-cerulean { + background-color: #0f4976; +} +.x-panel-nx-developer .nx-color.prussian-blue { + background-color: #013a68; +} +.x-panel-nx-developer .nx-color.light-cerise { + background-color: #de3d63; +} +.x-panel-nx-developer .nx-color.brick-red { + background-color: #c6254b; +} +.x-panel-nx-developer .nx-color.old-rose { + background-color: #b2314f; +} +.x-panel-nx-developer .nx-color.fire-brick { + background-color: #9e1e3c; +} +.x-panel-nx-developer .nx-color.shiraz { + background-color: #85253b; +} +.x-panel-nx-developer .nx-color.falu-red { + background-color: #77162d; +} +.x-panel-nx-developer .nx-color.sea-buckthorn { + background-color: #f39244; +} +.x-panel-nx-developer .nx-color.tahiti-gold { + background-color: #da792b; +} +.x-panel-nx-developer .nx-color.zest { + background-color: #c17536; +} +.x-panel-nx-developer .nx-color.rich-gold { + background-color: #ae6122; +} +.x-panel-nx-developer .nx-color.afghan-tan { + background-color: #925829; +} +.x-panel-nx-developer .nx-color.russet { + background-color: #83491a; +} +.x-panel-nx-developer .nx-color.elf-green { + background-color: #23a156; +} +.x-panel-nx-developer .nx-color.dark-pigment-green { + background-color: #0b893d; +} +.x-panel-nx-developer .nx-color.salem { + background-color: #1c8145; +} +.x-panel-nx-developer .nx-color.jewel { + background-color: #096e31; +} +.x-panel-nx-developer .nx-color.fun-green { + background-color: #156134; +} +.x-panel-nx-developer .nx-color.dark-jewel { + background-color: #0c4f26; +} + +/** + * @class NX.view.info.Entry + */ +.nx-info table { + border-spacing: 5px; +} + +.nx-info { + overflow: auto; +} + +.nx-info-entry td { + padding-bottom: 4px; + vertical-align: top; +} + +.nx-info-entry-name { + padding-left: 0; + padding-right: 6px; + font-weight: bold; + white-space: nowrap; +} + +.nx-info-entry-value { + padding-left: 0; + padding-right: 25px; + word-break: break-all; +} + +.nx-info-entry-value pre { + margin: 0; +} + +/** + * @class NX.view.drilldown.Actions + */ +.nx-actions { + background-color: white; + border-style: none; +} + +/** + * @class NX.view.drilldown.Details + */ +.x-panel-nx-drilldown-message { + border-color: #444444; + padding: 0; +} + +.x-panel-header-nx-drilldown-message { + font-size: 13px; + border: 1px solid #444444; +} +.x-panel-header-nx-drilldown-message .x-tool-img { + background-color: #444444; +} + +.x-panel-header-nx-drilldown-message-horizontal { + padding: 9px 9px 10px 9px; +} + +.x-panel-header-nx-drilldown-message-horizontal-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-nx-drilldown-message-vertical { + padding: 9px 9px 9px 10px; +} + +.x-panel-header-nx-drilldown-message-vertical-noborder { + padding: 10px 10px 10px 10px; +} + +.x-panel-header-text-container-nx-drilldown-message { + color: #333333; + font-size: 13px; + font-weight: bold; + font-family: arial, helvetica, verdana, sans-serif; + line-height: 15px; + padding: 1px 0 0; + text-transform: none; +} + +.x-panel-body-nx-drilldown-message { + background: transparent; + border-color: #444444; + color: black; + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + border-width: 1px; + border-style: solid; +} + +.x-panel-header-nx-drilldown-message { + background-image: none; + background-color: #444444; +} + +.x-panel-header-nx-drilldown-message-vertical { + background-image: none; + background-color: #444444; +} + +.x-panel .x-panel-header-nx-drilldown-message-collapsed-border-top { + border-bottom-width: 1px !important; +} +.x-panel .x-panel-header-nx-drilldown-message-collapsed-border-right { + border-left-width: 1px !important; +} +.x-panel .x-panel-header-nx-drilldown-message-collapsed-border-bottom { + border-top-width: 1px !important; +} +.x-panel .x-panel-header-nx-drilldown-message-collapsed-border-left { + border-right-width: 1px !important; +} + +/**/ +.x-panel-header-nx-drilldown-message-top:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-drilldown-message-bottom:after { + display: none; + content: "x-slicer:stretch:bottom"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-drilldown-message-left:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +/**/ +.x-panel-header-nx-drilldown-message-right:after { + display: none; + content: "x-slicer:stretch:left"; +} + +/**/ +/* */ +.x-panel-header-nx-drilldown-message-vertical .x-panel-header-text-container { + -webkit-transform: rotate(90deg); + -webkit-transform-origin: 0 0; + -moz-transform: rotate(90deg); + -moz-transform-origin: 0 0; + -o-transform: rotate(90deg); + -o-transform-origin: 0 0; + transform: rotate(90deg); + transform-origin: 0 0; +} +.x-ie9m .x-panel-header-nx-drilldown-message-vertical .x-panel-header-text-container { + background-color: #444444; + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1), progid:DXImageTransform.Microsoft.Chroma(color=#444444); +} + +.x-panel-header-nx-drilldown-message .x-panel-header-icon { + width: 16px; + height: 16px; + background-position: center center; +} +.x-panel-header-nx-drilldown-message .x-panel-header-glyph { + color: white; + font-size: 16px; + line-height: 16px; + opacity: 0.5; +} +.x-ie8m .x-panel-header-nx-drilldown-message .x-panel-header-glyph { + color: #a1a1a1; +} + +.x-panel-header-nx-drilldown-message-horizontal .x-panel-header-icon-before-title { + margin: 0 6px 0 0; +} +.x-panel-header-nx-drilldown-message-horizontal .x-panel-header-icon-after-title { + margin: 0 0 0 6px; +} + +.x-panel-header-nx-drilldown-message-vertical .x-panel-header-icon-before-title { + margin: 0 0 6px 0; +} +.x-panel-header-nx-drilldown-message-vertical .x-panel-header-icon-after-title { + margin: 6px 0 0 0; +} + +.x-panel-header-nx-drilldown-message-horizontal .x-tool-after-title { + margin: 0 0 0 6px; +} +.x-panel-header-nx-drilldown-message-horizontal .x-tool-before-title { + margin: 0 6px 0 0; +} + +.x-panel-header-nx-drilldown-message-vertical .x-tool-after-title { + margin: 6px 0 0 0; +} +.x-panel-header-nx-drilldown-message-vertical .x-tool-before-title { + margin: 0 0 6px 0; +} + +.x-panel-nx-drilldown-message-resizable .x-panel-handle { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); + opacity: 0; +} + +.x-panel-nx-drilldown-message-outer-border-l { + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-b { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-bl { + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-r { + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-rl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-rb { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-rbl { + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-t { + border-top-color: #444444 !important; + border-top-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-tl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-tb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-tbl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-tr { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-trl { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-left-color: #444444 !important; + border-left-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-trb { + border-top-color: #444444 !important; + border-top-width: 1px !important; + border-right-color: #444444 !important; + border-right-width: 1px !important; + border-bottom-color: #444444 !important; + border-bottom-width: 1px !important; +} + +.x-panel-nx-drilldown-message-outer-border-trbl { + border-color: #444444 !important; + border-width: 1px !important; +} + +.x-panel-nx-drilldown-message { + padding-left: 8px !important; + padding-right: 8px !important; + background-color: white; +} +.x-panel-nx-drilldown-message .x-panel-header { + padding: 8px; + border: 1px solid !important; + height: 32px; + border-radius: 8px; +} +.x-panel-nx-drilldown-message .x-panel-header img { + margin-right: 5px !important; +} + +.x-panel-nx-drilldown-message.nx-drilldown-warning .x-panel-header { + border-color: #f2862f !important; + background-color: #f4f4f4; +} + +.x-panel-nx-drilldown-message.nx-drilldown-info .x-panel-header { + border-color: #2476c3 !important; + background-color: #f4f4f4; +} + +.x-docked-top.nx-drilldown-info { + padding: 10px 0 0 0; +} + +/** + * @class NX.view.drilldown.Drilldown + */ +.x-btn-nx-drilldown-large { + border-color: transparent; +} + +.x-btn-nx-drilldown-large { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 0; + border-style: solid; + background-image: none; + background-color: transparent; +} + +.x-btn-nx-drilldown-large-mc { + background-image: url(images/btn/btn-nx-drilldown-large-fbg.gif); + background-position: 0 top; + background-color: transparent; +} + +.x-nlg .x-btn-nx-drilldown-large { + background-image: url(images/btn/btn-nx-drilldown-large-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-nx-drilldown-large { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-nx-drilldown-large-frameInfo { + font-family: th-3-3-3-3-0-0-0-0-3-3-3-3; +} + +.x-btn-nx-drilldown-large-tl { + background-position: 0 -6px; +} + +.x-btn-nx-drilldown-large-tr { + background-position: right -9px; +} + +.x-btn-nx-drilldown-large-bl { + background-position: 0 -12px; +} + +.x-btn-nx-drilldown-large-br { + background-position: right -15px; +} + +.x-btn-nx-drilldown-large-ml { + background-position: 0 top; +} + +.x-btn-nx-drilldown-large-mr { + background-position: right top; +} + +.x-btn-nx-drilldown-large-tc { + background-position: 0 0; +} + +.x-btn-nx-drilldown-large-bc { + background-position: 0 -3px; +} + +.x-btn-nx-drilldown-large-tr, +.x-btn-nx-drilldown-large-br, +.x-btn-nx-drilldown-large-mr { + padding-right: 3px; +} + +.x-btn-nx-drilldown-large-tl, +.x-btn-nx-drilldown-large-bl, +.x-btn-nx-drilldown-large-ml { + padding-left: 3px; +} + +.x-btn-nx-drilldown-large-tc { + height: 3px; +} + +.x-btn-nx-drilldown-large-bc { + height: 3px; +} + +.x-btn-nx-drilldown-large-tl, +.x-btn-nx-drilldown-large-bl, +.x-btn-nx-drilldown-large-tr, +.x-btn-nx-drilldown-large-br, +.x-btn-nx-drilldown-large-tc, +.x-btn-nx-drilldown-large-bc, +.x-btn-nx-drilldown-large-ml, +.x-btn-nx-drilldown-large-mr { + zoom: 1; +} + +.x-btn-nx-drilldown-large-ml, +.x-btn-nx-drilldown-large-mr { + zoom: 1; +} + +.x-btn-nx-drilldown-large-mc { + padding: 0px 0px 0px 0px; +} + +.x-strict .x-ie7 .x-btn-nx-drilldown-large-tl, +.x-strict .x-ie7 .x-btn-nx-drilldown-large-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-nx-drilldown-large:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-drilldown-large-fbg.gif), bg:url(images/btn/btn-nx-drilldown-large-bg.gif)"; +} + +/**/ +/* */ +.x-btn-nx-drilldown-large .x-btn-inner { + font-size: 26px; + font-weight: bold; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #006bbf; + padding: 0 0; +} +.x-btn-nx-drilldown-large .x-btn-arrow { + background-image: url(images/button/nx-drilldown-large-arrow.png); +} +.x-btn-nx-drilldown-large .x-btn-arrow-right { + padding-right: 36px; +} +.x-btn-nx-drilldown-large .x-btn-arrow-bottom { + padding-bottom: 32px; +} +.x-btn-nx-drilldown-large .x-btn-glyph { + font-size: 32px; + line-height: 32px; + color: white; +} +.x-ie8m .x-btn-nx-drilldown-large .x-btn-glyph { + color: white; +} + +.x-btn-nx-drilldown-large-disabled { + background-image: none; + background-color: transparent; +} +.x-btn-nx-drilldown-large-disabled .x-btn-inner { + color: #333333; +} + +.x-btn-nx-drilldown-large-icon .x-btn-button, +.x-btn-nx-drilldown-large-noicon .x-btn-button { + height: 32px; +} +.x-btn-nx-drilldown-large-icon .x-btn-inner, +.x-btn-nx-drilldown-large-noicon .x-btn-inner { + line-height: 32px; +} + +.x-btn-nx-drilldown-large-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-drilldown-large-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-drilldown-large-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-nx-drilldown-large-icon .x-btn-inner { + width: 32px; + padding: 0; +} +.x-btn-nx-drilldown-large-icon .x-btn-icon-el { + width: 32px; + height: 32px; +} + +.x-btn-nx-drilldown-large-icon-text-left .x-btn-button { + height: 32px; +} +.x-btn-nx-drilldown-large-icon-text-left .x-btn-inner { + line-height: 32px; + padding-left: 37px; +} +.x-btn-nx-drilldown-large-icon-text-left .x-btn-icon-el { + width: 32px; + right: auto; +} +.x-ie6 .x-btn-nx-drilldown-large-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-drilldown-large-icon-text-left .x-btn-icon-el { + height: 32px; +} + +.x-btn-nx-drilldown-large-icon-text-right .x-btn-button { + height: 32px; +} +.x-btn-nx-drilldown-large-icon-text-right .x-btn-inner { + line-height: 32px; + padding-right: 37px; +} +.x-btn-nx-drilldown-large-icon-text-right .x-btn-icon-el { + width: 32px; + left: auto; +} +.x-ie6 .x-btn-nx-drilldown-large-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-drilldown-large-icon-text-right .x-btn-icon-el { + height: 32px; +} + +.x-btn-nx-drilldown-large-icon-text-top .x-btn-inner { + padding-top: 37px; +} +.x-btn-nx-drilldown-large-icon-text-top .x-btn-icon-el { + height: 32px; + bottom: auto; +} +.x-ie6 .x-btn-nx-drilldown-large-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-drilldown-large-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-drilldown-large-icon-text-bottom .x-btn-inner { + padding-bottom: 37px; +} +.x-btn-nx-drilldown-large-icon-text-bottom .x-btn-icon-el { + height: 32px; + top: auto; +} +.x-ie6 .x-btn-nx-drilldown-large-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-drilldown-large-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-drilldown-large-focus { + background-image: none; + background-color: transparent; +} +.x-btn-nx-drilldown-large-focus .x-btn-inner { + color: #0f4976; +} + +.x-btn-nx-drilldown-large-over { + background-image: none; + background-color: transparent; +} +.x-btn-nx-drilldown-large-over .x-btn-inner { + color: #0f4976; +} + +.x-btn-nx-drilldown-large-menu-active, +.x-btn-nx-drilldown-large-pressed { + background-image: none; + background-color: transparent; +} +.x-btn-nx-drilldown-large-menu-active .x-btn-inner, +.x-btn-nx-drilldown-large-pressed .x-btn-inner { + color: #96caee; +} + +.x-btn-nx-drilldown-large-focus .x-frame-tl, +.x-btn-nx-drilldown-large-focus .x-frame-bl, +.x-btn-nx-drilldown-large-focus .x-frame-tr, +.x-btn-nx-drilldown-large-focus .x-frame-br, +.x-btn-nx-drilldown-large-focus .x-frame-tc, +.x-btn-nx-drilldown-large-focus .x-frame-bc { + background-image: url(images/btn/btn-nx-drilldown-large-focus-corners.gif); +} +.x-btn-nx-drilldown-large-focus .x-frame-ml, +.x-btn-nx-drilldown-large-focus .x-frame-mr { + background-image: url(images/btn/btn-nx-drilldown-large-focus-sides.gif); +} +.x-btn-nx-drilldown-large-focus .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-nx-drilldown-large-focus-fbg.gif); +} + +.x-btn-nx-drilldown-large-over .x-frame-tl, +.x-btn-nx-drilldown-large-over .x-frame-bl, +.x-btn-nx-drilldown-large-over .x-frame-tr, +.x-btn-nx-drilldown-large-over .x-frame-br, +.x-btn-nx-drilldown-large-over .x-frame-tc, +.x-btn-nx-drilldown-large-over .x-frame-bc { + background-image: url(images/btn/btn-nx-drilldown-large-over-corners.gif); +} +.x-btn-nx-drilldown-large-over .x-frame-ml, +.x-btn-nx-drilldown-large-over .x-frame-mr { + background-image: url(images/btn/btn-nx-drilldown-large-over-sides.gif); +} +.x-btn-nx-drilldown-large-over .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-nx-drilldown-large-over-fbg.gif); +} + +.x-btn-nx-drilldown-large-menu-active .x-frame-tl, +.x-btn-nx-drilldown-large-menu-active .x-frame-bl, +.x-btn-nx-drilldown-large-menu-active .x-frame-tr, +.x-btn-nx-drilldown-large-menu-active .x-frame-br, +.x-btn-nx-drilldown-large-menu-active .x-frame-tc, +.x-btn-nx-drilldown-large-menu-active .x-frame-bc, +.x-btn-nx-drilldown-large-pressed .x-frame-tl, +.x-btn-nx-drilldown-large-pressed .x-frame-bl, +.x-btn-nx-drilldown-large-pressed .x-frame-tr, +.x-btn-nx-drilldown-large-pressed .x-frame-br, +.x-btn-nx-drilldown-large-pressed .x-frame-tc, +.x-btn-nx-drilldown-large-pressed .x-frame-bc { + background-image: url(images/btn/btn-nx-drilldown-large-pressed-corners.gif); +} +.x-btn-nx-drilldown-large-menu-active .x-frame-ml, +.x-btn-nx-drilldown-large-menu-active .x-frame-mr, +.x-btn-nx-drilldown-large-pressed .x-frame-ml, +.x-btn-nx-drilldown-large-pressed .x-frame-mr { + background-image: url(images/btn/btn-nx-drilldown-large-pressed-sides.gif); +} +.x-btn-nx-drilldown-large-menu-active .x-frame-mc, +.x-btn-nx-drilldown-large-pressed .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-nx-drilldown-large-pressed-fbg.gif); +} + +.x-btn-nx-drilldown-large-disabled .x-frame-tl, +.x-btn-nx-drilldown-large-disabled .x-frame-bl, +.x-btn-nx-drilldown-large-disabled .x-frame-tr, +.x-btn-nx-drilldown-large-disabled .x-frame-br, +.x-btn-nx-drilldown-large-disabled .x-frame-tc, +.x-btn-nx-drilldown-large-disabled .x-frame-bc { + background-image: url(images/btn/btn-nx-drilldown-large-disabled-corners.gif); +} +.x-btn-nx-drilldown-large-disabled .x-frame-ml, +.x-btn-nx-drilldown-large-disabled .x-frame-mr { + background-image: url(images/btn/btn-nx-drilldown-large-disabled-sides.gif); +} +.x-btn-nx-drilldown-large-disabled .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-nx-drilldown-large-disabled-fbg.gif); +} + +.x-nlg .x-btn-nx-drilldown-large-focus { + background-image: url(images/btn/btn-nx-drilldown-large-focus-bg.gif); +} + +.x-nlg .x-btn-nx-drilldown-large-over { + background-image: url(images/btn/btn-nx-drilldown-large-over-bg.gif); +} + +.x-nlg .x-btn-nx-drilldown-large-menu-active, +.x-nlg .x-btn-nx-drilldown-large-pressed { + background-image: url(images/btn/btn-nx-drilldown-large-pressed-bg.gif); +} + +.x-nlg .x-btn-nx-drilldown-large-disabled { + background-image: url(images/btn/btn-nx-drilldown-large-disabled-bg.gif); +} + +.x-nbr .x-btn-nx-drilldown-large { + background-image: none; +} + +.x-btn-nx-drilldown-large .x-btn-split-right { + background-image: url(images/button/nx-drilldown-large-s-arrow.png); + padding-right: 38px; +} +.x-btn-nx-drilldown-large .x-btn-split-bottom { + background-image: url(images/button/nx-drilldown-large-s-arrow-b.png); + padding-bottom: 34px; +} + +/**/ +.x-btn-nx-drilldown-large-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-large-focus-corners.gif), sides:url(images/btn/btn-nx-drilldown-large-focus-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-large-focus-fbg.gif), bg:url(images/btn/btn-nx-drilldown-large-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-drilldown-large-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-large-over-corners.gif), sides:url(images/btn/btn-nx-drilldown-large-over-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-large-over-fbg.gif), bg:url(images/btn/btn-nx-drilldown-large-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-drilldown-large-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-large-pressed-corners.gif), sides:url(images/btn/btn-nx-drilldown-large-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-large-pressed-fbg.gif), bg:url(images/btn/btn-nx-drilldown-large-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-drilldown-large-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-large-disabled-corners.gif), sides:url(images/btn/btn-nx-drilldown-large-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-large-disabled-fbg.gif), bg:url(images/btn/btn-nx-drilldown-large-disabled-bg.gif)"; +} + +/**/ +/* */ +.x-btn-nx-drilldown-large { + padding: 0 !important; +} + +.x-btn-nx-drilldown-medium { + border-color: transparent; +} + +.x-btn-nx-drilldown-medium { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 0; + border-style: solid; + background-image: none; + background-color: transparent; +} + +.x-btn-nx-drilldown-medium-mc { + background-image: url(images/btn/btn-nx-drilldown-medium-fbg.gif); + background-position: 0 top; + background-color: transparent; +} + +.x-nlg .x-btn-nx-drilldown-medium { + background-image: url(images/btn/btn-nx-drilldown-medium-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-nx-drilldown-medium { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-nx-drilldown-medium-frameInfo { + font-family: th-3-3-3-3-0-0-0-0-3-3-3-3; +} + +.x-btn-nx-drilldown-medium-tl { + background-position: 0 -6px; +} + +.x-btn-nx-drilldown-medium-tr { + background-position: right -9px; +} + +.x-btn-nx-drilldown-medium-bl { + background-position: 0 -12px; +} + +.x-btn-nx-drilldown-medium-br { + background-position: right -15px; +} + +.x-btn-nx-drilldown-medium-ml { + background-position: 0 top; +} + +.x-btn-nx-drilldown-medium-mr { + background-position: right top; +} + +.x-btn-nx-drilldown-medium-tc { + background-position: 0 0; +} + +.x-btn-nx-drilldown-medium-bc { + background-position: 0 -3px; +} + +.x-btn-nx-drilldown-medium-tr, +.x-btn-nx-drilldown-medium-br, +.x-btn-nx-drilldown-medium-mr { + padding-right: 3px; +} + +.x-btn-nx-drilldown-medium-tl, +.x-btn-nx-drilldown-medium-bl, +.x-btn-nx-drilldown-medium-ml { + padding-left: 3px; +} + +.x-btn-nx-drilldown-medium-tc { + height: 3px; +} + +.x-btn-nx-drilldown-medium-bc { + height: 3px; +} + +.x-btn-nx-drilldown-medium-tl, +.x-btn-nx-drilldown-medium-bl, +.x-btn-nx-drilldown-medium-tr, +.x-btn-nx-drilldown-medium-br, +.x-btn-nx-drilldown-medium-tc, +.x-btn-nx-drilldown-medium-bc, +.x-btn-nx-drilldown-medium-ml, +.x-btn-nx-drilldown-medium-mr { + zoom: 1; +} + +.x-btn-nx-drilldown-medium-ml, +.x-btn-nx-drilldown-medium-mr { + zoom: 1; +} + +.x-btn-nx-drilldown-medium-mc { + padding: 0px 0px 0px 0px; +} + +.x-strict .x-ie7 .x-btn-nx-drilldown-medium-tl, +.x-strict .x-ie7 .x-btn-nx-drilldown-medium-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-nx-drilldown-medium:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-drilldown-medium-fbg.gif), bg:url(images/btn/btn-nx-drilldown-medium-bg.gif)"; +} + +/**/ +/* */ +.x-btn-nx-drilldown-medium .x-btn-inner { + font-size: 18px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #006bbf; + padding: 0 0; +} +.x-btn-nx-drilldown-medium .x-btn-arrow { + background-image: url(images/button/nx-drilldown-medium-arrow.png); +} +.x-btn-nx-drilldown-medium .x-btn-arrow-right { + padding-right: 30px; +} +.x-btn-nx-drilldown-medium .x-btn-arrow-bottom { + padding-bottom: 26px; +} +.x-btn-nx-drilldown-medium .x-btn-glyph { + font-size: 24px; + line-height: 24px; + color: white; +} +.x-ie8m .x-btn-nx-drilldown-medium .x-btn-glyph { + color: white; +} + +.x-btn-nx-drilldown-medium-disabled { + background-image: none; + background-color: transparent; +} +.x-btn-nx-drilldown-medium-disabled .x-btn-inner { + color: #333333; +} + +.x-btn-nx-drilldown-medium-icon .x-btn-button, +.x-btn-nx-drilldown-medium-noicon .x-btn-button { + height: 24px; +} +.x-btn-nx-drilldown-medium-icon .x-btn-inner, +.x-btn-nx-drilldown-medium-noicon .x-btn-inner { + line-height: 24px; +} + +.x-btn-nx-drilldown-medium-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-drilldown-medium-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-drilldown-medium-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-nx-drilldown-medium-icon .x-btn-inner { + width: 24px; + padding: 0; +} +.x-btn-nx-drilldown-medium-icon .x-btn-icon-el { + width: 24px; + height: 24px; +} + +.x-btn-nx-drilldown-medium-icon-text-left .x-btn-button { + height: 24px; +} +.x-btn-nx-drilldown-medium-icon-text-left .x-btn-inner { + line-height: 24px; + padding-left: 29px; +} +.x-btn-nx-drilldown-medium-icon-text-left .x-btn-icon-el { + width: 24px; + right: auto; +} +.x-ie6 .x-btn-nx-drilldown-medium-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-drilldown-medium-icon-text-left .x-btn-icon-el { + height: 24px; +} + +.x-btn-nx-drilldown-medium-icon-text-right .x-btn-button { + height: 24px; +} +.x-btn-nx-drilldown-medium-icon-text-right .x-btn-inner { + line-height: 24px; + padding-right: 29px; +} +.x-btn-nx-drilldown-medium-icon-text-right .x-btn-icon-el { + width: 24px; + left: auto; +} +.x-ie6 .x-btn-nx-drilldown-medium-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-drilldown-medium-icon-text-right .x-btn-icon-el { + height: 24px; +} + +.x-btn-nx-drilldown-medium-icon-text-top .x-btn-inner { + padding-top: 29px; +} +.x-btn-nx-drilldown-medium-icon-text-top .x-btn-icon-el { + height: 24px; + bottom: auto; +} +.x-ie6 .x-btn-nx-drilldown-medium-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-drilldown-medium-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-drilldown-medium-icon-text-bottom .x-btn-inner { + padding-bottom: 29px; +} +.x-btn-nx-drilldown-medium-icon-text-bottom .x-btn-icon-el { + height: 24px; + top: auto; +} +.x-ie6 .x-btn-nx-drilldown-medium-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-drilldown-medium-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-drilldown-medium-focus { + background-image: none; + background-color: transparent; +} +.x-btn-nx-drilldown-medium-focus .x-btn-inner { + color: #0f4976; +} + +.x-btn-nx-drilldown-medium-over { + background-image: none; + background-color: transparent; +} +.x-btn-nx-drilldown-medium-over .x-btn-inner { + color: #0f4976; +} + +.x-btn-nx-drilldown-medium-menu-active, +.x-btn-nx-drilldown-medium-pressed { + background-image: none; + background-color: transparent; +} +.x-btn-nx-drilldown-medium-menu-active .x-btn-inner, +.x-btn-nx-drilldown-medium-pressed .x-btn-inner { + color: #96caee; +} + +.x-btn-nx-drilldown-medium-focus .x-frame-tl, +.x-btn-nx-drilldown-medium-focus .x-frame-bl, +.x-btn-nx-drilldown-medium-focus .x-frame-tr, +.x-btn-nx-drilldown-medium-focus .x-frame-br, +.x-btn-nx-drilldown-medium-focus .x-frame-tc, +.x-btn-nx-drilldown-medium-focus .x-frame-bc { + background-image: url(images/btn/btn-nx-drilldown-medium-focus-corners.gif); +} +.x-btn-nx-drilldown-medium-focus .x-frame-ml, +.x-btn-nx-drilldown-medium-focus .x-frame-mr { + background-image: url(images/btn/btn-nx-drilldown-medium-focus-sides.gif); +} +.x-btn-nx-drilldown-medium-focus .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-nx-drilldown-medium-focus-fbg.gif); +} + +.x-btn-nx-drilldown-medium-over .x-frame-tl, +.x-btn-nx-drilldown-medium-over .x-frame-bl, +.x-btn-nx-drilldown-medium-over .x-frame-tr, +.x-btn-nx-drilldown-medium-over .x-frame-br, +.x-btn-nx-drilldown-medium-over .x-frame-tc, +.x-btn-nx-drilldown-medium-over .x-frame-bc { + background-image: url(images/btn/btn-nx-drilldown-medium-over-corners.gif); +} +.x-btn-nx-drilldown-medium-over .x-frame-ml, +.x-btn-nx-drilldown-medium-over .x-frame-mr { + background-image: url(images/btn/btn-nx-drilldown-medium-over-sides.gif); +} +.x-btn-nx-drilldown-medium-over .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-nx-drilldown-medium-over-fbg.gif); +} + +.x-btn-nx-drilldown-medium-menu-active .x-frame-tl, +.x-btn-nx-drilldown-medium-menu-active .x-frame-bl, +.x-btn-nx-drilldown-medium-menu-active .x-frame-tr, +.x-btn-nx-drilldown-medium-menu-active .x-frame-br, +.x-btn-nx-drilldown-medium-menu-active .x-frame-tc, +.x-btn-nx-drilldown-medium-menu-active .x-frame-bc, +.x-btn-nx-drilldown-medium-pressed .x-frame-tl, +.x-btn-nx-drilldown-medium-pressed .x-frame-bl, +.x-btn-nx-drilldown-medium-pressed .x-frame-tr, +.x-btn-nx-drilldown-medium-pressed .x-frame-br, +.x-btn-nx-drilldown-medium-pressed .x-frame-tc, +.x-btn-nx-drilldown-medium-pressed .x-frame-bc { + background-image: url(images/btn/btn-nx-drilldown-medium-pressed-corners.gif); +} +.x-btn-nx-drilldown-medium-menu-active .x-frame-ml, +.x-btn-nx-drilldown-medium-menu-active .x-frame-mr, +.x-btn-nx-drilldown-medium-pressed .x-frame-ml, +.x-btn-nx-drilldown-medium-pressed .x-frame-mr { + background-image: url(images/btn/btn-nx-drilldown-medium-pressed-sides.gif); +} +.x-btn-nx-drilldown-medium-menu-active .x-frame-mc, +.x-btn-nx-drilldown-medium-pressed .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-nx-drilldown-medium-pressed-fbg.gif); +} + +.x-btn-nx-drilldown-medium-disabled .x-frame-tl, +.x-btn-nx-drilldown-medium-disabled .x-frame-bl, +.x-btn-nx-drilldown-medium-disabled .x-frame-tr, +.x-btn-nx-drilldown-medium-disabled .x-frame-br, +.x-btn-nx-drilldown-medium-disabled .x-frame-tc, +.x-btn-nx-drilldown-medium-disabled .x-frame-bc { + background-image: url(images/btn/btn-nx-drilldown-medium-disabled-corners.gif); +} +.x-btn-nx-drilldown-medium-disabled .x-frame-ml, +.x-btn-nx-drilldown-medium-disabled .x-frame-mr { + background-image: url(images/btn/btn-nx-drilldown-medium-disabled-sides.gif); +} +.x-btn-nx-drilldown-medium-disabled .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-nx-drilldown-medium-disabled-fbg.gif); +} + +.x-nlg .x-btn-nx-drilldown-medium-focus { + background-image: url(images/btn/btn-nx-drilldown-medium-focus-bg.gif); +} + +.x-nlg .x-btn-nx-drilldown-medium-over { + background-image: url(images/btn/btn-nx-drilldown-medium-over-bg.gif); +} + +.x-nlg .x-btn-nx-drilldown-medium-menu-active, +.x-nlg .x-btn-nx-drilldown-medium-pressed { + background-image: url(images/btn/btn-nx-drilldown-medium-pressed-bg.gif); +} + +.x-nlg .x-btn-nx-drilldown-medium-disabled { + background-image: url(images/btn/btn-nx-drilldown-medium-disabled-bg.gif); +} + +.x-nbr .x-btn-nx-drilldown-medium { + background-image: none; +} + +.x-btn-nx-drilldown-medium .x-btn-split-right { + background-image: url(images/button/nx-drilldown-medium-s-arrow.png); + padding-right: 32px; +} +.x-btn-nx-drilldown-medium .x-btn-split-bottom { + background-image: url(images/button/nx-drilldown-medium-s-arrow-b.png); + padding-bottom: 28px; +} + +/**/ +.x-btn-nx-drilldown-medium-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-medium-focus-corners.gif), sides:url(images/btn/btn-nx-drilldown-medium-focus-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-medium-focus-fbg.gif), bg:url(images/btn/btn-nx-drilldown-medium-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-drilldown-medium-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-medium-over-corners.gif), sides:url(images/btn/btn-nx-drilldown-medium-over-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-medium-over-fbg.gif), bg:url(images/btn/btn-nx-drilldown-medium-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-drilldown-medium-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-medium-pressed-corners.gif), sides:url(images/btn/btn-nx-drilldown-medium-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-medium-pressed-fbg.gif), bg:url(images/btn/btn-nx-drilldown-medium-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-drilldown-medium-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-medium-disabled-corners.gif), sides:url(images/btn/btn-nx-drilldown-medium-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-medium-disabled-fbg.gif), bg:url(images/btn/btn-nx-drilldown-medium-disabled-bg.gif)"; +} + +/**/ +/* */ +.x-btn-nx-drilldown-medium { + padding: 0 2px 0 6px; +} +.x-btn-nx-drilldown-medium .x-btn-inner { + text-overflow: ellipsis; +} + +.nx-feature-content .x-panel-header .x-btn-nx-drilldown-medium { + top: 7px !important; +} + +.x-btn-nx-drilldown-toolbar-small { + border-color: transparent; +} + +.x-btn-nx-drilldown-toolbar-small { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + padding: 3px 3px 3px 3px; + border-width: 1px; + border-style: solid; + background-image: none; + background-color: transparent; +} + +.x-btn-nx-drilldown-toolbar-small-mc { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-fbg.gif); + background-position: 0 top; + background-color: transparent; +} + +.x-nlg .x-btn-nx-drilldown-toolbar-small { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-bg.gif); + background-position: 0 top; +} + +.x-nbr .x-btn-nx-drilldown-toolbar-small { + padding: 0 !important; + border-width: 0 !important; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + -ms-border-radius: 0px; + -o-border-radius: 0px; + border-radius: 0px; + background-color: transparent; + background-image: none; +} + +.x-btn-nx-drilldown-toolbar-small-frameInfo { + font-family: th-3-3-3-3-1-1-1-1-3-3-3-3; +} + +.x-btn-nx-drilldown-toolbar-small-tl { + background-position: 0 -6px; +} + +.x-btn-nx-drilldown-toolbar-small-tr { + background-position: right -9px; +} + +.x-btn-nx-drilldown-toolbar-small-bl { + background-position: 0 -12px; +} + +.x-btn-nx-drilldown-toolbar-small-br { + background-position: right -15px; +} + +.x-btn-nx-drilldown-toolbar-small-ml { + background-position: 0 top; +} + +.x-btn-nx-drilldown-toolbar-small-mr { + background-position: right top; +} + +.x-btn-nx-drilldown-toolbar-small-tc { + background-position: 0 0; +} + +.x-btn-nx-drilldown-toolbar-small-bc { + background-position: 0 -3px; +} + +.x-btn-nx-drilldown-toolbar-small-tr, +.x-btn-nx-drilldown-toolbar-small-br, +.x-btn-nx-drilldown-toolbar-small-mr { + padding-right: 3px; +} + +.x-btn-nx-drilldown-toolbar-small-tl, +.x-btn-nx-drilldown-toolbar-small-bl, +.x-btn-nx-drilldown-toolbar-small-ml { + padding-left: 3px; +} + +.x-btn-nx-drilldown-toolbar-small-tc { + height: 3px; +} + +.x-btn-nx-drilldown-toolbar-small-bc { + height: 3px; +} + +.x-btn-nx-drilldown-toolbar-small-tl, +.x-btn-nx-drilldown-toolbar-small-bl, +.x-btn-nx-drilldown-toolbar-small-tr, +.x-btn-nx-drilldown-toolbar-small-br, +.x-btn-nx-drilldown-toolbar-small-tc, +.x-btn-nx-drilldown-toolbar-small-bc, +.x-btn-nx-drilldown-toolbar-small-ml, +.x-btn-nx-drilldown-toolbar-small-mr { + zoom: 1; +} + +.x-btn-nx-drilldown-toolbar-small-ml, +.x-btn-nx-drilldown-toolbar-small-mr { + zoom: 1; +} + +.x-btn-nx-drilldown-toolbar-small-mc { + padding: 1px 1px 1px 1px; +} + +.x-strict .x-ie7 .x-btn-nx-drilldown-toolbar-small-tl, +.x-strict .x-ie7 .x-btn-nx-drilldown-toolbar-small-bl { + position: relative; + right: 0; +} + +/**/ +.x-btn-nx-drilldown-toolbar-small:after { + display: none; + content: "x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-drilldown-toolbar-small-fbg.gif), bg:url(images/btn/btn-nx-drilldown-toolbar-small-bg.gif)"; +} + +/**/ +/* */ +.x-btn-nx-drilldown-toolbar-small .x-btn-inner { + font-size: 13px; + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #006bbf; + padding: 0 0; +} +.x-btn-nx-drilldown-toolbar-small .x-btn-arrow { + background-image: url(images/button/nx-drilldown-toolbar-small-arrow.png); +} +.x-btn-nx-drilldown-toolbar-small .x-btn-arrow-right { + padding-right: 21px; +} +.x-btn-nx-drilldown-toolbar-small .x-btn-arrow-bottom { + padding-bottom: 18px; +} +.x-btn-nx-drilldown-toolbar-small .x-btn-glyph { + font-size: 16px; + line-height: 16px; + color: #006bbf; +} +.x-ie8m .x-btn-nx-drilldown-toolbar-small .x-btn-glyph { + color: #006bbf; +} + +.x-btn-nx-drilldown-toolbar-small-disabled { + background-image: none; + background-color: transparent; +} +.x-btn-nx-drilldown-toolbar-small-disabled .x-btn-inner { + color: #333333; +} + +.x-btn-nx-drilldown-toolbar-small-icon .x-btn-button, +.x-btn-nx-drilldown-toolbar-small-noicon .x-btn-button { + height: 16px; +} +.x-btn-nx-drilldown-toolbar-small-icon .x-btn-inner, +.x-btn-nx-drilldown-toolbar-small-noicon .x-btn-inner { + line-height: 16px; +} + +.x-btn-nx-drilldown-toolbar-small-icon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-drilldown-toolbar-small-noicon .x-btn-arrow-right .x-btn-inner, +.x-btn-nx-drilldown-toolbar-small-icon-text-left .x-btn-arrow-right .x-btn-inner { + padding-right: 0; +} + +.x-btn-nx-drilldown-toolbar-small-icon .x-btn-inner { + width: 16px; + padding: 0; +} +.x-btn-nx-drilldown-toolbar-small-icon .x-btn-icon-el { + width: 16px; + height: 16px; +} + +.x-btn-nx-drilldown-toolbar-small-icon-text-left .x-btn-button { + height: 16px; +} +.x-btn-nx-drilldown-toolbar-small-icon-text-left .x-btn-inner { + line-height: 16px; + padding-left: 21px; +} +.x-btn-nx-drilldown-toolbar-small-icon-text-left .x-btn-icon-el { + width: 16px; + right: auto; +} +.x-ie6 .x-btn-nx-drilldown-toolbar-small-icon-text-left .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-drilldown-toolbar-small-icon-text-left .x-btn-icon-el { + height: 16px; +} + +.x-btn-nx-drilldown-toolbar-small-icon-text-right .x-btn-button { + height: 16px; +} +.x-btn-nx-drilldown-toolbar-small-icon-text-right .x-btn-inner { + line-height: 16px; + padding-right: 21px; +} +.x-btn-nx-drilldown-toolbar-small-icon-text-right .x-btn-icon-el { + width: 16px; + left: auto; +} +.x-ie6 .x-btn-nx-drilldown-toolbar-small-icon-text-right .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-drilldown-toolbar-small-icon-text-right .x-btn-icon-el { + height: 16px; +} + +.x-btn-nx-drilldown-toolbar-small-icon-text-top .x-btn-inner { + padding-top: 21px; +} +.x-btn-nx-drilldown-toolbar-small-icon-text-top .x-btn-icon-el { + height: 16px; + bottom: auto; +} +.x-ie6 .x-btn-nx-drilldown-toolbar-small-icon-text-top .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-drilldown-toolbar-small-icon-text-top .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-drilldown-toolbar-small-icon-text-bottom .x-btn-inner { + padding-bottom: 21px; +} +.x-btn-nx-drilldown-toolbar-small-icon-text-bottom .x-btn-icon-el { + height: 16px; + top: auto; +} +.x-ie6 .x-btn-nx-drilldown-toolbar-small-icon-text-bottom .x-btn-icon-el, .x-quirks .x-ie .x-btn-nx-drilldown-toolbar-small-icon-text-bottom .x-btn-icon-el { + width: 100%; +} + +.x-btn-nx-drilldown-toolbar-small-focus { + background-image: none; + background-color: transparent; +} +.x-btn-nx-drilldown-toolbar-small-focus .x-btn-inner { + color: #0f4976; +} + +.x-btn-nx-drilldown-toolbar-small-over { + background-image: none; + background-color: transparent; +} +.x-btn-nx-drilldown-toolbar-small-over .x-btn-inner { + color: #0f4976; +} + +.x-btn-nx-drilldown-toolbar-small-menu-active, +.x-btn-nx-drilldown-toolbar-small-pressed { + background-image: none; + background-color: transparent; +} +.x-btn-nx-drilldown-toolbar-small-menu-active .x-btn-inner, +.x-btn-nx-drilldown-toolbar-small-pressed .x-btn-inner { + color: #96caee; +} + +.x-btn-nx-drilldown-toolbar-small-focus .x-frame-tl, +.x-btn-nx-drilldown-toolbar-small-focus .x-frame-bl, +.x-btn-nx-drilldown-toolbar-small-focus .x-frame-tr, +.x-btn-nx-drilldown-toolbar-small-focus .x-frame-br, +.x-btn-nx-drilldown-toolbar-small-focus .x-frame-tc, +.x-btn-nx-drilldown-toolbar-small-focus .x-frame-bc { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-focus-corners.gif); +} +.x-btn-nx-drilldown-toolbar-small-focus .x-frame-ml, +.x-btn-nx-drilldown-toolbar-small-focus .x-frame-mr { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-focus-sides.gif); +} +.x-btn-nx-drilldown-toolbar-small-focus .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-focus-fbg.gif); +} + +.x-btn-nx-drilldown-toolbar-small-over .x-frame-tl, +.x-btn-nx-drilldown-toolbar-small-over .x-frame-bl, +.x-btn-nx-drilldown-toolbar-small-over .x-frame-tr, +.x-btn-nx-drilldown-toolbar-small-over .x-frame-br, +.x-btn-nx-drilldown-toolbar-small-over .x-frame-tc, +.x-btn-nx-drilldown-toolbar-small-over .x-frame-bc { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-over-corners.gif); +} +.x-btn-nx-drilldown-toolbar-small-over .x-frame-ml, +.x-btn-nx-drilldown-toolbar-small-over .x-frame-mr { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-over-sides.gif); +} +.x-btn-nx-drilldown-toolbar-small-over .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-over-fbg.gif); +} + +.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-tl, +.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-bl, +.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-tr, +.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-br, +.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-tc, +.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-bc, +.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-tl, +.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-bl, +.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-tr, +.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-br, +.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-tc, +.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-bc { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-pressed-corners.gif); +} +.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-ml, +.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-mr, +.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-ml, +.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-mr { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-pressed-sides.gif); +} +.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-mc, +.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-pressed-fbg.gif); +} + +.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-tl, +.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-bl, +.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-tr, +.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-br, +.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-tc, +.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-bc { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-disabled-corners.gif); +} +.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-ml, +.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-mr { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-disabled-sides.gif); +} +.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-mc { + background-color: transparent; + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-disabled-fbg.gif); +} + +.x-nlg .x-btn-nx-drilldown-toolbar-small-focus { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-focus-bg.gif); +} + +.x-nlg .x-btn-nx-drilldown-toolbar-small-over { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-over-bg.gif); +} + +.x-nlg .x-btn-nx-drilldown-toolbar-small-menu-active, +.x-nlg .x-btn-nx-drilldown-toolbar-small-pressed { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-pressed-bg.gif); +} + +.x-nlg .x-btn-nx-drilldown-toolbar-small-disabled { + background-image: url(images/btn/btn-nx-drilldown-toolbar-small-disabled-bg.gif); +} + +.x-nbr .x-btn-nx-drilldown-toolbar-small { + background-image: none; +} + +.x-btn-nx-drilldown-toolbar-small .x-btn-split-right { + background-image: url(images/button/nx-drilldown-toolbar-small-s-arrow.png); + padding-right: 23px; +} +.x-btn-nx-drilldown-toolbar-small .x-btn-split-bottom { + background-image: url(images/button/nx-drilldown-toolbar-small-s-arrow-b.png); + padding-bottom: 20px; +} + +/**/ +.x-btn-nx-drilldown-toolbar-small-focus:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-toolbar-small-focus-corners.gif), sides:url(images/btn/btn-nx-drilldown-toolbar-small-focus-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-toolbar-small-focus-fbg.gif), bg:url(images/btn/btn-nx-drilldown-toolbar-small-focus-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-drilldown-toolbar-small-over:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-toolbar-small-over-corners.gif), sides:url(images/btn/btn-nx-drilldown-toolbar-small-over-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-toolbar-small-over-fbg.gif), bg:url(images/btn/btn-nx-drilldown-toolbar-small-over-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-drilldown-toolbar-small-pressed:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-toolbar-small-pressed-corners.gif), sides:url(images/btn/btn-nx-drilldown-toolbar-small-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-toolbar-small-pressed-fbg.gif), bg:url(images/btn/btn-nx-drilldown-toolbar-small-pressed-bg.gif)"; +} + +/**/ +/* */ +/**/ +.x-btn-nx-drilldown-toolbar-small-disabled:after { + display: none; + content: "x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-toolbar-small-disabled-corners.gif), sides:url(images/btn/btn-nx-drilldown-toolbar-small-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-toolbar-small-disabled-fbg.gif), bg:url(images/btn/btn-nx-drilldown-toolbar-small-disabled-bg.gif)"; +} + +/**/ +/* */ +.nx-breadcrumb-separator { + font-size: 18px; + top: 7px !important; + color: gray; + padding-left: 12px; +} + +.nx-breadcrumb-icon { + top: 9px !important; + margin-left: 13px; +} + +/** + * @class NX.wizard.Panel + */ +.nx-wizard-panel { + padding: 12px 12px 12px 12px; + border-top: 1px solid #dddddd !important; +} +.nx-wizard-panel .screencontainer, .nx-wizard-panel .screenheader { + background-color: white; + border: 1px solid #dddddd !important; +} +.nx-wizard-panel .screencontainer { + padding: 12px 12px 12px 12px; + border-top: 0 !important; +} +.nx-wizard-panel .screenheader { + padding: 7px 8px 0 8px !important; + border-bottom: 0 !important; +} +.nx-wizard-panel .screenheader .title { + font-size: 22px; + font-weight: bold; +} +.nx-wizard-panel .screenheader .progress { + font-size: 13px; + font-weight: bold; + color: #777777; +} + +/** + * @class NX.coreui.view.capability.CapabilityList + */ +.nx-red-marker td { + color: #db2852; +} + +/** + * @class NX.coreui.view.component.ComponentDetails + */ +.nx-coreui-component-details { + background-color: white; +} + +/** + * @class NX.coreui.view.component.ComponentAssetInfo + */ +.nx-coreui-component-componentassetinfo { + border-top: 1px solid silver; +} +.nx-coreui-component-componentassetinfo > .x-panel-header { + background-color: white; +} +.nx-coreui-component-componentassetinfo > .x-panel-header * { + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 18px; + line-height: 20px; + color: black; +} + +/** + * @class NX.coreui.view.component.ComponentInfo + */ +.nx-coreui-component-componentinfo { + border-top: 1px solid silver; +} +.nx-coreui-component-componentinfo > .x-panel-header { + background-color: white; +} +.nx-coreui-component-componentinfo > .x-panel-header * { + font-weight: normal; + font-family: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 18px; + line-height: 20px; + color: black; +} + +/** + * @class NX.coreui.view.component.AssetAttributes + */ +.nx-coreui-component-assetattributes .attribute-value { + word-wrap: break-word; + white-space: normal !important; +} + +/** + * @class NX.coreui.view.logging.LogViewer + */ +.nx-log-viewer-field, +.nx-log-viewer-field textarea { + background-color: white !important; +} + +/** + * @class NX.coreui.view.search.SearchFeature + */ +.nx-coreui-searchfeature .criteria { + background-color: white; + padding: 0 0 10px 10px; +} +.nx-coreui-searchfeature .criteria .more-criteria { + margin: 26px 0 0 0; +} + +/** + * @class NX.coreui.view.support.Metrics + */ +.nx-coreui-support-metrics .metricwidget { + margin: 0 20px 20px 0; +} + +/** + * @class NX.proui.view.usertoken.UserTokenDetails + */ +.nx-user-token-field { + padding: 2px; + font-family: "Courier New", Courier, monospace; + font-size: 13px; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-prod_01.css b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-prod_01.css new file mode 100644 index 0000000000000000000000000000000000000000..41ba4b1b560a2e572f374a989def38d38aacff5f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-prod_01.css @@ -0,0 +1 @@ +a:not([class*="x-"]){color:#006bbf;text-decoration:none}a:not([class*="x-"]):visited{color:#14629e}a:not([class*="x-"]):focus,a:not([class*="x-"]):hover{color:#0f4976;text-decoration:underline}a:not([class*="x-"]):active{color:#96caee}p{margin-top:0;margin-bottom:1em}th{text-align:left}td:not([class|=x]){padding-right:5px}td:not([class|=x]):last-child{padding-right:0}.x-fa:before{font-family:FontAwesome}@font-face{font-family:'FontAwesome';src:url("font-awesome/fontawesome-webfont.eot?v=4.5.0");src:url("font-awesome/fontawesome-webfont.eot?#iefix&v=4.5.0") format("embedded-opentype"),url("font-awesome/fontawesome-webfont.woff2?v=4.5.0") format("woff2"),url("font-awesome/fontawesome-webfont.woff?v=4.5.0") format("woff"),url("font-awesome/fontawesome-webfont.ttf?v=4.5.0") format("truetype"),url("font-awesome/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.286em;text-align:center}.fa-ul{padding-left:0;margin-left:2.143em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.143em;width:2.143em;top:.143em;text-align:center}.fa-li.fa-lg{left:-1.857em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0);-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:white}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.x-dd-drag-proxy,.x-dd-drag-current{z-index:1000000!important;pointer-events:none}.x-body{margin:0}img{border:0}.x-border-box,.x-border-box *{box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-webkit-box-sizing:border-box}.x-rtl{direction:rtl}.x-ltr{direction:ltr}.x-clear{overflow:hidden;clear:both;font-size:0;line-height:0;display:table}.x-strict .x-ie7 .x-clear{height:0;width:0}.x-layer{position:absolute!important;overflow:hidden;zoom:1}.x-fixed-layer{position:fixed!important;overflow:hidden;zoom:1}.x-shim{position:absolute;left:0;top:0;overflow:hidden;filter:alpha(opacity=0);opacity:0}.x-hide-display{display:none!important}.x-hide-visibility{visibility:hidden!important}.x-ie6 .x-item-disabled{filter:none}.x-hidden,.x-hide-offsets{display:block!important;visibility:hidden!important;position:absolute!important;top:-10000px!important}.x-hide-nosize{height:0!important;width:0!important}.x-hide-clip{position:absolute!important;clip:rect(0,0,0,0);clip:rect(0 0 0 0)}.x-masked-relative{position:relative}.x-ie-shadow{background-color:#777;display:none;position:absolute;overflow:hidden;zoom:1}.x-unselectable{user-select:none;-o-user-select:none;-ms-user-select:none;-moz-user-select:-moz-none;-webkit-user-select:none;cursor:default}.x-selectable{cursor:auto;-moz-user-select:text;-webkit-user-select:text;-ms-user-select:text;user-select:text;-o-user-select:text}.x-list-plain{list-style-type:none;margin:0;padding:0}.x-table-plain{border-collapse:collapse;border-spacing:0;font-size:1em}.x-frame-tl,.x-frame-tr,.x-frame-tc,.x-frame-bl,.x-frame-br,.x-frame-bc{overflow:hidden;background-repeat:no-repeat}.x-frame-tc,.x-frame-bc{background-repeat:repeat-x}.x-frame-mc{background-repeat:repeat-x;overflow:hidden}.x-proxy-el{position:absolute;background:#b4b4b4;filter:alpha(opacity=80);opacity:.8}.x-css-shadow{position:absolute;-webkit-border-radius:5px;-moz-border-radius:5px;-ms-border-radius:5px;-o-border-radius:5px;border-radius:5px}.x-item-disabled,.x-item-disabled *{cursor:default}.x-webkit *:focus{outline:none!important}.x-box-item{position:absolute!important;left:0;top:0}.x-docked{position:absolute!important;z-index:1}.x-docked-vertical{position:static}.x-docked-top{border-bottom-width:0!important}.x-docked-bottom{border-top-width:0!important}.x-docked-left{border-right-width:0!important}.x-docked-right{border-left-width:0!important}.x-docked-noborder-top{border-top-width:0!important}.x-docked-noborder-right{border-right-width:0!important}.x-docked-noborder-bottom{border-bottom-width:0!important}.x-docked-noborder-left{border-left-width:0!important}.x-noborder-l{border-left-width:0!important}.x-noborder-b{border-bottom-width:0!important}.x-noborder-bl{border-bottom-width:0!important;border-left-width:0!important}.x-noborder-r{border-right-width:0!important}.x-noborder-rl{border-right-width:0!important;border-left-width:0!important}.x-noborder-rb{border-right-width:0!important;border-bottom-width:0!important}.x-noborder-rbl{border-right-width:0!important;border-bottom-width:0!important;border-left-width:0!important}.x-noborder-t{border-top-width:0!important}.x-noborder-tl{border-top-width:0!important;border-left-width:0!important}.x-noborder-tb{border-top-width:0!important;border-bottom-width:0!important}.x-noborder-tbl{border-top-width:0!important;border-bottom-width:0!important;border-left-width:0!important}.x-noborder-tr{border-top-width:0!important;border-right-width:0!important}.x-noborder-trl{border-top-width:0!important;border-right-width:0!important;border-left-width:0!important}.x-noborder-trb{border-top-width:0!important;border-right-width:0!important;border-bottom-width:0!important}.x-noborder-trbl{border-width:0!important}.x-btn{display:inline-block;position:relative;zoom:1;*display:inline;outline:0;cursor:pointer;white-space:nowrap;vertical-align:middle;text-decoration:none}.x-btn-wrap{position:relative;display:block}.x-btn-button{position:relative;display:block;text-decoration:none;overflow:hidden;zoom:1}.x-btn-inner{display:block;white-space:nowrap;overflow:hidden;zoom:1}.x-btn-icon-el{top:0;right:0;bottom:0;left:0;position:absolute;background-repeat:no-repeat;text-align:center}.x-btn-inner-center{text-align:center}.x-btn-inner-left{text-align:left}.x-btn-inner-right{text-align:right}.x-box-layout-ct{overflow:hidden;zoom:1}.x-box-target{position:absolute;width:20000px;top:0;left:0;height:1px}.x-box-inner{overflow:hidden;zoom:1;position:relative;left:0;top:0}.x-horizontal-box-overflow-body{float:left}.x-box-scroller{position:relative;background-repeat:no-repeat}.x-box-scroller-left,.x-box-scroller-right{float:left;height:100%;z-index:5}.x-box-scroller-top .x-box-scroller,.x-box-scroller-bottom .x-box-scroller{line-height:0;font-size:0;background-position:center 0}.x-box-menu-after{float:right}.x-toolbar-text{white-space:nowrap}.x-toolbar-separator{display:block;font-size:1px;overflow:hidden;cursor:default;border:0;width:0;height:0;line-height:0}.x-quirks .x-ie .x-toolbar .x-toolbar-separator-horizontal{width:2px}.x-toolbar-scroller{padding-left:0}.x-toolbar-plain{border:0}.x-header-icon{background-repeat:no-repeat;background-position:0 0;vertical-align:middle;text-align:center}.x-header-text-container{overflow:hidden;-o-text-overflow:ellipsis;text-overflow:ellipsis}.x-dd-drag-repair .x-dd-drag-ghost{filter:alpha(opacity=60);opacity:.6}.x-dd-drag-repair .x-dd-drop-icon{display:none}.x-dd-drag-ghost{filter:alpha(opacity=85);opacity:.85;padding:5px;padding-left:20px;white-space:nowrap;color:#000;font:normal 12px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border:1px solid;border-color:#ddd #bbb #bbb #ddd;background-color:#fff}.x-dd-drop-icon{position:absolute;top:3px;left:3px;display:block;width:16px;height:16px;background-color:transparent;background-position:center;background-repeat:no-repeat;z-index:1}.x-dd-drop-ok .x-dd-drop-icon{background-image:url(images/dd/drop-yes.png)}.x-dd-drop-ok-add .x-dd-drop-icon{background-image:url(images/dd/drop-add.png)}.x-dd-drop-nodrop div.x-dd-drop-icon{background-image:url(images/dd/drop-no.png)}.x-panel,.x-plain{overflow:hidden;position:relative}.x-panel{outline:0}.x-ie .x-panel-header,.x-ie .x-panel-header-tl,.x-ie .x-panel-header-tc,.x-ie .x-panel-header-tr,.x-ie .x-panel-header-ml,.x-ie .x-panel-header-mc,.x-ie .x-panel-header-mr,.x-ie .x-panel-header-bl,.x-ie .x-panel-header-bc,.x-ie .x-panel-header-br{zoom:1}.x-ie8 td.x-frame-mc{vertical-align:top}.x-panel-body{overflow:hidden;position:relative}.x-nlg .x-panel-header-vertical .x-frame-mc{background-repeat:repeat-y}.x-panel-header-plain,.x-panel-body-plain{border:0;padding:0}.x-tip{position:absolute;overflow:visible}.x-tip-body{overflow:hidden;position:relative}.x-tip-anchor{position:absolute;overflow:hidden;border-style:solid}div.x-editor{overflow:visible}.x-form-item-label{display:block}.x-form-item-label-right{text-align:right}.x-form-item-label-top{display:block;zoom:1}.x-form-invalid-icon{overflow:hidden}.x-form-invalid-icon ul{display:none}.x-window{outline:0;overflow:hidden}.x-window .x-window-wrap{position:relative}.x-window-body{position:relative;overflow:hidden}.x-form-textarea{overflow:auto;resize:none}.x-safari.x-mac .x-form-textarea{margin-bottom:-2px}.x-form-display-field-body{vertical-align:top}.x-progress{position:relative;border-style:solid;overflow:hidden}.x-progress-bar{overflow:hidden;position:absolute;width:0;height:100%}.x-progress-text{overflow:hidden;position:absolute}.x-message-box .x-form-display-field{height:auto}.x-fit-item{position:relative}.x-grid-row,.x-grid-data-row{outline:0}.x-grid-view{overflow:hidden;position:relative}.x-grid-table{table-layout:fixed;border-collapse:separate}.x-grid-td{overflow:hidden;border-width:0;vertical-align:top}.x-grid-cell-inner{overflow:hidden;white-space:nowrap;zoom:1}.x-grid-resize-marker{position:absolute;z-index:5;top:0}.x-mask{z-index:100;position:absolute;top:0;left:0;width:100%;height:100%;zoom:1}.x-mask-fixed{position:fixed}.x-mask-shim{z-index:100;position:absolute;top:0;left:0;width:100%;height:100%}.x-mask-msg{z-index:101;position:absolute}.col-move-top,.col-move-bottom{position:absolute;top:0;line-height:0;font-size:0;overflow:hidden;z-index:20000;background:no-repeat center top transparent}.x-grid-header-ct{cursor:default}.x-column-header{position:absolute;overflow:hidden;background-repeat:repeat-x}.x-column-header-inner{zoom:1;white-space:nowrap;position:relative;overflow:hidden}.x-column-header-text{white-space:nowrap;background-repeat:no-repeat}.x-column-header-trigger{display:none;height:100%;background-repeat:no-repeat;position:absolute;right:0;top:0;z-index:2}.x-column-header-over .x-column-header-trigger,.x-column-header-open .x-column-header-trigger{display:block}.x-column-header-align-right{text-align:right}.x-column-header-align-left{text-align:left}.x-column-header-align-center{text-align:center}.x-autowidth-table .x-grid-table{table-layout:auto;width:auto!important}.x-tree-view{overflow:hidden}.x-tree-elbow-img,.x-tree-icon{background-repeat:no-repeat;background-position:0 center;vertical-align:top}.x-tree-checkbox{border:0;padding:0;vertical-align:top;position:relative;background-color:transparent}.x-tree-animator-wrap{overflow:hidden}.x-tree-node-text{zoom:1}.x-form-cb-wrap{vertical-align:top}.x-form-cb-wrap-inner{position:relative;zoom:1}.x-form-cb{position:absolute;left:0;right:auto;vertical-align:top;overflow:hidden;padding:0;border:0}.x-form-cb::-moz-focus-inner{padding:0;border:0}.x-form-cb-after{left:auto;right:0}.x-form-cb-label{display:inline-block;zoom:1}td.x-grid-rowwrap .x-grid-table{border:0}td.x-grid-rowwrap .x-grid-cell{border-bottom:0;background-color:transparent}.x-grid-rowbody{zoom:1}.x-grid-row-body-hidden{display:none}.x-grid-row-expander{font-size:0;line-height:0}.x-slider{outline:0;zoom:1;position:relative}.x-slider-inner{position:relative;left:0;top:0;overflow:visible;zoom:1}.x-slider-vert .x-slider-inner{background:repeat-y 0 0}.x-slider-end{zoom:1}.x-slider-thumb{position:absolute;background:no-repeat 0 0}.x-slider-horz .x-slider-thumb{left:0}.x-slider-vert .x-slider-thumb{bottom:0}.x-menu{outline:0}.x-menu-item{white-space:nowrap;overflow:hidden}.x-menu-item-cmp{margin:2px}.x-menu-item-cmp .x-field-label-cell{vertical-align:middle}.x-menu-icon-separator{position:absolute;top:0;z-index:0;height:100%;overflow:hidden}.x-menu-plain .x-menu-icon-separator{display:none}.x-menu-item-link{text-decoration:none;outline:0;display:block}.x-menu-item-text{display:inline-block;zoom:1}.x-menu-item-icon,.x-menu-item-icon-right,.x-menu-item-arrow{font-size:0;position:absolute;text-align:center}.x-form-trigger{cursor:pointer;overflow:hidden;background-repeat:no-repeat}.x-item-disabled .x-form-trigger{cursor:default}.x-trigger-noedit{cursor:default}.x-form-trigger-wrap{vertical-align:top;border-collapse:separate}.x-surface{display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;overflow:hidden}.rvml{behavior:url(#default#VML)}.x-surface tspan{user-select:none;-o-user-select:none;-ms-user-select:none;-moz-user-select:-moz-none;-webkit-user-select:none;cursor:default}.x-vml-sprite{position:absolute;left:0;top:0;width:1px;height:1px}.x-vml-group{position:absolute;left:0;top:0;width:1000px;height:1000px}.x-vml-measure-span{position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;display:inline}.x-vml-base{position:relative;top:0;left:0;overflow:hidden;display:inline-block}.x-vml-base{position:relative;top:0;left:0;overflow:hidden;display:inline-block}svg,vml{overflow:hidden}.x-table-layout{font-size:1em}.x-btn-group{position:relative;overflow:hidden}.x-btn-group-body{position:relative;zoom:1}.x-btn-group-body .x-table-layout-cell{vertical-align:top}.x-viewport,.x-viewport body{margin:0;padding:0;border:0 none;overflow:hidden;height:100%;position:static}.x-fieldset{display:block;position:relative}.x-fieldset-header{overflow:hidden}.x-fieldset-header .x-form-item,.x-fieldset-header .x-tool{float:left}.x-fieldset-header .x-fieldset-header-text{float:left}.x-fieldset-header .x-form-cb-wrap{font-size:0;line-height:0;height:auto}.x-fieldset-header .x-form-cb{margin:0;position:static}.x-form-spinner-up,.x-form-spinner-down{font-size:0}.x-strict .x-ie9 .x-boundlist-list-ct{min-height:0\%}.x-form-item{vertical-align:top;table-layout:fixed}.x-form-item-body{position:relative}.x-form-form-item td{border-top:1px solid transparent}.x-datepicker{position:relative}.x-datepicker .x-monthpicker{left:0;top:0;display:block}.x-datepicker .x-monthpicker-months,.x-datepicker .x-monthpicker-years{height:100%}.x-datepicker-inner{table-layout:fixed;width:100%;border-collapse:separate}.x-datepicker-cell{padding:0}.x-datepicker-header{position:relative;zoom:1}.x-datepicker-arrow{position:absolute;outline:0;font-size:0}.x-datepicker-column-header{padding:0}.x-datepicker-date{display:block;zoom:1;text-decoration:none}.x-monthpicker{display:table}.x-monthpicker-body{height:100%;position:relative}.x-monthpicker-months,.x-monthpicker-years{float:left}.x-monthpicker-item{float:left}.x-monthpicker-item-inner{display:block;text-decoration:none}.x-monthpicker-yearnav-button-ct{float:left;text-align:center}.x-monthpicker-yearnav-button{display:inline-block;outline:0;font-size:0}.x-monthpicker-buttons{width:100%}.x-datepicker .x-monthpicker-buttons{position:absolute;bottom:0}.x-form-file-btn{overflow:hidden}.x-form-file-input{border:0;position:absolute;cursor:pointer;top:-2px;right:-2px;filter:alpha(opacity=0);opacity:0;font-size:1000px}.x-form-item-hidden{margin:0}.x-color-picker-item{float:left;text-decoration:none}.x-color-picker-item-inner{display:block;font-size:1px}.x-html-editor-tb .x-toolbar{position:static!important}.x-htmleditor-iframe{display:block;overflow:auto}.x-grid-cell-inner-action-col{line-height:0;font-size:0}.x-grid-cell-inner-checkcolumn{line-height:0;font-size:0}.x-grid-group,.x-grid-group-body,.x-grid-group-hd{zoom:1}.x-group-hd-container{overflow:hidden}.x-grid-group-hd{white-space:nowrap;outline:0}.x-grid-row-body-hidden,.x-grid-group-collapsed{display:none}.x-grid-editor .x-form-cb-wrap{text-align:center}.x-grid-editor .x-form-cb{position:static}.x-grid-editor .x-form-display-field{margin:0;white-space:nowrap;overflow:hidden}.x-grid-editor div.x-form-action-col-field{line-height:0}.x-grid-row-editor{position:absolute;overflow:visible;z-index:1}.x-grid-row-editor-buttons{position:absolute;white-space:nowrap}.x-abs-layout-ct{position:relative}.x-abs-layout-item{position:absolute!important}.x-splitter{font-size:1px}.x-splitter-horizontal{cursor:e-resize;cursor:row-resize}.x-splitter-vertical{cursor:e-resize;cursor:col-resize}.x-splitter-collapsed,.x-splitter-horizontal-noresize,.x-splitter-vertical-noresize{cursor:default}.x-splitter-active{z-index:4}.x-collapse-el{position:absolute;background-repeat:no-repeat}.x-border-layout-ct{overflow:hidden;zoom:1}.x-border-layout-ct{position:relative}.x-border-region-slide-in{z-index:5}.x-region-collapsed-placeholder{z-index:4}.x-column{float:left}.x-ie6 .x-column{display:inline}.x-quirks .x-ie .x-form-layout-table,.x-quirks .x-ie .x-form-layout-table tbody tr.x-form-item{position:relative}.x-form-layout-table{border-collapse:separate;border-spacing:0 2px}.x-ie6 .x-form-layout-table{border-collapse:collapse;border-spacing:0}.x-resizable-overlay{position:absolute;left:0;top:0;width:100%;height:100%;display:none;z-index:200000;background-color:#fff;filter:alpha(opacity=0);opacity:0}.x-resizable-wrapped{box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-webkit-box-sizing:border-box}a.x-tab{text-decoration:none}a.x-tab .x-tab-inner-left{text-align:left}a.x-tab .x-tab-inner-right{text-align:right}.x-tab-bar{position:relative}.x-column-header-checkbox .x-column-header-text{display:block;background-repeat:no-repeat;font-size:0}.x-grid-cell-row-checker{vertical-align:middle;background-repeat:no-repeat;font-size:0}.x-tab{outline:0;display:block;white-space:nowrap;z-index:1}.x-tab-active{z-index:3}.x-tab-wrap{display:block;position:relative}.x-tab-button{zoom:1;display:block}.x-tab-inner{display:block;text-align:center;white-space:nowrap;text-overflow:ellipsis;-o-text-overflow:ellipsis;overflow:hidden;zoom:1}.x-btn-icon-el{top:0;right:0;bottom:0;left:0;position:absolute;background-repeat:no-repeat;text-align:center}.x-tab-bar{z-index:1}.x-tab-bar-body{z-index:2;position:relative}.x-tab-bar-strip{position:absolute;line-height:0;font-size:0;z-index:1}.x-tab-bar-horizontal .x-tab-bar-strip{width:100%;left:0}.x-tab-bar-vertical .x-tab-bar-strip{height:100%;top:0}.x-tab-bar-strip-top{bottom:0}.x-tab-bar-strip-bottom{top:0}.x-tab-bar-strip-left{right:0}.x-tab-bar-strip-right{left:0}.x-tab-bar-plain{background:transparent!important}.x-tab-icon-el{position:absolute;background-repeat:no-repeat;top:0;left:0;right:auto;bottom:0}.x-tab-close-btn{display:block;position:absolute;font-size:0;line-height:0;background:no-repeat}.x-tab-mc{overflow:visible}.x-body{color:#333;font-size:13px;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;background:#f5f5f5}.x-animating-size,.x-collapsed{overflow:hidden!important}.x-btn-default-small{border-color:#333}.x-btn-default-small{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:#606060;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#6f6f6f),color-stop(50%,#606060),color-stop(51%,#5b5b5b),color-stop(100%,#606060));background-image:-webkit-linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060);background-image:-moz-linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060);background-image:-o-linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060);background-image:-ms-linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060);background-image:linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060)}.x-btn-default-small-mc{background-image:url(images/btn/btn-default-small-fbg.gif);background-position:0 top;background-color:#606060}.x-nlg .x-btn-default-small{background-image:url(images/btn/btn-default-small-bg.gif);background-position:0 top}.x-nbr .x-btn-default-small{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-default-small-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-default-small-tl{background-position:0 -6px}.x-btn-default-small-tr{background-position:right -9px}.x-btn-default-small-bl{background-position:0 -12px}.x-btn-default-small-br{background-position:right -15px}.x-btn-default-small-ml{background-position:0 top}.x-btn-default-small-mr{background-position:right top}.x-btn-default-small-tc{background-position:0 0}.x-btn-default-small-bc{background-position:0 -3px}.x-btn-default-small-tr,.x-btn-default-small-br,.x-btn-default-small-mr{padding-right:3px}.x-btn-default-small-tl,.x-btn-default-small-bl,.x-btn-default-small-ml{padding-left:3px}.x-btn-default-small-tc{height:3px}.x-btn-default-small-bc{height:3px}.x-btn-default-small-tl,.x-btn-default-small-bl,.x-btn-default-small-tr,.x-btn-default-small-br,.x-btn-default-small-tc,.x-btn-default-small-bc,.x-btn-default-small-ml,.x-btn-default-small-mr{zoom:1;background-image:url(images/btn/btn-default-small-corners.gif)}.x-btn-default-small-ml,.x-btn-default-small-mr{zoom:1;background-image:url(images/btn/btn-default-small-sides.gif)}.x-btn-default-small-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-default-small-tl,.x-strict .x-ie7 .x-btn-default-small-bl{position:relative;right:0}.x-btn-default-small:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-small-fbg.gif), bg:url(images/btn/btn-default-small-bg.gif), corners:url(images/btn/btn-default-small-corners.gif), sides:url(images/btn/btn-default-small-sides.gif)"}.x-btn-default-small .x-btn-inner{font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:white;padding:0 5px}.x-btn-default-small .x-btn-arrow{background-image:url(images/button/default-small-arrow.png)}.x-btn-default-small .x-btn-arrow-right{padding-right:21px}.x-btn-default-small .x-btn-arrow-bottom{padding-bottom:18px}.x-btn-default-small .x-btn-glyph{font-size:16px;line-height:16px;color:white}.x-ie8m .x-btn-default-small .x-btn-glyph{color:white}.x-btn-default-small-disabled{background-image:none;background-color:#444;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#565656),color-stop(50%,#444),color-stop(51%,#404040),color-stop(100%,#444));background-image:-webkit-linear-gradient(top,#565656,#444 50%,#404040 51%,#444);background-image:-moz-linear-gradient(top,#565656,#444 50%,#404040 51%,#444);background-image:-o-linear-gradient(top,#565656,#444 50%,#404040 51%,#444);background-image:-ms-linear-gradient(top,#565656,#444 50%,#404040 51%,#444);background-image:linear-gradient(top,#565656,#444 50%,#404040 51%,#444)}.x-btn-default-small-icon .x-btn-button,.x-btn-default-small-noicon .x-btn-button{height:16px}.x-btn-default-small-icon .x-btn-inner,.x-btn-default-small-noicon .x-btn-inner{line-height:16px}.x-btn-default-small-icon .x-btn-arrow-right .x-btn-inner,.x-btn-default-small-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-default-small-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-default-small-icon .x-btn-inner{width:16px;padding:0}.x-btn-default-small-icon .x-btn-icon-el{width:16px;height:16px}.x-btn-default-small-icon-text-left .x-btn-button{height:16px}.x-btn-default-small-icon-text-left .x-btn-inner{line-height:16px;padding-left:21px}.x-btn-default-small-icon-text-left .x-btn-icon-el{width:16px;right:auto}.x-ie6 .x-btn-default-small-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-small-icon-text-left .x-btn-icon-el{height:16px}.x-btn-default-small-icon-text-right .x-btn-button{height:16px}.x-btn-default-small-icon-text-right .x-btn-inner{line-height:16px;padding-right:21px}.x-btn-default-small-icon-text-right .x-btn-icon-el{width:16px;left:auto}.x-ie6 .x-btn-default-small-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-small-icon-text-right .x-btn-icon-el{height:16px}.x-btn-default-small-icon-text-top .x-btn-inner{padding-top:21px}.x-btn-default-small-icon-text-top .x-btn-icon-el{height:16px;bottom:auto}.x-ie6 .x-btn-default-small-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-small-icon-text-top .x-btn-icon-el{width:100%}.x-btn-default-small-icon-text-bottom .x-btn-inner{padding-bottom:21px}.x-btn-default-small-icon-text-bottom .x-btn-icon-el{height:16px;top:auto}.x-ie6 .x-btn-default-small-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-small-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-default-small-focus{border-color:#96caee;background-image:none;background-color:#3e3e3e;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#515151),color-stop(50%,#3e3e3e),color-stop(51%,#3a3a3a),color-stop(100%,#3e3e3e));background-image:-webkit-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-moz-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-o-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-ms-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e)}.x-btn-default-small-over{background-image:none;background-color:#3e3e3e;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#515151),color-stop(50%,#3e3e3e),color-stop(51%,#3a3a3a),color-stop(100%,#3e3e3e));background-image:-webkit-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-moz-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-o-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-ms-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e)}.x-btn-default-small-menu-active,.x-btn-default-small-pressed{background-image:none;background-color:#333;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#333),color-stop(50%,#303030),color-stop(51%,#333),color-stop(100%,#474747));background-image:-webkit-linear-gradient(top,#333,#303030 50%,#333 51%,#474747);background-image:-moz-linear-gradient(top,#333,#303030 50%,#333 51%,#474747);background-image:-o-linear-gradient(top,#333,#303030 50%,#333 51%,#474747);background-image:-ms-linear-gradient(top,#333,#303030 50%,#333 51%,#474747);background-image:linear-gradient(top,#333,#303030 50%,#333 51%,#474747)}.x-btn-default-small-focus .x-frame-tl,.x-btn-default-small-focus .x-frame-bl,.x-btn-default-small-focus .x-frame-tr,.x-btn-default-small-focus .x-frame-br,.x-btn-default-small-focus .x-frame-tc,.x-btn-default-small-focus .x-frame-bc{background-image:url(images/btn/btn-default-small-focus-corners.gif)}.x-btn-default-small-focus .x-frame-ml,.x-btn-default-small-focus .x-frame-mr{background-image:url(images/btn/btn-default-small-focus-sides.gif)}.x-btn-default-small-focus .x-frame-mc{background-color:#3e3e3e;background-image:url(images/btn/btn-default-small-focus-fbg.gif)}.x-btn-default-small-over .x-frame-tl,.x-btn-default-small-over .x-frame-bl,.x-btn-default-small-over .x-frame-tr,.x-btn-default-small-over .x-frame-br,.x-btn-default-small-over .x-frame-tc,.x-btn-default-small-over .x-frame-bc{background-image:url(images/btn/btn-default-small-over-corners.gif)}.x-btn-default-small-over .x-frame-ml,.x-btn-default-small-over .x-frame-mr{background-image:url(images/btn/btn-default-small-over-sides.gif)}.x-btn-default-small-over .x-frame-mc{background-color:#3e3e3e;background-image:url(images/btn/btn-default-small-over-fbg.gif)}.x-btn-default-small-menu-active .x-frame-tl,.x-btn-default-small-menu-active .x-frame-bl,.x-btn-default-small-menu-active .x-frame-tr,.x-btn-default-small-menu-active .x-frame-br,.x-btn-default-small-menu-active .x-frame-tc,.x-btn-default-small-menu-active .x-frame-bc,.x-btn-default-small-pressed .x-frame-tl,.x-btn-default-small-pressed .x-frame-bl,.x-btn-default-small-pressed .x-frame-tr,.x-btn-default-small-pressed .x-frame-br,.x-btn-default-small-pressed .x-frame-tc,.x-btn-default-small-pressed .x-frame-bc{background-image:url(images/btn/btn-default-small-pressed-corners.gif)}.x-btn-default-small-menu-active .x-frame-ml,.x-btn-default-small-menu-active .x-frame-mr,.x-btn-default-small-pressed .x-frame-ml,.x-btn-default-small-pressed .x-frame-mr{background-image:url(images/btn/btn-default-small-pressed-sides.gif)}.x-btn-default-small-menu-active .x-frame-mc,.x-btn-default-small-pressed .x-frame-mc{background-color:#333;background-image:url(images/btn/btn-default-small-pressed-fbg.gif)}.x-btn-default-small-disabled .x-frame-tl,.x-btn-default-small-disabled .x-frame-bl,.x-btn-default-small-disabled .x-frame-tr,.x-btn-default-small-disabled .x-frame-br,.x-btn-default-small-disabled .x-frame-tc,.x-btn-default-small-disabled .x-frame-bc{background-image:url(images/btn/btn-default-small-disabled-corners.gif)}.x-btn-default-small-disabled .x-frame-ml,.x-btn-default-small-disabled .x-frame-mr{background-image:url(images/btn/btn-default-small-disabled-sides.gif)}.x-btn-default-small-disabled .x-frame-mc{background-color:#444;background-image:url(images/btn/btn-default-small-disabled-fbg.gif)}.x-nlg .x-btn-default-small-focus{background-image:url(images/btn/btn-default-small-focus-bg.gif)}.x-nlg .x-btn-default-small-over{background-image:url(images/btn/btn-default-small-over-bg.gif)}.x-nlg .x-btn-default-small-menu-active,.x-nlg .x-btn-default-small-pressed{background-image:url(images/btn/btn-default-small-pressed-bg.gif)}.x-nlg .x-btn-default-small-disabled{background-image:url(images/btn/btn-default-small-disabled-bg.gif)}.x-nbr .x-btn-default-small{background-image:none}.x-btn-default-small .x-btn-split-right{background-image:url(images/button/default-small-s-arrow.png);padding-right:23px}.x-btn-default-small .x-btn-split-bottom{background-image:url(images/button/default-small-s-arrow-b.png);padding-bottom:20px}.x-btn-default-small-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-default-small-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-focus-corners.gif), sides:url(images/btn/btn-default-small-focus-sides.gif), frame-bg:url(images/btn/btn-default-small-focus-fbg.gif), bg:url(images/btn/btn-default-small-focus-bg.gif)"}.x-btn-default-small-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-over-corners.gif), sides:url(images/btn/btn-default-small-over-sides.gif), frame-bg:url(images/btn/btn-default-small-over-fbg.gif), bg:url(images/btn/btn-default-small-over-bg.gif)"}.x-btn-default-small-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-pressed-corners.gif), sides:url(images/btn/btn-default-small-pressed-sides.gif), frame-bg:url(images/btn/btn-default-small-pressed-fbg.gif), bg:url(images/btn/btn-default-small-pressed-bg.gif)"}.x-btn-default-small-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-disabled-corners.gif), sides:url(images/btn/btn-default-small-disabled-sides.gif), frame-bg:url(images/btn/btn-default-small-disabled-fbg.gif), bg:url(images/btn/btn-default-small-disabled-bg.gif)"}.x-btn-default-medium{border-color:#333}.x-btn-default-medium{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:#606060;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#6f6f6f),color-stop(50%,#606060),color-stop(51%,#5b5b5b),color-stop(100%,#606060));background-image:-webkit-linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060);background-image:-moz-linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060);background-image:-o-linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060);background-image:-ms-linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060);background-image:linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060)}.x-btn-default-medium-mc{background-image:url(images/btn/btn-default-medium-fbg.gif);background-position:0 top;background-color:#606060}.x-nlg .x-btn-default-medium{background-image:url(images/btn/btn-default-medium-bg.gif);background-position:0 top}.x-nbr .x-btn-default-medium{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-default-medium-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-default-medium-tl{background-position:0 -6px}.x-btn-default-medium-tr{background-position:right -9px}.x-btn-default-medium-bl{background-position:0 -12px}.x-btn-default-medium-br{background-position:right -15px}.x-btn-default-medium-ml{background-position:0 top}.x-btn-default-medium-mr{background-position:right top}.x-btn-default-medium-tc{background-position:0 0}.x-btn-default-medium-bc{background-position:0 -3px}.x-btn-default-medium-tr,.x-btn-default-medium-br,.x-btn-default-medium-mr{padding-right:3px}.x-btn-default-medium-tl,.x-btn-default-medium-bl,.x-btn-default-medium-ml{padding-left:3px}.x-btn-default-medium-tc{height:3px}.x-btn-default-medium-bc{height:3px}.x-btn-default-medium-tl,.x-btn-default-medium-bl,.x-btn-default-medium-tr,.x-btn-default-medium-br,.x-btn-default-medium-tc,.x-btn-default-medium-bc,.x-btn-default-medium-ml,.x-btn-default-medium-mr{zoom:1;background-image:url(images/btn/btn-default-medium-corners.gif)}.x-btn-default-medium-ml,.x-btn-default-medium-mr{zoom:1;background-image:url(images/btn/btn-default-medium-sides.gif)}.x-btn-default-medium-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-default-medium-tl,.x-strict .x-ie7 .x-btn-default-medium-bl{position:relative;right:0}.x-btn-default-medium:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-medium-fbg.gif), bg:url(images/btn/btn-default-medium-bg.gif), corners:url(images/btn/btn-default-medium-corners.gif), sides:url(images/btn/btn-default-medium-sides.gif)"}.x-btn-default-medium .x-btn-inner{font-size:14px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:white;padding:0 8px}.x-btn-default-medium .x-btn-arrow{background-image:url(images/button/default-medium-arrow.png)}.x-btn-default-medium .x-btn-arrow-right{padding-right:30px}.x-btn-default-medium .x-btn-arrow-bottom{padding-bottom:26px}.x-btn-default-medium .x-btn-glyph{font-size:24px;line-height:24px;color:white}.x-ie8m .x-btn-default-medium .x-btn-glyph{color:white}.x-btn-default-medium-disabled{background-image:none;background-color:#444;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#565656),color-stop(50%,#444),color-stop(51%,#404040),color-stop(100%,#444));background-image:-webkit-linear-gradient(top,#565656,#444 50%,#404040 51%,#444);background-image:-moz-linear-gradient(top,#565656,#444 50%,#404040 51%,#444);background-image:-o-linear-gradient(top,#565656,#444 50%,#404040 51%,#444);background-image:-ms-linear-gradient(top,#565656,#444 50%,#404040 51%,#444);background-image:linear-gradient(top,#565656,#444 50%,#404040 51%,#444)}.x-btn-default-medium-icon .x-btn-button,.x-btn-default-medium-noicon .x-btn-button{height:24px}.x-btn-default-medium-icon .x-btn-inner,.x-btn-default-medium-noicon .x-btn-inner{line-height:24px}.x-btn-default-medium-icon .x-btn-arrow-right .x-btn-inner,.x-btn-default-medium-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-default-medium-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-default-medium-icon .x-btn-inner{width:24px;padding:0}.x-btn-default-medium-icon .x-btn-icon-el{width:24px;height:24px}.x-btn-default-medium-icon-text-left .x-btn-button{height:24px}.x-btn-default-medium-icon-text-left .x-btn-inner{line-height:24px;padding-left:29px}.x-btn-default-medium-icon-text-left .x-btn-icon-el{width:24px;right:auto}.x-ie6 .x-btn-default-medium-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-medium-icon-text-left .x-btn-icon-el{height:24px}.x-btn-default-medium-icon-text-right .x-btn-button{height:24px}.x-btn-default-medium-icon-text-right .x-btn-inner{line-height:24px;padding-right:29px}.x-btn-default-medium-icon-text-right .x-btn-icon-el{width:24px;left:auto}.x-ie6 .x-btn-default-medium-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-medium-icon-text-right .x-btn-icon-el{height:24px}.x-btn-default-medium-icon-text-top .x-btn-inner{padding-top:29px}.x-btn-default-medium-icon-text-top .x-btn-icon-el{height:24px;bottom:auto}.x-ie6 .x-btn-default-medium-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-medium-icon-text-top .x-btn-icon-el{width:100%}.x-btn-default-medium-icon-text-bottom .x-btn-inner{padding-bottom:29px}.x-btn-default-medium-icon-text-bottom .x-btn-icon-el{height:24px;top:auto}.x-ie6 .x-btn-default-medium-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-medium-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-default-medium-focus{border-color:#96caee;background-image:none;background-color:#3e3e3e;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#515151),color-stop(50%,#3e3e3e),color-stop(51%,#3a3a3a),color-stop(100%,#3e3e3e));background-image:-webkit-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-moz-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-o-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-ms-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e)}.x-btn-default-medium-over{background-image:none;background-color:#3e3e3e;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#515151),color-stop(50%,#3e3e3e),color-stop(51%,#3a3a3a),color-stop(100%,#3e3e3e));background-image:-webkit-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-moz-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-o-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-ms-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e)}.x-btn-default-medium-menu-active,.x-btn-default-medium-pressed{background-image:none;background-color:#333;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#333),color-stop(50%,#303030),color-stop(51%,#333),color-stop(100%,#474747));background-image:-webkit-linear-gradient(top,#333,#303030 50%,#333 51%,#474747);background-image:-moz-linear-gradient(top,#333,#303030 50%,#333 51%,#474747);background-image:-o-linear-gradient(top,#333,#303030 50%,#333 51%,#474747);background-image:-ms-linear-gradient(top,#333,#303030 50%,#333 51%,#474747);background-image:linear-gradient(top,#333,#303030 50%,#333 51%,#474747)}.x-btn-default-medium-focus .x-frame-tl,.x-btn-default-medium-focus .x-frame-bl,.x-btn-default-medium-focus .x-frame-tr,.x-btn-default-medium-focus .x-frame-br,.x-btn-default-medium-focus .x-frame-tc,.x-btn-default-medium-focus .x-frame-bc{background-image:url(images/btn/btn-default-medium-focus-corners.gif)}.x-btn-default-medium-focus .x-frame-ml,.x-btn-default-medium-focus .x-frame-mr{background-image:url(images/btn/btn-default-medium-focus-sides.gif)}.x-btn-default-medium-focus .x-frame-mc{background-color:#3e3e3e;background-image:url(images/btn/btn-default-medium-focus-fbg.gif)}.x-btn-default-medium-over .x-frame-tl,.x-btn-default-medium-over .x-frame-bl,.x-btn-default-medium-over .x-frame-tr,.x-btn-default-medium-over .x-frame-br,.x-btn-default-medium-over .x-frame-tc,.x-btn-default-medium-over .x-frame-bc{background-image:url(images/btn/btn-default-medium-over-corners.gif)}.x-btn-default-medium-over .x-frame-ml,.x-btn-default-medium-over .x-frame-mr{background-image:url(images/btn/btn-default-medium-over-sides.gif)}.x-btn-default-medium-over .x-frame-mc{background-color:#3e3e3e;background-image:url(images/btn/btn-default-medium-over-fbg.gif)}.x-btn-default-medium-menu-active .x-frame-tl,.x-btn-default-medium-menu-active .x-frame-bl,.x-btn-default-medium-menu-active .x-frame-tr,.x-btn-default-medium-menu-active .x-frame-br,.x-btn-default-medium-menu-active .x-frame-tc,.x-btn-default-medium-menu-active .x-frame-bc,.x-btn-default-medium-pressed .x-frame-tl,.x-btn-default-medium-pressed .x-frame-bl,.x-btn-default-medium-pressed .x-frame-tr,.x-btn-default-medium-pressed .x-frame-br,.x-btn-default-medium-pressed .x-frame-tc,.x-btn-default-medium-pressed .x-frame-bc{background-image:url(images/btn/btn-default-medium-pressed-corners.gif)}.x-btn-default-medium-menu-active .x-frame-ml,.x-btn-default-medium-menu-active .x-frame-mr,.x-btn-default-medium-pressed .x-frame-ml,.x-btn-default-medium-pressed .x-frame-mr{background-image:url(images/btn/btn-default-medium-pressed-sides.gif)}.x-btn-default-medium-menu-active .x-frame-mc,.x-btn-default-medium-pressed .x-frame-mc{background-color:#333;background-image:url(images/btn/btn-default-medium-pressed-fbg.gif)}.x-btn-default-medium-disabled .x-frame-tl,.x-btn-default-medium-disabled .x-frame-bl,.x-btn-default-medium-disabled .x-frame-tr,.x-btn-default-medium-disabled .x-frame-br,.x-btn-default-medium-disabled .x-frame-tc,.x-btn-default-medium-disabled .x-frame-bc{background-image:url(images/btn/btn-default-medium-disabled-corners.gif)}.x-btn-default-medium-disabled .x-frame-ml,.x-btn-default-medium-disabled .x-frame-mr{background-image:url(images/btn/btn-default-medium-disabled-sides.gif)}.x-btn-default-medium-disabled .x-frame-mc{background-color:#444;background-image:url(images/btn/btn-default-medium-disabled-fbg.gif)}.x-nlg .x-btn-default-medium-focus{background-image:url(images/btn/btn-default-medium-focus-bg.gif)}.x-nlg .x-btn-default-medium-over{background-image:url(images/btn/btn-default-medium-over-bg.gif)}.x-nlg .x-btn-default-medium-menu-active,.x-nlg .x-btn-default-medium-pressed{background-image:url(images/btn/btn-default-medium-pressed-bg.gif)}.x-nlg .x-btn-default-medium-disabled{background-image:url(images/btn/btn-default-medium-disabled-bg.gif)}.x-nbr .x-btn-default-medium{background-image:none}.x-btn-default-medium .x-btn-split-right{background-image:url(images/button/default-medium-s-arrow.png);padding-right:32px}.x-btn-default-medium .x-btn-split-bottom{background-image:url(images/button/default-medium-s-arrow-b.png);padding-bottom:28px}.x-btn-default-medium-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-default-medium-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-medium-focus-corners.gif), sides:url(images/btn/btn-default-medium-focus-sides.gif), frame-bg:url(images/btn/btn-default-medium-focus-fbg.gif), bg:url(images/btn/btn-default-medium-focus-bg.gif)"}.x-btn-default-medium-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-medium-over-corners.gif), sides:url(images/btn/btn-default-medium-over-sides.gif), frame-bg:url(images/btn/btn-default-medium-over-fbg.gif), bg:url(images/btn/btn-default-medium-over-bg.gif)"}.x-btn-default-medium-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-medium-pressed-corners.gif), sides:url(images/btn/btn-default-medium-pressed-sides.gif), frame-bg:url(images/btn/btn-default-medium-pressed-fbg.gif), bg:url(images/btn/btn-default-medium-pressed-bg.gif)"}.x-btn-default-medium-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-medium-disabled-corners.gif), sides:url(images/btn/btn-default-medium-disabled-sides.gif), frame-bg:url(images/btn/btn-default-medium-disabled-fbg.gif), bg:url(images/btn/btn-default-medium-disabled-bg.gif)"}.x-btn-default-large{border-color:#333}.x-btn-default-large{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:#606060;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#6f6f6f),color-stop(50%,#606060),color-stop(51%,#5b5b5b),color-stop(100%,#606060));background-image:-webkit-linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060);background-image:-moz-linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060);background-image:-o-linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060);background-image:-ms-linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060);background-image:linear-gradient(top,#6f6f6f,#606060 50%,#5b5b5b 51%,#606060)}.x-btn-default-large-mc{background-image:url(images/btn/btn-default-large-fbg.gif);background-position:0 top;background-color:#606060}.x-nlg .x-btn-default-large{background-image:url(images/btn/btn-default-large-bg.gif);background-position:0 top}.x-nbr .x-btn-default-large{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-default-large-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-default-large-tl{background-position:0 -6px}.x-btn-default-large-tr{background-position:right -9px}.x-btn-default-large-bl{background-position:0 -12px}.x-btn-default-large-br{background-position:right -15px}.x-btn-default-large-ml{background-position:0 top}.x-btn-default-large-mr{background-position:right top}.x-btn-default-large-tc{background-position:0 0}.x-btn-default-large-bc{background-position:0 -3px}.x-btn-default-large-tr,.x-btn-default-large-br,.x-btn-default-large-mr{padding-right:3px}.x-btn-default-large-tl,.x-btn-default-large-bl,.x-btn-default-large-ml{padding-left:3px}.x-btn-default-large-tc{height:3px}.x-btn-default-large-bc{height:3px}.x-btn-default-large-tl,.x-btn-default-large-bl,.x-btn-default-large-tr,.x-btn-default-large-br,.x-btn-default-large-tc,.x-btn-default-large-bc,.x-btn-default-large-ml,.x-btn-default-large-mr{zoom:1;background-image:url(images/btn/btn-default-large-corners.gif)}.x-btn-default-large-ml,.x-btn-default-large-mr{zoom:1;background-image:url(images/btn/btn-default-large-sides.gif)}.x-btn-default-large-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-default-large-tl,.x-strict .x-ie7 .x-btn-default-large-bl{position:relative;right:0}.x-btn-default-large:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-large-fbg.gif), bg:url(images/btn/btn-default-large-bg.gif), corners:url(images/btn/btn-default-large-corners.gif), sides:url(images/btn/btn-default-large-sides.gif)"}.x-btn-default-large .x-btn-inner{font-size:16px;font-weight:bold;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:white;padding:0 10px}.x-btn-default-large .x-btn-arrow{background-image:url(images/button/default-large-arrow.png)}.x-btn-default-large .x-btn-arrow-right{padding-right:36px}.x-btn-default-large .x-btn-arrow-bottom{padding-bottom:32px}.x-btn-default-large .x-btn-glyph{font-size:32px;line-height:32px;color:white}.x-ie8m .x-btn-default-large .x-btn-glyph{color:white}.x-btn-default-large-disabled{background-image:none;background-color:#444;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#565656),color-stop(50%,#444),color-stop(51%,#404040),color-stop(100%,#444));background-image:-webkit-linear-gradient(top,#565656,#444 50%,#404040 51%,#444);background-image:-moz-linear-gradient(top,#565656,#444 50%,#404040 51%,#444);background-image:-o-linear-gradient(top,#565656,#444 50%,#404040 51%,#444);background-image:-ms-linear-gradient(top,#565656,#444 50%,#404040 51%,#444);background-image:linear-gradient(top,#565656,#444 50%,#404040 51%,#444)}.x-btn-default-large-icon .x-btn-button,.x-btn-default-large-noicon .x-btn-button{height:32px}.x-btn-default-large-icon .x-btn-inner,.x-btn-default-large-noicon .x-btn-inner{line-height:32px}.x-btn-default-large-icon .x-btn-arrow-right .x-btn-inner,.x-btn-default-large-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-default-large-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-default-large-icon .x-btn-inner{width:32px;padding:0}.x-btn-default-large-icon .x-btn-icon-el{width:32px;height:32px}.x-btn-default-large-icon-text-left .x-btn-button{height:32px}.x-btn-default-large-icon-text-left .x-btn-inner{line-height:32px;padding-left:37px}.x-btn-default-large-icon-text-left .x-btn-icon-el{width:32px;right:auto}.x-ie6 .x-btn-default-large-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-large-icon-text-left .x-btn-icon-el{height:32px}.x-btn-default-large-icon-text-right .x-btn-button{height:32px}.x-btn-default-large-icon-text-right .x-btn-inner{line-height:32px;padding-right:37px}.x-btn-default-large-icon-text-right .x-btn-icon-el{width:32px;left:auto}.x-ie6 .x-btn-default-large-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-large-icon-text-right .x-btn-icon-el{height:32px}.x-btn-default-large-icon-text-top .x-btn-inner{padding-top:37px}.x-btn-default-large-icon-text-top .x-btn-icon-el{height:32px;bottom:auto}.x-ie6 .x-btn-default-large-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-large-icon-text-top .x-btn-icon-el{width:100%}.x-btn-default-large-icon-text-bottom .x-btn-inner{padding-bottom:37px}.x-btn-default-large-icon-text-bottom .x-btn-icon-el{height:32px;top:auto}.x-ie6 .x-btn-default-large-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-large-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-default-large-focus{border-color:#96caee;background-image:none;background-color:#3e3e3e;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#515151),color-stop(50%,#3e3e3e),color-stop(51%,#3a3a3a),color-stop(100%,#3e3e3e));background-image:-webkit-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-moz-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-o-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-ms-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e)}.x-btn-default-large-over{background-image:none;background-color:#3e3e3e;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#515151),color-stop(50%,#3e3e3e),color-stop(51%,#3a3a3a),color-stop(100%,#3e3e3e));background-image:-webkit-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-moz-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-o-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:-ms-linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e);background-image:linear-gradient(top,#515151,#3e3e3e 50%,#3a3a3a 51%,#3e3e3e)}.x-btn-default-large-menu-active,.x-btn-default-large-pressed{background-image:none;background-color:#333;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#333),color-stop(50%,#303030),color-stop(51%,#333),color-stop(100%,#474747));background-image:-webkit-linear-gradient(top,#333,#303030 50%,#333 51%,#474747);background-image:-moz-linear-gradient(top,#333,#303030 50%,#333 51%,#474747);background-image:-o-linear-gradient(top,#333,#303030 50%,#333 51%,#474747);background-image:-ms-linear-gradient(top,#333,#303030 50%,#333 51%,#474747);background-image:linear-gradient(top,#333,#303030 50%,#333 51%,#474747)}.x-btn-default-large-focus .x-frame-tl,.x-btn-default-large-focus .x-frame-bl,.x-btn-default-large-focus .x-frame-tr,.x-btn-default-large-focus .x-frame-br,.x-btn-default-large-focus .x-frame-tc,.x-btn-default-large-focus .x-frame-bc{background-image:url(images/btn/btn-default-large-focus-corners.gif)}.x-btn-default-large-focus .x-frame-ml,.x-btn-default-large-focus .x-frame-mr{background-image:url(images/btn/btn-default-large-focus-sides.gif)}.x-btn-default-large-focus .x-frame-mc{background-color:#3e3e3e;background-image:url(images/btn/btn-default-large-focus-fbg.gif)}.x-btn-default-large-over .x-frame-tl,.x-btn-default-large-over .x-frame-bl,.x-btn-default-large-over .x-frame-tr,.x-btn-default-large-over .x-frame-br,.x-btn-default-large-over .x-frame-tc,.x-btn-default-large-over .x-frame-bc{background-image:url(images/btn/btn-default-large-over-corners.gif)}.x-btn-default-large-over .x-frame-ml,.x-btn-default-large-over .x-frame-mr{background-image:url(images/btn/btn-default-large-over-sides.gif)}.x-btn-default-large-over .x-frame-mc{background-color:#3e3e3e;background-image:url(images/btn/btn-default-large-over-fbg.gif)}.x-btn-default-large-menu-active .x-frame-tl,.x-btn-default-large-menu-active .x-frame-bl,.x-btn-default-large-menu-active .x-frame-tr,.x-btn-default-large-menu-active .x-frame-br,.x-btn-default-large-menu-active .x-frame-tc,.x-btn-default-large-menu-active .x-frame-bc,.x-btn-default-large-pressed .x-frame-tl,.x-btn-default-large-pressed .x-frame-bl,.x-btn-default-large-pressed .x-frame-tr,.x-btn-default-large-pressed .x-frame-br,.x-btn-default-large-pressed .x-frame-tc,.x-btn-default-large-pressed .x-frame-bc{background-image:url(images/btn/btn-default-large-pressed-corners.gif)}.x-btn-default-large-menu-active .x-frame-ml,.x-btn-default-large-menu-active .x-frame-mr,.x-btn-default-large-pressed .x-frame-ml,.x-btn-default-large-pressed .x-frame-mr{background-image:url(images/btn/btn-default-large-pressed-sides.gif)}.x-btn-default-large-menu-active .x-frame-mc,.x-btn-default-large-pressed .x-frame-mc{background-color:#333;background-image:url(images/btn/btn-default-large-pressed-fbg.gif)}.x-btn-default-large-disabled .x-frame-tl,.x-btn-default-large-disabled .x-frame-bl,.x-btn-default-large-disabled .x-frame-tr,.x-btn-default-large-disabled .x-frame-br,.x-btn-default-large-disabled .x-frame-tc,.x-btn-default-large-disabled .x-frame-bc{background-image:url(images/btn/btn-default-large-disabled-corners.gif)}.x-btn-default-large-disabled .x-frame-ml,.x-btn-default-large-disabled .x-frame-mr{background-image:url(images/btn/btn-default-large-disabled-sides.gif)}.x-btn-default-large-disabled .x-frame-mc{background-color:#444;background-image:url(images/btn/btn-default-large-disabled-fbg.gif)}.x-nlg .x-btn-default-large-focus{background-image:url(images/btn/btn-default-large-focus-bg.gif)}.x-nlg .x-btn-default-large-over{background-image:url(images/btn/btn-default-large-over-bg.gif)}.x-nlg .x-btn-default-large-menu-active,.x-nlg .x-btn-default-large-pressed{background-image:url(images/btn/btn-default-large-pressed-bg.gif)}.x-nlg .x-btn-default-large-disabled{background-image:url(images/btn/btn-default-large-disabled-bg.gif)}.x-nbr .x-btn-default-large{background-image:none}.x-btn-default-large .x-btn-split-right{background-image:url(images/button/default-large-s-arrow.png);padding-right:38px}.x-btn-default-large .x-btn-split-bottom{background-image:url(images/button/default-large-s-arrow-b.png);padding-bottom:34px}.x-btn-default-large-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-default-large-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-large-focus-corners.gif), sides:url(images/btn/btn-default-large-focus-sides.gif), frame-bg:url(images/btn/btn-default-large-focus-fbg.gif), bg:url(images/btn/btn-default-large-focus-bg.gif)"}.x-btn-default-large-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-large-over-corners.gif), sides:url(images/btn/btn-default-large-over-sides.gif), frame-bg:url(images/btn/btn-default-large-over-fbg.gif), bg:url(images/btn/btn-default-large-over-bg.gif)"}.x-btn-default-large-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-large-pressed-corners.gif), sides:url(images/btn/btn-default-large-pressed-sides.gif), frame-bg:url(images/btn/btn-default-large-pressed-fbg.gif), bg:url(images/btn/btn-default-large-pressed-bg.gif)"}.x-btn-default-large-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-large-disabled-corners.gif), sides:url(images/btn/btn-default-large-disabled-sides.gif), frame-bg:url(images/btn/btn-default-large-disabled-fbg.gif), bg:url(images/btn/btn-default-large-disabled-bg.gif)"}.x-btn-default-toolbar-small{border-color:#e1e1e1}.x-btn-default-toolbar-small{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:#f5f5f5;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-default-toolbar-small-mc{background-image:url(images/btn/btn-default-toolbar-small-fbg.gif);background-position:0 top;background-color:#f5f5f5}.x-nlg .x-btn-default-toolbar-small{background-image:url(images/btn/btn-default-toolbar-small-bg.gif);background-position:0 top}.x-nbr .x-btn-default-toolbar-small{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-default-toolbar-small-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-default-toolbar-small-tl{background-position:0 -6px}.x-btn-default-toolbar-small-tr{background-position:right -9px}.x-btn-default-toolbar-small-bl{background-position:0 -12px}.x-btn-default-toolbar-small-br{background-position:right -15px}.x-btn-default-toolbar-small-ml{background-position:0 top}.x-btn-default-toolbar-small-mr{background-position:right top}.x-btn-default-toolbar-small-tc{background-position:0 0}.x-btn-default-toolbar-small-bc{background-position:0 -3px}.x-btn-default-toolbar-small-tr,.x-btn-default-toolbar-small-br,.x-btn-default-toolbar-small-mr{padding-right:3px}.x-btn-default-toolbar-small-tl,.x-btn-default-toolbar-small-bl,.x-btn-default-toolbar-small-ml{padding-left:3px}.x-btn-default-toolbar-small-tc{height:3px}.x-btn-default-toolbar-small-bc{height:3px}.x-btn-default-toolbar-small-tl,.x-btn-default-toolbar-small-bl,.x-btn-default-toolbar-small-tr,.x-btn-default-toolbar-small-br,.x-btn-default-toolbar-small-tc,.x-btn-default-toolbar-small-bc,.x-btn-default-toolbar-small-ml,.x-btn-default-toolbar-small-mr{zoom:1;background-image:url(images/btn/btn-default-toolbar-small-corners.gif)}.x-btn-default-toolbar-small-ml,.x-btn-default-toolbar-small-mr{zoom:1;background-image:url(images/btn/btn-default-toolbar-small-sides.gif)}.x-btn-default-toolbar-small-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-default-toolbar-small-tl,.x-strict .x-ie7 .x-btn-default-toolbar-small-bl{position:relative;right:0}.x-btn-default-toolbar-small:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-toolbar-small-fbg.gif), bg:url(images/btn/btn-default-toolbar-small-bg.gif), corners:url(images/btn/btn-default-toolbar-small-corners.gif), sides:url(images/btn/btn-default-toolbar-small-sides.gif)"}.x-btn-default-toolbar-small .x-btn-inner{font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#333;padding:0 5px}.x-btn-default-toolbar-small .x-btn-arrow{background-image:url(images/button/default-toolbar-small-arrow.png)}.x-btn-default-toolbar-small .x-btn-arrow-right{padding-right:21px}.x-btn-default-toolbar-small .x-btn-arrow-bottom{padding-bottom:18px}.x-btn-default-toolbar-small .x-btn-glyph{font-size:16px;line-height:16px;color:#333}.x-ie8m .x-btn-default-toolbar-small .x-btn-glyph{color:#333}.x-btn-default-toolbar-small-disabled{background-image:none;background-color:#f5f5f5;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-default-toolbar-small-icon .x-btn-button,.x-btn-default-toolbar-small-noicon .x-btn-button{height:16px}.x-btn-default-toolbar-small-icon .x-btn-inner,.x-btn-default-toolbar-small-noicon .x-btn-inner{line-height:16px}.x-btn-default-toolbar-small-icon .x-btn-arrow-right .x-btn-inner,.x-btn-default-toolbar-small-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-default-toolbar-small-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-default-toolbar-small-icon .x-btn-inner{width:16px;padding:0}.x-btn-default-toolbar-small-icon .x-btn-icon-el{width:16px;height:16px}.x-btn-default-toolbar-small-icon-text-left .x-btn-button{height:16px}.x-btn-default-toolbar-small-icon-text-left .x-btn-inner{line-height:16px;padding-left:21px}.x-btn-default-toolbar-small-icon-text-left .x-btn-icon-el{width:16px;right:auto}.x-ie6 .x-btn-default-toolbar-small-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-toolbar-small-icon-text-left .x-btn-icon-el{height:16px}.x-btn-default-toolbar-small-icon-text-right .x-btn-button{height:16px}.x-btn-default-toolbar-small-icon-text-right .x-btn-inner{line-height:16px;padding-right:21px}.x-btn-default-toolbar-small-icon-text-right .x-btn-icon-el{width:16px;left:auto}.x-ie6 .x-btn-default-toolbar-small-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-toolbar-small-icon-text-right .x-btn-icon-el{height:16px}.x-btn-default-toolbar-small-icon-text-top .x-btn-inner{padding-top:21px}.x-btn-default-toolbar-small-icon-text-top .x-btn-icon-el{height:16px;bottom:auto}.x-ie6 .x-btn-default-toolbar-small-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-toolbar-small-icon-text-top .x-btn-icon-el{width:100%}.x-btn-default-toolbar-small-icon-text-bottom .x-btn-inner{padding-bottom:21px}.x-btn-default-toolbar-small-icon-text-bottom .x-btn-icon-el{height:16px;top:auto}.x-ie6 .x-btn-default-toolbar-small-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-toolbar-small-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-default-toolbar-small-focus{border-color:#96caee;background-image:none;background-color:#ebebeb;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-default-toolbar-small-over{border-color:#d8d8d8;background-image:none;background-color:#ebebeb;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#cbcbcb),color-stop(100%,#b8b8b8));background-image:-webkit-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-moz-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-o-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-ms-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:linear-gradient(top,#cbcbcb,#b8b8b8)}.x-btn-default-toolbar-small-menu-active,.x-btn-default-toolbar-small-pressed{border-color:#cecece;background-image:none;background-color:#e1e1e1;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#919191),color-stop(100%,#808080));background-image:-webkit-linear-gradient(top,#919191,#808080);background-image:-moz-linear-gradient(top,#919191,#808080);background-image:-o-linear-gradient(top,#919191,#808080);background-image:-ms-linear-gradient(top,#919191,#808080);background-image:linear-gradient(top,#919191,#808080)}.x-btn-default-toolbar-small-menu-active .x-btn-inner,.x-btn-default-toolbar-small-pressed .x-btn-inner{color:white}.x-btn-default-toolbar-small-focus .x-frame-tl,.x-btn-default-toolbar-small-focus .x-frame-bl,.x-btn-default-toolbar-small-focus .x-frame-tr,.x-btn-default-toolbar-small-focus .x-frame-br,.x-btn-default-toolbar-small-focus .x-frame-tc,.x-btn-default-toolbar-small-focus .x-frame-bc{background-image:url(images/btn/btn-default-toolbar-small-focus-corners.gif)}.x-btn-default-toolbar-small-focus .x-frame-ml,.x-btn-default-toolbar-small-focus .x-frame-mr{background-image:url(images/btn/btn-default-toolbar-small-focus-sides.gif)}.x-btn-default-toolbar-small-focus .x-frame-mc{background-color:#ebebeb;background-image:url(images/btn/btn-default-toolbar-small-focus-fbg.gif)}.x-btn-default-toolbar-small-over .x-frame-tl,.x-btn-default-toolbar-small-over .x-frame-bl,.x-btn-default-toolbar-small-over .x-frame-tr,.x-btn-default-toolbar-small-over .x-frame-br,.x-btn-default-toolbar-small-over .x-frame-tc,.x-btn-default-toolbar-small-over .x-frame-bc{background-image:url(images/btn/btn-default-toolbar-small-over-corners.gif)}.x-btn-default-toolbar-small-over .x-frame-ml,.x-btn-default-toolbar-small-over .x-frame-mr{background-image:url(images/btn/btn-default-toolbar-small-over-sides.gif)}.x-btn-default-toolbar-small-over .x-frame-mc{background-color:#ebebeb;background-image:url(images/btn/btn-default-toolbar-small-over-fbg.gif)}.x-btn-default-toolbar-small-menu-active .x-frame-tl,.x-btn-default-toolbar-small-menu-active .x-frame-bl,.x-btn-default-toolbar-small-menu-active .x-frame-tr,.x-btn-default-toolbar-small-menu-active .x-frame-br,.x-btn-default-toolbar-small-menu-active .x-frame-tc,.x-btn-default-toolbar-small-menu-active .x-frame-bc,.x-btn-default-toolbar-small-pressed .x-frame-tl,.x-btn-default-toolbar-small-pressed .x-frame-bl,.x-btn-default-toolbar-small-pressed .x-frame-tr,.x-btn-default-toolbar-small-pressed .x-frame-br,.x-btn-default-toolbar-small-pressed .x-frame-tc,.x-btn-default-toolbar-small-pressed .x-frame-bc{background-image:url(images/btn/btn-default-toolbar-small-pressed-corners.gif)}.x-btn-default-toolbar-small-menu-active .x-frame-ml,.x-btn-default-toolbar-small-menu-active .x-frame-mr,.x-btn-default-toolbar-small-pressed .x-frame-ml,.x-btn-default-toolbar-small-pressed .x-frame-mr{background-image:url(images/btn/btn-default-toolbar-small-pressed-sides.gif)}.x-btn-default-toolbar-small-menu-active .x-frame-mc,.x-btn-default-toolbar-small-pressed .x-frame-mc{background-color:#e1e1e1;background-image:url(images/btn/btn-default-toolbar-small-pressed-fbg.gif)}.x-btn-default-toolbar-small-disabled .x-frame-tl,.x-btn-default-toolbar-small-disabled .x-frame-bl,.x-btn-default-toolbar-small-disabled .x-frame-tr,.x-btn-default-toolbar-small-disabled .x-frame-br,.x-btn-default-toolbar-small-disabled .x-frame-tc,.x-btn-default-toolbar-small-disabled .x-frame-bc{background-image:url(images/btn/btn-default-toolbar-small-disabled-corners.gif)}.x-btn-default-toolbar-small-disabled .x-frame-ml,.x-btn-default-toolbar-small-disabled .x-frame-mr{background-image:url(images/btn/btn-default-toolbar-small-disabled-sides.gif)}.x-btn-default-toolbar-small-disabled .x-frame-mc{background-color:#f5f5f5;background-image:url(images/btn/btn-default-toolbar-small-disabled-fbg.gif)}.x-nlg .x-btn-default-toolbar-small-focus{background-image:url(images/btn/btn-default-toolbar-small-focus-bg.gif)}.x-nlg .x-btn-default-toolbar-small-over{background-image:url(images/btn/btn-default-toolbar-small-over-bg.gif)}.x-nlg .x-btn-default-toolbar-small-menu-active,.x-nlg .x-btn-default-toolbar-small-pressed{background-image:url(images/btn/btn-default-toolbar-small-pressed-bg.gif)}.x-nlg .x-btn-default-toolbar-small-disabled{background-image:url(images/btn/btn-default-toolbar-small-disabled-bg.gif)}.x-nbr .x-btn-default-toolbar-small{background-image:none}.x-btn-default-toolbar-small .x-btn-split-right{background-image:url(images/button/default-toolbar-small-s-arrow.png);padding-right:23px}.x-btn-default-toolbar-small .x-btn-split-bottom{background-image:url(images/button/default-toolbar-small-s-arrow-b.png);padding-bottom:20px}.x-btn-default-toolbar-small-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-default-toolbar-small-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-small-focus-corners.gif), sides:url(images/btn/btn-default-toolbar-small-focus-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-small-focus-fbg.gif), bg:url(images/btn/btn-default-toolbar-small-focus-bg.gif)"}.x-btn-default-toolbar-small-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-small-over-corners.gif), sides:url(images/btn/btn-default-toolbar-small-over-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-small-over-fbg.gif), bg:url(images/btn/btn-default-toolbar-small-over-bg.gif)"}.x-btn-default-toolbar-small-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-small-pressed-corners.gif), sides:url(images/btn/btn-default-toolbar-small-pressed-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-small-pressed-fbg.gif), bg:url(images/btn/btn-default-toolbar-small-pressed-bg.gif)"}.x-btn-default-toolbar-small-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-small-disabled-corners.gif), sides:url(images/btn/btn-default-toolbar-small-disabled-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-small-disabled-fbg.gif), bg:url(images/btn/btn-default-toolbar-small-disabled-bg.gif)"}.x-btn-default-toolbar-medium{border-color:#e1e1e1}.x-btn-default-toolbar-medium{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:#f5f5f5;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-default-toolbar-medium-mc{background-image:url(images/btn/btn-default-toolbar-medium-fbg.gif);background-position:0 top;background-color:#f5f5f5}.x-nlg .x-btn-default-toolbar-medium{background-image:url(images/btn/btn-default-toolbar-medium-bg.gif);background-position:0 top}.x-nbr .x-btn-default-toolbar-medium{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-default-toolbar-medium-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-default-toolbar-medium-tl{background-position:0 -6px}.x-btn-default-toolbar-medium-tr{background-position:right -9px}.x-btn-default-toolbar-medium-bl{background-position:0 -12px}.x-btn-default-toolbar-medium-br{background-position:right -15px}.x-btn-default-toolbar-medium-ml{background-position:0 top}.x-btn-default-toolbar-medium-mr{background-position:right top}.x-btn-default-toolbar-medium-tc{background-position:0 0}.x-btn-default-toolbar-medium-bc{background-position:0 -3px}.x-btn-default-toolbar-medium-tr,.x-btn-default-toolbar-medium-br,.x-btn-default-toolbar-medium-mr{padding-right:3px}.x-btn-default-toolbar-medium-tl,.x-btn-default-toolbar-medium-bl,.x-btn-default-toolbar-medium-ml{padding-left:3px}.x-btn-default-toolbar-medium-tc{height:3px}.x-btn-default-toolbar-medium-bc{height:3px}.x-btn-default-toolbar-medium-tl,.x-btn-default-toolbar-medium-bl,.x-btn-default-toolbar-medium-tr,.x-btn-default-toolbar-medium-br,.x-btn-default-toolbar-medium-tc,.x-btn-default-toolbar-medium-bc,.x-btn-default-toolbar-medium-ml,.x-btn-default-toolbar-medium-mr{zoom:1;background-image:url(images/btn/btn-default-toolbar-medium-corners.gif)}.x-btn-default-toolbar-medium-ml,.x-btn-default-toolbar-medium-mr{zoom:1;background-image:url(images/btn/btn-default-toolbar-medium-sides.gif)}.x-btn-default-toolbar-medium-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-default-toolbar-medium-tl,.x-strict .x-ie7 .x-btn-default-toolbar-medium-bl{position:relative;right:0}.x-btn-default-toolbar-medium:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-toolbar-medium-fbg.gif), bg:url(images/btn/btn-default-toolbar-medium-bg.gif), corners:url(images/btn/btn-default-toolbar-medium-corners.gif), sides:url(images/btn/btn-default-toolbar-medium-sides.gif)"}.x-btn-default-toolbar-medium .x-btn-inner{font-size:14px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#333;padding:0 8px}.x-btn-default-toolbar-medium .x-btn-arrow{background-image:url(images/button/default-toolbar-medium-arrow.png)}.x-btn-default-toolbar-medium .x-btn-arrow-right{padding-right:30px}.x-btn-default-toolbar-medium .x-btn-arrow-bottom{padding-bottom:26px}.x-btn-default-toolbar-medium .x-btn-glyph{font-size:24px;line-height:24px;color:#333}.x-ie8m .x-btn-default-toolbar-medium .x-btn-glyph{color:#333}.x-btn-default-toolbar-medium-disabled{background-image:none;background-color:#f5f5f5;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-default-toolbar-medium-icon .x-btn-button,.x-btn-default-toolbar-medium-noicon .x-btn-button{height:24px}.x-btn-default-toolbar-medium-icon .x-btn-inner,.x-btn-default-toolbar-medium-noicon .x-btn-inner{line-height:24px}.x-btn-default-toolbar-medium-icon .x-btn-arrow-right .x-btn-inner,.x-btn-default-toolbar-medium-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-default-toolbar-medium-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-default-toolbar-medium-icon .x-btn-inner{width:24px;padding:0}.x-btn-default-toolbar-medium-icon .x-btn-icon-el{width:24px;height:24px}.x-btn-default-toolbar-medium-icon-text-left .x-btn-button{height:24px}.x-btn-default-toolbar-medium-icon-text-left .x-btn-inner{line-height:24px;padding-left:29px}.x-btn-default-toolbar-medium-icon-text-left .x-btn-icon-el{width:24px;right:auto}.x-ie6 .x-btn-default-toolbar-medium-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-toolbar-medium-icon-text-left .x-btn-icon-el{height:24px}.x-btn-default-toolbar-medium-icon-text-right .x-btn-button{height:24px}.x-btn-default-toolbar-medium-icon-text-right .x-btn-inner{line-height:24px;padding-right:29px}.x-btn-default-toolbar-medium-icon-text-right .x-btn-icon-el{width:24px;left:auto}.x-ie6 .x-btn-default-toolbar-medium-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-toolbar-medium-icon-text-right .x-btn-icon-el{height:24px}.x-btn-default-toolbar-medium-icon-text-top .x-btn-inner{padding-top:29px}.x-btn-default-toolbar-medium-icon-text-top .x-btn-icon-el{height:24px;bottom:auto}.x-ie6 .x-btn-default-toolbar-medium-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-toolbar-medium-icon-text-top .x-btn-icon-el{width:100%}.x-btn-default-toolbar-medium-icon-text-bottom .x-btn-inner{padding-bottom:29px}.x-btn-default-toolbar-medium-icon-text-bottom .x-btn-icon-el{height:24px;top:auto}.x-ie6 .x-btn-default-toolbar-medium-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-toolbar-medium-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-default-toolbar-medium-focus{border-color:#96caee;background-image:none;background-color:#ebebeb;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-default-toolbar-medium-over{border-color:#d8d8d8;background-image:none;background-color:#ebebeb;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#cbcbcb),color-stop(100%,#b8b8b8));background-image:-webkit-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-moz-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-o-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-ms-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:linear-gradient(top,#cbcbcb,#b8b8b8)}.x-btn-default-toolbar-medium-menu-active,.x-btn-default-toolbar-medium-pressed{border-color:#cecece;background-image:none;background-color:#e1e1e1;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#919191),color-stop(100%,#808080));background-image:-webkit-linear-gradient(top,#919191,#808080);background-image:-moz-linear-gradient(top,#919191,#808080);background-image:-o-linear-gradient(top,#919191,#808080);background-image:-ms-linear-gradient(top,#919191,#808080);background-image:linear-gradient(top,#919191,#808080)}.x-btn-default-toolbar-medium-menu-active .x-btn-inner,.x-btn-default-toolbar-medium-pressed .x-btn-inner{color:white}.x-btn-default-toolbar-medium-focus .x-frame-tl,.x-btn-default-toolbar-medium-focus .x-frame-bl,.x-btn-default-toolbar-medium-focus .x-frame-tr,.x-btn-default-toolbar-medium-focus .x-frame-br,.x-btn-default-toolbar-medium-focus .x-frame-tc,.x-btn-default-toolbar-medium-focus .x-frame-bc{background-image:url(images/btn/btn-default-toolbar-medium-focus-corners.gif)}.x-btn-default-toolbar-medium-focus .x-frame-ml,.x-btn-default-toolbar-medium-focus .x-frame-mr{background-image:url(images/btn/btn-default-toolbar-medium-focus-sides.gif)}.x-btn-default-toolbar-medium-focus .x-frame-mc{background-color:#ebebeb;background-image:url(images/btn/btn-default-toolbar-medium-focus-fbg.gif)}.x-btn-default-toolbar-medium-over .x-frame-tl,.x-btn-default-toolbar-medium-over .x-frame-bl,.x-btn-default-toolbar-medium-over .x-frame-tr,.x-btn-default-toolbar-medium-over .x-frame-br,.x-btn-default-toolbar-medium-over .x-frame-tc,.x-btn-default-toolbar-medium-over .x-frame-bc{background-image:url(images/btn/btn-default-toolbar-medium-over-corners.gif)}.x-btn-default-toolbar-medium-over .x-frame-ml,.x-btn-default-toolbar-medium-over .x-frame-mr{background-image:url(images/btn/btn-default-toolbar-medium-over-sides.gif)}.x-btn-default-toolbar-medium-over .x-frame-mc{background-color:#ebebeb;background-image:url(images/btn/btn-default-toolbar-medium-over-fbg.gif)}.x-btn-default-toolbar-medium-menu-active .x-frame-tl,.x-btn-default-toolbar-medium-menu-active .x-frame-bl,.x-btn-default-toolbar-medium-menu-active .x-frame-tr,.x-btn-default-toolbar-medium-menu-active .x-frame-br,.x-btn-default-toolbar-medium-menu-active .x-frame-tc,.x-btn-default-toolbar-medium-menu-active .x-frame-bc,.x-btn-default-toolbar-medium-pressed .x-frame-tl,.x-btn-default-toolbar-medium-pressed .x-frame-bl,.x-btn-default-toolbar-medium-pressed .x-frame-tr,.x-btn-default-toolbar-medium-pressed .x-frame-br,.x-btn-default-toolbar-medium-pressed .x-frame-tc,.x-btn-default-toolbar-medium-pressed .x-frame-bc{background-image:url(images/btn/btn-default-toolbar-medium-pressed-corners.gif)}.x-btn-default-toolbar-medium-menu-active .x-frame-ml,.x-btn-default-toolbar-medium-menu-active .x-frame-mr,.x-btn-default-toolbar-medium-pressed .x-frame-ml,.x-btn-default-toolbar-medium-pressed .x-frame-mr{background-image:url(images/btn/btn-default-toolbar-medium-pressed-sides.gif)}.x-btn-default-toolbar-medium-menu-active .x-frame-mc,.x-btn-default-toolbar-medium-pressed .x-frame-mc{background-color:#e1e1e1;background-image:url(images/btn/btn-default-toolbar-medium-pressed-fbg.gif)}.x-btn-default-toolbar-medium-disabled .x-frame-tl,.x-btn-default-toolbar-medium-disabled .x-frame-bl,.x-btn-default-toolbar-medium-disabled .x-frame-tr,.x-btn-default-toolbar-medium-disabled .x-frame-br,.x-btn-default-toolbar-medium-disabled .x-frame-tc,.x-btn-default-toolbar-medium-disabled .x-frame-bc{background-image:url(images/btn/btn-default-toolbar-medium-disabled-corners.gif)}.x-btn-default-toolbar-medium-disabled .x-frame-ml,.x-btn-default-toolbar-medium-disabled .x-frame-mr{background-image:url(images/btn/btn-default-toolbar-medium-disabled-sides.gif)}.x-btn-default-toolbar-medium-disabled .x-frame-mc{background-color:#f5f5f5;background-image:url(images/btn/btn-default-toolbar-medium-disabled-fbg.gif)}.x-nlg .x-btn-default-toolbar-medium-focus{background-image:url(images/btn/btn-default-toolbar-medium-focus-bg.gif)}.x-nlg .x-btn-default-toolbar-medium-over{background-image:url(images/btn/btn-default-toolbar-medium-over-bg.gif)}.x-nlg .x-btn-default-toolbar-medium-menu-active,.x-nlg .x-btn-default-toolbar-medium-pressed{background-image:url(images/btn/btn-default-toolbar-medium-pressed-bg.gif)}.x-nlg .x-btn-default-toolbar-medium-disabled{background-image:url(images/btn/btn-default-toolbar-medium-disabled-bg.gif)}.x-nbr .x-btn-default-toolbar-medium{background-image:none}.x-btn-default-toolbar-medium .x-btn-split-right{background-image:url(images/button/default-toolbar-medium-s-arrow.png);padding-right:32px}.x-btn-default-toolbar-medium .x-btn-split-bottom{background-image:url(images/button/default-toolbar-medium-s-arrow-b.png);padding-bottom:28px}.x-btn-default-toolbar-medium-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-default-toolbar-medium-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-medium-focus-corners.gif), sides:url(images/btn/btn-default-toolbar-medium-focus-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-medium-focus-fbg.gif), bg:url(images/btn/btn-default-toolbar-medium-focus-bg.gif)"}.x-btn-default-toolbar-medium-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-medium-over-corners.gif), sides:url(images/btn/btn-default-toolbar-medium-over-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-medium-over-fbg.gif), bg:url(images/btn/btn-default-toolbar-medium-over-bg.gif)"}.x-btn-default-toolbar-medium-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-medium-pressed-corners.gif), sides:url(images/btn/btn-default-toolbar-medium-pressed-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-medium-pressed-fbg.gif), bg:url(images/btn/btn-default-toolbar-medium-pressed-bg.gif)"}.x-btn-default-toolbar-medium-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-medium-disabled-corners.gif), sides:url(images/btn/btn-default-toolbar-medium-disabled-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-medium-disabled-fbg.gif), bg:url(images/btn/btn-default-toolbar-medium-disabled-bg.gif)"}.x-btn-default-toolbar-large{border-color:#e1e1e1}.x-btn-default-toolbar-large{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:#f5f5f5;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-default-toolbar-large-mc{background-image:url(images/btn/btn-default-toolbar-large-fbg.gif);background-position:0 top;background-color:#f5f5f5}.x-nlg .x-btn-default-toolbar-large{background-image:url(images/btn/btn-default-toolbar-large-bg.gif);background-position:0 top}.x-nbr .x-btn-default-toolbar-large{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-default-toolbar-large-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-default-toolbar-large-tl{background-position:0 -6px}.x-btn-default-toolbar-large-tr{background-position:right -9px}.x-btn-default-toolbar-large-bl{background-position:0 -12px}.x-btn-default-toolbar-large-br{background-position:right -15px}.x-btn-default-toolbar-large-ml{background-position:0 top}.x-btn-default-toolbar-large-mr{background-position:right top}.x-btn-default-toolbar-large-tc{background-position:0 0}.x-btn-default-toolbar-large-bc{background-position:0 -3px}.x-btn-default-toolbar-large-tr,.x-btn-default-toolbar-large-br,.x-btn-default-toolbar-large-mr{padding-right:3px}.x-btn-default-toolbar-large-tl,.x-btn-default-toolbar-large-bl,.x-btn-default-toolbar-large-ml{padding-left:3px}.x-btn-default-toolbar-large-tc{height:3px}.x-btn-default-toolbar-large-bc{height:3px}.x-btn-default-toolbar-large-tl,.x-btn-default-toolbar-large-bl,.x-btn-default-toolbar-large-tr,.x-btn-default-toolbar-large-br,.x-btn-default-toolbar-large-tc,.x-btn-default-toolbar-large-bc,.x-btn-default-toolbar-large-ml,.x-btn-default-toolbar-large-mr{zoom:1;background-image:url(images/btn/btn-default-toolbar-large-corners.gif)}.x-btn-default-toolbar-large-ml,.x-btn-default-toolbar-large-mr{zoom:1;background-image:url(images/btn/btn-default-toolbar-large-sides.gif)}.x-btn-default-toolbar-large-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-default-toolbar-large-tl,.x-strict .x-ie7 .x-btn-default-toolbar-large-bl{position:relative;right:0}.x-btn-default-toolbar-large:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-toolbar-large-fbg.gif), bg:url(images/btn/btn-default-toolbar-large-bg.gif), corners:url(images/btn/btn-default-toolbar-large-corners.gif), sides:url(images/btn/btn-default-toolbar-large-sides.gif)"}.x-btn-default-toolbar-large .x-btn-inner{font-size:16px;font-weight:bold;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#333;padding:0 10px}.x-btn-default-toolbar-large .x-btn-arrow{background-image:url(images/button/default-toolbar-large-arrow.png)}.x-btn-default-toolbar-large .x-btn-arrow-right{padding-right:36px}.x-btn-default-toolbar-large .x-btn-arrow-bottom{padding-bottom:32px}.x-btn-default-toolbar-large .x-btn-glyph{font-size:32px;line-height:32px;color:#333}.x-ie8m .x-btn-default-toolbar-large .x-btn-glyph{color:#333}.x-btn-default-toolbar-large-disabled{background-image:none;background-color:#f5f5f5;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-default-toolbar-large-icon .x-btn-button,.x-btn-default-toolbar-large-noicon .x-btn-button{height:32px}.x-btn-default-toolbar-large-icon .x-btn-inner,.x-btn-default-toolbar-large-noicon .x-btn-inner{line-height:32px}.x-btn-default-toolbar-large-icon .x-btn-arrow-right .x-btn-inner,.x-btn-default-toolbar-large-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-default-toolbar-large-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-default-toolbar-large-icon .x-btn-inner{width:32px;padding:0}.x-btn-default-toolbar-large-icon .x-btn-icon-el{width:32px;height:32px}.x-btn-default-toolbar-large-icon-text-left .x-btn-button{height:32px}.x-btn-default-toolbar-large-icon-text-left .x-btn-inner{line-height:32px;padding-left:37px}.x-btn-default-toolbar-large-icon-text-left .x-btn-icon-el{width:32px;right:auto}.x-ie6 .x-btn-default-toolbar-large-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-toolbar-large-icon-text-left .x-btn-icon-el{height:32px}.x-btn-default-toolbar-large-icon-text-right .x-btn-button{height:32px}.x-btn-default-toolbar-large-icon-text-right .x-btn-inner{line-height:32px;padding-right:37px}.x-btn-default-toolbar-large-icon-text-right .x-btn-icon-el{width:32px;left:auto}.x-ie6 .x-btn-default-toolbar-large-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-toolbar-large-icon-text-right .x-btn-icon-el{height:32px}.x-btn-default-toolbar-large-icon-text-top .x-btn-inner{padding-top:37px}.x-btn-default-toolbar-large-icon-text-top .x-btn-icon-el{height:32px;bottom:auto}.x-ie6 .x-btn-default-toolbar-large-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-toolbar-large-icon-text-top .x-btn-icon-el{width:100%}.x-btn-default-toolbar-large-icon-text-bottom .x-btn-inner{padding-bottom:37px}.x-btn-default-toolbar-large-icon-text-bottom .x-btn-icon-el{height:32px;top:auto}.x-ie6 .x-btn-default-toolbar-large-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-toolbar-large-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-default-toolbar-large-focus{border-color:#96caee;background-image:none;background-color:#ebebeb;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-default-toolbar-large-over{border-color:#d8d8d8;background-image:none;background-color:#ebebeb;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#cbcbcb),color-stop(100%,#b8b8b8));background-image:-webkit-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-moz-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-o-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-ms-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:linear-gradient(top,#cbcbcb,#b8b8b8)}.x-btn-default-toolbar-large-menu-active,.x-btn-default-toolbar-large-pressed{border-color:#cecece;background-image:none;background-color:#e1e1e1;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#919191),color-stop(100%,#808080));background-image:-webkit-linear-gradient(top,#919191,#808080);background-image:-moz-linear-gradient(top,#919191,#808080);background-image:-o-linear-gradient(top,#919191,#808080);background-image:-ms-linear-gradient(top,#919191,#808080);background-image:linear-gradient(top,#919191,#808080)}.x-btn-default-toolbar-large-menu-active .x-btn-inner,.x-btn-default-toolbar-large-pressed .x-btn-inner{color:white}.x-btn-default-toolbar-large-focus .x-frame-tl,.x-btn-default-toolbar-large-focus .x-frame-bl,.x-btn-default-toolbar-large-focus .x-frame-tr,.x-btn-default-toolbar-large-focus .x-frame-br,.x-btn-default-toolbar-large-focus .x-frame-tc,.x-btn-default-toolbar-large-focus .x-frame-bc{background-image:url(images/btn/btn-default-toolbar-large-focus-corners.gif)}.x-btn-default-toolbar-large-focus .x-frame-ml,.x-btn-default-toolbar-large-focus .x-frame-mr{background-image:url(images/btn/btn-default-toolbar-large-focus-sides.gif)}.x-btn-default-toolbar-large-focus .x-frame-mc{background-color:#ebebeb;background-image:url(images/btn/btn-default-toolbar-large-focus-fbg.gif)}.x-btn-default-toolbar-large-over .x-frame-tl,.x-btn-default-toolbar-large-over .x-frame-bl,.x-btn-default-toolbar-large-over .x-frame-tr,.x-btn-default-toolbar-large-over .x-frame-br,.x-btn-default-toolbar-large-over .x-frame-tc,.x-btn-default-toolbar-large-over .x-frame-bc{background-image:url(images/btn/btn-default-toolbar-large-over-corners.gif)}.x-btn-default-toolbar-large-over .x-frame-ml,.x-btn-default-toolbar-large-over .x-frame-mr{background-image:url(images/btn/btn-default-toolbar-large-over-sides.gif)}.x-btn-default-toolbar-large-over .x-frame-mc{background-color:#ebebeb;background-image:url(images/btn/btn-default-toolbar-large-over-fbg.gif)}.x-btn-default-toolbar-large-menu-active .x-frame-tl,.x-btn-default-toolbar-large-menu-active .x-frame-bl,.x-btn-default-toolbar-large-menu-active .x-frame-tr,.x-btn-default-toolbar-large-menu-active .x-frame-br,.x-btn-default-toolbar-large-menu-active .x-frame-tc,.x-btn-default-toolbar-large-menu-active .x-frame-bc,.x-btn-default-toolbar-large-pressed .x-frame-tl,.x-btn-default-toolbar-large-pressed .x-frame-bl,.x-btn-default-toolbar-large-pressed .x-frame-tr,.x-btn-default-toolbar-large-pressed .x-frame-br,.x-btn-default-toolbar-large-pressed .x-frame-tc,.x-btn-default-toolbar-large-pressed .x-frame-bc{background-image:url(images/btn/btn-default-toolbar-large-pressed-corners.gif)}.x-btn-default-toolbar-large-menu-active .x-frame-ml,.x-btn-default-toolbar-large-menu-active .x-frame-mr,.x-btn-default-toolbar-large-pressed .x-frame-ml,.x-btn-default-toolbar-large-pressed .x-frame-mr{background-image:url(images/btn/btn-default-toolbar-large-pressed-sides.gif)}.x-btn-default-toolbar-large-menu-active .x-frame-mc,.x-btn-default-toolbar-large-pressed .x-frame-mc{background-color:#e1e1e1;background-image:url(images/btn/btn-default-toolbar-large-pressed-fbg.gif)}.x-btn-default-toolbar-large-disabled .x-frame-tl,.x-btn-default-toolbar-large-disabled .x-frame-bl,.x-btn-default-toolbar-large-disabled .x-frame-tr,.x-btn-default-toolbar-large-disabled .x-frame-br,.x-btn-default-toolbar-large-disabled .x-frame-tc,.x-btn-default-toolbar-large-disabled .x-frame-bc{background-image:url(images/btn/btn-default-toolbar-large-disabled-corners.gif)}.x-btn-default-toolbar-large-disabled .x-frame-ml,.x-btn-default-toolbar-large-disabled .x-frame-mr{background-image:url(images/btn/btn-default-toolbar-large-disabled-sides.gif)}.x-btn-default-toolbar-large-disabled .x-frame-mc{background-color:#f5f5f5;background-image:url(images/btn/btn-default-toolbar-large-disabled-fbg.gif)}.x-nlg .x-btn-default-toolbar-large-focus{background-image:url(images/btn/btn-default-toolbar-large-focus-bg.gif)}.x-nlg .x-btn-default-toolbar-large-over{background-image:url(images/btn/btn-default-toolbar-large-over-bg.gif)}.x-nlg .x-btn-default-toolbar-large-menu-active,.x-nlg .x-btn-default-toolbar-large-pressed{background-image:url(images/btn/btn-default-toolbar-large-pressed-bg.gif)}.x-nlg .x-btn-default-toolbar-large-disabled{background-image:url(images/btn/btn-default-toolbar-large-disabled-bg.gif)}.x-nbr .x-btn-default-toolbar-large{background-image:none}.x-btn-default-toolbar-large .x-btn-split-right{background-image:url(images/button/default-toolbar-large-s-arrow.png);padding-right:38px}.x-btn-default-toolbar-large .x-btn-split-bottom{background-image:url(images/button/default-toolbar-large-s-arrow-b.png);padding-bottom:34px}.x-btn-default-toolbar-large-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-default-toolbar-large-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-large-focus-corners.gif), sides:url(images/btn/btn-default-toolbar-large-focus-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-large-focus-fbg.gif), bg:url(images/btn/btn-default-toolbar-large-focus-bg.gif)"}.x-btn-default-toolbar-large-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-large-over-corners.gif), sides:url(images/btn/btn-default-toolbar-large-over-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-large-over-fbg.gif), bg:url(images/btn/btn-default-toolbar-large-over-bg.gif)"}.x-btn-default-toolbar-large-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-large-pressed-corners.gif), sides:url(images/btn/btn-default-toolbar-large-pressed-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-large-pressed-fbg.gif), bg:url(images/btn/btn-default-toolbar-large-pressed-bg.gif)"}.x-btn-default-toolbar-large-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-toolbar-large-disabled-corners.gif), sides:url(images/btn/btn-default-toolbar-large-disabled-sides.gif), frame-bg:url(images/btn/btn-default-toolbar-large-disabled-fbg.gif), bg:url(images/btn/btn-default-toolbar-large-disabled-bg.gif)"}.x-btn-icon-text-left .x-btn-icon-el{background-position:left center}.x-btn-icon-text-right .x-btn-icon-el{background-position:right center}.x-btn-icon-text-top .x-btn-icon-el{background-position:center top}.x-btn-icon-text-bottom .x-btn-icon-el{background-position:center bottom}.x-btn-arrow-right{background-position:right center}.x-btn-arrow-bottom{background-position:center bottom}.x-btn-arrow{background-repeat:no-repeat}.x-btn-split{display:block;background-repeat:no-repeat}.x-btn-split-right{background-position:right center}.x-btn-split-bottom{background-position:center bottom}.x-cycle-fixed-width .x-btn-inner{text-align:inherit}.x-toolbar{font-size:13px;border-style:solid;padding:6px 0 6px 8px}.x-toolbar-item{margin:0 8px 0 0}.x-toolbar-text{margin:0 6px 0 4px;color:#303030;line-height:16px;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-weight:normal}.x-toolbar-separator-horizontal{margin:0 8px 0 0;height:14px;border-style:solid;border-width:0 0 0 1px;border-left-color:#e1e1e1;border-right-color:white}.x-toolbar-footer{background:transparent;border:0;margin:0;padding:6px 0 6px 6px}.x-toolbar-footer .x-toolbar-item{margin:0 6px 0 0}.x-toolbar-spacer{width:2px}.x-toolbar-more-icon{background-image:url(images/toolbar/more.png)!important;background-position:center center!important;background-repeat:no-repeat}.x-toolbar-default{border-color:silver;border-width:1px;background-image:none;background-color:transparent}.x-toolbar-default .x-box-scroller{cursor:pointer}.x-toolbar-default .x-box-scroller-disabled{cursor:default}.x-toolbar-default .x-tool-img{background-image:url(images/tools/tool-sprites-dark.png);background-color:transparent}.x-toolbar-scroll-left{background-image:url(images/toolbar/scroll-left.png);background-position:0 0;width:16px;height:16px;border-style:solid;border-color:#8db2e3;border-width:0;margin-top:4px}.x-toolbar-scroll-left-hover{background-position:0 0}.x-toolbar-scroll-right{background-image:url(images/toolbar/scroll-right.png);width:16px;height:16px;border-style:solid;border-color:#8db2e3;border-width:0;margin-top:4px}.x-toolbar-scroll-right-hover{background-position:-16px 0}.x-toolbar .x-box-menu-after{margin:0 8px}.x-toolbar-vertical{padding:6px 8px 0 8px}.x-toolbar-vertical .x-toolbar-item{margin:0 0 6px 0}.x-toolbar-vertical .x-toolbar-text{margin:4px 0 6px 0}.x-toolbar-vertical .x-toolbar-separator-vertical{margin:0 5px 6px;border-style:solid none;border-width:1px 0 0;border-top-color:#e1e1e1;border-bottom-color:white}.x-toolbar-vertical .x-box-menu-after{margin:6px 0}.x-header-draggable .x-header-body,.x-header-ghost{cursor:move}.x-header-text{white-space:nowrap}.x-panel-ghost{filter:alpha(opacity=50);opacity:.5}.x-panel-default{border-color:#444;padding:0}.x-panel-header-default{font-size:13px;border:1px solid #444}.x-panel-header-default .x-tool-img{background-color:#444}.x-panel-header-default-horizontal{padding:9px 9px 10px 9px}.x-panel-header-default-horizontal-noborder{padding:10px 10px 10px 10px}.x-panel-header-default-vertical{padding:9px 9px 9px 10px}.x-panel-header-default-vertical-noborder{padding:10px 10px 10px 10px}.x-panel-header-text-container-default{color:white;font-size:13px;font-weight:bold;font-family:arial,helvetica,verdana,sans-serif;line-height:15px;padding:1px 0 0;text-transform:none}.x-panel-body-default{background:transparent;border-color:#444;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-header-default{background-image:none;background-color:#444}.x-panel-header-default-vertical{background-image:none;background-color:#444}.x-panel .x-panel-header-default-collapsed-border-top{border-bottom-width:1px!important}.x-panel .x-panel-header-default-collapsed-border-right{border-left-width:1px!important}.x-panel .x-panel-header-default-collapsed-border-bottom{border-top-width:1px!important}.x-panel .x-panel-header-default-collapsed-border-left{border-right-width:1px!important}.x-panel-header-default-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-default-bottom:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-default-left:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-default-right:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-default-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-default-vertical .x-panel-header-text-container{background-color:#444;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#444444)}.x-panel-header-default .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-default .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-default .x-panel-header-glyph{color:#a1a1a1}.x-panel-header-default-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-default-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-default-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-default-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-default-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-default-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-default-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-default-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-default-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-default-outer-border-l{border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-outer-border-b{border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-default-outer-border-bl{border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-outer-border-r{border-right-color:#444!important;border-right-width:1px!important}.x-panel-default-outer-border-rl{border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-outer-border-rb{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-default-outer-border-rbl{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-outer-border-t{border-top-color:#444!important;border-top-width:1px!important}.x-panel-default-outer-border-tl{border-top-color:#444!important;border-top-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-outer-border-tb{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-default-outer-border-tbl{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-outer-border-tr{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important}.x-panel-default-outer-border-trl{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-outer-border-trb{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-default-outer-border-trbl{border-color:#444!important;border-width:1px!important}.x-panel-default-framed{border-color:#444;padding:0}.x-panel-header-default-framed{font-size:13px;border:2px solid #444}.x-panel-header-default-framed .x-tool-img{background-color:#444}.x-panel-header-default-framed-horizontal{padding:5px}.x-panel-header-default-framed-horizontal-noborder{padding:7px 7px 5px 7px}.x-panel-header-default-framed-vertical{padding:5px 5px 5px 5px}.x-panel-header-default-framed-vertical-noborder{padding:7px 7px 7px 5px}.x-panel-header-text-container-default-framed{color:white;font-size:13px;font-weight:bold;font-family:arial,helvetica,verdana,sans-serif;line-height:15px;padding:1px 0 0;text-transform:none}.x-panel-body-default-framed{background:white;border-color:#444;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-default-framed{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:2px 2px 2px 2px;border-width:2px;border-style:solid;background-color:white}.x-panel-default-framed-mc{background-color:white}.x-nbr .x-panel-default-framed{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-default-framed-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-2-2-2-2}.x-panel-default-framed-tl{background-position:0 -8px}.x-panel-default-framed-tr{background-position:right -12px}.x-panel-default-framed-bl{background-position:0 -16px}.x-panel-default-framed-br{background-position:right -20px}.x-panel-default-framed-ml{background-position:0 top}.x-panel-default-framed-mr{background-position:right top}.x-panel-default-framed-tc{background-position:0 0}.x-panel-default-framed-bc{background-position:0 -4px}.x-panel-default-framed-tr,.x-panel-default-framed-br,.x-panel-default-framed-mr{padding-right:4px}.x-panel-default-framed-tl,.x-panel-default-framed-bl,.x-panel-default-framed-ml{padding-left:4px}.x-panel-default-framed-tc{height:4px}.x-panel-default-framed-bc{height:4px}.x-panel-default-framed-tl,.x-panel-default-framed-bl,.x-panel-default-framed-tr,.x-panel-default-framed-br,.x-panel-default-framed-tc,.x-panel-default-framed-bc,.x-panel-default-framed-ml,.x-panel-default-framed-mr{zoom:1;background-image:url(images/panel/panel-default-framed-corners.gif)}.x-panel-default-framed-ml,.x-panel-default-framed-mr{zoom:1;background-image:url(images/panel/panel-default-framed-sides.gif);background-repeat:repeat-y}.x-panel-default-framed-mc{padding:0}.x-strict .x-ie7 .x-panel-default-framed-tl,.x-strict .x-ie7 .x-panel-default-framed-bl{position:relative;right:0}.x-panel-default-framed:after{display:none;content:"x-slicer:corners:url(images/panel/panel-default-framed-corners.gif), sides:url(images/panel/panel-default-framed-sides.gif)"}.x-panel-header-default-framed-top{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:5px 5px 5px 5px;border-width:2px 2px 0 2px;border-style:solid;background-color:#444}.x-panel-header-default-framed-top-mc{background-color:#444}.x-nbr .x-panel-header-default-framed-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-default-framed-top-frameInfo{font-family:dh-4-4-0-0-2-2-0-2-5-5-5-5}.x-panel-header-default-framed-top-tl{background-position:0 -8px}.x-panel-header-default-framed-top-tr{background-position:right -12px}.x-panel-header-default-framed-top-bl{background-position:0 -16px}.x-panel-header-default-framed-top-br{background-position:right -20px}.x-panel-header-default-framed-top-ml{background-position:0 top}.x-panel-header-default-framed-top-mr{background-position:right top}.x-panel-header-default-framed-top-tc{background-position:0 0}.x-panel-header-default-framed-top-bc{background-position:0 -4px}.x-panel-header-default-framed-top-tr,.x-panel-header-default-framed-top-br,.x-panel-header-default-framed-top-mr{padding-right:4px}.x-panel-header-default-framed-top-tl,.x-panel-header-default-framed-top-bl,.x-panel-header-default-framed-top-ml{padding-left:4px}.x-panel-header-default-framed-top-tc{height:4px}.x-panel-header-default-framed-top-bc{height:0}.x-panel-header-default-framed-top-tl,.x-panel-header-default-framed-top-bl,.x-panel-header-default-framed-top-tr,.x-panel-header-default-framed-top-br,.x-panel-header-default-framed-top-tc,.x-panel-header-default-framed-top-bc,.x-panel-header-default-framed-top-ml,.x-panel-header-default-framed-top-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-top-corners.gif)}.x-panel-header-default-framed-top-ml,.x-panel-header-default-framed-top-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-top-sides.gif);background-repeat:repeat-y}.x-panel-header-default-framed-top-mc{padding:3px 3px 5px 3px}.x-strict .x-ie7 .x-panel-header-default-framed-top-tl,.x-strict .x-ie7 .x-panel-header-default-framed-top-bl{position:relative;right:0}.x-panel-header-default-framed-top:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-default-framed-top-corners.gif), sides:url(images/panel-header/panel-header-default-framed-top-sides.gif)"}.x-panel-header-default-framed-right{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:5px 5px 5px 5px;border-width:2px 2px 2px 0;border-style:solid;background-color:#444}.x-panel-header-default-framed-right-mc{background-color:#444}.x-nbr .x-panel-header-default-framed-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-default-framed-right-frameInfo{font-family:dh-0-4-4-0-2-2-2-0-5-5-5-5}.x-panel-header-default-framed-right-tl{background-position:0 -8px}.x-panel-header-default-framed-right-tr{background-position:right -12px}.x-panel-header-default-framed-right-bl{background-position:0 -16px}.x-panel-header-default-framed-right-br{background-position:right -20px}.x-panel-header-default-framed-right-ml{background-position:0 right}.x-panel-header-default-framed-right-mr{background-position:right right}.x-panel-header-default-framed-right-tc{background-position:0 0}.x-panel-header-default-framed-right-bc{background-position:0 -4px}.x-panel-header-default-framed-right-tr,.x-panel-header-default-framed-right-br,.x-panel-header-default-framed-right-mr{padding-right:4px}.x-panel-header-default-framed-right-tl,.x-panel-header-default-framed-right-bl,.x-panel-header-default-framed-right-ml{padding-left:0}.x-panel-header-default-framed-right-tc{height:4px}.x-panel-header-default-framed-right-bc{height:4px}.x-panel-header-default-framed-right-tl,.x-panel-header-default-framed-right-bl,.x-panel-header-default-framed-right-tr,.x-panel-header-default-framed-right-br,.x-panel-header-default-framed-right-tc,.x-panel-header-default-framed-right-bc,.x-panel-header-default-framed-right-ml,.x-panel-header-default-framed-right-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-right-corners.gif)}.x-panel-header-default-framed-right-ml,.x-panel-header-default-framed-right-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-right-sides.gif);background-repeat:repeat-y}.x-panel-header-default-framed-right-mc{padding:3px 3px 3px 5px}.x-strict .x-ie7 .x-panel-header-default-framed-right-tl,.x-strict .x-ie7 .x-panel-header-default-framed-right-bl{position:relative;right:0}.x-panel-header-default-framed-right:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-default-framed-right-corners.gif), sides:url(images/panel-header/panel-header-default-framed-right-sides.gif)"}.x-panel-header-default-framed-bottom{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:5px 5px 5px 5px;border-width:0 2px 2px 2px;border-style:solid;background-color:#444}.x-panel-header-default-framed-bottom-mc{background-color:#444}.x-nbr .x-panel-header-default-framed-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-default-framed-bottom-frameInfo{font-family:dh-0-0-4-4-0-2-2-2-5-5-5-5}.x-panel-header-default-framed-bottom-tl{background-position:0 -8px}.x-panel-header-default-framed-bottom-tr{background-position:right -12px}.x-panel-header-default-framed-bottom-bl{background-position:0 -16px}.x-panel-header-default-framed-bottom-br{background-position:right -20px}.x-panel-header-default-framed-bottom-ml{background-position:0 bottom}.x-panel-header-default-framed-bottom-mr{background-position:right bottom}.x-panel-header-default-framed-bottom-tc{background-position:0 0}.x-panel-header-default-framed-bottom-bc{background-position:0 -4px}.x-panel-header-default-framed-bottom-tr,.x-panel-header-default-framed-bottom-br,.x-panel-header-default-framed-bottom-mr{padding-right:4px}.x-panel-header-default-framed-bottom-tl,.x-panel-header-default-framed-bottom-bl,.x-panel-header-default-framed-bottom-ml{padding-left:4px}.x-panel-header-default-framed-bottom-tc{height:0}.x-panel-header-default-framed-bottom-bc{height:4px}.x-panel-header-default-framed-bottom-tl,.x-panel-header-default-framed-bottom-bl,.x-panel-header-default-framed-bottom-tr,.x-panel-header-default-framed-bottom-br,.x-panel-header-default-framed-bottom-tc,.x-panel-header-default-framed-bottom-bc,.x-panel-header-default-framed-bottom-ml,.x-panel-header-default-framed-bottom-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-bottom-corners.gif)}.x-panel-header-default-framed-bottom-ml,.x-panel-header-default-framed-bottom-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-bottom-sides.gif);background-repeat:repeat-y}.x-panel-header-default-framed-bottom-mc{padding:5px 3px 3px 3px}.x-strict .x-ie7 .x-panel-header-default-framed-bottom-tl,.x-strict .x-ie7 .x-panel-header-default-framed-bottom-bl{position:relative;right:0}.x-panel-header-default-framed-bottom:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-default-framed-bottom-corners.gif), sides:url(images/panel-header/panel-header-default-framed-bottom-sides.gif)"}.x-panel-header-default-framed-left{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:5px 5px 5px 5px;border-width:2px 0 2px 2px;border-style:solid;background-color:#444}.x-panel-header-default-framed-left-mc{background-color:#444}.x-nbr .x-panel-header-default-framed-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-default-framed-left-frameInfo{font-family:dh-4-0-0-4-2-0-2-2-5-5-5-5}.x-panel-header-default-framed-left-tl{background-position:0 -8px}.x-panel-header-default-framed-left-tr{background-position:right -12px}.x-panel-header-default-framed-left-bl{background-position:0 -16px}.x-panel-header-default-framed-left-br{background-position:right -20px}.x-panel-header-default-framed-left-ml{background-position:0 left}.x-panel-header-default-framed-left-mr{background-position:right left}.x-panel-header-default-framed-left-tc{background-position:0 0}.x-panel-header-default-framed-left-bc{background-position:0 -4px}.x-panel-header-default-framed-left-tr,.x-panel-header-default-framed-left-br,.x-panel-header-default-framed-left-mr{padding-right:0}.x-panel-header-default-framed-left-tl,.x-panel-header-default-framed-left-bl,.x-panel-header-default-framed-left-ml{padding-left:4px}.x-panel-header-default-framed-left-tc{height:4px}.x-panel-header-default-framed-left-bc{height:4px}.x-panel-header-default-framed-left-tl,.x-panel-header-default-framed-left-bl,.x-panel-header-default-framed-left-tr,.x-panel-header-default-framed-left-br,.x-panel-header-default-framed-left-tc,.x-panel-header-default-framed-left-bc,.x-panel-header-default-framed-left-ml,.x-panel-header-default-framed-left-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-left-corners.gif)}.x-panel-header-default-framed-left-ml,.x-panel-header-default-framed-left-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-left-sides.gif);background-repeat:repeat-y}.x-panel-header-default-framed-left-mc{padding:3px 5px 3px 3px}.x-strict .x-ie7 .x-panel-header-default-framed-left-tl,.x-strict .x-ie7 .x-panel-header-default-framed-left-bl{position:relative;right:0}.x-panel-header-default-framed-left:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-default-framed-left-corners.gif), sides:url(images/panel-header/panel-header-default-framed-left-sides.gif)"}.x-panel-header-default-framed-collapsed-top{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:5px 5px 5px 5px;border-width:2px;border-style:solid;background-color:#444}.x-panel-header-default-framed-collapsed-top-mc{background-color:#444}.x-nbr .x-panel-header-default-framed-collapsed-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-default-framed-collapsed-top-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-5-5-5-5}.x-panel-header-default-framed-collapsed-top-tl{background-position:0 -8px}.x-panel-header-default-framed-collapsed-top-tr{background-position:right -12px}.x-panel-header-default-framed-collapsed-top-bl{background-position:0 -16px}.x-panel-header-default-framed-collapsed-top-br{background-position:right -20px}.x-panel-header-default-framed-collapsed-top-ml{background-position:0 top}.x-panel-header-default-framed-collapsed-top-mr{background-position:right top}.x-panel-header-default-framed-collapsed-top-tc{background-position:0 0}.x-panel-header-default-framed-collapsed-top-bc{background-position:0 -4px}.x-panel-header-default-framed-collapsed-top-tr,.x-panel-header-default-framed-collapsed-top-br,.x-panel-header-default-framed-collapsed-top-mr{padding-right:4px}.x-panel-header-default-framed-collapsed-top-tl,.x-panel-header-default-framed-collapsed-top-bl,.x-panel-header-default-framed-collapsed-top-ml{padding-left:4px}.x-panel-header-default-framed-collapsed-top-tc{height:4px}.x-panel-header-default-framed-collapsed-top-bc{height:4px}.x-panel-header-default-framed-collapsed-top-tl,.x-panel-header-default-framed-collapsed-top-bl,.x-panel-header-default-framed-collapsed-top-tr,.x-panel-header-default-framed-collapsed-top-br,.x-panel-header-default-framed-collapsed-top-tc,.x-panel-header-default-framed-collapsed-top-bc,.x-panel-header-default-framed-collapsed-top-ml,.x-panel-header-default-framed-collapsed-top-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-collapsed-top-corners.gif)}.x-panel-header-default-framed-collapsed-top-ml,.x-panel-header-default-framed-collapsed-top-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-collapsed-top-sides.gif);background-repeat:repeat-y}.x-panel-header-default-framed-collapsed-top-mc{padding:3px 3px 3px 3px}.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-top-tl,.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-top-bl{position:relative;right:0}.x-panel-header-default-framed-collapsed-top:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-default-framed-collapsed-top-corners.gif), sides:url(images/panel-header/panel-header-default-framed-collapsed-top-sides.gif)"}.x-panel-header-default-framed-collapsed-right{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:5px 5px 5px 5px;border-width:2px;border-style:solid;background-color:#444}.x-panel-header-default-framed-collapsed-right-mc{background-color:#444}.x-nbr .x-panel-header-default-framed-collapsed-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-default-framed-collapsed-right-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-5-5-5-5}.x-panel-header-default-framed-collapsed-right-tl{background-position:0 -8px}.x-panel-header-default-framed-collapsed-right-tr{background-position:right -12px}.x-panel-header-default-framed-collapsed-right-bl{background-position:0 -16px}.x-panel-header-default-framed-collapsed-right-br{background-position:right -20px}.x-panel-header-default-framed-collapsed-right-ml{background-position:0 right}.x-panel-header-default-framed-collapsed-right-mr{background-position:right right}.x-panel-header-default-framed-collapsed-right-tc{background-position:0 0}.x-panel-header-default-framed-collapsed-right-bc{background-position:0 -4px}.x-panel-header-default-framed-collapsed-right-tr,.x-panel-header-default-framed-collapsed-right-br,.x-panel-header-default-framed-collapsed-right-mr{padding-right:4px}.x-panel-header-default-framed-collapsed-right-tl,.x-panel-header-default-framed-collapsed-right-bl,.x-panel-header-default-framed-collapsed-right-ml{padding-left:4px}.x-panel-header-default-framed-collapsed-right-tc{height:4px}.x-panel-header-default-framed-collapsed-right-bc{height:4px}.x-panel-header-default-framed-collapsed-right-tl,.x-panel-header-default-framed-collapsed-right-bl,.x-panel-header-default-framed-collapsed-right-tr,.x-panel-header-default-framed-collapsed-right-br,.x-panel-header-default-framed-collapsed-right-tc,.x-panel-header-default-framed-collapsed-right-bc,.x-panel-header-default-framed-collapsed-right-ml,.x-panel-header-default-framed-collapsed-right-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-collapsed-right-corners.gif)}.x-panel-header-default-framed-collapsed-right-ml,.x-panel-header-default-framed-collapsed-right-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-collapsed-right-sides.gif);background-repeat:repeat-y}.x-panel-header-default-framed-collapsed-right-mc{padding:3px 3px 3px 3px}.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-right-tl,.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-right-bl{position:relative;right:0}.x-panel-header-default-framed-collapsed-right:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-default-framed-collapsed-right-corners.gif), sides:url(images/panel-header/panel-header-default-framed-collapsed-right-sides.gif)"}.x-panel-header-default-framed-collapsed-bottom{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:5px 5px 5px 5px;border-width:2px;border-style:solid;background-color:#444}.x-panel-header-default-framed-collapsed-bottom-mc{background-color:#444}.x-nbr .x-panel-header-default-framed-collapsed-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-default-framed-collapsed-bottom-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-5-5-5-5}.x-panel-header-default-framed-collapsed-bottom-tl{background-position:0 -8px}.x-panel-header-default-framed-collapsed-bottom-tr{background-position:right -12px}.x-panel-header-default-framed-collapsed-bottom-bl{background-position:0 -16px}.x-panel-header-default-framed-collapsed-bottom-br{background-position:right -20px}.x-panel-header-default-framed-collapsed-bottom-ml{background-position:0 bottom}.x-panel-header-default-framed-collapsed-bottom-mr{background-position:right bottom}.x-panel-header-default-framed-collapsed-bottom-tc{background-position:0 0}.x-panel-header-default-framed-collapsed-bottom-bc{background-position:0 -4px}.x-panel-header-default-framed-collapsed-bottom-tr,.x-panel-header-default-framed-collapsed-bottom-br,.x-panel-header-default-framed-collapsed-bottom-mr{padding-right:4px}.x-panel-header-default-framed-collapsed-bottom-tl,.x-panel-header-default-framed-collapsed-bottom-bl,.x-panel-header-default-framed-collapsed-bottom-ml{padding-left:4px}.x-panel-header-default-framed-collapsed-bottom-tc{height:4px}.x-panel-header-default-framed-collapsed-bottom-bc{height:4px}.x-panel-header-default-framed-collapsed-bottom-tl,.x-panel-header-default-framed-collapsed-bottom-bl,.x-panel-header-default-framed-collapsed-bottom-tr,.x-panel-header-default-framed-collapsed-bottom-br,.x-panel-header-default-framed-collapsed-bottom-tc,.x-panel-header-default-framed-collapsed-bottom-bc,.x-panel-header-default-framed-collapsed-bottom-ml,.x-panel-header-default-framed-collapsed-bottom-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-collapsed-bottom-corners.gif)}.x-panel-header-default-framed-collapsed-bottom-ml,.x-panel-header-default-framed-collapsed-bottom-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-collapsed-bottom-sides.gif);background-repeat:repeat-y}.x-panel-header-default-framed-collapsed-bottom-mc{padding:3px 3px 3px 3px}.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-bottom-tl,.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-bottom-bl{position:relative;right:0}.x-panel-header-default-framed-collapsed-bottom:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-default-framed-collapsed-bottom-corners.gif), sides:url(images/panel-header/panel-header-default-framed-collapsed-bottom-sides.gif)"}.x-panel-header-default-framed-collapsed-left{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:5px 5px 5px 5px;border-width:2px;border-style:solid;background-color:#444}.x-panel-header-default-framed-collapsed-left-mc{background-color:#444}.x-nbr .x-panel-header-default-framed-collapsed-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-default-framed-collapsed-left-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-5-5-5-5}.x-panel-header-default-framed-collapsed-left-tl{background-position:0 -8px}.x-panel-header-default-framed-collapsed-left-tr{background-position:right -12px}.x-panel-header-default-framed-collapsed-left-bl{background-position:0 -16px}.x-panel-header-default-framed-collapsed-left-br{background-position:right -20px}.x-panel-header-default-framed-collapsed-left-ml{background-position:0 left}.x-panel-header-default-framed-collapsed-left-mr{background-position:right left}.x-panel-header-default-framed-collapsed-left-tc{background-position:0 0}.x-panel-header-default-framed-collapsed-left-bc{background-position:0 -4px}.x-panel-header-default-framed-collapsed-left-tr,.x-panel-header-default-framed-collapsed-left-br,.x-panel-header-default-framed-collapsed-left-mr{padding-right:4px}.x-panel-header-default-framed-collapsed-left-tl,.x-panel-header-default-framed-collapsed-left-bl,.x-panel-header-default-framed-collapsed-left-ml{padding-left:4px}.x-panel-header-default-framed-collapsed-left-tc{height:4px}.x-panel-header-default-framed-collapsed-left-bc{height:4px}.x-panel-header-default-framed-collapsed-left-tl,.x-panel-header-default-framed-collapsed-left-bl,.x-panel-header-default-framed-collapsed-left-tr,.x-panel-header-default-framed-collapsed-left-br,.x-panel-header-default-framed-collapsed-left-tc,.x-panel-header-default-framed-collapsed-left-bc,.x-panel-header-default-framed-collapsed-left-ml,.x-panel-header-default-framed-collapsed-left-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-collapsed-left-corners.gif)}.x-panel-header-default-framed-collapsed-left-ml,.x-panel-header-default-framed-collapsed-left-mr{zoom:1;background-image:url(images/panel-header/panel-header-default-framed-collapsed-left-sides.gif);background-repeat:repeat-y}.x-panel-header-default-framed-collapsed-left-mc{padding:3px 3px 3px 3px}.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-left-tl,.x-strict .x-ie7 .x-panel-header-default-framed-collapsed-left-bl{position:relative;right:0}.x-panel-header-default-framed-collapsed-left:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-default-framed-collapsed-left-corners.gif), sides:url(images/panel-header/panel-header-default-framed-collapsed-left-sides.gif)"}.x-panel .x-panel-header-default-framed-top{border-bottom-width:2px!important}.x-panel .x-panel-header-default-framed-right{border-left-width:2px!important}.x-panel .x-panel-header-default-framed-bottom{border-top-width:2px!important}.x-panel .x-panel-header-default-framed-left{border-right-width:2px!important}.x-nbr .x-panel-header-default-framed-collapsed-top{border-bottom-width:0!important}.x-nbr .x-panel-header-default-framed-collapsed-right{border-left-width:0!important}.x-nbr .x-panel-header-default-framed-collapsed-bottom{border-top-width:0!important}.x-nbr .x-panel-header-default-framed-collapsed-left{border-right-width:0!important}.x-panel-header-default-framed-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-default-framed-vertical .x-panel-header-text-container{background-color:#444;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#444444)}.x-panel-header-default-framed .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-default-framed .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-default-framed .x-panel-header-glyph{color:#a1a1a1}.x-panel-header-default-framed-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-default-framed-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-default-framed-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-default-framed-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-default-framed-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-default-framed-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-default-framed-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-default-framed-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-default-framed-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-default-framed-outer-border-l{border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-framed-outer-border-b{border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-default-framed-outer-border-bl{border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-framed-outer-border-r{border-right-color:#444!important;border-right-width:1px!important}.x-panel-default-framed-outer-border-rl{border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-framed-outer-border-rb{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-default-framed-outer-border-rbl{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-framed-outer-border-t{border-top-color:#444!important;border-top-width:1px!important}.x-panel-default-framed-outer-border-tl{border-top-color:#444!important;border-top-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-framed-outer-border-tb{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-default-framed-outer-border-tbl{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-framed-outer-border-tr{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important}.x-panel-default-framed-outer-border-trl{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-default-framed-outer-border-trb{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-default-framed-outer-border-trbl{border-color:#444!important;border-width:1px!important}.x-tip-anchor{position:absolute;overflow:hidden;height:10px;width:10px;border-style:solid;border-width:5px;border-color:#f5c649;zoom:1}.x-content-box .x-tip-anchor{height:0;width:0}.x-tip-anchor-top{border-top-color:transparent;border-left-color:transparent;border-right-color:transparent;_border-top-color:pink;_border-left-color:pink;_border-right-color:pink;_filter:chroma(color=pink)}.x-tip-anchor-bottom{border-bottom-color:transparent;border-left-color:transparent;border-right-color:transparent;_border-bottom-color:pink;_border-left-color:pink;_border-right-color:pink;_filter:chroma(color=pink)}.x-tip-anchor-left{border-top-color:transparent;border-bottom-color:transparent;border-left-color:transparent;_border-top-color:pink;_border-bottom-color:pink;_border-left-color:pink;_filter:chroma(color=pink)}.x-tip-anchor-right{border-top-color:transparent;border-bottom-color:transparent;border-right-color:transparent;_border-top-color:pink;_border-bottom-color:pink;_border-right-color:pink;_filter:chroma(color=pink)}.x-tip-default{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:2px 2px 2px 2px;border-width:1px;border-style:solid;background-color:#fffaee}.x-tip-default-mc{background-color:#fffaee}.x-nbr .x-tip-default{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-tip-default-frameInfo{font-family:th-3-3-3-3-1-1-1-1-2-2-2-2}.x-tip-default-tl{background-position:0 -6px}.x-tip-default-tr{background-position:right -9px}.x-tip-default-bl{background-position:0 -12px}.x-tip-default-br{background-position:right -15px}.x-tip-default-ml{background-position:0 top}.x-tip-default-mr{background-position:right top}.x-tip-default-tc{background-position:0 0}.x-tip-default-bc{background-position:0 -3px}.x-tip-default-tr,.x-tip-default-br,.x-tip-default-mr{padding-right:3px}.x-tip-default-tl,.x-tip-default-bl,.x-tip-default-ml{padding-left:3px}.x-tip-default-tc{height:3px}.x-tip-default-bc{height:3px}.x-tip-default-tl,.x-tip-default-bl,.x-tip-default-tr,.x-tip-default-br,.x-tip-default-tc,.x-tip-default-bc,.x-tip-default-ml,.x-tip-default-mr{zoom:1;background-image:url(images/tip/tip-default-corners.gif)}.x-tip-default-ml,.x-tip-default-mr{zoom:1;background-image:url(images/tip/tip-default-sides.gif);background-repeat:repeat-y}.x-tip-default-mc{padding:0}.x-strict .x-ie7 .x-tip-default-tl,.x-strict .x-ie7 .x-tip-default-bl{position:relative;right:0}.x-tip-default:after{display:none;content:"x-slicer:corners:url(images/tip/tip-default-corners.gif), sides:url(images/tip/tip-default-sides.gif)"}.x-tip-default{border-color:#f5c649}.x-tip-default .x-tool-img{background-image:url(images/tools/tool-sprites-dark.png);background-color:#fffaee}.x-tip-header-default .x-tool-after-title{margin:0 0 0 6px}.x-tip-header-default .x-tool-before-title{margin:0 6px 0 0}.x-tip-header-body-default{padding:3px 3px 0 3px}.x-tip-header-text-container-default{color:black;font-size:13px;font-weight:bold}.x-tip-body-default{padding:3px;color:black;font-size:13px;font-weight:normal}.x-tip-body-default a{color:#006bbf}.x-tip-form-invalid{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:2px 2px 2px 2px;border-width:1px;border-style:solid;background-color:#fffaee}.x-tip-form-invalid-mc{background-color:#fffaee}.x-nbr .x-tip-form-invalid{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-tip-form-invalid-frameInfo{font-family:th-3-3-3-3-1-1-1-1-2-2-2-2}.x-tip-form-invalid-tl{background-position:0 -6px}.x-tip-form-invalid-tr{background-position:right -9px}.x-tip-form-invalid-bl{background-position:0 -12px}.x-tip-form-invalid-br{background-position:right -15px}.x-tip-form-invalid-ml{background-position:0 top}.x-tip-form-invalid-mr{background-position:right top}.x-tip-form-invalid-tc{background-position:0 0}.x-tip-form-invalid-bc{background-position:0 -3px}.x-tip-form-invalid-tr,.x-tip-form-invalid-br,.x-tip-form-invalid-mr{padding-right:3px}.x-tip-form-invalid-tl,.x-tip-form-invalid-bl,.x-tip-form-invalid-ml{padding-left:3px}.x-tip-form-invalid-tc{height:3px}.x-tip-form-invalid-bc{height:3px}.x-tip-form-invalid-tl,.x-tip-form-invalid-bl,.x-tip-form-invalid-tr,.x-tip-form-invalid-br,.x-tip-form-invalid-tc,.x-tip-form-invalid-bc,.x-tip-form-invalid-ml,.x-tip-form-invalid-mr{zoom:1;background-image:url(images/tip/tip-form-invalid-corners.gif)}.x-tip-form-invalid-ml,.x-tip-form-invalid-mr{zoom:1;background-image:url(images/tip/tip-form-invalid-sides.gif);background-repeat:repeat-y}.x-tip-form-invalid-mc{padding:0}.x-strict .x-ie7 .x-tip-form-invalid-tl,.x-strict .x-ie7 .x-tip-form-invalid-bl{position:relative;right:0}.x-tip-form-invalid:after{display:none;content:"x-slicer:corners:url(images/tip/tip-form-invalid-corners.gif), sides:url(images/tip/tip-form-invalid-sides.gif)"}.x-tip-form-invalid{border-color:#f5c649}.x-tip-form-invalid .x-tool-img{background-image:url(images/tools/tool-sprites-dark.png);background-color:#fffaee}.x-tip-header-form-invalid .x-tool-after-title{margin:0 0 0 6px}.x-tip-header-form-invalid .x-tool-before-title{margin:0 6px 0 0}.x-tip-header-body-form-invalid{padding:3px 3px 0 3px}.x-tip-header-text-container-form-invalid{color:black;font-size:13px;font-weight:bold}.x-tip-body-form-invalid{padding:3px 3px 3px 22px;color:black;font-size:13px;font-weight:normal}.x-tip-body-form-invalid a{color:#006bbf}.x-tip-body-form-invalid{background:1px 1px no-repeat;background-image:url(images/form/exclamation.png)}.x-tip-body-form-invalid li{margin-bottom:4px}.x-tip-body-form-invalid li.last{margin-bottom:0}.x-editor .x-form-item-body{padding-bottom:0}.x-form-invalid-under{padding:2px 2px 2px 20px;color:#cf4c35;font:normal 13px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;line-height:16px;background:no-repeat 0 2px;background-image:url(images/form/exclamation.png)}div.x-lbl-top-err-icon{margin-bottom:4px}.x-form-invalid-icon{width:16px;height:16px;margin:0 5px;background-image:url(images/form/exclamation.png);background-repeat:no-repeat}.x-form-item-label{color:#333;font:normal 13px/17px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;margin-top:4px}.x-autocontainer-form-item,.x-anchor-form-item,.x-vbox-form-item,.x-table-form-item{margin-bottom:5px}.x-ie6 .x-form-form-item td{border-top-width:0}.x-ie6 td.x-form-item-pad{height:5px}.x-form-field{color:#333}.x-form-item,.x-form-field{font:normal 13px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-form-type-text textarea.x-form-invalid-field,.x-form-type-text input.x-form-invalid-field,.x-form-type-password textarea.x-form-invalid-field,.x-form-type-password input.x-form-invalid-field,.x-form-type-number textarea.x-form-invalid-field,.x-form-type-number input.x-form-invalid-field,.x-form-type-email textarea.x-form-invalid-field,.x-form-type-email input.x-form-invalid-field,.x-form-type-search textarea.x-form-invalid-field,.x-form-type-search input.x-form-invalid-field,.x-form-type-tel textarea.x-form-invalid-field,.x-form-type-tel input.x-form-invalid-field{background-color:white;border-color:#cf4c35}.x-item-disabled .x-form-item-label,.x-item-disabled .x-form-field,.x-item-disabled .x-form-display-field,.x-item-disabled .x-form-cb-label,.x-item-disabled .x-form-trigger{filter:alpha(opacity=30);opacity:.3}.x-form-text{color:#333;padding:4px 6px 3px 6px;background:white repeat-x 0 0;border-width:1px;border-style:solid;border-color:silver #d9d9d9 #d9d9d9;height:24px;line-height:15px}.x-content-box .x-form-text{height:15px}.x-form-focus{border-color:#96caee!important}.x-form-empty-field,textarea.x-form-empty-field{color:gray}.x-quirks .x-ie .x-form-text,.x-ie7m .x-form-text{margin-top:-1px;margin-bottom:-1px}.x-window-ghost{filter:alpha(opacity=50);opacity:.5}.x-window-default{border-color:#606060;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px}.x-window-default{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:2px 2px 2px 2px;border-width:2px;border-style:solid;background-color:white}.x-window-default-mc{background-color:white}.x-nbr .x-window-default{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-default-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-2-2-2-2}.x-window-default-tl{background-position:0 -8px}.x-window-default-tr{background-position:right -12px}.x-window-default-bl{background-position:0 -16px}.x-window-default-br{background-position:right -20px}.x-window-default-ml{background-position:0 top}.x-window-default-mr{background-position:right top}.x-window-default-tc{background-position:0 0}.x-window-default-bc{background-position:0 -4px}.x-window-default-tr,.x-window-default-br,.x-window-default-mr{padding-right:4px}.x-window-default-tl,.x-window-default-bl,.x-window-default-ml{padding-left:4px}.x-window-default-tc{height:4px}.x-window-default-bc{height:4px}.x-window-default-tl,.x-window-default-bl,.x-window-default-tr,.x-window-default-br,.x-window-default-tc,.x-window-default-bc,.x-window-default-ml,.x-window-default-mr{zoom:1;background-image:url(images/window/window-default-corners.gif)}.x-window-default-ml,.x-window-default-mr{zoom:1;background-image:url(images/window/window-default-sides.gif);background-repeat:repeat-y}.x-window-default-mc{padding:0}.x-strict .x-ie7 .x-window-default-tl,.x-strict .x-ie7 .x-window-default-bl{position:relative;right:0}.x-window-default:after{display:none;content:"x-slicer:corners:url(images/window/window-default-corners.gif), sides:url(images/window/window-default-sides.gif)"}.x-window-body-default{border-color:#606060;border-width:1px;border-style:solid;background:white;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-window-header-default{font-size:18px;border-color:#606060;zoom:1;background-color:#ebebeb}.x-window-header-default .x-tool-img{background-image:url(images/tools/tool-sprites-dark.png);background-color:#ebebeb}.x-window-header-default-vertical .x-window-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-window-header-default-vertical .x-window-header-text-container{background-color:#ebebeb;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#ebebeb)}.x-window-header-text-container-default{color:#333;font-weight:bold;line-height:22px;font-family:arial,helvetica,verdana,sans-serif;font-size:18px;padding:1px 0 0;text-transform:none}.x-window-header-default-top{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:10px 10px 8px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-default-top-mc{background-color:#ebebeb}.x-nbr .x-window-header-default-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-default-top-frameInfo{font-family:dh-4-4-0-0-0-0-0-0-10-10-8-10}.x-window-header-default-top-tl{background-position:0 -8px}.x-window-header-default-top-tr{background-position:right -12px}.x-window-header-default-top-bl{background-position:0 -16px}.x-window-header-default-top-br{background-position:right -20px}.x-window-header-default-top-ml{background-position:0 top}.x-window-header-default-top-mr{background-position:right top}.x-window-header-default-top-tc{background-position:0 0}.x-window-header-default-top-bc{background-position:0 -4px}.x-window-header-default-top-tr,.x-window-header-default-top-br,.x-window-header-default-top-mr{padding-right:4px}.x-window-header-default-top-tl,.x-window-header-default-top-bl,.x-window-header-default-top-ml{padding-left:4px}.x-window-header-default-top-tc{height:4px}.x-window-header-default-top-bc{height:0}.x-window-header-default-top-tl,.x-window-header-default-top-bl,.x-window-header-default-top-tr,.x-window-header-default-top-br,.x-window-header-default-top-tc,.x-window-header-default-top-bc,.x-window-header-default-top-ml,.x-window-header-default-top-mr{zoom:1;background-image:url(images/window-header/window-header-default-top-corners.gif)}.x-window-header-default-top-ml,.x-window-header-default-top-mr{zoom:1;background-image:url(images/window-header/window-header-default-top-sides.gif);background-repeat:repeat-y}.x-window-header-default-top-mc{padding:6px 6px 8px 6px}.x-strict .x-ie7 .x-window-header-default-top-tl,.x-strict .x-ie7 .x-window-header-default-top-bl{position:relative;right:0}.x-window-header-default-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-default-top-corners.gif), sides:url(images/window-header/window-header-default-top-sides.gif)"}.x-window-header-default-right{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:10px 10px 10px 8px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-default-right-mc{background-color:#ebebeb}.x-nbr .x-window-header-default-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-default-right-frameInfo{font-family:dh-0-4-4-0-0-0-0-0-10-10-10-8}.x-window-header-default-right-tl{background-position:0 -8px}.x-window-header-default-right-tr{background-position:right -12px}.x-window-header-default-right-bl{background-position:0 -16px}.x-window-header-default-right-br{background-position:right -20px}.x-window-header-default-right-ml{background-position:0 top}.x-window-header-default-right-mr{background-position:right top}.x-window-header-default-right-tc{background-position:0 0}.x-window-header-default-right-bc{background-position:0 -4px}.x-window-header-default-right-tr,.x-window-header-default-right-br,.x-window-header-default-right-mr{padding-right:4px}.x-window-header-default-right-tl,.x-window-header-default-right-bl,.x-window-header-default-right-ml{padding-left:0}.x-window-header-default-right-tc{height:4px}.x-window-header-default-right-bc{height:4px}.x-window-header-default-right-tl,.x-window-header-default-right-bl,.x-window-header-default-right-tr,.x-window-header-default-right-br,.x-window-header-default-right-tc,.x-window-header-default-right-bc,.x-window-header-default-right-ml,.x-window-header-default-right-mr{zoom:1;background-image:url(images/window-header/window-header-default-right-corners.gif)}.x-window-header-default-right-ml,.x-window-header-default-right-mr{zoom:1;background-image:url(images/window-header/window-header-default-right-sides.gif);background-repeat:repeat-y}.x-window-header-default-right-mc{padding:6px 6px 6px 8px}.x-strict .x-ie7 .x-window-header-default-right-tl,.x-strict .x-ie7 .x-window-header-default-right-bl{position:relative;right:0}.x-window-header-default-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-default-right-corners.gif), sides:url(images/window-header/window-header-default-right-sides.gif)"}.x-window-header-default-bottom{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:8px 10px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-default-bottom-mc{background-color:#ebebeb}.x-nbr .x-window-header-default-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-default-bottom-frameInfo{font-family:dh-0-0-4-4-0-0-0-0-8-10-10-10}.x-window-header-default-bottom-tl{background-position:0 -8px}.x-window-header-default-bottom-tr{background-position:right -12px}.x-window-header-default-bottom-bl{background-position:0 -16px}.x-window-header-default-bottom-br{background-position:right -20px}.x-window-header-default-bottom-ml{background-position:0 top}.x-window-header-default-bottom-mr{background-position:right top}.x-window-header-default-bottom-tc{background-position:0 0}.x-window-header-default-bottom-bc{background-position:0 -4px}.x-window-header-default-bottom-tr,.x-window-header-default-bottom-br,.x-window-header-default-bottom-mr{padding-right:4px}.x-window-header-default-bottom-tl,.x-window-header-default-bottom-bl,.x-window-header-default-bottom-ml{padding-left:4px}.x-window-header-default-bottom-tc{height:0}.x-window-header-default-bottom-bc{height:4px}.x-window-header-default-bottom-tl,.x-window-header-default-bottom-bl,.x-window-header-default-bottom-tr,.x-window-header-default-bottom-br,.x-window-header-default-bottom-tc,.x-window-header-default-bottom-bc,.x-window-header-default-bottom-ml,.x-window-header-default-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-default-bottom-corners.gif)}.x-window-header-default-bottom-ml,.x-window-header-default-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-default-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-default-bottom-mc{padding:8px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-default-bottom-tl,.x-strict .x-ie7 .x-window-header-default-bottom-bl{position:relative;right:0}.x-window-header-default-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-default-bottom-corners.gif), sides:url(images/window-header/window-header-default-bottom-sides.gif)"}.x-window-header-default-left{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:10px 8px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-default-left-mc{background-color:#ebebeb}.x-nbr .x-window-header-default-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-default-left-frameInfo{font-family:dh-4-0-0-4-0-0-0-0-10-8-10-10}.x-window-header-default-left-tl{background-position:0 -8px}.x-window-header-default-left-tr{background-position:right -12px}.x-window-header-default-left-bl{background-position:0 -16px}.x-window-header-default-left-br{background-position:right -20px}.x-window-header-default-left-ml{background-position:0 top}.x-window-header-default-left-mr{background-position:right top}.x-window-header-default-left-tc{background-position:0 0}.x-window-header-default-left-bc{background-position:0 -4px}.x-window-header-default-left-tr,.x-window-header-default-left-br,.x-window-header-default-left-mr{padding-right:0}.x-window-header-default-left-tl,.x-window-header-default-left-bl,.x-window-header-default-left-ml{padding-left:4px}.x-window-header-default-left-tc{height:4px}.x-window-header-default-left-bc{height:4px}.x-window-header-default-left-tl,.x-window-header-default-left-bl,.x-window-header-default-left-tr,.x-window-header-default-left-br,.x-window-header-default-left-tc,.x-window-header-default-left-bc,.x-window-header-default-left-ml,.x-window-header-default-left-mr{zoom:1;background-image:url(images/window-header/window-header-default-left-corners.gif)}.x-window-header-default-left-ml,.x-window-header-default-left-mr{zoom:1;background-image:url(images/window-header/window-header-default-left-sides.gif);background-repeat:repeat-y}.x-window-header-default-left-mc{padding:6px 8px 6px 6px}.x-strict .x-ie7 .x-window-header-default-left-tl,.x-strict .x-ie7 .x-window-header-default-left-bl{position:relative;right:0}.x-window-header-default-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-default-left-corners.gif), sides:url(images/window-header/window-header-default-left-sides.gif)"}.x-window-header-default-collapsed-top{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-default-collapsed-top-mc{background-color:#ebebeb}.x-nbr .x-window-header-default-collapsed-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-default-collapsed-top-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-default-collapsed-top-tl{background-position:0 -8px}.x-window-header-default-collapsed-top-tr{background-position:right -12px}.x-window-header-default-collapsed-top-bl{background-position:0 -16px}.x-window-header-default-collapsed-top-br{background-position:right -20px}.x-window-header-default-collapsed-top-ml{background-position:0 top}.x-window-header-default-collapsed-top-mr{background-position:right top}.x-window-header-default-collapsed-top-tc{background-position:0 0}.x-window-header-default-collapsed-top-bc{background-position:0 -4px}.x-window-header-default-collapsed-top-tr,.x-window-header-default-collapsed-top-br,.x-window-header-default-collapsed-top-mr{padding-right:4px}.x-window-header-default-collapsed-top-tl,.x-window-header-default-collapsed-top-bl,.x-window-header-default-collapsed-top-ml{padding-left:4px}.x-window-header-default-collapsed-top-tc{height:4px}.x-window-header-default-collapsed-top-bc{height:4px}.x-window-header-default-collapsed-top-tl,.x-window-header-default-collapsed-top-bl,.x-window-header-default-collapsed-top-tr,.x-window-header-default-collapsed-top-br,.x-window-header-default-collapsed-top-tc,.x-window-header-default-collapsed-top-bc,.x-window-header-default-collapsed-top-ml,.x-window-header-default-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-default-collapsed-top-corners.gif)}.x-window-header-default-collapsed-top-ml,.x-window-header-default-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-default-collapsed-top-sides.gif);background-repeat:repeat-y}.x-window-header-default-collapsed-top-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-default-collapsed-top-tl,.x-strict .x-ie7 .x-window-header-default-collapsed-top-bl{position:relative;right:0}.x-window-header-default-collapsed-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-default-collapsed-top-corners.gif), sides:url(images/window-header/window-header-default-collapsed-top-sides.gif)"}.x-window-header-default-collapsed-right{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-default-collapsed-right-mc{background-color:#ebebeb}.x-nbr .x-window-header-default-collapsed-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-default-collapsed-right-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-default-collapsed-right-tl{background-position:0 -8px}.x-window-header-default-collapsed-right-tr{background-position:right -12px}.x-window-header-default-collapsed-right-bl{background-position:0 -16px}.x-window-header-default-collapsed-right-br{background-position:right -20px}.x-window-header-default-collapsed-right-ml{background-position:0 top}.x-window-header-default-collapsed-right-mr{background-position:right top}.x-window-header-default-collapsed-right-tc{background-position:0 0}.x-window-header-default-collapsed-right-bc{background-position:0 -4px}.x-window-header-default-collapsed-right-tr,.x-window-header-default-collapsed-right-br,.x-window-header-default-collapsed-right-mr{padding-right:4px}.x-window-header-default-collapsed-right-tl,.x-window-header-default-collapsed-right-bl,.x-window-header-default-collapsed-right-ml{padding-left:4px}.x-window-header-default-collapsed-right-tc{height:4px}.x-window-header-default-collapsed-right-bc{height:4px}.x-window-header-default-collapsed-right-tl,.x-window-header-default-collapsed-right-bl,.x-window-header-default-collapsed-right-tr,.x-window-header-default-collapsed-right-br,.x-window-header-default-collapsed-right-tc,.x-window-header-default-collapsed-right-bc,.x-window-header-default-collapsed-right-ml,.x-window-header-default-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-default-collapsed-right-corners.gif)}.x-window-header-default-collapsed-right-ml,.x-window-header-default-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-default-collapsed-right-sides.gif);background-repeat:repeat-y}.x-window-header-default-collapsed-right-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-default-collapsed-right-tl,.x-strict .x-ie7 .x-window-header-default-collapsed-right-bl{position:relative;right:0}.x-window-header-default-collapsed-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-default-collapsed-right-corners.gif), sides:url(images/window-header/window-header-default-collapsed-right-sides.gif)"}.x-window-header-default-collapsed-bottom{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-default-collapsed-bottom-mc{background-color:#ebebeb}.x-nbr .x-window-header-default-collapsed-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-default-collapsed-bottom-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-default-collapsed-bottom-tl{background-position:0 -8px}.x-window-header-default-collapsed-bottom-tr{background-position:right -12px}.x-window-header-default-collapsed-bottom-bl{background-position:0 -16px}.x-window-header-default-collapsed-bottom-br{background-position:right -20px}.x-window-header-default-collapsed-bottom-ml{background-position:0 top}.x-window-header-default-collapsed-bottom-mr{background-position:right top}.x-window-header-default-collapsed-bottom-tc{background-position:0 0}.x-window-header-default-collapsed-bottom-bc{background-position:0 -4px}.x-window-header-default-collapsed-bottom-tr,.x-window-header-default-collapsed-bottom-br,.x-window-header-default-collapsed-bottom-mr{padding-right:4px}.x-window-header-default-collapsed-bottom-tl,.x-window-header-default-collapsed-bottom-bl,.x-window-header-default-collapsed-bottom-ml{padding-left:4px}.x-window-header-default-collapsed-bottom-tc{height:4px}.x-window-header-default-collapsed-bottom-bc{height:4px}.x-window-header-default-collapsed-bottom-tl,.x-window-header-default-collapsed-bottom-bl,.x-window-header-default-collapsed-bottom-tr,.x-window-header-default-collapsed-bottom-br,.x-window-header-default-collapsed-bottom-tc,.x-window-header-default-collapsed-bottom-bc,.x-window-header-default-collapsed-bottom-ml,.x-window-header-default-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-default-collapsed-bottom-corners.gif)}.x-window-header-default-collapsed-bottom-ml,.x-window-header-default-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-default-collapsed-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-default-collapsed-bottom-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-default-collapsed-bottom-tl,.x-strict .x-ie7 .x-window-header-default-collapsed-bottom-bl{position:relative;right:0}.x-window-header-default-collapsed-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-default-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-default-collapsed-bottom-sides.gif)"}.x-window-header-default-collapsed-left{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-default-collapsed-left-mc{background-color:#ebebeb}.x-nbr .x-window-header-default-collapsed-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-default-collapsed-left-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-default-collapsed-left-tl{background-position:0 -8px}.x-window-header-default-collapsed-left-tr{background-position:right -12px}.x-window-header-default-collapsed-left-bl{background-position:0 -16px}.x-window-header-default-collapsed-left-br{background-position:right -20px}.x-window-header-default-collapsed-left-ml{background-position:0 top}.x-window-header-default-collapsed-left-mr{background-position:right top}.x-window-header-default-collapsed-left-tc{background-position:0 0}.x-window-header-default-collapsed-left-bc{background-position:0 -4px}.x-window-header-default-collapsed-left-tr,.x-window-header-default-collapsed-left-br,.x-window-header-default-collapsed-left-mr{padding-right:4px}.x-window-header-default-collapsed-left-tl,.x-window-header-default-collapsed-left-bl,.x-window-header-default-collapsed-left-ml{padding-left:4px}.x-window-header-default-collapsed-left-tc{height:4px}.x-window-header-default-collapsed-left-bc{height:4px}.x-window-header-default-collapsed-left-tl,.x-window-header-default-collapsed-left-bl,.x-window-header-default-collapsed-left-tr,.x-window-header-default-collapsed-left-br,.x-window-header-default-collapsed-left-tc,.x-window-header-default-collapsed-left-bc,.x-window-header-default-collapsed-left-ml,.x-window-header-default-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-default-collapsed-left-corners.gif)}.x-window-header-default-collapsed-left-ml,.x-window-header-default-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-default-collapsed-left-sides.gif);background-repeat:repeat-y}.x-window-header-default-collapsed-left-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-default-collapsed-left-tl,.x-strict .x-ie7 .x-window-header-default-collapsed-left-bl{position:relative;right:0}.x-window-header-default-collapsed-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-default-collapsed-left-corners.gif), sides:url(images/window-header/window-header-default-collapsed-left-sides.gif)"}.x-window-header-default .x-window-header-icon{width:16px;height:16px;color:#333;font-size:16px;line-height:16px;background-position:center center}.x-window-header-default .x-window-header-glyph{color:#333;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-window-header-default .x-window-header-glyph{color:#8f8f8f}.x-window-header-default-horizontal .x-window-header-icon-before-title{margin:0 6px 0 0}.x-window-header-default-horizontal .x-window-header-icon-after-title{margin:0 0 0 6px}.x-window-header-default-vertical .x-window-header-icon-before-title{margin:0 0 6px 0}.x-window-header-default-vertical .x-window-header-icon-after-title{margin:6px 0 0 0}.x-window-header-default-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-window-header-default-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-window-header-default-vertical .x-tool-after-title{margin:6px 0 0 0}.x-window-header-default-vertical .x-tool-before-title{margin:0 0 6px 0}.x-window-header-default{border-width:0!important}.x-nbr .x-window-default-collapsed .x-window-header{border-width:0!important}.x-window-default-outer-border-l{border-left-color:#606060!important;border-left-width:1px!important}.x-window-default-outer-border-b{border-bottom-color:#606060!important;border-bottom-width:1px!important}.x-window-default-outer-border-bl{border-bottom-color:#606060!important;border-bottom-width:1px!important;border-left-color:#606060!important;border-left-width:1px!important}.x-window-default-outer-border-r{border-right-color:#606060!important;border-right-width:1px!important}.x-window-default-outer-border-rl{border-right-color:#606060!important;border-right-width:1px!important;border-left-color:#606060!important;border-left-width:1px!important}.x-window-default-outer-border-rb{border-right-color:#606060!important;border-right-width:1px!important;border-bottom-color:#606060!important;border-bottom-width:1px!important}.x-window-default-outer-border-rbl{border-right-color:#606060!important;border-right-width:1px!important;border-bottom-color:#606060!important;border-bottom-width:1px!important;border-left-color:#606060!important;border-left-width:1px!important}.x-window-default-outer-border-t{border-top-color:#606060!important;border-top-width:1px!important}.x-window-default-outer-border-tl{border-top-color:#606060!important;border-top-width:1px!important;border-left-color:#606060!important;border-left-width:1px!important}.x-window-default-outer-border-tb{border-top-color:#606060!important;border-top-width:1px!important;border-bottom-color:#606060!important;border-bottom-width:1px!important}.x-window-default-outer-border-tbl{border-top-color:#606060!important;border-top-width:1px!important;border-bottom-color:#606060!important;border-bottom-width:1px!important;border-left-color:#606060!important;border-left-width:1px!important}.x-window-default-outer-border-tr{border-top-color:#606060!important;border-top-width:1px!important;border-right-color:#606060!important;border-right-width:1px!important}.x-window-default-outer-border-trl{border-top-color:#606060!important;border-top-width:1px!important;border-right-color:#606060!important;border-right-width:1px!important;border-left-color:#606060!important;border-left-width:1px!important}.x-window-default-outer-border-trb{border-top-color:#606060!important;border-top-width:1px!important;border-right-color:#606060!important;border-right-width:1px!important;border-bottom-color:#606060!important;border-bottom-width:1px!important}.x-window-default-outer-border-trbl{border-color:#606060!important;border-width:1px!important}.x-window-body-plain{background-color:transparent}.x-form-textarea{line-height:normal;height:auto}.x-content-box .x-form-textarea{height:auto}.x-form-display-field{height:24px}.x-form-display-field{font:normal 13px/17px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#333;margin-top:4px}.x-progress-default{background-color:#f5f5f5;border-width:0;height:20px;border-color:#444}.x-content-box .x-progress-default{height:20px}.x-progress-default .x-progress-bar-default{background-image:none;background-color:#cdcdcd}.x-progress-default .x-progress-text{color:#666;font-weight:bold;font-size:13px;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;text-align:center;line-height:20px}.x-progress-default .x-progress-text-back{color:#666;line-height:20px}.x-progress-bar-default:after{display:none;content:"x-slicer:stretch:bottom"}.x-message-box .x-window-body{background-color:white;border-width:0}.x-message-box-info,.x-message-box-warning,.x-message-box-question,.x-message-box-error{background-position:left top;background-repeat:no-repeat}.x-message-box-info{background-image:url(images/shared/icon-info.png)}.x-message-box-warning{background-image:url(images/shared/icon-warning.png)}.x-message-box-question{background-image:url(images/shared/icon-question.png)}.x-message-box-error{background-image:url(images/shared/icon-error.png)}.x-grid-body{background:transparent;border-width:1px;border-style:solid;border-color:silver}.x-grid-empty{padding:10px;color:gray;background-color:transparent;font:normal 13px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-grid-cell{color:"none";font:normal 13px/15px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;background-color:white;border-color:#ededed;border-style:solid}.x-grid-row-alt .x-grid-td{background-color:#fafafa}.x-grid-row-before-over .x-grid-td{border-bottom-style:solid;border-bottom-color:#e7e7e7}.x-grid-row-over .x-grid-td{border-bottom-style:solid;border-bottom-color:#e7e7e7}.x-grid-row-before-selected .x-grid-td{border-bottom-style:solid;border-bottom-color:#cdcdcd}.x-grid-row-selected .x-grid-td{border-bottom-style:solid;border-bottom-color:#cdcdcd}.x-grid-row-before-focused .x-grid-td{border-bottom-style:solid;border-bottom-color:#e7e7e7}.x-grid-row-focused .x-grid-td{background-color:#e7e7e7}.x-grid-row-over .x-grid-td{background-color:#e7e7e7}.x-grid-row-selected .x-grid-td{background-color:#cdcdcd}.x-grid-row-focused .x-grid-td{border-bottom-style:solid;border-bottom-color:#e7e7e7}.x-grid-with-row-lines .x-grid-row-focused-first .x-grid-td{border-top:1px solid #e7e7e7}.x-grid-row-selected .x-grid-row-summary .x-grid-td{border-bottom-color:#cdcdcd;border-top-width:0}.x-grid-row-focused .x-grid-row-summary .x-grid-td{border-bottom-color:#e7e7e7;border-top-width:0}.x-grid-with-row-lines .x-grid-td{border-bottom-width:1px}.x-grid-with-row-lines .x-grid-table{border-top:1px solid white}.x-grid-with-row-lines .x-grid-table-over-first{border-top-style:solid;border-top-color:#e7e7e7}.x-grid-with-row-lines .x-grid-table-selected-first{border-top-style:solid;border-top-color:#cdcdcd}.x-grid-with-row-lines .x-grid-table-focused-first{border-top-style:solid;border-top-color:#e7e7e7}.x-grid-cell-inner{text-overflow:ellipsis;padding:5px 10px 4px 10px}.x-grid-cell-special{border-color:#ededed;border-style:solid;border-right-width:1px 0}.x-grid-dirty-cell{background:url(images/grid/dirty.png) no-repeat 0 0}.x-grid-row .x-grid-cell-selected{color:"none";background-color:#cdcdcd}.x-grid-with-col-lines .x-grid-cell{border-right-width:1px}.x-grid-resize-marker{width:1px;background-color:#0f0f0f}.x-mask{filter:alpha(opacity=30);opacity:.3;background:black;cursor:default}.x-mask-msg{padding:8px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;background:#e5e5e5}.x-mask-msg-inner{padding:0;background-color:transparent;color:#666;font:normal 13px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-mask-msg-text{padding:21px 0 0;background-image:url(images/loadmask/loading.gif);background-repeat:no-repeat;background-position:center 0}.col-move-top,.col-move-bottom{width:9px;height:9px}.col-move-top{background-image:url(images/grid/col-move-top.png)}.col-move-bottom{background-image:url(images/grid/col-move-bottom.png)}.x-grid-header-ct{border:1px solid #444;border-bottom-color:#f5f5f5;background-color:#f5f5f5}.x-accordion-item .x-grid-header-ct{border-width:0 0 1px!important}.x-accordion-item .x-grid-header-ct-hidden{border:0!important}.x-grid-body{border-top-color:silver}.x-hmenu-sort-asc{background-image:url(images/grid/hmenu-asc.png)}.x-hmenu-sort-desc{background-image:url(images/grid/hmenu-desc.png)}.x-cols-icon{background-image:url(images/grid/columns.png)}.x-column-header{border-right:1px solid silver;color:#666;font:bold 13px/15px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;background-color:#f5f5f5}.x-group-sub-header{background:transparent;border-top:1px solid silver}.x-group-sub-header .x-column-header-inner{padding:6px 10px 7px 10px}.x-column-header-inner{padding:7px 10px 7px 10px;text-overflow:ellipsis}.x-column-header-inner-empty{text-overflow:clip}.x-column-header-over,.x-column-header-sort-ASC,.x-column-header-sort-DESC{background-image:none;background-color:#f1f1f1}.x-column-header-open{background-color:#f1f1f1}.x-column-header-open .x-column-header-trigger{background-color:#e4e4e4}.x-column-header-trigger{width:18px;cursor:pointer;background-color:transparent;background-position:center center}.x-column-header-align-right .x-column-header-text{margin-right:12px}.x-column-header-sort-ASC .x-column-header-text,.x-column-header-sort-DESC .x-column-header-text{padding-right:17px;background-position:right center}.x-column-header-sort-ASC .x-column-header-text{background-image:url(images/grid/sort_asc.png)}.x-column-header-sort-DESC .x-column-header-text{background-image:url(images/grid/sort_desc.png)}.x-tree-expander{cursor:pointer}.x-tree-arrows .x-tree-expander{background-image:url(images/tree/arrows.png)}.x-tree-arrows .x-tree-expander-over .x-tree-expander{background-position:-32px center}.x-tree-arrows .x-grid-tree-node-expanded .x-tree-expander{background-position:-16px center}.x-tree-arrows .x-grid-tree-node-expanded .x-tree-expander-over .x-tree-expander{background-position:-48px center}.x-tree-lines .x-tree-elbow{background-image:url(images/tree/elbow.png)}.x-tree-lines .x-tree-elbow-end{background-image:url(images/tree/elbow-end.png)}.x-tree-lines .x-tree-elbow-plus{background-image:url(images/tree/elbow-plus.png)}.x-tree-lines .x-tree-elbow-end-plus{background-image:url(images/tree/elbow-end-plus.png)}.x-tree-lines .x-grid-tree-node-expanded .x-tree-elbow-plus{background-image:url(images/tree/elbow-minus.png)}.x-tree-lines .x-grid-tree-node-expanded .x-tree-elbow-end-plus{background-image:url(images/tree/elbow-end-minus.png)}.x-tree-lines .x-tree-elbow-line{background-image:url(images/tree/elbow-line.png)}.x-tree-no-lines .x-tree-expander{background-image:url(images/tree/elbow-plus-nl.png)}.x-tree-no-lines .x-grid-tree-node-expanded .x-tree-expander{background-image:url(images/tree/elbow-minus-nl.png)}.x-tree-icon{width:16px;height:24px}.x-tree-elbow-img{width:18px;height:24px;margin-right:2px}.x-tree-icon,.x-tree-elbow-img,.x-tree-checkbox{margin-top:-5px;margin-bottom:-4px}.x-tree-icon-leaf{background-image:url(images/tree/leaf.png)}.x-tree-icon-parent{background-image:url(images/tree/folder.png)}.x-grid-tree-node-expanded .x-tree-icon-parent{background-image:url(images/tree/folder-open.png)}.x-tree-checkbox{margin-right:4px;top:5px;width:15px;height:15px;background-image:url(images/form/checkbox.png)}.x-tree-checkbox-checked{background-position:0 -15px}.x-grid-tree-loading .x-tree-icon{background-image:url(images/tree/loading.gif)}.x-grid-cell-inner-treecolumn{*font-size:1px}.x-tree-node-text{*font-size:13px;padding-left:4px}.x-grid-cell-inner-treecolumn{padding:5px 10px 4px 6px}.x-form-cb-wrap{height:24px}.x-form-cb{margin-top:5px}.x-form-checkbox{width:15px;height:15px;background:url(images/form/checkbox.png) no-repeat}.x-form-cb-checked .x-form-checkbox{background-position:0 -15px}.x-form-checkbox-focus{background-position:-15px 0}.x-form-cb-checked .x-form-checkbox-focus{background-position:-15px -15px}.x-form-cb-label{margin-top:4px;font:normal 13px/17px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-form-cb-label-before{padding-right:19px}.x-form-cb-label-after{padding-left:19px}.x-form-cb-wrap-inner-no-box-label .x-form-cb{position:static}.x-quirks .x-ie .x-form-cb-wrap-inner-no-box-label,.x-ie7m .x-form-cb-wrap-inner-no-box-label{display:inline;width:15px;zoom:1}.x-grid-rowwrap{border-color:#ededed;border-style:solid}.x-grid-rowbody{font:normal 13px/15px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;padding:5px 10px 5px 10px}.x-grid-cell-inner-row-expander{padding:7px 6px 6px 6px}.x-grid-row-expander{width:11px;height:11px;cursor:pointer;background-image:url(images/grid/group-collapse.png)}.x-grid-row-collapsed .x-grid-row-expander{background-image:url(images/grid/group-expand.png)}.x-slider-horz{padding-left:7px;background:no-repeat 0 -15px}.x-slider-horz .x-slider-end{padding-right:7px;background:no-repeat right -30px}.x-slider-horz .x-slider-inner{height:15px}.x-ie6 .x-form-item .x-slider-horz,.x-ie7 .x-form-item .x-slider-horz,.x-quirks .x-ie .x-form-item .x-slider-horz{margin-top:5px}.x-slider-horz .x-slider-thumb{width:15px;height:15px;margin-left:-7px;background-image:url(images/slider/slider-thumb.png)}.x-slider-horz .x-slider-thumb-over{background-position:-15px -15px}.x-slider-horz .x-slider-thumb-drag{background-position:-30px -30px}.x-slider-vert{padding-top:7px;background:no-repeat -30px 0}.x-slider-vert .x-slider-end{padding-bottom:7px;background:no-repeat -15px bottom;width:15px}.x-slider-vert .x-slider-inner{width:15px}.x-slider-vert .x-slider-thumb{width:15px;height:15px;margin-bottom:-7px;background-image:url(images/slider/slider-v-thumb.png)}.x-slider-vert .x-slider-thumb-over{background-position:-15px -15px}.x-slider-vert .x-slider-thumb-drag{background-position:-30px -30px}.x-slider-horz,.x-slider-horz .x-slider-end,.x-slider-horz .x-slider-inner{background-image:url(images/slider/slider-bg.png)}.x-slider-vert,.x-slider-vert .x-slider-end,.x-slider-vert .x-slider-inner{background-image:url(images/slider/slider-v-bg.png)}.x-menu{border-style:solid;border-width:1px;border-color:#e1e1e1}.x-menu-body{background:white;padding:0}.x-menu-icon-separator{left:26px;border-left:solid 1px #e1e1e1;background-color:white;width:1px}.x-menu-item{cursor:pointer}.x-menu-item-text,.x-menu-item-cmp{margin:0 5px 0 5px}.x-menu-item-indent{margin-left:32px}.x-menu-item-indent-no-separator{margin-left:26px}.x-menu-item-indent-right-icon{margin-right:31px}.x-menu-item-indent-right-arrow{margin-right:22px}.x-menu-item-active{background-image:none;background-color:#dedede}.x-nlg .x-menu-item-active{background:#dedede repeat-x left top;background-image:url(images/menu/menu-item-active-bg.gif)}.x-menu-item-icon{width:16px;height:16px;top:4px;left:5px;background-position:center center}.x-menu-item-active .x-menu-item-icon{top:4px;left:5px}.x-menu-item-glyph{font-size:16px;line-height:16px;color:gray;opacity:.5}.x-ie8m .x-menu-item-glyph{color:#bfbfbf}.x-menu-item-icon-right{width:16px;height:16px;top:4px;right:5px;background-position:center center}.x-menu-item-active .x-menu-item-icon-right{top:4px;right:5px}.x-menu-item-text{font:normal 13px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;line-height:23px;padding-top:1px;color:black;cursor:pointer}.x-menu-item-checked .x-menu-item-checkbox{background-image:url(images/menu/checked.png)}.x-menu-item-checked .x-menu-group-icon{background-image:url(images/menu/group-checked.png)}.x-menu-item-unchecked .x-menu-item-checkbox{background-image:url(images/menu/unchecked.png)}.x-menu-item-unchecked .x-menu-group-icon{background-image:none}.x-menu-item-separator{height:1px;border-top:solid 1px #e1e1e1;background-color:white;margin:2px 0;padding:0}.x-menu-item-arrow{width:12px;height:9px;top:8px;right:0;background-image:url(images/menu/menu-parent.png)}.x-menu-item-active .x-menu-item-arrow{top:8px;right:0}.x-menu-item-disabled{filter:alpha(opacity=50);opacity:.5}.x-content-box .x-menu-icon-separator{width:0}.x-content-box .x-menu-item-separator{height:0}.x-ie .x-menu-item-disabled .x-menu-item-icon{filter:alpha(opacity=50);opacity:.5}.x-ie .x-menu-item-disabled .x-menu-item-text{background-color:transparent}.x-menu-item .x-form-item-label{font-size:13px;color:black}.x-menu-scroll-top{height:16px;background-image:url(images/menu/scroll-top.png)}.x-menu-scroll-bottom{height:16px;background-image:url(images/menu/scroll-bottom.png)}.x-menu-scroll-top,.x-menu-scroll-bottom{filter:alpha(opacity=50);opacity:.5;background-color:white}.x-menu-scroll-top-hover,.x-menu-scroll-bottom-hover{filter:alpha(opacity=60);opacity:.6}.x-menu-scroll-top-pressed,.x-menu-scroll-bottom-pressed{filter:alpha(opacity=70);opacity:.7}.x-menu-item-link:after{display:none;content:"x-slicer:bg:url(images/menu/menu-item-active-bg.gif), stretch:bottom"}.x-form-trigger{background:url(images/form/trigger.png);width:22px}.x-trigger-cell{background-color:white;width:22px}.x-form-trigger-over{background-position:-22px 0}.x-form-trigger-wrap-focus .x-form-trigger{background-position:-66px 0}.x-form-trigger-wrap-focus .x-form-trigger-over{background-position:-88px 0}.x-form-trigger-click,.x-form-trigger-wrap-focus .x-form-trigger-click{background-position:-44px 0}.x-form-clear-trigger{background-image:url(images/form/clear-trigger.png)}.x-form-search-trigger{background-image:url(images/form/search-trigger.png)}.x-quirks .prefixie6 .x-form-trigger-input-cell{height:24px}.x-quirks .prefixie6 .x-field-toolbar .x-form-trigger-input-cell{height:24px}.x-btn-group-default{border-color:#e4e4e4;-webkit-box-shadow:white 0 1px 0 0 inset,white 0 -1px 0 0 inset,white -1px 0 0 0 inset,white 1px 0 0 0 inset;-moz-box-shadow:white 0 1px 0 0 inset,white 0 -1px 0 0 inset,white -1px 0 0 0 inset,white 1px 0 0 0 inset;box-shadow:white 0 1px 0 0 inset,white 0 -1px 0 0 inset,white -1px 0 0 0 inset,white 1px 0 0 0 inset}.x-btn-group-header-default{padding:4px 5px;line-height:16px;background:#e4e4e4;-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0}.x-btn-group-header-default .x-tool-img{background-image:url(images/tools/tool-sprites-dark.png);background-color:#e4e4e4}.x-btn-group-header-text-container-default{font:normal 13px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;line-height:16px;color:#666}.x-btn-group-body-default{padding:0 1px}.x-btn-group-body-default .x-table-layout{border-spacing:5px}.x-btn-group-default-framed{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:0 1px 0 1px;border-width:3px;border-style:solid;background-color:white}.x-btn-group-default-framed-mc{background-color:white}.x-nbr .x-btn-group-default-framed{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-btn-group-default-framed-frameInfo{font-family:dh-3-3-3-3-3-3-3-3-0-1-0-1}.x-btn-group-default-framed-tl{background-position:0 -6px}.x-btn-group-default-framed-tr{background-position:right -9px}.x-btn-group-default-framed-bl{background-position:0 -12px}.x-btn-group-default-framed-br{background-position:right -15px}.x-btn-group-default-framed-ml{background-position:0 top}.x-btn-group-default-framed-mr{background-position:right top}.x-btn-group-default-framed-tc{background-position:0 0}.x-btn-group-default-framed-bc{background-position:0 -3px}.x-btn-group-default-framed-tr,.x-btn-group-default-framed-br,.x-btn-group-default-framed-mr{padding-right:3px}.x-btn-group-default-framed-tl,.x-btn-group-default-framed-bl,.x-btn-group-default-framed-ml{padding-left:3px}.x-btn-group-default-framed-tc{height:3px}.x-btn-group-default-framed-bc{height:3px}.x-btn-group-default-framed-tl,.x-btn-group-default-framed-bl,.x-btn-group-default-framed-tr,.x-btn-group-default-framed-br,.x-btn-group-default-framed-tc,.x-btn-group-default-framed-bc,.x-btn-group-default-framed-ml,.x-btn-group-default-framed-mr{zoom:1;background-image:url(images/btn-group/btn-group-default-framed-corners.gif)}.x-btn-group-default-framed-ml,.x-btn-group-default-framed-mr{zoom:1;background-image:url(images/btn-group/btn-group-default-framed-sides.gif);background-repeat:repeat-y}.x-btn-group-default-framed-mc{padding:0 1px 0 1px}.x-strict .x-ie7 .x-btn-group-default-framed-tl,.x-strict .x-ie7 .x-btn-group-default-framed-bl{position:relative;right:0}.x-btn-group-default-framed:after{display:none;content:"x-slicer:corners:url(images/btn-group/btn-group-default-framed-corners.gif), sides:url(images/btn-group/btn-group-default-framed-sides.gif)"}.x-btn-group-default-framed-notitle{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:0 1px 0 1px;border-width:3px;border-style:solid;background-color:white}.x-btn-group-default-framed-notitle-mc{background-color:white}.x-nbr .x-btn-group-default-framed-notitle{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-btn-group-default-framed-notitle-frameInfo{font-family:dh-3-3-3-3-3-3-3-3-0-1-0-1}.x-btn-group-default-framed-notitle-tl{background-position:0 -6px}.x-btn-group-default-framed-notitle-tr{background-position:right -9px}.x-btn-group-default-framed-notitle-bl{background-position:0 -12px}.x-btn-group-default-framed-notitle-br{background-position:right -15px}.x-btn-group-default-framed-notitle-ml{background-position:0 top}.x-btn-group-default-framed-notitle-mr{background-position:right top}.x-btn-group-default-framed-notitle-tc{background-position:0 0}.x-btn-group-default-framed-notitle-bc{background-position:0 -3px}.x-btn-group-default-framed-notitle-tr,.x-btn-group-default-framed-notitle-br,.x-btn-group-default-framed-notitle-mr{padding-right:3px}.x-btn-group-default-framed-notitle-tl,.x-btn-group-default-framed-notitle-bl,.x-btn-group-default-framed-notitle-ml{padding-left:3px}.x-btn-group-default-framed-notitle-tc{height:3px}.x-btn-group-default-framed-notitle-bc{height:3px}.x-btn-group-default-framed-notitle-tl,.x-btn-group-default-framed-notitle-bl,.x-btn-group-default-framed-notitle-tr,.x-btn-group-default-framed-notitle-br,.x-btn-group-default-framed-notitle-tc,.x-btn-group-default-framed-notitle-bc,.x-btn-group-default-framed-notitle-ml,.x-btn-group-default-framed-notitle-mr{zoom:1;background-image:url(images/btn-group/btn-group-default-framed-notitle-corners.gif)}.x-btn-group-default-framed-notitle-ml,.x-btn-group-default-framed-notitle-mr{zoom:1;background-image:url(images/btn-group/btn-group-default-framed-notitle-sides.gif);background-repeat:repeat-y}.x-btn-group-default-framed-notitle-mc{padding:0 1px 0 1px}.x-strict .x-ie7 .x-btn-group-default-framed-notitle-tl,.x-strict .x-ie7 .x-btn-group-default-framed-notitle-bl{position:relative;right:0}.x-btn-group-default-framed-notitle:after{display:none;content:"x-slicer:corners:url(images/btn-group/btn-group-default-framed-notitle-corners.gif), sides:url(images/btn-group/btn-group-default-framed-notitle-sides.gif)"}.x-btn-group-default-framed{border-color:#e4e4e4;-webkit-box-shadow:white 0 1px 0 0 inset,white 0 -1px 0 0 inset,white -1px 0 0 0 inset,white 1px 0 0 0 inset;-moz-box-shadow:white 0 1px 0 0 inset,white 0 -1px 0 0 inset,white -1px 0 0 0 inset,white 1px 0 0 0 inset;box-shadow:white 0 1px 0 0 inset,white 0 -1px 0 0 inset,white -1px 0 0 0 inset,white 1px 0 0 0 inset}.x-btn-group-header-default-framed{padding:4px 5px;line-height:16px;background:#e4e4e4;-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px;border-top-right-radius:3px}.x-btn-group-header-default-framed .x-tool-img{background-image:url(images/tools/tool-sprites-dark.png);background-color:#e4e4e4}.x-btn-group-header-text-container-default-framed{font:normal 13px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;line-height:16px;color:#666}.x-btn-group-body-default-framed{padding:0 1px 0 1px}.x-btn-group-body-default-framed .x-table-layout{border-spacing:5px}.x-form-checkboxgroup-body{padding:0 4px}.x-form-invalid .x-form-checkboxgroup-body{border:1px solid #cf4c35}.x-check-group-alt{background:#f5f5f5;border-top:1px dotted #f5f5f5;border-bottom:1px dotted #f5f5f5}.x-form-check-group-label{color:#333;padding:2px;margin:0 30px 5px 0;border-width:0 0 1px 0;border-style:solid;border-color:#333}.x-fieldset{border:0 solid #b5b8c8;padding:10px 0 25px 0;margin:0 0 10px;overflow:hidden}.x-ie8m .x-fieldset,.x-quirks .x-ie .x-fieldset{padding-top:0}.x-ie8m .x-fieldset .x-fieldset-body,.x-quirks .x-ie .x-fieldset .x-fieldset-body{padding-top:10px}.x-fieldset-header-checkbox{line-height:16px;margin:1px 0 0}.x-fieldset-header{padding:0 8px 0 0}.x-fieldset-header .x-tool{margin-top:1px;padding:0}.x-fieldset-header-text{font:bold 13px/16px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:black;padding:1px 0}.x-fieldset-header-text-collapsible{cursor:pointer}.x-fieldset-with-title .x-fieldset-header-checkbox,.x-fieldset-with-title .x-tool{margin:1px 3px 0 0}.x-webkit .x-fieldset-header{-webkit-padding-start:0;-webkit-padding-end:8px}.x-opera .x-fieldset-with-legend{margin-top:-0}.x-opera.x-mac .x-fieldset-header-text{padding:2px 0 0}.x-strict .x-ie8 .x-fieldset-header{margin-bottom:-0}.x-strict .x-ie8 .x-fieldset-header .x-tool,.x-strict .x-ie8 .x-fieldset-header .x-fieldset-header-text,.x-strict .x-ie8 .x-fieldset-header .x-fieldset-header-checkbox{position:relative;top:-0}.x-quirks .x-ie .x-fieldset-header,.x-ie8m .x-fieldset-header{padding-left:-2;padding-right:6px}.x-fieldset-collapsed{padding-bottom:0!important;border-width:1px 1px 0 1px!important;border-left-color:transparent!important;border-right-color:transparent!important}.x-fieldset-collapsed .x-fieldset-body{display:none}.x-ie6 .x-fieldset-collapsed{border-width:1px 0 0 0!important;padding-bottom:0!important;margin-left:1px;margin-right:1px}.x-ie .x-fieldset-bwrap{zoom:1}.x-fieldset .x-tool-toggle{background-image:url(images/fieldset/collapse-tool.png);background-position:0 0}.x-fieldset .x-tool-over .x-tool-toggle{background-position:0 -15px}.x-fieldset-collapsed .x-tool-toggle{background-position:-15px 0}.x-fieldset-collapsed .x-tool-over .x-tool-toggle{background-position:-15px -15px}.x-ie .x-fieldset-noborder legend{position:relative;margin-bottom:23px}.x-ie .x-fieldset-noborder legend span{position:absolute;left:16px}x-fieldset-body{overflow:hidden}div.x-form-spinner-up,div.x-form-spinner-down{background-image:url(images/form/spinner.png);background-color:white;width:22px;height:11px}.x-form-spinner-down{background-position:0 -11px}.x-form-trigger-wrap-focus .x-form-spinner-down{background-position:-66px -11px}.x-form-trigger-wrap .x-form-spinner-down-over{background-position:-22px -11px}.x-form-trigger-wrap-focus .x-form-spinner-down-over{background-position:-88px -11px}.x-form-trigger-wrap .x-form-spinner-down-click{background-position:-44px -11px}.x-tbar-page-number{width:30px}.x-tbar-page-first{background-image:url(images/grid/page-first.png)}.x-tbar-page-prev{background-image:url(images/grid/page-prev.png)}.x-tbar-page-next{background-image:url(images/grid/page-next.png)}.x-tbar-page-last{background-image:url(images/grid/page-last.png)}.x-tbar-loading{background-image:url(images/grid/refresh.png)}.x-boundlist{border-width:1px;border-style:solid;border-color:#e1e1e1;background:white}.x-strict .x-ie7m .x-boundlist-list-ct{position:relative}.x-boundlist-item{padding:0 6px;font:normal 13px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;line-height:22px;cursor:pointer;cursor:hand;position:relative;zoom:1;border-width:1px;border-style:dotted;border-color:white}.x-boundlist-selected{background:#cdcdcd;border-color:#cdcdcd}.x-boundlist-item-over{background:#dedede;border-color:#dedede}.x-boundlist-floating{border-top-width:0}.x-boundlist-above{border-top-width:1px;border-bottom-width:1px}.x-form-radio{width:15px;height:15px;background:url(images/form/radio.png) no-repeat}.x-form-cb-checked .x-form-radio{background-position:0 -15px}.x-form-radio-focus{background-position:-15px 0}.x-form-cb-checked .x-form-radio-focus{background-position:-15px -15px}.x-datepicker{border-width:1px;border-style:solid;border-color:#e1e1e1;background-color:white;width:212px}.x-datepicker-header{padding:4px 6px;text-align:center;background-image:none;background-color:#f5f5f5}.x-datepicker-arrow{width:12px;height:12px;top:9px;cursor:pointer;background-color:#f5f5f5;filter:alpha(opacity=70);opacity:.7}a.x-datepicker-arrow:hover{filter:alpha(opacity=100);opacity:1}.x-datepicker-next{right:6px;background-image:url(images/datepicker/arrow-right.png)}.x-datepicker-prev{left:6px;background-image:url(images/datepicker/arrow-left.png)}.x-datepicker-month .x-btn,.x-datepicker-month .x-btn .x-btn-tc,.x-datepicker-month .x-btn .x-btn-tl,.x-datepicker-month .x-btn .x-btn-tr,.x-datepicker-month .x-btn .x-btn-mc,.x-datepicker-month .x-btn .x-btn-ml,.x-datepicker-month .x-btn .x-btn-mr,.x-datepicker-month .x-btn .x-btn-bc,.x-datepicker-month .x-btn .x-btn-bl,.x-datepicker-month .x-btn .x-btn-br{background:transparent;border-width:0!important}.x-datepicker-month .x-btn-inner{color:#606060}.x-datepicker-month .x-btn-split-right{background-image:url(images/datepicker/month-arrow.png);padding-right:8px}.x-datepicker-column-header{width:30px;color:#333;font:bold 13px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;text-align:right;background-image:none;background-color:white}.x-datepicker-column-header-inner{line-height:25px;padding:0 9px 0 0}.x-datepicker-cell{text-align:right;border-width:1px;border-style:solid;border-color:white}.x-datepicker-date{padding:0 7px 0 0;font:normal 13px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#333;cursor:pointer;line-height:23px}a.x-datepicker-date:hover{color:#333;background-color:#eee}.x-datepicker-selected{border-style:solid;border-color:#606060}.x-datepicker-selected .x-datepicker-date{background-color:#dedede;font-weight:bold}.x-datepicker-today{border-color:darkred;border-style:solid}.x-datepicker-prevday .x-datepicker-date,.x-datepicker-nextday .x-datepicker-date{color:#f2f2f2}.x-datepicker-disabled a.x-datepicker-date{background-color:#eee;cursor:default;color:#b3b3b3}.x-datepicker-disabled a.x-datepicker-date:hover{background-color:#eee}.x-datepicker-footer,.x-monthpicker-buttons{padding:3px 0;background-image:none;background-color:#f5f5f5;text-align:center}.x-datepicker-footer .x-btn,.x-monthpicker-buttons .x-btn{margin:0 3px 0 2px}.x-monthpicker{width:212px;border-width:1px;border-style:solid;border-color:#e1e1e1;background-color:white}.x-monthpicker-months{border-width:0 1px 0 0;border-color:#e1e1e1;border-style:solid;width:105px}.x-monthpicker-months .x-monthpicker-item{width:52px}.x-monthpicker-years{width:105px}.x-monthpicker-years .x-monthpicker-item{width:52px}.x-monthpicker-item{margin:5px 0 5px;font:normal 13px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;text-align:center}.x-monthpicker-item-inner{margin:0 5px 0 5px;color:#333;border-width:1px;border-style:solid;border-color:white;line-height:22px;cursor:pointer}a.x-monthpicker-item-inner:hover{background-color:#eee}.x-monthpicker-selected{background-color:#dedede;border-style:solid;border-color:#606060}.x-monthpicker-yearnav{height:34px}.x-monthpicker-yearnav-button-ct{width:52px}.x-monthpicker-yearnav-button{height:12px;width:12px;cursor:pointer;margin-top:11px;filter:alpha(opacity=70);opacity:.7;background-color:white}a.x-monthpicker-yearnav-button:hover{filter:alpha(opacity=100);opacity:1}.x-monthpicker-yearnav-next{background-image:url(images/datepicker/arrow-right.png);background-position:0 0}.x-monthpicker-yearnav-next-over{background-position:0 0}.x-monthpicker-yearnav-prev{background-image:url(images/datepicker/arrow-left.png);background-position:0 0}.x-monthpicker-yearnav-prev-over{background-position:0 0}.x-monthpicker-small .x-monthpicker-item{margin:2px 0 2px}.x-monthpicker-small .x-monthpicker-item-inner{margin:0 5px 0 5px}.x-monthpicker-small .x-monthpicker-yearnav{height:28px}.x-monthpicker-small .x-monthpicker-yearnav-button{margin-top:8px}.x-datepicker-header:after{display:none;content:"x-slicer:stretch:bottom"}.x-datepicker-footer:after{display:none;content:"x-slicer:stretch:bottom"}.x-form-date-trigger{background-image:url(images/form/date-trigger.png)}.x-form-file-wrap .x-form-text{color:gray}.x-color-picker{width:192px;height:120px;background-color:white;border-color:white;border-width:0;border-style:solid}.x-color-picker-item{width:24px;height:24px;border-width:1px;border-color:white;border-style:solid;background-color:white;cursor:pointer;padding:2px}.x-content-box .x-color-picker-item{width:18px;height:18px}a.x-color-picker-item:hover{border-color:#8bb8f3;background-color:#e6e6e6}.x-color-picker-selected{border-color:#8bb8f3;background-color:#e6e6e6}.x-color-picker-item-inner{line-height:16px;border-color:#e1e1e1;border-width:1px;border-style:solid}.x-html-editor-tb .x-btn-text{background:transparent no-repeat;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-bold,.x-menu-item div.x-edit-bold{background-position:0 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-italic,.x-menu-item div.x-edit-italic{background-position:-16px 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-underline,.x-menu-item div.x-edit-underline{background-position:-32px 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-forecolor,.x-menu-item div.x-edit-forecolor{background-position:-160px 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-backcolor,.x-menu-item div.x-edit-backcolor{background-position:-176px 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-justifyleft,.x-menu-item div.x-edit-justifyleft{background-position:-112px 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-justifycenter,.x-menu-item div.x-edit-justifycenter{background-position:-128px 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-justifyright,.x-menu-item div.x-edit-justifyright{background-position:-144px 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-insertorderedlist,.x-menu-item div.x-edit-insertorderedlist{background-position:-80px 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-insertunorderedlist,.x-menu-item div.x-edit-insertunorderedlist{background-position:-96px 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-increasefontsize,.x-menu-item div.x-edit-increasefontsize{background-position:-48px 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-decreasefontsize,.x-menu-item div.x-edit-decreasefontsize{background-position:-64px 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-sourceedit,.x-menu-item div.x-edit-sourceedit{background-position:-192px 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tb .x-edit-createlink,.x-menu-item div.x-edit-createlink{background-position:-208px 0;background-image:url(images/editor/tb-sprite.png)}.x-html-editor-tip .x-tip-bd .x-tip-bd-inner{padding:5px;padding-bottom:1px}.x-html-editor-tb .x-font-select{font-size:13px;font-family:inherit}.x-html-editor-wrap textarea{font:normal 13px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;background-color:white;resize:none}.x-grid-drop-indicator{position:absolute;height:1px;line-height:0;background-color:#77bc71;overflow:visible;pointer-events:none}.x-grid-drop-indicator .x-grid-drop-indicator-left{position:absolute;top:-8px;left:-12px;background-image:url(images/grid/dd-insert-arrow-right.png);height:16px;width:16px}.x-grid-drop-indicator .x-grid-drop-indicator-right{position:absolute;top:-8px;right:-11px;background-image:url(images/grid/dd-insert-arrow-left.png);height:16px;width:16px}.x-ie6 .x-grid-drop-indicator-left{background-image:url(images/grid/dd-insert-arrow-right.png)}.x-ie6 .x-grid-drop-indicator-right{background-image:url(images/grid/dd-insert-arrow-left.png)}.x-grid-cell-inner-action-col{padding:4px 4px 4px 4px}.x-action-col-cell .x-item-disabled{filter:alpha(opacity=30);opacity:.3}.x-action-col-icon{height:16px;width:16px;cursor:pointer}.x-grid-cell-inner-checkcolumn{padding:5px 10px 4px 10px}.x-grid-checkcolumn{width:15px;height:15px;background:url(images/form/checkbox.png) 0 0 no-repeat}.x-item-disabled .x-grid-checkcolumn{filter:alpha(opacity=30);opacity:.3}.x-grid-checkcolumn-checked{background-position:0 -15px}.x-grid-cell-inner-row-numberer{padding:5px 5px 4px 3px}.x-grid-group-hd{border-width:0 0 1px 0;border-style:solid;border-color:silver;padding:8px 4px 8px 4px;background:#f5f5f5;cursor:pointer}.x-grid-group-hd-not-collapsible{cursor:default}.x-grid-group-hd-collapsible .x-grid-group-title{background-repeat:no-repeat;background-position:left center;background-image:url(images/grid/group-collapse.png);padding:0 0 0 17px}.x-grid-group-title{color:#666;font:bold 13px/15px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-grid-group-hd-collapsed .x-grid-group-title{background-image:url(images/grid/group-expand.png)}.x-grid-group-collapsed .x-grid-group-title{background-image:url(images/grid/group-expand.png)}.x-group-by-icon{background-image:url(images/grid/group-by.png)}.x-show-groups-icon{background-image:url(images/grid/group-by.png)}.x-docked-summary{background:transparent!important}.x-docked-summary .x-grid-table{width:100%;border:0 none}.x-grid-row-summary .x-grid-cell,.x-grid-row-summary .x-grid-rowwrap,.x-grid-row-summary .x-grid-cell-rowbody{border-color:#ededed;background-color:transparent!important;border-top-width:0;font:normal 13px/15px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-docked-summary-bottom .x-grid-row-summary .x-grid-cell{border-bottom:0 none}.x-docked-summary-top .x-grid-row-summary .x-grid-cell{border-top:0 none}.x-grid-with-row-lines .x-grid-table-summary{border:0}.x-grid-locked .x-grid-inner-locked{border-width:0 1px 0 0;border-style:solid}.x-grid-locked-split .x-grid-inner-normal{border-width:0 0 0 1px;border-style:solid}.x-grid-inner-locked .x-column-header-last,.x-grid-inner-locked .x-grid-cell-last{border-right-width:0!important}.x-hmenu-lock{background-image:url(images/grid/hmenu-lock.png)}.x-hmenu-unlock{background-image:url(images/grid/hmenu-unlock.png)}.x-grid-editor .x-form-text{font:normal 13px/15px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;padding:4px 9px 3px 9px}.x-gecko .x-grid-editor .x-form-text{padding-left:8px;padding-right:8px}.x-grid-editor .x-form-display-field{height:24px}.x-grid-editor .x-form-display-field{font:normal 13px/15px "Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;padding:5px 10px 4px 10px;text-overflow:ellipsis}.x-grid-editor .x-form-action-col-field{padding:4px 4px 4px 4px}.x-tree-cell-editor .x-form-text{padding-left:3px;padding-right:3px}.x-gecko .x-tree-cell-editor .x-form-text{padding-left:2px;padding-right:2px}.x-grid-row-editor .x-field{margin:0 3px 0 2px}.x-grid-row-editor .x-form-display-field{padding:5px 7px 4px 8px}.x-grid-row-editor .x-form-action-col-field{padding:4px 1px 4px 2px}.x-grid-row-editor .x-form-text{padding:4px 6px 3px 7px}.x-gecko .x-grid-row-editor .x-form-text{padding-left:6px;padding-right:5px}.x-grid-row-editor .x-panel-body{border-top:1px solid #e1e1e1!important;border-bottom:1px solid #e1e1e1!important;padding:5px 0 5px 0;background-color:#e4e4e4}.x-grid-with-col-lines .x-grid-row-editor .x-form-cb{margin-right:1px}.x-grid-row-editor-buttons-default-bottom{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:5px;-webkit-border-bottom-right-radius:5px;border-bottom-right-radius:5px;-moz-border-radius-bottomleft:5px;-webkit-border-bottom-left-radius:5px;border-bottom-left-radius:5px;padding:5px 5px 5px 5px;border-width:0 1px 1px 1px;border-style:solid;background-color:#e4e4e4}.x-grid-row-editor-buttons-default-bottom-mc{background-color:#e4e4e4}.x-nbr .x-grid-row-editor-buttons-default-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-grid-row-editor-buttons-default-bottom-frameInfo{font-family:th-0-0-5-5-0-1-1-1-5-5-5-5}.x-grid-row-editor-buttons-default-bottom-tl{background-position:0 -10px}.x-grid-row-editor-buttons-default-bottom-tr{background-position:right -15px}.x-grid-row-editor-buttons-default-bottom-bl{background-position:0 -20px}.x-grid-row-editor-buttons-default-bottom-br{background-position:right -25px}.x-grid-row-editor-buttons-default-bottom-ml{background-position:0 top}.x-grid-row-editor-buttons-default-bottom-mr{background-position:right top}.x-grid-row-editor-buttons-default-bottom-tc{background-position:0 0}.x-grid-row-editor-buttons-default-bottom-bc{background-position:0 -5px}.x-grid-row-editor-buttons-default-bottom-tr,.x-grid-row-editor-buttons-default-bottom-br,.x-grid-row-editor-buttons-default-bottom-mr{padding-right:5px}.x-grid-row-editor-buttons-default-bottom-tl,.x-grid-row-editor-buttons-default-bottom-bl,.x-grid-row-editor-buttons-default-bottom-ml{padding-left:5px}.x-grid-row-editor-buttons-default-bottom-tc{height:0}.x-grid-row-editor-buttons-default-bottom-bc{height:5px}.x-grid-row-editor-buttons-default-bottom-tl,.x-grid-row-editor-buttons-default-bottom-bl,.x-grid-row-editor-buttons-default-bottom-tr,.x-grid-row-editor-buttons-default-bottom-br,.x-grid-row-editor-buttons-default-bottom-tc,.x-grid-row-editor-buttons-default-bottom-bc,.x-grid-row-editor-buttons-default-bottom-ml,.x-grid-row-editor-buttons-default-bottom-mr{zoom:1;background-image:url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-corners.gif)}.x-grid-row-editor-buttons-default-bottom-ml,.x-grid-row-editor-buttons-default-bottom-mr{zoom:1;background-image:url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-sides.gif);background-repeat:repeat-y}.x-grid-row-editor-buttons-default-bottom-mc{padding:5px 1px 1px 1px}.x-strict .x-ie7 .x-grid-row-editor-buttons-default-bottom-tl,.x-strict .x-ie7 .x-grid-row-editor-buttons-default-bottom-bl{position:relative;right:0}.x-grid-row-editor-buttons-default-bottom:after{display:none;content:"x-slicer:corners:url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-corners.gif), sides:url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-sides.gif)"}.x-grid-row-editor-buttons-default-top{-moz-border-radius-topleft:5px;-webkit-border-top-left-radius:5px;border-top-left-radius:5px;-moz-border-radius-topright:5px;-webkit-border-top-right-radius:5px;border-top-right-radius:5px;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:5px 5px 5px 5px;border-width:1px 1px 0 1px;border-style:solid;background-color:#e4e4e4}.x-grid-row-editor-buttons-default-top-mc{background-color:#e4e4e4}.x-nbr .x-grid-row-editor-buttons-default-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-grid-row-editor-buttons-default-top-frameInfo{font-family:th-5-5-0-0-1-1-0-1-5-5-5-5}.x-grid-row-editor-buttons-default-top-tl{background-position:0 -10px}.x-grid-row-editor-buttons-default-top-tr{background-position:right -15px}.x-grid-row-editor-buttons-default-top-bl{background-position:0 -20px}.x-grid-row-editor-buttons-default-top-br{background-position:right -25px}.x-grid-row-editor-buttons-default-top-ml{background-position:0 top}.x-grid-row-editor-buttons-default-top-mr{background-position:right top}.x-grid-row-editor-buttons-default-top-tc{background-position:0 0}.x-grid-row-editor-buttons-default-top-bc{background-position:0 -5px}.x-grid-row-editor-buttons-default-top-tr,.x-grid-row-editor-buttons-default-top-br,.x-grid-row-editor-buttons-default-top-mr{padding-right:5px}.x-grid-row-editor-buttons-default-top-tl,.x-grid-row-editor-buttons-default-top-bl,.x-grid-row-editor-buttons-default-top-ml{padding-left:5px}.x-grid-row-editor-buttons-default-top-tc{height:5px}.x-grid-row-editor-buttons-default-top-bc{height:0}.x-grid-row-editor-buttons-default-top-tl,.x-grid-row-editor-buttons-default-top-bl,.x-grid-row-editor-buttons-default-top-tr,.x-grid-row-editor-buttons-default-top-br,.x-grid-row-editor-buttons-default-top-tc,.x-grid-row-editor-buttons-default-top-bc,.x-grid-row-editor-buttons-default-top-ml,.x-grid-row-editor-buttons-default-top-mr{zoom:1;background-image:url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-corners.gif)}.x-grid-row-editor-buttons-default-top-ml,.x-grid-row-editor-buttons-default-top-mr{zoom:1;background-image:url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-sides.gif);background-repeat:repeat-y}.x-grid-row-editor-buttons-default-top-mc{padding:1px 1px 5px 1px}.x-strict .x-ie7 .x-grid-row-editor-buttons-default-top-tl,.x-strict .x-ie7 .x-grid-row-editor-buttons-default-top-bl{position:relative;right:0}.x-grid-row-editor-buttons-default-top:after{display:none;content:"x-slicer:corners:url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-corners.gif), sides:url(images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-sides.gif)"}.x-grid-row-editor-buttons{border-color:#e1e1e1}.x-row-editor-update-button{margin-right:3px}.x-row-editor-cancel-button{margin-left:2px}.x-grid-row-editor-errors .x-tip-body{padding:5px}.x-grid-row-editor-errors-item{list-style:disc;margin-left:15px}.x-accordion-layout-ct{background-color:white;padding:5px 5px 0}.x-accordion-hd .x-panel-header-text-container{color:#666;font-weight:bold;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;text-transform:none}.x-accordion-item{margin:0 0 5px}.x-accordion-item .x-accordion-hd{background:#e4e4e4;border-top-color:white;padding:8px 10px}.x-accordion-item .x-accordion-hd-sibling-expanded{border-top-color:#444}.x-accordion-item .x-accordion-hd-last-collapsed{border-bottom-color:#e4e4e4}.x-accordion-item .x-accordion-body{border-width:0}.x-accordion-hd .x-tool-collapse-top,.x-accordion-hd .x-tool-collapse-bottom{background-position:0 -272px}.x-accordion-hd .x-tool-expand-top,.x-accordion-hd .x-tool-expand-bottom{background-position:0 -256px}.x-accordion-hd .x-tool-img{background-image:url(images/tools/tool-sprites-dark.png);background-color:#e4e4e4}.x-collapse-el{cursor:pointer}.x-layout-split-left,.x-layout-split-right{top:50%;margin-top:-24px;width:8px;height:48px}.x-layout-split-top,.x-layout-split-bottom{left:50%;width:48px;height:8px;margin-left:-24px}.x-layout-split-left{background-image:url(images/util/splitter/mini-left.png)}.x-layout-split-right{background-image:url(images/util/splitter/mini-right.png)}.x-layout-split-top{background-image:url(images/util/splitter/mini-top.png)}.x-layout-split-bottom{background-image:url(images/util/splitter/mini-bottom.png)}.x-splitter-collapsed .x-layout-split-left{background-image:url(images/util/splitter/mini-right.png)}.x-splitter-collapsed .x-layout-split-right{background-image:url(images/util/splitter/mini-left.png)}.x-splitter-collapsed .x-layout-split-top{background-image:url(images/util/splitter/mini-bottom.png)}.x-splitter-collapsed .x-layout-split-bottom{background-image:url(images/util/splitter/mini-top.png)}.x-splitter-active{background-color:#b4b4b4;filter:alpha(opacity=80);opacity:.8}.x-splitter-active .x-collapse-el{filter:alpha(opacity=30);opacity:.3}.x-border-layout-ct{background-color:#606060}.x-tool{cursor:pointer}.x-tool-img{overflow:hidden;width:16px;height:16px;background-image:url(images/tools/tool-sprites.png);margin:0}.x-tool .x-tool-img{filter:alpha(opacity=50);opacity:.5}.x-tool-over .x-tool-img{filter:alpha(opacity=60);opacity:.6}.x-tool-pressed .x-tool-img{filter:alpha(opacity=70);opacity:.7}.x-tool-placeholder{visibility:hidden}.x-tool-close{background-position:0 0}.x-tool-minimize{background-position:0 -16px}.x-tool-maximize{background-position:0 -32px}.x-tool-restore{background-position:0 -48px}.x-tool-toggle{background-position:0 -64px}.x-panel-collapsed .x-tool-toggle{background-position:0 -80px}.x-tool-gear{background-position:0 -96px}.x-tool-prev{background-position:0 -112px}.x-tool-next{background-position:0 -128px}.x-tool-pin{background-position:0 -144px}.x-tool-unpin{background-position:0 -160px}.x-tool-right{background-position:0 -176px}.x-tool-left{background-position:0 -192px}.x-tool-down{background-position:0 -208px}.x-tool-up{background-position:0 -224px}.x-tool-refresh{background-position:0 -240px}.x-tool-plus{background-position:0 -256px}.x-tool-minus{background-position:0 -272px}.x-tool-search{background-position:0 -288px}.x-tool-save{background-position:0 -304px}.x-tool-help{background-position:0 -320px}.x-tool-print{background-position:0 -336px}.x-tool-expand{background-position:0 -352px}.x-tool-collapse{background-position:0 -368px}.x-tool-resize{background-position:0 -384px}.x-tool-move{background-position:0 -400px}.x-tool-expand-bottom,.x-tool-collapse-bottom{background-position:0 -208px}.x-tool-expand-top,.x-tool-collapse-top{background-position:0 -224px}.x-tool-expand-left,.x-tool-collapse-left{background-position:0 -192px}.x-tool-expand-right,.x-tool-collapse-right{background-position:0 -176px}.x-resizable-handle{position:absolute;z-index:100;font-size:1px;line-height:2px;overflow:hidden;zoom:1;filter:alpha(opacity=0);opacity:0;background-color:#fff;-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px}.x-collapsed .x-resizable-handle{display:none}.x-resizable-handle-north{cursor:n-resize}.x-resizable-handle-south{cursor:s-resize}.x-resizable-handle-east{cursor:e-resize}.x-resizable-handle-west{cursor:w-resize}.x-resizable-handle-southeast{cursor:se-resize}.x-resizable-handle-northwest{cursor:nw-resize}.x-resizable-handle-northeast{cursor:ne-resize}.x-resizable-handle-southwest{cursor:sw-resize}.x-resizable-handle-east{width:2px;height:100%;right:0;top:0}.x-resizable-handle-south{width:100%;height:2px;left:0;bottom:0}.x-resizable-handle-west{width:2px;height:100%;left:0;top:0}.x-resizable-handle-north{width:100%;height:2px;left:0;top:0}.x-resizable-handle-southeast{width:2px;height:2px;right:0;bottom:0;z-index:101}.x-resizable-handle-northwest{width:2px;height:2px;left:0;top:0;z-index:101}.x-resizable-handle-northeast{width:2px;height:2px;right:0;top:0;z-index:101}.x-resizable-handle-southwest{width:2px;height:2px;left:0;bottom:0;z-index:101}.x-window .x-window-handle{filter:alpha(opacity=0);opacity:0}.x-window-collapsed .x-window-handle{display:none}.x-resizable-proxy{border:1px dashed #3b5a82;position:absolute;overflow:hidden;z-index:50000}.x-resizable-handle-over,.x-resizable-pinned .x-resizable-handle{filter:alpha(opacity=100);opacity:1}.x-resizable-handle-east-over,.x-resizable-handle-west-over{background-image:url(images/sizer/e-handle.png)}.x-resizable-handle-south-over,.x-resizable-handle-north-over{background-image:url(images/sizer/s-handle.png)}.x-resizable-handle-southeast-over{background-position:top left;background-image:url(images/sizer/se-handle.png)}.x-resizable-handle-northwest-over{background-position:bottom right;background-image:url(images/sizer/nw-handle.png)}.x-resizable-handle-northeast-over{background-position:bottom left;background-image:url(images/sizer/ne-handle.png)}.x-resizable-handle-southwest-over{background-position:top right;background-image:url(images/sizer/sw-handle.png)}.x-resizable-pinned .x-resizable-handle-east,.x-resizable-pinned .x-resizable-handle-west{background-image:url(images/sizer/e-handle.png)}.x-resizable-pinned .x-resizable-handle-south,.x-resizable-pinned .x-resizable-handle-north{background-image:url(images/sizer/s-handle.png)}.x-resizable-pinned .x-resizable-handle-southeast{background-position:top left;background-image:url(images/sizer/se-handle.png)}.x-resizable-pinned .x-resizable-handle-northwest{background-position:bottom right;background-image:url(images/sizer/nw-handle.png)}.x-resizable-pinned .x-resizable-handle-northeast{background-position:bottom left;background-image:url(images/sizer/ne-handle.png)}.x-resizable-pinned .x-resizable-handle-southwest{background-position:top right;background-image:url(images/sizer/sw-handle.png)}.x-tab-default-top{-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:8px 12px 7px 12px;border-width:0;border-style:solid;background-color:#6f6f6f}.x-tab-default-top-mc{background-color:#6f6f6f}.x-nbr .x-tab-default-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-tab-default-top-frameInfo{font-family:th-3-3-0-0-0-0-0-0-8-12-7-12}.x-tab-default-top-tl{background-position:0 -6px}.x-tab-default-top-tr{background-position:right -9px}.x-tab-default-top-bl{background-position:0 -12px}.x-tab-default-top-br{background-position:right -15px}.x-tab-default-top-ml{background-position:0 top}.x-tab-default-top-mr{background-position:right top}.x-tab-default-top-tc{background-position:0 0}.x-tab-default-top-bc{background-position:0 -3px}.x-tab-default-top-tr,.x-tab-default-top-br,.x-tab-default-top-mr{padding-right:3px}.x-tab-default-top-tl,.x-tab-default-top-bl,.x-tab-default-top-ml{padding-left:3px}.x-tab-default-top-tc{height:3px}.x-tab-default-top-bc{height:0}.x-tab-default-top-tl,.x-tab-default-top-bl,.x-tab-default-top-tr,.x-tab-default-top-br,.x-tab-default-top-tc,.x-tab-default-top-bc,.x-tab-default-top-ml,.x-tab-default-top-mr{zoom:1;background-image:url(images/tab/tab-default-top-corners.gif)}.x-tab-default-top-ml,.x-tab-default-top-mr{zoom:1;background-image:url(images/tab/tab-default-top-sides.gif);background-repeat:repeat-y}.x-tab-default-top-mc{padding:5px 9px 7px 9px}.x-strict .x-ie7 .x-tab-default-top-tl,.x-strict .x-ie7 .x-tab-default-top-bl{position:relative;right:0}.x-tab-default-top:after{display:none;content:"x-slicer:corners:url(images/tab/tab-default-top-corners.gif), sides:url(images/tab/tab-default-top-sides.gif)"}.x-tab-default-bottom{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-bottomleft:3px;-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;padding:8px 12px 7px 12px;border-width:0;border-style:solid;background-color:#6f6f6f}.x-tab-default-bottom-mc{background-color:#6f6f6f}.x-nbr .x-tab-default-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-tab-default-bottom-frameInfo{font-family:th-0-0-3-3-0-0-0-0-8-12-7-12}.x-tab-default-bottom-tl{background-position:0 -6px}.x-tab-default-bottom-tr{background-position:right -9px}.x-tab-default-bottom-bl{background-position:0 -12px}.x-tab-default-bottom-br{background-position:right -15px}.x-tab-default-bottom-ml{background-position:0 top}.x-tab-default-bottom-mr{background-position:right top}.x-tab-default-bottom-tc{background-position:0 0}.x-tab-default-bottom-bc{background-position:0 -3px}.x-tab-default-bottom-tr,.x-tab-default-bottom-br,.x-tab-default-bottom-mr{padding-right:3px}.x-tab-default-bottom-tl,.x-tab-default-bottom-bl,.x-tab-default-bottom-ml{padding-left:3px}.x-tab-default-bottom-tc{height:0}.x-tab-default-bottom-bc{height:3px}.x-tab-default-bottom-tl,.x-tab-default-bottom-bl,.x-tab-default-bottom-tr,.x-tab-default-bottom-br,.x-tab-default-bottom-tc,.x-tab-default-bottom-bc,.x-tab-default-bottom-ml,.x-tab-default-bottom-mr{zoom:1;background-image:url(images/tab/tab-default-bottom-corners.gif)}.x-tab-default-bottom-ml,.x-tab-default-bottom-mr{zoom:1;background-image:url(images/tab/tab-default-bottom-sides.gif);background-repeat:repeat-y}.x-tab-default-bottom-mc{padding:8px 9px 4px 9px}.x-strict .x-ie7 .x-tab-default-bottom-tl,.x-strict .x-ie7 .x-tab-default-bottom-bl{position:relative;right:0}.x-tab-default-bottom:after{display:none;content:"x-slicer:corners:url(images/tab/tab-default-bottom-corners.gif), sides:url(images/tab/tab-default-bottom-sides.gif)"}.x-tab-default-left{-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:8px 12px 7px 12px;border-width:0;border-style:solid;background-color:#6f6f6f}.x-tab-default-left-mc{background-color:#6f6f6f}.x-nbr .x-tab-default-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-tab-default-left-frameInfo{font-family:th-3-3-0-0-0-0-0-0-8-12-7-12}.x-tab-default-left-tl{background-position:0 -6px}.x-tab-default-left-tr{background-position:right -9px}.x-tab-default-left-bl{background-position:0 -12px}.x-tab-default-left-br{background-position:right -15px}.x-tab-default-left-ml{background-position:0 top}.x-tab-default-left-mr{background-position:right top}.x-tab-default-left-tc{background-position:0 0}.x-tab-default-left-bc{background-position:0 -3px}.x-tab-default-left-tr,.x-tab-default-left-br,.x-tab-default-left-mr{padding-right:3px}.x-tab-default-left-tl,.x-tab-default-left-bl,.x-tab-default-left-ml{padding-left:3px}.x-tab-default-left-tc{height:3px}.x-tab-default-left-bc{height:0}.x-tab-default-left-tl,.x-tab-default-left-bl,.x-tab-default-left-tr,.x-tab-default-left-br,.x-tab-default-left-tc,.x-tab-default-left-bc,.x-tab-default-left-ml,.x-tab-default-left-mr{zoom:1;background-image:url(images/tab/tab-default-top-corners.gif)}.x-tab-default-left-ml,.x-tab-default-left-mr{zoom:1;background-image:url(images/tab/tab-default-top-sides.gif);background-repeat:repeat-y}.x-tab-default-left-mc{padding:5px 9px 7px 9px}.x-strict .x-ie7 .x-tab-default-left-tl,.x-strict .x-ie7 .x-tab-default-left-bl{position:relative;right:0}.x-tab-default-left:after{display:none;content:"x-slicer:corners:url(images/tab/tab-default-top-corners.gif), sides:url(images/tab/tab-default-top-sides.gif)"}.x-tab-default-right{-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:8px 12px 7px 12px;border-width:0;border-style:solid;background-color:#6f6f6f}.x-tab-default-right-mc{background-color:#6f6f6f}.x-nbr .x-tab-default-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-tab-default-right-frameInfo{font-family:th-3-3-0-0-0-0-0-0-8-12-7-12}.x-tab-default-right-tl{background-position:0 -6px}.x-tab-default-right-tr{background-position:right -9px}.x-tab-default-right-bl{background-position:0 -12px}.x-tab-default-right-br{background-position:right -15px}.x-tab-default-right-ml{background-position:0 top}.x-tab-default-right-mr{background-position:right top}.x-tab-default-right-tc{background-position:0 0}.x-tab-default-right-bc{background-position:0 -3px}.x-tab-default-right-tr,.x-tab-default-right-br,.x-tab-default-right-mr{padding-right:3px}.x-tab-default-right-tl,.x-tab-default-right-bl,.x-tab-default-right-ml{padding-left:3px}.x-tab-default-right-tc{height:3px}.x-tab-default-right-bc{height:0}.x-tab-default-right-tl,.x-tab-default-right-bl,.x-tab-default-right-tr,.x-tab-default-right-br,.x-tab-default-right-tc,.x-tab-default-right-bc,.x-tab-default-right-ml,.x-tab-default-right-mr{zoom:1;background-image:url(images/tab/tab-default-top-corners.gif)}.x-tab-default-right-ml,.x-tab-default-right-mr{zoom:1;background-image:url(images/tab/tab-default-top-sides.gif);background-repeat:repeat-y}.x-tab-default-right-mc{padding:5px 9px 7px 9px}.x-strict .x-ie7 .x-tab-default-right-tl,.x-strict .x-ie7 .x-tab-default-right-bl{position:relative;right:0}.x-tab-default-right:after{display:none;content:"x-slicer:corners:url(images/tab/tab-default-top-corners.gif), sides:url(images/tab/tab-default-top-sides.gif)"}.x-tab-default{border-color:#444;margin:0 1px 0 0;cursor:pointer}.x-tab-default .x-tab-inner{font-size:13px;font-weight:bold;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:white;line-height:16px}.x-tab-default .x-tab-icon-el{width:16px;height:16px;line-height:16px;background-position:center center}.x-tab-default .x-tab-glyph{font-size:16px;color:white;opacity:.5}.x-ie8m .x-tab-default .x-tab-glyph{color:#b7b7b7}.x-strict .x-ie9 .x-tab-bar-vertical .x-tab-default{padding-left:0}.x-strict .x-ie9 .x-tab-bar-vertical .x-tab-default .x-tab-button{padding-left:12px}.x-strict .x-ie9 .x-tab-bar-vertical .x-tab-default .x-tab-icon-el{left:12px}.x-tab-default-icon .x-tab-inner{width:16px}.x-tab-default-left{margin:0 0 0 1px}.x-tab-default-top,.x-tab-default-left,.x-tab-default-right{border-bottom:0 solid #444}.x-tab-default-bottom{border-top:0 solid #444}.x-tab-default-left{-webkit-transform:rotate(270deg);-webkit-transform-origin:100% 0;-moz-transform:rotate(270deg);-moz-transform-origin:100% 0;-o-transform:rotate(270deg);-o-transform-origin:100% 0;transform:rotate(270deg);transform-origin:100% 0}.x-ie9m .x-tab-default-left{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3)}.x-tab-default-right{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-tab-default-right{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1)}.x-tab-default-icon-text-left .x-tab-inner{padding-left:22px}.x-tab-default-over{background-color:#7f7f7f}.x-tab-default-over .x-tab-glyph{color:white}.x-ie8m .x-tab-default-over .x-tab-glyph{color:#bfbfbf}.x-tab-default-active{background-color:#bdbdbd}.x-tab-default-active .x-tab-inner{color:#444}.x-tab-default-active .x-tab-glyph{color:#444}.x-ie8m .x-tab-default-active .x-tab-glyph{color:gray}.x-tab-default-top-active,.x-tab-default-left-active,.x-tab-default-right-active{border-bottom:0 solid #bdbdbd}.x-tab-default-bottom-active{border-top:0 solid #bdbdbd}.x-tab-default-disabled{cursor:default}.x-tab-default-disabled .x-tab-inner{filter:alpha(opacity=30);opacity:.3}.x-tab-default-disabled .x-tab-icon-el{filter:alpha(opacity=50);opacity:.5}.x-tab-default-disabled .x-tab-glyph{color:white;opacity:.3;filter:none}.x-ie8m .x-tab-default-disabled .x-tab-glyph{color:#9a9a9a}.x-tab-default-top-disabled,.x-tab-default-left-disabled,.x-tab-default-right-disabled{border-color:#444 #444 #444}.x-tab-default-bottom-disabled{border-color:#444 #444 #444 #444}.x-nbr .x-tab-default{background-image:none}.x-tab-default-top-over .x-frame-tl,.x-tab-default-top-over .x-frame-bl,.x-tab-default-top-over .x-frame-tr,.x-tab-default-top-over .x-frame-br,.x-tab-default-top-over .x-frame-tc,.x-tab-default-top-over .x-frame-bc,.x-tab-default-left-over .x-frame-tl,.x-tab-default-left-over .x-frame-bl,.x-tab-default-left-over .x-frame-tr,.x-tab-default-left-over .x-frame-br,.x-tab-default-left-over .x-frame-tc,.x-tab-default-left-over .x-frame-bc,.x-tab-default-right-over .x-frame-tl,.x-tab-default-right-over .x-frame-bl,.x-tab-default-right-over .x-frame-tr,.x-tab-default-right-over .x-frame-br,.x-tab-default-right-over .x-frame-tc,.x-tab-default-right-over .x-frame-bc{background-image:url(images/tab/tab-default-top-over-corners.gif)}.x-tab-default-top-over .x-frame-ml,.x-tab-default-top-over .x-frame-mr,.x-tab-default-left-over .x-frame-ml,.x-tab-default-left-over .x-frame-mr,.x-tab-default-right-over .x-frame-ml,.x-tab-default-right-over .x-frame-mr{background-image:url(images/tab/tab-default-top-over-sides.gif)}.x-tab-default-top-over .x-frame-mc,.x-tab-default-left-over .x-frame-mc,.x-tab-default-right-over .x-frame-mc{background-color:#7f7f7f}.x-tab-default-bottom-over .x-frame-tl,.x-tab-default-bottom-over .x-frame-bl,.x-tab-default-bottom-over .x-frame-tr,.x-tab-default-bottom-over .x-frame-br,.x-tab-default-bottom-over .x-frame-tc,.x-tab-default-bottom-over .x-frame-bc{background-image:url(images/tab/tab-default-bottom-over-corners.gif)}.x-tab-default-bottom-over .x-frame-ml,.x-tab-default-bottom-over .x-frame-mr{background-image:url(images/tab/tab-default-bottom-over-sides.gif)}.x-tab-default-bottom-over .x-frame-mc{background-color:#7f7f7f}.x-tab-default-top-active .x-frame-tl,.x-tab-default-top-active .x-frame-bl,.x-tab-default-top-active .x-frame-tr,.x-tab-default-top-active .x-frame-br,.x-tab-default-top-active .x-frame-tc,.x-tab-default-top-active .x-frame-bc,.x-tab-default-left-active .x-frame-tl,.x-tab-default-left-active .x-frame-bl,.x-tab-default-left-active .x-frame-tr,.x-tab-default-left-active .x-frame-br,.x-tab-default-left-active .x-frame-tc,.x-tab-default-left-active .x-frame-bc,.x-tab-default-right-active .x-frame-tl,.x-tab-default-right-active .x-frame-bl,.x-tab-default-right-active .x-frame-tr,.x-tab-default-right-active .x-frame-br,.x-tab-default-right-active .x-frame-tc,.x-tab-default-right-active .x-frame-bc{background-image:url(images/tab/tab-default-top-active-corners.gif)}.x-tab-default-top-active .x-frame-ml,.x-tab-default-top-active .x-frame-mr,.x-tab-default-left-active .x-frame-ml,.x-tab-default-left-active .x-frame-mr,.x-tab-default-right-active .x-frame-ml,.x-tab-default-right-active .x-frame-mr{background-image:url(images/tab/tab-default-top-active-sides.gif)}.x-tab-default-top-active .x-frame-mc,.x-tab-default-left-active .x-frame-mc,.x-tab-default-right-active .x-frame-mc{background-color:#bdbdbd}.x-tab-default-bottom-active .x-frame-tl,.x-tab-default-bottom-active .x-frame-bl,.x-tab-default-bottom-active .x-frame-tr,.x-tab-default-bottom-active .x-frame-br,.x-tab-default-bottom-active .x-frame-tc,.x-tab-default-bottom-active .x-frame-bc{background-image:url(images/tab/tab-default-bottom-active-corners.gif)}.x-tab-default-bottom-active .x-frame-ml,.x-tab-default-bottom-active .x-frame-mr{background-image:url(images/tab/tab-default-bottom-active-sides.gif)}.x-tab-default-bottom-active .x-frame-mc{background-color:#bdbdbd}.x-tab-default-top-disabled .x-frame-tl,.x-tab-default-top-disabled .x-frame-bl,.x-tab-default-top-disabled .x-frame-tr,.x-tab-default-top-disabled .x-frame-br,.x-tab-default-top-disabled .x-frame-tc,.x-tab-default-top-disabled .x-frame-bc,.x-tab-default-left-disabled .x-frame-tl,.x-tab-default-left-disabled .x-frame-bl,.x-tab-default-left-disabled .x-frame-tr,.x-tab-default-left-disabled .x-frame-br,.x-tab-default-left-disabled .x-frame-tc,.x-tab-default-left-disabled .x-frame-bc,.x-tab-default-right-disabled .x-frame-tl,.x-tab-default-right-disabled .x-frame-bl,.x-tab-default-right-disabled .x-frame-tr,.x-tab-default-right-disabled .x-frame-br,.x-tab-default-right-disabled .x-frame-tc,.x-tab-default-right-disabled .x-frame-bc{background-image:url(images/tab/tab-default-top-disabled-corners.gif)}.x-tab-default-top-disabled .x-frame-ml,.x-tab-default-top-disabled .x-frame-mr,.x-tab-default-left-disabled .x-frame-ml,.x-tab-default-left-disabled .x-frame-mr,.x-tab-default-right-disabled .x-frame-ml,.x-tab-default-right-disabled .x-frame-mr{background-image:url(images/tab/tab-default-top-disabled-sides.gif)}.x-tab-default-top-disabled .x-frame-mc,.x-tab-default-left-disabled .x-frame-mc,.x-tab-default-right-disabled .x-frame-mc{background-color:#6f6f6f}.x-tab-default-bottom-disabled .x-frame-tl,.x-tab-default-bottom-disabled .x-frame-bl,.x-tab-default-bottom-disabled .x-frame-tr,.x-tab-default-bottom-disabled .x-frame-br,.x-tab-default-bottom-disabled .x-frame-tc,.x-tab-default-bottom-disabled .x-frame-bc{background-image:url(images/tab/tab-default-bottom-disabled-corners.gif)}.x-tab-default-bottom-disabled .x-frame-ml,.x-tab-default-bottom-disabled .x-frame-mr{background-image:url(images/tab/tab-default-bottom-disabled-sides.gif)}.x-tab-default-bottom-disabled .x-frame-mc{background-color:#6f6f6f}.x-tab-default .x-tab-close-btn{width:12px;height:12px;background-image:url(images/tab/tab-default-close.png)}.x-tab-default .x-tab-close-btn-over{background-position:-12px 0}.x-tab-default .x-tab-close-btn{top:2px;right:2px}.x-tab-default-disabled .x-tab-close-btn{filter:alpha(opacity=30);opacity:.3;background-position:0 0}.x-tab-default-pressed .x-tab-close-btn{background-position:-24px 0}.x-tab-default-closable .x-tab-wrap{padding-right:15px}.x-tab-default-top-over:after{display:none;content:"x-slicer:corners:url(images/tab/tab-default-top-over-corners.gif), sides:url(images/tab/tab-default-top-over-sides.gif), stretch:bottom"}.x-tab-default-bottom-over:after{display:none;content:"x-slicer:corners:url(images/tab/tab-default-bottom-over-corners.gif), sides:url(images/tab/tab-default-bottom-over-sides.gif), stretch:top"}.x-tab-default-top-active:after{display:none;content:"x-slicer:corners:url(images/tab/tab-default-top-active-corners.gif), sides:url(images/tab/tab-default-top-active-sides.gif), stretch:bottom"}.x-tab-default-bottom-active:after{display:none;content:"x-slicer:corners:url(images/tab/tab-default-bottom-active-corners.gif), sides:url(images/tab/tab-default-bottom-active-sides.gif), stretch:top"}.x-tab-default-top-disabled:after{display:none;content:"x-slicer:corners:url(images/tab/tab-default-top-disabled-corners.gif), sides:url(images/tab/tab-default-top-disabled-sides.gif), stretch:bottom"}.x-tab-default-bottom-disabled:after{display:none;content:"x-slicer:corners:url(images/tab/tab-default-bottom-disabled-corners.gif), sides:url(images/tab/tab-default-bottom-disabled-sides.gif), stretch:top"}.x-tab-bar-default-top{padding:0}.x-tab-bar-default-bottom{padding:0}.x-tab-bar-default-left{padding:0}.x-tab-bar-default-right{padding:0}.x-tab-bar-default-horizontal{height:36px}.x-content-box .x-tab-bar-default-horizontal{height:36px}.x-tab-bar-default-vertical{width:36px}.x-content-box .x-tab-bar-default-vertical{width:36px}.x-tab-bar-body-default-top{padding-bottom:5px}.x-tab-bar-body-default-bottom{padding-top:5px}.x-tab-bar-body-default-left{padding-right:5px}.x-tab-bar-body-default-right{padding-left:5px}.x-tab-bar-strip-default{border-style:solid;border-color:#444;background-color:#bdbdbd}.x-content-box .x-tab-bar-strip-default-horizontal{height:5px}.x-content-box .x-tab-bar-strip-default-vertical{width:5px}.x-tab-bar-strip-default-top{border-width:0;height:5px}.x-tab-bar-plain .x-tab-bar-strip-default-top{border-width:0}.x-tab-bar-strip-default-bottom{border-width:0;height:5px}.x-tab-bar-plain .x-tab-bar-strip-default-bottom{border-width:0}.x-tab-bar-strip-default-left{border-width:0;width:5px}.x-tab-bar-plain .x-tab-bar-strip-default-left{border-width:0}.x-tab-bar-strip-default-right{border-width:0;width:5px}.x-tab-bar-plain .x-tab-bar-strip-default-right{border-width:0}.x-tab-bar-default{background-color:#444}.x-tab-bar-default .x-box-scroller{cursor:pointer;filter:alpha(opacity=50);opacity:.5;background-color:#444}.x-tab-bar-default .x-box-scroller-plain .x-box-scroller{background-color:transparent}.x-ie8m .x-tab-bar-default .x-box-scroller-plain .x-box-scroller{background-color:#fff}.x-tab-bar-default .x-box-scroller-hover{filter:alpha(opacity=60);opacity:.6}.x-tab-bar-default .x-box-scroller-pressed{filter:alpha(opacity=70);opacity:.7}.x-tab-bar-default .x-tabbar-scroll-left,.x-tab-bar-default .x-tabbar-scroll-right{height:31px;width:24px}.x-tab-bar-default .x-tabbar-scroll-top,.x-tab-bar-default .x-tabbar-scroll-bottom{width:31px;height:24px}.x-tab-bar-default-bottom .x-box-scroller{margin-top:0}.x-tab-bar-default-right .x-box-scroller{margin-left:0}.x-tab-bar-default .x-tabbar-scroll-left{background-image:url(images/tab-bar/default-scroll-left.png)}.x-tab-bar-default .x-tabbar-scroll-right{background-image:url(images/tab-bar/default-scroll-right.png)}.x-tab-bar-default .x-tabbar-scroll-top{background-image:url(images/tab-bar/default-scroll-top.png)}.x-tab-bar-default .x-tabbar-scroll-bottom{background-image:url(images/tab-bar/default-scroll-bottom.png)}.x-tab-bar-default .x-box-scroller-plain .x-tabbar-scroll-left{background-image:url(images/tab-bar/default-plain-scroll-left.png)}.x-tab-bar-default .x-box-scroller-plain .x-tabbar-scroll-right{background-image:url(images/tab-bar/default-plain-scroll-right.png)}.x-tab-bar-default .x-box-scroller-plain .x-tabbar-scroll-top{background-image:url(images/tab-bar/default-plain-scroll-top.png)}.x-tab-bar-default .x-box-scroller-plain .x-tabbar-scroll-bottom{background-image:url(images/tab-bar/default-plain-scroll-bottom.png)}.x-tab-bar-default .x-box-scroller-disabled{filter:alpha(opacity=25);opacity:.25;cursor:default}.x-tab-bar-default-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-tab-bar-default-bottom:after{display:none;content:"x-slicer:stretch:top"}.x-tab-bar-default-left:after{display:none;content:"x-slicer:stretch:right"}.x-tab-bar-default-right:after{display:none;content:"x-slicer:stretch:left"}.x-tab-bar-plain{border-width:0;padding:0;height:36px}.x-column-header-checkbox{border-color:#f5f5f5}.x-grid-row-checker,.x-column-header-checkbox .x-column-header-text{height:15px;width:15px;background-image:url(images/form/checkbox.png);line-height:15px}.x-column-header-checkbox .x-column-header-inner{padding:7px 4px 7px 4px}.x-grid-cell-row-checker .x-grid-cell-inner{padding:5px 4px 4px 4px}.x-grid-hd-checker-on .x-column-header-text,.x-grid-row-selected .x-grid-row-checker,.x-grid-row-checked .x-grid-row-checker{background-position:0 -15px}.x-tree-drop-ok-append .x-dd-drop-icon{background-image:url(images/tree/drop-append.png)}.x-tree-drop-ok-above .x-dd-drop-icon{background-image:url(images/tree/drop-above.png)}.x-tree-drop-ok-below .x-dd-drop-icon{background-image:url(images/tree/drop-below.png)}.x-tree-drop-ok-between .x-dd-drop-icon{background-image:url(images/tree/drop-between.png)}.x-tree-ddindicator{height:1px;border-width:1px 0 0;border-style:dotted;border-color:green}.x-btn-plain-toolbar-small{border-color:transparent}.x-btn-plain-toolbar-small{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:transparent}.x-btn-plain-toolbar-small-mc{background-image:url(images/btn/btn-plain-toolbar-small-fbg.gif);background-position:0 top;background-color:transparent}.x-nlg .x-btn-plain-toolbar-small{background-image:url(images/btn/btn-plain-toolbar-small-bg.gif);background-position:0 top}.x-nbr .x-btn-plain-toolbar-small{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-plain-toolbar-small-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-plain-toolbar-small-tl{background-position:0 -6px}.x-btn-plain-toolbar-small-tr{background-position:right -9px}.x-btn-plain-toolbar-small-bl{background-position:0 -12px}.x-btn-plain-toolbar-small-br{background-position:right -15px}.x-btn-plain-toolbar-small-ml{background-position:0 top}.x-btn-plain-toolbar-small-mr{background-position:right top}.x-btn-plain-toolbar-small-tc{background-position:0 0}.x-btn-plain-toolbar-small-bc{background-position:0 -3px}.x-btn-plain-toolbar-small-tr,.x-btn-plain-toolbar-small-br,.x-btn-plain-toolbar-small-mr{padding-right:3px}.x-btn-plain-toolbar-small-tl,.x-btn-plain-toolbar-small-bl,.x-btn-plain-toolbar-small-ml{padding-left:3px}.x-btn-plain-toolbar-small-tc{height:3px}.x-btn-plain-toolbar-small-bc{height:3px}.x-btn-plain-toolbar-small-tl,.x-btn-plain-toolbar-small-bl,.x-btn-plain-toolbar-small-tr,.x-btn-plain-toolbar-small-br,.x-btn-plain-toolbar-small-tc,.x-btn-plain-toolbar-small-bc,.x-btn-plain-toolbar-small-ml,.x-btn-plain-toolbar-small-mr{zoom:1}.x-btn-plain-toolbar-small-ml,.x-btn-plain-toolbar-small-mr{zoom:1}.x-btn-plain-toolbar-small-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-plain-toolbar-small-tl,.x-strict .x-ie7 .x-btn-plain-toolbar-small-bl{position:relative;right:0}.x-btn-plain-toolbar-small:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-plain-toolbar-small-fbg.gif), bg:url(images/btn/btn-plain-toolbar-small-bg.gif)"}.x-btn-plain-toolbar-small .x-btn-inner{font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#333;padding:0 5px}.x-btn-plain-toolbar-small .x-btn-arrow{background-image:url(images/button/plain-toolbar-small-arrow.png)}.x-btn-plain-toolbar-small .x-btn-arrow-right{padding-right:21px}.x-btn-plain-toolbar-small .x-btn-arrow-bottom{padding-bottom:18px}.x-btn-plain-toolbar-small .x-btn-glyph{font-size:16px;line-height:16px;color:#333}.x-ie8m .x-btn-plain-toolbar-small .x-btn-glyph{color:#333}.x-btn-plain-toolbar-small-disabled{background-image:none;background-color:transparent}.x-btn-plain-toolbar-small-icon .x-btn-button,.x-btn-plain-toolbar-small-noicon .x-btn-button{height:16px}.x-btn-plain-toolbar-small-icon .x-btn-inner,.x-btn-plain-toolbar-small-noicon .x-btn-inner{line-height:16px}.x-btn-plain-toolbar-small-icon .x-btn-arrow-right .x-btn-inner,.x-btn-plain-toolbar-small-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-plain-toolbar-small-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-plain-toolbar-small-icon .x-btn-inner{width:16px;padding:0}.x-btn-plain-toolbar-small-icon .x-btn-icon-el{width:16px;height:16px}.x-btn-plain-toolbar-small-icon-text-left .x-btn-button{height:16px}.x-btn-plain-toolbar-small-icon-text-left .x-btn-inner{line-height:16px;padding-left:21px}.x-btn-plain-toolbar-small-icon-text-left .x-btn-icon-el{width:16px;right:auto}.x-ie6 .x-btn-plain-toolbar-small-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-plain-toolbar-small-icon-text-left .x-btn-icon-el{height:16px}.x-btn-plain-toolbar-small-icon-text-right .x-btn-button{height:16px}.x-btn-plain-toolbar-small-icon-text-right .x-btn-inner{line-height:16px;padding-right:21px}.x-btn-plain-toolbar-small-icon-text-right .x-btn-icon-el{width:16px;left:auto}.x-ie6 .x-btn-plain-toolbar-small-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-plain-toolbar-small-icon-text-right .x-btn-icon-el{height:16px}.x-btn-plain-toolbar-small-icon-text-top .x-btn-inner{padding-top:21px}.x-btn-plain-toolbar-small-icon-text-top .x-btn-icon-el{height:16px;bottom:auto}.x-ie6 .x-btn-plain-toolbar-small-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-plain-toolbar-small-icon-text-top .x-btn-icon-el{width:100%}.x-btn-plain-toolbar-small-icon-text-bottom .x-btn-inner{padding-bottom:21px}.x-btn-plain-toolbar-small-icon-text-bottom .x-btn-icon-el{height:16px;top:auto}.x-ie6 .x-btn-plain-toolbar-small-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-plain-toolbar-small-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-plain-toolbar-small-focus{border-color:#96caee;background-image:none;background-color:#ebebeb;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-plain-toolbar-small-over{border-color:#d8d8d8;background-image:none;background-color:#ebebeb;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#cbcbcb),color-stop(100%,#b8b8b8));background-image:-webkit-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-moz-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-o-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-ms-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:linear-gradient(top,#cbcbcb,#b8b8b8)}.x-btn-plain-toolbar-small-menu-active,.x-btn-plain-toolbar-small-pressed{border-color:#cecece;background-image:none;background-color:#e1e1e1;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#919191),color-stop(100%,#808080));background-image:-webkit-linear-gradient(top,#919191,#808080);background-image:-moz-linear-gradient(top,#919191,#808080);background-image:-o-linear-gradient(top,#919191,#808080);background-image:-ms-linear-gradient(top,#919191,#808080);background-image:linear-gradient(top,#919191,#808080)}.x-btn-plain-toolbar-small-menu-active .x-btn-inner,.x-btn-plain-toolbar-small-pressed .x-btn-inner{color:white}.x-btn-plain-toolbar-small-focus .x-frame-tl,.x-btn-plain-toolbar-small-focus .x-frame-bl,.x-btn-plain-toolbar-small-focus .x-frame-tr,.x-btn-plain-toolbar-small-focus .x-frame-br,.x-btn-plain-toolbar-small-focus .x-frame-tc,.x-btn-plain-toolbar-small-focus .x-frame-bc{background-image:url(images/btn/btn-plain-toolbar-small-focus-corners.gif)}.x-btn-plain-toolbar-small-focus .x-frame-ml,.x-btn-plain-toolbar-small-focus .x-frame-mr{background-image:url(images/btn/btn-plain-toolbar-small-focus-sides.gif)}.x-btn-plain-toolbar-small-focus .x-frame-mc{background-color:#ebebeb;background-image:url(images/btn/btn-plain-toolbar-small-focus-fbg.gif)}.x-btn-plain-toolbar-small-over .x-frame-tl,.x-btn-plain-toolbar-small-over .x-frame-bl,.x-btn-plain-toolbar-small-over .x-frame-tr,.x-btn-plain-toolbar-small-over .x-frame-br,.x-btn-plain-toolbar-small-over .x-frame-tc,.x-btn-plain-toolbar-small-over .x-frame-bc{background-image:url(images/btn/btn-plain-toolbar-small-over-corners.gif)}.x-btn-plain-toolbar-small-over .x-frame-ml,.x-btn-plain-toolbar-small-over .x-frame-mr{background-image:url(images/btn/btn-plain-toolbar-small-over-sides.gif)}.x-btn-plain-toolbar-small-over .x-frame-mc{background-color:#ebebeb;background-image:url(images/btn/btn-plain-toolbar-small-over-fbg.gif)}.x-btn-plain-toolbar-small-menu-active .x-frame-tl,.x-btn-plain-toolbar-small-menu-active .x-frame-bl,.x-btn-plain-toolbar-small-menu-active .x-frame-tr,.x-btn-plain-toolbar-small-menu-active .x-frame-br,.x-btn-plain-toolbar-small-menu-active .x-frame-tc,.x-btn-plain-toolbar-small-menu-active .x-frame-bc,.x-btn-plain-toolbar-small-pressed .x-frame-tl,.x-btn-plain-toolbar-small-pressed .x-frame-bl,.x-btn-plain-toolbar-small-pressed .x-frame-tr,.x-btn-plain-toolbar-small-pressed .x-frame-br,.x-btn-plain-toolbar-small-pressed .x-frame-tc,.x-btn-plain-toolbar-small-pressed .x-frame-bc{background-image:url(images/btn/btn-plain-toolbar-small-pressed-corners.gif)}.x-btn-plain-toolbar-small-menu-active .x-frame-ml,.x-btn-plain-toolbar-small-menu-active .x-frame-mr,.x-btn-plain-toolbar-small-pressed .x-frame-ml,.x-btn-plain-toolbar-small-pressed .x-frame-mr{background-image:url(images/btn/btn-plain-toolbar-small-pressed-sides.gif)}.x-btn-plain-toolbar-small-menu-active .x-frame-mc,.x-btn-plain-toolbar-small-pressed .x-frame-mc{background-color:#e1e1e1;background-image:url(images/btn/btn-plain-toolbar-small-pressed-fbg.gif)}.x-btn-plain-toolbar-small-disabled .x-frame-tl,.x-btn-plain-toolbar-small-disabled .x-frame-bl,.x-btn-plain-toolbar-small-disabled .x-frame-tr,.x-btn-plain-toolbar-small-disabled .x-frame-br,.x-btn-plain-toolbar-small-disabled .x-frame-tc,.x-btn-plain-toolbar-small-disabled .x-frame-bc{background-image:url(images/btn/btn-plain-toolbar-small-disabled-corners.gif)}.x-btn-plain-toolbar-small-disabled .x-frame-ml,.x-btn-plain-toolbar-small-disabled .x-frame-mr{background-image:url(images/btn/btn-plain-toolbar-small-disabled-sides.gif)}.x-btn-plain-toolbar-small-disabled .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-plain-toolbar-small-disabled-fbg.gif)}.x-nlg .x-btn-plain-toolbar-small-focus{background-image:url(images/btn/btn-plain-toolbar-small-focus-bg.gif)}.x-nlg .x-btn-plain-toolbar-small-over{background-image:url(images/btn/btn-plain-toolbar-small-over-bg.gif)}.x-nlg .x-btn-plain-toolbar-small-menu-active,.x-nlg .x-btn-plain-toolbar-small-pressed{background-image:url(images/btn/btn-plain-toolbar-small-pressed-bg.gif)}.x-nlg .x-btn-plain-toolbar-small-disabled{background-image:url(images/btn/btn-plain-toolbar-small-disabled-bg.gif)}.x-nbr .x-btn-plain-toolbar-small{background-image:none}.x-btn-plain-toolbar-small .x-btn-split-right{background-image:url(images/button/plain-toolbar-small-s-arrow.png);padding-right:23px}.x-btn-plain-toolbar-small .x-btn-split-bottom{background-image:url(images/button/plain-toolbar-small-s-arrow-b.png);padding-bottom:20px}.x-btn-plain-toolbar-small-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-plain-toolbar-small-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-small-focus-corners.gif), sides:url(images/btn/btn-plain-toolbar-small-focus-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-small-focus-fbg.gif), bg:url(images/btn/btn-plain-toolbar-small-focus-bg.gif)"}.x-btn-plain-toolbar-small-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-small-over-corners.gif), sides:url(images/btn/btn-plain-toolbar-small-over-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-small-over-fbg.gif), bg:url(images/btn/btn-plain-toolbar-small-over-bg.gif)"}.x-btn-plain-toolbar-small-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-small-pressed-corners.gif), sides:url(images/btn/btn-plain-toolbar-small-pressed-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-small-pressed-fbg.gif), bg:url(images/btn/btn-plain-toolbar-small-pressed-bg.gif)"}.x-btn-plain-toolbar-small-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-small-disabled-corners.gif), sides:url(images/btn/btn-plain-toolbar-small-disabled-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-small-disabled-fbg.gif), bg:url(images/btn/btn-plain-toolbar-small-disabled-bg.gif)"}.x-btn-plain-toolbar-medium{border-color:transparent}.x-btn-plain-toolbar-medium{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:transparent}.x-btn-plain-toolbar-medium-mc{background-image:url(images/btn/btn-plain-toolbar-medium-fbg.gif);background-position:0 top;background-color:transparent}.x-nlg .x-btn-plain-toolbar-medium{background-image:url(images/btn/btn-plain-toolbar-medium-bg.gif);background-position:0 top}.x-nbr .x-btn-plain-toolbar-medium{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-plain-toolbar-medium-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-plain-toolbar-medium-tl{background-position:0 -6px}.x-btn-plain-toolbar-medium-tr{background-position:right -9px}.x-btn-plain-toolbar-medium-bl{background-position:0 -12px}.x-btn-plain-toolbar-medium-br{background-position:right -15px}.x-btn-plain-toolbar-medium-ml{background-position:0 top}.x-btn-plain-toolbar-medium-mr{background-position:right top}.x-btn-plain-toolbar-medium-tc{background-position:0 0}.x-btn-plain-toolbar-medium-bc{background-position:0 -3px}.x-btn-plain-toolbar-medium-tr,.x-btn-plain-toolbar-medium-br,.x-btn-plain-toolbar-medium-mr{padding-right:3px}.x-btn-plain-toolbar-medium-tl,.x-btn-plain-toolbar-medium-bl,.x-btn-plain-toolbar-medium-ml{padding-left:3px}.x-btn-plain-toolbar-medium-tc{height:3px}.x-btn-plain-toolbar-medium-bc{height:3px}.x-btn-plain-toolbar-medium-tl,.x-btn-plain-toolbar-medium-bl,.x-btn-plain-toolbar-medium-tr,.x-btn-plain-toolbar-medium-br,.x-btn-plain-toolbar-medium-tc,.x-btn-plain-toolbar-medium-bc,.x-btn-plain-toolbar-medium-ml,.x-btn-plain-toolbar-medium-mr{zoom:1}.x-btn-plain-toolbar-medium-ml,.x-btn-plain-toolbar-medium-mr{zoom:1}.x-btn-plain-toolbar-medium-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-plain-toolbar-medium-tl,.x-strict .x-ie7 .x-btn-plain-toolbar-medium-bl{position:relative;right:0}.x-btn-plain-toolbar-medium:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-plain-toolbar-medium-fbg.gif), bg:url(images/btn/btn-plain-toolbar-medium-bg.gif)"}.x-btn-plain-toolbar-medium .x-btn-inner{font-size:14px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#333;padding:0 8px}.x-btn-plain-toolbar-medium .x-btn-arrow{background-image:url(images/button/plain-toolbar-medium-arrow.png)}.x-btn-plain-toolbar-medium .x-btn-arrow-right{padding-right:30px}.x-btn-plain-toolbar-medium .x-btn-arrow-bottom{padding-bottom:26px}.x-btn-plain-toolbar-medium .x-btn-glyph{font-size:24px;line-height:24px;color:#333}.x-ie8m .x-btn-plain-toolbar-medium .x-btn-glyph{color:#333}.x-btn-plain-toolbar-medium-disabled{background-image:none;background-color:transparent}.x-btn-plain-toolbar-medium-icon .x-btn-button,.x-btn-plain-toolbar-medium-noicon .x-btn-button{height:24px}.x-btn-plain-toolbar-medium-icon .x-btn-inner,.x-btn-plain-toolbar-medium-noicon .x-btn-inner{line-height:24px}.x-btn-plain-toolbar-medium-icon .x-btn-arrow-right .x-btn-inner,.x-btn-plain-toolbar-medium-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-plain-toolbar-medium-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-plain-toolbar-medium-icon .x-btn-inner{width:24px;padding:0}.x-btn-plain-toolbar-medium-icon .x-btn-icon-el{width:24px;height:24px}.x-btn-plain-toolbar-medium-icon-text-left .x-btn-button{height:24px}.x-btn-plain-toolbar-medium-icon-text-left .x-btn-inner{line-height:24px;padding-left:29px}.x-btn-plain-toolbar-medium-icon-text-left .x-btn-icon-el{width:24px;right:auto}.x-ie6 .x-btn-plain-toolbar-medium-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-plain-toolbar-medium-icon-text-left .x-btn-icon-el{height:24px}.x-btn-plain-toolbar-medium-icon-text-right .x-btn-button{height:24px}.x-btn-plain-toolbar-medium-icon-text-right .x-btn-inner{line-height:24px;padding-right:29px}.x-btn-plain-toolbar-medium-icon-text-right .x-btn-icon-el{width:24px;left:auto}.x-ie6 .x-btn-plain-toolbar-medium-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-plain-toolbar-medium-icon-text-right .x-btn-icon-el{height:24px}.x-btn-plain-toolbar-medium-icon-text-top .x-btn-inner{padding-top:29px}.x-btn-plain-toolbar-medium-icon-text-top .x-btn-icon-el{height:24px;bottom:auto}.x-ie6 .x-btn-plain-toolbar-medium-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-plain-toolbar-medium-icon-text-top .x-btn-icon-el{width:100%}.x-btn-plain-toolbar-medium-icon-text-bottom .x-btn-inner{padding-bottom:29px}.x-btn-plain-toolbar-medium-icon-text-bottom .x-btn-icon-el{height:24px;top:auto}.x-ie6 .x-btn-plain-toolbar-medium-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-plain-toolbar-medium-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-plain-toolbar-medium-focus{border-color:#96caee;background-image:none;background-color:#ebebeb;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-plain-toolbar-medium-over{border-color:#d8d8d8;background-image:none;background-color:#ebebeb;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#cbcbcb),color-stop(100%,#b8b8b8));background-image:-webkit-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-moz-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-o-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-ms-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:linear-gradient(top,#cbcbcb,#b8b8b8)}.x-btn-plain-toolbar-medium-menu-active,.x-btn-plain-toolbar-medium-pressed{border-color:#cecece;background-image:none;background-color:#e1e1e1;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#919191),color-stop(100%,#808080));background-image:-webkit-linear-gradient(top,#919191,#808080);background-image:-moz-linear-gradient(top,#919191,#808080);background-image:-o-linear-gradient(top,#919191,#808080);background-image:-ms-linear-gradient(top,#919191,#808080);background-image:linear-gradient(top,#919191,#808080)}.x-btn-plain-toolbar-medium-menu-active .x-btn-inner,.x-btn-plain-toolbar-medium-pressed .x-btn-inner{color:white}.x-btn-plain-toolbar-medium-focus .x-frame-tl,.x-btn-plain-toolbar-medium-focus .x-frame-bl,.x-btn-plain-toolbar-medium-focus .x-frame-tr,.x-btn-plain-toolbar-medium-focus .x-frame-br,.x-btn-plain-toolbar-medium-focus .x-frame-tc,.x-btn-plain-toolbar-medium-focus .x-frame-bc{background-image:url(images/btn/btn-plain-toolbar-medium-focus-corners.gif)}.x-btn-plain-toolbar-medium-focus .x-frame-ml,.x-btn-plain-toolbar-medium-focus .x-frame-mr{background-image:url(images/btn/btn-plain-toolbar-medium-focus-sides.gif)}.x-btn-plain-toolbar-medium-focus .x-frame-mc{background-color:#ebebeb;background-image:url(images/btn/btn-plain-toolbar-medium-focus-fbg.gif)}.x-btn-plain-toolbar-medium-over .x-frame-tl,.x-btn-plain-toolbar-medium-over .x-frame-bl,.x-btn-plain-toolbar-medium-over .x-frame-tr,.x-btn-plain-toolbar-medium-over .x-frame-br,.x-btn-plain-toolbar-medium-over .x-frame-tc,.x-btn-plain-toolbar-medium-over .x-frame-bc{background-image:url(images/btn/btn-plain-toolbar-medium-over-corners.gif)}.x-btn-plain-toolbar-medium-over .x-frame-ml,.x-btn-plain-toolbar-medium-over .x-frame-mr{background-image:url(images/btn/btn-plain-toolbar-medium-over-sides.gif)}.x-btn-plain-toolbar-medium-over .x-frame-mc{background-color:#ebebeb;background-image:url(images/btn/btn-plain-toolbar-medium-over-fbg.gif)}.x-btn-plain-toolbar-medium-menu-active .x-frame-tl,.x-btn-plain-toolbar-medium-menu-active .x-frame-bl,.x-btn-plain-toolbar-medium-menu-active .x-frame-tr,.x-btn-plain-toolbar-medium-menu-active .x-frame-br,.x-btn-plain-toolbar-medium-menu-active .x-frame-tc,.x-btn-plain-toolbar-medium-menu-active .x-frame-bc,.x-btn-plain-toolbar-medium-pressed .x-frame-tl,.x-btn-plain-toolbar-medium-pressed .x-frame-bl,.x-btn-plain-toolbar-medium-pressed .x-frame-tr,.x-btn-plain-toolbar-medium-pressed .x-frame-br,.x-btn-plain-toolbar-medium-pressed .x-frame-tc,.x-btn-plain-toolbar-medium-pressed .x-frame-bc{background-image:url(images/btn/btn-plain-toolbar-medium-pressed-corners.gif)}.x-btn-plain-toolbar-medium-menu-active .x-frame-ml,.x-btn-plain-toolbar-medium-menu-active .x-frame-mr,.x-btn-plain-toolbar-medium-pressed .x-frame-ml,.x-btn-plain-toolbar-medium-pressed .x-frame-mr{background-image:url(images/btn/btn-plain-toolbar-medium-pressed-sides.gif)}.x-btn-plain-toolbar-medium-menu-active .x-frame-mc,.x-btn-plain-toolbar-medium-pressed .x-frame-mc{background-color:#e1e1e1;background-image:url(images/btn/btn-plain-toolbar-medium-pressed-fbg.gif)}.x-btn-plain-toolbar-medium-disabled .x-frame-tl,.x-btn-plain-toolbar-medium-disabled .x-frame-bl,.x-btn-plain-toolbar-medium-disabled .x-frame-tr,.x-btn-plain-toolbar-medium-disabled .x-frame-br,.x-btn-plain-toolbar-medium-disabled .x-frame-tc,.x-btn-plain-toolbar-medium-disabled .x-frame-bc{background-image:url(images/btn/btn-plain-toolbar-medium-disabled-corners.gif)}.x-btn-plain-toolbar-medium-disabled .x-frame-ml,.x-btn-plain-toolbar-medium-disabled .x-frame-mr{background-image:url(images/btn/btn-plain-toolbar-medium-disabled-sides.gif)}.x-btn-plain-toolbar-medium-disabled .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-plain-toolbar-medium-disabled-fbg.gif)}.x-nlg .x-btn-plain-toolbar-medium-focus{background-image:url(images/btn/btn-plain-toolbar-medium-focus-bg.gif)}.x-nlg .x-btn-plain-toolbar-medium-over{background-image:url(images/btn/btn-plain-toolbar-medium-over-bg.gif)}.x-nlg .x-btn-plain-toolbar-medium-menu-active,.x-nlg .x-btn-plain-toolbar-medium-pressed{background-image:url(images/btn/btn-plain-toolbar-medium-pressed-bg.gif)}.x-nlg .x-btn-plain-toolbar-medium-disabled{background-image:url(images/btn/btn-plain-toolbar-medium-disabled-bg.gif)}.x-nbr .x-btn-plain-toolbar-medium{background-image:none}.x-btn-plain-toolbar-medium .x-btn-split-right{background-image:url(images/button/plain-toolbar-medium-s-arrow.png);padding-right:32px}.x-btn-plain-toolbar-medium .x-btn-split-bottom{background-image:url(images/button/plain-toolbar-medium-s-arrow-b.png);padding-bottom:28px}.x-btn-plain-toolbar-medium-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-plain-toolbar-medium-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-medium-focus-corners.gif), sides:url(images/btn/btn-plain-toolbar-medium-focus-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-medium-focus-fbg.gif), bg:url(images/btn/btn-plain-toolbar-medium-focus-bg.gif)"}.x-btn-plain-toolbar-medium-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-medium-over-corners.gif), sides:url(images/btn/btn-plain-toolbar-medium-over-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-medium-over-fbg.gif), bg:url(images/btn/btn-plain-toolbar-medium-over-bg.gif)"}.x-btn-plain-toolbar-medium-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-medium-pressed-corners.gif), sides:url(images/btn/btn-plain-toolbar-medium-pressed-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-medium-pressed-fbg.gif), bg:url(images/btn/btn-plain-toolbar-medium-pressed-bg.gif)"}.x-btn-plain-toolbar-medium-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-medium-disabled-corners.gif), sides:url(images/btn/btn-plain-toolbar-medium-disabled-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-medium-disabled-fbg.gif), bg:url(images/btn/btn-plain-toolbar-medium-disabled-bg.gif)"}.x-btn-plain-toolbar-large{border-color:transparent}.x-btn-plain-toolbar-large{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:transparent}.x-btn-plain-toolbar-large-mc{background-image:url(images/btn/btn-plain-toolbar-large-fbg.gif);background-position:0 top;background-color:transparent}.x-nlg .x-btn-plain-toolbar-large{background-image:url(images/btn/btn-plain-toolbar-large-bg.gif);background-position:0 top}.x-nbr .x-btn-plain-toolbar-large{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-plain-toolbar-large-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-plain-toolbar-large-tl{background-position:0 -6px}.x-btn-plain-toolbar-large-tr{background-position:right -9px}.x-btn-plain-toolbar-large-bl{background-position:0 -12px}.x-btn-plain-toolbar-large-br{background-position:right -15px}.x-btn-plain-toolbar-large-ml{background-position:0 top}.x-btn-plain-toolbar-large-mr{background-position:right top}.x-btn-plain-toolbar-large-tc{background-position:0 0}.x-btn-plain-toolbar-large-bc{background-position:0 -3px}.x-btn-plain-toolbar-large-tr,.x-btn-plain-toolbar-large-br,.x-btn-plain-toolbar-large-mr{padding-right:3px}.x-btn-plain-toolbar-large-tl,.x-btn-plain-toolbar-large-bl,.x-btn-plain-toolbar-large-ml{padding-left:3px}.x-btn-plain-toolbar-large-tc{height:3px}.x-btn-plain-toolbar-large-bc{height:3px}.x-btn-plain-toolbar-large-tl,.x-btn-plain-toolbar-large-bl,.x-btn-plain-toolbar-large-tr,.x-btn-plain-toolbar-large-br,.x-btn-plain-toolbar-large-tc,.x-btn-plain-toolbar-large-bc,.x-btn-plain-toolbar-large-ml,.x-btn-plain-toolbar-large-mr{zoom:1}.x-btn-plain-toolbar-large-ml,.x-btn-plain-toolbar-large-mr{zoom:1}.x-btn-plain-toolbar-large-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-plain-toolbar-large-tl,.x-strict .x-ie7 .x-btn-plain-toolbar-large-bl{position:relative;right:0}.x-btn-plain-toolbar-large:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-plain-toolbar-large-fbg.gif), bg:url(images/btn/btn-plain-toolbar-large-bg.gif)"}.x-btn-plain-toolbar-large .x-btn-inner{font-size:16px;font-weight:bold;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#333;padding:0 10px} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-prod_02.css b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-prod_02.css new file mode 100644 index 0000000000000000000000000000000000000000..00189da17628cabbd67d70501ef84a1d1dc4c145 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-prod_02.css @@ -0,0 +1 @@ +background-image:url(images/button/plain-toolbar-large-arrow.png)}.x-btn-plain-toolbar-large .x-btn-arrow-right{padding-right:36px}.x-btn-plain-toolbar-large .x-btn-arrow-bottom{padding-bottom:32px}.x-btn-plain-toolbar-large .x-btn-glyph{font-size:32px;line-height:32px;color:#333}.x-ie8m .x-btn-plain-toolbar-large .x-btn-glyph{color:#333}.x-btn-plain-toolbar-large-disabled{background-image:none;background-color:transparent}.x-btn-plain-toolbar-large-icon .x-btn-button,.x-btn-plain-toolbar-large-noicon .x-btn-button{height:32px}.x-btn-plain-toolbar-large-icon .x-btn-inner,.x-btn-plain-toolbar-large-noicon .x-btn-inner{line-height:32px}.x-btn-plain-toolbar-large-icon .x-btn-arrow-right .x-btn-inner,.x-btn-plain-toolbar-large-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-plain-toolbar-large-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-plain-toolbar-large-icon .x-btn-inner{width:32px;padding:0}.x-btn-plain-toolbar-large-icon .x-btn-icon-el{width:32px;height:32px}.x-btn-plain-toolbar-large-icon-text-left .x-btn-button{height:32px}.x-btn-plain-toolbar-large-icon-text-left .x-btn-inner{line-height:32px;padding-left:37px}.x-btn-plain-toolbar-large-icon-text-left .x-btn-icon-el{width:32px;right:auto}.x-ie6 .x-btn-plain-toolbar-large-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-plain-toolbar-large-icon-text-left .x-btn-icon-el{height:32px}.x-btn-plain-toolbar-large-icon-text-right .x-btn-button{height:32px}.x-btn-plain-toolbar-large-icon-text-right .x-btn-inner{line-height:32px;padding-right:37px}.x-btn-plain-toolbar-large-icon-text-right .x-btn-icon-el{width:32px;left:auto}.x-ie6 .x-btn-plain-toolbar-large-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-plain-toolbar-large-icon-text-right .x-btn-icon-el{height:32px}.x-btn-plain-toolbar-large-icon-text-top .x-btn-inner{padding-top:37px}.x-btn-plain-toolbar-large-icon-text-top .x-btn-icon-el{height:32px;bottom:auto}.x-ie6 .x-btn-plain-toolbar-large-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-plain-toolbar-large-icon-text-top .x-btn-icon-el{width:100%}.x-btn-plain-toolbar-large-icon-text-bottom .x-btn-inner{padding-bottom:37px}.x-btn-plain-toolbar-large-icon-text-bottom .x-btn-icon-el{height:32px;top:auto}.x-ie6 .x-btn-plain-toolbar-large-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-plain-toolbar-large-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-plain-toolbar-large-focus{border-color:#96caee;background-image:none;background-color:#ebebeb;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-plain-toolbar-large-over{border-color:#d8d8d8;background-image:none;background-color:#ebebeb;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#cbcbcb),color-stop(100%,#b8b8b8));background-image:-webkit-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-moz-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-o-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-ms-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:linear-gradient(top,#cbcbcb,#b8b8b8)}.x-btn-plain-toolbar-large-menu-active,.x-btn-plain-toolbar-large-pressed{border-color:#cecece;background-image:none;background-color:#e1e1e1;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#919191),color-stop(100%,#808080));background-image:-webkit-linear-gradient(top,#919191,#808080);background-image:-moz-linear-gradient(top,#919191,#808080);background-image:-o-linear-gradient(top,#919191,#808080);background-image:-ms-linear-gradient(top,#919191,#808080);background-image:linear-gradient(top,#919191,#808080)}.x-btn-plain-toolbar-large-menu-active .x-btn-inner,.x-btn-plain-toolbar-large-pressed .x-btn-inner{color:white}.x-btn-plain-toolbar-large-focus .x-frame-tl,.x-btn-plain-toolbar-large-focus .x-frame-bl,.x-btn-plain-toolbar-large-focus .x-frame-tr,.x-btn-plain-toolbar-large-focus .x-frame-br,.x-btn-plain-toolbar-large-focus .x-frame-tc,.x-btn-plain-toolbar-large-focus .x-frame-bc{background-image:url(images/btn/btn-plain-toolbar-large-focus-corners.gif)}.x-btn-plain-toolbar-large-focus .x-frame-ml,.x-btn-plain-toolbar-large-focus .x-frame-mr{background-image:url(images/btn/btn-plain-toolbar-large-focus-sides.gif)}.x-btn-plain-toolbar-large-focus .x-frame-mc{background-color:#ebebeb;background-image:url(images/btn/btn-plain-toolbar-large-focus-fbg.gif)}.x-btn-plain-toolbar-large-over .x-frame-tl,.x-btn-plain-toolbar-large-over .x-frame-bl,.x-btn-plain-toolbar-large-over .x-frame-tr,.x-btn-plain-toolbar-large-over .x-frame-br,.x-btn-plain-toolbar-large-over .x-frame-tc,.x-btn-plain-toolbar-large-over .x-frame-bc{background-image:url(images/btn/btn-plain-toolbar-large-over-corners.gif)}.x-btn-plain-toolbar-large-over .x-frame-ml,.x-btn-plain-toolbar-large-over .x-frame-mr{background-image:url(images/btn/btn-plain-toolbar-large-over-sides.gif)}.x-btn-plain-toolbar-large-over .x-frame-mc{background-color:#ebebeb;background-image:url(images/btn/btn-plain-toolbar-large-over-fbg.gif)}.x-btn-plain-toolbar-large-menu-active .x-frame-tl,.x-btn-plain-toolbar-large-menu-active .x-frame-bl,.x-btn-plain-toolbar-large-menu-active .x-frame-tr,.x-btn-plain-toolbar-large-menu-active .x-frame-br,.x-btn-plain-toolbar-large-menu-active .x-frame-tc,.x-btn-plain-toolbar-large-menu-active .x-frame-bc,.x-btn-plain-toolbar-large-pressed .x-frame-tl,.x-btn-plain-toolbar-large-pressed .x-frame-bl,.x-btn-plain-toolbar-large-pressed .x-frame-tr,.x-btn-plain-toolbar-large-pressed .x-frame-br,.x-btn-plain-toolbar-large-pressed .x-frame-tc,.x-btn-plain-toolbar-large-pressed .x-frame-bc{background-image:url(images/btn/btn-plain-toolbar-large-pressed-corners.gif)}.x-btn-plain-toolbar-large-menu-active .x-frame-ml,.x-btn-plain-toolbar-large-menu-active .x-frame-mr,.x-btn-plain-toolbar-large-pressed .x-frame-ml,.x-btn-plain-toolbar-large-pressed .x-frame-mr{background-image:url(images/btn/btn-plain-toolbar-large-pressed-sides.gif)}.x-btn-plain-toolbar-large-menu-active .x-frame-mc,.x-btn-plain-toolbar-large-pressed .x-frame-mc{background-color:#e1e1e1;background-image:url(images/btn/btn-plain-toolbar-large-pressed-fbg.gif)}.x-btn-plain-toolbar-large-disabled .x-frame-tl,.x-btn-plain-toolbar-large-disabled .x-frame-bl,.x-btn-plain-toolbar-large-disabled .x-frame-tr,.x-btn-plain-toolbar-large-disabled .x-frame-br,.x-btn-plain-toolbar-large-disabled .x-frame-tc,.x-btn-plain-toolbar-large-disabled .x-frame-bc{background-image:url(images/btn/btn-plain-toolbar-large-disabled-corners.gif)}.x-btn-plain-toolbar-large-disabled .x-frame-ml,.x-btn-plain-toolbar-large-disabled .x-frame-mr{background-image:url(images/btn/btn-plain-toolbar-large-disabled-sides.gif)}.x-btn-plain-toolbar-large-disabled .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-plain-toolbar-large-disabled-fbg.gif)}.x-nlg .x-btn-plain-toolbar-large-focus{background-image:url(images/btn/btn-plain-toolbar-large-focus-bg.gif)}.x-nlg .x-btn-plain-toolbar-large-over{background-image:url(images/btn/btn-plain-toolbar-large-over-bg.gif)}.x-nlg .x-btn-plain-toolbar-large-menu-active,.x-nlg .x-btn-plain-toolbar-large-pressed{background-image:url(images/btn/btn-plain-toolbar-large-pressed-bg.gif)}.x-nlg .x-btn-plain-toolbar-large-disabled{background-image:url(images/btn/btn-plain-toolbar-large-disabled-bg.gif)}.x-nbr .x-btn-plain-toolbar-large{background-image:none}.x-btn-plain-toolbar-large .x-btn-split-right{background-image:url(images/button/plain-toolbar-large-s-arrow.png);padding-right:38px}.x-btn-plain-toolbar-large .x-btn-split-bottom{background-image:url(images/button/plain-toolbar-large-s-arrow-b.png);padding-bottom:34px}.x-btn-plain-toolbar-large-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-plain-toolbar-large-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-large-focus-corners.gif), sides:url(images/btn/btn-plain-toolbar-large-focus-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-large-focus-fbg.gif), bg:url(images/btn/btn-plain-toolbar-large-focus-bg.gif)"}.x-btn-plain-toolbar-large-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-large-over-corners.gif), sides:url(images/btn/btn-plain-toolbar-large-over-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-large-over-fbg.gif), bg:url(images/btn/btn-plain-toolbar-large-over-bg.gif)"}.x-btn-plain-toolbar-large-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-large-pressed-corners.gif), sides:url(images/btn/btn-plain-toolbar-large-pressed-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-large-pressed-fbg.gif), bg:url(images/btn/btn-plain-toolbar-large-pressed-bg.gif)"}.x-btn-plain-toolbar-large-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-plain-toolbar-large-disabled-corners.gif), sides:url(images/btn/btn-plain-toolbar-large-disabled-sides.gif), frame-bg:url(images/btn/btn-plain-toolbar-large-disabled-fbg.gif), bg:url(images/btn/btn-plain-toolbar-large-disabled-bg.gif)"}.x-btn-plain-toolbar-small-disabled .x-btn-icon-el,.x-btn-plain-toolbar-medium-disabled .x-btn-icon-el,.x-btn-plain-toolbar-large-disabled .x-btn-icon-el{background-color:transparent}.x-strict .x-ie8 .x-btn-plain-toolbar-small-disabled .x-btn-icon-el,.x-strict .x-ie8 .x-btn-plain-toolbar-medium-disabled .x-btn-icon-el,.x-strict .x-ie8 .x-btn-plain-toolbar-large-disabled .x-btn-icon-el{filter:alpha(opacity=50);opacity:.5}.x-toolbar-default .x-toolbar-scroll-left{margin-right:4px}.x-toolbar-default .x-toolbar-scroll-right{margin-left:4px}.x-toolbar-default .x-toolbar-scroll-left,.x-toolbar-default .x-toolbar-scroll-right{filter:alpha(opacity=60);opacity:.6}.x-toolbar-default .x-toolbar-scroll-left-hover,.x-toolbar-default .x-toolbar-scroll-right-hover{background-position:0 0;filter:alpha(opacity=80);opacity:.8}.x-toolbar-default .x-toolbar-scroll-left-pressed,.x-toolbar-default .x-toolbar-scroll-right-pressed{background-position:0 0;filter:alpha(opacity=100);opacity:1}.x-toolbar-default .x-box-scroller-disabled{filter:alpha(opacity=25);opacity:.25}.x-toolbar-default .x-box-scroller{background-color:transparent}.x-toolbar-scroller{padding:6px 4px 6px 4px}.x-toolbar-vertical-scroller{padding:3px 8px 3px 8px}.x-panel-light{border-color:#e4e4e4;padding:0}.x-panel-header-light{font-size:13px;border:1px solid #e4e4e4}.x-panel-header-light .x-tool-img{background-image:url(images/tools/tool-sprites-dark.png);background-color:#e4e4e4}.x-panel-header-light-horizontal{padding:9px 9px 10px 9px}.x-panel-header-light-horizontal-noborder{padding:10px 10px 10px 10px}.x-panel-header-light-vertical{padding:9px 9px 9px 10px}.x-panel-header-light-vertical-noborder{padding:10px 10px 10px 10px}.x-panel-header-text-container-light{color:#666;font-size:13px;font-weight:bold;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;line-height:15px;padding:1px 0 0;text-transform:none}.x-panel-body-light{background:transparent;border-color:#e4e4e4;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-header-light{background-image:none;background-color:#e4e4e4}.x-panel-header-light-vertical{background-image:none;background-color:#e4e4e4}.x-panel .x-panel-header-light-collapsed-border-top{border-bottom-width:1px!important}.x-panel .x-panel-header-light-collapsed-border-right{border-left-width:1px!important}.x-panel .x-panel-header-light-collapsed-border-bottom{border-top-width:1px!important}.x-panel .x-panel-header-light-collapsed-border-left{border-right-width:1px!important}.x-panel-header-light-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-light-bottom:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-light-left:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-light-right:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-light-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-light-vertical .x-panel-header-text-container{background-color:#e4e4e4;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#e4e4e4)}.x-panel-header-light .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-light .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-light .x-panel-header-glyph{color:#f1f1f1}.x-panel-header-light-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-light-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-light-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-light-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-light-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-light-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-light-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-light-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-light-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-light-outer-border-l{border-left-color:#e4e4e4!important;border-left-width:1px!important}.x-panel-light-outer-border-b{border-bottom-color:#e4e4e4!important;border-bottom-width:1px!important}.x-panel-light-outer-border-bl{border-bottom-color:#e4e4e4!important;border-bottom-width:1px!important;border-left-color:#e4e4e4!important;border-left-width:1px!important}.x-panel-light-outer-border-r{border-right-color:#e4e4e4!important;border-right-width:1px!important}.x-panel-light-outer-border-rl{border-right-color:#e4e4e4!important;border-right-width:1px!important;border-left-color:#e4e4e4!important;border-left-width:1px!important}.x-panel-light-outer-border-rb{border-right-color:#e4e4e4!important;border-right-width:1px!important;border-bottom-color:#e4e4e4!important;border-bottom-width:1px!important}.x-panel-light-outer-border-rbl{border-right-color:#e4e4e4!important;border-right-width:1px!important;border-bottom-color:#e4e4e4!important;border-bottom-width:1px!important;border-left-color:#e4e4e4!important;border-left-width:1px!important}.x-panel-light-outer-border-t{border-top-color:#e4e4e4!important;border-top-width:1px!important}.x-panel-light-outer-border-tl{border-top-color:#e4e4e4!important;border-top-width:1px!important;border-left-color:#e4e4e4!important;border-left-width:1px!important}.x-panel-light-outer-border-tb{border-top-color:#e4e4e4!important;border-top-width:1px!important;border-bottom-color:#e4e4e4!important;border-bottom-width:1px!important}.x-panel-light-outer-border-tbl{border-top-color:#e4e4e4!important;border-top-width:1px!important;border-bottom-color:#e4e4e4!important;border-bottom-width:1px!important;border-left-color:#e4e4e4!important;border-left-width:1px!important}.x-panel-light-outer-border-tr{border-top-color:#e4e4e4!important;border-top-width:1px!important;border-right-color:#e4e4e4!important;border-right-width:1px!important}.x-panel-light-outer-border-trl{border-top-color:#e4e4e4!important;border-top-width:1px!important;border-right-color:#e4e4e4!important;border-right-width:1px!important;border-left-color:#e4e4e4!important;border-left-width:1px!important}.x-panel-light-outer-border-trb{border-top-color:#e4e4e4!important;border-top-width:1px!important;border-right-color:#e4e4e4!important;border-right-width:1px!important;border-bottom-color:#e4e4e4!important;border-bottom-width:1px!important}.x-panel-light-outer-border-trbl{border-color:#e4e4e4!important;border-width:1px!important}.x-panel-light-framed{border-color:#e4e4e4;padding:0}.x-panel-header-light-framed{font-size:13px;border:2px solid #e4e4e4}.x-panel-header-light-framed .x-tool-img{background-image:url(images/tools/tool-sprites-dark.png);background-color:#e4e4e4}.x-panel-header-light-framed-horizontal{padding:5px}.x-panel-header-light-framed-horizontal-noborder{padding:7px 7px 5px 7px}.x-panel-header-light-framed-vertical{padding:5px 5px 5px 5px}.x-panel-header-light-framed-vertical-noborder{padding:7px 7px 7px 5px}.x-panel-header-text-container-light-framed{color:#666;font-size:13px;font-weight:bold;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;line-height:15px;padding:1px 0 0;text-transform:none}.x-panel-body-light-framed{background:white;border-color:#e4e4e4;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-light-framed{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:2px 2px 2px 2px;border-width:2px;border-style:solid;background-color:white}.x-panel-light-framed-mc{background-color:white}.x-nbr .x-panel-light-framed{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-light-framed-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-2-2-2-2}.x-panel-light-framed-tl{background-position:0 -8px}.x-panel-light-framed-tr{background-position:right -12px}.x-panel-light-framed-bl{background-position:0 -16px}.x-panel-light-framed-br{background-position:right -20px}.x-panel-light-framed-ml{background-position:0 top}.x-panel-light-framed-mr{background-position:right top}.x-panel-light-framed-tc{background-position:0 0}.x-panel-light-framed-bc{background-position:0 -4px}.x-panel-light-framed-tr,.x-panel-light-framed-br,.x-panel-light-framed-mr{padding-right:4px}.x-panel-light-framed-tl,.x-panel-light-framed-bl,.x-panel-light-framed-ml{padding-left:4px}.x-panel-light-framed-tc{height:4px}.x-panel-light-framed-bc{height:4px}.x-panel-light-framed-tl,.x-panel-light-framed-bl,.x-panel-light-framed-tr,.x-panel-light-framed-br,.x-panel-light-framed-tc,.x-panel-light-framed-bc,.x-panel-light-framed-ml,.x-panel-light-framed-mr{zoom:1;background-image:url(images/panel/panel-light-framed-corners.gif)}.x-panel-light-framed-ml,.x-panel-light-framed-mr{zoom:1;background-image:url(images/panel/panel-light-framed-sides.gif);background-repeat:repeat-y}.x-panel-light-framed-mc{padding:0}.x-strict .x-ie7 .x-panel-light-framed-tl,.x-strict .x-ie7 .x-panel-light-framed-bl{position:relative;right:0}.x-panel-light-framed:after{display:none;content:"x-slicer:corners:url(images/panel/panel-light-framed-corners.gif), sides:url(images/panel/panel-light-framed-sides.gif)"}.x-panel-header-light-framed-top{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:5px 5px 5px 5px;border-width:2px 2px 0 2px;border-style:solid;background-color:#e4e4e4}.x-panel-header-light-framed-top-mc{background-color:#e4e4e4}.x-nbr .x-panel-header-light-framed-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-light-framed-top-frameInfo{font-family:dh-4-4-0-0-2-2-0-2-5-5-5-5}.x-panel-header-light-framed-top-tl{background-position:0 -8px}.x-panel-header-light-framed-top-tr{background-position:right -12px}.x-panel-header-light-framed-top-bl{background-position:0 -16px}.x-panel-header-light-framed-top-br{background-position:right -20px}.x-panel-header-light-framed-top-ml{background-position:0 top}.x-panel-header-light-framed-top-mr{background-position:right top}.x-panel-header-light-framed-top-tc{background-position:0 0}.x-panel-header-light-framed-top-bc{background-position:0 -4px}.x-panel-header-light-framed-top-tr,.x-panel-header-light-framed-top-br,.x-panel-header-light-framed-top-mr{padding-right:4px}.x-panel-header-light-framed-top-tl,.x-panel-header-light-framed-top-bl,.x-panel-header-light-framed-top-ml{padding-left:4px}.x-panel-header-light-framed-top-tc{height:4px}.x-panel-header-light-framed-top-bc{height:0}.x-panel-header-light-framed-top-tl,.x-panel-header-light-framed-top-bl,.x-panel-header-light-framed-top-tr,.x-panel-header-light-framed-top-br,.x-panel-header-light-framed-top-tc,.x-panel-header-light-framed-top-bc,.x-panel-header-light-framed-top-ml,.x-panel-header-light-framed-top-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-top-corners.gif)}.x-panel-header-light-framed-top-ml,.x-panel-header-light-framed-top-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-top-sides.gif);background-repeat:repeat-y}.x-panel-header-light-framed-top-mc{padding:3px 3px 5px 3px}.x-strict .x-ie7 .x-panel-header-light-framed-top-tl,.x-strict .x-ie7 .x-panel-header-light-framed-top-bl{position:relative;right:0}.x-panel-header-light-framed-top:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-light-framed-top-corners.gif), sides:url(images/panel-header/panel-header-light-framed-top-sides.gif)"}.x-panel-header-light-framed-right{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:5px 5px 5px 5px;border-width:2px 2px 2px 0;border-style:solid;background-color:#e4e4e4}.x-panel-header-light-framed-right-mc{background-color:#e4e4e4}.x-nbr .x-panel-header-light-framed-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-light-framed-right-frameInfo{font-family:dh-0-4-4-0-2-2-2-0-5-5-5-5}.x-panel-header-light-framed-right-tl{background-position:0 -8px}.x-panel-header-light-framed-right-tr{background-position:right -12px}.x-panel-header-light-framed-right-bl{background-position:0 -16px}.x-panel-header-light-framed-right-br{background-position:right -20px}.x-panel-header-light-framed-right-ml{background-position:0 right}.x-panel-header-light-framed-right-mr{background-position:right right}.x-panel-header-light-framed-right-tc{background-position:0 0}.x-panel-header-light-framed-right-bc{background-position:0 -4px}.x-panel-header-light-framed-right-tr,.x-panel-header-light-framed-right-br,.x-panel-header-light-framed-right-mr{padding-right:4px}.x-panel-header-light-framed-right-tl,.x-panel-header-light-framed-right-bl,.x-panel-header-light-framed-right-ml{padding-left:0}.x-panel-header-light-framed-right-tc{height:4px}.x-panel-header-light-framed-right-bc{height:4px}.x-panel-header-light-framed-right-tl,.x-panel-header-light-framed-right-bl,.x-panel-header-light-framed-right-tr,.x-panel-header-light-framed-right-br,.x-panel-header-light-framed-right-tc,.x-panel-header-light-framed-right-bc,.x-panel-header-light-framed-right-ml,.x-panel-header-light-framed-right-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-right-corners.gif)}.x-panel-header-light-framed-right-ml,.x-panel-header-light-framed-right-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-right-sides.gif);background-repeat:repeat-y}.x-panel-header-light-framed-right-mc{padding:3px 3px 3px 5px}.x-strict .x-ie7 .x-panel-header-light-framed-right-tl,.x-strict .x-ie7 .x-panel-header-light-framed-right-bl{position:relative;right:0}.x-panel-header-light-framed-right:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-light-framed-right-corners.gif), sides:url(images/panel-header/panel-header-light-framed-right-sides.gif)"}.x-panel-header-light-framed-bottom{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:5px 5px 5px 5px;border-width:0 2px 2px 2px;border-style:solid;background-color:#e4e4e4}.x-panel-header-light-framed-bottom-mc{background-color:#e4e4e4}.x-nbr .x-panel-header-light-framed-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-light-framed-bottom-frameInfo{font-family:dh-0-0-4-4-0-2-2-2-5-5-5-5}.x-panel-header-light-framed-bottom-tl{background-position:0 -8px}.x-panel-header-light-framed-bottom-tr{background-position:right -12px}.x-panel-header-light-framed-bottom-bl{background-position:0 -16px}.x-panel-header-light-framed-bottom-br{background-position:right -20px}.x-panel-header-light-framed-bottom-ml{background-position:0 bottom}.x-panel-header-light-framed-bottom-mr{background-position:right bottom}.x-panel-header-light-framed-bottom-tc{background-position:0 0}.x-panel-header-light-framed-bottom-bc{background-position:0 -4px}.x-panel-header-light-framed-bottom-tr,.x-panel-header-light-framed-bottom-br,.x-panel-header-light-framed-bottom-mr{padding-right:4px}.x-panel-header-light-framed-bottom-tl,.x-panel-header-light-framed-bottom-bl,.x-panel-header-light-framed-bottom-ml{padding-left:4px}.x-panel-header-light-framed-bottom-tc{height:0}.x-panel-header-light-framed-bottom-bc{height:4px}.x-panel-header-light-framed-bottom-tl,.x-panel-header-light-framed-bottom-bl,.x-panel-header-light-framed-bottom-tr,.x-panel-header-light-framed-bottom-br,.x-panel-header-light-framed-bottom-tc,.x-panel-header-light-framed-bottom-bc,.x-panel-header-light-framed-bottom-ml,.x-panel-header-light-framed-bottom-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-bottom-corners.gif)}.x-panel-header-light-framed-bottom-ml,.x-panel-header-light-framed-bottom-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-bottom-sides.gif);background-repeat:repeat-y}.x-panel-header-light-framed-bottom-mc{padding:5px 3px 3px 3px}.x-strict .x-ie7 .x-panel-header-light-framed-bottom-tl,.x-strict .x-ie7 .x-panel-header-light-framed-bottom-bl{position:relative;right:0}.x-panel-header-light-framed-bottom:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-light-framed-bottom-corners.gif), sides:url(images/panel-header/panel-header-light-framed-bottom-sides.gif)"}.x-panel-header-light-framed-left{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:5px 5px 5px 5px;border-width:2px 0 2px 2px;border-style:solid;background-color:#e4e4e4}.x-panel-header-light-framed-left-mc{background-color:#e4e4e4}.x-nbr .x-panel-header-light-framed-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-light-framed-left-frameInfo{font-family:dh-4-0-0-4-2-0-2-2-5-5-5-5}.x-panel-header-light-framed-left-tl{background-position:0 -8px}.x-panel-header-light-framed-left-tr{background-position:right -12px}.x-panel-header-light-framed-left-bl{background-position:0 -16px}.x-panel-header-light-framed-left-br{background-position:right -20px}.x-panel-header-light-framed-left-ml{background-position:0 left}.x-panel-header-light-framed-left-mr{background-position:right left}.x-panel-header-light-framed-left-tc{background-position:0 0}.x-panel-header-light-framed-left-bc{background-position:0 -4px}.x-panel-header-light-framed-left-tr,.x-panel-header-light-framed-left-br,.x-panel-header-light-framed-left-mr{padding-right:0}.x-panel-header-light-framed-left-tl,.x-panel-header-light-framed-left-bl,.x-panel-header-light-framed-left-ml{padding-left:4px}.x-panel-header-light-framed-left-tc{height:4px}.x-panel-header-light-framed-left-bc{height:4px}.x-panel-header-light-framed-left-tl,.x-panel-header-light-framed-left-bl,.x-panel-header-light-framed-left-tr,.x-panel-header-light-framed-left-br,.x-panel-header-light-framed-left-tc,.x-panel-header-light-framed-left-bc,.x-panel-header-light-framed-left-ml,.x-panel-header-light-framed-left-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-left-corners.gif)}.x-panel-header-light-framed-left-ml,.x-panel-header-light-framed-left-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-left-sides.gif);background-repeat:repeat-y}.x-panel-header-light-framed-left-mc{padding:3px 5px 3px 3px}.x-strict .x-ie7 .x-panel-header-light-framed-left-tl,.x-strict .x-ie7 .x-panel-header-light-framed-left-bl{position:relative;right:0}.x-panel-header-light-framed-left:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-light-framed-left-corners.gif), sides:url(images/panel-header/panel-header-light-framed-left-sides.gif)"}.x-panel-header-light-framed-collapsed-top{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:5px 5px 5px 5px;border-width:2px;border-style:solid;background-color:#e4e4e4}.x-panel-header-light-framed-collapsed-top-mc{background-color:#e4e4e4}.x-nbr .x-panel-header-light-framed-collapsed-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-light-framed-collapsed-top-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-5-5-5-5}.x-panel-header-light-framed-collapsed-top-tl{background-position:0 -8px}.x-panel-header-light-framed-collapsed-top-tr{background-position:right -12px}.x-panel-header-light-framed-collapsed-top-bl{background-position:0 -16px}.x-panel-header-light-framed-collapsed-top-br{background-position:right -20px}.x-panel-header-light-framed-collapsed-top-ml{background-position:0 top}.x-panel-header-light-framed-collapsed-top-mr{background-position:right top}.x-panel-header-light-framed-collapsed-top-tc{background-position:0 0}.x-panel-header-light-framed-collapsed-top-bc{background-position:0 -4px}.x-panel-header-light-framed-collapsed-top-tr,.x-panel-header-light-framed-collapsed-top-br,.x-panel-header-light-framed-collapsed-top-mr{padding-right:4px}.x-panel-header-light-framed-collapsed-top-tl,.x-panel-header-light-framed-collapsed-top-bl,.x-panel-header-light-framed-collapsed-top-ml{padding-left:4px}.x-panel-header-light-framed-collapsed-top-tc{height:4px}.x-panel-header-light-framed-collapsed-top-bc{height:4px}.x-panel-header-light-framed-collapsed-top-tl,.x-panel-header-light-framed-collapsed-top-bl,.x-panel-header-light-framed-collapsed-top-tr,.x-panel-header-light-framed-collapsed-top-br,.x-panel-header-light-framed-collapsed-top-tc,.x-panel-header-light-framed-collapsed-top-bc,.x-panel-header-light-framed-collapsed-top-ml,.x-panel-header-light-framed-collapsed-top-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-collapsed-top-corners.gif)}.x-panel-header-light-framed-collapsed-top-ml,.x-panel-header-light-framed-collapsed-top-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-collapsed-top-sides.gif);background-repeat:repeat-y}.x-panel-header-light-framed-collapsed-top-mc{padding:3px 3px 3px 3px}.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-top-tl,.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-top-bl{position:relative;right:0}.x-panel-header-light-framed-collapsed-top:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-light-framed-collapsed-top-corners.gif), sides:url(images/panel-header/panel-header-light-framed-collapsed-top-sides.gif)"}.x-panel-header-light-framed-collapsed-right{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:5px 5px 5px 5px;border-width:2px;border-style:solid;background-color:#e4e4e4}.x-panel-header-light-framed-collapsed-right-mc{background-color:#e4e4e4}.x-nbr .x-panel-header-light-framed-collapsed-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-light-framed-collapsed-right-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-5-5-5-5}.x-panel-header-light-framed-collapsed-right-tl{background-position:0 -8px}.x-panel-header-light-framed-collapsed-right-tr{background-position:right -12px}.x-panel-header-light-framed-collapsed-right-bl{background-position:0 -16px}.x-panel-header-light-framed-collapsed-right-br{background-position:right -20px}.x-panel-header-light-framed-collapsed-right-ml{background-position:0 right}.x-panel-header-light-framed-collapsed-right-mr{background-position:right right}.x-panel-header-light-framed-collapsed-right-tc{background-position:0 0}.x-panel-header-light-framed-collapsed-right-bc{background-position:0 -4px}.x-panel-header-light-framed-collapsed-right-tr,.x-panel-header-light-framed-collapsed-right-br,.x-panel-header-light-framed-collapsed-right-mr{padding-right:4px}.x-panel-header-light-framed-collapsed-right-tl,.x-panel-header-light-framed-collapsed-right-bl,.x-panel-header-light-framed-collapsed-right-ml{padding-left:4px}.x-panel-header-light-framed-collapsed-right-tc{height:4px}.x-panel-header-light-framed-collapsed-right-bc{height:4px}.x-panel-header-light-framed-collapsed-right-tl,.x-panel-header-light-framed-collapsed-right-bl,.x-panel-header-light-framed-collapsed-right-tr,.x-panel-header-light-framed-collapsed-right-br,.x-panel-header-light-framed-collapsed-right-tc,.x-panel-header-light-framed-collapsed-right-bc,.x-panel-header-light-framed-collapsed-right-ml,.x-panel-header-light-framed-collapsed-right-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-collapsed-right-corners.gif)}.x-panel-header-light-framed-collapsed-right-ml,.x-panel-header-light-framed-collapsed-right-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-collapsed-right-sides.gif);background-repeat:repeat-y}.x-panel-header-light-framed-collapsed-right-mc{padding:3px 3px 3px 3px}.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-right-tl,.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-right-bl{position:relative;right:0}.x-panel-header-light-framed-collapsed-right:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-light-framed-collapsed-right-corners.gif), sides:url(images/panel-header/panel-header-light-framed-collapsed-right-sides.gif)"}.x-panel-header-light-framed-collapsed-bottom{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:5px 5px 5px 5px;border-width:2px;border-style:solid;background-color:#e4e4e4}.x-panel-header-light-framed-collapsed-bottom-mc{background-color:#e4e4e4}.x-nbr .x-panel-header-light-framed-collapsed-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-light-framed-collapsed-bottom-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-5-5-5-5}.x-panel-header-light-framed-collapsed-bottom-tl{background-position:0 -8px}.x-panel-header-light-framed-collapsed-bottom-tr{background-position:right -12px}.x-panel-header-light-framed-collapsed-bottom-bl{background-position:0 -16px}.x-panel-header-light-framed-collapsed-bottom-br{background-position:right -20px}.x-panel-header-light-framed-collapsed-bottom-ml{background-position:0 bottom}.x-panel-header-light-framed-collapsed-bottom-mr{background-position:right bottom}.x-panel-header-light-framed-collapsed-bottom-tc{background-position:0 0}.x-panel-header-light-framed-collapsed-bottom-bc{background-position:0 -4px}.x-panel-header-light-framed-collapsed-bottom-tr,.x-panel-header-light-framed-collapsed-bottom-br,.x-panel-header-light-framed-collapsed-bottom-mr{padding-right:4px}.x-panel-header-light-framed-collapsed-bottom-tl,.x-panel-header-light-framed-collapsed-bottom-bl,.x-panel-header-light-framed-collapsed-bottom-ml{padding-left:4px}.x-panel-header-light-framed-collapsed-bottom-tc{height:4px}.x-panel-header-light-framed-collapsed-bottom-bc{height:4px}.x-panel-header-light-framed-collapsed-bottom-tl,.x-panel-header-light-framed-collapsed-bottom-bl,.x-panel-header-light-framed-collapsed-bottom-tr,.x-panel-header-light-framed-collapsed-bottom-br,.x-panel-header-light-framed-collapsed-bottom-tc,.x-panel-header-light-framed-collapsed-bottom-bc,.x-panel-header-light-framed-collapsed-bottom-ml,.x-panel-header-light-framed-collapsed-bottom-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-collapsed-bottom-corners.gif)}.x-panel-header-light-framed-collapsed-bottom-ml,.x-panel-header-light-framed-collapsed-bottom-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-collapsed-bottom-sides.gif);background-repeat:repeat-y}.x-panel-header-light-framed-collapsed-bottom-mc{padding:3px 3px 3px 3px}.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-bottom-tl,.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-bottom-bl{position:relative;right:0}.x-panel-header-light-framed-collapsed-bottom:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-light-framed-collapsed-bottom-corners.gif), sides:url(images/panel-header/panel-header-light-framed-collapsed-bottom-sides.gif)"}.x-panel-header-light-framed-collapsed-left{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:5px 5px 5px 5px;border-width:2px;border-style:solid;background-color:#e4e4e4}.x-panel-header-light-framed-collapsed-left-mc{background-color:#e4e4e4}.x-nbr .x-panel-header-light-framed-collapsed-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-panel-header-light-framed-collapsed-left-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-5-5-5-5}.x-panel-header-light-framed-collapsed-left-tl{background-position:0 -8px}.x-panel-header-light-framed-collapsed-left-tr{background-position:right -12px}.x-panel-header-light-framed-collapsed-left-bl{background-position:0 -16px}.x-panel-header-light-framed-collapsed-left-br{background-position:right -20px}.x-panel-header-light-framed-collapsed-left-ml{background-position:0 left}.x-panel-header-light-framed-collapsed-left-mr{background-position:right left}.x-panel-header-light-framed-collapsed-left-tc{background-position:0 0}.x-panel-header-light-framed-collapsed-left-bc{background-position:0 -4px}.x-panel-header-light-framed-collapsed-left-tr,.x-panel-header-light-framed-collapsed-left-br,.x-panel-header-light-framed-collapsed-left-mr{padding-right:4px}.x-panel-header-light-framed-collapsed-left-tl,.x-panel-header-light-framed-collapsed-left-bl,.x-panel-header-light-framed-collapsed-left-ml{padding-left:4px}.x-panel-header-light-framed-collapsed-left-tc{height:4px}.x-panel-header-light-framed-collapsed-left-bc{height:4px}.x-panel-header-light-framed-collapsed-left-tl,.x-panel-header-light-framed-collapsed-left-bl,.x-panel-header-light-framed-collapsed-left-tr,.x-panel-header-light-framed-collapsed-left-br,.x-panel-header-light-framed-collapsed-left-tc,.x-panel-header-light-framed-collapsed-left-bc,.x-panel-header-light-framed-collapsed-left-ml,.x-panel-header-light-framed-collapsed-left-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-collapsed-left-corners.gif)}.x-panel-header-light-framed-collapsed-left-ml,.x-panel-header-light-framed-collapsed-left-mr{zoom:1;background-image:url(images/panel-header/panel-header-light-framed-collapsed-left-sides.gif);background-repeat:repeat-y}.x-panel-header-light-framed-collapsed-left-mc{padding:3px 3px 3px 3px}.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-left-tl,.x-strict .x-ie7 .x-panel-header-light-framed-collapsed-left-bl{position:relative;right:0}.x-panel-header-light-framed-collapsed-left:after{display:none;content:"x-slicer:corners:url(images/panel-header/panel-header-light-framed-collapsed-left-corners.gif), sides:url(images/panel-header/panel-header-light-framed-collapsed-left-sides.gif)"}.x-panel .x-panel-header-light-framed-top{border-bottom-width:2px!important}.x-panel .x-panel-header-light-framed-right{border-left-width:2px!important}.x-panel .x-panel-header-light-framed-bottom{border-top-width:2px!important}.x-panel .x-panel-header-light-framed-left{border-right-width:2px!important}.x-nbr .x-panel-header-light-framed-collapsed-top{border-bottom-width:0!important}.x-nbr .x-panel-header-light-framed-collapsed-right{border-left-width:0!important}.x-nbr .x-panel-header-light-framed-collapsed-bottom{border-top-width:0!important}.x-nbr .x-panel-header-light-framed-collapsed-left{border-right-width:0!important}.x-panel-header-light-framed-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-light-framed-vertical .x-panel-header-text-container{background-color:#e4e4e4;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#e4e4e4)}.x-panel-header-light-framed .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-light-framed .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-light-framed .x-panel-header-glyph{color:#f1f1f1}.x-panel-header-light-framed-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-light-framed-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-light-framed-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-light-framed-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-light-framed-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-light-framed-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-light-framed-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-light-framed-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-light-framed-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-light-framed-outer-border-l{border-left-color:#e4e4e4!important;border-left-width:2px!important}.x-panel-light-framed-outer-border-b{border-bottom-color:#e4e4e4!important;border-bottom-width:2px!important}.x-panel-light-framed-outer-border-bl{border-bottom-color:#e4e4e4!important;border-bottom-width:2px!important;border-left-color:#e4e4e4!important;border-left-width:2px!important}.x-panel-light-framed-outer-border-r{border-right-color:#e4e4e4!important;border-right-width:2px!important}.x-panel-light-framed-outer-border-rl{border-right-color:#e4e4e4!important;border-right-width:2px!important;border-left-color:#e4e4e4!important;border-left-width:2px!important}.x-panel-light-framed-outer-border-rb{border-right-color:#e4e4e4!important;border-right-width:2px!important;border-bottom-color:#e4e4e4!important;border-bottom-width:2px!important}.x-panel-light-framed-outer-border-rbl{border-right-color:#e4e4e4!important;border-right-width:2px!important;border-bottom-color:#e4e4e4!important;border-bottom-width:2px!important;border-left-color:#e4e4e4!important;border-left-width:2px!important}.x-panel-light-framed-outer-border-t{border-top-color:#e4e4e4!important;border-top-width:2px!important}.x-panel-light-framed-outer-border-tl{border-top-color:#e4e4e4!important;border-top-width:2px!important;border-left-color:#e4e4e4!important;border-left-width:2px!important}.x-panel-light-framed-outer-border-tb{border-top-color:#e4e4e4!important;border-top-width:2px!important;border-bottom-color:#e4e4e4!important;border-bottom-width:2px!important}.x-panel-light-framed-outer-border-tbl{border-top-color:#e4e4e4!important;border-top-width:2px!important;border-bottom-color:#e4e4e4!important;border-bottom-width:2px!important;border-left-color:#e4e4e4!important;border-left-width:2px!important}.x-panel-light-framed-outer-border-tr{border-top-color:#e4e4e4!important;border-top-width:2px!important;border-right-color:#e4e4e4!important;border-right-width:2px!important}.x-panel-light-framed-outer-border-trl{border-top-color:#e4e4e4!important;border-top-width:2px!important;border-right-color:#e4e4e4!important;border-right-width:2px!important;border-left-color:#e4e4e4!important;border-left-width:2px!important}.x-panel-light-framed-outer-border-trb{border-top-color:#e4e4e4!important;border-top-width:2px!important;border-right-color:#e4e4e4!important;border-right-width:2px!important;border-bottom-color:#e4e4e4!important;border-bottom-width:2px!important}.x-panel-light-framed-outer-border-trbl{border-color:#e4e4e4!important;border-width:2px!important}.x-grid-header-ct{border:1px solid silver}.x-column-header-trigger{background-image:url(images/grid/hd-pop.png);border-left:1px solid silver}.x-column-header-last{border-right-width:0}.x-column-header-last .x-column-header-over .x-column-header-trigger{border-right:1px solid silver}.x-form-trigger{height:22px}.x-form-trigger-wrap{border:1px solid;border-color:silver #d9d9d9 #d9d9d9}.x-form-trigger-wrap .x-form-text{border-width:0;height:22px}.x-content-box .x-form-trigger-wrap .x-form-text{height:15px}.x-form-trigger-wrap-focus .x-form-trigger-wrap{border-color:#96caee!important}.x-form-invalid .x-form-trigger-wrap{border-color:#cf4c35}.x-form-file-wrap .x-form-trigger-wrap{border:0}.x-form-file-wrap .x-form-trigger-wrap .x-form-text{border:1px solid;border-color:silver #d9d9d9 #d9d9d9;height:24px}.x-content-box .x-form-file-wrap .x-form-trigger-wrap .x-form-text{height:15px}.x-html-editor-container{border:1px solid;border-color:silver #d9d9d9 #d9d9d9}.x-accordion-hd .x-tool-img{background-image:url(images/tools/tool-sprites-dark.png)}.x-accordion-item .x-accordion-hd-over{background-color:#ebebeb}.x-resizable-handle{background-color:#444;background-repeat:no-repeat}.x-resizable-handle-east-over,.x-resizable-handle-west-over{background-position:center}.x-resizable-handle-south-over,.x-resizable-handle-north-over{background-position:center}.x-resizable-handle-southeast-over{background-position:-2px -2px}.x-resizable-handle-northwest-over{background-position:2px 2px}.x-resizable-handle-northeast-over{background-position:-2px 2px}.x-resizable-handle-southwest-over{background-position:2px -2px}.x-resizable-pinned .x-resizable-handle-east,.x-resizable-pinned .x-resizable-handle-west{background-position:center}.x-resizable-pinned .x-resizable-handle-south,.x-resizable-pinned .x-resizable-handle-north{background-position:center}.x-resizable-pinned .x-resizable-handle-southeast{background-position:-2px -2px}.x-resizable-pinned .x-resizable-handle-northwest{background-position:2px 2px}.x-resizable-pinned .x-resizable-handle-northeast{background-position:-2px 2px}.x-resizable-pinned .x-resizable-handle-southwest{background-position:2px -2px}.nx-inset,.nx-coreui-component-details{padding:12px 12px 12px 12px}.nx-subsection{padding:12px 12px 12px 12px;background-color:white;border-top:1px solid #ddd!important;border-right:1px solid #ddd!important;border-bottom:1px solid #ddd!important;border-left:1px solid #ddd!important}.nx-form-important-msg{font-size:10px;font-weight:bold}.x-btn-default-small{border-color:#333}.x-btn-default-small{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:#606060;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-default-small-mc{background-image:url(images/btn/btn-default-small-fbg.gif);background-position:0 top;background-color:#606060}.x-nlg .x-btn-default-small{background-image:url(images/btn/btn-default-small-bg.gif);background-position:0 top}.x-nbr .x-btn-default-small{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-default-small-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-default-small-tl{background-position:0 -6px}.x-btn-default-small-tr{background-position:right -9px}.x-btn-default-small-bl{background-position:0 -12px}.x-btn-default-small-br{background-position:right -15px}.x-btn-default-small-ml{background-position:0 top}.x-btn-default-small-mr{background-position:right top}.x-btn-default-small-tc{background-position:0 0}.x-btn-default-small-bc{background-position:0 -3px}.x-btn-default-small-tr,.x-btn-default-small-br,.x-btn-default-small-mr{padding-right:3px}.x-btn-default-small-tl,.x-btn-default-small-bl,.x-btn-default-small-ml{padding-left:3px}.x-btn-default-small-tc{height:3px}.x-btn-default-small-bc{height:3px}.x-btn-default-small-tl,.x-btn-default-small-bl,.x-btn-default-small-tr,.x-btn-default-small-br,.x-btn-default-small-tc,.x-btn-default-small-bc,.x-btn-default-small-ml,.x-btn-default-small-mr{zoom:1;background-image:url(images/btn/btn-default-small-corners.gif)}.x-btn-default-small-ml,.x-btn-default-small-mr{zoom:1;background-image:url(images/btn/btn-default-small-sides.gif)}.x-btn-default-small-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-default-small-tl,.x-strict .x-ie7 .x-btn-default-small-bl{position:relative;right:0}.x-btn-default-small:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-default-small-fbg.gif), bg:url(images/btn/btn-default-small-bg.gif), corners:url(images/btn/btn-default-small-corners.gif), sides:url(images/btn/btn-default-small-sides.gif), stretch:bottom, frame-bg:url(images/btn/btn-default-small-fbg.gif), bg:url(images/btn/btn-default-small-bg.gif), corners:url(images/btn/btn-default-small-corners.gif), sides:url(images/btn/btn-default-small-sides.gif)"}.x-btn-default-small .x-btn-inner{font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#333;padding:0 5px}.x-btn-default-small .x-btn-arrow{background-image:url(images/button/default-small-arrow.png)}.x-btn-default-small .x-btn-arrow-right{padding-right:21px}.x-btn-default-small .x-btn-arrow-bottom{padding-bottom:18px}.x-btn-default-small .x-btn-glyph{font-size:16px;line-height:16px;color:#333}.x-ie8m .x-btn-default-small .x-btn-glyph{color:#333}.x-btn-default-small-disabled{background-image:none;background-color:#444;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-default-small-disabled .x-btn-inner{color:#777}.x-btn-default-small-icon .x-btn-button,.x-btn-default-small-noicon .x-btn-button{height:16px}.x-btn-default-small-icon .x-btn-inner,.x-btn-default-small-noicon .x-btn-inner{line-height:16px}.x-btn-default-small-icon .x-btn-arrow-right .x-btn-inner,.x-btn-default-small-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-default-small-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-default-small-icon .x-btn-inner{width:16px;padding:0}.x-btn-default-small-icon .x-btn-icon-el{width:16px;height:16px}.x-btn-default-small-icon-text-left .x-btn-button{height:16px}.x-btn-default-small-icon-text-left .x-btn-inner{line-height:16px;padding-left:21px}.x-btn-default-small-icon-text-left .x-btn-icon-el{width:16px;right:auto}.x-ie6 .x-btn-default-small-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-small-icon-text-left .x-btn-icon-el{height:16px}.x-btn-default-small-icon-text-right .x-btn-button{height:16px}.x-btn-default-small-icon-text-right .x-btn-inner{line-height:16px;padding-right:21px}.x-btn-default-small-icon-text-right .x-btn-icon-el{width:16px;left:auto}.x-ie6 .x-btn-default-small-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-small-icon-text-right .x-btn-icon-el{height:16px}.x-btn-default-small-icon-text-top .x-btn-inner{padding-top:21px}.x-btn-default-small-icon-text-top .x-btn-icon-el{height:16px;bottom:auto}.x-ie6 .x-btn-default-small-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-small-icon-text-top .x-btn-icon-el{width:100%}.x-btn-default-small-icon-text-bottom .x-btn-inner{padding-bottom:21px}.x-btn-default-small-icon-text-bottom .x-btn-icon-el{height:16px;top:auto}.x-ie6 .x-btn-default-small-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-default-small-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-default-small-focus{border-color:#96caee;background-image:none;background-color:#3e3e3e;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-default-small-over{background-image:none;background-color:#3e3e3e;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#cbcbcb),color-stop(100%,#b8b8b8));background-image:-webkit-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-moz-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-o-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-ms-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:linear-gradient(top,#cbcbcb,#b8b8b8)}.x-btn-default-small-menu-active,.x-btn-default-small-pressed{background-image:none;background-color:#333;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#919191),color-stop(100%,#808080));background-image:-webkit-linear-gradient(top,#919191,#808080);background-image:-moz-linear-gradient(top,#919191,#808080);background-image:-o-linear-gradient(top,#919191,#808080);background-image:-ms-linear-gradient(top,#919191,#808080);background-image:linear-gradient(top,#919191,#808080)}.x-btn-default-small-menu-active .x-btn-inner,.x-btn-default-small-pressed .x-btn-inner{color:white}.x-btn-default-small-focus .x-frame-tl,.x-btn-default-small-focus .x-frame-bl,.x-btn-default-small-focus .x-frame-tr,.x-btn-default-small-focus .x-frame-br,.x-btn-default-small-focus .x-frame-tc,.x-btn-default-small-focus .x-frame-bc{background-image:url(images/btn/btn-default-small-focus-corners.gif)}.x-btn-default-small-focus .x-frame-ml,.x-btn-default-small-focus .x-frame-mr{background-image:url(images/btn/btn-default-small-focus-sides.gif)}.x-btn-default-small-focus .x-frame-mc{background-color:#3e3e3e;background-image:url(images/btn/btn-default-small-focus-fbg.gif)}.x-btn-default-small-over .x-frame-tl,.x-btn-default-small-over .x-frame-bl,.x-btn-default-small-over .x-frame-tr,.x-btn-default-small-over .x-frame-br,.x-btn-default-small-over .x-frame-tc,.x-btn-default-small-over .x-frame-bc{background-image:url(images/btn/btn-default-small-over-corners.gif)}.x-btn-default-small-over .x-frame-ml,.x-btn-default-small-over .x-frame-mr{background-image:url(images/btn/btn-default-small-over-sides.gif)}.x-btn-default-small-over .x-frame-mc{background-color:#3e3e3e;background-image:url(images/btn/btn-default-small-over-fbg.gif)}.x-btn-default-small-menu-active .x-frame-tl,.x-btn-default-small-menu-active .x-frame-bl,.x-btn-default-small-menu-active .x-frame-tr,.x-btn-default-small-menu-active .x-frame-br,.x-btn-default-small-menu-active .x-frame-tc,.x-btn-default-small-menu-active .x-frame-bc,.x-btn-default-small-pressed .x-frame-tl,.x-btn-default-small-pressed .x-frame-bl,.x-btn-default-small-pressed .x-frame-tr,.x-btn-default-small-pressed .x-frame-br,.x-btn-default-small-pressed .x-frame-tc,.x-btn-default-small-pressed .x-frame-bc{background-image:url(images/btn/btn-default-small-pressed-corners.gif)}.x-btn-default-small-menu-active .x-frame-ml,.x-btn-default-small-menu-active .x-frame-mr,.x-btn-default-small-pressed .x-frame-ml,.x-btn-default-small-pressed .x-frame-mr{background-image:url(images/btn/btn-default-small-pressed-sides.gif)}.x-btn-default-small-menu-active .x-frame-mc,.x-btn-default-small-pressed .x-frame-mc{background-color:#333;background-image:url(images/btn/btn-default-small-pressed-fbg.gif)}.x-btn-default-small-disabled .x-frame-tl,.x-btn-default-small-disabled .x-frame-bl,.x-btn-default-small-disabled .x-frame-tr,.x-btn-default-small-disabled .x-frame-br,.x-btn-default-small-disabled .x-frame-tc,.x-btn-default-small-disabled .x-frame-bc{background-image:url(images/btn/btn-default-small-disabled-corners.gif)}.x-btn-default-small-disabled .x-frame-ml,.x-btn-default-small-disabled .x-frame-mr{background-image:url(images/btn/btn-default-small-disabled-sides.gif)}.x-btn-default-small-disabled .x-frame-mc{background-color:#444;background-image:url(images/btn/btn-default-small-disabled-fbg.gif)}.x-nlg .x-btn-default-small-focus{background-image:url(images/btn/btn-default-small-focus-bg.gif)}.x-nlg .x-btn-default-small-over{background-image:url(images/btn/btn-default-small-over-bg.gif)}.x-nlg .x-btn-default-small-menu-active,.x-nlg .x-btn-default-small-pressed{background-image:url(images/btn/btn-default-small-pressed-bg.gif)}.x-nlg .x-btn-default-small-disabled{background-image:url(images/btn/btn-default-small-disabled-bg.gif)}.x-nbr .x-btn-default-small{background-image:none}.x-btn-default-small .x-btn-split-right{background-image:url(images/button/default-small-s-arrow.png);padding-right:23px}.x-btn-default-small .x-btn-split-bottom{background-image:url(images/button/default-small-s-arrow-b.png);padding-bottom:20px}.x-btn-default-small-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-default-small-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-focus-corners.gif), sides:url(images/btn/btn-default-small-focus-sides.gif), frame-bg:url(images/btn/btn-default-small-focus-fbg.gif), bg:url(images/btn/btn-default-small-focus-bg.gif), stretch:bottom, corners:url(images/btn/btn-default-small-focus-corners.gif), sides:url(images/btn/btn-default-small-focus-sides.gif), frame-bg:url(images/btn/btn-default-small-focus-fbg.gif), bg:url(images/btn/btn-default-small-focus-bg.gif)"}.x-btn-default-small-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-over-corners.gif), sides:url(images/btn/btn-default-small-over-sides.gif), frame-bg:url(images/btn/btn-default-small-over-fbg.gif), bg:url(images/btn/btn-default-small-over-bg.gif), stretch:bottom, corners:url(images/btn/btn-default-small-over-corners.gif), sides:url(images/btn/btn-default-small-over-sides.gif), frame-bg:url(images/btn/btn-default-small-over-fbg.gif), bg:url(images/btn/btn-default-small-over-bg.gif)"}.x-btn-default-small-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-pressed-corners.gif), sides:url(images/btn/btn-default-small-pressed-sides.gif), frame-bg:url(images/btn/btn-default-small-pressed-fbg.gif), bg:url(images/btn/btn-default-small-pressed-bg.gif), stretch:bottom, corners:url(images/btn/btn-default-small-pressed-corners.gif), sides:url(images/btn/btn-default-small-pressed-sides.gif), frame-bg:url(images/btn/btn-default-small-pressed-fbg.gif), bg:url(images/btn/btn-default-small-pressed-bg.gif)"}.x-btn-default-small-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-default-small-disabled-corners.gif), sides:url(images/btn/btn-default-small-disabled-sides.gif), frame-bg:url(images/btn/btn-default-small-disabled-fbg.gif), bg:url(images/btn/btn-default-small-disabled-bg.gif), stretch:bottom, corners:url(images/btn/btn-default-small-disabled-corners.gif), sides:url(images/btn/btn-default-small-disabled-sides.gif), frame-bg:url(images/btn/btn-default-small-disabled-fbg.gif), bg:url(images/btn/btn-default-small-disabled-bg.gif)"}.x-btn-nx-plain-small{border-color:#ddd}.x-btn-nx-plain-small{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:white;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-nx-plain-small-mc{background-image:url(images/btn/btn-nx-plain-small-fbg.gif);background-position:0 top;background-color:white}.x-nlg .x-btn-nx-plain-small{background-image:url(images/btn/btn-nx-plain-small-bg.gif);background-position:0 top}.x-nbr .x-btn-nx-plain-small{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-nx-plain-small-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-nx-plain-small-tl{background-position:0 -6px}.x-btn-nx-plain-small-tr{background-position:right -9px}.x-btn-nx-plain-small-bl{background-position:0 -12px}.x-btn-nx-plain-small-br{background-position:right -15px}.x-btn-nx-plain-small-ml{background-position:0 top}.x-btn-nx-plain-small-mr{background-position:right top}.x-btn-nx-plain-small-tc{background-position:0 0}.x-btn-nx-plain-small-bc{background-position:0 -3px}.x-btn-nx-plain-small-tr,.x-btn-nx-plain-small-br,.x-btn-nx-plain-small-mr{padding-right:3px}.x-btn-nx-plain-small-tl,.x-btn-nx-plain-small-bl,.x-btn-nx-plain-small-ml{padding-left:3px}.x-btn-nx-plain-small-tc{height:3px}.x-btn-nx-plain-small-bc{height:3px}.x-btn-nx-plain-small-tl,.x-btn-nx-plain-small-bl,.x-btn-nx-plain-small-tr,.x-btn-nx-plain-small-br,.x-btn-nx-plain-small-tc,.x-btn-nx-plain-small-bc,.x-btn-nx-plain-small-ml,.x-btn-nx-plain-small-mr{zoom:1;background-image:url(images/btn/btn-nx-plain-small-corners.gif)}.x-btn-nx-plain-small-ml,.x-btn-nx-plain-small-mr{zoom:1;background-image:url(images/btn/btn-nx-plain-small-sides.gif)}.x-btn-nx-plain-small-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-nx-plain-small-tl,.x-strict .x-ie7 .x-btn-nx-plain-small-bl{position:relative;right:0}.x-btn-nx-plain-small:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-plain-small-fbg.gif), bg:url(images/btn/btn-nx-plain-small-bg.gif), corners:url(images/btn/btn-nx-plain-small-corners.gif), sides:url(images/btn/btn-nx-plain-small-sides.gif)"}.x-btn-nx-plain-small .x-btn-inner{font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#333;padding:0 5px}.x-btn-nx-plain-small .x-btn-arrow{background-image:url(images/button/nx-plain-small-arrow.png)}.x-btn-nx-plain-small .x-btn-arrow-right{padding-right:21px}.x-btn-nx-plain-small .x-btn-arrow-bottom{padding-bottom:18px}.x-btn-nx-plain-small .x-btn-glyph{font-size:16px;line-height:16px;color:#333}.x-ie8m .x-btn-nx-plain-small .x-btn-glyph{color:#333}.x-btn-nx-plain-small-disabled{background-image:none;background-color:white;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-nx-plain-small-disabled .x-btn-inner{color:#777}.x-btn-nx-plain-small-icon .x-btn-button,.x-btn-nx-plain-small-noicon .x-btn-button{height:16px}.x-btn-nx-plain-small-icon .x-btn-inner,.x-btn-nx-plain-small-noicon .x-btn-inner{line-height:16px}.x-btn-nx-plain-small-icon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-plain-small-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-plain-small-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-nx-plain-small-icon .x-btn-inner{width:16px;padding:0}.x-btn-nx-plain-small-icon .x-btn-icon-el{width:16px;height:16px}.x-btn-nx-plain-small-icon-text-left .x-btn-button{height:16px}.x-btn-nx-plain-small-icon-text-left .x-btn-inner{line-height:16px;padding-left:21px}.x-btn-nx-plain-small-icon-text-left .x-btn-icon-el{width:16px;right:auto}.x-ie6 .x-btn-nx-plain-small-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-plain-small-icon-text-left .x-btn-icon-el{height:16px}.x-btn-nx-plain-small-icon-text-right .x-btn-button{height:16px}.x-btn-nx-plain-small-icon-text-right .x-btn-inner{line-height:16px;padding-right:21px}.x-btn-nx-plain-small-icon-text-right .x-btn-icon-el{width:16px;left:auto}.x-ie6 .x-btn-nx-plain-small-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-plain-small-icon-text-right .x-btn-icon-el{height:16px}.x-btn-nx-plain-small-icon-text-top .x-btn-inner{padding-top:21px}.x-btn-nx-plain-small-icon-text-top .x-btn-icon-el{height:16px;bottom:auto}.x-ie6 .x-btn-nx-plain-small-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-plain-small-icon-text-top .x-btn-icon-el{width:100%}.x-btn-nx-plain-small-icon-text-bottom .x-btn-inner{padding-bottom:21px}.x-btn-nx-plain-small-icon-text-bottom .x-btn-icon-el{height:16px;top:auto}.x-ie6 .x-btn-nx-plain-small-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-plain-small-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-nx-plain-small-focus{border-color:#cbcbcb;background-image:none;background-color:white;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6)}.x-btn-nx-plain-small-over{background-image:none;background-color:#cbcbcb;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#cbcbcb),color-stop(100%,#b8b8b8));background-image:-webkit-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-moz-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-o-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:-ms-linear-gradient(top,#cbcbcb,#b8b8b8);background-image:linear-gradient(top,#cbcbcb,#b8b8b8)}.x-btn-nx-plain-small-menu-active,.x-btn-nx-plain-small-pressed{background-image:none;background-color:#919191;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#919191),color-stop(100%,#808080));background-image:-webkit-linear-gradient(top,#919191,#808080);background-image:-moz-linear-gradient(top,#919191,#808080);background-image:-o-linear-gradient(top,#919191,#808080);background-image:-ms-linear-gradient(top,#919191,#808080);background-image:linear-gradient(top,#919191,#808080)}.x-btn-nx-plain-small-menu-active .x-btn-inner,.x-btn-nx-plain-small-pressed .x-btn-inner{color:white}.x-btn-nx-plain-small-focus .x-frame-tl,.x-btn-nx-plain-small-focus .x-frame-bl,.x-btn-nx-plain-small-focus .x-frame-tr,.x-btn-nx-plain-small-focus .x-frame-br,.x-btn-nx-plain-small-focus .x-frame-tc,.x-btn-nx-plain-small-focus .x-frame-bc{background-image:url(images/btn/btn-nx-plain-small-focus-corners.gif)}.x-btn-nx-plain-small-focus .x-frame-ml,.x-btn-nx-plain-small-focus .x-frame-mr{background-image:url(images/btn/btn-nx-plain-small-focus-sides.gif)}.x-btn-nx-plain-small-focus .x-frame-mc{background-color:white;background-image:url(images/btn/btn-nx-plain-small-focus-fbg.gif)}.x-btn-nx-plain-small-over .x-frame-tl,.x-btn-nx-plain-small-over .x-frame-bl,.x-btn-nx-plain-small-over .x-frame-tr,.x-btn-nx-plain-small-over .x-frame-br,.x-btn-nx-plain-small-over .x-frame-tc,.x-btn-nx-plain-small-over .x-frame-bc{background-image:url(images/btn/btn-nx-plain-small-over-corners.gif)}.x-btn-nx-plain-small-over .x-frame-ml,.x-btn-nx-plain-small-over .x-frame-mr{background-image:url(images/btn/btn-nx-plain-small-over-sides.gif)}.x-btn-nx-plain-small-over .x-frame-mc{background-color:#cbcbcb;background-image:url(images/btn/btn-nx-plain-small-over-fbg.gif)}.x-btn-nx-plain-small-menu-active .x-frame-tl,.x-btn-nx-plain-small-menu-active .x-frame-bl,.x-btn-nx-plain-small-menu-active .x-frame-tr,.x-btn-nx-plain-small-menu-active .x-frame-br,.x-btn-nx-plain-small-menu-active .x-frame-tc,.x-btn-nx-plain-small-menu-active .x-frame-bc,.x-btn-nx-plain-small-pressed .x-frame-tl,.x-btn-nx-plain-small-pressed .x-frame-bl,.x-btn-nx-plain-small-pressed .x-frame-tr,.x-btn-nx-plain-small-pressed .x-frame-br,.x-btn-nx-plain-small-pressed .x-frame-tc,.x-btn-nx-plain-small-pressed .x-frame-bc{background-image:url(images/btn/btn-nx-plain-small-pressed-corners.gif)}.x-btn-nx-plain-small-menu-active .x-frame-ml,.x-btn-nx-plain-small-menu-active .x-frame-mr,.x-btn-nx-plain-small-pressed .x-frame-ml,.x-btn-nx-plain-small-pressed .x-frame-mr{background-image:url(images/btn/btn-nx-plain-small-pressed-sides.gif)}.x-btn-nx-plain-small-menu-active .x-frame-mc,.x-btn-nx-plain-small-pressed .x-frame-mc{background-color:#919191;background-image:url(images/btn/btn-nx-plain-small-pressed-fbg.gif)}.x-btn-nx-plain-small-disabled .x-frame-tl,.x-btn-nx-plain-small-disabled .x-frame-bl,.x-btn-nx-plain-small-disabled .x-frame-tr,.x-btn-nx-plain-small-disabled .x-frame-br,.x-btn-nx-plain-small-disabled .x-frame-tc,.x-btn-nx-plain-small-disabled .x-frame-bc{background-image:url(images/btn/btn-nx-plain-small-disabled-corners.gif)}.x-btn-nx-plain-small-disabled .x-frame-ml,.x-btn-nx-plain-small-disabled .x-frame-mr{background-image:url(images/btn/btn-nx-plain-small-disabled-sides.gif)}.x-btn-nx-plain-small-disabled .x-frame-mc{background-color:white;background-image:url(images/btn/btn-nx-plain-small-disabled-fbg.gif)}.x-nlg .x-btn-nx-plain-small-focus{background-image:url(images/btn/btn-nx-plain-small-focus-bg.gif)}.x-nlg .x-btn-nx-plain-small-over{background-image:url(images/btn/btn-nx-plain-small-over-bg.gif)}.x-nlg .x-btn-nx-plain-small-menu-active,.x-nlg .x-btn-nx-plain-small-pressed{background-image:url(images/btn/btn-nx-plain-small-pressed-bg.gif)}.x-nlg .x-btn-nx-plain-small-disabled{background-image:url(images/btn/btn-nx-plain-small-disabled-bg.gif)}.x-nbr .x-btn-nx-plain-small{background-image:none}.x-btn-nx-plain-small .x-btn-split-right{background-image:url(images/button/nx-plain-small-s-arrow.png);padding-right:23px}.x-btn-nx-plain-small .x-btn-split-bottom{background-image:url(images/button/nx-plain-small-s-arrow-b.png);padding-bottom:20px}.x-btn-nx-plain-small-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-nx-plain-small-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-plain-small-focus-corners.gif), sides:url(images/btn/btn-nx-plain-small-focus-sides.gif), frame-bg:url(images/btn/btn-nx-plain-small-focus-fbg.gif), bg:url(images/btn/btn-nx-plain-small-focus-bg.gif)"}.x-btn-nx-plain-small-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-plain-small-over-corners.gif), sides:url(images/btn/btn-nx-plain-small-over-sides.gif), frame-bg:url(images/btn/btn-nx-plain-small-over-fbg.gif), bg:url(images/btn/btn-nx-plain-small-over-bg.gif)"}.x-btn-nx-plain-small-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-plain-small-pressed-corners.gif), sides:url(images/btn/btn-nx-plain-small-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-plain-small-pressed-fbg.gif), bg:url(images/btn/btn-nx-plain-small-pressed-bg.gif)"}.x-btn-nx-plain-small-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-plain-small-disabled-corners.gif), sides:url(images/btn/btn-nx-plain-small-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-plain-small-disabled-fbg.gif), bg:url(images/btn/btn-nx-plain-small-disabled-bg.gif)"}.x-btn-nx-primary-small{border-color:#333}.x-btn-nx-primary-small{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:#197ac5;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#197ac5),color-stop(100%,#0161ad));background-image:-webkit-linear-gradient(top,#197ac5,#0161ad);background-image:-moz-linear-gradient(top,#197ac5,#0161ad);background-image:-o-linear-gradient(top,#197ac5,#0161ad);background-image:-ms-linear-gradient(top,#197ac5,#0161ad);background-image:linear-gradient(top,#197ac5,#0161ad)}.x-btn-nx-primary-small-mc{background-image:url(images/btn/btn-nx-primary-small-fbg.gif);background-position:0 top;background-color:#197ac5}.x-nlg .x-btn-nx-primary-small{background-image:url(images/btn/btn-nx-primary-small-bg.gif);background-position:0 top}.x-nbr .x-btn-nx-primary-small{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-nx-primary-small-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-nx-primary-small-tl{background-position:0 -6px}.x-btn-nx-primary-small-tr{background-position:right -9px}.x-btn-nx-primary-small-bl{background-position:0 -12px}.x-btn-nx-primary-small-br{background-position:right -15px}.x-btn-nx-primary-small-ml{background-position:0 top}.x-btn-nx-primary-small-mr{background-position:right top}.x-btn-nx-primary-small-tc{background-position:0 0}.x-btn-nx-primary-small-bc{background-position:0 -3px}.x-btn-nx-primary-small-tr,.x-btn-nx-primary-small-br,.x-btn-nx-primary-small-mr{padding-right:3px}.x-btn-nx-primary-small-tl,.x-btn-nx-primary-small-bl,.x-btn-nx-primary-small-ml{padding-left:3px}.x-btn-nx-primary-small-tc{height:3px}.x-btn-nx-primary-small-bc{height:3px}.x-btn-nx-primary-small-tl,.x-btn-nx-primary-small-bl,.x-btn-nx-primary-small-tr,.x-btn-nx-primary-small-br,.x-btn-nx-primary-small-tc,.x-btn-nx-primary-small-bc,.x-btn-nx-primary-small-ml,.x-btn-nx-primary-small-mr{zoom:1;background-image:url(images/btn/btn-nx-primary-small-corners.gif)}.x-btn-nx-primary-small-ml,.x-btn-nx-primary-small-mr{zoom:1;background-image:url(images/btn/btn-nx-primary-small-sides.gif)}.x-btn-nx-primary-small-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-nx-primary-small-tl,.x-strict .x-ie7 .x-btn-nx-primary-small-bl{position:relative;right:0}.x-btn-nx-primary-small:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-primary-small-fbg.gif), bg:url(images/btn/btn-nx-primary-small-bg.gif), corners:url(images/btn/btn-nx-primary-small-corners.gif), sides:url(images/btn/btn-nx-primary-small-sides.gif)"}.x-btn-nx-primary-small .x-btn-inner{font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:white;padding:0 5px}.x-btn-nx-primary-small .x-btn-arrow{background-image:url(images/button/nx-primary-small-arrow.png)}.x-btn-nx-primary-small .x-btn-arrow-right{padding-right:21px}.x-btn-nx-primary-small .x-btn-arrow-bottom{padding-bottom:18px}.x-btn-nx-primary-small .x-btn-glyph{font-size:16px;line-height:16px;color:white}.x-ie8m .x-btn-nx-primary-small .x-btn-glyph{color:white}.x-btn-nx-primary-small-disabled{background-image:none;background-color:#197ac5;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#197ac5),color-stop(100%,#0161ad));background-image:-webkit-linear-gradient(top,#197ac5,#0161ad);background-image:-moz-linear-gradient(top,#197ac5,#0161ad);background-image:-o-linear-gradient(top,#197ac5,#0161ad);background-image:-ms-linear-gradient(top,#197ac5,#0161ad);background-image:linear-gradient(top,#197ac5,#0161ad)}.x-btn-nx-primary-small-icon .x-btn-button,.x-btn-nx-primary-small-noicon .x-btn-button{height:16px}.x-btn-nx-primary-small-icon .x-btn-inner,.x-btn-nx-primary-small-noicon .x-btn-inner{line-height:16px}.x-btn-nx-primary-small-icon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-primary-small-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-primary-small-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-nx-primary-small-icon .x-btn-inner{width:16px;padding:0}.x-btn-nx-primary-small-icon .x-btn-icon-el{width:16px;height:16px}.x-btn-nx-primary-small-icon-text-left .x-btn-button{height:16px}.x-btn-nx-primary-small-icon-text-left .x-btn-inner{line-height:16px;padding-left:21px}.x-btn-nx-primary-small-icon-text-left .x-btn-icon-el{width:16px;right:auto}.x-ie6 .x-btn-nx-primary-small-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-primary-small-icon-text-left .x-btn-icon-el{height:16px}.x-btn-nx-primary-small-icon-text-right .x-btn-button{height:16px}.x-btn-nx-primary-small-icon-text-right .x-btn-inner{line-height:16px;padding-right:21px}.x-btn-nx-primary-small-icon-text-right .x-btn-icon-el{width:16px;left:auto}.x-ie6 .x-btn-nx-primary-small-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-primary-small-icon-text-right .x-btn-icon-el{height:16px}.x-btn-nx-primary-small-icon-text-top .x-btn-inner{padding-top:21px}.x-btn-nx-primary-small-icon-text-top .x-btn-icon-el{height:16px;bottom:auto}.x-ie6 .x-btn-nx-primary-small-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-primary-small-icon-text-top .x-btn-icon-el{width:100%}.x-btn-nx-primary-small-icon-text-bottom .x-btn-inner{padding-bottom:21px}.x-btn-nx-primary-small-icon-text-bottom .x-btn-icon-el{height:16px;top:auto}.x-ie6 .x-btn-nx-primary-small-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-primary-small-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-nx-primary-small-focus{border-color:#96caee;background-image:none;background-color:#197ac5;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#197ac5),color-stop(100%,#0161ad));background-image:-webkit-linear-gradient(top,#197ac5,#0161ad);background-image:-moz-linear-gradient(top,#197ac5,#0161ad);background-image:-o-linear-gradient(top,#197ac5,#0161ad);background-image:-ms-linear-gradient(top,#197ac5,#0161ad);background-image:linear-gradient(top,#197ac5,#0161ad)}.x-btn-nx-primary-small-over{background-image:none;background-color:#14629e;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#14629e),color-stop(100%,#014e8a));background-image:-webkit-linear-gradient(top,#14629e,#014e8a);background-image:-moz-linear-gradient(top,#14629e,#014e8a);background-image:-o-linear-gradient(top,#14629e,#014e8a);background-image:-ms-linear-gradient(top,#14629e,#014e8a);background-image:linear-gradient(top,#14629e,#014e8a)}.x-btn-nx-primary-small-menu-active,.x-btn-nx-primary-small-pressed{background-image:none;background-color:#0f4976;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#0f4976),color-stop(100%,#013a68));background-image:-webkit-linear-gradient(top,#0f4976,#013a68);background-image:-moz-linear-gradient(top,#0f4976,#013a68);background-image:-o-linear-gradient(top,#0f4976,#013a68);background-image:-ms-linear-gradient(top,#0f4976,#013a68);background-image:linear-gradient(top,#0f4976,#013a68)}.x-btn-nx-primary-small-focus .x-frame-tl,.x-btn-nx-primary-small-focus .x-frame-bl,.x-btn-nx-primary-small-focus .x-frame-tr,.x-btn-nx-primary-small-focus .x-frame-br,.x-btn-nx-primary-small-focus .x-frame-tc,.x-btn-nx-primary-small-focus .x-frame-bc{background-image:url(images/btn/btn-nx-primary-small-focus-corners.gif)}.x-btn-nx-primary-small-focus .x-frame-ml,.x-btn-nx-primary-small-focus .x-frame-mr{background-image:url(images/btn/btn-nx-primary-small-focus-sides.gif)}.x-btn-nx-primary-small-focus .x-frame-mc{background-color:#197ac5;background-image:url(images/btn/btn-nx-primary-small-focus-fbg.gif)}.x-btn-nx-primary-small-over .x-frame-tl,.x-btn-nx-primary-small-over .x-frame-bl,.x-btn-nx-primary-small-over .x-frame-tr,.x-btn-nx-primary-small-over .x-frame-br,.x-btn-nx-primary-small-over .x-frame-tc,.x-btn-nx-primary-small-over .x-frame-bc{background-image:url(images/btn/btn-nx-primary-small-over-corners.gif)}.x-btn-nx-primary-small-over .x-frame-ml,.x-btn-nx-primary-small-over .x-frame-mr{background-image:url(images/btn/btn-nx-primary-small-over-sides.gif)}.x-btn-nx-primary-small-over .x-frame-mc{background-color:#14629e;background-image:url(images/btn/btn-nx-primary-small-over-fbg.gif)}.x-btn-nx-primary-small-menu-active .x-frame-tl,.x-btn-nx-primary-small-menu-active .x-frame-bl,.x-btn-nx-primary-small-menu-active .x-frame-tr,.x-btn-nx-primary-small-menu-active .x-frame-br,.x-btn-nx-primary-small-menu-active .x-frame-tc,.x-btn-nx-primary-small-menu-active .x-frame-bc,.x-btn-nx-primary-small-pressed .x-frame-tl,.x-btn-nx-primary-small-pressed .x-frame-bl,.x-btn-nx-primary-small-pressed .x-frame-tr,.x-btn-nx-primary-small-pressed .x-frame-br,.x-btn-nx-primary-small-pressed .x-frame-tc,.x-btn-nx-primary-small-pressed .x-frame-bc{background-image:url(images/btn/btn-nx-primary-small-pressed-corners.gif)}.x-btn-nx-primary-small-menu-active .x-frame-ml,.x-btn-nx-primary-small-menu-active .x-frame-mr,.x-btn-nx-primary-small-pressed .x-frame-ml,.x-btn-nx-primary-small-pressed .x-frame-mr{background-image:url(images/btn/btn-nx-primary-small-pressed-sides.gif)}.x-btn-nx-primary-small-menu-active .x-frame-mc,.x-btn-nx-primary-small-pressed .x-frame-mc{background-color:#0f4976;background-image:url(images/btn/btn-nx-primary-small-pressed-fbg.gif)}.x-btn-nx-primary-small-disabled .x-frame-tl,.x-btn-nx-primary-small-disabled .x-frame-bl,.x-btn-nx-primary-small-disabled .x-frame-tr,.x-btn-nx-primary-small-disabled .x-frame-br,.x-btn-nx-primary-small-disabled .x-frame-tc,.x-btn-nx-primary-small-disabled .x-frame-bc{background-image:url(images/btn/btn-nx-primary-small-disabled-corners.gif)}.x-btn-nx-primary-small-disabled .x-frame-ml,.x-btn-nx-primary-small-disabled .x-frame-mr{background-image:url(images/btn/btn-nx-primary-small-disabled-sides.gif)}.x-btn-nx-primary-small-disabled .x-frame-mc{background-color:#197ac5;background-image:url(images/btn/btn-nx-primary-small-disabled-fbg.gif)}.x-nlg .x-btn-nx-primary-small-focus{background-image:url(images/btn/btn-nx-primary-small-focus-bg.gif)}.x-nlg .x-btn-nx-primary-small-over{background-image:url(images/btn/btn-nx-primary-small-over-bg.gif)}.x-nlg .x-btn-nx-primary-small-menu-active,.x-nlg .x-btn-nx-primary-small-pressed{background-image:url(images/btn/btn-nx-primary-small-pressed-bg.gif)}.x-nlg .x-btn-nx-primary-small-disabled{background-image:url(images/btn/btn-nx-primary-small-disabled-bg.gif)}.x-nbr .x-btn-nx-primary-small{background-image:none}.x-btn-nx-primary-small .x-btn-split-right{background-image:url(images/button/nx-primary-small-s-arrow.png);padding-right:23px}.x-btn-nx-primary-small .x-btn-split-bottom{background-image:url(images/button/nx-primary-small-s-arrow-b.png);padding-bottom:20px}.x-btn-nx-primary-small-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-nx-primary-small-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-primary-small-focus-corners.gif), sides:url(images/btn/btn-nx-primary-small-focus-sides.gif), frame-bg:url(images/btn/btn-nx-primary-small-focus-fbg.gif), bg:url(images/btn/btn-nx-primary-small-focus-bg.gif)"}.x-btn-nx-primary-small-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-primary-small-over-corners.gif), sides:url(images/btn/btn-nx-primary-small-over-sides.gif), frame-bg:url(images/btn/btn-nx-primary-small-over-fbg.gif), bg:url(images/btn/btn-nx-primary-small-over-bg.gif)"}.x-btn-nx-primary-small-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-primary-small-pressed-corners.gif), sides:url(images/btn/btn-nx-primary-small-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-primary-small-pressed-fbg.gif), bg:url(images/btn/btn-nx-primary-small-pressed-bg.gif)"}.x-btn-nx-primary-small-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-primary-small-disabled-corners.gif), sides:url(images/btn/btn-nx-primary-small-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-primary-small-disabled-fbg.gif), bg:url(images/btn/btn-nx-primary-small-disabled-bg.gif)"}.x-btn-nx-danger-small{border-color:#333}.x-btn-nx-danger-small{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:#de3d63;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#de3d63),color-stop(100%,#c6254b));background-image:-webkit-linear-gradient(top,#de3d63,#c6254b);background-image:-moz-linear-gradient(top,#de3d63,#c6254b);background-image:-o-linear-gradient(top,#de3d63,#c6254b);background-image:-ms-linear-gradient(top,#de3d63,#c6254b);background-image:linear-gradient(top,#de3d63,#c6254b)}.x-btn-nx-danger-small-mc{background-image:url(images/btn/btn-nx-danger-small-fbg.gif);background-position:0 top;background-color:#de3d63}.x-nlg .x-btn-nx-danger-small{background-image:url(images/btn/btn-nx-danger-small-bg.gif);background-position:0 top}.x-nbr .x-btn-nx-danger-small{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-nx-danger-small-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-nx-danger-small-tl{background-position:0 -6px}.x-btn-nx-danger-small-tr{background-position:right -9px}.x-btn-nx-danger-small-bl{background-position:0 -12px}.x-btn-nx-danger-small-br{background-position:right -15px}.x-btn-nx-danger-small-ml{background-position:0 top}.x-btn-nx-danger-small-mr{background-position:right top}.x-btn-nx-danger-small-tc{background-position:0 0}.x-btn-nx-danger-small-bc{background-position:0 -3px}.x-btn-nx-danger-small-tr,.x-btn-nx-danger-small-br,.x-btn-nx-danger-small-mr{padding-right:3px}.x-btn-nx-danger-small-tl,.x-btn-nx-danger-small-bl,.x-btn-nx-danger-small-ml{padding-left:3px}.x-btn-nx-danger-small-tc{height:3px}.x-btn-nx-danger-small-bc{height:3px}.x-btn-nx-danger-small-tl,.x-btn-nx-danger-small-bl,.x-btn-nx-danger-small-tr,.x-btn-nx-danger-small-br,.x-btn-nx-danger-small-tc,.x-btn-nx-danger-small-bc,.x-btn-nx-danger-small-ml,.x-btn-nx-danger-small-mr{zoom:1;background-image:url(images/btn/btn-nx-danger-small-corners.gif)}.x-btn-nx-danger-small-ml,.x-btn-nx-danger-small-mr{zoom:1;background-image:url(images/btn/btn-nx-danger-small-sides.gif)}.x-btn-nx-danger-small-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-nx-danger-small-tl,.x-strict .x-ie7 .x-btn-nx-danger-small-bl{position:relative;right:0}.x-btn-nx-danger-small:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-danger-small-fbg.gif), bg:url(images/btn/btn-nx-danger-small-bg.gif), corners:url(images/btn/btn-nx-danger-small-corners.gif), sides:url(images/btn/btn-nx-danger-small-sides.gif)"}.x-btn-nx-danger-small .x-btn-inner{font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:white;padding:0 5px}.x-btn-nx-danger-small .x-btn-arrow{background-image:url(images/button/nx-danger-small-arrow.png)}.x-btn-nx-danger-small .x-btn-arrow-right{padding-right:21px}.x-btn-nx-danger-small .x-btn-arrow-bottom{padding-bottom:18px}.x-btn-nx-danger-small .x-btn-glyph{font-size:16px;line-height:16px;color:white}.x-ie8m .x-btn-nx-danger-small .x-btn-glyph{color:white}.x-btn-nx-danger-small-disabled{background-image:none;background-color:#de3d63;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#de3d63),color-stop(100%,#c6254b));background-image:-webkit-linear-gradient(top,#de3d63,#c6254b);background-image:-moz-linear-gradient(top,#de3d63,#c6254b);background-image:-o-linear-gradient(top,#de3d63,#c6254b);background-image:-ms-linear-gradient(top,#de3d63,#c6254b);background-image:linear-gradient(top,#de3d63,#c6254b)}.x-btn-nx-danger-small-icon .x-btn-button,.x-btn-nx-danger-small-noicon .x-btn-button{height:16px}.x-btn-nx-danger-small-icon .x-btn-inner,.x-btn-nx-danger-small-noicon .x-btn-inner{line-height:16px}.x-btn-nx-danger-small-icon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-danger-small-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-danger-small-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-nx-danger-small-icon .x-btn-inner{width:16px;padding:0}.x-btn-nx-danger-small-icon .x-btn-icon-el{width:16px;height:16px}.x-btn-nx-danger-small-icon-text-left .x-btn-button{height:16px}.x-btn-nx-danger-small-icon-text-left .x-btn-inner{line-height:16px;padding-left:21px}.x-btn-nx-danger-small-icon-text-left .x-btn-icon-el{width:16px;right:auto}.x-ie6 .x-btn-nx-danger-small-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-danger-small-icon-text-left .x-btn-icon-el{height:16px}.x-btn-nx-danger-small-icon-text-right .x-btn-button{height:16px}.x-btn-nx-danger-small-icon-text-right .x-btn-inner{line-height:16px;padding-right:21px}.x-btn-nx-danger-small-icon-text-right .x-btn-icon-el{width:16px;left:auto}.x-ie6 .x-btn-nx-danger-small-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-danger-small-icon-text-right .x-btn-icon-el{height:16px}.x-btn-nx-danger-small-icon-text-top .x-btn-inner{padding-top:21px}.x-btn-nx-danger-small-icon-text-top .x-btn-icon-el{height:16px;bottom:auto}.x-ie6 .x-btn-nx-danger-small-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-danger-small-icon-text-top .x-btn-icon-el{width:100%}.x-btn-nx-danger-small-icon-text-bottom .x-btn-inner{padding-bottom:21px}.x-btn-nx-danger-small-icon-text-bottom .x-btn-icon-el{height:16px;top:auto}.x-ie6 .x-btn-nx-danger-small-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-danger-small-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-nx-danger-small-focus{border-color:#96caee;background-image:none;background-color:#de3d63;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#de3d63),color-stop(100%,#c6254b));background-image:-webkit-linear-gradient(top,#de3d63,#c6254b);background-image:-moz-linear-gradient(top,#de3d63,#c6254b);background-image:-o-linear-gradient(top,#de3d63,#c6254b);background-image:-ms-linear-gradient(top,#de3d63,#c6254b);background-image:linear-gradient(top,#de3d63,#c6254b)}.x-btn-nx-danger-small-over{background-image:none;background-color:#b2314f;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#b2314f),color-stop(100%,#9e1e3c));background-image:-webkit-linear-gradient(top,#b2314f,#9e1e3c);background-image:-moz-linear-gradient(top,#b2314f,#9e1e3c);background-image:-o-linear-gradient(top,#b2314f,#9e1e3c);background-image:-ms-linear-gradient(top,#b2314f,#9e1e3c);background-image:linear-gradient(top,#b2314f,#9e1e3c)}.x-btn-nx-danger-small-menu-active,.x-btn-nx-danger-small-pressed{background-image:none;background-color:#85253b;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#85253b),color-stop(100%,#77162d));background-image:-webkit-linear-gradient(top,#85253b,#77162d);background-image:-moz-linear-gradient(top,#85253b,#77162d);background-image:-o-linear-gradient(top,#85253b,#77162d);background-image:-ms-linear-gradient(top,#85253b,#77162d);background-image:linear-gradient(top,#85253b,#77162d)}.x-btn-nx-danger-small-focus .x-frame-tl,.x-btn-nx-danger-small-focus .x-frame-bl,.x-btn-nx-danger-small-focus .x-frame-tr,.x-btn-nx-danger-small-focus .x-frame-br,.x-btn-nx-danger-small-focus .x-frame-tc,.x-btn-nx-danger-small-focus .x-frame-bc{background-image:url(images/btn/btn-nx-danger-small-focus-corners.gif)}.x-btn-nx-danger-small-focus .x-frame-ml,.x-btn-nx-danger-small-focus .x-frame-mr{background-image:url(images/btn/btn-nx-danger-small-focus-sides.gif)}.x-btn-nx-danger-small-focus .x-frame-mc{background-color:#de3d63;background-image:url(images/btn/btn-nx-danger-small-focus-fbg.gif)}.x-btn-nx-danger-small-over .x-frame-tl,.x-btn-nx-danger-small-over .x-frame-bl,.x-btn-nx-danger-small-over .x-frame-tr,.x-btn-nx-danger-small-over .x-frame-br,.x-btn-nx-danger-small-over .x-frame-tc,.x-btn-nx-danger-small-over .x-frame-bc{background-image:url(images/btn/btn-nx-danger-small-over-corners.gif)}.x-btn-nx-danger-small-over .x-frame-ml,.x-btn-nx-danger-small-over .x-frame-mr{background-image:url(images/btn/btn-nx-danger-small-over-sides.gif)}.x-btn-nx-danger-small-over .x-frame-mc{background-color:#b2314f;background-image:url(images/btn/btn-nx-danger-small-over-fbg.gif)}.x-btn-nx-danger-small-menu-active .x-frame-tl,.x-btn-nx-danger-small-menu-active .x-frame-bl,.x-btn-nx-danger-small-menu-active .x-frame-tr,.x-btn-nx-danger-small-menu-active .x-frame-br,.x-btn-nx-danger-small-menu-active .x-frame-tc,.x-btn-nx-danger-small-menu-active .x-frame-bc,.x-btn-nx-danger-small-pressed .x-frame-tl,.x-btn-nx-danger-small-pressed .x-frame-bl,.x-btn-nx-danger-small-pressed .x-frame-tr,.x-btn-nx-danger-small-pressed .x-frame-br,.x-btn-nx-danger-small-pressed .x-frame-tc,.x-btn-nx-danger-small-pressed .x-frame-bc{background-image:url(images/btn/btn-nx-danger-small-pressed-corners.gif)}.x-btn-nx-danger-small-menu-active .x-frame-ml,.x-btn-nx-danger-small-menu-active .x-frame-mr,.x-btn-nx-danger-small-pressed .x-frame-ml,.x-btn-nx-danger-small-pressed .x-frame-mr{background-image:url(images/btn/btn-nx-danger-small-pressed-sides.gif)}.x-btn-nx-danger-small-menu-active .x-frame-mc,.x-btn-nx-danger-small-pressed .x-frame-mc{background-color:#85253b;background-image:url(images/btn/btn-nx-danger-small-pressed-fbg.gif)}.x-btn-nx-danger-small-disabled .x-frame-tl,.x-btn-nx-danger-small-disabled .x-frame-bl,.x-btn-nx-danger-small-disabled .x-frame-tr,.x-btn-nx-danger-small-disabled .x-frame-br,.x-btn-nx-danger-small-disabled .x-frame-tc,.x-btn-nx-danger-small-disabled .x-frame-bc{background-image:url(images/btn/btn-nx-danger-small-disabled-corners.gif)}.x-btn-nx-danger-small-disabled .x-frame-ml,.x-btn-nx-danger-small-disabled .x-frame-mr{background-image:url(images/btn/btn-nx-danger-small-disabled-sides.gif)}.x-btn-nx-danger-small-disabled .x-frame-mc{background-color:#de3d63;background-image:url(images/btn/btn-nx-danger-small-disabled-fbg.gif)}.x-nlg .x-btn-nx-danger-small-focus{background-image:url(images/btn/btn-nx-danger-small-focus-bg.gif)}.x-nlg .x-btn-nx-danger-small-over{background-image:url(images/btn/btn-nx-danger-small-over-bg.gif)}.x-nlg .x-btn-nx-danger-small-menu-active,.x-nlg .x-btn-nx-danger-small-pressed{background-image:url(images/btn/btn-nx-danger-small-pressed-bg.gif)}.x-nlg .x-btn-nx-danger-small-disabled{background-image:url(images/btn/btn-nx-danger-small-disabled-bg.gif)}.x-nbr .x-btn-nx-danger-small{background-image:none}.x-btn-nx-danger-small .x-btn-split-right{background-image:url(images/button/nx-danger-small-s-arrow.png);padding-right:23px}.x-btn-nx-danger-small .x-btn-split-bottom{background-image:url(images/button/nx-danger-small-s-arrow-b.png);padding-bottom:20px}.x-btn-nx-danger-small-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-nx-danger-small-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-danger-small-focus-corners.gif), sides:url(images/btn/btn-nx-danger-small-focus-sides.gif), frame-bg:url(images/btn/btn-nx-danger-small-focus-fbg.gif), bg:url(images/btn/btn-nx-danger-small-focus-bg.gif)"}.x-btn-nx-danger-small-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-danger-small-over-corners.gif), sides:url(images/btn/btn-nx-danger-small-over-sides.gif), frame-bg:url(images/btn/btn-nx-danger-small-over-fbg.gif), bg:url(images/btn/btn-nx-danger-small-over-bg.gif)"}.x-btn-nx-danger-small-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-danger-small-pressed-corners.gif), sides:url(images/btn/btn-nx-danger-small-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-danger-small-pressed-fbg.gif), bg:url(images/btn/btn-nx-danger-small-pressed-bg.gif)"}.x-btn-nx-danger-small-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-danger-small-disabled-corners.gif), sides:url(images/btn/btn-nx-danger-small-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-danger-small-disabled-fbg.gif), bg:url(images/btn/btn-nx-danger-small-disabled-bg.gif)"}.x-btn-nx-warning-small{border-color:#333}.x-btn-nx-warning-small{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:#f39244;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#f39244),color-stop(100%,#da792b));background-image:-webkit-linear-gradient(top,#f39244,#da792b);background-image:-moz-linear-gradient(top,#f39244,#da792b);background-image:-o-linear-gradient(top,#f39244,#da792b);background-image:-ms-linear-gradient(top,#f39244,#da792b);background-image:linear-gradient(top,#f39244,#da792b)}.x-btn-nx-warning-small-mc{background-image:url(images/btn/btn-nx-warning-small-fbg.gif);background-position:0 top;background-color:#f39244}.x-nlg .x-btn-nx-warning-small{background-image:url(images/btn/btn-nx-warning-small-bg.gif);background-position:0 top}.x-nbr .x-btn-nx-warning-small{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-nx-warning-small-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-nx-warning-small-tl{background-position:0 -6px}.x-btn-nx-warning-small-tr{background-position:right -9px}.x-btn-nx-warning-small-bl{background-position:0 -12px}.x-btn-nx-warning-small-br{background-position:right -15px}.x-btn-nx-warning-small-ml{background-position:0 top}.x-btn-nx-warning-small-mr{background-position:right top}.x-btn-nx-warning-small-tc{background-position:0 0}.x-btn-nx-warning-small-bc{background-position:0 -3px}.x-btn-nx-warning-small-tr,.x-btn-nx-warning-small-br,.x-btn-nx-warning-small-mr{padding-right:3px}.x-btn-nx-warning-small-tl,.x-btn-nx-warning-small-bl,.x-btn-nx-warning-small-ml{padding-left:3px}.x-btn-nx-warning-small-tc{height:3px}.x-btn-nx-warning-small-bc{height:3px}.x-btn-nx-warning-small-tl,.x-btn-nx-warning-small-bl,.x-btn-nx-warning-small-tr,.x-btn-nx-warning-small-br,.x-btn-nx-warning-small-tc,.x-btn-nx-warning-small-bc,.x-btn-nx-warning-small-ml,.x-btn-nx-warning-small-mr{zoom:1;background-image:url(images/btn/btn-nx-warning-small-corners.gif)}.x-btn-nx-warning-small-ml,.x-btn-nx-warning-small-mr{zoom:1;background-image:url(images/btn/btn-nx-warning-small-sides.gif)}.x-btn-nx-warning-small-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-nx-warning-small-tl,.x-strict .x-ie7 .x-btn-nx-warning-small-bl{position:relative;right:0}.x-btn-nx-warning-small:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-warning-small-fbg.gif), bg:url(images/btn/btn-nx-warning-small-bg.gif), corners:url(images/btn/btn-nx-warning-small-corners.gif), sides:url(images/btn/btn-nx-warning-small-sides.gif)"}.x-btn-nx-warning-small .x-btn-inner{font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:white;padding:0 5px}.x-btn-nx-warning-small .x-btn-arrow{background-image:url(images/button/nx-warning-small-arrow.png)}.x-btn-nx-warning-small .x-btn-arrow-right{padding-right:21px}.x-btn-nx-warning-small .x-btn-arrow-bottom{padding-bottom:18px}.x-btn-nx-warning-small .x-btn-glyph{font-size:16px;line-height:16px;color:white}.x-ie8m .x-btn-nx-warning-small .x-btn-glyph{color:white}.x-btn-nx-warning-small-disabled{background-image:none;background-color:#f39244;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#f39244),color-stop(100%,#da792b));background-image:-webkit-linear-gradient(top,#f39244,#da792b);background-image:-moz-linear-gradient(top,#f39244,#da792b);background-image:-o-linear-gradient(top,#f39244,#da792b);background-image:-ms-linear-gradient(top,#f39244,#da792b);background-image:linear-gradient(top,#f39244,#da792b)}.x-btn-nx-warning-small-icon .x-btn-button,.x-btn-nx-warning-small-noicon .x-btn-button{height:16px}.x-btn-nx-warning-small-icon .x-btn-inner,.x-btn-nx-warning-small-noicon .x-btn-inner{line-height:16px}.x-btn-nx-warning-small-icon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-warning-small-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-warning-small-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-nx-warning-small-icon .x-btn-inner{width:16px;padding:0}.x-btn-nx-warning-small-icon .x-btn-icon-el{width:16px;height:16px}.x-btn-nx-warning-small-icon-text-left .x-btn-button{height:16px}.x-btn-nx-warning-small-icon-text-left .x-btn-inner{line-height:16px;padding-left:21px}.x-btn-nx-warning-small-icon-text-left .x-btn-icon-el{width:16px;right:auto}.x-ie6 .x-btn-nx-warning-small-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-warning-small-icon-text-left .x-btn-icon-el{height:16px}.x-btn-nx-warning-small-icon-text-right .x-btn-button{height:16px}.x-btn-nx-warning-small-icon-text-right .x-btn-inner{line-height:16px;padding-right:21px}.x-btn-nx-warning-small-icon-text-right .x-btn-icon-el{width:16px;left:auto}.x-ie6 .x-btn-nx-warning-small-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-warning-small-icon-text-right .x-btn-icon-el{height:16px}.x-btn-nx-warning-small-icon-text-top .x-btn-inner{padding-top:21px}.x-btn-nx-warning-small-icon-text-top .x-btn-icon-el{height:16px;bottom:auto}.x-ie6 .x-btn-nx-warning-small-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-warning-small-icon-text-top .x-btn-icon-el{width:100%}.x-btn-nx-warning-small-icon-text-bottom .x-btn-inner{padding-bottom:21px}.x-btn-nx-warning-small-icon-text-bottom .x-btn-icon-el{height:16px;top:auto}.x-ie6 .x-btn-nx-warning-small-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-warning-small-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-nx-warning-small-focus{border-color:#96caee;background-image:none;background-color:#f39244;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#f39244),color-stop(100%,#da792b));background-image:-webkit-linear-gradient(top,#f39244,#da792b);background-image:-moz-linear-gradient(top,#f39244,#da792b);background-image:-o-linear-gradient(top,#f39244,#da792b);background-image:-ms-linear-gradient(top,#f39244,#da792b);background-image:linear-gradient(top,#f39244,#da792b)}.x-btn-nx-warning-small-over{background-image:none;background-color:#c17536;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#c17536),color-stop(100%,#ae6122));background-image:-webkit-linear-gradient(top,#c17536,#ae6122);background-image:-moz-linear-gradient(top,#c17536,#ae6122);background-image:-o-linear-gradient(top,#c17536,#ae6122);background-image:-ms-linear-gradient(top,#c17536,#ae6122);background-image:linear-gradient(top,#c17536,#ae6122)}.x-btn-nx-warning-small-menu-active,.x-btn-nx-warning-small-pressed{background-image:none;background-color:#925829;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#925829),color-stop(100%,#83491a));background-image:-webkit-linear-gradient(top,#925829,#83491a);background-image:-moz-linear-gradient(top,#925829,#83491a);background-image:-o-linear-gradient(top,#925829,#83491a);background-image:-ms-linear-gradient(top,#925829,#83491a);background-image:linear-gradient(top,#925829,#83491a)}.x-btn-nx-warning-small-focus .x-frame-tl,.x-btn-nx-warning-small-focus .x-frame-bl,.x-btn-nx-warning-small-focus .x-frame-tr,.x-btn-nx-warning-small-focus .x-frame-br,.x-btn-nx-warning-small-focus .x-frame-tc,.x-btn-nx-warning-small-focus .x-frame-bc{background-image:url(images/btn/btn-nx-warning-small-focus-corners.gif)}.x-btn-nx-warning-small-focus .x-frame-ml,.x-btn-nx-warning-small-focus .x-frame-mr{background-image:url(images/btn/btn-nx-warning-small-focus-sides.gif)}.x-btn-nx-warning-small-focus .x-frame-mc{background-color:#f39244;background-image:url(images/btn/btn-nx-warning-small-focus-fbg.gif)}.x-btn-nx-warning-small-over .x-frame-tl,.x-btn-nx-warning-small-over .x-frame-bl,.x-btn-nx-warning-small-over .x-frame-tr,.x-btn-nx-warning-small-over .x-frame-br,.x-btn-nx-warning-small-over .x-frame-tc,.x-btn-nx-warning-small-over .x-frame-bc{background-image:url(images/btn/btn-nx-warning-small-over-corners.gif)}.x-btn-nx-warning-small-over .x-frame-ml,.x-btn-nx-warning-small-over .x-frame-mr{background-image:url(images/btn/btn-nx-warning-small-over-sides.gif)}.x-btn-nx-warning-small-over .x-frame-mc{background-color:#c17536;background-image:url(images/btn/btn-nx-warning-small-over-fbg.gif)}.x-btn-nx-warning-small-menu-active .x-frame-tl,.x-btn-nx-warning-small-menu-active .x-frame-bl,.x-btn-nx-warning-small-menu-active .x-frame-tr,.x-btn-nx-warning-small-menu-active .x-frame-br,.x-btn-nx-warning-small-menu-active .x-frame-tc,.x-btn-nx-warning-small-menu-active .x-frame-bc,.x-btn-nx-warning-small-pressed .x-frame-tl,.x-btn-nx-warning-small-pressed .x-frame-bl,.x-btn-nx-warning-small-pressed .x-frame-tr,.x-btn-nx-warning-small-pressed .x-frame-br,.x-btn-nx-warning-small-pressed .x-frame-tc,.x-btn-nx-warning-small-pressed .x-frame-bc{background-image:url(images/btn/btn-nx-warning-small-pressed-corners.gif)}.x-btn-nx-warning-small-menu-active .x-frame-ml,.x-btn-nx-warning-small-menu-active .x-frame-mr,.x-btn-nx-warning-small-pressed .x-frame-ml,.x-btn-nx-warning-small-pressed .x-frame-mr{background-image:url(images/btn/btn-nx-warning-small-pressed-sides.gif)}.x-btn-nx-warning-small-menu-active .x-frame-mc,.x-btn-nx-warning-small-pressed .x-frame-mc{background-color:#925829;background-image:url(images/btn/btn-nx-warning-small-pressed-fbg.gif)}.x-btn-nx-warning-small-disabled .x-frame-tl,.x-btn-nx-warning-small-disabled .x-frame-bl,.x-btn-nx-warning-small-disabled .x-frame-tr,.x-btn-nx-warning-small-disabled .x-frame-br,.x-btn-nx-warning-small-disabled .x-frame-tc,.x-btn-nx-warning-small-disabled .x-frame-bc{background-image:url(images/btn/btn-nx-warning-small-disabled-corners.gif)}.x-btn-nx-warning-small-disabled .x-frame-ml,.x-btn-nx-warning-small-disabled .x-frame-mr{background-image:url(images/btn/btn-nx-warning-small-disabled-sides.gif)}.x-btn-nx-warning-small-disabled .x-frame-mc{background-color:#f39244;background-image:url(images/btn/btn-nx-warning-small-disabled-fbg.gif)}.x-nlg .x-btn-nx-warning-small-focus{background-image:url(images/btn/btn-nx-warning-small-focus-bg.gif)}.x-nlg .x-btn-nx-warning-small-over{background-image:url(images/btn/btn-nx-warning-small-over-bg.gif)}.x-nlg .x-btn-nx-warning-small-menu-active,.x-nlg .x-btn-nx-warning-small-pressed{background-image:url(images/btn/btn-nx-warning-small-pressed-bg.gif)}.x-nlg .x-btn-nx-warning-small-disabled{background-image:url(images/btn/btn-nx-warning-small-disabled-bg.gif)}.x-nbr .x-btn-nx-warning-small{background-image:none}.x-btn-nx-warning-small .x-btn-split-right{background-image:url(images/button/nx-warning-small-s-arrow.png);padding-right:23px}.x-btn-nx-warning-small .x-btn-split-bottom{background-image:url(images/button/nx-warning-small-s-arrow-b.png);padding-bottom:20px}.x-btn-nx-warning-small-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-nx-warning-small-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-warning-small-focus-corners.gif), sides:url(images/btn/btn-nx-warning-small-focus-sides.gif), frame-bg:url(images/btn/btn-nx-warning-small-focus-fbg.gif), bg:url(images/btn/btn-nx-warning-small-focus-bg.gif)"}.x-btn-nx-warning-small-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-warning-small-over-corners.gif), sides:url(images/btn/btn-nx-warning-small-over-sides.gif), frame-bg:url(images/btn/btn-nx-warning-small-over-fbg.gif), bg:url(images/btn/btn-nx-warning-small-over-bg.gif)"}.x-btn-nx-warning-small-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-warning-small-pressed-corners.gif), sides:url(images/btn/btn-nx-warning-small-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-warning-small-pressed-fbg.gif), bg:url(images/btn/btn-nx-warning-small-pressed-bg.gif)"}.x-btn-nx-warning-small-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-warning-small-disabled-corners.gif), sides:url(images/btn/btn-nx-warning-small-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-warning-small-disabled-fbg.gif), bg:url(images/btn/btn-nx-warning-small-disabled-bg.gif)"}.x-btn-nx-success-small{border-color:#333}.x-btn-nx-success-small{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:#23a156;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#23a156),color-stop(100%,#0b893d));background-image:-webkit-linear-gradient(top,#23a156,#0b893d);background-image:-moz-linear-gradient(top,#23a156,#0b893d);background-image:-o-linear-gradient(top,#23a156,#0b893d);background-image:-ms-linear-gradient(top,#23a156,#0b893d);background-image:linear-gradient(top,#23a156,#0b893d)}.x-btn-nx-success-small-mc{background-image:url(images/btn/btn-nx-success-small-fbg.gif);background-position:0 top;background-color:#23a156}.x-nlg .x-btn-nx-success-small{background-image:url(images/btn/btn-nx-success-small-bg.gif);background-position:0 top}.x-nbr .x-btn-nx-success-small{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-nx-success-small-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-nx-success-small-tl{background-position:0 -6px}.x-btn-nx-success-small-tr{background-position:right -9px}.x-btn-nx-success-small-bl{background-position:0 -12px}.x-btn-nx-success-small-br{background-position:right -15px}.x-btn-nx-success-small-ml{background-position:0 top}.x-btn-nx-success-small-mr{background-position:right top}.x-btn-nx-success-small-tc{background-position:0 0}.x-btn-nx-success-small-bc{background-position:0 -3px}.x-btn-nx-success-small-tr,.x-btn-nx-success-small-br,.x-btn-nx-success-small-mr{padding-right:3px}.x-btn-nx-success-small-tl,.x-btn-nx-success-small-bl,.x-btn-nx-success-small-ml{padding-left:3px}.x-btn-nx-success-small-tc{height:3px}.x-btn-nx-success-small-bc{height:3px}.x-btn-nx-success-small-tl,.x-btn-nx-success-small-bl,.x-btn-nx-success-small-tr,.x-btn-nx-success-small-br,.x-btn-nx-success-small-tc,.x-btn-nx-success-small-bc,.x-btn-nx-success-small-ml,.x-btn-nx-success-small-mr{zoom:1;background-image:url(images/btn/btn-nx-success-small-corners.gif)}.x-btn-nx-success-small-ml,.x-btn-nx-success-small-mr{zoom:1;background-image:url(images/btn/btn-nx-success-small-sides.gif)}.x-btn-nx-success-small-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-nx-success-small-tl,.x-strict .x-ie7 .x-btn-nx-success-small-bl{position:relative;right:0}.x-btn-nx-success-small:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-success-small-fbg.gif), bg:url(images/btn/btn-nx-success-small-bg.gif), corners:url(images/btn/btn-nx-success-small-corners.gif), sides:url(images/btn/btn-nx-success-small-sides.gif)"}.x-btn-nx-success-small .x-btn-inner{font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:white;padding:0 5px}.x-btn-nx-success-small .x-btn-arrow{background-image:url(images/button/nx-success-small-arrow.png)}.x-btn-nx-success-small .x-btn-arrow-right{padding-right:21px}.x-btn-nx-success-small .x-btn-arrow-bottom{padding-bottom:18px}.x-btn-nx-success-small .x-btn-glyph{font-size:16px;line-height:16px;color:white}.x-ie8m .x-btn-nx-success-small .x-btn-glyph{color:white}.x-btn-nx-success-small-disabled{background-image:none;background-color:#23a156;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#23a156),color-stop(100%,#0b893d));background-image:-webkit-linear-gradient(top,#23a156,#0b893d);background-image:-moz-linear-gradient(top,#23a156,#0b893d);background-image:-o-linear-gradient(top,#23a156,#0b893d);background-image:-ms-linear-gradient(top,#23a156,#0b893d);background-image:linear-gradient(top,#23a156,#0b893d)}.x-btn-nx-success-small-icon .x-btn-button,.x-btn-nx-success-small-noicon .x-btn-button{height:16px}.x-btn-nx-success-small-icon .x-btn-inner,.x-btn-nx-success-small-noicon .x-btn-inner{line-height:16px}.x-btn-nx-success-small-icon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-success-small-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-success-small-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-nx-success-small-icon .x-btn-inner{width:16px;padding:0}.x-btn-nx-success-small-icon .x-btn-icon-el{width:16px;height:16px}.x-btn-nx-success-small-icon-text-left .x-btn-button{height:16px}.x-btn-nx-success-small-icon-text-left .x-btn-inner{line-height:16px;padding-left:21px}.x-btn-nx-success-small-icon-text-left .x-btn-icon-el{width:16px;right:auto}.x-ie6 .x-btn-nx-success-small-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-success-small-icon-text-left .x-btn-icon-el{height:16px}.x-btn-nx-success-small-icon-text-right .x-btn-button{height:16px}.x-btn-nx-success-small-icon-text-right .x-btn-inner{line-height:16px;padding-right:21px}.x-btn-nx-success-small-icon-text-right .x-btn-icon-el{width:16px;left:auto}.x-ie6 .x-btn-nx-success-small-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-success-small-icon-text-right .x-btn-icon-el{height:16px}.x-btn-nx-success-small-icon-text-top .x-btn-inner{padding-top:21px}.x-btn-nx-success-small-icon-text-top .x-btn-icon-el{height:16px;bottom:auto}.x-ie6 .x-btn-nx-success-small-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-success-small-icon-text-top .x-btn-icon-el{width:100%}.x-btn-nx-success-small-icon-text-bottom .x-btn-inner{padding-bottom:21px}.x-btn-nx-success-small-icon-text-bottom .x-btn-icon-el{height:16px;top:auto}.x-ie6 .x-btn-nx-success-small-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-success-small-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-nx-success-small-focus{border-color:#96caee;background-image:none;background-color:#23a156;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#23a156),color-stop(100%,#0b893d));background-image:-webkit-linear-gradient(top,#23a156,#0b893d);background-image:-moz-linear-gradient(top,#23a156,#0b893d);background-image:-o-linear-gradient(top,#23a156,#0b893d);background-image:-ms-linear-gradient(top,#23a156,#0b893d);background-image:linear-gradient(top,#23a156,#0b893d)}.x-btn-nx-success-small-over{background-image:none;background-color:#1c8145;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#1c8145),color-stop(100%,#096e31));background-image:-webkit-linear-gradient(top,#1c8145,#096e31);background-image:-moz-linear-gradient(top,#1c8145,#096e31);background-image:-o-linear-gradient(top,#1c8145,#096e31);background-image:-ms-linear-gradient(top,#1c8145,#096e31);background-image:linear-gradient(top,#1c8145,#096e31)}.x-btn-nx-success-small-menu-active,.x-btn-nx-success-small-pressed{background-image:none;background-color:#156134;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0%,#156134),color-stop(100%,#0c4f26));background-image:-webkit-linear-gradient(top,#156134,#0c4f26);background-image:-moz-linear-gradient(top,#156134,#0c4f26);background-image:-o-linear-gradient(top,#156134,#0c4f26);background-image:-ms-linear-gradient(top,#156134,#0c4f26);background-image:linear-gradient(top,#156134,#0c4f26)}.x-btn-nx-success-small-focus .x-frame-tl,.x-btn-nx-success-small-focus .x-frame-bl,.x-btn-nx-success-small-focus .x-frame-tr,.x-btn-nx-success-small-focus .x-frame-br,.x-btn-nx-success-small-focus .x-frame-tc,.x-btn-nx-success-small-focus .x-frame-bc{background-image:url(images/btn/btn-nx-success-small-focus-corners.gif)}.x-btn-nx-success-small-focus .x-frame-ml,.x-btn-nx-success-small-focus .x-frame-mr{background-image:url(images/btn/btn-nx-success-small-focus-sides.gif)}.x-btn-nx-success-small-focus .x-frame-mc{background-color:#23a156;background-image:url(images/btn/btn-nx-success-small-focus-fbg.gif)}.x-btn-nx-success-small-over .x-frame-tl,.x-btn-nx-success-small-over .x-frame-bl,.x-btn-nx-success-small-over .x-frame-tr,.x-btn-nx-success-small-over .x-frame-br,.x-btn-nx-success-small-over .x-frame-tc,.x-btn-nx-success-small-over .x-frame-bc{background-image:url(images/btn/btn-nx-success-small-over-corners.gif)}.x-btn-nx-success-small-over .x-frame-ml,.x-btn-nx-success-small-over .x-frame-mr{background-image:url(images/btn/btn-nx-success-small-over-sides.gif)}.x-btn-nx-success-small-over .x-frame-mc{background-color:#1c8145;background-image:url(images/btn/btn-nx-success-small-over-fbg.gif)}.x-btn-nx-success-small-menu-active .x-frame-tl,.x-btn-nx-success-small-menu-active .x-frame-bl,.x-btn-nx-success-small-menu-active .x-frame-tr,.x-btn-nx-success-small-menu-active .x-frame-br,.x-btn-nx-success-small-menu-active .x-frame-tc,.x-btn-nx-success-small-menu-active .x-frame-bc,.x-btn-nx-success-small-pressed .x-frame-tl,.x-btn-nx-success-small-pressed .x-frame-bl,.x-btn-nx-success-small-pressed .x-frame-tr,.x-btn-nx-success-small-pressed .x-frame-br,.x-btn-nx-success-small-pressed .x-frame-tc,.x-btn-nx-success-small-pressed .x-frame-bc{background-image:url(images/btn/btn-nx-success-small-pressed-corners.gif)}.x-btn-nx-success-small-menu-active .x-frame-ml,.x-btn-nx-success-small-menu-active .x-frame-mr,.x-btn-nx-success-small-pressed .x-frame-ml,.x-btn-nx-success-small-pressed .x-frame-mr{background-image:url(images/btn/btn-nx-success-small-pressed-sides.gif)}.x-btn-nx-success-small-menu-active .x-frame-mc,.x-btn-nx-success-small-pressed .x-frame-mc{background-color:#156134;background-image:url(images/btn/btn-nx-success-small-pressed-fbg.gif)}.x-btn-nx-success-small-disabled .x-frame-tl,.x-btn-nx-success-small-disabled .x-frame-bl,.x-btn-nx-success-small-disabled .x-frame-tr,.x-btn-nx-success-small-disabled .x-frame-br,.x-btn-nx-success-small-disabled .x-frame-tc,.x-btn-nx-success-small-disabled .x-frame-bc{background-image:url(images/btn/btn-nx-success-small-disabled-corners.gif)}.x-btn-nx-success-small-disabled .x-frame-ml,.x-btn-nx-success-small-disabled .x-frame-mr{background-image:url(images/btn/btn-nx-success-small-disabled-sides.gif)}.x-btn-nx-success-small-disabled .x-frame-mc{background-color:#23a156;background-image:url(images/btn/btn-nx-success-small-disabled-fbg.gif)}.x-nlg .x-btn-nx-success-small-focus{background-image:url(images/btn/btn-nx-success-small-focus-bg.gif)}.x-nlg .x-btn-nx-success-small-over{background-image:url(images/btn/btn-nx-success-small-over-bg.gif)}.x-nlg .x-btn-nx-success-small-menu-active,.x-nlg .x-btn-nx-success-small-pressed{background-image:url(images/btn/btn-nx-success-small-pressed-bg.gif)}.x-nlg .x-btn-nx-success-small-disabled{background-image:url(images/btn/btn-nx-success-small-disabled-bg.gif)}.x-nbr .x-btn-nx-success-small{background-image:none}.x-btn-nx-success-small .x-btn-split-right{background-image:url(images/button/nx-success-small-s-arrow.png);padding-right:23px}.x-btn-nx-success-small .x-btn-split-bottom{background-image:url(images/button/nx-success-small-s-arrow-b.png);padding-bottom:20px}.x-btn-nx-success-small-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-nx-success-small-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-success-small-focus-corners.gif), sides:url(images/btn/btn-nx-success-small-focus-sides.gif), frame-bg:url(images/btn/btn-nx-success-small-focus-fbg.gif), bg:url(images/btn/btn-nx-success-small-focus-bg.gif)"}.x-btn-nx-success-small-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-success-small-over-corners.gif), sides:url(images/btn/btn-nx-success-small-over-sides.gif), frame-bg:url(images/btn/btn-nx-success-small-over-fbg.gif), bg:url(images/btn/btn-nx-success-small-over-bg.gif)"}.x-btn-nx-success-small-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-success-small-pressed-corners.gif), sides:url(images/btn/btn-nx-success-small-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-success-small-pressed-fbg.gif), bg:url(images/btn/btn-nx-success-small-pressed-bg.gif)"}.x-btn-nx-success-small-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-success-small-disabled-corners.gif), sides:url(images/btn/btn-nx-success-small-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-success-small-disabled-fbg.gif), bg:url(images/btn/btn-nx-success-small-disabled-bg.gif)"}.x-btn-default-small .x-btn-glyph,.x-btn-plain-small .x-btn-glyph,.x-btn-primary-small .x-btn-glyph,.x-btn-danger-small .x-btn-glyph,.x-btn-warning-small .x-btn-glyph,.x-btn-success-small .x-btn-glyph{font-size:13px}.x-panel-nx-inset{border-color:#444;padding:12px 12px 0 12px}.x-panel-header-nx-inset{font-size:13px;border:1px solid #444}.x-panel-header-nx-inset .x-tool-img{background-color:#444}.x-panel-header-nx-inset-horizontal{padding:9px 9px 10px 9px}.x-panel-header-nx-inset-horizontal-noborder{padding:10px 10px 10px 10px}.x-panel-header-nx-inset-vertical{padding:9px 9px 9px 10px}.x-panel-header-nx-inset-vertical-noborder{padding:10px 10px 10px 10px}.x-panel-header-text-container-nx-inset{color:white;font-size:13px;font-weight:bold;font-family:arial,helvetica,verdana,sans-serif;line-height:15px;padding:1px 0 0;text-transform:none}.x-panel-body-nx-inset{background:transparent;border-color:#444;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-header-nx-inset{background-image:none;background-color:#444}.x-panel-header-nx-inset-vertical{background-image:none;background-color:#444}.x-panel .x-panel-header-nx-inset-collapsed-border-top{border-bottom-width:1px!important}.x-panel .x-panel-header-nx-inset-collapsed-border-right{border-left-width:1px!important}.x-panel .x-panel-header-nx-inset-collapsed-border-bottom{border-top-width:1px!important}.x-panel .x-panel-header-nx-inset-collapsed-border-left{border-right-width:1px!important}.x-panel-header-nx-inset-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-inset-bottom:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-inset-left:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-inset-right:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-inset-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-nx-inset-vertical .x-panel-header-text-container{background-color:#444;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#444444)}.x-panel-header-nx-inset .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-nx-inset .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-nx-inset .x-panel-header-glyph{color:#a1a1a1}.x-panel-header-nx-inset-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-nx-inset-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-nx-inset-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-nx-inset-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-nx-inset-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-nx-inset-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-nx-inset-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-nx-inset-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-nx-inset-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-nx-inset-outer-border-l{border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-inset-outer-border-b{border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-inset-outer-border-bl{border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-inset-outer-border-r{border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-inset-outer-border-rl{border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-inset-outer-border-rb{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-inset-outer-border-rbl{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-inset-outer-border-t{border-top-color:#444!important;border-top-width:1px!important}.x-panel-nx-inset-outer-border-tl{border-top-color:#444!important;border-top-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-inset-outer-border-tb{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-inset-outer-border-tbl{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-inset-outer-border-tr{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-inset-outer-border-trl{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-inset-outer-border-trb{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-inset-outer-border-trbl{border-color:#444!important;border-width:1px!important}.x-panel-nx-inset .x-panel-body .x-toolbar{padding:6px 0 6px 0}.x-panel-nx-inset .x-panel-nx-subsection-framed{margin:0 0 10px 0}.x-panel-nx-license-warning,.x-panel-nx-database-freeze-warning,.x-panel-nx-file-descriptor-warning{border-color:#444;padding:0}.x-panel-header-nx-license-warning{font-size:13px;border:1px solid #444}.x-panel-header-nx-license-warning .x-tool-img{background-color:#444}.x-panel-header-nx-license-warning-horizontal{padding:9px 9px 10px 9px}.x-panel-header-nx-license-warning-horizontal-noborder{padding:10px 10px 10px 10px}.x-panel-header-nx-license-warning-vertical{padding:9px 9px 9px 10px}.x-panel-header-nx-license-warning-vertical-noborder{padding:10px 10px 10px 10px}.x-panel-header-text-container-nx-license-warning{color:white;font-size:13px;font-weight:bold;font-family:arial,helvetica,verdana,sans-serif;line-height:15px;padding:1px 0 0;text-transform:none}.x-panel-body-nx-license-warning{background:transparent;border-color:#444;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-header-nx-license-warning{background-image:none;background-color:#444}.x-panel-header-nx-license-warning-vertical{background-image:none;background-color:#444}.x-panel .x-panel-header-nx-license-warning-collapsed-border-top{border-bottom-width:1px!important}.x-panel .x-panel-header-nx-license-warning-collapsed-border-right{border-left-width:1px!important}.x-panel .x-panel-header-nx-license-warning-collapsed-border-bottom{border-top-width:1px!important}.x-panel .x-panel-header-nx-license-warning-collapsed-border-left{border-right-width:1px!important}.x-panel-header-nx-license-warning-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-license-warning-bottom:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-license-warning-left:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-license-warning-right:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-license-warning-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-nx-license-warning-vertical .x-panel-header-text-container{background-color:#444;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#444444)}.x-panel-header-nx-license-warning .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-nx-license-warning .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-nx-license-warning .x-panel-header-glyph{color:#a1a1a1}.x-panel-header-nx-license-warning-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-nx-license-warning-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-nx-license-warning-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-nx-license-warning-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-nx-license-warning-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-nx-license-warning-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-nx-license-warning-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-nx-license-warning-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-nx-license-warning-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-nx-license-warning-outer-border-l{border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-license-warning-outer-border-b{border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-license-warning-outer-border-bl{border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-license-warning-outer-border-r{border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-license-warning-outer-border-rl{border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-license-warning-outer-border-rb{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-license-warning-outer-border-rbl{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-license-warning-outer-border-t{border-top-color:#444!important;border-top-width:1px!important}.x-panel-nx-license-warning-outer-border-tl{border-top-color:#444!important;border-top-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-license-warning-outer-border-tb{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-license-warning-outer-border-tbl{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-license-warning-outer-border-tr{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-license-warning-outer-border-trl{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-license-warning-outer-border-trb{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-license-warning-outer-border-trbl{border-color:#444!important;border-width:1px!important}.x-panel-nx-license-warning .x-panel-header,.x-panel-nx-database-freeze-warning .x-panel-header,.x-panel-nx-file-descriptor-warning .x-panel-header{padding:5px 16px}.x-panel-nx-license-warning,.x-panel-nx-database-freeze-warning,.x-panel-nx-file-descriptor-warning{color:white}.x-panel-nx-license-warning a,.x-panel-nx-database-freeze-warning a,.x-panel-nx-file-descriptor-warning a,.x-panel-nx-license-warning a:visited,.x-panel-nx-database-freeze-warning a:visited,.x-panel-nx-file-descriptor-warning a:visited,.x-panel-nx-license-warning a:active,.x-panel-nx-database-freeze-warning a:active,.x-panel-nx-file-descriptor-warning a:active{color:#ddd}.x-panel-nx-license-warning a:hover,.x-panel-nx-database-freeze-warning a:hover,.x-panel-nx-file-descriptor-warning a:hover{color:white;text-decoration:underline}.x-panel-nx-database-freeze-warning{background-color:#953ea9}.x-panel-header-text-container-nx-database-freeze-warning{margin-left:6px;font-weight:bold}.x-panel-nx-file-descriptor-warning{background-color:#953ea9}.x-panel-header-text-container-nx-file-descriptor-warning{margin-left:6px;font-weight:bold}.x-panel-nx-subsection{border-color:#444;padding:0}.x-panel-header-nx-subsection{font-size:18px;border:1px solid #444}.x-panel-header-nx-subsection .x-tool-img{background-color:white}.x-panel-header-nx-subsection-horizontal{padding:9px 9px 10px 9px}.x-panel-header-nx-subsection-horizontal-noborder{padding:10px 10px 10px 10px}.x-panel-header-nx-subsection-vertical{padding:9px 9px 9px 10px}.x-panel-header-nx-subsection-vertical-noborder{padding:10px 10px 10px 10px}.x-panel-header-text-container-nx-subsection{color:#333;font-size:18px;font-weight:bold;font-family:arial,helvetica,verdana,sans-serif;line-height:22px;padding:1px 0 0;text-transform:none}.x-panel-body-nx-subsection{background:transparent;border-color:#444;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-header-nx-subsection{background-image:none;background-color:white}.x-panel-header-nx-subsection-vertical{background-image:none;background-color:white}.x-panel .x-panel-header-nx-subsection-collapsed-border-top{border-bottom-width:1px!important}.x-panel .x-panel-header-nx-subsection-collapsed-border-right{border-left-width:1px!important}.x-panel .x-panel-header-nx-subsection-collapsed-border-bottom{border-top-width:1px!important}.x-panel .x-panel-header-nx-subsection-collapsed-border-left{border-right-width:1px!important}.x-panel-header-nx-subsection-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-subsection-bottom:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-subsection-left:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-subsection-right:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-subsection-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-nx-subsection-vertical .x-panel-header-text-container{background-color:white;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=white)}.x-panel-header-nx-subsection .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-nx-subsection .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-nx-subsection .x-panel-header-glyph{color:white}.x-panel-header-nx-subsection-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-nx-subsection-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-nx-subsection-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-nx-subsection-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-nx-subsection-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-nx-subsection-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-nx-subsection-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-nx-subsection-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-nx-subsection-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-nx-subsection-outer-border-l{border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-outer-border-b{border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-subsection-outer-border-bl{border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-outer-border-r{border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-subsection-outer-border-rl{border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-outer-border-rb{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-subsection-outer-border-rbl{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-outer-border-t{border-top-color:#444!important;border-top-width:1px!important}.x-panel-nx-subsection-outer-border-tl{border-top-color:#444!important;border-top-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-outer-border-tb{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-subsection-outer-border-tbl{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-outer-border-tr{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-subsection-outer-border-trl{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-outer-border-trb{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-subsection-outer-border-trbl{border-color:#444!important;border-width:1px!important}.x-panel-nx-subsection-framed{border-color:#444;padding:0}.x-panel-header-nx-subsection-framed{font-size:18px;border:1px solid #444}.x-panel-header-nx-subsection-framed .x-tool-img{background-color:white}.x-panel-header-nx-subsection-framed-horizontal{padding:9px 9px 10px 9px}.x-panel-header-nx-subsection-framed-horizontal-noborder{padding:10px 10px 10px 10px}.x-panel-header-nx-subsection-framed-vertical{padding:9px 9px 9px 10px}.x-panel-header-nx-subsection-framed-vertical-noborder{padding:10px 10px 10px 10px}.x-panel-header-text-container-nx-subsection-framed{color:#333;font-size:18px;font-weight:bold;font-family:arial,helvetica,verdana,sans-serif;line-height:22px;padding:1px 0 0;text-transform:none}.x-panel-body-nx-subsection-framed{background:white;border-color:#444;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-header-nx-subsection-framed{background-image:none;background-color:white}.x-panel-header-nx-subsection-framed-vertical{background-image:none;background-color:white}.x-panel .x-panel-header-nx-subsection-framed-collapsed-border-top{border-bottom-width:1px!important}.x-panel .x-panel-header-nx-subsection-framed-collapsed-border-right{border-left-width:1px!important}.x-panel .x-panel-header-nx-subsection-framed-collapsed-border-bottom{border-top-width:1px!important}.x-panel .x-panel-header-nx-subsection-framed-collapsed-border-left{border-right-width:1px!important}.x-panel-header-nx-subsection-framed-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-subsection-framed-bottom:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-subsection-framed-left:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-subsection-framed-right:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-subsection-framed-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-nx-subsection-framed-vertical .x-panel-header-text-container{background-color:white;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=white)}.x-panel-header-nx-subsection-framed .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-nx-subsection-framed .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-nx-subsection-framed .x-panel-header-glyph{color:white}.x-panel-header-nx-subsection-framed-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-nx-subsection-framed-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-nx-subsection-framed-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-nx-subsection-framed-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-nx-subsection-framed-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-nx-subsection-framed-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-nx-subsection-framed-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-nx-subsection-framed-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-nx-subsection-framed-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-nx-subsection-framed-outer-border-l{border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-framed-outer-border-b{border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-subsection-framed-outer-border-bl{border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-framed-outer-border-r{border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-subsection-framed-outer-border-rl{border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-framed-outer-border-rb{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-subsection-framed-outer-border-rbl{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-framed-outer-border-t{border-top-color:#444!important;border-top-width:1px!important}.x-panel-nx-subsection-framed-outer-border-tl{border-top-color:#444!important;border-top-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-framed-outer-border-tb{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-subsection-framed-outer-border-tbl{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-framed-outer-border-tr{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-subsection-framed-outer-border-trl{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-subsection-framed-outer-border-trb{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-subsection-framed-outer-border-trbl{border-color:#444!important;border-width:1px!important}.x-panel-nx-subsection>.x-panel-header{padding:10px 0 5px 0!important}.x-panel-nx-subsection-framed{border:#ddd 1px solid}.x-panel-nx-subsection-framed>.x-panel-header{padding:10px 9px 5px 9px!important}.x-panel-nx-subsection-framed>.x-panel-body{padding:9px 9px 10px 9px!important}.nx-hr{border-top:1px solid #ddd!important}.x-panel-nx-info-message{border-color:#444;padding:0}.x-panel-header-nx-info-message{font-size:13px;border:1px solid #444}.x-panel-header-nx-info-message .x-tool-img{background-color:white}.x-panel-header-nx-info-message-horizontal{padding:9px 9px 10px 9px}.x-panel-header-nx-info-message-horizontal-noborder{padding:10px 10px 10px 10px}.x-panel-header-nx-info-message-vertical{padding:9px 9px 9px 10px}.x-panel-header-nx-info-message-vertical-noborder{padding:10px 10px 10px 10px}.x-panel-header-text-container-nx-info-message{color:#333;font-size:13px;font-weight:bold;font-family:arial,helvetica,verdana,sans-serif;line-height:15px;padding:1px 0 0;text-transform:none}.x-panel-body-nx-info-message{background:transparent;border-color:#444;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-header-nx-info-message{background-image:none;background-color:white}.x-panel-header-nx-info-message-vertical{background-image:none;background-color:white}.x-panel .x-panel-header-nx-info-message-collapsed-border-top{border-bottom-width:1px!important}.x-panel .x-panel-header-nx-info-message-collapsed-border-right{border-left-width:1px!important}.x-panel .x-panel-header-nx-info-message-collapsed-border-bottom{border-top-width:1px!important}.x-panel .x-panel-header-nx-info-message-collapsed-border-left{border-right-width:1px!important}.x-panel-header-nx-info-message-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-info-message-bottom:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-info-message-left:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-info-message-right:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-info-message-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-nx-info-message-vertical .x-panel-header-text-container{background-color:white;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=white)}.x-panel-header-nx-info-message .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-nx-info-message .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-nx-info-message .x-panel-header-glyph{color:white}.x-panel-header-nx-info-message-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-nx-info-message-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-nx-info-message-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-nx-info-message-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-nx-info-message-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-nx-info-message-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-nx-info-message-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-nx-info-message-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-nx-info-message-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-nx-info-message-outer-border-l{border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-info-message-outer-border-b{border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-info-message-outer-border-bl{border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-info-message-outer-border-r{border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-info-message-outer-border-rl{border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-info-message-outer-border-rb{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-info-message-outer-border-rbl{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-info-message-outer-border-t{border-top-color:#444!important;border-top-width:1px!important}.x-panel-nx-info-message-outer-border-tl{border-top-color:#444!important;border-top-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-info-message-outer-border-tb{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-info-message-outer-border-tbl{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-info-message-outer-border-tr{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-info-message-outer-border-trl{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-info-message-outer-border-trb{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-info-message-outer-border-trbl{border-color:#444!important;border-width:1px!important}.x-panel-nx-info-message .x-header{padding:0 10px 10px 10px}.nx-search-result-list{border-top:1px solid #ddd}.nx-repeated-row{margin-bottom:5px}.x-form-field[readonly].x-form-field:not([role="combobox"]){color:#777;background-color:#ebebeb;opacity:1}.nx-combo-disabled input{color:#777;background-color:#ebebeb;opacity:1}.x-form-fieldcontainer .x-mask{background-color:#ddd;opacity:.6}.nx-float-left{position:relative;float:left!important;margin-right:5px}.nx-interstitial-label{font-size:13px;padding-top:5px}.nx-clear-both{clear:both}.x-field .nx-boxlabel{font-size:10px}::-ms-clear{width:0;height:0}.x-window-nx-inset{border-color:#606060;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px}.x-window-nx-inset{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:12px 12px 2px 12px;border-width:2px;border-style:solid;background-color:white}.x-window-nx-inset-mc{background-color:white}.x-nbr .x-window-nx-inset{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-nx-inset-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-12-12-2-12}.x-window-nx-inset-tl{background-position:0 -8px}.x-window-nx-inset-tr{background-position:right -12px}.x-window-nx-inset-bl{background-position:0 -16px}.x-window-nx-inset-br{background-position:right -20px}.x-window-nx-inset-ml{background-position:0 top}.x-window-nx-inset-mr{background-position:right top}.x-window-nx-inset-tc{background-position:0 0}.x-window-nx-inset-bc{background-position:0 -4px}.x-window-nx-inset-tr,.x-window-nx-inset-br,.x-window-nx-inset-mr{padding-right:4px}.x-window-nx-inset-tl,.x-window-nx-inset-bl,.x-window-nx-inset-ml{padding-left:4px}.x-window-nx-inset-tc{height:4px}.x-window-nx-inset-bc{height:4px}.x-window-nx-inset-tl,.x-window-nx-inset-bl,.x-window-nx-inset-tr,.x-window-nx-inset-br,.x-window-nx-inset-tc,.x-window-nx-inset-bc,.x-window-nx-inset-ml,.x-window-nx-inset-mr{zoom:1;background-image:url(images/window/window-nx-inset-corners.gif)}.x-window-nx-inset-ml,.x-window-nx-inset-mr{zoom:1;background-image:url(images/window/window-nx-inset-sides.gif);background-repeat:repeat-y}.x-window-nx-inset-mc{padding:10px 10px 0 10px}.x-strict .x-ie7 .x-window-nx-inset-tl,.x-strict .x-ie7 .x-window-nx-inset-bl{position:relative;right:0}.x-window-nx-inset:after{display:none;content:"x-slicer:corners:url(images/window/window-nx-inset-corners.gif), sides:url(images/window/window-nx-inset-sides.gif)"}.x-window-body-nx-inset{border-color:#606060;border-width:1px;border-style:solid;background:white;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-window-header-nx-inset{font-size:18px;border-color:#606060;zoom:1;background-color:#ebebeb}.x-window-header-nx-inset .x-tool-img{background-image:url(images/tools/tool-sprites-dark.png);background-color:#ebebeb}.x-window-header-nx-inset-vertical .x-window-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-window-header-nx-inset-vertical .x-window-header-text-container{background-color:#ebebeb;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#ebebeb)}.x-window-header-text-container-nx-inset{color:#333;font-weight:bold;line-height:22px;font-family:arial,helvetica,verdana,sans-serif;font-size:18px;padding:1px 0 0;text-transform:none}.x-window-header-nx-inset-top{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:10px 10px 6px 14px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-inset-top-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-inset-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-inset-top-frameInfo{font-family:dh-4-4-0-0-0-0-0-0-10-10-6-14}.x-window-header-nx-inset-top-tl{background-position:0 -8px}.x-window-header-nx-inset-top-tr{background-position:right -12px}.x-window-header-nx-inset-top-bl{background-position:0 -16px}.x-window-header-nx-inset-top-br{background-position:right -20px}.x-window-header-nx-inset-top-ml{background-position:0 top}.x-window-header-nx-inset-top-mr{background-position:right top}.x-window-header-nx-inset-top-tc{background-position:0 0}.x-window-header-nx-inset-top-bc{background-position:0 -4px}.x-window-header-nx-inset-top-tr,.x-window-header-nx-inset-top-br,.x-window-header-nx-inset-top-mr{padding-right:4px}.x-window-header-nx-inset-top-tl,.x-window-header-nx-inset-top-bl,.x-window-header-nx-inset-top-ml{padding-left:4px}.x-window-header-nx-inset-top-tc{height:4px}.x-window-header-nx-inset-top-bc{height:0}.x-window-header-nx-inset-top-tl,.x-window-header-nx-inset-top-bl,.x-window-header-nx-inset-top-tr,.x-window-header-nx-inset-top-br,.x-window-header-nx-inset-top-tc,.x-window-header-nx-inset-top-bc,.x-window-header-nx-inset-top-ml,.x-window-header-nx-inset-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-top-corners.gif)}.x-window-header-nx-inset-top-ml,.x-window-header-nx-inset-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-top-sides.gif);background-repeat:repeat-y}.x-window-header-nx-inset-top-mc{padding:6px 6px 6px 10px}.x-strict .x-ie7 .x-window-header-nx-inset-top-tl,.x-strict .x-ie7 .x-window-header-nx-inset-top-bl{position:relative;right:0}.x-window-header-nx-inset-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-inset-top-corners.gif), sides:url(images/window-header/window-header-nx-inset-top-sides.gif)"}.x-window-header-nx-inset-right{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:14px 10px 10px 6px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-inset-right-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-inset-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-inset-right-frameInfo{font-family:dh-0-4-4-0-0-0-0-0-14-10-10-6}.x-window-header-nx-inset-right-tl{background-position:0 -8px}.x-window-header-nx-inset-right-tr{background-position:right -12px}.x-window-header-nx-inset-right-bl{background-position:0 -16px}.x-window-header-nx-inset-right-br{background-position:right -20px}.x-window-header-nx-inset-right-ml{background-position:0 top}.x-window-header-nx-inset-right-mr{background-position:right top}.x-window-header-nx-inset-right-tc{background-position:0 0}.x-window-header-nx-inset-right-bc{background-position:0 -4px}.x-window-header-nx-inset-right-tr,.x-window-header-nx-inset-right-br,.x-window-header-nx-inset-right-mr{padding-right:4px}.x-window-header-nx-inset-right-tl,.x-window-header-nx-inset-right-bl,.x-window-header-nx-inset-right-ml{padding-left:0}.x-window-header-nx-inset-right-tc{height:4px}.x-window-header-nx-inset-right-bc{height:4px}.x-window-header-nx-inset-right-tl,.x-window-header-nx-inset-right-bl,.x-window-header-nx-inset-right-tr,.x-window-header-nx-inset-right-br,.x-window-header-nx-inset-right-tc,.x-window-header-nx-inset-right-bc,.x-window-header-nx-inset-right-ml,.x-window-header-nx-inset-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-right-corners.gif)}.x-window-header-nx-inset-right-ml,.x-window-header-nx-inset-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-right-sides.gif);background-repeat:repeat-y}.x-window-header-nx-inset-right-mc{padding:10px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-inset-right-tl,.x-strict .x-ie7 .x-window-header-nx-inset-right-bl{position:relative;right:0}.x-window-header-nx-inset-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-inset-right-corners.gif), sides:url(images/window-header/window-header-nx-inset-right-sides.gif)"}.x-window-header-nx-inset-bottom{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:6px 14px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-inset-bottom-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-inset-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-inset-bottom-frameInfo{font-family:dh-0-0-4-4-0-0-0-0-6-14-10-10}.x-window-header-nx-inset-bottom-tl{background-position:0 -8px}.x-window-header-nx-inset-bottom-tr{background-position:right -12px}.x-window-header-nx-inset-bottom-bl{background-position:0 -16px}.x-window-header-nx-inset-bottom-br{background-position:right -20px}.x-window-header-nx-inset-bottom-ml{background-position:0 top}.x-window-header-nx-inset-bottom-mr{background-position:right top}.x-window-header-nx-inset-bottom-tc{background-position:0 0}.x-window-header-nx-inset-bottom-bc{background-position:0 -4px}.x-window-header-nx-inset-bottom-tr,.x-window-header-nx-inset-bottom-br,.x-window-header-nx-inset-bottom-mr{padding-right:4px}.x-window-header-nx-inset-bottom-tl,.x-window-header-nx-inset-bottom-bl,.x-window-header-nx-inset-bottom-ml{padding-left:4px}.x-window-header-nx-inset-bottom-tc{height:0}.x-window-header-nx-inset-bottom-bc{height:4px}.x-window-header-nx-inset-bottom-tl,.x-window-header-nx-inset-bottom-bl,.x-window-header-nx-inset-bottom-tr,.x-window-header-nx-inset-bottom-br,.x-window-header-nx-inset-bottom-tc,.x-window-header-nx-inset-bottom-bc,.x-window-header-nx-inset-bottom-ml,.x-window-header-nx-inset-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-bottom-corners.gif)}.x-window-header-nx-inset-bottom-ml,.x-window-header-nx-inset-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-nx-inset-bottom-mc{padding:6px 10px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-inset-bottom-tl,.x-strict .x-ie7 .x-window-header-nx-inset-bottom-bl{position:relative;right:0}.x-window-header-nx-inset-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-inset-bottom-corners.gif), sides:url(images/window-header/window-header-nx-inset-bottom-sides.gif)"}.x-window-header-nx-inset-left{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:10px 6px 14px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-inset-left-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-inset-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-inset-left-frameInfo{font-family:dh-4-0-0-4-0-0-0-0-10-6-14-10}.x-window-header-nx-inset-left-tl{background-position:0 -8px}.x-window-header-nx-inset-left-tr{background-position:right -12px}.x-window-header-nx-inset-left-bl{background-position:0 -16px}.x-window-header-nx-inset-left-br{background-position:right -20px}.x-window-header-nx-inset-left-ml{background-position:0 top}.x-window-header-nx-inset-left-mr{background-position:right top}.x-window-header-nx-inset-left-tc{background-position:0 0}.x-window-header-nx-inset-left-bc{background-position:0 -4px}.x-window-header-nx-inset-left-tr,.x-window-header-nx-inset-left-br,.x-window-header-nx-inset-left-mr{padding-right:0}.x-window-header-nx-inset-left-tl,.x-window-header-nx-inset-left-bl,.x-window-header-nx-inset-left-ml{padding-left:4px}.x-window-header-nx-inset-left-tc{height:4px}.x-window-header-nx-inset-left-bc{height:4px}.x-window-header-nx-inset-left-tl,.x-window-header-nx-inset-left-bl,.x-window-header-nx-inset-left-tr,.x-window-header-nx-inset-left-br,.x-window-header-nx-inset-left-tc,.x-window-header-nx-inset-left-bc,.x-window-header-nx-inset-left-ml,.x-window-header-nx-inset-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-left-corners.gif)}.x-window-header-nx-inset-left-ml,.x-window-header-nx-inset-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-left-sides.gif);background-repeat:repeat-y}.x-window-header-nx-inset-left-mc{padding:6px 6px 10px 6px}.x-strict .x-ie7 .x-window-header-nx-inset-left-tl,.x-strict .x-ie7 .x-window-header-nx-inset-left-bl{position:relative;right:0}.x-window-header-nx-inset-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-inset-left-corners.gif), sides:url(images/window-header/window-header-nx-inset-left-sides.gif)"}.x-window-header-nx-inset-collapsed-top{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 8px 14px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-inset-collapsed-top-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-inset-collapsed-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-inset-collapsed-top-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-8-14}.x-window-header-nx-inset-collapsed-top-tl{background-position:0 -8px}.x-window-header-nx-inset-collapsed-top-tr{background-position:right -12px}.x-window-header-nx-inset-collapsed-top-bl{background-position:0 -16px}.x-window-header-nx-inset-collapsed-top-br{background-position:right -20px}.x-window-header-nx-inset-collapsed-top-ml{background-position:0 top}.x-window-header-nx-inset-collapsed-top-mr{background-position:right top}.x-window-header-nx-inset-collapsed-top-tc{background-position:0 0}.x-window-header-nx-inset-collapsed-top-bc{background-position:0 -4px}.x-window-header-nx-inset-collapsed-top-tr,.x-window-header-nx-inset-collapsed-top-br,.x-window-header-nx-inset-collapsed-top-mr{padding-right:4px}.x-window-header-nx-inset-collapsed-top-tl,.x-window-header-nx-inset-collapsed-top-bl,.x-window-header-nx-inset-collapsed-top-ml{padding-left:4px}.x-window-header-nx-inset-collapsed-top-tc{height:4px}.x-window-header-nx-inset-collapsed-top-bc{height:4px}.x-window-header-nx-inset-collapsed-top-tl,.x-window-header-nx-inset-collapsed-top-bl,.x-window-header-nx-inset-collapsed-top-tr,.x-window-header-nx-inset-collapsed-top-br,.x-window-header-nx-inset-collapsed-top-tc,.x-window-header-nx-inset-collapsed-top-bc,.x-window-header-nx-inset-collapsed-top-ml,.x-window-header-nx-inset-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-collapsed-top-corners.gif)}.x-window-header-nx-inset-collapsed-top-ml,.x-window-header-nx-inset-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-collapsed-top-sides.gif);background-repeat:repeat-y}.x-window-header-nx-inset-collapsed-top-mc{padding:6px 6px 4px 10px}.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-top-tl,.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-top-bl{position:relative;right:0}.x-window-header-nx-inset-collapsed-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-inset-collapsed-top-corners.gif), sides:url(images/window-header/window-header-nx-inset-collapsed-top-sides.gif)"}.x-window-header-nx-inset-collapsed-right{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:14px 10px 10px 8px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-inset-collapsed-right-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-inset-collapsed-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-inset-collapsed-right-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-14-10-10-8}.x-window-header-nx-inset-collapsed-right-tl{background-position:0 -8px}.x-window-header-nx-inset-collapsed-right-tr{background-position:right -12px}.x-window-header-nx-inset-collapsed-right-bl{background-position:0 -16px}.x-window-header-nx-inset-collapsed-right-br{background-position:right -20px}.x-window-header-nx-inset-collapsed-right-ml{background-position:0 top}.x-window-header-nx-inset-collapsed-right-mr{background-position:right top}.x-window-header-nx-inset-collapsed-right-tc{background-position:0 0}.x-window-header-nx-inset-collapsed-right-bc{background-position:0 -4px}.x-window-header-nx-inset-collapsed-right-tr,.x-window-header-nx-inset-collapsed-right-br,.x-window-header-nx-inset-collapsed-right-mr{padding-right:4px}.x-window-header-nx-inset-collapsed-right-tl,.x-window-header-nx-inset-collapsed-right-bl,.x-window-header-nx-inset-collapsed-right-ml{padding-left:4px}.x-window-header-nx-inset-collapsed-right-tc{height:4px}.x-window-header-nx-inset-collapsed-right-bc{height:4px}.x-window-header-nx-inset-collapsed-right-tl,.x-window-header-nx-inset-collapsed-right-bl,.x-window-header-nx-inset-collapsed-right-tr,.x-window-header-nx-inset-collapsed-right-br,.x-window-header-nx-inset-collapsed-right-tc,.x-window-header-nx-inset-collapsed-right-bc,.x-window-header-nx-inset-collapsed-right-ml,.x-window-header-nx-inset-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-collapsed-right-corners.gif)}.x-window-header-nx-inset-collapsed-right-ml,.x-window-header-nx-inset-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-collapsed-right-sides.gif);background-repeat:repeat-y}.x-window-header-nx-inset-collapsed-right-mc{padding:10px 6px 6px 4px}.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-right-tl,.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-right-bl{position:relative;right:0}.x-window-header-nx-inset-collapsed-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-inset-collapsed-right-corners.gif), sides:url(images/window-header/window-header-nx-inset-collapsed-right-sides.gif)"}.x-window-header-nx-inset-collapsed-bottom{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:8px 14px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-inset-collapsed-bottom-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-inset-collapsed-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-inset-collapsed-bottom-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-8-14-10-10}.x-window-header-nx-inset-collapsed-bottom-tl{background-position:0 -8px}.x-window-header-nx-inset-collapsed-bottom-tr{background-position:right -12px}.x-window-header-nx-inset-collapsed-bottom-bl{background-position:0 -16px}.x-window-header-nx-inset-collapsed-bottom-br{background-position:right -20px}.x-window-header-nx-inset-collapsed-bottom-ml{background-position:0 top}.x-window-header-nx-inset-collapsed-bottom-mr{background-position:right top}.x-window-header-nx-inset-collapsed-bottom-tc{background-position:0 0}.x-window-header-nx-inset-collapsed-bottom-bc{background-position:0 -4px}.x-window-header-nx-inset-collapsed-bottom-tr,.x-window-header-nx-inset-collapsed-bottom-br,.x-window-header-nx-inset-collapsed-bottom-mr{padding-right:4px}.x-window-header-nx-inset-collapsed-bottom-tl,.x-window-header-nx-inset-collapsed-bottom-bl,.x-window-header-nx-inset-collapsed-bottom-ml{padding-left:4px}.x-window-header-nx-inset-collapsed-bottom-tc{height:4px}.x-window-header-nx-inset-collapsed-bottom-bc{height:4px}.x-window-header-nx-inset-collapsed-bottom-tl,.x-window-header-nx-inset-collapsed-bottom-bl,.x-window-header-nx-inset-collapsed-bottom-tr,.x-window-header-nx-inset-collapsed-bottom-br,.x-window-header-nx-inset-collapsed-bottom-tc,.x-window-header-nx-inset-collapsed-bottom-bc,.x-window-header-nx-inset-collapsed-bottom-ml,.x-window-header-nx-inset-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-collapsed-bottom-corners.gif)}.x-window-header-nx-inset-collapsed-bottom-ml,.x-window-header-nx-inset-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-collapsed-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-nx-inset-collapsed-bottom-mc{padding:4px 10px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-bottom-tl,.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-bottom-bl{position:relative;right:0}.x-window-header-nx-inset-collapsed-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-inset-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-nx-inset-collapsed-bottom-sides.gif)"}.x-window-header-nx-inset-collapsed-left{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 8px 14px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-inset-collapsed-left-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-inset-collapsed-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-inset-collapsed-left-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-8-14-10}.x-window-header-nx-inset-collapsed-left-tl{background-position:0 -8px}.x-window-header-nx-inset-collapsed-left-tr{background-position:right -12px}.x-window-header-nx-inset-collapsed-left-bl{background-position:0 -16px}.x-window-header-nx-inset-collapsed-left-br{background-position:right -20px}.x-window-header-nx-inset-collapsed-left-ml{background-position:0 top}.x-window-header-nx-inset-collapsed-left-mr{background-position:right top}.x-window-header-nx-inset-collapsed-left-tc{background-position:0 0}.x-window-header-nx-inset-collapsed-left-bc{background-position:0 -4px}.x-window-header-nx-inset-collapsed-left-tr,.x-window-header-nx-inset-collapsed-left-br,.x-window-header-nx-inset-collapsed-left-mr{padding-right:4px}.x-window-header-nx-inset-collapsed-left-tl,.x-window-header-nx-inset-collapsed-left-bl,.x-window-header-nx-inset-collapsed-left-ml{padding-left:4px}.x-window-header-nx-inset-collapsed-left-tc{height:4px}.x-window-header-nx-inset-collapsed-left-bc{height:4px}.x-window-header-nx-inset-collapsed-left-tl,.x-window-header-nx-inset-collapsed-left-bl,.x-window-header-nx-inset-collapsed-left-tr,.x-window-header-nx-inset-collapsed-left-br,.x-window-header-nx-inset-collapsed-left-tc,.x-window-header-nx-inset-collapsed-left-bc,.x-window-header-nx-inset-collapsed-left-ml,.x-window-header-nx-inset-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-collapsed-left-corners.gif)}.x-window-header-nx-inset-collapsed-left-ml,.x-window-header-nx-inset-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-inset-collapsed-left-sides.gif);background-repeat:repeat-y}.x-window-header-nx-inset-collapsed-left-mc{padding:6px 4px 10px 6px}.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-left-tl,.x-strict .x-ie7 .x-window-header-nx-inset-collapsed-left-bl{position:relative;right:0}.x-window-header-nx-inset-collapsed-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-inset-collapsed-left-corners.gif), sides:url(images/window-header/window-header-nx-inset-collapsed-left-sides.gif)"}.x-window-header-nx-inset .x-window-header-icon{width:16px;height:16px;color:#333;font-size:16px;line-height:16px;background-position:center center}.x-window-header-nx-inset .x-window-header-glyph{color:#333;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-window-header-nx-inset .x-window-header-glyph{color:#8f8f8f}.x-window-header-nx-inset-horizontal .x-window-header-icon-before-title{margin:0 6px 0 0}.x-window-header-nx-inset-horizontal .x-window-header-icon-after-title{margin:0 0 0 6px}.x-window-header-nx-inset-vertical .x-window-header-icon-before-title{margin:0 0 6px 0}.x-window-header-nx-inset-vertical .x-window-header-icon-after-title{margin:6px 0 0 0}.x-window-header-nx-inset-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-window-header-nx-inset-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-window-header-nx-inset-vertical .x-tool-after-title{margin:6px 0 0 0}.x-window-header-nx-inset-vertical .x-tool-before-title{margin:0 0 6px 0}.x-window-header-nx-inset{border-width:0!important}.x-nbr .x-window-nx-inset-collapsed .x-window-header{border-width:0!important}.x-window-nx-inset-outer-border-l{border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-inset-outer-border-b{border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-inset-outer-border-bl{border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-inset-outer-border-r{border-right-color:#606060!important;border-right-width:2px!important}.x-window-nx-inset-outer-border-rl{border-right-color:#606060!important;border-right-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-inset-outer-border-rb{border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-inset-outer-border-rbl{border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-inset-outer-border-t{border-top-color:#606060!important;border-top-width:2px!important}.x-window-nx-inset-outer-border-tl{border-top-color:#606060!important;border-top-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-inset-outer-border-tb{border-top-color:#606060!important;border-top-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-inset-outer-border-tbl{border-top-color:#606060!important;border-top-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-inset-outer-border-tr{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important}.x-window-nx-inset-outer-border-trl{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-inset-outer-border-trb{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-inset-outer-border-trbl{border-color:#606060!important;border-width:2px!important}.x-window-nx-message-default{border-color:#606060;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px}.x-window-nx-message-default{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:2px;border-style:solid;background-color:white}.x-window-nx-message-default-mc{background-color:white}.x-nbr .x-window-nx-message-default{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-nx-message-default-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-10-10-10-10}.x-window-nx-message-default-tl{background-position:0 -8px}.x-window-nx-message-default-tr{background-position:right -12px}.x-window-nx-message-default-bl{background-position:0 -16px}.x-window-nx-message-default-br{background-position:right -20px}.x-window-nx-message-default-ml{background-position:0 top}.x-window-nx-message-default-mr{background-position:right top}.x-window-nx-message-default-tc{background-position:0 0}.x-window-nx-message-default-bc{background-position:0 -4px}.x-window-nx-message-default-tr,.x-window-nx-message-default-br,.x-window-nx-message-default-mr{padding-right:4px}.x-window-nx-message-default-tl,.x-window-nx-message-default-bl,.x-window-nx-message-default-ml{padding-left:4px}.x-window-nx-message-default-tc{height:4px}.x-window-nx-message-default-bc{height:4px}.x-window-nx-message-default-tl,.x-window-nx-message-default-bl,.x-window-nx-message-default-tr,.x-window-nx-message-default-br,.x-window-nx-message-default-tc,.x-window-nx-message-default-bc,.x-window-nx-message-default-ml,.x-window-nx-message-default-mr{zoom:1;background-image:url(images/window/window-nx-message-default-corners.gif)}.x-window-nx-message-default-ml,.x-window-nx-message-default-mr{zoom:1;background-image:url(images/window/window-nx-message-default-sides.gif);background-repeat:repeat-y}.x-window-nx-message-default-mc{padding:8px 8px 8px 8px}.x-strict .x-ie7 .x-window-nx-message-default-tl,.x-strict .x-ie7 .x-window-nx-message-default-bl{position:relative;right:0}.x-window-nx-message-default:after{display:none;content:"x-slicer:corners:url(images/window/window-nx-message-default-corners.gif), sides:url(images/window/window-nx-message-default-sides.gif)"}.x-window-body-nx-message-default{border-color:#606060;border-width:1px;border-style:solid;background:white;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-window-header-nx-message-default{font-size:13px;border-color:#606060;zoom:1;background-color:#ebebeb}.x-window-header-nx-message-default .x-tool-img{background-image:url(images/tools/tool-sprites-dark.png);background-color:#ebebeb}.x-window-header-nx-message-default-vertical .x-window-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-window-header-nx-message-default-vertical .x-window-header-text-container{background-color:#ebebeb;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#ebebeb)}.x-window-header-text-container-nx-message-default{color:#333;font-weight:bold;line-height:15px;font-family:arial,helvetica,verdana,sans-serif;font-size:13px;padding:1px 0 0;text-transform:none}.x-window-header-nx-message-default-top{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:10px 10px 8px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-message-default-top-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-message-default-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-default-top-frameInfo{font-family:dh-4-4-0-0-0-0-0-0-10-10-8-10}.x-window-header-nx-message-default-top-tl{background-position:0 -8px}.x-window-header-nx-message-default-top-tr{background-position:right -12px}.x-window-header-nx-message-default-top-bl{background-position:0 -16px}.x-window-header-nx-message-default-top-br{background-position:right -20px}.x-window-header-nx-message-default-top-ml{background-position:0 top}.x-window-header-nx-message-default-top-mr{background-position:right top}.x-window-header-nx-message-default-top-tc{background-position:0 0}.x-window-header-nx-message-default-top-bc{background-position:0 -4px}.x-window-header-nx-message-default-top-tr,.x-window-header-nx-message-default-top-br,.x-window-header-nx-message-default-top-mr{padding-right:4px}.x-window-header-nx-message-default-top-tl,.x-window-header-nx-message-default-top-bl,.x-window-header-nx-message-default-top-ml{padding-left:4px}.x-window-header-nx-message-default-top-tc{height:4px}.x-window-header-nx-message-default-top-bc{height:0}.x-window-header-nx-message-default-top-tl,.x-window-header-nx-message-default-top-bl,.x-window-header-nx-message-default-top-tr,.x-window-header-nx-message-default-top-br,.x-window-header-nx-message-default-top-tc,.x-window-header-nx-message-default-top-bc,.x-window-header-nx-message-default-top-ml,.x-window-header-nx-message-default-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-top-corners.gif)}.x-window-header-nx-message-default-top-ml,.x-window-header-nx-message-default-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-top-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-default-top-mc{padding:6px 6px 8px 6px}.x-strict .x-ie7 .x-window-header-nx-message-default-top-tl,.x-strict .x-ie7 .x-window-header-nx-message-default-top-bl{position:relative;right:0}.x-window-header-nx-message-default-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-default-top-corners.gif), sides:url(images/window-header/window-header-nx-message-default-top-sides.gif)"}.x-window-header-nx-message-default-right{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:10px 10px 10px 8px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-message-default-right-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-message-default-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-default-right-frameInfo{font-family:dh-0-4-4-0-0-0-0-0-10-10-10-8}.x-window-header-nx-message-default-right-tl{background-position:0 -8px}.x-window-header-nx-message-default-right-tr{background-position:right -12px}.x-window-header-nx-message-default-right-bl{background-position:0 -16px}.x-window-header-nx-message-default-right-br{background-position:right -20px}.x-window-header-nx-message-default-right-ml{background-position:0 top}.x-window-header-nx-message-default-right-mr{background-position:right top}.x-window-header-nx-message-default-right-tc{background-position:0 0}.x-window-header-nx-message-default-right-bc{background-position:0 -4px}.x-window-header-nx-message-default-right-tr,.x-window-header-nx-message-default-right-br,.x-window-header-nx-message-default-right-mr{padding-right:4px}.x-window-header-nx-message-default-right-tl,.x-window-header-nx-message-default-right-bl,.x-window-header-nx-message-default-right-ml{padding-left:0}.x-window-header-nx-message-default-right-tc{height:4px}.x-window-header-nx-message-default-right-bc{height:4px}.x-window-header-nx-message-default-right-tl,.x-window-header-nx-message-default-right-bl,.x-window-header-nx-message-default-right-tr,.x-window-header-nx-message-default-right-br,.x-window-header-nx-message-default-right-tc,.x-window-header-nx-message-default-right-bc,.x-window-header-nx-message-default-right-ml,.x-window-header-nx-message-default-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-right-corners.gif)}.x-window-header-nx-message-default-right-ml,.x-window-header-nx-message-default-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-right-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-default-right-mc{padding:6px 6px 6px 8px}.x-strict .x-ie7 .x-window-header-nx-message-default-right-tl,.x-strict .x-ie7 .x-window-header-nx-message-default-right-bl{position:relative;right:0}.x-window-header-nx-message-default-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-default-right-corners.gif), sides:url(images/window-header/window-header-nx-message-default-right-sides.gif)"}.x-window-header-nx-message-default-bottom{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:8px 10px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-message-default-bottom-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-message-default-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-default-bottom-frameInfo{font-family:dh-0-0-4-4-0-0-0-0-8-10-10-10}.x-window-header-nx-message-default-bottom-tl{background-position:0 -8px}.x-window-header-nx-message-default-bottom-tr{background-position:right -12px}.x-window-header-nx-message-default-bottom-bl{background-position:0 -16px}.x-window-header-nx-message-default-bottom-br{background-position:right -20px}.x-window-header-nx-message-default-bottom-ml{background-position:0 top}.x-window-header-nx-message-default-bottom-mr{background-position:right top}.x-window-header-nx-message-default-bottom-tc{background-position:0 0}.x-window-header-nx-message-default-bottom-bc{background-position:0 -4px}.x-window-header-nx-message-default-bottom-tr,.x-window-header-nx-message-default-bottom-br,.x-window-header-nx-message-default-bottom-mr{padding-right:4px}.x-window-header-nx-message-default-bottom-tl,.x-window-header-nx-message-default-bottom-bl,.x-window-header-nx-message-default-bottom-ml{padding-left:4px}.x-window-header-nx-message-default-bottom-tc{height:0}.x-window-header-nx-message-default-bottom-bc{height:4px}.x-window-header-nx-message-default-bottom-tl,.x-window-header-nx-message-default-bottom-bl,.x-window-header-nx-message-default-bottom-tr,.x-window-header-nx-message-default-bottom-br,.x-window-header-nx-message-default-bottom-tc,.x-window-header-nx-message-default-bottom-bc,.x-window-header-nx-message-default-bottom-ml,.x-window-header-nx-message-default-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-bottom-corners.gif)}.x-window-header-nx-message-default-bottom-ml,.x-window-header-nx-message-default-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-default-bottom-mc{padding:8px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-default-bottom-tl,.x-strict .x-ie7 .x-window-header-nx-message-default-bottom-bl{position:relative;right:0}.x-window-header-nx-message-default-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-default-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-default-bottom-sides.gif)"}.x-window-header-nx-message-default-left{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:10px 8px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-message-default-left-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-message-default-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-default-left-frameInfo{font-family:dh-4-0-0-4-0-0-0-0-10-8-10-10}.x-window-header-nx-message-default-left-tl{background-position:0 -8px}.x-window-header-nx-message-default-left-tr{background-position:right -12px}.x-window-header-nx-message-default-left-bl{background-position:0 -16px}.x-window-header-nx-message-default-left-br{background-position:right -20px}.x-window-header-nx-message-default-left-ml{background-position:0 top}.x-window-header-nx-message-default-left-mr{background-position:right top}.x-window-header-nx-message-default-left-tc{background-position:0 0}.x-window-header-nx-message-default-left-bc{background-position:0 -4px}.x-window-header-nx-message-default-left-tr,.x-window-header-nx-message-default-left-br,.x-window-header-nx-message-default-left-mr{padding-right:0}.x-window-header-nx-message-default-left-tl,.x-window-header-nx-message-default-left-bl,.x-window-header-nx-message-default-left-ml{padding-left:4px}.x-window-header-nx-message-default-left-tc{height:4px}.x-window-header-nx-message-default-left-bc{height:4px}.x-window-header-nx-message-default-left-tl,.x-window-header-nx-message-default-left-bl,.x-window-header-nx-message-default-left-tr,.x-window-header-nx-message-default-left-br,.x-window-header-nx-message-default-left-tc,.x-window-header-nx-message-default-left-bc,.x-window-header-nx-message-default-left-ml,.x-window-header-nx-message-default-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-left-corners.gif)}.x-window-header-nx-message-default-left-ml,.x-window-header-nx-message-default-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-left-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-default-left-mc{padding:6px 8px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-default-left-tl,.x-strict .x-ie7 .x-window-header-nx-message-default-left-bl{position:relative;right:0}.x-window-header-nx-message-default-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-default-left-corners.gif), sides:url(images/window-header/window-header-nx-message-default-left-sides.gif)"}.x-window-header-nx-message-default-collapsed-top{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-message-default-collapsed-top-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-message-default-collapsed-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-default-collapsed-top-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-default-collapsed-top-tl{background-position:0 -8px}.x-window-header-nx-message-default-collapsed-top-tr{background-position:right -12px}.x-window-header-nx-message-default-collapsed-top-bl{background-position:0 -16px}.x-window-header-nx-message-default-collapsed-top-br{background-position:right -20px}.x-window-header-nx-message-default-collapsed-top-ml{background-position:0 top}.x-window-header-nx-message-default-collapsed-top-mr{background-position:right top}.x-window-header-nx-message-default-collapsed-top-tc{background-position:0 0}.x-window-header-nx-message-default-collapsed-top-bc{background-position:0 -4px}.x-window-header-nx-message-default-collapsed-top-tr,.x-window-header-nx-message-default-collapsed-top-br,.x-window-header-nx-message-default-collapsed-top-mr{padding-right:4px}.x-window-header-nx-message-default-collapsed-top-tl,.x-window-header-nx-message-default-collapsed-top-bl,.x-window-header-nx-message-default-collapsed-top-ml{padding-left:4px}.x-window-header-nx-message-default-collapsed-top-tc{height:4px}.x-window-header-nx-message-default-collapsed-top-bc{height:4px}.x-window-header-nx-message-default-collapsed-top-tl,.x-window-header-nx-message-default-collapsed-top-bl,.x-window-header-nx-message-default-collapsed-top-tr,.x-window-header-nx-message-default-collapsed-top-br,.x-window-header-nx-message-default-collapsed-top-tc,.x-window-header-nx-message-default-collapsed-top-bc,.x-window-header-nx-message-default-collapsed-top-ml,.x-window-header-nx-message-default-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-collapsed-top-corners.gif)}.x-window-header-nx-message-default-collapsed-top-ml,.x-window-header-nx-message-default-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-collapsed-top-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-default-collapsed-top-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-top-tl,.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-top-bl{position:relative;right:0}.x-window-header-nx-message-default-collapsed-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-default-collapsed-top-corners.gif), sides:url(images/window-header/window-header-nx-message-default-collapsed-top-sides.gif)"}.x-window-header-nx-message-default-collapsed-right{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-message-default-collapsed-right-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-message-default-collapsed-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-default-collapsed-right-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-default-collapsed-right-tl{background-position:0 -8px}.x-window-header-nx-message-default-collapsed-right-tr{background-position:right -12px}.x-window-header-nx-message-default-collapsed-right-bl{background-position:0 -16px}.x-window-header-nx-message-default-collapsed-right-br{background-position:right -20px}.x-window-header-nx-message-default-collapsed-right-ml{background-position:0 top}.x-window-header-nx-message-default-collapsed-right-mr{background-position:right top}.x-window-header-nx-message-default-collapsed-right-tc{background-position:0 0}.x-window-header-nx-message-default-collapsed-right-bc{background-position:0 -4px}.x-window-header-nx-message-default-collapsed-right-tr,.x-window-header-nx-message-default-collapsed-right-br,.x-window-header-nx-message-default-collapsed-right-mr{padding-right:4px}.x-window-header-nx-message-default-collapsed-right-tl,.x-window-header-nx-message-default-collapsed-right-bl,.x-window-header-nx-message-default-collapsed-right-ml{padding-left:4px}.x-window-header-nx-message-default-collapsed-right-tc{height:4px}.x-window-header-nx-message-default-collapsed-right-bc{height:4px}.x-window-header-nx-message-default-collapsed-right-tl,.x-window-header-nx-message-default-collapsed-right-bl,.x-window-header-nx-message-default-collapsed-right-tr,.x-window-header-nx-message-default-collapsed-right-br,.x-window-header-nx-message-default-collapsed-right-tc,.x-window-header-nx-message-default-collapsed-right-bc,.x-window-header-nx-message-default-collapsed-right-ml,.x-window-header-nx-message-default-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-collapsed-right-corners.gif)}.x-window-header-nx-message-default-collapsed-right-ml,.x-window-header-nx-message-default-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-collapsed-right-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-default-collapsed-right-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-right-tl,.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-right-bl{position:relative;right:0}.x-window-header-nx-message-default-collapsed-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-default-collapsed-right-corners.gif), sides:url(images/window-header/window-header-nx-message-default-collapsed-right-sides.gif)"}.x-window-header-nx-message-default-collapsed-bottom{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-message-default-collapsed-bottom-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-message-default-collapsed-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-default-collapsed-bottom-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-default-collapsed-bottom-tl{background-position:0 -8px}.x-window-header-nx-message-default-collapsed-bottom-tr{background-position:right -12px}.x-window-header-nx-message-default-collapsed-bottom-bl{background-position:0 -16px}.x-window-header-nx-message-default-collapsed-bottom-br{background-position:right -20px}.x-window-header-nx-message-default-collapsed-bottom-ml{background-position:0 top}.x-window-header-nx-message-default-collapsed-bottom-mr{background-position:right top}.x-window-header-nx-message-default-collapsed-bottom-tc{background-position:0 0}.x-window-header-nx-message-default-collapsed-bottom-bc{background-position:0 -4px}.x-window-header-nx-message-default-collapsed-bottom-tr,.x-window-header-nx-message-default-collapsed-bottom-br,.x-window-header-nx-message-default-collapsed-bottom-mr{padding-right:4px}.x-window-header-nx-message-default-collapsed-bottom-tl,.x-window-header-nx-message-default-collapsed-bottom-bl,.x-window-header-nx-message-default-collapsed-bottom-ml{padding-left:4px}.x-window-header-nx-message-default-collapsed-bottom-tc{height:4px}.x-window-header-nx-message-default-collapsed-bottom-bc{height:4px}.x-window-header-nx-message-default-collapsed-bottom-tl,.x-window-header-nx-message-default-collapsed-bottom-bl,.x-window-header-nx-message-default-collapsed-bottom-tr,.x-window-header-nx-message-default-collapsed-bottom-br,.x-window-header-nx-message-default-collapsed-bottom-tc,.x-window-header-nx-message-default-collapsed-bottom-bc,.x-window-header-nx-message-default-collapsed-bottom-ml,.x-window-header-nx-message-default-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-collapsed-bottom-corners.gif)}.x-window-header-nx-message-default-collapsed-bottom-ml,.x-window-header-nx-message-default-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-collapsed-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-default-collapsed-bottom-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-bottom-tl,.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-bottom-bl{position:relative;right:0}.x-window-header-nx-message-default-collapsed-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-default-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-default-collapsed-bottom-sides.gif)"}.x-window-header-nx-message-default-collapsed-left{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#ebebeb}.x-window-header-nx-message-default-collapsed-left-mc{background-color:#ebebeb}.x-nbr .x-window-header-nx-message-default-collapsed-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-default-collapsed-left-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-default-collapsed-left-tl{background-position:0 -8px}.x-window-header-nx-message-default-collapsed-left-tr{background-position:right -12px}.x-window-header-nx-message-default-collapsed-left-bl{background-position:0 -16px}.x-window-header-nx-message-default-collapsed-left-br{background-position:right -20px}.x-window-header-nx-message-default-collapsed-left-ml{background-position:0 top}.x-window-header-nx-message-default-collapsed-left-mr{background-position:right top}.x-window-header-nx-message-default-collapsed-left-tc{background-position:0 0}.x-window-header-nx-message-default-collapsed-left-bc{background-position:0 -4px}.x-window-header-nx-message-default-collapsed-left-tr,.x-window-header-nx-message-default-collapsed-left-br,.x-window-header-nx-message-default-collapsed-left-mr{padding-right:4px}.x-window-header-nx-message-default-collapsed-left-tl,.x-window-header-nx-message-default-collapsed-left-bl,.x-window-header-nx-message-default-collapsed-left-ml{padding-left:4px}.x-window-header-nx-message-default-collapsed-left-tc{height:4px}.x-window-header-nx-message-default-collapsed-left-bc{height:4px}.x-window-header-nx-message-default-collapsed-left-tl,.x-window-header-nx-message-default-collapsed-left-bl,.x-window-header-nx-message-default-collapsed-left-tr,.x-window-header-nx-message-default-collapsed-left-br,.x-window-header-nx-message-default-collapsed-left-tc,.x-window-header-nx-message-default-collapsed-left-bc,.x-window-header-nx-message-default-collapsed-left-ml,.x-window-header-nx-message-default-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-collapsed-left-corners.gif)}.x-window-header-nx-message-default-collapsed-left-ml,.x-window-header-nx-message-default-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-default-collapsed-left-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-default-collapsed-left-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-left-tl,.x-strict .x-ie7 .x-window-header-nx-message-default-collapsed-left-bl{position:relative;right:0}.x-window-header-nx-message-default-collapsed-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-default-collapsed-left-corners.gif), sides:url(images/window-header/window-header-nx-message-default-collapsed-left-sides.gif)"}.x-window-header-nx-message-default .x-window-header-icon{width:16px;height:16px;color:#333;font-size:16px;line-height:16px;background-position:center center}.x-window-header-nx-message-default .x-window-header-glyph{color:#333;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-window-header-nx-message-default .x-window-header-glyph{color:#8f8f8f}.x-window-header-nx-message-default-horizontal .x-window-header-icon-before-title{margin:0 6px 0 0}.x-window-header-nx-message-default-horizontal .x-window-header-icon-after-title{margin:0 0 0 6px}.x-window-header-nx-message-default-vertical .x-window-header-icon-before-title{margin:0 0 6px 0}.x-window-header-nx-message-default-vertical .x-window-header-icon-after-title{margin:6px 0 0 0}.x-window-header-nx-message-default-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-window-header-nx-message-default-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-window-header-nx-message-default-vertical .x-tool-after-title{margin:6px 0 0 0}.x-window-header-nx-message-default-vertical .x-tool-before-title{margin:0 0 6px 0}.x-window-header-nx-message-default{border-width:0!important}.x-nbr .x-window-nx-message-default-collapsed .x-window-header{border-width:0!important}.x-window-nx-message-default-outer-border-l{border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-default-outer-border-b{border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-default-outer-border-bl{border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-default-outer-border-r{border-right-color:#606060!important;border-right-width:2px!important}.x-window-nx-message-default-outer-border-rl{border-right-color:#606060!important;border-right-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-default-outer-border-rb{border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-default-outer-border-rbl{border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-default-outer-border-t{border-top-color:#606060!important;border-top-width:2px!important}.x-window-nx-message-default-outer-border-tl{border-top-color:#606060!important;border-top-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-default-outer-border-tb{border-top-color:#606060!important;border-top-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-default-outer-border-tbl{border-top-color:#606060!important;border-top-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-default-outer-border-tr{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important}.x-window-nx-message-default-outer-border-trl{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-default-outer-border-trb{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-default-outer-border-trbl{border-color:#606060!important;border-width:2px!important}.x-window-nx-message-primary{border-color:#606060;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px}.x-window-nx-message-primary{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:2px;border-style:solid;background-color:white}.x-window-nx-message-primary-mc{background-color:white}.x-nbr .x-window-nx-message-primary{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-nx-message-primary-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-10-10-10-10}.x-window-nx-message-primary-tl{background-position:0 -8px}.x-window-nx-message-primary-tr{background-position:right -12px}.x-window-nx-message-primary-bl{background-position:0 -16px}.x-window-nx-message-primary-br{background-position:right -20px}.x-window-nx-message-primary-ml{background-position:0 top}.x-window-nx-message-primary-mr{background-position:right top}.x-window-nx-message-primary-tc{background-position:0 0}.x-window-nx-message-primary-bc{background-position:0 -4px}.x-window-nx-message-primary-tr,.x-window-nx-message-primary-br,.x-window-nx-message-primary-mr{padding-right:4px}.x-window-nx-message-primary-tl,.x-window-nx-message-primary-bl,.x-window-nx-message-primary-ml{padding-left:4px}.x-window-nx-message-primary-tc{height:4px}.x-window-nx-message-primary-bc{height:4px}.x-window-nx-message-primary-tl,.x-window-nx-message-primary-bl,.x-window-nx-message-primary-tr,.x-window-nx-message-primary-br,.x-window-nx-message-primary-tc,.x-window-nx-message-primary-bc,.x-window-nx-message-primary-ml,.x-window-nx-message-primary-mr{zoom:1;background-image:url(images/window/window-nx-message-primary-corners.gif)}.x-window-nx-message-primary-ml,.x-window-nx-message-primary-mr{zoom:1;background-image:url(images/window/window-nx-message-primary-sides.gif);background-repeat:repeat-y}.x-window-nx-message-primary-mc{padding:8px 8px 8px 8px}.x-strict .x-ie7 .x-window-nx-message-primary-tl,.x-strict .x-ie7 .x-window-nx-message-primary-bl{position:relative;right:0}.x-window-nx-message-primary:after{display:none;content:"x-slicer:corners:url(images/window/window-nx-message-primary-corners.gif), sides:url(images/window/window-nx-message-primary-sides.gif)"}.x-window-body-nx-message-primary{border-color:#606060;border-width:1px;border-style:solid;background:white;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-window-header-nx-message-primary{font-size:13px;border-color:#606060;zoom:1;background-color:#0047b2}.x-window-header-nx-message-primary .x-tool-img{background-color:#0047b2}.x-window-header-nx-message-primary-vertical .x-window-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-window-header-nx-message-primary-vertical .x-window-header-text-container{background-color:#0047b2;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#0047b2)}.x-window-header-text-container-nx-message-primary{color:white;font-weight:bold;line-height:15px;font-family:arial,helvetica,verdana,sans-serif;font-size:13px;padding:1px 0 0;text-transform:none}.x-window-header-nx-message-primary-top{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:10px 10px 8px 10px;border-width:0;border-style:solid;background-color:#0047b2}.x-window-header-nx-message-primary-top-mc{background-color:#0047b2}.x-nbr .x-window-header-nx-message-primary-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-primary-top-frameInfo{font-family:dh-4-4-0-0-0-0-0-0-10-10-8-10}.x-window-header-nx-message-primary-top-tl{background-position:0 -8px}.x-window-header-nx-message-primary-top-tr{background-position:right -12px}.x-window-header-nx-message-primary-top-bl{background-position:0 -16px}.x-window-header-nx-message-primary-top-br{background-position:right -20px}.x-window-header-nx-message-primary-top-ml{background-position:0 top}.x-window-header-nx-message-primary-top-mr{background-position:right top}.x-window-header-nx-message-primary-top-tc{background-position:0 0}.x-window-header-nx-message-primary-top-bc{background-position:0 -4px}.x-window-header-nx-message-primary-top-tr,.x-window-header-nx-message-primary-top-br,.x-window-header-nx-message-primary-top-mr{padding-right:4px}.x-window-header-nx-message-primary-top-tl,.x-window-header-nx-message-primary-top-bl,.x-window-header-nx-message-primary-top-ml{padding-left:4px}.x-window-header-nx-message-primary-top-tc{height:4px}.x-window-header-nx-message-primary-top-bc{height:0}.x-window-header-nx-message-primary-top-tl,.x-window-header-nx-message-primary-top-bl,.x-window-header-nx-message-primary-top-tr,.x-window-header-nx-message-primary-top-br,.x-window-header-nx-message-primary-top-tc,.x-window-header-nx-message-primary-top-bc,.x-window-header-nx-message-primary-top-ml,.x-window-header-nx-message-primary-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-top-corners.gif)}.x-window-header-nx-message-primary-top-ml,.x-window-header-nx-message-primary-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-top-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-primary-top-mc{padding:6px 6px 8px 6px}.x-strict .x-ie7 .x-window-header-nx-message-primary-top-tl,.x-strict .x-ie7 .x-window-header-nx-message-primary-top-bl{position:relative;right:0}.x-window-header-nx-message-primary-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-primary-top-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-top-sides.gif)"}.x-window-header-nx-message-primary-right{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:10px 10px 10px 8px;border-width:0;border-style:solid;background-color:#0047b2}.x-window-header-nx-message-primary-right-mc{background-color:#0047b2}.x-nbr .x-window-header-nx-message-primary-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-primary-right-frameInfo{font-family:dh-0-4-4-0-0-0-0-0-10-10-10-8}.x-window-header-nx-message-primary-right-tl{background-position:0 -8px}.x-window-header-nx-message-primary-right-tr{background-position:right -12px}.x-window-header-nx-message-primary-right-bl{background-position:0 -16px}.x-window-header-nx-message-primary-right-br{background-position:right -20px}.x-window-header-nx-message-primary-right-ml{background-position:0 top}.x-window-header-nx-message-primary-right-mr{background-position:right top}.x-window-header-nx-message-primary-right-tc{background-position:0 0}.x-window-header-nx-message-primary-right-bc{background-position:0 -4px}.x-window-header-nx-message-primary-right-tr,.x-window-header-nx-message-primary-right-br,.x-window-header-nx-message-primary-right-mr{padding-right:4px}.x-window-header-nx-message-primary-right-tl,.x-window-header-nx-message-primary-right-bl,.x-window-header-nx-message-primary-right-ml{padding-left:0}.x-window-header-nx-message-primary-right-tc{height:4px}.x-window-header-nx-message-primary-right-bc{height:4px}.x-window-header-nx-message-primary-right-tl,.x-window-header-nx-message-primary-right-bl,.x-window-header-nx-message-primary-right-tr,.x-window-header-nx-message-primary-right-br,.x-window-header-nx-message-primary-right-tc,.x-window-header-nx-message-primary-right-bc,.x-window-header-nx-message-primary-right-ml,.x-window-header-nx-message-primary-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-right-corners.gif)}.x-window-header-nx-message-primary-right-ml,.x-window-header-nx-message-primary-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-right-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-primary-right-mc{padding:6px 6px 6px 8px}.x-strict .x-ie7 .x-window-header-nx-message-primary-right-tl,.x-strict .x-ie7 .x-window-header-nx-message-primary-right-bl{position:relative;right:0}.x-window-header-nx-message-primary-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-primary-right-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-right-sides.gif)"}.x-window-header-nx-message-primary-bottom{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:8px 10px 10px 10px;border-width:0;border-style:solid;background-color:#0047b2}.x-window-header-nx-message-primary-bottom-mc{background-color:#0047b2}.x-nbr .x-window-header-nx-message-primary-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-primary-bottom-frameInfo{font-family:dh-0-0-4-4-0-0-0-0-8-10-10-10}.x-window-header-nx-message-primary-bottom-tl{background-position:0 -8px}.x-window-header-nx-message-primary-bottom-tr{background-position:right -12px}.x-window-header-nx-message-primary-bottom-bl{background-position:0 -16px}.x-window-header-nx-message-primary-bottom-br{background-position:right -20px}.x-window-header-nx-message-primary-bottom-ml{background-position:0 top}.x-window-header-nx-message-primary-bottom-mr{background-position:right top}.x-window-header-nx-message-primary-bottom-tc{background-position:0 0}.x-window-header-nx-message-primary-bottom-bc{background-position:0 -4px}.x-window-header-nx-message-primary-bottom-tr,.x-window-header-nx-message-primary-bottom-br,.x-window-header-nx-message-primary-bottom-mr{padding-right:4px}.x-window-header-nx-message-primary-bottom-tl,.x-window-header-nx-message-primary-bottom-bl,.x-window-header-nx-message-primary-bottom-ml{padding-left:4px}.x-window-header-nx-message-primary-bottom-tc{height:0}.x-window-header-nx-message-primary-bottom-bc{height:4px}.x-window-header-nx-message-primary-bottom-tl,.x-window-header-nx-message-primary-bottom-bl,.x-window-header-nx-message-primary-bottom-tr,.x-window-header-nx-message-primary-bottom-br,.x-window-header-nx-message-primary-bottom-tc,.x-window-header-nx-message-primary-bottom-bc,.x-window-header-nx-message-primary-bottom-ml,.x-window-header-nx-message-primary-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-bottom-corners.gif)}.x-window-header-nx-message-primary-bottom-ml,.x-window-header-nx-message-primary-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-primary-bottom-mc{padding:8px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-primary-bottom-tl,.x-strict .x-ie7 .x-window-header-nx-message-primary-bottom-bl{position:relative;right:0}.x-window-header-nx-message-primary-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-primary-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-bottom-sides.gif)"}.x-window-header-nx-message-primary-left{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:10px 8px 10px 10px;border-width:0;border-style:solid;background-color:#0047b2}.x-window-header-nx-message-primary-left-mc{background-color:#0047b2}.x-nbr .x-window-header-nx-message-primary-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-primary-left-frameInfo{font-family:dh-4-0-0-4-0-0-0-0-10-8-10-10}.x-window-header-nx-message-primary-left-tl{background-position:0 -8px}.x-window-header-nx-message-primary-left-tr{background-position:right -12px}.x-window-header-nx-message-primary-left-bl{background-position:0 -16px}.x-window-header-nx-message-primary-left-br{background-position:right -20px}.x-window-header-nx-message-primary-left-ml{background-position:0 top}.x-window-header-nx-message-primary-left-mr{background-position:right top}.x-window-header-nx-message-primary-left-tc{background-position:0 0}.x-window-header-nx-message-primary-left-bc{background-position:0 -4px}.x-window-header-nx-message-primary-left-tr,.x-window-header-nx-message-primary-left-br,.x-window-header-nx-message-primary-left-mr{padding-right:0}.x-window-header-nx-message-primary-left-tl,.x-window-header-nx-message-primary-left-bl,.x-window-header-nx-message-primary-left-ml{padding-left:4px}.x-window-header-nx-message-primary-left-tc{height:4px}.x-window-header-nx-message-primary-left-bc{height:4px}.x-window-header-nx-message-primary-left-tl,.x-window-header-nx-message-primary-left-bl,.x-window-header-nx-message-primary-left-tr,.x-window-header-nx-message-primary-left-br,.x-window-header-nx-message-primary-left-tc,.x-window-header-nx-message-primary-left-bc,.x-window-header-nx-message-primary-left-ml,.x-window-header-nx-message-primary-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-left-corners.gif)}.x-window-header-nx-message-primary-left-ml,.x-window-header-nx-message-primary-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-left-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-primary-left-mc{padding:6px 8px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-primary-left-tl,.x-strict .x-ie7 .x-window-header-nx-message-primary-left-bl{position:relative;right:0}.x-window-header-nx-message-primary-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-primary-left-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-left-sides.gif)"}.x-window-header-nx-message-primary-collapsed-top{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#0047b2}.x-window-header-nx-message-primary-collapsed-top-mc{background-color:#0047b2}.x-nbr .x-window-header-nx-message-primary-collapsed-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-primary-collapsed-top-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-primary-collapsed-top-tl{background-position:0 -8px}.x-window-header-nx-message-primary-collapsed-top-tr{background-position:right -12px}.x-window-header-nx-message-primary-collapsed-top-bl{background-position:0 -16px}.x-window-header-nx-message-primary-collapsed-top-br{background-position:right -20px}.x-window-header-nx-message-primary-collapsed-top-ml{background-position:0 top}.x-window-header-nx-message-primary-collapsed-top-mr{background-position:right top}.x-window-header-nx-message-primary-collapsed-top-tc{background-position:0 0}.x-window-header-nx-message-primary-collapsed-top-bc{background-position:0 -4px}.x-window-header-nx-message-primary-collapsed-top-tr,.x-window-header-nx-message-primary-collapsed-top-br,.x-window-header-nx-message-primary-collapsed-top-mr{padding-right:4px}.x-window-header-nx-message-primary-collapsed-top-tl,.x-window-header-nx-message-primary-collapsed-top-bl,.x-window-header-nx-message-primary-collapsed-top-ml{padding-left:4px}.x-window-header-nx-message-primary-collapsed-top-tc{height:4px}.x-window-header-nx-message-primary-collapsed-top-bc{height:4px}.x-window-header-nx-message-primary-collapsed-top-tl,.x-window-header-nx-message-primary-collapsed-top-bl,.x-window-header-nx-message-primary-collapsed-top-tr,.x-window-header-nx-message-primary-collapsed-top-br,.x-window-header-nx-message-primary-collapsed-top-tc,.x-window-header-nx-message-primary-collapsed-top-bc,.x-window-header-nx-message-primary-collapsed-top-ml,.x-window-header-nx-message-primary-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-collapsed-top-corners.gif)}.x-window-header-nx-message-primary-collapsed-top-ml,.x-window-header-nx-message-primary-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-collapsed-top-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-primary-collapsed-top-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-top-tl,.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-top-bl{position:relative;right:0}.x-window-header-nx-message-primary-collapsed-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-primary-collapsed-top-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-collapsed-top-sides.gif)"}.x-window-header-nx-message-primary-collapsed-right{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#0047b2}.x-window-header-nx-message-primary-collapsed-right-mc{background-color:#0047b2}.x-nbr .x-window-header-nx-message-primary-collapsed-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-primary-collapsed-right-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-primary-collapsed-right-tl{background-position:0 -8px}.x-window-header-nx-message-primary-collapsed-right-tr{background-position:right -12px}.x-window-header-nx-message-primary-collapsed-right-bl{background-position:0 -16px}.x-window-header-nx-message-primary-collapsed-right-br{background-position:right -20px}.x-window-header-nx-message-primary-collapsed-right-ml{background-position:0 top}.x-window-header-nx-message-primary-collapsed-right-mr{background-position:right top}.x-window-header-nx-message-primary-collapsed-right-tc{background-position:0 0}.x-window-header-nx-message-primary-collapsed-right-bc{background-position:0 -4px}.x-window-header-nx-message-primary-collapsed-right-tr,.x-window-header-nx-message-primary-collapsed-right-br,.x-window-header-nx-message-primary-collapsed-right-mr{padding-right:4px}.x-window-header-nx-message-primary-collapsed-right-tl,.x-window-header-nx-message-primary-collapsed-right-bl,.x-window-header-nx-message-primary-collapsed-right-ml{padding-left:4px}.x-window-header-nx-message-primary-collapsed-right-tc{height:4px}.x-window-header-nx-message-primary-collapsed-right-bc{height:4px}.x-window-header-nx-message-primary-collapsed-right-tl,.x-window-header-nx-message-primary-collapsed-right-bl,.x-window-header-nx-message-primary-collapsed-right-tr,.x-window-header-nx-message-primary-collapsed-right-br,.x-window-header-nx-message-primary-collapsed-right-tc,.x-window-header-nx-message-primary-collapsed-right-bc,.x-window-header-nx-message-primary-collapsed-right-ml,.x-window-header-nx-message-primary-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-collapsed-right-corners.gif)}.x-window-header-nx-message-primary-collapsed-right-ml,.x-window-header-nx-message-primary-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-collapsed-right-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-primary-collapsed-right-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-right-tl,.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-right-bl{position:relative;right:0}.x-window-header-nx-message-primary-collapsed-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-primary-collapsed-right-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-collapsed-right-sides.gif)"}.x-window-header-nx-message-primary-collapsed-bottom{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#0047b2}.x-window-header-nx-message-primary-collapsed-bottom-mc{background-color:#0047b2}.x-nbr .x-window-header-nx-message-primary-collapsed-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-primary-collapsed-bottom-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-primary-collapsed-bottom-tl{background-position:0 -8px}.x-window-header-nx-message-primary-collapsed-bottom-tr{background-position:right -12px}.x-window-header-nx-message-primary-collapsed-bottom-bl{background-position:0 -16px}.x-window-header-nx-message-primary-collapsed-bottom-br{background-position:right -20px}.x-window-header-nx-message-primary-collapsed-bottom-ml{background-position:0 top}.x-window-header-nx-message-primary-collapsed-bottom-mr{background-position:right top}.x-window-header-nx-message-primary-collapsed-bottom-tc{background-position:0 0}.x-window-header-nx-message-primary-collapsed-bottom-bc{background-position:0 -4px}.x-window-header-nx-message-primary-collapsed-bottom-tr,.x-window-header-nx-message-primary-collapsed-bottom-br,.x-window-header-nx-message-primary-collapsed-bottom-mr{padding-right:4px}.x-window-header-nx-message-primary-collapsed-bottom-tl,.x-window-header-nx-message-primary-collapsed-bottom-bl,.x-window-header-nx-message-primary-collapsed-bottom-ml{padding-left:4px}.x-window-header-nx-message-primary-collapsed-bottom-tc{height:4px}.x-window-header-nx-message-primary-collapsed-bottom-bc{height:4px}.x-window-header-nx-message-primary-collapsed-bottom-tl,.x-window-header-nx-message-primary-collapsed-bottom-bl,.x-window-header-nx-message-primary-collapsed-bottom-tr,.x-window-header-nx-message-primary-collapsed-bottom-br,.x-window-header-nx-message-primary-collapsed-bottom-tc,.x-window-header-nx-message-primary-collapsed-bottom-bc,.x-window-header-nx-message-primary-collapsed-bottom-ml,.x-window-header-nx-message-primary-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-collapsed-bottom-corners.gif)}.x-window-header-nx-message-primary-collapsed-bottom-ml,.x-window-header-nx-message-primary-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-collapsed-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-primary-collapsed-bottom-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-bottom-tl,.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-bottom-bl{position:relative;right:0}.x-window-header-nx-message-primary-collapsed-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-primary-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-collapsed-bottom-sides.gif)"}.x-window-header-nx-message-primary-collapsed-left{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#0047b2}.x-window-header-nx-message-primary-collapsed-left-mc{background-color:#0047b2}.x-nbr .x-window-header-nx-message-primary-collapsed-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-primary-collapsed-left-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-primary-collapsed-left-tl{background-position:0 -8px}.x-window-header-nx-message-primary-collapsed-left-tr{background-position:right -12px}.x-window-header-nx-message-primary-collapsed-left-bl{background-position:0 -16px}.x-window-header-nx-message-primary-collapsed-left-br{background-position:right -20px}.x-window-header-nx-message-primary-collapsed-left-ml{background-position:0 top}.x-window-header-nx-message-primary-collapsed-left-mr{background-position:right top}.x-window-header-nx-message-primary-collapsed-left-tc{background-position:0 0}.x-window-header-nx-message-primary-collapsed-left-bc{background-position:0 -4px}.x-window-header-nx-message-primary-collapsed-left-tr,.x-window-header-nx-message-primary-collapsed-left-br,.x-window-header-nx-message-primary-collapsed-left-mr{padding-right:4px}.x-window-header-nx-message-primary-collapsed-left-tl,.x-window-header-nx-message-primary-collapsed-left-bl,.x-window-header-nx-message-primary-collapsed-left-ml{padding-left:4px}.x-window-header-nx-message-primary-collapsed-left-tc{height:4px}.x-window-header-nx-message-primary-collapsed-left-bc{height:4px}.x-window-header-nx-message-primary-collapsed-left-tl,.x-window-header-nx-message-primary-collapsed-left-bl,.x-window-header-nx-message-primary-collapsed-left-tr,.x-window-header-nx-message-primary-collapsed-left-br,.x-window-header-nx-message-primary-collapsed-left-tc,.x-window-header-nx-message-primary-collapsed-left-bc,.x-window-header-nx-message-primary-collapsed-left-ml,.x-window-header-nx-message-primary-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-collapsed-left-corners.gif)}.x-window-header-nx-message-primary-collapsed-left-ml,.x-window-header-nx-message-primary-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-primary-collapsed-left-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-primary-collapsed-left-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-left-tl,.x-strict .x-ie7 .x-window-header-nx-message-primary-collapsed-left-bl{position:relative;right:0}.x-window-header-nx-message-primary-collapsed-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-primary-collapsed-left-corners.gif), sides:url(images/window-header/window-header-nx-message-primary-collapsed-left-sides.gif)"}.x-window-header-nx-message-primary .x-window-header-icon{width:16px;height:16px;color:#333;font-size:16px;line-height:16px;background-position:center center}.x-window-header-nx-message-primary .x-window-header-glyph{color:#333;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-window-header-nx-message-primary .x-window-header-glyph{color:#193d72}.x-window-header-nx-message-primary-horizontal .x-window-header-icon-before-title{margin:0 6px 0 0}.x-window-header-nx-message-primary-horizontal .x-window-header-icon-after-title{margin:0 0 0 6px}.x-window-header-nx-message-primary-vertical .x-window-header-icon-before-title{margin:0 0 6px 0}.x-window-header-nx-message-primary-vertical .x-window-header-icon-after-title{margin:6px 0 0 0}.x-window-header-nx-message-primary-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-window-header-nx-message-primary-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-window-header-nx-message-primary-vertical .x-tool-after-title{margin:6px 0 0 0}.x-window-header-nx-message-primary-vertical .x-tool-before-title{margin:0 0 6px 0}.x-window-header-nx-message-primary{border-width:0!important}.x-nbr .x-window-nx-message-primary-collapsed .x-window-header{border-width:0!important}.x-window-nx-message-primary-outer-border-l{border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-primary-outer-border-b{border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-primary-outer-border-bl{border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-primary-outer-border-r{border-right-color:#606060!important;border-right-width:2px!important}.x-window-nx-message-primary-outer-border-rl{border-right-color:#606060!important;border-right-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-primary-outer-border-rb{border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-primary-outer-border-rbl{border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-primary-outer-border-t{border-top-color:#606060!important;border-top-width:2px!important}.x-window-nx-message-primary-outer-border-tl{border-top-color:#606060!important;border-top-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-primary-outer-border-tb{border-top-color:#606060!important;border-top-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-primary-outer-border-tbl{border-top-color:#606060!important;border-top-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-primary-outer-border-tr{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important}.x-window-nx-message-primary-outer-border-trl{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-primary-outer-border-trb{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-primary-outer-border-trbl{border-color:#606060!important;border-width:2px!important}.x-window-nx-message-danger{border-color:#606060;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px}.x-window-nx-message-danger{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:2px;border-style:solid;background-color:white}.x-window-nx-message-danger-mc{background-color:white}.x-nbr .x-window-nx-message-danger{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-nx-message-danger-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-10-10-10-10}.x-window-nx-message-danger-tl{background-position:0 -8px}.x-window-nx-message-danger-tr{background-position:right -12px}.x-window-nx-message-danger-bl{background-position:0 -16px}.x-window-nx-message-danger-br{background-position:right -20px}.x-window-nx-message-danger-ml{background-position:0 top}.x-window-nx-message-danger-mr{background-position:right top}.x-window-nx-message-danger-tc{background-position:0 0}.x-window-nx-message-danger-bc{background-position:0 -4px}.x-window-nx-message-danger-tr,.x-window-nx-message-danger-br,.x-window-nx-message-danger-mr{padding-right:4px}.x-window-nx-message-danger-tl,.x-window-nx-message-danger-bl,.x-window-nx-message-danger-ml{padding-left:4px}.x-window-nx-message-danger-tc{height:4px}.x-window-nx-message-danger-bc{height:4px}.x-window-nx-message-danger-tl,.x-window-nx-message-danger-bl,.x-window-nx-message-danger-tr,.x-window-nx-message-danger-br,.x-window-nx-message-danger-tc,.x-window-nx-message-danger-bc,.x-window-nx-message-danger-ml,.x-window-nx-message-danger-mr{zoom:1;background-image:url(images/window/window-nx-message-danger-corners.gif)}.x-window-nx-message-danger-ml,.x-window-nx-message-danger-mr{zoom:1;background-image:url(images/window/window-nx-message-danger-sides.gif);background-repeat:repeat-y}.x-window-nx-message-danger-mc{padding:8px 8px 8px 8px}.x-strict .x-ie7 .x-window-nx-message-danger-tl,.x-strict .x-ie7 .x-window-nx-message-danger-bl{position:relative;right:0}.x-window-nx-message-danger:after{display:none;content:"x-slicer:corners:url(images/window/window-nx-message-danger-corners.gif), sides:url(images/window/window-nx-message-danger-sides.gif)"}.x-window-body-nx-message-danger{border-color:#606060;border-width:1px;border-style:solid;background:white;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-window-header-nx-message-danger{font-size:13px;border-color:#606060;zoom:1;background-color:#db2852}.x-window-header-nx-message-danger .x-tool-img{background-color:#db2852}.x-window-header-nx-message-danger-vertical .x-window-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-window-header-nx-message-danger-vertical .x-window-header-text-container{background-color:#db2852;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#db2852)}.x-window-header-text-container-nx-message-danger{color:white;font-weight:bold;line-height:15px;font-family:arial,helvetica,verdana,sans-serif;font-size:13px;padding:1px 0 0;text-transform:none}.x-window-header-nx-message-danger-top{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:10px 10px 8px 10px;border-width:0;border-style:solid;background-color:#db2852}.x-window-header-nx-message-danger-top-mc{background-color:#db2852}.x-nbr .x-window-header-nx-message-danger-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-danger-top-frameInfo{font-family:dh-4-4-0-0-0-0-0-0-10-10-8-10}.x-window-header-nx-message-danger-top-tl{background-position:0 -8px}.x-window-header-nx-message-danger-top-tr{background-position:right -12px}.x-window-header-nx-message-danger-top-bl{background-position:0 -16px}.x-window-header-nx-message-danger-top-br{background-position:right -20px}.x-window-header-nx-message-danger-top-ml{background-position:0 top}.x-window-header-nx-message-danger-top-mr{background-position:right top}.x-window-header-nx-message-danger-top-tc{background-position:0 0}.x-window-header-nx-message-danger-top-bc{background-position:0 -4px}.x-window-header-nx-message-danger-top-tr,.x-window-header-nx-message-danger-top-br,.x-window-header-nx-message-danger-top-mr{padding-right:4px}.x-window-header-nx-message-danger-top-tl,.x-window-header-nx-message-danger-top-bl,.x-window-header-nx-message-danger-top-ml{padding-left:4px}.x-window-header-nx-message-danger-top-tc{height:4px}.x-window-header-nx-message-danger-top-bc{height:0}.x-window-header-nx-message-danger-top-tl,.x-window-header-nx-message-danger-top-bl,.x-window-header-nx-message-danger-top-tr,.x-window-header-nx-message-danger-top-br,.x-window-header-nx-message-danger-top-tc,.x-window-header-nx-message-danger-top-bc,.x-window-header-nx-message-danger-top-ml,.x-window-header-nx-message-danger-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-top-corners.gif)}.x-window-header-nx-message-danger-top-ml,.x-window-header-nx-message-danger-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-top-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-danger-top-mc{padding:6px 6px 8px 6px}.x-strict .x-ie7 .x-window-header-nx-message-danger-top-tl,.x-strict .x-ie7 .x-window-header-nx-message-danger-top-bl{position:relative;right:0}.x-window-header-nx-message-danger-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-danger-top-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-top-sides.gif)"}.x-window-header-nx-message-danger-right{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:10px 10px 10px 8px;border-width:0;border-style:solid;background-color:#db2852}.x-window-header-nx-message-danger-right-mc{background-color:#db2852}.x-nbr .x-window-header-nx-message-danger-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-danger-right-frameInfo{font-family:dh-0-4-4-0-0-0-0-0-10-10-10-8}.x-window-header-nx-message-danger-right-tl{background-position:0 -8px}.x-window-header-nx-message-danger-right-tr{background-position:right -12px}.x-window-header-nx-message-danger-right-bl{background-position:0 -16px}.x-window-header-nx-message-danger-right-br{background-position:right -20px}.x-window-header-nx-message-danger-right-ml{background-position:0 top}.x-window-header-nx-message-danger-right-mr{background-position:right top}.x-window-header-nx-message-danger-right-tc{background-position:0 0}.x-window-header-nx-message-danger-right-bc{background-position:0 -4px}.x-window-header-nx-message-danger-right-tr,.x-window-header-nx-message-danger-right-br,.x-window-header-nx-message-danger-right-mr{padding-right:4px}.x-window-header-nx-message-danger-right-tl,.x-window-header-nx-message-danger-right-bl,.x-window-header-nx-message-danger-right-ml{padding-left:0}.x-window-header-nx-message-danger-right-tc{height:4px}.x-window-header-nx-message-danger-right-bc{height:4px}.x-window-header-nx-message-danger-right-tl,.x-window-header-nx-message-danger-right-bl,.x-window-header-nx-message-danger-right-tr,.x-window-header-nx-message-danger-right-br,.x-window-header-nx-message-danger-right-tc,.x-window-header-nx-message-danger-right-bc,.x-window-header-nx-message-danger-right-ml,.x-window-header-nx-message-danger-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-right-corners.gif)}.x-window-header-nx-message-danger-right-ml,.x-window-header-nx-message-danger-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-right-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-danger-right-mc{padding:6px 6px 6px 8px}.x-strict .x-ie7 .x-window-header-nx-message-danger-right-tl,.x-strict .x-ie7 .x-window-header-nx-message-danger-right-bl{position:relative;right:0}.x-window-header-nx-message-danger-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-danger-right-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-right-sides.gif)"}.x-window-header-nx-message-danger-bottom{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:8px 10px 10px 10px;border-width:0;border-style:solid;background-color:#db2852}.x-window-header-nx-message-danger-bottom-mc{background-color:#db2852}.x-nbr .x-window-header-nx-message-danger-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-danger-bottom-frameInfo{font-family:dh-0-0-4-4-0-0-0-0-8-10-10-10}.x-window-header-nx-message-danger-bottom-tl{background-position:0 -8px}.x-window-header-nx-message-danger-bottom-tr{background-position:right -12px}.x-window-header-nx-message-danger-bottom-bl{background-position:0 -16px}.x-window-header-nx-message-danger-bottom-br{background-position:right -20px}.x-window-header-nx-message-danger-bottom-ml{background-position:0 top}.x-window-header-nx-message-danger-bottom-mr{background-position:right top}.x-window-header-nx-message-danger-bottom-tc{background-position:0 0}.x-window-header-nx-message-danger-bottom-bc{background-position:0 -4px}.x-window-header-nx-message-danger-bottom-tr,.x-window-header-nx-message-danger-bottom-br,.x-window-header-nx-message-danger-bottom-mr{padding-right:4px}.x-window-header-nx-message-danger-bottom-tl,.x-window-header-nx-message-danger-bottom-bl,.x-window-header-nx-message-danger-bottom-ml{padding-left:4px}.x-window-header-nx-message-danger-bottom-tc{height:0}.x-window-header-nx-message-danger-bottom-bc{height:4px}.x-window-header-nx-message-danger-bottom-tl,.x-window-header-nx-message-danger-bottom-bl,.x-window-header-nx-message-danger-bottom-tr,.x-window-header-nx-message-danger-bottom-br,.x-window-header-nx-message-danger-bottom-tc,.x-window-header-nx-message-danger-bottom-bc,.x-window-header-nx-message-danger-bottom-ml,.x-window-header-nx-message-danger-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-bottom-corners.gif)}.x-window-header-nx-message-danger-bottom-ml,.x-window-header-nx-message-danger-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-danger-bottom-mc{padding:8px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-danger-bottom-tl,.x-strict .x-ie7 .x-window-header-nx-message-danger-bottom-bl{position:relative;right:0}.x-window-header-nx-message-danger-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-danger-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-bottom-sides.gif)"}.x-window-header-nx-message-danger-left{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:10px 8px 10px 10px;border-width:0;border-style:solid;background-color:#db2852}.x-window-header-nx-message-danger-left-mc{background-color:#db2852}.x-nbr .x-window-header-nx-message-danger-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-danger-left-frameInfo{font-family:dh-4-0-0-4-0-0-0-0-10-8-10-10}.x-window-header-nx-message-danger-left-tl{background-position:0 -8px}.x-window-header-nx-message-danger-left-tr{background-position:right -12px}.x-window-header-nx-message-danger-left-bl{background-position:0 -16px}.x-window-header-nx-message-danger-left-br{background-position:right -20px}.x-window-header-nx-message-danger-left-ml{background-position:0 top}.x-window-header-nx-message-danger-left-mr{background-position:right top}.x-window-header-nx-message-danger-left-tc{background-position:0 0}.x-window-header-nx-message-danger-left-bc{background-position:0 -4px}.x-window-header-nx-message-danger-left-tr,.x-window-header-nx-message-danger-left-br,.x-window-header-nx-message-danger-left-mr{padding-right:0}.x-window-header-nx-message-danger-left-tl,.x-window-header-nx-message-danger-left-bl,.x-window-header-nx-message-danger-left-ml{padding-left:4px}.x-window-header-nx-message-danger-left-tc{height:4px}.x-window-header-nx-message-danger-left-bc{height:4px}.x-window-header-nx-message-danger-left-tl,.x-window-header-nx-message-danger-left-bl,.x-window-header-nx-message-danger-left-tr,.x-window-header-nx-message-danger-left-br,.x-window-header-nx-message-danger-left-tc,.x-window-header-nx-message-danger-left-bc,.x-window-header-nx-message-danger-left-ml,.x-window-header-nx-message-danger-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-left-corners.gif)}.x-window-header-nx-message-danger-left-ml,.x-window-header-nx-message-danger-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-left-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-danger-left-mc{padding:6px 8px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-danger-left-tl,.x-strict .x-ie7 .x-window-header-nx-message-danger-left-bl{position:relative;right:0}.x-window-header-nx-message-danger-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-danger-left-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-left-sides.gif)"}.x-window-header-nx-message-danger-collapsed-top{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#db2852}.x-window-header-nx-message-danger-collapsed-top-mc{background-color:#db2852}.x-nbr .x-window-header-nx-message-danger-collapsed-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-danger-collapsed-top-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-danger-collapsed-top-tl{background-position:0 -8px}.x-window-header-nx-message-danger-collapsed-top-tr{background-position:right -12px}.x-window-header-nx-message-danger-collapsed-top-bl{background-position:0 -16px}.x-window-header-nx-message-danger-collapsed-top-br{background-position:right -20px}.x-window-header-nx-message-danger-collapsed-top-ml{background-position:0 top}.x-window-header-nx-message-danger-collapsed-top-mr{background-position:right top}.x-window-header-nx-message-danger-collapsed-top-tc{background-position:0 0}.x-window-header-nx-message-danger-collapsed-top-bc{background-position:0 -4px}.x-window-header-nx-message-danger-collapsed-top-tr,.x-window-header-nx-message-danger-collapsed-top-br,.x-window-header-nx-message-danger-collapsed-top-mr{padding-right:4px}.x-window-header-nx-message-danger-collapsed-top-tl,.x-window-header-nx-message-danger-collapsed-top-bl,.x-window-header-nx-message-danger-collapsed-top-ml{padding-left:4px}.x-window-header-nx-message-danger-collapsed-top-tc{height:4px}.x-window-header-nx-message-danger-collapsed-top-bc{height:4px}.x-window-header-nx-message-danger-collapsed-top-tl,.x-window-header-nx-message-danger-collapsed-top-bl,.x-window-header-nx-message-danger-collapsed-top-tr,.x-window-header-nx-message-danger-collapsed-top-br,.x-window-header-nx-message-danger-collapsed-top-tc,.x-window-header-nx-message-danger-collapsed-top-bc,.x-window-header-nx-message-danger-collapsed-top-ml,.x-window-header-nx-message-danger-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-collapsed-top-corners.gif)}.x-window-header-nx-message-danger-collapsed-top-ml,.x-window-header-nx-message-danger-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-collapsed-top-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-danger-collapsed-top-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-top-tl,.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-top-bl{position:relative;right:0}.x-window-header-nx-message-danger-collapsed-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-danger-collapsed-top-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-collapsed-top-sides.gif)"}.x-window-header-nx-message-danger-collapsed-right{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#db2852}.x-window-header-nx-message-danger-collapsed-right-mc{background-color:#db2852}.x-nbr .x-window-header-nx-message-danger-collapsed-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-danger-collapsed-right-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-danger-collapsed-right-tl{background-position:0 -8px}.x-window-header-nx-message-danger-collapsed-right-tr{background-position:right -12px}.x-window-header-nx-message-danger-collapsed-right-bl{background-position:0 -16px}.x-window-header-nx-message-danger-collapsed-right-br{background-position:right -20px}.x-window-header-nx-message-danger-collapsed-right-ml{background-position:0 top}.x-window-header-nx-message-danger-collapsed-right-mr{background-position:right top}.x-window-header-nx-message-danger-collapsed-right-tc{background-position:0 0}.x-window-header-nx-message-danger-collapsed-right-bc{background-position:0 -4px}.x-window-header-nx-message-danger-collapsed-right-tr,.x-window-header-nx-message-danger-collapsed-right-br,.x-window-header-nx-message-danger-collapsed-right-mr{padding-right:4px}.x-window-header-nx-message-danger-collapsed-right-tl,.x-window-header-nx-message-danger-collapsed-right-bl,.x-window-header-nx-message-danger-collapsed-right-ml{padding-left:4px}.x-window-header-nx-message-danger-collapsed-right-tc{height:4px}.x-window-header-nx-message-danger-collapsed-right-bc{height:4px}.x-window-header-nx-message-danger-collapsed-right-tl,.x-window-header-nx-message-danger-collapsed-right-bl,.x-window-header-nx-message-danger-collapsed-right-tr,.x-window-header-nx-message-danger-collapsed-right-br,.x-window-header-nx-message-danger-collapsed-right-tc,.x-window-header-nx-message-danger-collapsed-right-bc,.x-window-header-nx-message-danger-collapsed-right-ml,.x-window-header-nx-message-danger-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-collapsed-right-corners.gif)}.x-window-header-nx-message-danger-collapsed-right-ml,.x-window-header-nx-message-danger-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-collapsed-right-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-danger-collapsed-right-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-right-tl,.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-right-bl{position:relative;right:0}.x-window-header-nx-message-danger-collapsed-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-danger-collapsed-right-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-collapsed-right-sides.gif)"}.x-window-header-nx-message-danger-collapsed-bottom{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#db2852}.x-window-header-nx-message-danger-collapsed-bottom-mc{background-color:#db2852}.x-nbr .x-window-header-nx-message-danger-collapsed-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-danger-collapsed-bottom-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-danger-collapsed-bottom-tl{background-position:0 -8px}.x-window-header-nx-message-danger-collapsed-bottom-tr{background-position:right -12px}.x-window-header-nx-message-danger-collapsed-bottom-bl{background-position:0 -16px}.x-window-header-nx-message-danger-collapsed-bottom-br{background-position:right -20px}.x-window-header-nx-message-danger-collapsed-bottom-ml{background-position:0 top}.x-window-header-nx-message-danger-collapsed-bottom-mr{background-position:right top}.x-window-header-nx-message-danger-collapsed-bottom-tc{background-position:0 0}.x-window-header-nx-message-danger-collapsed-bottom-bc{background-position:0 -4px}.x-window-header-nx-message-danger-collapsed-bottom-tr,.x-window-header-nx-message-danger-collapsed-bottom-br,.x-window-header-nx-message-danger-collapsed-bottom-mr{padding-right:4px}.x-window-header-nx-message-danger-collapsed-bottom-tl,.x-window-header-nx-message-danger-collapsed-bottom-bl,.x-window-header-nx-message-danger-collapsed-bottom-ml{padding-left:4px}.x-window-header-nx-message-danger-collapsed-bottom-tc{height:4px}.x-window-header-nx-message-danger-collapsed-bottom-bc{height:4px}.x-window-header-nx-message-danger-collapsed-bottom-tl,.x-window-header-nx-message-danger-collapsed-bottom-bl,.x-window-header-nx-message-danger-collapsed-bottom-tr,.x-window-header-nx-message-danger-collapsed-bottom-br,.x-window-header-nx-message-danger-collapsed-bottom-tc,.x-window-header-nx-message-danger-collapsed-bottom-bc,.x-window-header-nx-message-danger-collapsed-bottom-ml,.x-window-header-nx-message-danger-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-collapsed-bottom-corners.gif)}.x-window-header-nx-message-danger-collapsed-bottom-ml,.x-window-header-nx-message-danger-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-collapsed-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-danger-collapsed-bottom-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-bottom-tl,.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-bottom-bl{position:relative;right:0}.x-window-header-nx-message-danger-collapsed-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-danger-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-collapsed-bottom-sides.gif)"}.x-window-header-nx-message-danger-collapsed-left{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#db2852}.x-window-header-nx-message-danger-collapsed-left-mc{background-color:#db2852}.x-nbr .x-window-header-nx-message-danger-collapsed-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-danger-collapsed-left-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-danger-collapsed-left-tl{background-position:0 -8px}.x-window-header-nx-message-danger-collapsed-left-tr{background-position:right -12px}.x-window-header-nx-message-danger-collapsed-left-bl{background-position:0 -16px}.x-window-header-nx-message-danger-collapsed-left-br{background-position:right -20px}.x-window-header-nx-message-danger-collapsed-left-ml{background-position:0 top}.x-window-header-nx-message-danger-collapsed-left-mr{background-position:right top}.x-window-header-nx-message-danger-collapsed-left-tc{background-position:0 0}.x-window-header-nx-message-danger-collapsed-left-bc{background-position:0 -4px}.x-window-header-nx-message-danger-collapsed-left-tr,.x-window-header-nx-message-danger-collapsed-left-br,.x-window-header-nx-message-danger-collapsed-left-mr{padding-right:4px}.x-window-header-nx-message-danger-collapsed-left-tl,.x-window-header-nx-message-danger-collapsed-left-bl,.x-window-header-nx-message-danger-collapsed-left-ml{padding-left:4px}.x-window-header-nx-message-danger-collapsed-left-tc{height:4px}.x-window-header-nx-message-danger-collapsed-left-bc{height:4px}.x-window-header-nx-message-danger-collapsed-left-tl,.x-window-header-nx-message-danger-collapsed-left-bl,.x-window-header-nx-message-danger-collapsed-left-tr,.x-window-header-nx-message-danger-collapsed-left-br,.x-window-header-nx-message-danger-collapsed-left-tc,.x-window-header-nx-message-danger-collapsed-left-bc,.x-window-header-nx-message-danger-collapsed-left-ml,.x-window-header-nx-message-danger-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-collapsed-left-corners.gif)}.x-window-header-nx-message-danger-collapsed-left-ml,.x-window-header-nx-message-danger-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-danger-collapsed-left-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-danger-collapsed-left-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-left-tl,.x-strict .x-ie7 .x-window-header-nx-message-danger-collapsed-left-bl{position:relative;right:0}.x-window-header-nx-message-danger-collapsed-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-danger-collapsed-left-corners.gif), sides:url(images/window-header/window-header-nx-message-danger-collapsed-left-sides.gif)"}.x-window-header-nx-message-danger .x-window-header-icon{width:16px;height:16px;color:#333;font-size:16px;line-height:16px;background-position:center center}.x-window-header-nx-message-danger .x-window-header-glyph{color:#333;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-window-header-nx-message-danger .x-window-header-glyph{color:#872d42}.x-window-header-nx-message-danger-horizontal .x-window-header-icon-before-title{margin:0 6px 0 0}.x-window-header-nx-message-danger-horizontal .x-window-header-icon-after-title{margin:0 0 0 6px}.x-window-header-nx-message-danger-vertical .x-window-header-icon-before-title{margin:0 0 6px 0}.x-window-header-nx-message-danger-vertical .x-window-header-icon-after-title{margin:6px 0 0 0}.x-window-header-nx-message-danger-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-window-header-nx-message-danger-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-window-header-nx-message-danger-vertical .x-tool-after-title{margin:6px 0 0 0}.x-window-header-nx-message-danger-vertical .x-tool-before-title{margin:0 0 6px 0}.x-window-header-nx-message-danger{border-width:0!important}.x-nbr .x-window-nx-message-danger-collapsed .x-window-header{border-width:0!important}.x-window-nx-message-danger-outer-border-l{border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-danger-outer-border-b{border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-danger-outer-border-bl{border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-danger-outer-border-r{border-right-color:#606060!important;border-right-width:2px!important}.x-window-nx-message-danger-outer-border-rl{border-right-color:#606060!important;border-right-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-danger-outer-border-rb{border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-danger-outer-border-rbl{border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-danger-outer-border-t{border-top-color:#606060!important;border-top-width:2px!important}.x-window-nx-message-danger-outer-border-tl{border-top-color:#606060!important;border-top-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-danger-outer-border-tb{border-top-color:#606060!important;border-top-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-danger-outer-border-tbl{border-top-color:#606060!important;border-top-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-danger-outer-border-tr{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important}.x-window-nx-message-danger-outer-border-trl{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-danger-outer-border-trb{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-danger-outer-border-trbl{border-color:#606060!important;border-width:2px!important}.x-window-nx-message-warning{border-color:#606060;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px}.x-window-nx-message-warning{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:2px;border-style:solid;background-color:white}.x-window-nx-message-warning-mc{background-color:white}.x-nbr .x-window-nx-message-warning{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-nx-message-warning-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-10-10-10-10}.x-window-nx-message-warning-tl{background-position:0 -8px}.x-window-nx-message-warning-tr{background-position:right -12px}.x-window-nx-message-warning-bl{background-position:0 -16px}.x-window-nx-message-warning-br{background-position:right -20px}.x-window-nx-message-warning-ml{background-position:0 top}.x-window-nx-message-warning-mr{background-position:right top}.x-window-nx-message-warning-tc{background-position:0 0}.x-window-nx-message-warning-bc{background-position:0 -4px}.x-window-nx-message-warning-tr,.x-window-nx-message-warning-br,.x-window-nx-message-warning-mr{padding-right:4px}.x-window-nx-message-warning-tl,.x-window-nx-message-warning-bl,.x-window-nx-message-warning-ml{padding-left:4px}.x-window-nx-message-warning-tc{height:4px}.x-window-nx-message-warning-bc{height:4px}.x-window-nx-message-warning-tl,.x-window-nx-message-warning-bl,.x-window-nx-message-warning-tr,.x-window-nx-message-warning-br,.x-window-nx-message-warning-tc,.x-window-nx-message-warning-bc,.x-window-nx-message-warning-ml,.x-window-nx-message-warning-mr{zoom:1;background-image:url(images/window/window-nx-message-warning-corners.gif)}.x-window-nx-message-warning-ml,.x-window-nx-message-warning-mr{zoom:1;background-image:url(images/window/window-nx-message-warning-sides.gif);background-repeat:repeat-y}.x-window-nx-message-warning-mc{padding:8px 8px 8px 8px}.x-strict .x-ie7 .x-window-nx-message-warning-tl,.x-strict .x-ie7 .x-window-nx-message-warning-bl{position:relative;right:0}.x-window-nx-message-warning:after{display:none;content:"x-slicer:corners:url(images/window/window-nx-message-warning-corners.gif), sides:url(images/window/window-nx-message-warning-sides.gif)"}.x-window-body-nx-message-warning{border-color:#606060;border-width:1px;border-style:solid;background:white;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-window-header-nx-message-warning{font-size:13px;border-color:#606060;zoom:1;background-color:#f2862f}.x-window-header-nx-message-warning .x-tool-img{background-color:#f2862f}.x-window-header-nx-message-warning-vertical .x-window-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-window-header-nx-message-warning-vertical .x-window-header-text-container{background-color:#f2862f;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#f2862f)}.x-window-header-text-container-nx-message-warning{color:white;font-weight:bold;line-height:15px;font-family:arial,helvetica,verdana,sans-serif;font-size:13px;padding:1px 0 0;text-transform:none}.x-window-header-nx-message-warning-top{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:10px 10px 8px 10px;border-width:0;border-style:solid;background-color:#f2862f}.x-window-header-nx-message-warning-top-mc{background-color:#f2862f}.x-nbr .x-window-header-nx-message-warning-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-warning-top-frameInfo{font-family:dh-4-4-0-0-0-0-0-0-10-10-8-10}.x-window-header-nx-message-warning-top-tl{background-position:0 -8px}.x-window-header-nx-message-warning-top-tr{background-position:right -12px}.x-window-header-nx-message-warning-top-bl{background-position:0 -16px}.x-window-header-nx-message-warning-top-br{background-position:right -20px}.x-window-header-nx-message-warning-top-ml{background-position:0 top}.x-window-header-nx-message-warning-top-mr{background-position:right top}.x-window-header-nx-message-warning-top-tc{background-position:0 0}.x-window-header-nx-message-warning-top-bc{background-position:0 -4px}.x-window-header-nx-message-warning-top-tr,.x-window-header-nx-message-warning-top-br,.x-window-header-nx-message-warning-top-mr{padding-right:4px}.x-window-header-nx-message-warning-top-tl,.x-window-header-nx-message-warning-top-bl,.x-window-header-nx-message-warning-top-ml{padding-left:4px}.x-window-header-nx-message-warning-top-tc{height:4px}.x-window-header-nx-message-warning-top-bc{height:0}.x-window-header-nx-message-warning-top-tl,.x-window-header-nx-message-warning-top-bl,.x-window-header-nx-message-warning-top-tr,.x-window-header-nx-message-warning-top-br,.x-window-header-nx-message-warning-top-tc,.x-window-header-nx-message-warning-top-bc,.x-window-header-nx-message-warning-top-ml,.x-window-header-nx-message-warning-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-top-corners.gif)}.x-window-header-nx-message-warning-top-ml,.x-window-header-nx-message-warning-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-top-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-warning-top-mc{padding:6px 6px 8px 6px}.x-strict .x-ie7 .x-window-header-nx-message-warning-top-tl,.x-strict .x-ie7 .x-window-header-nx-message-warning-top-bl{position:relative;right:0}.x-window-header-nx-message-warning-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-warning-top-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-top-sides.gif)"}.x-window-header-nx-message-warning-right{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:10px 10px 10px 8px;border-width:0;border-style:solid;background-color:#f2862f}.x-window-header-nx-message-warning-right-mc{background-color:#f2862f}.x-nbr .x-window-header-nx-message-warning-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-warning-right-frameInfo{font-family:dh-0-4-4-0-0-0-0-0-10-10-10-8}.x-window-header-nx-message-warning-right-tl{background-position:0 -8px}.x-window-header-nx-message-warning-right-tr{background-position:right -12px}.x-window-header-nx-message-warning-right-bl{background-position:0 -16px}.x-window-header-nx-message-warning-right-br{background-position:right -20px}.x-window-header-nx-message-warning-right-ml{background-position:0 top}.x-window-header-nx-message-warning-right-mr{background-position:right top}.x-window-header-nx-message-warning-right-tc{background-position:0 0}.x-window-header-nx-message-warning-right-bc{background-position:0 -4px}.x-window-header-nx-message-warning-right-tr,.x-window-header-nx-message-warning-right-br,.x-window-header-nx-message-warning-right-mr{padding-right:4px}.x-window-header-nx-message-warning-right-tl,.x-window-header-nx-message-warning-right-bl,.x-window-header-nx-message-warning-right-ml{padding-left:0}.x-window-header-nx-message-warning-right-tc{height:4px}.x-window-header-nx-message-warning-right-bc{height:4px}.x-window-header-nx-message-warning-right-tl,.x-window-header-nx-message-warning-right-bl,.x-window-header-nx-message-warning-right-tr,.x-window-header-nx-message-warning-right-br,.x-window-header-nx-message-warning-right-tc,.x-window-header-nx-message-warning-right-bc,.x-window-header-nx-message-warning-right-ml,.x-window-header-nx-message-warning-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-right-corners.gif)}.x-window-header-nx-message-warning-right-ml,.x-window-header-nx-message-warning-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-right-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-warning-right-mc{padding:6px 6px 6px 8px}.x-strict .x-ie7 .x-window-header-nx-message-warning-right-tl,.x-strict .x-ie7 .x-window-header-nx-message-warning-right-bl{position:relative;right:0}.x-window-header-nx-message-warning-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-warning-right-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-right-sides.gif)"}.x-window-header-nx-message-warning-bottom{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:8px 10px 10px 10px;border-width:0;border-style:solid;background-color:#f2862f}.x-window-header-nx-message-warning-bottom-mc{background-color:#f2862f}.x-nbr .x-window-header-nx-message-warning-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-warning-bottom-frameInfo{font-family:dh-0-0-4-4-0-0-0-0-8-10-10-10}.x-window-header-nx-message-warning-bottom-tl{background-position:0 -8px}.x-window-header-nx-message-warning-bottom-tr{background-position:right -12px}.x-window-header-nx-message-warning-bottom-bl{background-position:0 -16px}.x-window-header-nx-message-warning-bottom-br{background-position:right -20px}.x-window-header-nx-message-warning-bottom-ml{background-position:0 top}.x-window-header-nx-message-warning-bottom-mr{background-position:right top}.x-window-header-nx-message-warning-bottom-tc{background-position:0 0}.x-window-header-nx-message-warning-bottom-bc{background-position:0 -4px}.x-window-header-nx-message-warning-bottom-tr,.x-window-header-nx-message-warning-bottom-br,.x-window-header-nx-message-warning-bottom-mr{padding-right:4px}.x-window-header-nx-message-warning-bottom-tl,.x-window-header-nx-message-warning-bottom-bl,.x-window-header-nx-message-warning-bottom-ml{padding-left:4px}.x-window-header-nx-message-warning-bottom-tc{height:0}.x-window-header-nx-message-warning-bottom-bc{height:4px}.x-window-header-nx-message-warning-bottom-tl,.x-window-header-nx-message-warning-bottom-bl,.x-window-header-nx-message-warning-bottom-tr,.x-window-header-nx-message-warning-bottom-br,.x-window-header-nx-message-warning-bottom-tc,.x-window-header-nx-message-warning-bottom-bc,.x-window-header-nx-message-warning-bottom-ml,.x-window-header-nx-message-warning-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-bottom-corners.gif)}.x-window-header-nx-message-warning-bottom-ml,.x-window-header-nx-message-warning-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-warning-bottom-mc{padding:8px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-warning-bottom-tl,.x-strict .x-ie7 .x-window-header-nx-message-warning-bottom-bl{position:relative;right:0}.x-window-header-nx-message-warning-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-warning-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-bottom-sides.gif)"}.x-window-header-nx-message-warning-left{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:10px 8px 10px 10px;border-width:0;border-style:solid;background-color:#f2862f}.x-window-header-nx-message-warning-left-mc{background-color:#f2862f}.x-nbr .x-window-header-nx-message-warning-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-warning-left-frameInfo{font-family:dh-4-0-0-4-0-0-0-0-10-8-10-10}.x-window-header-nx-message-warning-left-tl{background-position:0 -8px}.x-window-header-nx-message-warning-left-tr{background-position:right -12px}.x-window-header-nx-message-warning-left-bl{background-position:0 -16px}.x-window-header-nx-message-warning-left-br{background-position:right -20px}.x-window-header-nx-message-warning-left-ml{background-position:0 top}.x-window-header-nx-message-warning-left-mr{background-position:right top}.x-window-header-nx-message-warning-left-tc{background-position:0 0}.x-window-header-nx-message-warning-left-bc{background-position:0 -4px}.x-window-header-nx-message-warning-left-tr,.x-window-header-nx-message-warning-left-br,.x-window-header-nx-message-warning-left-mr{padding-right:0}.x-window-header-nx-message-warning-left-tl,.x-window-header-nx-message-warning-left-bl,.x-window-header-nx-message-warning-left-ml{padding-left:4px}.x-window-header-nx-message-warning-left-tc{height:4px}.x-window-header-nx-message-warning-left-bc{height:4px}.x-window-header-nx-message-warning-left-tl,.x-window-header-nx-message-warning-left-bl,.x-window-header-nx-message-warning-left-tr,.x-window-header-nx-message-warning-left-br,.x-window-header-nx-message-warning-left-tc,.x-window-header-nx-message-warning-left-bc,.x-window-header-nx-message-warning-left-ml,.x-window-header-nx-message-warning-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-left-corners.gif)}.x-window-header-nx-message-warning-left-ml,.x-window-header-nx-message-warning-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-left-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-warning-left-mc{padding:6px 8px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-warning-left-tl,.x-strict .x-ie7 .x-window-header-nx-message-warning-left-bl{position:relative;right:0}.x-window-header-nx-message-warning-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-warning-left-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-left-sides.gif)"}.x-window-header-nx-message-warning-collapsed-top{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#f2862f}.x-window-header-nx-message-warning-collapsed-top-mc{background-color:#f2862f}.x-nbr .x-window-header-nx-message-warning-collapsed-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-warning-collapsed-top-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-warning-collapsed-top-tl{background-position:0 -8px}.x-window-header-nx-message-warning-collapsed-top-tr{background-position:right -12px}.x-window-header-nx-message-warning-collapsed-top-bl{background-position:0 -16px}.x-window-header-nx-message-warning-collapsed-top-br{background-position:right -20px}.x-window-header-nx-message-warning-collapsed-top-ml{background-position:0 top}.x-window-header-nx-message-warning-collapsed-top-mr{background-position:right top}.x-window-header-nx-message-warning-collapsed-top-tc{background-position:0 0}.x-window-header-nx-message-warning-collapsed-top-bc{background-position:0 -4px}.x-window-header-nx-message-warning-collapsed-top-tr,.x-window-header-nx-message-warning-collapsed-top-br,.x-window-header-nx-message-warning-collapsed-top-mr{padding-right:4px}.x-window-header-nx-message-warning-collapsed-top-tl,.x-window-header-nx-message-warning-collapsed-top-bl,.x-window-header-nx-message-warning-collapsed-top-ml{padding-left:4px}.x-window-header-nx-message-warning-collapsed-top-tc{height:4px}.x-window-header-nx-message-warning-collapsed-top-bc{height:4px}.x-window-header-nx-message-warning-collapsed-top-tl,.x-window-header-nx-message-warning-collapsed-top-bl,.x-window-header-nx-message-warning-collapsed-top-tr,.x-window-header-nx-message-warning-collapsed-top-br,.x-window-header-nx-message-warning-collapsed-top-tc,.x-window-header-nx-message-warning-collapsed-top-bc,.x-window-header-nx-message-warning-collapsed-top-ml,.x-window-header-nx-message-warning-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-collapsed-top-corners.gif)}.x-window-header-nx-message-warning-collapsed-top-ml,.x-window-header-nx-message-warning-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-collapsed-top-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-warning-collapsed-top-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-top-tl,.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-top-bl{position:relative;right:0}.x-window-header-nx-message-warning-collapsed-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-warning-collapsed-top-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-collapsed-top-sides.gif)"}.x-window-header-nx-message-warning-collapsed-right{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#f2862f}.x-window-header-nx-message-warning-collapsed-right-mc{background-color:#f2862f}.x-nbr .x-window-header-nx-message-warning-collapsed-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-warning-collapsed-right-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-warning-collapsed-right-tl{background-position:0 -8px}.x-window-header-nx-message-warning-collapsed-right-tr{background-position:right -12px}.x-window-header-nx-message-warning-collapsed-right-bl{background-position:0 -16px}.x-window-header-nx-message-warning-collapsed-right-br{background-position:right -20px}.x-window-header-nx-message-warning-collapsed-right-ml{background-position:0 top}.x-window-header-nx-message-warning-collapsed-right-mr{background-position:right top}.x-window-header-nx-message-warning-collapsed-right-tc{background-position:0 0}.x-window-header-nx-message-warning-collapsed-right-bc{background-position:0 -4px}.x-window-header-nx-message-warning-collapsed-right-tr,.x-window-header-nx-message-warning-collapsed-right-br,.x-window-header-nx-message-warning-collapsed-right-mr{padding-right:4px}.x-window-header-nx-message-warning-collapsed-right-tl,.x-window-header-nx-message-warning-collapsed-right-bl,.x-window-header-nx-message-warning-collapsed-right-ml{padding-left:4px}.x-window-header-nx-message-warning-collapsed-right-tc{height:4px}.x-window-header-nx-message-warning-collapsed-right-bc{height:4px}.x-window-header-nx-message-warning-collapsed-right-tl,.x-window-header-nx-message-warning-collapsed-right-bl,.x-window-header-nx-message-warning-collapsed-right-tr,.x-window-header-nx-message-warning-collapsed-right-br,.x-window-header-nx-message-warning-collapsed-right-tc,.x-window-header-nx-message-warning-collapsed-right-bc,.x-window-header-nx-message-warning-collapsed-right-ml,.x-window-header-nx-message-warning-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-collapsed-right-corners.gif)}.x-window-header-nx-message-warning-collapsed-right-ml,.x-window-header-nx-message-warning-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-collapsed-right-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-warning-collapsed-right-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-right-tl,.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-right-bl{position:relative;right:0}.x-window-header-nx-message-warning-collapsed-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-warning-collapsed-right-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-collapsed-right-sides.gif)"}.x-window-header-nx-message-warning-collapsed-bottom{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#f2862f}.x-window-header-nx-message-warning-collapsed-bottom-mc{background-color:#f2862f}.x-nbr .x-window-header-nx-message-warning-collapsed-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-warning-collapsed-bottom-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-warning-collapsed-bottom-tl{background-position:0 -8px}.x-window-header-nx-message-warning-collapsed-bottom-tr{background-position:right -12px}.x-window-header-nx-message-warning-collapsed-bottom-bl{background-position:0 -16px}.x-window-header-nx-message-warning-collapsed-bottom-br{background-position:right -20px}.x-window-header-nx-message-warning-collapsed-bottom-ml{background-position:0 top}.x-window-header-nx-message-warning-collapsed-bottom-mr{background-position:right top}.x-window-header-nx-message-warning-collapsed-bottom-tc{background-position:0 0}.x-window-header-nx-message-warning-collapsed-bottom-bc{background-position:0 -4px}.x-window-header-nx-message-warning-collapsed-bottom-tr,.x-window-header-nx-message-warning-collapsed-bottom-br,.x-window-header-nx-message-warning-collapsed-bottom-mr{padding-right:4px}.x-window-header-nx-message-warning-collapsed-bottom-tl,.x-window-header-nx-message-warning-collapsed-bottom-bl,.x-window-header-nx-message-warning-collapsed-bottom-ml{padding-left:4px}.x-window-header-nx-message-warning-collapsed-bottom-tc{height:4px}.x-window-header-nx-message-warning-collapsed-bottom-bc{height:4px}.x-window-header-nx-message-warning-collapsed-bottom-tl,.x-window-header-nx-message-warning-collapsed-bottom-bl,.x-window-header-nx-message-warning-collapsed-bottom-tr,.x-window-header-nx-message-warning-collapsed-bottom-br,.x-window-header-nx-message-warning-collapsed-bottom-tc,.x-window-header-nx-message-warning-collapsed-bottom-bc,.x-window-header-nx-message-warning-collapsed-bottom-ml,.x-window-header-nx-message-warning-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-collapsed-bottom-corners.gif)}.x-window-header-nx-message-warning-collapsed-bottom-ml,.x-window-header-nx-message-warning-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-collapsed-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-warning-collapsed-bottom-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-bottom-tl,.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-bottom-bl{position:relative;right:0}.x-window-header-nx-message-warning-collapsed-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-warning-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-collapsed-bottom-sides.gif)"}.x-window-header-nx-message-warning-collapsed-left{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#f2862f}.x-window-header-nx-message-warning-collapsed-left-mc{background-color:#f2862f}.x-nbr .x-window-header-nx-message-warning-collapsed-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-warning-collapsed-left-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-warning-collapsed-left-tl{background-position:0 -8px}.x-window-header-nx-message-warning-collapsed-left-tr{background-position:right -12px}.x-window-header-nx-message-warning-collapsed-left-bl{background-position:0 -16px}.x-window-header-nx-message-warning-collapsed-left-br{background-position:right -20px}.x-window-header-nx-message-warning-collapsed-left-ml{background-position:0 top}.x-window-header-nx-message-warning-collapsed-left-mr{background-position:right top}.x-window-header-nx-message-warning-collapsed-left-tc{background-position:0 0}.x-window-header-nx-message-warning-collapsed-left-bc{background-position:0 -4px}.x-window-header-nx-message-warning-collapsed-left-tr,.x-window-header-nx-message-warning-collapsed-left-br,.x-window-header-nx-message-warning-collapsed-left-mr{padding-right:4px}.x-window-header-nx-message-warning-collapsed-left-tl,.x-window-header-nx-message-warning-collapsed-left-bl,.x-window-header-nx-message-warning-collapsed-left-ml{padding-left:4px}.x-window-header-nx-message-warning-collapsed-left-tc{height:4px}.x-window-header-nx-message-warning-collapsed-left-bc{height:4px}.x-window-header-nx-message-warning-collapsed-left-tl,.x-window-header-nx-message-warning-collapsed-left-bl,.x-window-header-nx-message-warning-collapsed-left-tr,.x-window-header-nx-message-warning-collapsed-left-br,.x-window-header-nx-message-warning-collapsed-left-tc,.x-window-header-nx-message-warning-collapsed-left-bc,.x-window-header-nx-message-warning-collapsed-left-ml,.x-window-header-nx-message-warning-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-collapsed-left-corners.gif)}.x-window-header-nx-message-warning-collapsed-left-ml,.x-window-header-nx-message-warning-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-warning-collapsed-left-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-warning-collapsed-left-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-left-tl,.x-strict .x-ie7 .x-window-header-nx-message-warning-collapsed-left-bl{position:relative;right:0}.x-window-header-nx-message-warning-collapsed-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-warning-collapsed-left-corners.gif), sides:url(images/window-header/window-header-nx-message-warning-collapsed-left-sides.gif)"}.x-window-header-nx-message-warning .x-window-header-icon{width:16px;height:16px;color:#333;font-size:16px;line-height:16px;background-position:center center}.x-window-header-nx-message-warning .x-window-header-glyph{color:#333;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-window-header-nx-message-warning .x-window-header-glyph{color:#925c31}.x-window-header-nx-message-warning-horizontal .x-window-header-icon-before-title{margin:0 6px 0 0}.x-window-header-nx-message-warning-horizontal .x-window-header-icon-after-title{margin:0 0 0 6px}.x-window-header-nx-message-warning-vertical .x-window-header-icon-before-title{margin:0 0 6px 0}.x-window-header-nx-message-warning-vertical .x-window-header-icon-after-title{margin:6px 0 0 0}.x-window-header-nx-message-warning-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-window-header-nx-message-warning-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-window-header-nx-message-warning-vertical .x-tool-after-title{margin:6px 0 0 0}.x-window-header-nx-message-warning-vertical .x-tool-before-title{margin:0 0 6px 0}.x-window-header-nx-message-warning{border-width:0!important}.x-nbr .x-window-nx-message-warning-collapsed .x-window-header{border-width:0!important}.x-window-nx-message-warning-outer-border-l{border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-warning-outer-border-b{border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-warning-outer-border-bl{border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-warning-outer-border-r{border-right-color:#606060!important;border-right-width:2px!important}.x-window-nx-message-warning-outer-border-rl{border-right-color:#606060!important;border-right-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-warning-outer-border-rb{border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-warning-outer-border-rbl{border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-warning-outer-border-t{border-top-color:#606060!important;border-top-width:2px!important}.x-window-nx-message-warning-outer-border-tl{border-top-color:#606060!important;border-top-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-warning-outer-border-tb{border-top-color:#606060!important;border-top-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-warning-outer-border-tbl{border-top-color:#606060!important;border-top-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-warning-outer-border-tr{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important}.x-window-nx-message-warning-outer-border-trl{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-warning-outer-border-trb{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-warning-outer-border-trbl{border-color:#606060!important;border-width:2px!important}.x-window-nx-message-success{border-color:#606060;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px}.x-window-nx-message-success{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:2px;border-style:solid;background-color:white}.x-window-nx-message-success-mc{background-color:white}.x-nbr .x-window-nx-message-success{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-nx-message-success-frameInfo{font-family:dh-4-4-4-4-2-2-2-2-10-10-10-10}.x-window-nx-message-success-tl{background-position:0 -8px}.x-window-nx-message-success-tr{background-position:right -12px}.x-window-nx-message-success-bl{background-position:0 -16px}.x-window-nx-message-success-br{background-position:right -20px}.x-window-nx-message-success-ml{background-position:0 top}.x-window-nx-message-success-mr{background-position:right top}.x-window-nx-message-success-tc{background-position:0 0}.x-window-nx-message-success-bc{background-position:0 -4px}.x-window-nx-message-success-tr,.x-window-nx-message-success-br,.x-window-nx-message-success-mr{padding-right:4px}.x-window-nx-message-success-tl,.x-window-nx-message-success-bl,.x-window-nx-message-success-ml{padding-left:4px}.x-window-nx-message-success-tc{height:4px}.x-window-nx-message-success-bc{height:4px}.x-window-nx-message-success-tl,.x-window-nx-message-success-bl,.x-window-nx-message-success-tr,.x-window-nx-message-success-br,.x-window-nx-message-success-tc,.x-window-nx-message-success-bc,.x-window-nx-message-success-ml,.x-window-nx-message-success-mr{zoom:1;background-image:url(images/window/window-nx-message-success-corners.gif)}.x-window-nx-message-success-ml,.x-window-nx-message-success-mr{zoom:1;background-image:url(images/window/window-nx-message-success-sides.gif);background-repeat:repeat-y}.x-window-nx-message-success-mc{padding:8px 8px 8px 8px}.x-strict .x-ie7 .x-window-nx-message-success-tl,.x-strict .x-ie7 .x-window-nx-message-success-bl{position:relative;right:0}.x-window-nx-message-success:after{display:none;content:"x-slicer:corners:url(images/window/window-nx-message-success-corners.gif), sides:url(images/window/window-nx-message-success-sides.gif)"}.x-window-body-nx-message-success{border-color:#606060;border-width:1px;border-style:solid;background:white;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-window-header-nx-message-success{font-size:13px;border-color:#606060;zoom:1;background-color:#0b9743}.x-window-header-nx-message-success .x-tool-img{background-color:#0b9743}.x-window-header-nx-message-success-vertical .x-window-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-window-header-nx-message-success-vertical .x-window-header-text-container{background-color:#0b9743;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#0b9743)}.x-window-header-text-container-nx-message-success{color:white;font-weight:bold;line-height:15px;font-family:arial,helvetica,verdana,sans-serif;font-size:13px;padding:1px 0 0;text-transform:none}.x-window-header-nx-message-success-top{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:10px 10px 8px 10px;border-width:0;border-style:solid;background-color:#0b9743}.x-window-header-nx-message-success-top-mc{background-color:#0b9743}.x-nbr .x-window-header-nx-message-success-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-success-top-frameInfo{font-family:dh-4-4-0-0-0-0-0-0-10-10-8-10}.x-window-header-nx-message-success-top-tl{background-position:0 -8px}.x-window-header-nx-message-success-top-tr{background-position:right -12px}.x-window-header-nx-message-success-top-bl{background-position:0 -16px}.x-window-header-nx-message-success-top-br{background-position:right -20px}.x-window-header-nx-message-success-top-ml{background-position:0 top}.x-window-header-nx-message-success-top-mr{background-position:right top}.x-window-header-nx-message-success-top-tc{background-position:0 0}.x-window-header-nx-message-success-top-bc{background-position:0 -4px}.x-window-header-nx-message-success-top-tr,.x-window-header-nx-message-success-top-br,.x-window-header-nx-message-success-top-mr{padding-right:4px}.x-window-header-nx-message-success-top-tl,.x-window-header-nx-message-success-top-bl,.x-window-header-nx-message-success-top-ml{padding-left:4px}.x-window-header-nx-message-success-top-tc{height:4px}.x-window-header-nx-message-success-top-bc{height:0}.x-window-header-nx-message-success-top-tl,.x-window-header-nx-message-success-top-bl,.x-window-header-nx-message-success-top-tr,.x-window-header-nx-message-success-top-br,.x-window-header-nx-message-success-top-tc,.x-window-header-nx-message-success-top-bc,.x-window-header-nx-message-success-top-ml,.x-window-header-nx-message-success-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-top-corners.gif)}.x-window-header-nx-message-success-top-ml,.x-window-header-nx-message-success-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-top-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-success-top-mc{padding:6px 6px 8px 6px}.x-strict .x-ie7 .x-window-header-nx-message-success-top-tl,.x-strict .x-ie7 .x-window-header-nx-message-success-top-bl{position:relative;right:0}.x-window-header-nx-message-success-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-success-top-corners.gif), sides:url(images/window-header/window-header-nx-message-success-top-sides.gif)"}.x-window-header-nx-message-success-right{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;padding:10px 10px 10px 8px;border-width:0;border-style:solid;background-color:#0b9743}.x-window-header-nx-message-success-right-mc{background-color:#0b9743}.x-nbr .x-window-header-nx-message-success-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-success-right-frameInfo{font-family:dh-0-4-4-0-0-0-0-0-10-10-10-8}.x-window-header-nx-message-success-right-tl{background-position:0 -8px}.x-window-header-nx-message-success-right-tr{background-position:right -12px}.x-window-header-nx-message-success-right-bl{background-position:0 -16px}.x-window-header-nx-message-success-right-br{background-position:right -20px}.x-window-header-nx-message-success-right-ml{background-position:0 top}.x-window-header-nx-message-success-right-mr{background-position:right top}.x-window-header-nx-message-success-right-tc{background-position:0 0}.x-window-header-nx-message-success-right-bc{background-position:0 -4px}.x-window-header-nx-message-success-right-tr,.x-window-header-nx-message-success-right-br,.x-window-header-nx-message-success-right-mr{padding-right:4px}.x-window-header-nx-message-success-right-tl,.x-window-header-nx-message-success-right-bl,.x-window-header-nx-message-success-right-ml{padding-left:0}.x-window-header-nx-message-success-right-tc{height:4px}.x-window-header-nx-message-success-right-bc{height:4px}.x-window-header-nx-message-success-right-tl,.x-window-header-nx-message-success-right-bl,.x-window-header-nx-message-success-right-tr,.x-window-header-nx-message-success-right-br,.x-window-header-nx-message-success-right-tc,.x-window-header-nx-message-success-right-bc,.x-window-header-nx-message-success-right-ml,.x-window-header-nx-message-success-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-right-corners.gif)}.x-window-header-nx-message-success-right-ml,.x-window-header-nx-message-success-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-right-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-success-right-mc{padding:6px 6px 6px 8px}.x-strict .x-ie7 .x-window-header-nx-message-success-right-tl,.x-strict .x-ie7 .x-window-header-nx-message-success-right-bl{position:relative;right:0}.x-window-header-nx-message-success-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-success-right-corners.gif), sides:url(images/window-header/window-header-nx-message-success-right-sides.gif)"}.x-window-header-nx-message-success-bottom{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:8px 10px 10px 10px;border-width:0;border-style:solid;background-color:#0b9743}.x-window-header-nx-message-success-bottom-mc{background-color:#0b9743}.x-nbr .x-window-header-nx-message-success-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-success-bottom-frameInfo{font-family:dh-0-0-4-4-0-0-0-0-8-10-10-10}.x-window-header-nx-message-success-bottom-tl{background-position:0 -8px}.x-window-header-nx-message-success-bottom-tr{background-position:right -12px}.x-window-header-nx-message-success-bottom-bl{background-position:0 -16px}.x-window-header-nx-message-success-bottom-br{background-position:right -20px}.x-window-header-nx-message-success-bottom-ml{background-position:0 top}.x-window-header-nx-message-success-bottom-mr{background-position:right top}.x-window-header-nx-message-success-bottom-tc{background-position:0 0}.x-window-header-nx-message-success-bottom-bc{background-position:0 -4px}.x-window-header-nx-message-success-bottom-tr,.x-window-header-nx-message-success-bottom-br,.x-window-header-nx-message-success-bottom-mr{padding-right:4px}.x-window-header-nx-message-success-bottom-tl,.x-window-header-nx-message-success-bottom-bl,.x-window-header-nx-message-success-bottom-ml{padding-left:4px}.x-window-header-nx-message-success-bottom-tc{height:0}.x-window-header-nx-message-success-bottom-bc{height:4px}.x-window-header-nx-message-success-bottom-tl,.x-window-header-nx-message-success-bottom-bl,.x-window-header-nx-message-success-bottom-tr,.x-window-header-nx-message-success-bottom-br,.x-window-header-nx-message-success-bottom-tc,.x-window-header-nx-message-success-bottom-bc,.x-window-header-nx-message-success-bottom-ml,.x-window-header-nx-message-success-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-bottom-corners.gif)}.x-window-header-nx-message-success-bottom-ml,.x-window-header-nx-message-success-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-success-bottom-mc{padding:8px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-success-bottom-tl,.x-strict .x-ie7 .x-window-header-nx-message-success-bottom-bl{position:relative;right:0}.x-window-header-nx-message-success-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-success-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-success-bottom-sides.gif)"}.x-window-header-nx-message-success-left{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;padding:10px 8px 10px 10px;border-width:0;border-style:solid;background-color:#0b9743}.x-window-header-nx-message-success-left-mc{background-color:#0b9743}.x-nbr .x-window-header-nx-message-success-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-success-left-frameInfo{font-family:dh-4-0-0-4-0-0-0-0-10-8-10-10}.x-window-header-nx-message-success-left-tl{background-position:0 -8px}.x-window-header-nx-message-success-left-tr{background-position:right -12px}.x-window-header-nx-message-success-left-bl{background-position:0 -16px}.x-window-header-nx-message-success-left-br{background-position:right -20px}.x-window-header-nx-message-success-left-ml{background-position:0 top}.x-window-header-nx-message-success-left-mr{background-position:right top}.x-window-header-nx-message-success-left-tc{background-position:0 0}.x-window-header-nx-message-success-left-bc{background-position:0 -4px}.x-window-header-nx-message-success-left-tr,.x-window-header-nx-message-success-left-br,.x-window-header-nx-message-success-left-mr{padding-right:0}.x-window-header-nx-message-success-left-tl,.x-window-header-nx-message-success-left-bl,.x-window-header-nx-message-success-left-ml{padding-left:4px}.x-window-header-nx-message-success-left-tc{height:4px}.x-window-header-nx-message-success-left-bc{height:4px}.x-window-header-nx-message-success-left-tl,.x-window-header-nx-message-success-left-bl,.x-window-header-nx-message-success-left-tr,.x-window-header-nx-message-success-left-br,.x-window-header-nx-message-success-left-tc,.x-window-header-nx-message-success-left-bc,.x-window-header-nx-message-success-left-ml,.x-window-header-nx-message-success-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-left-corners.gif)}.x-window-header-nx-message-success-left-ml,.x-window-header-nx-message-success-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-left-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-success-left-mc{padding:6px 8px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-success-left-tl,.x-strict .x-ie7 .x-window-header-nx-message-success-left-bl{position:relative;right:0}.x-window-header-nx-message-success-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-success-left-corners.gif), sides:url(images/window-header/window-header-nx-message-success-left-sides.gif)"}.x-window-header-nx-message-success-collapsed-top{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#0b9743}.x-window-header-nx-message-success-collapsed-top-mc{background-color:#0b9743}.x-nbr .x-window-header-nx-message-success-collapsed-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-success-collapsed-top-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-success-collapsed-top-tl{background-position:0 -8px}.x-window-header-nx-message-success-collapsed-top-tr{background-position:right -12px}.x-window-header-nx-message-success-collapsed-top-bl{background-position:0 -16px}.x-window-header-nx-message-success-collapsed-top-br{background-position:right -20px}.x-window-header-nx-message-success-collapsed-top-ml{background-position:0 top}.x-window-header-nx-message-success-collapsed-top-mr{background-position:right top}.x-window-header-nx-message-success-collapsed-top-tc{background-position:0 0}.x-window-header-nx-message-success-collapsed-top-bc{background-position:0 -4px}.x-window-header-nx-message-success-collapsed-top-tr,.x-window-header-nx-message-success-collapsed-top-br,.x-window-header-nx-message-success-collapsed-top-mr{padding-right:4px}.x-window-header-nx-message-success-collapsed-top-tl,.x-window-header-nx-message-success-collapsed-top-bl,.x-window-header-nx-message-success-collapsed-top-ml{padding-left:4px}.x-window-header-nx-message-success-collapsed-top-tc{height:4px}.x-window-header-nx-message-success-collapsed-top-bc{height:4px}.x-window-header-nx-message-success-collapsed-top-tl,.x-window-header-nx-message-success-collapsed-top-bl,.x-window-header-nx-message-success-collapsed-top-tr,.x-window-header-nx-message-success-collapsed-top-br,.x-window-header-nx-message-success-collapsed-top-tc,.x-window-header-nx-message-success-collapsed-top-bc,.x-window-header-nx-message-success-collapsed-top-ml,.x-window-header-nx-message-success-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-collapsed-top-corners.gif)}.x-window-header-nx-message-success-collapsed-top-ml,.x-window-header-nx-message-success-collapsed-top-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-collapsed-top-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-success-collapsed-top-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-top-tl,.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-top-bl{position:relative;right:0}.x-window-header-nx-message-success-collapsed-top:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-success-collapsed-top-corners.gif), sides:url(images/window-header/window-header-nx-message-success-collapsed-top-sides.gif)"}.x-window-header-nx-message-success-collapsed-right{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#0b9743}.x-window-header-nx-message-success-collapsed-right-mc{background-color:#0b9743}.x-nbr .x-window-header-nx-message-success-collapsed-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-success-collapsed-right-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-success-collapsed-right-tl{background-position:0 -8px}.x-window-header-nx-message-success-collapsed-right-tr{background-position:right -12px}.x-window-header-nx-message-success-collapsed-right-bl{background-position:0 -16px}.x-window-header-nx-message-success-collapsed-right-br{background-position:right -20px}.x-window-header-nx-message-success-collapsed-right-ml{background-position:0 top}.x-window-header-nx-message-success-collapsed-right-mr{background-position:right top}.x-window-header-nx-message-success-collapsed-right-tc{background-position:0 0}.x-window-header-nx-message-success-collapsed-right-bc{background-position:0 -4px}.x-window-header-nx-message-success-collapsed-right-tr,.x-window-header-nx-message-success-collapsed-right-br,.x-window-header-nx-message-success-collapsed-right-mr{padding-right:4px}.x-window-header-nx-message-success-collapsed-right-tl,.x-window-header-nx-message-success-collapsed-right-bl,.x-window-header-nx-message-success-collapsed-right-ml{padding-left:4px}.x-window-header-nx-message-success-collapsed-right-tc{height:4px}.x-window-header-nx-message-success-collapsed-right-bc{height:4px}.x-window-header-nx-message-success-collapsed-right-tl,.x-window-header-nx-message-success-collapsed-right-bl,.x-window-header-nx-message-success-collapsed-right-tr,.x-window-header-nx-message-success-collapsed-right-br,.x-window-header-nx-message-success-collapsed-right-tc,.x-window-header-nx-message-success-collapsed-right-bc,.x-window-header-nx-message-success-collapsed-right-ml,.x-window-header-nx-message-success-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-collapsed-right-corners.gif)}.x-window-header-nx-message-success-collapsed-right-ml,.x-window-header-nx-message-success-collapsed-right-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-collapsed-right-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-success-collapsed-right-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-right-tl,.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-right-bl{position:relative;right:0}.x-window-header-nx-message-success-collapsed-right:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-success-collapsed-right-corners.gif), sides:url(images/window-header/window-header-nx-message-success-collapsed-right-sides.gif)"}.x-window-header-nx-message-success-collapsed-bottom{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#0b9743}.x-window-header-nx-message-success-collapsed-bottom-mc{background-color:#0b9743}.x-nbr .x-window-header-nx-message-success-collapsed-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-success-collapsed-bottom-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-success-collapsed-bottom-tl{background-position:0 -8px}.x-window-header-nx-message-success-collapsed-bottom-tr{background-position:right -12px}.x-window-header-nx-message-success-collapsed-bottom-bl{background-position:0 -16px}.x-window-header-nx-message-success-collapsed-bottom-br{background-position:right -20px}.x-window-header-nx-message-success-collapsed-bottom-ml{background-position:0 top}.x-window-header-nx-message-success-collapsed-bottom-mr{background-position:right top}.x-window-header-nx-message-success-collapsed-bottom-tc{background-position:0 0}.x-window-header-nx-message-success-collapsed-bottom-bc{background-position:0 -4px}.x-window-header-nx-message-success-collapsed-bottom-tr,.x-window-header-nx-message-success-collapsed-bottom-br,.x-window-header-nx-message-success-collapsed-bottom-mr{padding-right:4px}.x-window-header-nx-message-success-collapsed-bottom-tl,.x-window-header-nx-message-success-collapsed-bottom-bl,.x-window-header-nx-message-success-collapsed-bottom-ml{padding-left:4px}.x-window-header-nx-message-success-collapsed-bottom-tc{height:4px}.x-window-header-nx-message-success-collapsed-bottom-bc{height:4px}.x-window-header-nx-message-success-collapsed-bottom-tl,.x-window-header-nx-message-success-collapsed-bottom-bl,.x-window-header-nx-message-success-collapsed-bottom-tr,.x-window-header-nx-message-success-collapsed-bottom-br,.x-window-header-nx-message-success-collapsed-bottom-tc,.x-window-header-nx-message-success-collapsed-bottom-bc,.x-window-header-nx-message-success-collapsed-bottom-ml,.x-window-header-nx-message-success-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-collapsed-bottom-corners.gif)}.x-window-header-nx-message-success-collapsed-bottom-ml,.x-window-header-nx-message-success-collapsed-bottom-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-collapsed-bottom-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-success-collapsed-bottom-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-bottom-tl,.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-bottom-bl{position:relative;right:0}.x-window-header-nx-message-success-collapsed-bottom:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-success-collapsed-bottom-corners.gif), sides:url(images/window-header/window-header-nx-message-success-collapsed-bottom-sides.gif)"}.x-window-header-nx-message-success-collapsed-left{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;padding:10px 10px 10px 10px;border-width:0;border-style:solid;background-color:#0b9743}.x-window-header-nx-message-success-collapsed-left-mc{background-color:#0b9743}.x-nbr .x-window-header-nx-message-success-collapsed-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-window-header-nx-message-success-collapsed-left-frameInfo{font-family:dh-4-4-4-4-0-0-0-0-10-10-10-10}.x-window-header-nx-message-success-collapsed-left-tl{background-position:0 -8px}.x-window-header-nx-message-success-collapsed-left-tr{background-position:right -12px}.x-window-header-nx-message-success-collapsed-left-bl{background-position:0 -16px}.x-window-header-nx-message-success-collapsed-left-br{background-position:right -20px}.x-window-header-nx-message-success-collapsed-left-ml{background-position:0 top}.x-window-header-nx-message-success-collapsed-left-mr{background-position:right top}.x-window-header-nx-message-success-collapsed-left-tc{background-position:0 0}.x-window-header-nx-message-success-collapsed-left-bc{background-position:0 -4px}.x-window-header-nx-message-success-collapsed-left-tr,.x-window-header-nx-message-success-collapsed-left-br,.x-window-header-nx-message-success-collapsed-left-mr{padding-right:4px}.x-window-header-nx-message-success-collapsed-left-tl,.x-window-header-nx-message-success-collapsed-left-bl,.x-window-header-nx-message-success-collapsed-left-ml{padding-left:4px}.x-window-header-nx-message-success-collapsed-left-tc{height:4px}.x-window-header-nx-message-success-collapsed-left-bc{height:4px}.x-window-header-nx-message-success-collapsed-left-tl,.x-window-header-nx-message-success-collapsed-left-bl,.x-window-header-nx-message-success-collapsed-left-tr,.x-window-header-nx-message-success-collapsed-left-br,.x-window-header-nx-message-success-collapsed-left-tc,.x-window-header-nx-message-success-collapsed-left-bc,.x-window-header-nx-message-success-collapsed-left-ml,.x-window-header-nx-message-success-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-collapsed-left-corners.gif)}.x-window-header-nx-message-success-collapsed-left-ml,.x-window-header-nx-message-success-collapsed-left-mr{zoom:1;background-image:url(images/window-header/window-header-nx-message-success-collapsed-left-sides.gif);background-repeat:repeat-y}.x-window-header-nx-message-success-collapsed-left-mc{padding:6px 6px 6px 6px}.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-left-tl,.x-strict .x-ie7 .x-window-header-nx-message-success-collapsed-left-bl{position:relative;right:0}.x-window-header-nx-message-success-collapsed-left:after{display:none;content:"x-slicer:corners:url(images/window-header/window-header-nx-message-success-collapsed-left-corners.gif), sides:url(images/window-header/window-header-nx-message-success-collapsed-left-sides.gif)"}.x-window-header-nx-message-success .x-window-header-icon{width:16px;height:16px;color:#333;font-size:16px;line-height:16px;background-position:center center}.x-window-header-nx-message-success .x-window-header-glyph{color:#333;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-window-header-nx-message-success .x-window-header-glyph{color:#1f653b}.x-window-header-nx-message-success-horizontal .x-window-header-icon-before-title{margin:0 6px 0 0}.x-window-header-nx-message-success-horizontal .x-window-header-icon-after-title{margin:0 0 0 6px}.x-window-header-nx-message-success-vertical .x-window-header-icon-before-title{margin:0 0 6px 0}.x-window-header-nx-message-success-vertical .x-window-header-icon-after-title{margin:6px 0 0 0}.x-window-header-nx-message-success-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-window-header-nx-message-success-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-window-header-nx-message-success-vertical .x-tool-after-title{margin:6px 0 0 0}.x-window-header-nx-message-success-vertical .x-tool-before-title{margin:0 0 6px 0}.x-window-header-nx-message-success{border-width:0!important}.x-nbr .x-window-nx-message-success-collapsed .x-window-header{border-width:0!important}.x-window-nx-message-success-outer-border-l{border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-success-outer-border-b{border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-success-outer-border-bl{border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-success-outer-border-r{border-right-color:#606060!important;border-right-width:2px!important}.x-window-nx-message-success-outer-border-rl{border-right-color:#606060!important;border-right-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-success-outer-border-rb{border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-success-outer-border-rbl{border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-success-outer-border-t{border-top-color:#606060!important;border-top-width:2px!important}.x-window-nx-message-success-outer-border-tl{border-top-color:#606060!important;border-top-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-success-outer-border-tb{border-top-color:#606060!important;border-top-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-success-outer-border-tbl{border-top-color:#606060!important;border-top-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-success-outer-border-tr{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important}.x-window-nx-message-success-outer-border-trl{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important;border-left-color:#606060!important;border-left-width:2px!important}.x-window-nx-message-success-outer-border-trb{border-top-color:#606060!important;border-top-width:2px!important;border-right-color:#606060!important;border-right-width:2px!important;border-bottom-color:#606060!important;border-bottom-width:2px!important}.x-window-nx-message-success-outer-border-trbl{border-color:#606060!important;border-width:2px!important}.fixed-modal{top:0!important}.x-window-body .x-toolbar-footer{padding-left:0!important}.nx-middle-align{vertical-align:middle}.nx-iframe-full{background-color:white}.x-panel-borderless{border-color:#444;padding:5px 0 0 0}.x-panel-header-borderless{font-size:13px;border:1px solid #444}.x-panel-header-borderless .x-tool-img{background-color:#444}.x-panel-header-borderless-horizontal{padding:9px 9px 10px 9px}.x-panel-header-borderless-horizontal-noborder{padding:10px 10px 10px 10px}.x-panel-header-borderless-vertical{padding:9px 9px 9px 10px}.x-panel-header-borderless-vertical-noborder{padding:10px 10px 10px 10px}.x-panel-header-text-container-borderless{color:white;font-size:13px;font-weight:bold;font-family:arial,helvetica,verdana,sans-serif;line-height:15px;padding:1px 0 0;text-transform:none}.x-panel-body-borderless{background:transparent;border-color:#444;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:0;border-style:solid}.x-panel-header-borderless{background-image:none;background-color:#444}.x-panel-header-borderless-vertical{background-image:none;background-color:#444}.x-panel .x-panel-header-borderless-collapsed-border-top{border-bottom-width:1px!important}.x-panel .x-panel-header-borderless-collapsed-border-right{border-left-width:1px!important}.x-panel .x-panel-header-borderless-collapsed-border-bottom{border-top-width:1px!important}.x-panel .x-panel-header-borderless-collapsed-border-left{border-right-width:1px!important}.x-panel-header-borderless-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-borderless-bottom:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-borderless-left:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-borderless-right:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-borderless-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-borderless-vertical .x-panel-header-text-container{background-color:#444;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#444444)}.x-panel-header-borderless .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-borderless .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-borderless .x-panel-header-glyph{color:#a1a1a1}.x-panel-header-borderless-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-borderless-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-borderless-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-borderless-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-borderless-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-borderless-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-borderless-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-borderless-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-borderless-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-borderless-outer-border-l{border-left-color:#444!important;border-left-width:1px!important}.x-panel-borderless-outer-border-b{border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-borderless-outer-border-bl{border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-borderless-outer-border-r{border-right-color:#444!important;border-right-width:1px!important}.x-panel-borderless-outer-border-rl{border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-borderless-outer-border-rb{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-borderless-outer-border-rbl{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-borderless-outer-border-t{border-top-color:#444!important;border-top-width:1px!important}.x-panel-borderless-outer-border-tl{border-top-color:#444!important;border-top-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-borderless-outer-border-tb{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-borderless-outer-border-tbl{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-borderless-outer-border-tr{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important}.x-panel-borderless-outer-border-trl{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-borderless-outer-border-trb{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-borderless-outer-border-trbl{border-color:#444!important;border-width:1px!important}.nx-rowexpander{padding:5px}.nx-rowexpander tbody td:first-child{padding-right:5px;font-weight:bold}.nx-disabled-row td{color:#777}.nx-disabled-row td button{color:#777}.nx-table-header-label{font-weight:bold}.x-form-checkboxgroup-body{padding:0}fieldset.nx-form-section{margin-bottom:0;padding-bottom:20px}fieldset.nx-form-section .x-fieldset-body{padding-left:10px}fieldset.nx-form-section.nx-no-title{padding-top:0}fieldset.nx-form-section>legend>span>div>.x-fieldset-header-text{font-size:18px;line-height:22px}.nx-multiselect .x-panel-header{background-color:transparent;border:0;padding-left:0;padding-bottom:9px;padding-right:0}.nx-multiselect .x-panel-header .x-header-text{color:#333;font-weight:normal}.nx-multiselect .x-form-item .x-form-trigger-wrap{margin-bottom:9px}.nx-multiselect .x-panel-body{border:1px #ddd solid!important}.x-form-fieldcontainer.nx-invalid .nx-multiselect:nth-child(3) .x-panel-body{border:1px #db2852 solid!important}.x-form-multiselect-body .x-boundlist .x-mask{background:0}.nx-itemselector-disabled .x-form-item-label{opacity:1}.x-form-itemselector-body .x-form-item{margin:0}.nx-monospace-field textarea,.nx-monospace-field input{font-family:"Courier New",Courier,monospace;font-size:13px}.x-panel-nx-light{border-color:#444;padding:0}.x-panel-header-nx-light{font-size:16px;border:1px solid #444}.x-panel-header-nx-light .x-tool-img{background-color:#f4f4f4}.x-panel-header-nx-light-horizontal{padding:9px 9px 10px 9px}.x-panel-header-nx-light-horizontal-noborder{padding:10px 10px 10px 10px}.x-panel-header-nx-light-vertical{padding:9px 9px 9px 10px}.x-panel-header-nx-light-vertical-noborder{padding:10px 10px 10px 10px}.x-panel-header-text-container-nx-light{color:#333;font-size:16px;font-weight:bold;font-family:arial,helvetica,verdana,sans-serif;line-height:20px;padding:1px 0 0;text-transform:none}.x-panel-body-nx-light{background:transparent;border-color:#444;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-header-nx-light{background-image:none;background-color:#f4f4f4}.x-panel-header-nx-light-vertical{background-image:none;background-color:#f4f4f4}.x-panel .x-panel-header-nx-light-collapsed-border-top{border-bottom-width:1px!important}.x-panel .x-panel-header-nx-light-collapsed-border-right{border-left-width:1px!important}.x-panel .x-panel-header-nx-light-collapsed-border-bottom{border-top-width:1px!important}.x-panel .x-panel-header-nx-light-collapsed-border-left{border-right-width:1px!important}.x-panel-header-nx-light-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-light-bottom:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-light-left:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-light-right:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-light-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-nx-light-vertical .x-panel-header-text-container{background-color:#f4f4f4;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#f4f4f4)}.x-panel-header-nx-light .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-nx-light .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-nx-light .x-panel-header-glyph{color:#f9f9f9}.x-panel-header-nx-light-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-nx-light-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-nx-light-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-nx-light-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-nx-light-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-nx-light-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-nx-light-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-nx-light-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-nx-light-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-nx-light-outer-border-l{border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-light-outer-border-b{border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-light-outer-border-bl{border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-light-outer-border-r{border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-light-outer-border-rl{border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-light-outer-border-rb{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-light-outer-border-rbl{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-light-outer-border-t{border-top-color:#444!important;border-top-width:1px!important}.x-panel-nx-light-outer-border-tl{border-top-color:#444!important;border-top-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-light-outer-border-tb{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-light-outer-border-tbl{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-light-outer-border-tr{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-light-outer-border-trl{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-light-outer-border-trb{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-light-outer-border-trbl{border-color:#444!important;border-width:1px!important}.x-tab-nx-light-top{-webkit-border-radius:6px;-moz-border-radius:6px;-ms-border-radius:6px;-o-border-radius:6px;border-radius:6px;padding:5px 12px 6px 12px;border-width:1px 1px 0 1px;border-style:solid;background-color:#f4f4f4}.x-tab-nx-light-top-mc{background-color:#f4f4f4}.x-nbr .x-tab-nx-light-top{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-tab-nx-light-top-frameInfo{font-family:th-6-6-6-6-1-1-0-1-5-12-6-12}.x-tab-nx-light-top-tl{background-position:0 -12px}.x-tab-nx-light-top-tr{background-position:right -18px}.x-tab-nx-light-top-bl{background-position:0 -24px}.x-tab-nx-light-top-br{background-position:right -30px}.x-tab-nx-light-top-ml{background-position:0 top}.x-tab-nx-light-top-mr{background-position:right top}.x-tab-nx-light-top-tc{background-position:0 0}.x-tab-nx-light-top-bc{background-position:0 -6px}.x-tab-nx-light-top-tr,.x-tab-nx-light-top-br,.x-tab-nx-light-top-mr{padding-right:6px}.x-tab-nx-light-top-tl,.x-tab-nx-light-top-bl,.x-tab-nx-light-top-ml{padding-left:6px}.x-tab-nx-light-top-tc{height:6px}.x-tab-nx-light-top-bc{height:6px}.x-tab-nx-light-top-tl,.x-tab-nx-light-top-bl,.x-tab-nx-light-top-tr,.x-tab-nx-light-top-br,.x-tab-nx-light-top-tc,.x-tab-nx-light-top-bc,.x-tab-nx-light-top-ml,.x-tab-nx-light-top-mr{zoom:1;background-image:url(images/tab/tab-nx-light-top-corners.gif)}.x-tab-nx-light-top-ml,.x-tab-nx-light-top-mr{zoom:1;background-image:url(images/tab/tab-nx-light-top-sides.gif);background-repeat:repeat-y}.x-tab-nx-light-top-mc{padding:0 7px 0 7px}.x-strict .x-ie7 .x-tab-nx-light-top-tl,.x-strict .x-ie7 .x-tab-nx-light-top-bl{position:relative;right:0}.x-tab-nx-light-top:after{display:none;content:"x-slicer:corners:url(images/tab/tab-nx-light-top-corners.gif), sides:url(images/tab/tab-nx-light-top-sides.gif)"}.x-tab-nx-light-bottom{-moz-border-radius-topleft:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-topright:6px;-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-moz-border-radius-bottomright:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-bottomleft:6px;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;padding:6px 12px 5px 12px;border-width:0 1px 1px 1px;border-style:solid;background-color:#f4f4f4}.x-tab-nx-light-bottom-mc{background-color:#f4f4f4}.x-nbr .x-tab-nx-light-bottom{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-tab-nx-light-bottom-frameInfo{font-family:th-6-6-6-6-0-1-1-1-6-12-5-12}.x-tab-nx-light-bottom-tl{background-position:0 -12px}.x-tab-nx-light-bottom-tr{background-position:right -18px}.x-tab-nx-light-bottom-bl{background-position:0 -24px}.x-tab-nx-light-bottom-br{background-position:right -30px}.x-tab-nx-light-bottom-ml{background-position:0 top}.x-tab-nx-light-bottom-mr{background-position:right top}.x-tab-nx-light-bottom-tc{background-position:0 0}.x-tab-nx-light-bottom-bc{background-position:0 -6px}.x-tab-nx-light-bottom-tr,.x-tab-nx-light-bottom-br,.x-tab-nx-light-bottom-mr{padding-right:6px}.x-tab-nx-light-bottom-tl,.x-tab-nx-light-bottom-bl,.x-tab-nx-light-bottom-ml{padding-left:6px}.x-tab-nx-light-bottom-tc{height:6px}.x-tab-nx-light-bottom-bc{height:6px}.x-tab-nx-light-bottom-tl,.x-tab-nx-light-bottom-bl,.x-tab-nx-light-bottom-tr,.x-tab-nx-light-bottom-br,.x-tab-nx-light-bottom-tc,.x-tab-nx-light-bottom-bc,.x-tab-nx-light-bottom-ml,.x-tab-nx-light-bottom-mr{zoom:1;background-image:url(images/tab/tab-nx-light-bottom-corners.gif)}.x-tab-nx-light-bottom-ml,.x-tab-nx-light-bottom-mr{zoom:1;background-image:url(images/tab/tab-nx-light-bottom-sides.gif);background-repeat:repeat-y}.x-tab-nx-light-bottom-mc{padding:0 7px 0 7px}.x-strict .x-ie7 .x-tab-nx-light-bottom-tl,.x-strict .x-ie7 .x-tab-nx-light-bottom-bl{position:relative;right:0}.x-tab-nx-light-bottom:after{display:none;content:"x-slicer:corners:url(images/tab/tab-nx-light-bottom-corners.gif), sides:url(images/tab/tab-nx-light-bottom-sides.gif)"}.x-tab-nx-light-left{-webkit-border-radius:6px;-moz-border-radius:6px;-ms-border-radius:6px;-o-border-radius:6px;border-radius:6px;padding:5px 12px 6px 12px;border-width:1px 1px 0 1px;border-style:solid;background-color:#f4f4f4}.x-tab-nx-light-left-mc{background-color:#f4f4f4}.x-nbr .x-tab-nx-light-left{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-tab-nx-light-left-frameInfo{font-family:th-6-6-6-6-1-1-0-1-5-12-6-12}.x-tab-nx-light-left-tl{background-position:0 -12px}.x-tab-nx-light-left-tr{background-position:right -18px}.x-tab-nx-light-left-bl{background-position:0 -24px}.x-tab-nx-light-left-br{background-position:right -30px}.x-tab-nx-light-left-ml{background-position:0 top}.x-tab-nx-light-left-mr{background-position:right top}.x-tab-nx-light-left-tc{background-position:0 0}.x-tab-nx-light-left-bc{background-position:0 -6px}.x-tab-nx-light-left-tr,.x-tab-nx-light-left-br,.x-tab-nx-light-left-mr{padding-right:6px}.x-tab-nx-light-left-tl,.x-tab-nx-light-left-bl,.x-tab-nx-light-left-ml{padding-left:6px}.x-tab-nx-light-left-tc{height:6px}.x-tab-nx-light-left-bc{height:6px}.x-tab-nx-light-left-tl,.x-tab-nx-light-left-bl,.x-tab-nx-light-left-tr,.x-tab-nx-light-left-br,.x-tab-nx-light-left-tc,.x-tab-nx-light-left-bc,.x-tab-nx-light-left-ml,.x-tab-nx-light-left-mr{zoom:1;background-image:url(images/tab/tab-nx-light-top-corners.gif)}.x-tab-nx-light-left-ml,.x-tab-nx-light-left-mr{zoom:1;background-image:url(images/tab/tab-nx-light-top-sides.gif);background-repeat:repeat-y}.x-tab-nx-light-left-mc{padding:0 7px 0 7px}.x-strict .x-ie7 .x-tab-nx-light-left-tl,.x-strict .x-ie7 .x-tab-nx-light-left-bl{position:relative;right:0}.x-tab-nx-light-left:after{display:none;content:"x-slicer:corners:url(images/tab/tab-nx-light-top-corners.gif), sides:url(images/tab/tab-nx-light-top-sides.gif)"}.x-tab-nx-light-right{-webkit-border-radius:6px;-moz-border-radius:6px;-ms-border-radius:6px;-o-border-radius:6px;border-radius:6px;padding:5px 12px 6px 12px;border-width:1px 1px 0 1px;border-style:solid;background-color:#f4f4f4}.x-tab-nx-light-right-mc{background-color:#f4f4f4}.x-nbr .x-tab-nx-light-right{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent}.x-tab-nx-light-right-frameInfo{font-family:th-6-6-6-6-1-1-0-1-5-12-6-12}.x-tab-nx-light-right-tl{background-position:0 -12px}.x-tab-nx-light-right-tr{background-position:right -18px}.x-tab-nx-light-right-bl{background-position:0 -24px}.x-tab-nx-light-right-br{background-position:right -30px}.x-tab-nx-light-right-ml{background-position:0 top}.x-tab-nx-light-right-mr{background-position:right top}.x-tab-nx-light-right-tc{background-position:0 0}.x-tab-nx-light-right-bc{background-position:0 -6px}.x-tab-nx-light-right-tr,.x-tab-nx-light-right-br,.x-tab-nx-light-right-mr{padding-right:6px}.x-tab-nx-light-right-tl,.x-tab-nx-light-right-bl,.x-tab-nx-light-right-ml{padding-left:6px}.x-tab-nx-light-right-tc{height:6px}.x-tab-nx-light-right-bc{height:6px}.x-tab-nx-light-right-tl,.x-tab-nx-light-right-bl,.x-tab-nx-light-right-tr,.x-tab-nx-light-right-br,.x-tab-nx-light-right-tc,.x-tab-nx-light-right-bc,.x-tab-nx-light-right-ml,.x-tab-nx-light-right-mr{zoom:1;background-image:url(images/tab/tab-nx-light-top-corners.gif)}.x-tab-nx-light-right-ml,.x-tab-nx-light-right-mr{zoom:1;background-image:url(images/tab/tab-nx-light-top-sides.gif);background-repeat:repeat-y}.x-tab-nx-light-right-mc{padding:0 7px 0 7px}.x-strict .x-ie7 .x-tab-nx-light-right-tl,.x-strict .x-ie7 .x-tab-nx-light-right-bl{position:relative;right:0}.x-tab-nx-light-right:after{display:none;content:"x-slicer:corners:url(images/tab/tab-nx-light-top-corners.gif), sides:url(images/tab/tab-nx-light-top-sides.gif)"}.x-tab-nx-light{border-color:#f4f4f4;margin:0 2px;cursor:pointer}.x-tab-nx-light .x-tab-inner{font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#006bbf;line-height:16px}.x-tab-nx-light .x-tab-icon-el{width:16px;height:16px;line-height:16px;background-position:center center}.x-tab-nx-light .x-tab-glyph{font-size:16px;color:white;opacity:.5}.x-ie8m .x-tab-nx-light .x-tab-glyph{color:#f9f9f9}.x-strict .x-ie9 .x-tab-bar-vertical .x-tab-nx-light{padding-left:0}.x-strict .x-ie9 .x-tab-bar-vertical .x-tab-nx-light .x-tab-button{padding-left:12px}.x-strict .x-ie9 .x-tab-bar-vertical .x-tab-nx-light .x-tab-icon-el{left:12px}.x-tab-nx-light-icon .x-tab-inner{width:16px}.x-tab-nx-light-left{margin:0 2px 0 2px}.x-tab-nx-light-top,.x-tab-nx-light-left,.x-tab-nx-light-right{border-bottom:1px solid #444}.x-tab-nx-light-bottom{border-top:1px solid #444}.x-tab-nx-light-left{-webkit-transform:rotate(270deg);-webkit-transform-origin:100% 0;-moz-transform:rotate(270deg);-moz-transform-origin:100% 0;-o-transform:rotate(270deg);-o-transform-origin:100% 0;transform:rotate(270deg);transform-origin:100% 0}.x-ie9m .x-tab-nx-light-left{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3)}.x-tab-nx-light-right{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-tab-nx-light-right{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1)}.x-tab-nx-light-icon-text-left .x-tab-inner{padding-left:22px}.x-tab-nx-light-over{border-color:#ddd;background-color:#ebebeb}.x-tab-nx-light-over .x-tab-glyph{color:white}.x-ie8m .x-tab-nx-light-over .x-tab-glyph{color:#f5f5f5}.x-tab-nx-light-active{border-color:white;background-color:white}.x-tab-nx-light-active .x-tab-inner{color:#333}.x-tab-nx-light-active .x-tab-glyph{color:#444}.x-ie8m .x-tab-nx-light-active .x-tab-glyph{color:#a1a1a1}.x-tab-nx-light-top-active,.x-tab-nx-light-left-active,.x-tab-nx-light-right-active{border-bottom:1px solid white}.x-tab-nx-light-bottom-active{border-top:1px solid white}.x-tab-nx-light-disabled{cursor:default}.x-tab-nx-light-disabled .x-tab-inner{color:#333;filter:alpha(opacity=30);opacity:.3}.x-tab-nx-light-disabled .x-tab-icon-el{filter:alpha(opacity=50);opacity:.5}.x-tab-nx-light-disabled .x-tab-glyph{color:white;opacity:.3;filter:none}.x-ie8m .x-tab-nx-light-disabled .x-tab-glyph{color:#f7f7f7}.x-tab-nx-light-top-disabled,.x-tab-nx-light-left-disabled,.x-tab-nx-light-right-disabled{border-color:#f4f4f4 #f4f4f4 #444}.x-tab-nx-light-bottom-disabled{border-color:#444 #f4f4f4 #f4f4f4 #f4f4f4}.x-nbr .x-tab-nx-light{background-image:none}.x-tab-nx-light-top-over .x-frame-tl,.x-tab-nx-light-top-over .x-frame-bl,.x-tab-nx-light-top-over .x-frame-tr,.x-tab-nx-light-top-over .x-frame-br,.x-tab-nx-light-top-over .x-frame-tc,.x-tab-nx-light-top-over .x-frame-bc,.x-tab-nx-light-left-over .x-frame-tl,.x-tab-nx-light-left-over .x-frame-bl,.x-tab-nx-light-left-over .x-frame-tr,.x-tab-nx-light-left-over .x-frame-br,.x-tab-nx-light-left-over .x-frame-tc,.x-tab-nx-light-left-over .x-frame-bc,.x-tab-nx-light-right-over .x-frame-tl,.x-tab-nx-light-right-over .x-frame-bl,.x-tab-nx-light-right-over .x-frame-tr,.x-tab-nx-light-right-over .x-frame-br,.x-tab-nx-light-right-over .x-frame-tc,.x-tab-nx-light-right-over .x-frame-bc{background-image:url(images/tab/tab-nx-light-top-over-corners.gif)}.x-tab-nx-light-top-over .x-frame-ml,.x-tab-nx-light-top-over .x-frame-mr,.x-tab-nx-light-left-over .x-frame-ml,.x-tab-nx-light-left-over .x-frame-mr,.x-tab-nx-light-right-over .x-frame-ml,.x-tab-nx-light-right-over .x-frame-mr{background-image:url(images/tab/tab-nx-light-top-over-sides.gif)}.x-tab-nx-light-top-over .x-frame-mc,.x-tab-nx-light-left-over .x-frame-mc,.x-tab-nx-light-right-over .x-frame-mc{background-color:#ebebeb}.x-tab-nx-light-bottom-over .x-frame-tl,.x-tab-nx-light-bottom-over .x-frame-bl,.x-tab-nx-light-bottom-over .x-frame-tr,.x-tab-nx-light-bottom-over .x-frame-br,.x-tab-nx-light-bottom-over .x-frame-tc,.x-tab-nx-light-bottom-over .x-frame-bc{background-image:url(images/tab/tab-nx-light-bottom-over-corners.gif)}.x-tab-nx-light-bottom-over .x-frame-ml,.x-tab-nx-light-bottom-over .x-frame-mr{background-image:url(images/tab/tab-nx-light-bottom-over-sides.gif)}.x-tab-nx-light-bottom-over .x-frame-mc{background-color:#ebebeb}.x-tab-nx-light-top-active .x-frame-tl,.x-tab-nx-light-top-active .x-frame-bl,.x-tab-nx-light-top-active .x-frame-tr,.x-tab-nx-light-top-active .x-frame-br,.x-tab-nx-light-top-active .x-frame-tc,.x-tab-nx-light-top-active .x-frame-bc,.x-tab-nx-light-left-active .x-frame-tl,.x-tab-nx-light-left-active .x-frame-bl,.x-tab-nx-light-left-active .x-frame-tr,.x-tab-nx-light-left-active .x-frame-br,.x-tab-nx-light-left-active .x-frame-tc,.x-tab-nx-light-left-active .x-frame-bc,.x-tab-nx-light-right-active .x-frame-tl,.x-tab-nx-light-right-active .x-frame-bl,.x-tab-nx-light-right-active .x-frame-tr,.x-tab-nx-light-right-active .x-frame-br,.x-tab-nx-light-right-active .x-frame-tc,.x-tab-nx-light-right-active .x-frame-bc{background-image:url(images/tab/tab-nx-light-top-active-corners.gif)}.x-tab-nx-light-top-active .x-frame-ml,.x-tab-nx-light-top-active .x-frame-mr,.x-tab-nx-light-left-active .x-frame-ml,.x-tab-nx-light-left-active .x-frame-mr,.x-tab-nx-light-right-active .x-frame-ml,.x-tab-nx-light-right-active .x-frame-mr{background-image:url(images/tab/tab-nx-light-top-active-sides.gif)}.x-tab-nx-light-top-active .x-frame-mc,.x-tab-nx-light-left-active .x-frame-mc,.x-tab-nx-light-right-active .x-frame-mc{background-color:white}.x-tab-nx-light-bottom-active .x-frame-tl,.x-tab-nx-light-bottom-active .x-frame-bl,.x-tab-nx-light-bottom-active .x-frame-tr,.x-tab-nx-light-bottom-active .x-frame-br,.x-tab-nx-light-bottom-active .x-frame-tc,.x-tab-nx-light-bottom-active .x-frame-bc{background-image:url(images/tab/tab-nx-light-bottom-active-corners.gif)}.x-tab-nx-light-bottom-active .x-frame-ml,.x-tab-nx-light-bottom-active .x-frame-mr{background-image:url(images/tab/tab-nx-light-bottom-active-sides.gif)}.x-tab-nx-light-bottom-active .x-frame-mc{background-color:white}.x-tab-nx-light-top-disabled .x-frame-tl,.x-tab-nx-light-top-disabled .x-frame-bl,.x-tab-nx-light-top-disabled .x-frame-tr,.x-tab-nx-light-top-disabled .x-frame-br,.x-tab-nx-light-top-disabled .x-frame-tc,.x-tab-nx-light-top-disabled .x-frame-bc,.x-tab-nx-light-left-disabled .x-frame-tl,.x-tab-nx-light-left-disabled .x-frame-bl,.x-tab-nx-light-left-disabled .x-frame-tr,.x-tab-nx-light-left-disabled .x-frame-br,.x-tab-nx-light-left-disabled .x-frame-tc,.x-tab-nx-light-left-disabled .x-frame-bc,.x-tab-nx-light-right-disabled .x-frame-tl,.x-tab-nx-light-right-disabled .x-frame-bl,.x-tab-nx-light-right-disabled .x-frame-tr,.x-tab-nx-light-right-disabled .x-frame-br,.x-tab-nx-light-right-disabled .x-frame-tc,.x-tab-nx-light-right-disabled .x-frame-bc{background-image:url(images/tab/tab-nx-light-top-disabled-corners.gif)}.x-tab-nx-light-top-disabled .x-frame-ml,.x-tab-nx-light-top-disabled .x-frame-mr,.x-tab-nx-light-left-disabled .x-frame-ml,.x-tab-nx-light-left-disabled .x-frame-mr,.x-tab-nx-light-right-disabled .x-frame-ml,.x-tab-nx-light-right-disabled .x-frame-mr{background-image:url(images/tab/tab-nx-light-top-disabled-sides.gif)}.x-tab-nx-light-top-disabled .x-frame-mc,.x-tab-nx-light-left-disabled .x-frame-mc,.x-tab-nx-light-right-disabled .x-frame-mc{background-color:#f4f4f4}.x-tab-nx-light-bottom-disabled .x-frame-tl,.x-tab-nx-light-bottom-disabled .x-frame-bl,.x-tab-nx-light-bottom-disabled .x-frame-tr,.x-tab-nx-light-bottom-disabled .x-frame-br,.x-tab-nx-light-bottom-disabled .x-frame-tc,.x-tab-nx-light-bottom-disabled .x-frame-bc{background-image:url(images/tab/tab-nx-light-bottom-disabled-corners.gif)}.x-tab-nx-light-bottom-disabled .x-frame-ml,.x-tab-nx-light-bottom-disabled .x-frame-mr{background-image:url(images/tab/tab-nx-light-bottom-disabled-sides.gif)}.x-tab-nx-light-bottom-disabled .x-frame-mc{background-color:#f4f4f4}.x-nbr .x-tab-nx-light-top,.x-nbr .x-tab-nx-light-left,.x-nbr .x-tab-nx-light-right{border-bottom-width:1px!important}.x-nbr .x-tab-nx-light-bottom{border-top-width:1px!important}.x-tab-nx-light .x-tab-close-btn{width:12px;height:12px;background-image:url(images/tab/tab-nx-light-close.png)}.x-tab-nx-light .x-tab-close-btn-over{background-position:-12px 0}.x-tab-nx-light .x-tab-close-btn{top:2px;right:2px} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-prod_03.css b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-prod_03.css new file mode 100644 index 0000000000000000000000000000000000000000..8184626361df0b62ee6bc40cb1da6f0a0f5d5837 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/baseapp-prod_03.css @@ -0,0 +1 @@ +.x-tab-nx-light-disabled .x-tab-close-btn{filter:alpha(opacity=30);opacity:.3;background-position:0 0}.x-tab-nx-light-pressed .x-tab-close-btn{background-position:-24px 0}.x-tab-nx-light-closable .x-tab-wrap{padding-right:15px}.x-tab-nx-light-top-over:after{display:none;content:"x-slicer:corners:url(images/tab/tab-nx-light-top-over-corners.gif), sides:url(images/tab/tab-nx-light-top-over-sides.gif), stretch:bottom"}.x-tab-nx-light-bottom-over:after{display:none;content:"x-slicer:corners:url(images/tab/tab-nx-light-bottom-over-corners.gif), sides:url(images/tab/tab-nx-light-bottom-over-sides.gif), stretch:top"}.x-tab-nx-light-top-active:after{display:none;content:"x-slicer:corners:url(images/tab/tab-nx-light-top-active-corners.gif), sides:url(images/tab/tab-nx-light-top-active-sides.gif), stretch:bottom"}.x-tab-nx-light-bottom-active:after{display:none;content:"x-slicer:corners:url(images/tab/tab-nx-light-bottom-active-corners.gif), sides:url(images/tab/tab-nx-light-bottom-active-sides.gif), stretch:top"}.x-tab-nx-light-top-disabled:after{display:none;content:"x-slicer:corners:url(images/tab/tab-nx-light-top-disabled-corners.gif), sides:url(images/tab/tab-nx-light-top-disabled-sides.gif), stretch:bottom"}.x-tab-nx-light-bottom-disabled:after{display:none;content:"x-slicer:corners:url(images/tab/tab-nx-light-bottom-disabled-corners.gif), sides:url(images/tab/tab-nx-light-bottom-disabled-sides.gif), stretch:top"}.x-tab-bar-nx-light-top{padding:5px 0 2px 0}.x-tab-bar-nx-light-bottom{padding:2px 0 5px 0}.x-tab-bar-nx-light-left{padding:0 2px 0 5px}.x-tab-bar-nx-light-right{padding:0 5px 0 2px}.x-tab-bar-nx-light-horizontal{height:39px}.x-content-box .x-tab-bar-nx-light-horizontal{height:32px}.x-tab-bar-nx-light-vertical{width:39px}.x-content-box .x-tab-bar-nx-light-vertical{width:32px}.x-tab-bar-body-nx-light-top{padding-bottom:0}.x-tab-bar-body-nx-light-bottom{padding-top:0}.x-tab-bar-body-nx-light-left{padding-right:0}.x-tab-bar-body-nx-light-right{padding-left:0}.x-tab-bar-strip-nx-light{border-style:solid;border-color:#cbcbcb;background-color:#f4f4f4}.x-content-box .x-tab-bar-strip-nx-light-horizontal{height:-1px}.x-content-box .x-tab-bar-strip-nx-light-vertical{width:-1px}.x-tab-bar-strip-nx-light-top{border-width:1px;height:1px}.x-tab-bar-plain .x-tab-bar-strip-nx-light-top{border-width:1px}.x-tab-bar-strip-nx-light-bottom{border-width:1px 1px 1px 1px;height:1px}.x-tab-bar-plain .x-tab-bar-strip-nx-light-bottom{border-width:1px 1px 1px 1px}.x-tab-bar-strip-nx-light-left{border-width:1px 1px 1px 1px;width:1px}.x-tab-bar-plain .x-tab-bar-strip-nx-light-left{border-width:1px 1px 1px 1px}.x-tab-bar-strip-nx-light-right{border-width:1px 1px 1px 1px;width:1px}.x-tab-bar-plain .x-tab-bar-strip-nx-light-right{border-width:1px 1px 1px 1px}.x-tab-bar-nx-light{background-color:#f4f4f4}.x-tab-bar-nx-light .x-box-scroller{cursor:pointer;filter:alpha(opacity=50);opacity:.5;background-color:#f4f4f4}.x-tab-bar-nx-light .x-box-scroller-plain .x-box-scroller{background-color:transparent}.x-ie8m .x-tab-bar-nx-light .x-box-scroller-plain .x-box-scroller{background-color:#fff}.x-tab-bar-nx-light .x-box-scroller-hover{filter:alpha(opacity=60);opacity:.6}.x-tab-bar-nx-light .x-box-scroller-pressed{filter:alpha(opacity=70);opacity:.7}.x-tab-bar-nx-light .x-tabbar-scroll-left,.x-tab-bar-nx-light .x-tabbar-scroll-right{height:31px;width:24px}.x-tab-bar-nx-light .x-tabbar-scroll-top,.x-tab-bar-nx-light .x-tabbar-scroll-bottom{width:31px;height:24px}.x-tab-bar-nx-light-bottom .x-box-scroller{margin-top:1px}.x-tab-bar-nx-light-right .x-box-scroller{margin-left:1px}.x-tab-bar-nx-light .x-tabbar-scroll-left{background-image:url(images/tab-bar/nx-light-scroll-left.png)}.x-tab-bar-nx-light .x-tabbar-scroll-right{background-image:url(images/tab-bar/nx-light-scroll-right.png)}.x-tab-bar-nx-light .x-tabbar-scroll-top{background-image:url(images/tab-bar/nx-light-scroll-top.png)}.x-tab-bar-nx-light .x-tabbar-scroll-bottom{background-image:url(images/tab-bar/nx-light-scroll-bottom.png)}.x-tab-bar-nx-light .x-box-scroller-plain .x-tabbar-scroll-left{background-image:url(images/tab-bar/nx-light-plain-scroll-left.png)}.x-tab-bar-nx-light .x-box-scroller-plain .x-tabbar-scroll-right{background-image:url(images/tab-bar/nx-light-plain-scroll-right.png)}.x-tab-bar-nx-light .x-box-scroller-plain .x-tabbar-scroll-top{background-image:url(images/tab-bar/nx-light-plain-scroll-top.png)}.x-tab-bar-nx-light .x-box-scroller-plain .x-tabbar-scroll-bottom{background-image:url(images/tab-bar/nx-light-plain-scroll-bottom.png)}.x-tab-bar-nx-light .x-box-scroller-disabled{filter:alpha(opacity=25);opacity:.25;cursor:default}.x-tab-bar-nx-light-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-tab-bar-nx-light-bottom:after{display:none;content:"x-slicer:stretch:top"}.x-tab-bar-nx-light-left:after{display:none;content:"x-slicer:stretch:right"}.x-tab-bar-nx-light-right:after{display:none;content:"x-slicer:stretch:left"}.x-tab-bar-body-nx-light-top{padding-left:5px}.x-tab-bar-strip-nx-light-top{border-bottom:1px solid #ddd;border-top:0}.x-tab-nx-light-top,.x-tab-nx-light-top-disabled{border-bottom:1px solid #f4f4f4}.x-tab-nx-light-top-active,.x-tab-nx-light-top-active.x-tab-nx-light-top-over{border-bottom:1px solid #f4f4f4}.x-tab-nx-light-top-over{border-bottom:1px solid #ddd}.x-tab-bar+.x-panel-body{z-index:0}.nx-optionalfieldset{margin:10px 25px 0 0}.nx-optionalfieldset .x-fieldset-body{background-color:#f4f4f4;border:1px solid #cbcbcb;padding:5px 20px 10px 20px;position:relative;margin-left:25px;width:auto!important}.nx-optionalfieldset .nx-optionalfieldset .x-fieldset-body{background-color:rgba(0,0,0,0.05)}.nx-mask-without-spinner .x-mask-msg-text{background:transparent!important;padding:5px!important}.nx-aboutwindow .x-window{padding:0}.nx-aboutwindow .summary{background-color:#f4f4f4}.nx-aboutwindow .logo{margin:10px}.nx-authenticate .message{margin-bottom:10px}.nx-unlicensed{background-color:white}.nx-unlicensed .title{color:#333;font-size:20px;font-weight:bold;text-align:center;padding:20px}.nx-unlicensed .description{font-size:16px}.nx-expire-session #expire{color:#db2852;font-size:20px;margin:12px}.nx-unsupported-browser{background-color:white}.nx-unsupported-browser .title{color:#333;font-size:20px;font-weight:bold;text-align:center;padding:20px}.nx-unsupported-browser .description{font-size:16px}.nx-unsupported-browser .icons{padding:20px 0 50px 0}.nx-modebutton{padding:6px 0 0 0;color:white}.nx-modebutton .x-btn-glyph{font-size:24px}.nx-modebutton.x-icon-text-left .x-btn-inner{line-height:25px!important;font-size:15px;padding-left:36px;padding-right:10px}.nx-modebutton.x-icon-text-left .x-btn-glyph{text-align:left;padding-left:10px}.nx-modebutton+.nx-caret{border-left:5px solid transparent;border-right:5px solid transparent}.nx-modebutton:hover,.nx-modebutton:focus{background-color:#ddd}.nx-modebutton:hover .x-btn-inner,.nx-modebutton:focus .x-btn-inner{color:#006bbf}.nx-modebutton:hover .x-btn-glyph,.nx-modebutton:focus .x-btn-glyph{color:#006bbf}.nx-modebutton.x-pressed{background-color:#006bbf}.nx-modebutton.x-pressed .x-btn-inner{color:white}.nx-modebutton.x-pressed .x-btn-glyph{color:white}.nx-modebutton.x-pressed+.nx-caret{border-bottom:5px solid white}.nx-quicksearch .x-form-trigger-wrap{border-radius:11px;padding-left:3px;padding-right:0;background-color:#444;border-color:#333}.nx-quicksearch .x-form-trigger-wrap input.x-form-field,.nx-quicksearch .x-form-trigger-wrap .x-trigger-cell,.nx-quicksearch .x-form-trigger-wrap .x-form-trigger,.nx-quicksearch .x-form-trigger-wrap .x-form-trigger-input-cell{background-color:rgba(0,0,0,0)}.nx-quicksearch .x-form-trigger-wrap input.x-form-field,.nx-quicksearch .x-form-trigger-wrap .x-trigger-cell,.nx-quicksearch .x-form-trigger-wrap .x-form-trigger,.nx-quicksearch .x-form-trigger-wrap .x-form-text{color:white}.nx-header-panel .x-toolbar{background-color:black;padding:0 0 0 16px}.nx-header-panel .productname{color:white;font-family:'Proxima Nova Thin','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:20px;line-height:1.2em}.nx-header-panel .productspec{color:white;font-family:'Proxima Nova Semibold','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:11px;line-height:1.1em;letter-spacing:.05em}.x-btn-nx-header-toolbar-medium{border-color:#e1e1e1}.x-btn-nx-header-toolbar-medium{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:0;border-style:solid;background-image:none;background-color:transparent}.x-btn-nx-header-toolbar-medium-mc{background-image:url(images/btn/btn-nx-header-toolbar-medium-fbg.gif);background-position:0 top;background-color:transparent}.x-nlg .x-btn-nx-header-toolbar-medium{background-image:url(images/btn/btn-nx-header-toolbar-medium-bg.gif);background-position:0 top}.x-nbr .x-btn-nx-header-toolbar-medium{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-nx-header-toolbar-medium-frameInfo{font-family:th-3-3-3-3-0-0-0-0-3-3-3-3}.x-btn-nx-header-toolbar-medium-tl{background-position:0 -6px}.x-btn-nx-header-toolbar-medium-tr{background-position:right -9px}.x-btn-nx-header-toolbar-medium-bl{background-position:0 -12px}.x-btn-nx-header-toolbar-medium-br{background-position:right -15px}.x-btn-nx-header-toolbar-medium-ml{background-position:0 top}.x-btn-nx-header-toolbar-medium-mr{background-position:right top}.x-btn-nx-header-toolbar-medium-tc{background-position:0 0}.x-btn-nx-header-toolbar-medium-bc{background-position:0 -3px}.x-btn-nx-header-toolbar-medium-tr,.x-btn-nx-header-toolbar-medium-br,.x-btn-nx-header-toolbar-medium-mr{padding-right:3px}.x-btn-nx-header-toolbar-medium-tl,.x-btn-nx-header-toolbar-medium-bl,.x-btn-nx-header-toolbar-medium-ml{padding-left:3px}.x-btn-nx-header-toolbar-medium-tc{height:3px}.x-btn-nx-header-toolbar-medium-bc{height:3px}.x-btn-nx-header-toolbar-medium-tl,.x-btn-nx-header-toolbar-medium-bl,.x-btn-nx-header-toolbar-medium-tr,.x-btn-nx-header-toolbar-medium-br,.x-btn-nx-header-toolbar-medium-tc,.x-btn-nx-header-toolbar-medium-bc,.x-btn-nx-header-toolbar-medium-ml,.x-btn-nx-header-toolbar-medium-mr{zoom:1}.x-btn-nx-header-toolbar-medium-ml,.x-btn-nx-header-toolbar-medium-mr{zoom:1}.x-btn-nx-header-toolbar-medium-mc{padding:0}.x-strict .x-ie7 .x-btn-nx-header-toolbar-medium-tl,.x-strict .x-ie7 .x-btn-nx-header-toolbar-medium-bl{position:relative;right:0}.x-btn-nx-header-toolbar-medium:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-header-toolbar-medium-fbg.gif), bg:url(images/btn/btn-nx-header-toolbar-medium-bg.gif)"}.x-btn-nx-header-toolbar-medium .x-btn-inner{font-size:14px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:white;padding:0 8px}.x-btn-nx-header-toolbar-medium .x-btn-arrow{background-image:url(images/button/nx-header-toolbar-medium-arrow.png)}.x-btn-nx-header-toolbar-medium .x-btn-arrow-right{padding-right:30px}.x-btn-nx-header-toolbar-medium .x-btn-arrow-bottom{padding-bottom:26px}.x-btn-nx-header-toolbar-medium .x-btn-glyph{font-size:24px;line-height:24px;color:white}.x-ie8m .x-btn-nx-header-toolbar-medium .x-btn-glyph{color:white}.x-btn-nx-header-toolbar-medium-disabled{background-image:none;background-color:transparent}.x-btn-nx-header-toolbar-medium-icon .x-btn-button,.x-btn-nx-header-toolbar-medium-noicon .x-btn-button{height:24px}.x-btn-nx-header-toolbar-medium-icon .x-btn-inner,.x-btn-nx-header-toolbar-medium-noicon .x-btn-inner{line-height:24px}.x-btn-nx-header-toolbar-medium-icon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-header-toolbar-medium-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-header-toolbar-medium-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-nx-header-toolbar-medium-icon .x-btn-inner{width:24px;padding:0}.x-btn-nx-header-toolbar-medium-icon .x-btn-icon-el{width:24px;height:24px}.x-btn-nx-header-toolbar-medium-icon-text-left .x-btn-button{height:24px}.x-btn-nx-header-toolbar-medium-icon-text-left .x-btn-inner{line-height:24px;padding-left:29px}.x-btn-nx-header-toolbar-medium-icon-text-left .x-btn-icon-el{width:24px;right:auto}.x-ie6 .x-btn-nx-header-toolbar-medium-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-header-toolbar-medium-icon-text-left .x-btn-icon-el{height:24px}.x-btn-nx-header-toolbar-medium-icon-text-right .x-btn-button{height:24px}.x-btn-nx-header-toolbar-medium-icon-text-right .x-btn-inner{line-height:24px;padding-right:29px}.x-btn-nx-header-toolbar-medium-icon-text-right .x-btn-icon-el{width:24px;left:auto}.x-ie6 .x-btn-nx-header-toolbar-medium-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-header-toolbar-medium-icon-text-right .x-btn-icon-el{height:24px}.x-btn-nx-header-toolbar-medium-icon-text-top .x-btn-inner{padding-top:29px}.x-btn-nx-header-toolbar-medium-icon-text-top .x-btn-icon-el{height:24px;bottom:auto}.x-ie6 .x-btn-nx-header-toolbar-medium-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-header-toolbar-medium-icon-text-top .x-btn-icon-el{width:100%}.x-btn-nx-header-toolbar-medium-icon-text-bottom .x-btn-inner{padding-bottom:29px}.x-btn-nx-header-toolbar-medium-icon-text-bottom .x-btn-icon-el{height:24px;top:auto}.x-ie6 .x-btn-nx-header-toolbar-medium-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-header-toolbar-medium-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-nx-header-toolbar-medium-focus{border-color:#96caee;background-image:none;background-color:#1c8145}.x-btn-nx-header-toolbar-medium-over{border-color:#d8d8d8;background-image:none;background-color:#1c8145}.x-btn-nx-header-toolbar-medium-menu-active,.x-btn-nx-header-toolbar-medium-pressed{border-color:#cecece;background-image:none;background-color:#096e31}.x-btn-nx-header-toolbar-medium-focus .x-frame-tl,.x-btn-nx-header-toolbar-medium-focus .x-frame-bl,.x-btn-nx-header-toolbar-medium-focus .x-frame-tr,.x-btn-nx-header-toolbar-medium-focus .x-frame-br,.x-btn-nx-header-toolbar-medium-focus .x-frame-tc,.x-btn-nx-header-toolbar-medium-focus .x-frame-bc{background-image:url(images/btn/btn-nx-header-toolbar-medium-focus-corners.gif)}.x-btn-nx-header-toolbar-medium-focus .x-frame-ml,.x-btn-nx-header-toolbar-medium-focus .x-frame-mr{background-image:url(images/btn/btn-nx-header-toolbar-medium-focus-sides.gif)}.x-btn-nx-header-toolbar-medium-focus .x-frame-mc{background-color:#1c8145;background-image:url(images/btn/btn-nx-header-toolbar-medium-focus-fbg.gif)}.x-btn-nx-header-toolbar-medium-over .x-frame-tl,.x-btn-nx-header-toolbar-medium-over .x-frame-bl,.x-btn-nx-header-toolbar-medium-over .x-frame-tr,.x-btn-nx-header-toolbar-medium-over .x-frame-br,.x-btn-nx-header-toolbar-medium-over .x-frame-tc,.x-btn-nx-header-toolbar-medium-over .x-frame-bc{background-image:url(images/btn/btn-nx-header-toolbar-medium-over-corners.gif)}.x-btn-nx-header-toolbar-medium-over .x-frame-ml,.x-btn-nx-header-toolbar-medium-over .x-frame-mr{background-image:url(images/btn/btn-nx-header-toolbar-medium-over-sides.gif)}.x-btn-nx-header-toolbar-medium-over .x-frame-mc{background-color:#1c8145;background-image:url(images/btn/btn-nx-header-toolbar-medium-over-fbg.gif)}.x-btn-nx-header-toolbar-medium-menu-active .x-frame-tl,.x-btn-nx-header-toolbar-medium-menu-active .x-frame-bl,.x-btn-nx-header-toolbar-medium-menu-active .x-frame-tr,.x-btn-nx-header-toolbar-medium-menu-active .x-frame-br,.x-btn-nx-header-toolbar-medium-menu-active .x-frame-tc,.x-btn-nx-header-toolbar-medium-menu-active .x-frame-bc,.x-btn-nx-header-toolbar-medium-pressed .x-frame-tl,.x-btn-nx-header-toolbar-medium-pressed .x-frame-bl,.x-btn-nx-header-toolbar-medium-pressed .x-frame-tr,.x-btn-nx-header-toolbar-medium-pressed .x-frame-br,.x-btn-nx-header-toolbar-medium-pressed .x-frame-tc,.x-btn-nx-header-toolbar-medium-pressed .x-frame-bc{background-image:url(images/btn/btn-nx-header-toolbar-medium-pressed-corners.gif)}.x-btn-nx-header-toolbar-medium-menu-active .x-frame-ml,.x-btn-nx-header-toolbar-medium-menu-active .x-frame-mr,.x-btn-nx-header-toolbar-medium-pressed .x-frame-ml,.x-btn-nx-header-toolbar-medium-pressed .x-frame-mr{background-image:url(images/btn/btn-nx-header-toolbar-medium-pressed-sides.gif)}.x-btn-nx-header-toolbar-medium-menu-active .x-frame-mc,.x-btn-nx-header-toolbar-medium-pressed .x-frame-mc{background-color:#096e31;background-image:url(images/btn/btn-nx-header-toolbar-medium-pressed-fbg.gif)}.x-btn-nx-header-toolbar-medium-disabled .x-frame-tl,.x-btn-nx-header-toolbar-medium-disabled .x-frame-bl,.x-btn-nx-header-toolbar-medium-disabled .x-frame-tr,.x-btn-nx-header-toolbar-medium-disabled .x-frame-br,.x-btn-nx-header-toolbar-medium-disabled .x-frame-tc,.x-btn-nx-header-toolbar-medium-disabled .x-frame-bc{background-image:url(images/btn/btn-nx-header-toolbar-medium-disabled-corners.gif)}.x-btn-nx-header-toolbar-medium-disabled .x-frame-ml,.x-btn-nx-header-toolbar-medium-disabled .x-frame-mr{background-image:url(images/btn/btn-nx-header-toolbar-medium-disabled-sides.gif)}.x-btn-nx-header-toolbar-medium-disabled .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-nx-header-toolbar-medium-disabled-fbg.gif)}.x-nlg .x-btn-nx-header-toolbar-medium-focus{background-image:url(images/btn/btn-nx-header-toolbar-medium-focus-bg.gif)}.x-nlg .x-btn-nx-header-toolbar-medium-over{background-image:url(images/btn/btn-nx-header-toolbar-medium-over-bg.gif)}.x-nlg .x-btn-nx-header-toolbar-medium-menu-active,.x-nlg .x-btn-nx-header-toolbar-medium-pressed{background-image:url(images/btn/btn-nx-header-toolbar-medium-pressed-bg.gif)}.x-nlg .x-btn-nx-header-toolbar-medium-disabled{background-image:url(images/btn/btn-nx-header-toolbar-medium-disabled-bg.gif)}.x-nbr .x-btn-nx-header-toolbar-medium{background-image:none}.x-btn-nx-header-toolbar-medium .x-btn-split-right{background-image:url(images/button/nx-header-toolbar-medium-s-arrow.png);padding-right:32px}.x-btn-nx-header-toolbar-medium .x-btn-split-bottom{background-image:url(images/button/nx-header-toolbar-medium-s-arrow-b.png);padding-bottom:28px}.x-btn-nx-header-toolbar-medium-disabled{filter:alpha(opacity=50);opacity:.5}.x-btn-nx-header-toolbar-medium-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-header-toolbar-medium-focus-corners.gif), sides:url(images/btn/btn-nx-header-toolbar-medium-focus-sides.gif), frame-bg:url(images/btn/btn-nx-header-toolbar-medium-focus-fbg.gif), bg:url(images/btn/btn-nx-header-toolbar-medium-focus-bg.gif)"}.x-btn-nx-header-toolbar-medium-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-header-toolbar-medium-over-corners.gif), sides:url(images/btn/btn-nx-header-toolbar-medium-over-sides.gif), frame-bg:url(images/btn/btn-nx-header-toolbar-medium-over-fbg.gif), bg:url(images/btn/btn-nx-header-toolbar-medium-over-bg.gif)"}.x-btn-nx-header-toolbar-medium-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-header-toolbar-medium-pressed-corners.gif), sides:url(images/btn/btn-nx-header-toolbar-medium-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-header-toolbar-medium-pressed-fbg.gif), bg:url(images/btn/btn-nx-header-toolbar-medium-pressed-bg.gif)"}.x-btn-nx-header-toolbar-medium-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-header-toolbar-medium-disabled-corners.gif), sides:url(images/btn/btn-nx-header-toolbar-medium-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-header-toolbar-medium-disabled-fbg.gif), bg:url(images/btn/btn-nx-header-toolbar-medium-disabled-bg.gif)"}.x-btn-nx-header-toolbar-medium{height:39px;border-radius:0;padding:3px 6px}.x-btn-nx-header-toolbar-medium .x-btn-wrap{margin-top:4px}.nx-footer{background-color:#444}.nx-footer .copyright{color:#cbcbcb;font-size:8px;text-align:right;padding:1px 2px 0 0}.x-panel-nx-feature-menu{border-color:#444;padding:0}.x-panel-header-nx-feature-menu{font-size:13px;border:1px solid #444}.x-panel-header-nx-feature-menu .x-tool-img{background-color:#444}.x-panel-header-nx-feature-menu-horizontal{padding:4px}.x-panel-header-nx-feature-menu-horizontal-noborder{padding:5px 5px 4px 5px}.x-panel-header-nx-feature-menu-vertical{padding:4px 4px 4px 4px}.x-panel-header-nx-feature-menu-vertical-noborder{padding:5px 5px 5px 4px}.x-panel-header-text-container-nx-feature-menu{color:white;font-size:13px;font-weight:bold;font-family:arial,helvetica,verdana,sans-serif;line-height:15px;padding:1px 0 0;text-transform:none}.x-panel-body-nx-feature-menu{background:#ddd;border-color:#444;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-header-nx-feature-menu{background-image:none;background-color:#444}.x-panel-header-nx-feature-menu-vertical{background-image:none;background-color:#444}.x-panel .x-panel-header-nx-feature-menu-collapsed-border-top{border-bottom-width:1px!important}.x-panel .x-panel-header-nx-feature-menu-collapsed-border-right{border-left-width:1px!important}.x-panel .x-panel-header-nx-feature-menu-collapsed-border-bottom{border-top-width:1px!important}.x-panel .x-panel-header-nx-feature-menu-collapsed-border-left{border-right-width:1px!important}.x-panel-header-nx-feature-menu-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-feature-menu-bottom:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-feature-menu-left:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-feature-menu-right:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-feature-menu-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-nx-feature-menu-vertical .x-panel-header-text-container{background-color:#444;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#444444)}.x-panel-header-nx-feature-menu .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-nx-feature-menu .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-nx-feature-menu .x-panel-header-glyph{color:#a1a1a1}.x-panel-header-nx-feature-menu-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-nx-feature-menu-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-nx-feature-menu-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-nx-feature-menu-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-nx-feature-menu-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-nx-feature-menu-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-nx-feature-menu-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-nx-feature-menu-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-nx-feature-menu-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-nx-feature-menu-outer-border-l{border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-feature-menu-outer-border-b{border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-feature-menu-outer-border-bl{border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-feature-menu-outer-border-r{border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-feature-menu-outer-border-rl{border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-feature-menu-outer-border-rb{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-feature-menu-outer-border-rbl{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-feature-menu-outer-border-t{border-top-color:#444!important;border-top-width:1px!important}.x-panel-nx-feature-menu-outer-border-tl{border-top-color:#444!important;border-top-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-feature-menu-outer-border-tb{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-feature-menu-outer-border-tbl{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-feature-menu-outer-border-tr{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-feature-menu-outer-border-trl{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-feature-menu-outer-border-trb{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-feature-menu-outer-border-trbl{border-color:#444!important;border-width:1px!important}.x-panel-nx-feature-menu{border-width:0 2px 0 0;border-color:#cbcbcb;border-style:solid}.x-panel-nx-feature-menu .x-grid-header-ct{border:0}.x-panel-nx-feature-menu .x-panel-body-nx-feature-menu .x-grid-cell{background-color:#ddd}.x-panel-nx-feature-menu .x-panel-body-nx-feature-menu .x-grid-row-over .x-grid-td{background-color:#ebebeb}.x-panel-nx-feature-menu .x-panel-body-nx-feature-menu .x-grid-row-selected .x-grid-td{color:white;background-color:#2476c3}.x-panel-nx-feature-content{border-color:#444;padding:0}.x-panel-header-nx-feature-content{font-size:13px;border:1px solid #444}.x-panel-header-nx-feature-content .x-tool-img{background-image:url(images/tools/tool-sprites-dark.png);background-color:white}.x-panel-header-nx-feature-content-horizontal{padding:10px 8px 10px 10px}.x-panel-header-nx-feature-content-horizontal-noborder{padding:11px 9px 10px 11px}.x-panel-header-nx-feature-content-vertical{padding:10px 10px 8px 10px}.x-panel-header-nx-feature-content-vertical-noborder{padding:11px 11px 9px 10px}.x-panel-header-text-container-nx-feature-content{color:white;font-size:13px;font-weight:bold;font-family:arial,helvetica,verdana,sans-serif;line-height:15px;padding:1px 0 0;text-transform:none}.x-panel-body-nx-feature-content{background:#f4f4f4;border-color:#444;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-header-nx-feature-content{background-image:none;background-color:white}.x-panel-header-nx-feature-content-vertical{background-image:none;background-color:white}.x-panel .x-panel-header-nx-feature-content-collapsed-border-top{border-bottom-width:1px!important}.x-panel .x-panel-header-nx-feature-content-collapsed-border-right{border-left-width:1px!important}.x-panel .x-panel-header-nx-feature-content-collapsed-border-bottom{border-top-width:1px!important}.x-panel .x-panel-header-nx-feature-content-collapsed-border-left{border-right-width:1px!important}.x-panel-header-nx-feature-content-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-feature-content-bottom:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-feature-content-left:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-feature-content-right:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-feature-content-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-nx-feature-content-vertical .x-panel-header-text-container{background-color:white;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=white)}.x-panel-header-nx-feature-content .x-panel-header-icon{width:32px;height:32px;background-position:center center}.x-panel-header-nx-feature-content .x-panel-header-glyph{color:white;font-size:32px;line-height:32px;opacity:.5}.x-ie8m .x-panel-header-nx-feature-content .x-panel-header-glyph{color:white}.x-panel-header-nx-feature-content-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-nx-feature-content-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-nx-feature-content-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-nx-feature-content-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-nx-feature-content-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-nx-feature-content-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-nx-feature-content-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-nx-feature-content-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-nx-feature-content-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-nx-feature-content-outer-border-l{border-left-color:#444!important;border-left-width:0!important}.x-panel-nx-feature-content-outer-border-b{border-bottom-color:#444!important;border-bottom-width:0!important}.x-panel-nx-feature-content-outer-border-bl{border-bottom-color:#444!important;border-bottom-width:0!important;border-left-color:#444!important;border-left-width:0!important}.x-panel-nx-feature-content-outer-border-r{border-right-color:#444!important;border-right-width:0!important}.x-panel-nx-feature-content-outer-border-rl{border-right-color:#444!important;border-right-width:0!important;border-left-color:#444!important;border-left-width:0!important}.x-panel-nx-feature-content-outer-border-rb{border-right-color:#444!important;border-right-width:0!important;border-bottom-color:#444!important;border-bottom-width:0!important}.x-panel-nx-feature-content-outer-border-rbl{border-right-color:#444!important;border-right-width:0!important;border-bottom-color:#444!important;border-bottom-width:0!important;border-left-color:#444!important;border-left-width:0!important}.x-panel-nx-feature-content-outer-border-t{border-top-color:#444!important;border-top-width:0!important}.x-panel-nx-feature-content-outer-border-tl{border-top-color:#444!important;border-top-width:0!important;border-left-color:#444!important;border-left-width:0!important}.x-panel-nx-feature-content-outer-border-tb{border-top-color:#444!important;border-top-width:0!important;border-bottom-color:#444!important;border-bottom-width:0!important}.x-panel-nx-feature-content-outer-border-tbl{border-top-color:#444!important;border-top-width:0!important;border-bottom-color:#444!important;border-bottom-width:0!important;border-left-color:#444!important;border-left-width:0!important}.x-panel-nx-feature-content-outer-border-tr{border-top-color:#444!important;border-top-width:0!important;border-right-color:#444!important;border-right-width:0!important}.x-panel-nx-feature-content-outer-border-trl{border-top-color:#444!important;border-top-width:0!important;border-right-color:#444!important;border-right-width:0!important;border-left-color:#444!important;border-left-width:0!important}.x-panel-nx-feature-content-outer-border-trb{border-top-color:#444!important;border-top-width:0!important;border-right-color:#444!important;border-right-width:0!important;border-bottom-color:#444!important;border-bottom-width:0!important}.x-panel-nx-feature-content-outer-border-trbl{border-color:#444!important;border-width:0!important}.nx-feature-content .nx-feature-group{border-top:1px #ddd solid!important;margin:0!important}.nx-feature-content .x-panel-body .x-toolbar{padding:12px 8px 0 0}.nx-feature-content .x-panel-body .x-toolbar.x-toolbar-footer{background-color:white}.nx-feature-content .x-panel-body .x-panel-nx-subsection-framed>div+.x-toolbar,.nx-feature-content .x-panel-body .nx-actions.x-toolbar{padding:0 8px 0 0}.nx-feature-content .x-panel-body .x-panel-nx-subsection-framed>div+.x-toolbar .x-toolbar-item,.nx-feature-content .x-panel-body .nx-actions.x-toolbar .x-toolbar-item{margin:6px 0 6px 8px}.nx-feature-name{font-size:26px;font-weight:bold}.nx-feature-description{font-size:13px;font-weight:normal;top:12px!important;padding-left:7px}.nx-feature-group{margin-top:16px;margin-left:16px}.nx-feature-group .item-wrap{border:1px solid rgba(0,0,0,0);float:left;width:200px;height:42px;margin:5px;padding:5px;cursor:pointer}.nx-feature-group .x-item-over{border:1px solid #cbcbcb;background:#ebebeb;border-radius:5px;-moz-border-radius:5px}.nx-feature-group .x-item-selected{border:1px solid gray;background:#cbcbcb;border-radius:5px;-moz-border-radius:5px}.nx-feature-group img{height:32px;width:32px;margin-right:5px}.nx-feature-notfound{background-color:white}.nx-feature-notfound .title{color:#333;font-size:20px;font-weight:bold;text-align:center;padding:20px}.nx-feature-notfound .description{font-size:16px}.nx-feature-notvisible{background-color:#f4f4f4}.nx-feature-notvisible .title{color:#333;font-size:20px;font-weight:bold;text-align:center;padding:20px}.nx-feature-notvisible .description{font-size:16px}.x-panel-nx-developer{border-color:#444;padding:0}.x-panel-header-nx-developer{font-size:13px;border:1px solid #444}.x-panel-header-nx-developer .x-tool-img{background-color:#014e8a}.x-panel-header-nx-developer-horizontal{padding:4px}.x-panel-header-nx-developer-horizontal-noborder{padding:5px 5px 4px 5px}.x-panel-header-nx-developer-vertical{padding:4px 4px 4px 4px}.x-panel-header-nx-developer-vertical-noborder{padding:5px 5px 5px 4px}.x-panel-header-text-container-nx-developer{color:white;font-size:13px;font-weight:bold;font-family:arial,helvetica,verdana,sans-serif;line-height:15px;padding:1px 0 0;text-transform:none}.x-panel-body-nx-developer{background:white;border-color:#444;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-header-nx-developer{background-image:none;background-color:#014e8a}.x-panel-header-nx-developer-vertical{background-image:none;background-color:#014e8a}.x-panel .x-panel-header-nx-developer-collapsed-border-top{border-bottom-width:1px!important}.x-panel .x-panel-header-nx-developer-collapsed-border-right{border-left-width:1px!important}.x-panel .x-panel-header-nx-developer-collapsed-border-bottom{border-top-width:1px!important}.x-panel .x-panel-header-nx-developer-collapsed-border-left{border-right-width:1px!important}.x-panel-header-nx-developer-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-developer-bottom:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-developer-left:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-developer-right:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-developer-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-nx-developer-vertical .x-panel-header-text-container{background-color:#014e8a;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#014e8a)}.x-panel-header-nx-developer .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-nx-developer .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-nx-developer .x-panel-header-glyph{color:#80a6c4}.x-panel-header-nx-developer-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-nx-developer-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-nx-developer-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-nx-developer-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-nx-developer-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-nx-developer-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-nx-developer-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-nx-developer-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-nx-developer-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-nx-developer-outer-border-l{border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-developer-outer-border-b{border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-developer-outer-border-bl{border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-developer-outer-border-r{border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-developer-outer-border-rl{border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-developer-outer-border-rb{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-developer-outer-border-rbl{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-developer-outer-border-t{border-top-color:#444!important;border-top-width:1px!important}.x-panel-nx-developer-outer-border-tl{border-top-color:#444!important;border-top-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-developer-outer-border-tb{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-developer-outer-border-tbl{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-developer-outer-border-tr{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-developer-outer-border-trl{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-developer-outer-border-trb{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-developer-outer-border-trbl{border-color:#444!important;border-width:1px!important}.x-panel-nx-developer .x-panel-body .x-tabpanel-child .x-box-inner{overflow-y:auto}.x-panel-nx-developer .nx-hbox{float:left;margin-right:20px}.x-panel-nx-developer .nx-vbox{margin-bottom:20px}.x-panel-nx-developer .nx-section-header{font-size:13px;padding-bottom:5px;text-transform:uppercase}.x-panel-nx-developer table thead th{font-size:13px;text-transform:uppercase;text-align:left}.x-panel-nx-developer table tbody tr .nx-color{height:10px;width:10px}.x-panel-nx-developer .nx-proxima-nova-regular{font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif}.x-panel-nx-developer .nx-proxima-nova-bold{font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:bold}.x-panel-nx-developer .nx-courier-new-regular{font-family:"Courier New",Courier,monospace}.x-panel-nx-developer .nx-sample-h1{font-size:20px;font-weight:100}.x-panel-nx-developer .nx-sample-h2{font-size:26px;font-weight:bold}.x-panel-nx-developer .nx-sample-h3{font-size:22px;font-weight:bold}.x-panel-nx-developer .nx-sample-h4{font-size:18px;font-weight:bold}.x-panel-nx-developer .nx-sample-h5{font-size:13px;font-weight:bold}.x-panel-nx-developer .nx-sample-body{font-size:13px;font-weight:normal}.x-panel-nx-developer .nx-sample-code{font-size:13px;font-weight:normal;font-family:"Courier New",Courier,monospace}.x-panel-nx-developer .nx-sample-utility{font-size:10px;font-weight:normal}.x-panel-nx-developer .nx-color{border:1px #ddd solid;margin:0 3px 3px 0!important;height:40px;width:80px}.x-panel-nx-developer .nx-color.black{background-color:black}.x-panel-nx-developer .nx-color.night-rider{background-color:#333}.x-panel-nx-developer .nx-color.charcoal{background-color:#444}.x-panel-nx-developer .nx-color.dark-gray{background-color:#777}.x-panel-nx-developer .nx-color.gray{background-color:gray}.x-panel-nx-developer .nx-color.light-gray{background-color:#cbcbcb}.x-panel-nx-developer .nx-color.gainsboro{background-color:#ddd}.x-panel-nx-developer .nx-color.smoke{background-color:#ebebeb}.x-panel-nx-developer .nx-color.light-smoke{background-color:#f4f4f4}.x-panel-nx-developer .nx-color.cerise{background-color:#db2852}.x-panel-nx-developer .nx-color.sun{background-color:#f2862f}.x-panel-nx-developer .nx-color.energy-yellow{background-color:#f5c649}.x-panel-nx-developer .nx-color.cobalt{background-color:#0047b2}.x-panel-nx-developer .nx-color.cerulean-blue{background-color:#2476c3}.x-panel-nx-developer .nx-color.citrus{background-color:#84c900}.x-panel-nx-developer .nx-color.free-speech-red{background-color:#c70000}.x-panel-nx-developer .nx-color.energy-yellow{background-color:#f5c649}.x-panel-nx-developer .nx-color.floral-white{background-color:#fffaee}.x-panel-nx-developer .nx-color.pigment-green{background-color:#0b9743}.x-panel-nx-developer .nx-color.madang{background-color:#b6e9ab}.x-panel-nx-developer .nx-color.venetian-red{background-color:#bc0430}.x-panel-nx-developer .nx-color.beauty-bush{background-color:#edb2af}.x-panel-nx-developer .nx-color.navy-blue{background-color:#006bbf}.x-panel-nx-developer .nx-color.cornflower{background-color:#96caee}.x-panel-nx-developer .nx-color.affair{background-color:#875393}.x-panel-nx-developer .nx-color.east-side{background-color:#b087b9}.x-panel-nx-developer .nx-color.blue-chalk{background-color:#dac5df}.x-panel-nx-developer .nx-color.white{background-color:white}.x-panel-nx-developer .nx-color.light-gainsboro{background-color:#e6e6e6}.x-panel-nx-developer .nx-color.light-gray{background-color:#cbcbcb}.x-panel-nx-developer .nx-color.silver{background-color:#b8b8b8}.x-panel-nx-developer .nx-color.suva-gray{background-color:#919191}.x-panel-nx-developer .nx-color.gray{background-color:gray}.x-panel-nx-developer .nx-color.denim{background-color:#197ac5}.x-panel-nx-developer .nx-color.light-cobalt{background-color:#0161ad}.x-panel-nx-developer .nx-color.dark-denim{background-color:#014e8a}.x-panel-nx-developer .nx-color.smalt{background-color:#0f4976}.x-panel-nx-developer .nx-color.dark-cerulean{background-color:#0f4976}.x-panel-nx-developer .nx-color.prussian-blue{background-color:#013a68}.x-panel-nx-developer .nx-color.light-cerise{background-color:#de3d63}.x-panel-nx-developer .nx-color.brick-red{background-color:#c6254b}.x-panel-nx-developer .nx-color.old-rose{background-color:#b2314f}.x-panel-nx-developer .nx-color.fire-brick{background-color:#9e1e3c}.x-panel-nx-developer .nx-color.shiraz{background-color:#85253b}.x-panel-nx-developer .nx-color.falu-red{background-color:#77162d}.x-panel-nx-developer .nx-color.sea-buckthorn{background-color:#f39244}.x-panel-nx-developer .nx-color.tahiti-gold{background-color:#da792b}.x-panel-nx-developer .nx-color.zest{background-color:#c17536}.x-panel-nx-developer .nx-color.rich-gold{background-color:#ae6122}.x-panel-nx-developer .nx-color.afghan-tan{background-color:#925829}.x-panel-nx-developer .nx-color.russet{background-color:#83491a}.x-panel-nx-developer .nx-color.elf-green{background-color:#23a156}.x-panel-nx-developer .nx-color.dark-pigment-green{background-color:#0b893d}.x-panel-nx-developer .nx-color.salem{background-color:#1c8145}.x-panel-nx-developer .nx-color.jewel{background-color:#096e31}.x-panel-nx-developer .nx-color.fun-green{background-color:#156134}.x-panel-nx-developer .nx-color.dark-jewel{background-color:#0c4f26}.nx-info table{border-spacing:5px}.nx-info{overflow:auto}.nx-info-entry td{padding-bottom:4px;vertical-align:top}.nx-info-entry-name{padding-left:0;padding-right:6px;font-weight:bold;white-space:nowrap}.nx-info-entry-value{padding-left:0;padding-right:25px;word-break:break-all}.nx-info-entry-value pre{margin:0}.nx-actions{background-color:white;border-style:none}.x-panel-nx-drilldown-message{border-color:#444;padding:0}.x-panel-header-nx-drilldown-message{font-size:13px;border:1px solid #444}.x-panel-header-nx-drilldown-message .x-tool-img{background-color:#444}.x-panel-header-nx-drilldown-message-horizontal{padding:9px 9px 10px 9px}.x-panel-header-nx-drilldown-message-horizontal-noborder{padding:10px 10px 10px 10px}.x-panel-header-nx-drilldown-message-vertical{padding:9px 9px 9px 10px}.x-panel-header-nx-drilldown-message-vertical-noborder{padding:10px 10px 10px 10px}.x-panel-header-text-container-nx-drilldown-message{color:#333;font-size:13px;font-weight:bold;font-family:arial,helvetica,verdana,sans-serif;line-height:15px;padding:1px 0 0;text-transform:none}.x-panel-body-nx-drilldown-message{background:transparent;border-color:#444;color:black;font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;border-width:1px;border-style:solid}.x-panel-header-nx-drilldown-message{background-image:none;background-color:#444}.x-panel-header-nx-drilldown-message-vertical{background-image:none;background-color:#444}.x-panel .x-panel-header-nx-drilldown-message-collapsed-border-top{border-bottom-width:1px!important}.x-panel .x-panel-header-nx-drilldown-message-collapsed-border-right{border-left-width:1px!important}.x-panel .x-panel-header-nx-drilldown-message-collapsed-border-bottom{border-top-width:1px!important}.x-panel .x-panel-header-nx-drilldown-message-collapsed-border-left{border-right-width:1px!important}.x-panel-header-nx-drilldown-message-top:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-drilldown-message-bottom:after{display:none;content:"x-slicer:stretch:bottom"}.x-panel-header-nx-drilldown-message-left:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-drilldown-message-right:after{display:none;content:"x-slicer:stretch:left"}.x-panel-header-nx-drilldown-message-vertical .x-panel-header-text-container{-webkit-transform:rotate(90deg);-webkit-transform-origin:0 0;-moz-transform:rotate(90deg);-moz-transform-origin:0 0;-o-transform:rotate(90deg);-o-transform-origin:0 0;transform:rotate(90deg);transform-origin:0 0}.x-ie9m .x-panel-header-nx-drilldown-message-vertical .x-panel-header-text-container{background-color:#444;filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1),progid:DXImageTransform.Microsoft.Chroma(color=#444444)}.x-panel-header-nx-drilldown-message .x-panel-header-icon{width:16px;height:16px;background-position:center center}.x-panel-header-nx-drilldown-message .x-panel-header-glyph{color:white;font-size:16px;line-height:16px;opacity:.5}.x-ie8m .x-panel-header-nx-drilldown-message .x-panel-header-glyph{color:#a1a1a1}.x-panel-header-nx-drilldown-message-horizontal .x-panel-header-icon-before-title{margin:0 6px 0 0}.x-panel-header-nx-drilldown-message-horizontal .x-panel-header-icon-after-title{margin:0 0 0 6px}.x-panel-header-nx-drilldown-message-vertical .x-panel-header-icon-before-title{margin:0 0 6px 0}.x-panel-header-nx-drilldown-message-vertical .x-panel-header-icon-after-title{margin:6px 0 0 0}.x-panel-header-nx-drilldown-message-horizontal .x-tool-after-title{margin:0 0 0 6px}.x-panel-header-nx-drilldown-message-horizontal .x-tool-before-title{margin:0 6px 0 0}.x-panel-header-nx-drilldown-message-vertical .x-tool-after-title{margin:6px 0 0 0}.x-panel-header-nx-drilldown-message-vertical .x-tool-before-title{margin:0 0 6px 0}.x-panel-nx-drilldown-message-resizable .x-panel-handle{filter:alpha(opacity=0);opacity:0}.x-panel-nx-drilldown-message-outer-border-l{border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-drilldown-message-outer-border-b{border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-drilldown-message-outer-border-bl{border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-drilldown-message-outer-border-r{border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-drilldown-message-outer-border-rl{border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-drilldown-message-outer-border-rb{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-drilldown-message-outer-border-rbl{border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-drilldown-message-outer-border-t{border-top-color:#444!important;border-top-width:1px!important}.x-panel-nx-drilldown-message-outer-border-tl{border-top-color:#444!important;border-top-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-drilldown-message-outer-border-tb{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-drilldown-message-outer-border-tbl{border-top-color:#444!important;border-top-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-drilldown-message-outer-border-tr{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important}.x-panel-nx-drilldown-message-outer-border-trl{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-left-color:#444!important;border-left-width:1px!important}.x-panel-nx-drilldown-message-outer-border-trb{border-top-color:#444!important;border-top-width:1px!important;border-right-color:#444!important;border-right-width:1px!important;border-bottom-color:#444!important;border-bottom-width:1px!important}.x-panel-nx-drilldown-message-outer-border-trbl{border-color:#444!important;border-width:1px!important}.x-panel-nx-drilldown-message{padding-left:8px!important;padding-right:8px!important;background-color:white}.x-panel-nx-drilldown-message .x-panel-header{padding:8px;border:1px solid!important;height:32px;border-radius:8px}.x-panel-nx-drilldown-message .x-panel-header img{margin-right:5px!important}.x-panel-nx-drilldown-message.nx-drilldown-warning .x-panel-header{border-color:#f2862f!important;background-color:#f4f4f4}.x-panel-nx-drilldown-message.nx-drilldown-info .x-panel-header{border-color:#2476c3!important;background-color:#f4f4f4}.x-docked-top.nx-drilldown-info{padding:10px 0 0 0}.x-btn-nx-drilldown-large{border-color:transparent}.x-btn-nx-drilldown-large{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:0;border-style:solid;background-image:none;background-color:transparent}.x-btn-nx-drilldown-large-mc{background-image:url(images/btn/btn-nx-drilldown-large-fbg.gif);background-position:0 top;background-color:transparent}.x-nlg .x-btn-nx-drilldown-large{background-image:url(images/btn/btn-nx-drilldown-large-bg.gif);background-position:0 top}.x-nbr .x-btn-nx-drilldown-large{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-nx-drilldown-large-frameInfo{font-family:th-3-3-3-3-0-0-0-0-3-3-3-3}.x-btn-nx-drilldown-large-tl{background-position:0 -6px}.x-btn-nx-drilldown-large-tr{background-position:right -9px}.x-btn-nx-drilldown-large-bl{background-position:0 -12px}.x-btn-nx-drilldown-large-br{background-position:right -15px}.x-btn-nx-drilldown-large-ml{background-position:0 top}.x-btn-nx-drilldown-large-mr{background-position:right top}.x-btn-nx-drilldown-large-tc{background-position:0 0}.x-btn-nx-drilldown-large-bc{background-position:0 -3px}.x-btn-nx-drilldown-large-tr,.x-btn-nx-drilldown-large-br,.x-btn-nx-drilldown-large-mr{padding-right:3px}.x-btn-nx-drilldown-large-tl,.x-btn-nx-drilldown-large-bl,.x-btn-nx-drilldown-large-ml{padding-left:3px}.x-btn-nx-drilldown-large-tc{height:3px}.x-btn-nx-drilldown-large-bc{height:3px}.x-btn-nx-drilldown-large-tl,.x-btn-nx-drilldown-large-bl,.x-btn-nx-drilldown-large-tr,.x-btn-nx-drilldown-large-br,.x-btn-nx-drilldown-large-tc,.x-btn-nx-drilldown-large-bc,.x-btn-nx-drilldown-large-ml,.x-btn-nx-drilldown-large-mr{zoom:1}.x-btn-nx-drilldown-large-ml,.x-btn-nx-drilldown-large-mr{zoom:1}.x-btn-nx-drilldown-large-mc{padding:0}.x-strict .x-ie7 .x-btn-nx-drilldown-large-tl,.x-strict .x-ie7 .x-btn-nx-drilldown-large-bl{position:relative;right:0}.x-btn-nx-drilldown-large:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-drilldown-large-fbg.gif), bg:url(images/btn/btn-nx-drilldown-large-bg.gif)"}.x-btn-nx-drilldown-large .x-btn-inner{font-size:26px;font-weight:bold;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#006bbf;padding:0}.x-btn-nx-drilldown-large .x-btn-arrow{background-image:url(images/button/nx-drilldown-large-arrow.png)}.x-btn-nx-drilldown-large .x-btn-arrow-right{padding-right:36px}.x-btn-nx-drilldown-large .x-btn-arrow-bottom{padding-bottom:32px}.x-btn-nx-drilldown-large .x-btn-glyph{font-size:32px;line-height:32px;color:white}.x-ie8m .x-btn-nx-drilldown-large .x-btn-glyph{color:white}.x-btn-nx-drilldown-large-disabled{background-image:none;background-color:transparent}.x-btn-nx-drilldown-large-disabled .x-btn-inner{color:#333}.x-btn-nx-drilldown-large-icon .x-btn-button,.x-btn-nx-drilldown-large-noicon .x-btn-button{height:32px}.x-btn-nx-drilldown-large-icon .x-btn-inner,.x-btn-nx-drilldown-large-noicon .x-btn-inner{line-height:32px}.x-btn-nx-drilldown-large-icon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-drilldown-large-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-drilldown-large-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-nx-drilldown-large-icon .x-btn-inner{width:32px;padding:0}.x-btn-nx-drilldown-large-icon .x-btn-icon-el{width:32px;height:32px}.x-btn-nx-drilldown-large-icon-text-left .x-btn-button{height:32px}.x-btn-nx-drilldown-large-icon-text-left .x-btn-inner{line-height:32px;padding-left:37px}.x-btn-nx-drilldown-large-icon-text-left .x-btn-icon-el{width:32px;right:auto}.x-ie6 .x-btn-nx-drilldown-large-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-drilldown-large-icon-text-left .x-btn-icon-el{height:32px}.x-btn-nx-drilldown-large-icon-text-right .x-btn-button{height:32px}.x-btn-nx-drilldown-large-icon-text-right .x-btn-inner{line-height:32px;padding-right:37px}.x-btn-nx-drilldown-large-icon-text-right .x-btn-icon-el{width:32px;left:auto}.x-ie6 .x-btn-nx-drilldown-large-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-drilldown-large-icon-text-right .x-btn-icon-el{height:32px}.x-btn-nx-drilldown-large-icon-text-top .x-btn-inner{padding-top:37px}.x-btn-nx-drilldown-large-icon-text-top .x-btn-icon-el{height:32px;bottom:auto}.x-ie6 .x-btn-nx-drilldown-large-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-drilldown-large-icon-text-top .x-btn-icon-el{width:100%}.x-btn-nx-drilldown-large-icon-text-bottom .x-btn-inner{padding-bottom:37px}.x-btn-nx-drilldown-large-icon-text-bottom .x-btn-icon-el{height:32px;top:auto}.x-ie6 .x-btn-nx-drilldown-large-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-drilldown-large-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-nx-drilldown-large-focus{background-image:none;background-color:transparent}.x-btn-nx-drilldown-large-focus .x-btn-inner{color:#0f4976}.x-btn-nx-drilldown-large-over{background-image:none;background-color:transparent}.x-btn-nx-drilldown-large-over .x-btn-inner{color:#0f4976}.x-btn-nx-drilldown-large-menu-active,.x-btn-nx-drilldown-large-pressed{background-image:none;background-color:transparent}.x-btn-nx-drilldown-large-menu-active .x-btn-inner,.x-btn-nx-drilldown-large-pressed .x-btn-inner{color:#96caee}.x-btn-nx-drilldown-large-focus .x-frame-tl,.x-btn-nx-drilldown-large-focus .x-frame-bl,.x-btn-nx-drilldown-large-focus .x-frame-tr,.x-btn-nx-drilldown-large-focus .x-frame-br,.x-btn-nx-drilldown-large-focus .x-frame-tc,.x-btn-nx-drilldown-large-focus .x-frame-bc{background-image:url(images/btn/btn-nx-drilldown-large-focus-corners.gif)}.x-btn-nx-drilldown-large-focus .x-frame-ml,.x-btn-nx-drilldown-large-focus .x-frame-mr{background-image:url(images/btn/btn-nx-drilldown-large-focus-sides.gif)}.x-btn-nx-drilldown-large-focus .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-nx-drilldown-large-focus-fbg.gif)}.x-btn-nx-drilldown-large-over .x-frame-tl,.x-btn-nx-drilldown-large-over .x-frame-bl,.x-btn-nx-drilldown-large-over .x-frame-tr,.x-btn-nx-drilldown-large-over .x-frame-br,.x-btn-nx-drilldown-large-over .x-frame-tc,.x-btn-nx-drilldown-large-over .x-frame-bc{background-image:url(images/btn/btn-nx-drilldown-large-over-corners.gif)}.x-btn-nx-drilldown-large-over .x-frame-ml,.x-btn-nx-drilldown-large-over .x-frame-mr{background-image:url(images/btn/btn-nx-drilldown-large-over-sides.gif)}.x-btn-nx-drilldown-large-over .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-nx-drilldown-large-over-fbg.gif)}.x-btn-nx-drilldown-large-menu-active .x-frame-tl,.x-btn-nx-drilldown-large-menu-active .x-frame-bl,.x-btn-nx-drilldown-large-menu-active .x-frame-tr,.x-btn-nx-drilldown-large-menu-active .x-frame-br,.x-btn-nx-drilldown-large-menu-active .x-frame-tc,.x-btn-nx-drilldown-large-menu-active .x-frame-bc,.x-btn-nx-drilldown-large-pressed .x-frame-tl,.x-btn-nx-drilldown-large-pressed .x-frame-bl,.x-btn-nx-drilldown-large-pressed .x-frame-tr,.x-btn-nx-drilldown-large-pressed .x-frame-br,.x-btn-nx-drilldown-large-pressed .x-frame-tc,.x-btn-nx-drilldown-large-pressed .x-frame-bc{background-image:url(images/btn/btn-nx-drilldown-large-pressed-corners.gif)}.x-btn-nx-drilldown-large-menu-active .x-frame-ml,.x-btn-nx-drilldown-large-menu-active .x-frame-mr,.x-btn-nx-drilldown-large-pressed .x-frame-ml,.x-btn-nx-drilldown-large-pressed .x-frame-mr{background-image:url(images/btn/btn-nx-drilldown-large-pressed-sides.gif)}.x-btn-nx-drilldown-large-menu-active .x-frame-mc,.x-btn-nx-drilldown-large-pressed .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-nx-drilldown-large-pressed-fbg.gif)}.x-btn-nx-drilldown-large-disabled .x-frame-tl,.x-btn-nx-drilldown-large-disabled .x-frame-bl,.x-btn-nx-drilldown-large-disabled .x-frame-tr,.x-btn-nx-drilldown-large-disabled .x-frame-br,.x-btn-nx-drilldown-large-disabled .x-frame-tc,.x-btn-nx-drilldown-large-disabled .x-frame-bc{background-image:url(images/btn/btn-nx-drilldown-large-disabled-corners.gif)}.x-btn-nx-drilldown-large-disabled .x-frame-ml,.x-btn-nx-drilldown-large-disabled .x-frame-mr{background-image:url(images/btn/btn-nx-drilldown-large-disabled-sides.gif)}.x-btn-nx-drilldown-large-disabled .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-nx-drilldown-large-disabled-fbg.gif)}.x-nlg .x-btn-nx-drilldown-large-focus{background-image:url(images/btn/btn-nx-drilldown-large-focus-bg.gif)}.x-nlg .x-btn-nx-drilldown-large-over{background-image:url(images/btn/btn-nx-drilldown-large-over-bg.gif)}.x-nlg .x-btn-nx-drilldown-large-menu-active,.x-nlg .x-btn-nx-drilldown-large-pressed{background-image:url(images/btn/btn-nx-drilldown-large-pressed-bg.gif)}.x-nlg .x-btn-nx-drilldown-large-disabled{background-image:url(images/btn/btn-nx-drilldown-large-disabled-bg.gif)}.x-nbr .x-btn-nx-drilldown-large{background-image:none}.x-btn-nx-drilldown-large .x-btn-split-right{background-image:url(images/button/nx-drilldown-large-s-arrow.png);padding-right:38px}.x-btn-nx-drilldown-large .x-btn-split-bottom{background-image:url(images/button/nx-drilldown-large-s-arrow-b.png);padding-bottom:34px}.x-btn-nx-drilldown-large-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-large-focus-corners.gif), sides:url(images/btn/btn-nx-drilldown-large-focus-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-large-focus-fbg.gif), bg:url(images/btn/btn-nx-drilldown-large-focus-bg.gif)"}.x-btn-nx-drilldown-large-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-large-over-corners.gif), sides:url(images/btn/btn-nx-drilldown-large-over-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-large-over-fbg.gif), bg:url(images/btn/btn-nx-drilldown-large-over-bg.gif)"}.x-btn-nx-drilldown-large-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-large-pressed-corners.gif), sides:url(images/btn/btn-nx-drilldown-large-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-large-pressed-fbg.gif), bg:url(images/btn/btn-nx-drilldown-large-pressed-bg.gif)"}.x-btn-nx-drilldown-large-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-large-disabled-corners.gif), sides:url(images/btn/btn-nx-drilldown-large-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-large-disabled-fbg.gif), bg:url(images/btn/btn-nx-drilldown-large-disabled-bg.gif)"}.x-btn-nx-drilldown-large{padding:0!important}.x-btn-nx-drilldown-medium{border-color:transparent}.x-btn-nx-drilldown-medium{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:0;border-style:solid;background-image:none;background-color:transparent}.x-btn-nx-drilldown-medium-mc{background-image:url(images/btn/btn-nx-drilldown-medium-fbg.gif);background-position:0 top;background-color:transparent}.x-nlg .x-btn-nx-drilldown-medium{background-image:url(images/btn/btn-nx-drilldown-medium-bg.gif);background-position:0 top}.x-nbr .x-btn-nx-drilldown-medium{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-nx-drilldown-medium-frameInfo{font-family:th-3-3-3-3-0-0-0-0-3-3-3-3}.x-btn-nx-drilldown-medium-tl{background-position:0 -6px}.x-btn-nx-drilldown-medium-tr{background-position:right -9px}.x-btn-nx-drilldown-medium-bl{background-position:0 -12px}.x-btn-nx-drilldown-medium-br{background-position:right -15px}.x-btn-nx-drilldown-medium-ml{background-position:0 top}.x-btn-nx-drilldown-medium-mr{background-position:right top}.x-btn-nx-drilldown-medium-tc{background-position:0 0}.x-btn-nx-drilldown-medium-bc{background-position:0 -3px}.x-btn-nx-drilldown-medium-tr,.x-btn-nx-drilldown-medium-br,.x-btn-nx-drilldown-medium-mr{padding-right:3px}.x-btn-nx-drilldown-medium-tl,.x-btn-nx-drilldown-medium-bl,.x-btn-nx-drilldown-medium-ml{padding-left:3px}.x-btn-nx-drilldown-medium-tc{height:3px}.x-btn-nx-drilldown-medium-bc{height:3px}.x-btn-nx-drilldown-medium-tl,.x-btn-nx-drilldown-medium-bl,.x-btn-nx-drilldown-medium-tr,.x-btn-nx-drilldown-medium-br,.x-btn-nx-drilldown-medium-tc,.x-btn-nx-drilldown-medium-bc,.x-btn-nx-drilldown-medium-ml,.x-btn-nx-drilldown-medium-mr{zoom:1}.x-btn-nx-drilldown-medium-ml,.x-btn-nx-drilldown-medium-mr{zoom:1}.x-btn-nx-drilldown-medium-mc{padding:0}.x-strict .x-ie7 .x-btn-nx-drilldown-medium-tl,.x-strict .x-ie7 .x-btn-nx-drilldown-medium-bl{position:relative;right:0}.x-btn-nx-drilldown-medium:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-drilldown-medium-fbg.gif), bg:url(images/btn/btn-nx-drilldown-medium-bg.gif)"}.x-btn-nx-drilldown-medium .x-btn-inner{font-size:18px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#006bbf;padding:0}.x-btn-nx-drilldown-medium .x-btn-arrow{background-image:url(images/button/nx-drilldown-medium-arrow.png)}.x-btn-nx-drilldown-medium .x-btn-arrow-right{padding-right:30px}.x-btn-nx-drilldown-medium .x-btn-arrow-bottom{padding-bottom:26px}.x-btn-nx-drilldown-medium .x-btn-glyph{font-size:24px;line-height:24px;color:white}.x-ie8m .x-btn-nx-drilldown-medium .x-btn-glyph{color:white}.x-btn-nx-drilldown-medium-disabled{background-image:none;background-color:transparent}.x-btn-nx-drilldown-medium-disabled .x-btn-inner{color:#333}.x-btn-nx-drilldown-medium-icon .x-btn-button,.x-btn-nx-drilldown-medium-noicon .x-btn-button{height:24px}.x-btn-nx-drilldown-medium-icon .x-btn-inner,.x-btn-nx-drilldown-medium-noicon .x-btn-inner{line-height:24px}.x-btn-nx-drilldown-medium-icon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-drilldown-medium-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-drilldown-medium-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-nx-drilldown-medium-icon .x-btn-inner{width:24px;padding:0}.x-btn-nx-drilldown-medium-icon .x-btn-icon-el{width:24px;height:24px}.x-btn-nx-drilldown-medium-icon-text-left .x-btn-button{height:24px}.x-btn-nx-drilldown-medium-icon-text-left .x-btn-inner{line-height:24px;padding-left:29px}.x-btn-nx-drilldown-medium-icon-text-left .x-btn-icon-el{width:24px;right:auto}.x-ie6 .x-btn-nx-drilldown-medium-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-drilldown-medium-icon-text-left .x-btn-icon-el{height:24px}.x-btn-nx-drilldown-medium-icon-text-right .x-btn-button{height:24px}.x-btn-nx-drilldown-medium-icon-text-right .x-btn-inner{line-height:24px;padding-right:29px}.x-btn-nx-drilldown-medium-icon-text-right .x-btn-icon-el{width:24px;left:auto}.x-ie6 .x-btn-nx-drilldown-medium-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-drilldown-medium-icon-text-right .x-btn-icon-el{height:24px}.x-btn-nx-drilldown-medium-icon-text-top .x-btn-inner{padding-top:29px}.x-btn-nx-drilldown-medium-icon-text-top .x-btn-icon-el{height:24px;bottom:auto}.x-ie6 .x-btn-nx-drilldown-medium-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-drilldown-medium-icon-text-top .x-btn-icon-el{width:100%}.x-btn-nx-drilldown-medium-icon-text-bottom .x-btn-inner{padding-bottom:29px}.x-btn-nx-drilldown-medium-icon-text-bottom .x-btn-icon-el{height:24px;top:auto}.x-ie6 .x-btn-nx-drilldown-medium-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-drilldown-medium-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-nx-drilldown-medium-focus{background-image:none;background-color:transparent}.x-btn-nx-drilldown-medium-focus .x-btn-inner{color:#0f4976}.x-btn-nx-drilldown-medium-over{background-image:none;background-color:transparent}.x-btn-nx-drilldown-medium-over .x-btn-inner{color:#0f4976}.x-btn-nx-drilldown-medium-menu-active,.x-btn-nx-drilldown-medium-pressed{background-image:none;background-color:transparent}.x-btn-nx-drilldown-medium-menu-active .x-btn-inner,.x-btn-nx-drilldown-medium-pressed .x-btn-inner{color:#96caee}.x-btn-nx-drilldown-medium-focus .x-frame-tl,.x-btn-nx-drilldown-medium-focus .x-frame-bl,.x-btn-nx-drilldown-medium-focus .x-frame-tr,.x-btn-nx-drilldown-medium-focus .x-frame-br,.x-btn-nx-drilldown-medium-focus .x-frame-tc,.x-btn-nx-drilldown-medium-focus .x-frame-bc{background-image:url(images/btn/btn-nx-drilldown-medium-focus-corners.gif)}.x-btn-nx-drilldown-medium-focus .x-frame-ml,.x-btn-nx-drilldown-medium-focus .x-frame-mr{background-image:url(images/btn/btn-nx-drilldown-medium-focus-sides.gif)}.x-btn-nx-drilldown-medium-focus .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-nx-drilldown-medium-focus-fbg.gif)}.x-btn-nx-drilldown-medium-over .x-frame-tl,.x-btn-nx-drilldown-medium-over .x-frame-bl,.x-btn-nx-drilldown-medium-over .x-frame-tr,.x-btn-nx-drilldown-medium-over .x-frame-br,.x-btn-nx-drilldown-medium-over .x-frame-tc,.x-btn-nx-drilldown-medium-over .x-frame-bc{background-image:url(images/btn/btn-nx-drilldown-medium-over-corners.gif)}.x-btn-nx-drilldown-medium-over .x-frame-ml,.x-btn-nx-drilldown-medium-over .x-frame-mr{background-image:url(images/btn/btn-nx-drilldown-medium-over-sides.gif)}.x-btn-nx-drilldown-medium-over .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-nx-drilldown-medium-over-fbg.gif)}.x-btn-nx-drilldown-medium-menu-active .x-frame-tl,.x-btn-nx-drilldown-medium-menu-active .x-frame-bl,.x-btn-nx-drilldown-medium-menu-active .x-frame-tr,.x-btn-nx-drilldown-medium-menu-active .x-frame-br,.x-btn-nx-drilldown-medium-menu-active .x-frame-tc,.x-btn-nx-drilldown-medium-menu-active .x-frame-bc,.x-btn-nx-drilldown-medium-pressed .x-frame-tl,.x-btn-nx-drilldown-medium-pressed .x-frame-bl,.x-btn-nx-drilldown-medium-pressed .x-frame-tr,.x-btn-nx-drilldown-medium-pressed .x-frame-br,.x-btn-nx-drilldown-medium-pressed .x-frame-tc,.x-btn-nx-drilldown-medium-pressed .x-frame-bc{background-image:url(images/btn/btn-nx-drilldown-medium-pressed-corners.gif)}.x-btn-nx-drilldown-medium-menu-active .x-frame-ml,.x-btn-nx-drilldown-medium-menu-active .x-frame-mr,.x-btn-nx-drilldown-medium-pressed .x-frame-ml,.x-btn-nx-drilldown-medium-pressed .x-frame-mr{background-image:url(images/btn/btn-nx-drilldown-medium-pressed-sides.gif)}.x-btn-nx-drilldown-medium-menu-active .x-frame-mc,.x-btn-nx-drilldown-medium-pressed .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-nx-drilldown-medium-pressed-fbg.gif)}.x-btn-nx-drilldown-medium-disabled .x-frame-tl,.x-btn-nx-drilldown-medium-disabled .x-frame-bl,.x-btn-nx-drilldown-medium-disabled .x-frame-tr,.x-btn-nx-drilldown-medium-disabled .x-frame-br,.x-btn-nx-drilldown-medium-disabled .x-frame-tc,.x-btn-nx-drilldown-medium-disabled .x-frame-bc{background-image:url(images/btn/btn-nx-drilldown-medium-disabled-corners.gif)}.x-btn-nx-drilldown-medium-disabled .x-frame-ml,.x-btn-nx-drilldown-medium-disabled .x-frame-mr{background-image:url(images/btn/btn-nx-drilldown-medium-disabled-sides.gif)}.x-btn-nx-drilldown-medium-disabled .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-nx-drilldown-medium-disabled-fbg.gif)}.x-nlg .x-btn-nx-drilldown-medium-focus{background-image:url(images/btn/btn-nx-drilldown-medium-focus-bg.gif)}.x-nlg .x-btn-nx-drilldown-medium-over{background-image:url(images/btn/btn-nx-drilldown-medium-over-bg.gif)}.x-nlg .x-btn-nx-drilldown-medium-menu-active,.x-nlg .x-btn-nx-drilldown-medium-pressed{background-image:url(images/btn/btn-nx-drilldown-medium-pressed-bg.gif)}.x-nlg .x-btn-nx-drilldown-medium-disabled{background-image:url(images/btn/btn-nx-drilldown-medium-disabled-bg.gif)}.x-nbr .x-btn-nx-drilldown-medium{background-image:none}.x-btn-nx-drilldown-medium .x-btn-split-right{background-image:url(images/button/nx-drilldown-medium-s-arrow.png);padding-right:32px}.x-btn-nx-drilldown-medium .x-btn-split-bottom{background-image:url(images/button/nx-drilldown-medium-s-arrow-b.png);padding-bottom:28px}.x-btn-nx-drilldown-medium-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-medium-focus-corners.gif), sides:url(images/btn/btn-nx-drilldown-medium-focus-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-medium-focus-fbg.gif), bg:url(images/btn/btn-nx-drilldown-medium-focus-bg.gif)"}.x-btn-nx-drilldown-medium-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-medium-over-corners.gif), sides:url(images/btn/btn-nx-drilldown-medium-over-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-medium-over-fbg.gif), bg:url(images/btn/btn-nx-drilldown-medium-over-bg.gif)"}.x-btn-nx-drilldown-medium-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-medium-pressed-corners.gif), sides:url(images/btn/btn-nx-drilldown-medium-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-medium-pressed-fbg.gif), bg:url(images/btn/btn-nx-drilldown-medium-pressed-bg.gif)"}.x-btn-nx-drilldown-medium-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-medium-disabled-corners.gif), sides:url(images/btn/btn-nx-drilldown-medium-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-medium-disabled-fbg.gif), bg:url(images/btn/btn-nx-drilldown-medium-disabled-bg.gif)"}.x-btn-nx-drilldown-medium{padding:0 2px 0 6px}.x-btn-nx-drilldown-medium .x-btn-inner{text-overflow:ellipsis}.nx-feature-content .x-panel-header .x-btn-nx-drilldown-medium{top:7px!important}.x-btn-nx-drilldown-toolbar-small{border-color:transparent}.x-btn-nx-drilldown-toolbar-small{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;padding:3px 3px 3px 3px;border-width:1px;border-style:solid;background-image:none;background-color:transparent}.x-btn-nx-drilldown-toolbar-small-mc{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-fbg.gif);background-position:0 top;background-color:transparent}.x-nlg .x-btn-nx-drilldown-toolbar-small{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-bg.gif);background-position:0 top}.x-nbr .x-btn-nx-drilldown-toolbar-small{padding:0!important;border-width:0!important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;background-color:transparent;background-image:none}.x-btn-nx-drilldown-toolbar-small-frameInfo{font-family:th-3-3-3-3-1-1-1-1-3-3-3-3}.x-btn-nx-drilldown-toolbar-small-tl{background-position:0 -6px}.x-btn-nx-drilldown-toolbar-small-tr{background-position:right -9px}.x-btn-nx-drilldown-toolbar-small-bl{background-position:0 -12px}.x-btn-nx-drilldown-toolbar-small-br{background-position:right -15px}.x-btn-nx-drilldown-toolbar-small-ml{background-position:0 top}.x-btn-nx-drilldown-toolbar-small-mr{background-position:right top}.x-btn-nx-drilldown-toolbar-small-tc{background-position:0 0}.x-btn-nx-drilldown-toolbar-small-bc{background-position:0 -3px}.x-btn-nx-drilldown-toolbar-small-tr,.x-btn-nx-drilldown-toolbar-small-br,.x-btn-nx-drilldown-toolbar-small-mr{padding-right:3px}.x-btn-nx-drilldown-toolbar-small-tl,.x-btn-nx-drilldown-toolbar-small-bl,.x-btn-nx-drilldown-toolbar-small-ml{padding-left:3px}.x-btn-nx-drilldown-toolbar-small-tc{height:3px}.x-btn-nx-drilldown-toolbar-small-bc{height:3px}.x-btn-nx-drilldown-toolbar-small-tl,.x-btn-nx-drilldown-toolbar-small-bl,.x-btn-nx-drilldown-toolbar-small-tr,.x-btn-nx-drilldown-toolbar-small-br,.x-btn-nx-drilldown-toolbar-small-tc,.x-btn-nx-drilldown-toolbar-small-bc,.x-btn-nx-drilldown-toolbar-small-ml,.x-btn-nx-drilldown-toolbar-small-mr{zoom:1}.x-btn-nx-drilldown-toolbar-small-ml,.x-btn-nx-drilldown-toolbar-small-mr{zoom:1}.x-btn-nx-drilldown-toolbar-small-mc{padding:1px 1px 1px 1px}.x-strict .x-ie7 .x-btn-nx-drilldown-toolbar-small-tl,.x-strict .x-ie7 .x-btn-nx-drilldown-toolbar-small-bl{position:relative;right:0}.x-btn-nx-drilldown-toolbar-small:after{display:none;content:"x-slicer:stretch:bottom, frame-bg:url(images/btn/btn-nx-drilldown-toolbar-small-fbg.gif), bg:url(images/btn/btn-nx-drilldown-toolbar-small-bg.gif)"}.x-btn-nx-drilldown-toolbar-small .x-btn-inner{font-size:13px;font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;color:#006bbf;padding:0}.x-btn-nx-drilldown-toolbar-small .x-btn-arrow{background-image:url(images/button/nx-drilldown-toolbar-small-arrow.png)}.x-btn-nx-drilldown-toolbar-small .x-btn-arrow-right{padding-right:21px}.x-btn-nx-drilldown-toolbar-small .x-btn-arrow-bottom{padding-bottom:18px}.x-btn-nx-drilldown-toolbar-small .x-btn-glyph{font-size:16px;line-height:16px;color:#006bbf}.x-ie8m .x-btn-nx-drilldown-toolbar-small .x-btn-glyph{color:#006bbf}.x-btn-nx-drilldown-toolbar-small-disabled{background-image:none;background-color:transparent}.x-btn-nx-drilldown-toolbar-small-disabled .x-btn-inner{color:#333}.x-btn-nx-drilldown-toolbar-small-icon .x-btn-button,.x-btn-nx-drilldown-toolbar-small-noicon .x-btn-button{height:16px}.x-btn-nx-drilldown-toolbar-small-icon .x-btn-inner,.x-btn-nx-drilldown-toolbar-small-noicon .x-btn-inner{line-height:16px}.x-btn-nx-drilldown-toolbar-small-icon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-drilldown-toolbar-small-noicon .x-btn-arrow-right .x-btn-inner,.x-btn-nx-drilldown-toolbar-small-icon-text-left .x-btn-arrow-right .x-btn-inner{padding-right:0}.x-btn-nx-drilldown-toolbar-small-icon .x-btn-inner{width:16px;padding:0}.x-btn-nx-drilldown-toolbar-small-icon .x-btn-icon-el{width:16px;height:16px}.x-btn-nx-drilldown-toolbar-small-icon-text-left .x-btn-button{height:16px}.x-btn-nx-drilldown-toolbar-small-icon-text-left .x-btn-inner{line-height:16px;padding-left:21px}.x-btn-nx-drilldown-toolbar-small-icon-text-left .x-btn-icon-el{width:16px;right:auto}.x-ie6 .x-btn-nx-drilldown-toolbar-small-icon-text-left .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-drilldown-toolbar-small-icon-text-left .x-btn-icon-el{height:16px}.x-btn-nx-drilldown-toolbar-small-icon-text-right .x-btn-button{height:16px}.x-btn-nx-drilldown-toolbar-small-icon-text-right .x-btn-inner{line-height:16px;padding-right:21px}.x-btn-nx-drilldown-toolbar-small-icon-text-right .x-btn-icon-el{width:16px;left:auto}.x-ie6 .x-btn-nx-drilldown-toolbar-small-icon-text-right .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-drilldown-toolbar-small-icon-text-right .x-btn-icon-el{height:16px}.x-btn-nx-drilldown-toolbar-small-icon-text-top .x-btn-inner{padding-top:21px}.x-btn-nx-drilldown-toolbar-small-icon-text-top .x-btn-icon-el{height:16px;bottom:auto}.x-ie6 .x-btn-nx-drilldown-toolbar-small-icon-text-top .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-drilldown-toolbar-small-icon-text-top .x-btn-icon-el{width:100%}.x-btn-nx-drilldown-toolbar-small-icon-text-bottom .x-btn-inner{padding-bottom:21px}.x-btn-nx-drilldown-toolbar-small-icon-text-bottom .x-btn-icon-el{height:16px;top:auto}.x-ie6 .x-btn-nx-drilldown-toolbar-small-icon-text-bottom .x-btn-icon-el,.x-quirks .x-ie .x-btn-nx-drilldown-toolbar-small-icon-text-bottom .x-btn-icon-el{width:100%}.x-btn-nx-drilldown-toolbar-small-focus{background-image:none;background-color:transparent}.x-btn-nx-drilldown-toolbar-small-focus .x-btn-inner{color:#0f4976}.x-btn-nx-drilldown-toolbar-small-over{background-image:none;background-color:transparent}.x-btn-nx-drilldown-toolbar-small-over .x-btn-inner{color:#0f4976}.x-btn-nx-drilldown-toolbar-small-menu-active,.x-btn-nx-drilldown-toolbar-small-pressed{background-image:none;background-color:transparent}.x-btn-nx-drilldown-toolbar-small-menu-active .x-btn-inner,.x-btn-nx-drilldown-toolbar-small-pressed .x-btn-inner{color:#96caee}.x-btn-nx-drilldown-toolbar-small-focus .x-frame-tl,.x-btn-nx-drilldown-toolbar-small-focus .x-frame-bl,.x-btn-nx-drilldown-toolbar-small-focus .x-frame-tr,.x-btn-nx-drilldown-toolbar-small-focus .x-frame-br,.x-btn-nx-drilldown-toolbar-small-focus .x-frame-tc,.x-btn-nx-drilldown-toolbar-small-focus .x-frame-bc{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-focus-corners.gif)}.x-btn-nx-drilldown-toolbar-small-focus .x-frame-ml,.x-btn-nx-drilldown-toolbar-small-focus .x-frame-mr{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-focus-sides.gif)}.x-btn-nx-drilldown-toolbar-small-focus .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-nx-drilldown-toolbar-small-focus-fbg.gif)}.x-btn-nx-drilldown-toolbar-small-over .x-frame-tl,.x-btn-nx-drilldown-toolbar-small-over .x-frame-bl,.x-btn-nx-drilldown-toolbar-small-over .x-frame-tr,.x-btn-nx-drilldown-toolbar-small-over .x-frame-br,.x-btn-nx-drilldown-toolbar-small-over .x-frame-tc,.x-btn-nx-drilldown-toolbar-small-over .x-frame-bc{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-over-corners.gif)}.x-btn-nx-drilldown-toolbar-small-over .x-frame-ml,.x-btn-nx-drilldown-toolbar-small-over .x-frame-mr{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-over-sides.gif)}.x-btn-nx-drilldown-toolbar-small-over .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-nx-drilldown-toolbar-small-over-fbg.gif)}.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-tl,.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-bl,.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-tr,.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-br,.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-tc,.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-bc,.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-tl,.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-bl,.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-tr,.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-br,.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-tc,.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-bc{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-pressed-corners.gif)}.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-ml,.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-mr,.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-ml,.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-mr{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-pressed-sides.gif)}.x-btn-nx-drilldown-toolbar-small-menu-active .x-frame-mc,.x-btn-nx-drilldown-toolbar-small-pressed .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-nx-drilldown-toolbar-small-pressed-fbg.gif)}.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-tl,.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-bl,.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-tr,.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-br,.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-tc,.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-bc{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-disabled-corners.gif)}.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-ml,.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-mr{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-disabled-sides.gif)}.x-btn-nx-drilldown-toolbar-small-disabled .x-frame-mc{background-color:transparent;background-image:url(images/btn/btn-nx-drilldown-toolbar-small-disabled-fbg.gif)}.x-nlg .x-btn-nx-drilldown-toolbar-small-focus{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-focus-bg.gif)}.x-nlg .x-btn-nx-drilldown-toolbar-small-over{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-over-bg.gif)}.x-nlg .x-btn-nx-drilldown-toolbar-small-menu-active,.x-nlg .x-btn-nx-drilldown-toolbar-small-pressed{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-pressed-bg.gif)}.x-nlg .x-btn-nx-drilldown-toolbar-small-disabled{background-image:url(images/btn/btn-nx-drilldown-toolbar-small-disabled-bg.gif)}.x-nbr .x-btn-nx-drilldown-toolbar-small{background-image:none}.x-btn-nx-drilldown-toolbar-small .x-btn-split-right{background-image:url(images/button/nx-drilldown-toolbar-small-s-arrow.png);padding-right:23px}.x-btn-nx-drilldown-toolbar-small .x-btn-split-bottom{background-image:url(images/button/nx-drilldown-toolbar-small-s-arrow-b.png);padding-bottom:20px}.x-btn-nx-drilldown-toolbar-small-focus:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-toolbar-small-focus-corners.gif), sides:url(images/btn/btn-nx-drilldown-toolbar-small-focus-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-toolbar-small-focus-fbg.gif), bg:url(images/btn/btn-nx-drilldown-toolbar-small-focus-bg.gif)"}.x-btn-nx-drilldown-toolbar-small-over:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-toolbar-small-over-corners.gif), sides:url(images/btn/btn-nx-drilldown-toolbar-small-over-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-toolbar-small-over-fbg.gif), bg:url(images/btn/btn-nx-drilldown-toolbar-small-over-bg.gif)"}.x-btn-nx-drilldown-toolbar-small-pressed:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-toolbar-small-pressed-corners.gif), sides:url(images/btn/btn-nx-drilldown-toolbar-small-pressed-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-toolbar-small-pressed-fbg.gif), bg:url(images/btn/btn-nx-drilldown-toolbar-small-pressed-bg.gif)"}.x-btn-nx-drilldown-toolbar-small-disabled:after{display:none;content:"x-slicer:stretch:bottom, corners:url(images/btn/btn-nx-drilldown-toolbar-small-disabled-corners.gif), sides:url(images/btn/btn-nx-drilldown-toolbar-small-disabled-sides.gif), frame-bg:url(images/btn/btn-nx-drilldown-toolbar-small-disabled-fbg.gif), bg:url(images/btn/btn-nx-drilldown-toolbar-small-disabled-bg.gif)"}.nx-breadcrumb-separator{font-size:18px;top:7px!important;color:gray;padding-left:12px}.nx-breadcrumb-icon{top:9px!important;margin-left:13px}.nx-wizard-panel{padding:12px 12px 12px 12px;border-top:1px solid #ddd!important}.nx-wizard-panel .screencontainer,.nx-wizard-panel .screenheader{background-color:white;border:1px solid #ddd!important}.nx-wizard-panel .screencontainer{padding:12px 12px 12px 12px;border-top:0!important}.nx-wizard-panel .screenheader{padding:7px 8px 0 8px!important;border-bottom:0!important}.nx-wizard-panel .screenheader .title{font-size:22px;font-weight:bold}.nx-wizard-panel .screenheader .progress{font-size:13px;font-weight:bold;color:#777}.nx-red-marker td{color:#db2852}.nx-coreui-component-details{background-color:white}.nx-coreui-component-componentassetinfo{border-top:1px solid silver}.nx-coreui-component-componentassetinfo>.x-panel-header{background-color:white}.nx-coreui-component-componentassetinfo>.x-panel-header *{font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:18px;line-height:20px;color:black}.nx-coreui-component-componentinfo{border-top:1px solid silver}.nx-coreui-component-componentinfo>.x-panel-header{background-color:white}.nx-coreui-component-componentinfo>.x-panel-header *{font-weight:normal;font-family:"Proxima Nova","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:18px;line-height:20px;color:black}.nx-coreui-component-assetattributes .attribute-value{word-wrap:break-word;white-space:normal!important}.nx-log-viewer-field,.nx-log-viewer-field textarea{background-color:white!important}.nx-coreui-searchfeature .criteria{background-color:white;padding:0 0 10px 10px}.nx-coreui-searchfeature .criteria .more-criteria{margin:26px 0 0 0}.nx-coreui-support-metrics .metricwidget{margin:0 20px 20px 0}.nx-user-token-field{padding:2px;font-family:"Courier New",Courier,monospace;font-size:13px} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/browserconfig.xml b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/browserconfig.xml new file mode 100644 index 0000000000000000000000000000000000000000..7b1730de6ac8121dc130f5e6c40266fe4b21d8c3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/browserconfig.xml @@ -0,0 +1,26 @@ + + + + + + + + + + #00a300 + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/favicon-16x16.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..93f5dabecbaf2fdec67598db87bfecdf49287536 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/favicon-16x16.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/favicon-32x32.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..ea8d5729646361bb78ec55e606d8ab9d747427c4 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/favicon-32x32.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/favicon.ico b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..076b8a3d1913481e4442b38c5a4a549f10fc6129 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/favicon.ico differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/FontAwesome.otf b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..3ed7f8b48ad9bfab52eb03822fefcd6b77d2e680 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/FontAwesome.otf differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.eot b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..9b6afaedc0fd7aaf927a07f82da9c11022251b8b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.eot differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.svg b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.svg new file mode 100644 index 0000000000000000000000000000000000000000..d05688e9e28e1bee95662f3b668f410d37dfa214 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.svg @@ -0,0 +1,655 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.ttf b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..26dea7951a73079223b50653c455c5adf46a4648 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.ttf differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.woff b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..dc35ce3c2cf688c89b0bd0d4a82bc4be82b14c40 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.woff differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.woff2 b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..500e5172534171f678e01f7569d66f9257036a09 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/font-awesome/fontawesome-webfont.woff2 differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x100/nexus-clear.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x100/nexus-clear.png new file mode 100644 index 0000000000000000000000000000000000000000..6efc5502e119544e8f3f501b0cb92030a6823645 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x100/nexus-clear.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x100/nexus.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x100/nexus.png new file mode 100644 index 0000000000000000000000000000000000000000..4c79c6ea49702988951282a9253e8a3c62f51e25 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x100/nexus.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x100/sonatype.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x100/sonatype.png new file mode 100644 index 0000000000000000000000000000000000000000..7bcb5668a5ce03a7a765eef3b342ccddd4de4639 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x100/sonatype.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/accept.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/accept.png new file mode 100644 index 0000000000000000000000000000000000000000..719e39151bbdcb581c0e096459bfc964fdc202c1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/accept.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/bell.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/bell.png new file mode 100644 index 0000000000000000000000000000000000000000..8dce996f93f6204067f2cf64cd26c83d7b4ccaa1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/bell.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/brain_trainer.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/brain_trainer.png new file mode 100644 index 0000000000000000000000000000000000000000..4b8f4f5b939227e4c09f25bd94cf3620f096d13e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/brain_trainer.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/bug.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/bug.png new file mode 100644 index 0000000000000000000000000000000000000000..c7299fd7d185664861543b111aa95b93f13eb37f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/bug.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/cross.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..33a383748bca465d6d0e7be4fa2ce83c861e36ba Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/cross.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/dashboard.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..2150eaf57b7c334cc082911035e014aadb44853b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/dashboard.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/emotion_cool.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/emotion_cool.png new file mode 100644 index 0000000000000000000000000000000000000000..333faa6831117aa2326900220915c5ef9a35e93e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/emotion_cool.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/emotion_smile.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/emotion_smile.png new file mode 100644 index 0000000000000000000000000000000000000000..d7d39ff5227d662fc457f713316e12a62ec5e2e9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/emotion_smile.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/exclamation.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/exclamation.png new file mode 100644 index 0000000000000000000000000000000000000000..d49653ad8f8b992f70ac4c9baf12635ca73e5f0e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/exclamation.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/house.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/house.png new file mode 100644 index 0000000000000000000000000000000000000000..da024e5ad48ea2d6949c58e26dec214719219781 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/house.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/information.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/information.png new file mode 100644 index 0000000000000000000000000000000000000000..85c1876b5faba5f7af90b67c6fdad4b3d6306acd Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/information.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/nexus.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/nexus.png new file mode 100644 index 0000000000000000000000000000000000000000..0aa4917daca8a9c573e3e9804f67cf95c04030fc Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/nexus.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/sonatype.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/sonatype.png new file mode 100644 index 0000000000000000000000000000000000000000..7fb0e30d5e85a30ed743705f5faedb16c649a858 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/sonatype.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/support.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/support.png new file mode 100644 index 0000000000000000000000000000000000000000..1ae1c84a979ec17fa2a517016c1e8b9aaede92ad Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/support.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/tick.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/tick.png new file mode 100644 index 0000000000000000000000000000000000000000..c277e6b4043b2b9a7c7388e076d2c493a23272c7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/tick.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/users_4.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/users_4.png new file mode 100644 index 0000000000000000000000000000000000000000..c86f64b787a3979fbcbe2ee0370c0c258c9695ff Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/users_4.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/warning.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/warning.png new file mode 100644 index 0000000000000000000000000000000000000000..2c39360a3e5c64d0a9806130f4ac83e1aa7d559f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x16/warning.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x200/nexus-clear.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x200/nexus-clear.png new file mode 100644 index 0000000000000000000000000000000000000000..c1b53698093a1867b18484484ed4f4e0ea180a0e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x200/nexus-clear.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x24/nexus.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x24/nexus.png new file mode 100644 index 0000000000000000000000000000000000000000..1c68c0743380731a2b3f34424a275defd5c3755d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x24/nexus.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x24/sonatype.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x24/sonatype.png new file mode 100644 index 0000000000000000000000000000000000000000..06c3c7575a16ebcb7ac131017dc0cf8cee2225d5 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x24/sonatype.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/accept.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/accept.png new file mode 100644 index 0000000000000000000000000000000000000000..7786ac77ea5918f5ba473c6cc95738ba744572a2 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/accept.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/bell.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/bell.png new file mode 100644 index 0000000000000000000000000000000000000000..663e246baeb671f11d4e3307a8108c5ab0e373c6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/bell.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/brain_trainer.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/brain_trainer.png new file mode 100644 index 0000000000000000000000000000000000000000..b07117da7cf34677ee9a8de2935b2a5516a367dd Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/brain_trainer.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/bug.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/bug.png new file mode 100644 index 0000000000000000000000000000000000000000..563c364c79d9f66c71df08cb7e04b82abd38e0f3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/bug.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/cross.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..4ee1253736b73693b282fff8b698b445756c0548 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/cross.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/dashboard.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..12961f037de51ea528dbaa32fd601b360f574329 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/dashboard.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/emotion_cool.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/emotion_cool.png new file mode 100644 index 0000000000000000000000000000000000000000..6bcec02a4a2f9dbc5d2473336f7836ec0a771855 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/emotion_cool.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/emotion_smile.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/emotion_smile.png new file mode 100644 index 0000000000000000000000000000000000000000..ad09a526a1f54529372596e812019b3191753aeb Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/emotion_smile.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/exclamation.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/exclamation.png new file mode 100644 index 0000000000000000000000000000000000000000..8270104d4bf2ee2ae9879a522b8d73ede9919785 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/exclamation.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/house.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/house.png new file mode 100644 index 0000000000000000000000000000000000000000..51e3c50339298d6dc88d4191d04acdb52d2793c1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/house.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/information.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/information.png new file mode 100644 index 0000000000000000000000000000000000000000..93c67f2bf0e6e07da700bc508b7b9f55e5c6f89a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/information.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/nexus.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/nexus.png new file mode 100644 index 0000000000000000000000000000000000000000..e9ebf2e735d416c14a808b18454f41d97f51fde8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/nexus.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/sonatype.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/sonatype.png new file mode 100644 index 0000000000000000000000000000000000000000..e9d5ab74410d50444c5341cea8adb0f49d07e657 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/sonatype.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/support.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/support.png new file mode 100644 index 0000000000000000000000000000000000000000..c003c0df03723818ad451aced085d8776923a48d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/support.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/tick.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/tick.png new file mode 100644 index 0000000000000000000000000000000000000000..71be0f67f66f743ee18c8f927d3bcdc42e2790c9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/tick.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/users_4.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/users_4.png new file mode 100644 index 0000000000000000000000000000000000000000..0ebec84b0daa0b7553c0fc6f382a51e5c546d09d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/users_4.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/warning.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/warning.png new file mode 100644 index 0000000000000000000000000000000000000000..311e72652b2e264dc48791d7719a8f8309f5f345 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x32/warning.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x48/nexus.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x48/nexus.png new file mode 100644 index 0000000000000000000000000000000000000000..693947ccd659b4ad0a64e43bec0395064127307a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x48/nexus.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x48/sonatype.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x48/sonatype.png new file mode 100644 index 0000000000000000000000000000000000000000..d35ac46592111506fee15b2d2aeee9f260f864c4 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x48/sonatype.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/chrome.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/chrome.png new file mode 100644 index 0000000000000000000000000000000000000000..2673630f30e39a6b1cb443d04d3e95d6a46e50cd Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/chrome.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/firefox.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/firefox.png new file mode 100644 index 0000000000000000000000000000000000000000..13b15c6af32108db0d24b0e1227ab9340eae2764 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/firefox.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/ie.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/ie.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f75a86e4cb57e5ae2a9482f2d4109cff46910b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/ie.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/opera.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/opera.png new file mode 100644 index 0000000000000000000000000000000000000000..2e2382d5c146d242017670cf763bc0f06236ce13 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/opera.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/safari.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/safari.png new file mode 100644 index 0000000000000000000000000000000000000000..c51bb9df3e33b82513e5313a32af5e4a302a3cd9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/icons/x72/safari.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn-group/btn-group-default-framed-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn-group/btn-group-default-framed-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..6a60fee8734baa84a0e6463fb8e39b4d5a205cea Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn-group/btn-group-default-framed-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn-group/btn-group-default-framed-notitle-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn-group/btn-group-default-framed-notitle-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..6a60fee8734baa84a0e6463fb8e39b4d5a205cea Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn-group/btn-group-default-framed-notitle-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn-group/btn-group-default-framed-notitle-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn-group/btn-group-default-framed-notitle-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..802f41fa971ead0a0e6b9075ad5ca288fc2f6d9c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn-group/btn-group-default-framed-notitle-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn-group/btn-group-default-framed-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn-group/btn-group-default-framed-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..3f5736415d5a612d9a43426afa81c790e28c7c52 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn-group/btn-group-default-framed-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..6968664a8002efdd8b7b397300c89676720f9f16 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..beb58abe91b6a98f0e50a41d637e8185fe2cac73 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-disabled-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-disabled-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..60224be1e4f4c7cb37e0c65f83e859ed0c811964 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-disabled-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-disabled-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-disabled-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..436e020cb73acfcbb75d15cc111e017404649c05 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-disabled-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-disabled-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-disabled-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..f73eb572bed7eddea0afed22e1ab7596c0ff6ebd Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-disabled-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-disabled-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-disabled-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..0690d9de42b9fc08e1e1ce51f183696e39859041 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-disabled-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..b3fe0ef0cde66cdd25612b07a9186848cfcf929e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-focus-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-focus-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..97305d84b78712de22d834a5d08a5100bd7c8f15 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-focus-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-focus-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-focus-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..4a87fedcf0a6040227636110c82ed78e0355cf5a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-focus-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-focus-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-focus-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..4ee32d07ab4c061f3e11e20816f8f55ec408ada3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-focus-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-focus-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-focus-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..ece53bce8991d5af48be33f2b9d8926fed128fd7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-focus-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-over-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-over-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..97305d84b78712de22d834a5d08a5100bd7c8f15 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-over-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-over-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-over-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..815cbccf47e1074984ff46d88e7f79f802958426 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-over-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-over-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-over-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..4ee32d07ab4c061f3e11e20816f8f55ec408ada3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-over-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-over-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-over-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..424a0010aeac9afadf939ef36de1e4c466bbd290 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-over-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-pressed-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-pressed-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..ef766ed24c6c5ae5687c046aa4ec6e0ad5d72ad1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-pressed-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-pressed-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-pressed-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..a55bd0f55b29d29184c40bef5a9193f09f95ccc0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-pressed-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-pressed-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-pressed-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..26996b64cf2a8f77fcc9a1b9d4021c8ee2a2e79f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-pressed-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-pressed-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-pressed-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..d749f7c76d44df7c17dc8e9f6385d9595a4d14bf Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-pressed-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..f57b1854df7d7a4d038f4096b7b707a41234e178 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-large-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..52df12b50fe071926d131d6589754a3906210bc8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..beb58abe91b6a98f0e50a41d637e8185fe2cac73 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-disabled-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-disabled-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..f5014b02cf76cc6e7c3eda42da12e1345879341a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-disabled-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-disabled-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-disabled-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..436e020cb73acfcbb75d15cc111e017404649c05 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-disabled-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-disabled-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-disabled-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..1c6f38d73be7f8d40d9b9f44a969e97fe4d803a5 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-disabled-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-disabled-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-disabled-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..366087511ad042a9f530baa4a4db69615c3ad989 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-disabled-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..a37919e113ac664ee6cfb82505cf015986266348 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-focus-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-focus-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..1feac7799480a874aa9f8bd2fd43e38f232c3850 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-focus-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-focus-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-focus-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..4a87fedcf0a6040227636110c82ed78e0355cf5a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-focus-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-focus-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-focus-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..c4b0100200c7129363b578805ba7ff0fd915b2f1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-focus-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-focus-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-focus-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..1620790faaf7c15bc720fdf235b1000ae7a2a0fb Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-focus-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-over-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-over-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..1feac7799480a874aa9f8bd2fd43e38f232c3850 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-over-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-over-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-over-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..815cbccf47e1074984ff46d88e7f79f802958426 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-over-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-over-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-over-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..c4b0100200c7129363b578805ba7ff0fd915b2f1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-over-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-over-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-over-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..4dc1064c64753947e1fd89fb9ce0d770bef90cfb Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-over-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-pressed-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-pressed-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..9b2122e135373f24a384df77f6829d21960b2b3d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-pressed-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-pressed-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-pressed-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..0f831d73fe6e54042944eb425e1e94e2e99557cf Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-pressed-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-pressed-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-pressed-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..afb4232b7d5a1c096ff972597efd8bf98c5d9420 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-pressed-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-pressed-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-pressed-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..5c1c9e946d20133ac41a9fb6a41e1609744d3943 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-pressed-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..c6002e95ba0350a9cc0ca2b0f05e188acea04141 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-medium-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..e4003d19b538961eb3b1772fd08fee35a88ec547 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..72c83cd38af5311dc9b6c3dc8adda0ad3d636716 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-disabled-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-disabled-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..e4003d19b538961eb3b1772fd08fee35a88ec547 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-disabled-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-disabled-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-disabled-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..72a9086307ddf205ac856836400693ba377b8217 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-disabled-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-disabled-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-disabled-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..6e3bc88429eb7ba10dca1ee0c1b5274d5a1ddd26 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-disabled-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-disabled-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-disabled-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..1f358a5680b0da5a444ad991a0b792fabdb62770 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-disabled-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..6e3bc88429eb7ba10dca1ee0c1b5274d5a1ddd26 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-focus-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-focus-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..e4003d19b538961eb3b1772fd08fee35a88ec547 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-focus-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-focus-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-focus-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..fe66314e49d42b19cb5bb6e4f3bfcb62feb92759 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-focus-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-focus-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-focus-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..6e3bc88429eb7ba10dca1ee0c1b5274d5a1ddd26 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-focus-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-focus-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-focus-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..ce2e9a66490959f5310813ab7aad8fce0b9ce121 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-focus-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-over-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-over-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..875266b9caec2ba43ac4527a96ae35dff317f857 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-over-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-over-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-over-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..66d071b26bab4880c2b0f7c5ba00835c9caeecb0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-over-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-over-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-over-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..89a40dffedfd355ff1c99ba48eddc5f48cf8045b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-over-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-over-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-over-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..e8250ad8977e68b527f9dc32589aa719f5544a02 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-over-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-pressed-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-pressed-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..59135c8b01e951145f6fa0ec70e0f322ca404351 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-pressed-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-pressed-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-pressed-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..42ae3d66c6810c7bfd561f2ad15fbd526f61f7d3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-pressed-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-pressed-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-pressed-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..ddb1a5f7edc674a487c95f7ddbc3e6c2427ae1d7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-pressed-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-pressed-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-pressed-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..c481f75c60ef44c0af86b66a7ae28456aa0c4440 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-pressed-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..1f358a5680b0da5a444ad991a0b792fabdb62770 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-small-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..a4e4c4a16a402d303d3393f09d175d08b67f9168 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..76730aa5b38a8ecde54f63e336a0ae618931a967 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-disabled-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-disabled-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..a4e4c4a16a402d303d3393f09d175d08b67f9168 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-disabled-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-disabled-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-disabled-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..76730aa5b38a8ecde54f63e336a0ae618931a967 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-disabled-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-disabled-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-disabled-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..83f72c12185586caf69c27d2f61619ef932eeebd Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-disabled-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-disabled-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-disabled-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..606fb33dc42d37ba54eedbea21a9e463fb45634c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-disabled-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..83f72c12185586caf69c27d2f61619ef932eeebd Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-focus-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-focus-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..a4e4c4a16a402d303d3393f09d175d08b67f9168 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-focus-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-focus-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-focus-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..2dde5cd8767780ada7aafadffe2dc50666b5821c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-focus-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-focus-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-focus-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..83f72c12185586caf69c27d2f61619ef932eeebd Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-focus-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-focus-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-focus-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..3af85e84366995558c65fcb2dd1cca3e6ebe61fa Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-focus-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-over-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-over-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..e1fbcba462719b3263f3e3013fd2ade9089a08f9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-over-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-over-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-over-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..50b633ff33b6beb255f21b16b46d66c635e44292 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-over-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-over-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-over-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..2f1fd0290eec9f41ad987adb0476022d7f803e59 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-over-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-over-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-over-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..31580ea983185a3c490fb187c4a5119ef23c4e8e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-over-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-pressed-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-pressed-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..82d5ff561a99b3b59c743e498f713e22c85b665f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-pressed-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-pressed-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-pressed-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..16257b2f0f857277c25de55890a39d0328372c2a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-pressed-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-pressed-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-pressed-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..e789239b8f8ae6db5cb30a956f431bfb8f5655ec Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-pressed-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-pressed-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-pressed-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..af950391143cfe643d9106206bbfd76b46ce34c8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-pressed-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..606fb33dc42d37ba54eedbea21a9e463fb45634c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-large-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..b5357c80f51bf476bef0245d627339a168431a9c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..95f4577516652b93b6221f37c3dd9dde6b3fee6f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-disabled-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-disabled-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..b5357c80f51bf476bef0245d627339a168431a9c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-disabled-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-disabled-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-disabled-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..96bc2d0e32a89545064519ade11ee2023a6a7bf1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-disabled-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-disabled-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-disabled-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..e37911689d6daa2c22df647e9f7ee3b7dc3d2d23 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-disabled-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-disabled-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-disabled-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..b80d6612db72a9cd1025692457e5a75f26d63939 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-disabled-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..e37911689d6daa2c22df647e9f7ee3b7dc3d2d23 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-focus-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-focus-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..b5357c80f51bf476bef0245d627339a168431a9c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-focus-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-focus-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-focus-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..18bc35fdd8ab29750f03701fd91b4a2744b6b385 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-focus-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-focus-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-focus-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..e37911689d6daa2c22df647e9f7ee3b7dc3d2d23 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-focus-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-focus-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-focus-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..abb155ffd247a35ce15e8e662344d795635ec32f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-focus-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-over-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-over-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..b15832f193a15b4ec3a7f6a3ed74a33689f7f3b6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-over-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-over-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-over-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..50b633ff33b6beb255f21b16b46d66c635e44292 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-over-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-over-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-over-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..16af565d1768eeb3d2ef0ae1639e8d209fe5a983 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-over-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-over-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-over-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..51fcd68f28b16f0adddd23b58ee5ebeea8b809bb Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-over-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-pressed-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-pressed-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..d24b5f4650c937927f129a605a5e54eb10f6c4d1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-pressed-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-pressed-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-pressed-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..c6eb88182c35f1c1994b9ab6625b81b15c5dfed3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-pressed-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-pressed-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-pressed-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..fb6482c938ea1a183c68224fa3ab956a6175cc4b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-pressed-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-pressed-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-pressed-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..e8eceeba74a376ab9f4f3f7fdc616432973dcbc5 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-pressed-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..b80d6612db72a9cd1025692457e5a75f26d63939 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-medium-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..e4003d19b538961eb3b1772fd08fee35a88ec547 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..06e2fac5998871009feaff139fa7f945de7a90f1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-disabled-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-disabled-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..e4003d19b538961eb3b1772fd08fee35a88ec547 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-disabled-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-disabled-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-disabled-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..06e2fac5998871009feaff139fa7f945de7a90f1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-disabled-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-disabled-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-disabled-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..6e3bc88429eb7ba10dca1ee0c1b5274d5a1ddd26 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-disabled-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-disabled-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-disabled-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..deb6f5748f16e5b26dd4df7ba084107396ffdd3f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-disabled-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..6e3bc88429eb7ba10dca1ee0c1b5274d5a1ddd26 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-focus-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-focus-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..e4003d19b538961eb3b1772fd08fee35a88ec547 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-focus-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-focus-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-focus-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..c96186e34544043bf9ee375fe8114dbb4ef6af3f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-focus-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-focus-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-focus-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..6e3bc88429eb7ba10dca1ee0c1b5274d5a1ddd26 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-focus-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-focus-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-focus-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..ce2e9a66490959f5310813ab7aad8fce0b9ce121 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-focus-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-over-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-over-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..875266b9caec2ba43ac4527a96ae35dff317f857 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-over-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-over-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-over-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..50b633ff33b6beb255f21b16b46d66c635e44292 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-over-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-over-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-over-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..89a40dffedfd355ff1c99ba48eddc5f48cf8045b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-over-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-over-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-over-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..94839e02fe24f7a7064899634731d73c732e101c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-over-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-pressed-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-pressed-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..59135c8b01e951145f6fa0ec70e0f322ca404351 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-pressed-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-pressed-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-pressed-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..c6eb88182c35f1c1994b9ab6625b81b15c5dfed3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-pressed-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-pressed-fbg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-pressed-fbg.gif new file mode 100644 index 0000000000000000000000000000000000000000..ddb1a5f7edc674a487c95f7ddbc3e6c2427ae1d7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-pressed-fbg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-pressed-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-pressed-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..6643921770032fd1ec0dbb43232e54c12da97952 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-pressed-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..deb6f5748f16e5b26dd4df7ba084107396ffdd3f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/btn/btn-default-toolbar-small-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..941afc58a085358d91b7054a79b7707056e5d4a9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ae051d953f8e69fe5beab0596aec5d33b87f17ac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/danger-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fc67c0c3522a9b722cd602e09b1919b30834f9da Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..6a0c17dbaf88c5dae3cef565cd062732f32cf8d0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..f818b67cdbe4550f7733589686ea03db0ba4f326 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..2a4c3b10bbb25b7ace76e8aaf4ca4f1838677ec1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..c13635e28011c4c8645cbd8d65215ad5789bc24c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..8c743180fd8e97f9ad569aa3f4cec79c95245b95 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-large-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..5f3acb5da8cba8ba5e154503ff59ad39edcbc85b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..735595c4bedbe0a353853ba10c087eec46717eac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..01d24ca03cf689ddedb97a1e98eaaeaec29006e1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..248743bb86b47a50448fbc0a16b634cf9bc5f0ce Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..799218dc4e2a3049da07b4e7e1d0fa5aef8ba8b6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a437e2e664e853f7ebdd333b86d334c7a07c8587 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ea14ed550d5376cf802e5c26886e30d9cfa48024 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-arrow-rtl.psd b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-arrow-rtl.psd new file mode 100644 index 0000000000000000000000000000000000000000..59e1183936ac275ea7564a0375e7f47bbf31ea59 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-arrow-rtl.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..b7ad1b8b0f11265f55cc4d2c2e301c5f2d0f1bf0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-arrow.psd b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-arrow.psd new file mode 100644 index 0000000000000000000000000000000000000000..c310763b515ad9b349e418f3f8a94680e9f152c4 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-arrow.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..bb6ac1103148e7cfed939420479288d5855dc786 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..6a432cca33a6c7a8d0630eb1a947942011464fe9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2f77cf08b977d13808873a6601626f70c9c6c9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9a65e8e163d0a707619f11d099103308bcedece1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..7ae10261d70c2e0d5cff97548b86fc388a58c147 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..de5242a4fd5ef3ec27a8249e67392192d6613f6b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..a357f5d9e4ce93dedc335190f080cee7d7244f3a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..3f903bebe4d660ba4e94022e4a898b99401f1868 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ea815642073d2809ef6c7731088cec34fcc99b37 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..8fd013ada2991435bd1903dce351674916af153a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-large-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..b1830bd4bbd44b9d7e06d4023a8c31b78abbbfc7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..e94f50fa624f88cf41f1758a1f277f573104fdfc Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..8e51018f6b54b0456e3d988e52f4b2e8e2aadaf6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..404c14d33c24e0ab3ba0ed28759001b9ff4850e3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11e63f47859a175a71e4d6b9830856dc1255690e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..3ef559d452dcd29d2963787bd74f56df3219669d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..7bff9239fc0dcac881694fa4c01230f7017baa1c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..0858a9316071da2469600177b6ddb3342bdf4d01 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..bb6ac1103148e7cfed939420479288d5855dc786 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..6a432cca33a6c7a8d0630eb1a947942011464fe9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2f77cf08b977d13808873a6601626f70c9c6c9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9a65e8e163d0a707619f11d099103308bcedece1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/default-toolbar-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..5f3acb5da8cba8ba5e154503ff59ad39edcbc85b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..735595c4bedbe0a353853ba10c087eec46717eac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..01d24ca03cf689ddedb97a1e98eaaeaec29006e1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..248743bb86b47a50448fbc0a16b634cf9bc5f0ce Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..799218dc4e2a3049da07b4e7e1d0fa5aef8ba8b6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a437e2e664e853f7ebdd333b86d334c7a07c8587 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/header-toolbar-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..5f3acb5da8cba8ba5e154503ff59ad39edcbc85b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..735595c4bedbe0a353853ba10c087eec46717eac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..01d24ca03cf689ddedb97a1e98eaaeaec29006e1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..248743bb86b47a50448fbc0a16b634cf9bc5f0ce Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..799218dc4e2a3049da07b4e7e1d0fa5aef8ba8b6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a437e2e664e853f7ebdd333b86d334c7a07c8587 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/headermode-toolbar-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/loading-spinner.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/loading-spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..55f59960f8065f1043eaddf807e1cd0403239ac4 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/loading-spinner.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/loading.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/loading.png new file mode 100644 index 0000000000000000000000000000000000000000..f9486e882ca746e9e28ab785e7377b5e757e99a8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/loading.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nexus-milestone.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nexus-milestone.png new file mode 100644 index 0000000000000000000000000000000000000000..c19e9bf2067d44168ea83552ae70f1d499fe0769 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nexus-milestone.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-danger-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-danger-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-danger-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-danger-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-danger-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-danger-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-danger-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-danger-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-danger-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-danger-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-danger-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-danger-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-large-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-large-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..6a0c17dbaf88c5dae3cef565cd062732f32cf8d0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-large-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-large-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-large-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..2a4c3b10bbb25b7ace76e8aaf4ca4f1838677ec1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-large-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-large-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-large-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..c13635e28011c4c8645cbd8d65215ad5789bc24c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-large-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-large-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-large-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..8c743180fd8e97f9ad569aa3f4cec79c95245b95 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-large-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..735595c4bedbe0a353853ba10c087eec46717eac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..248743bb86b47a50448fbc0a16b634cf9bc5f0ce Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..799218dc4e2a3049da07b4e7e1d0fa5aef8ba8b6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a437e2e664e853f7ebdd333b86d334c7a07c8587 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-toolbar-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-toolbar-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..0858a9316071da2469600177b6ddb3342bdf4d01 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-toolbar-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-toolbar-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-toolbar-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..6a432cca33a6c7a8d0630eb1a947942011464fe9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-toolbar-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-toolbar-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-toolbar-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2f77cf08b977d13808873a6601626f70c9c6c9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-toolbar-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-toolbar-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-toolbar-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9a65e8e163d0a707619f11d099103308bcedece1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-drilldown-toolbar-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-header-toolbar-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-header-toolbar-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..735595c4bedbe0a353853ba10c087eec46717eac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-header-toolbar-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-header-toolbar-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-header-toolbar-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..248743bb86b47a50448fbc0a16b634cf9bc5f0ce Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-header-toolbar-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-header-toolbar-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-header-toolbar-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..799218dc4e2a3049da07b4e7e1d0fa5aef8ba8b6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-header-toolbar-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-header-toolbar-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-header-toolbar-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a437e2e664e853f7ebdd333b86d334c7a07c8587 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-header-toolbar-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-plain-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-plain-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..0858a9316071da2469600177b6ddb3342bdf4d01 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-plain-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-plain-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-plain-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..6a432cca33a6c7a8d0630eb1a947942011464fe9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-plain-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-plain-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-plain-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2f77cf08b977d13808873a6601626f70c9c6c9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-plain-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-plain-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-plain-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9a65e8e163d0a707619f11d099103308bcedece1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-plain-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-primary-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-primary-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-primary-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-primary-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-primary-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-primary-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-primary-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-primary-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-primary-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-primary-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-primary-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-primary-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-success-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-success-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-success-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-success-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-success-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-success-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-success-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-success-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-success-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-success-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-success-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-success-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-warning-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-warning-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-warning-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-warning-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-warning-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-warning-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-warning-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-warning-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-warning-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-warning-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-warning-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/nx-warning-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..7bff9239fc0dcac881694fa4c01230f7017baa1c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..0858a9316071da2469600177b6ddb3342bdf4d01 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..bb6ac1103148e7cfed939420479288d5855dc786 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..6a432cca33a6c7a8d0630eb1a947942011464fe9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2f77cf08b977d13808873a6601626f70c9c6c9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9a65e8e163d0a707619f11d099103308bcedece1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..7ae10261d70c2e0d5cff97548b86fc388a58c147 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..de5242a4fd5ef3ec27a8249e67392192d6613f6b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..a357f5d9e4ce93dedc335190f080cee7d7244f3a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..3f903bebe4d660ba4e94022e4a898b99401f1868 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ea815642073d2809ef6c7731088cec34fcc99b37 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..8fd013ada2991435bd1903dce351674916af153a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-large-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..b1830bd4bbd44b9d7e06d4023a8c31b78abbbfc7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..e94f50fa624f88cf41f1758a1f277f573104fdfc Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..8e51018f6b54b0456e3d988e52f4b2e8e2aadaf6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..404c14d33c24e0ab3ba0ed28759001b9ff4850e3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11e63f47859a175a71e4d6b9830856dc1255690e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..3ef559d452dcd29d2963787bd74f56df3219669d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-medium-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..7bff9239fc0dcac881694fa4c01230f7017baa1c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..0858a9316071da2469600177b6ddb3342bdf4d01 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..bb6ac1103148e7cfed939420479288d5855dc786 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..6a432cca33a6c7a8d0630eb1a947942011464fe9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2f77cf08b977d13808873a6601626f70c9c6c9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9a65e8e163d0a707619f11d099103308bcedece1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/plain-toolbar-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..941afc58a085358d91b7054a79b7707056e5d4a9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ae051d953f8e69fe5beab0596aec5d33b87f17ac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/primary-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/s.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/s.gif new file mode 100644 index 0000000000000000000000000000000000000000..1d11fa9ada9e93505b3d736acb204083f45d5fbf Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/s.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..941afc58a085358d91b7054a79b7707056e5d4a9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ae051d953f8e69fe5beab0596aec5d33b87f17ac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/success-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..941afc58a085358d91b7054a79b7707056e5d4a9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..229533e21710c03bb8e63cf14a64b2ce1d17bc4e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-s-arrow-b-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-s-arrow-b-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ae051d953f8e69fe5beab0596aec5d33b87f17ac Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-s-arrow-b-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-s-arrow-b.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-s-arrow-b.png new file mode 100644 index 0000000000000000000000000000000000000000..24bde9c56d20da6c16c83520f802ef5c7d5946ef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-s-arrow-b.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-s-arrow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-s-arrow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..11fbd8493baf896545101827118826200215aa84 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-s-arrow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-s-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-s-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..043b3dec1e471e6cf2f65f678f3fa0ee2c21e765 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/button/warning-small-s-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/datepicker/arrow-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/datepicker/arrow-left.png new file mode 100644 index 0000000000000000000000000000000000000000..25f5b0874a1409259283fc43c839228c354b7196 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/datepicker/arrow-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/datepicker/arrow-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/datepicker/arrow-right.png new file mode 100644 index 0000000000000000000000000000000000000000..70653fc62852efe074dbdc2baea107425c7bdfc7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/datepicker/arrow-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/datepicker/month-arrow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/datepicker/month-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..f724135422e3dfb27a3b0b4ec65151670ccad363 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/datepicker/month-arrow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/dd/drop-add.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/dd/drop-add.png new file mode 100644 index 0000000000000000000000000000000000000000..9f66b4709474a66fad9def9c410b5aae715ba291 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/dd/drop-add.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/dd/drop-no.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/dd/drop-no.png new file mode 100644 index 0000000000000000000000000000000000000000..9a0f571821171d95ec44d9953aec867de9530f3a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/dd/drop-no.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/dd/drop-yes.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/dd/drop-yes.png new file mode 100644 index 0000000000000000000000000000000000000000..9f66b4709474a66fad9def9c410b5aae715ba291 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/dd/drop-yes.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/editor/tb-sprite.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/editor/tb-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..40ec064287569a0fd182c99cdf706c1d2d16247e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/editor/tb-sprite.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/fieldset/collapse-tool.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/fieldset/collapse-tool.png new file mode 100644 index 0000000000000000000000000000000000000000..6363d95bc60a59be841f30959bbafd668ba7907a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/fieldset/collapse-tool.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/checkbox.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/checkbox.png new file mode 100644 index 0000000000000000000000000000000000000000..4c840e484639471206b0b214bf9b2ff0e205ae0d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/checkbox.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/clear-trigger-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/clear-trigger-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fe28b6b7876de2328d69ad5625ca0d21ed9ee531 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/clear-trigger-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/clear-trigger.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/clear-trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..fe28b6b7876de2328d69ad5625ca0d21ed9ee531 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/clear-trigger.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/date-trigger-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/date-trigger-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..1d9dfa679a1b72235006f9736eb7b87534febb89 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/date-trigger-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/date-trigger.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/date-trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..1d9dfa679a1b72235006f9736eb7b87534febb89 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/date-trigger.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/exclamation.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/exclamation.png new file mode 100644 index 0000000000000000000000000000000000000000..37d5573dde6f8cee203dc1ac0d0ab06fd1a43020 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/exclamation.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/fa-minus-circle-trigger.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/fa-minus-circle-trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..0fc7e1d72ef19a263b22e49b1d6bfa13109dbc34 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/fa-minus-circle-trigger.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/fa-times-circle-trigger.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/fa-times-circle-trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..4570b9c5913f36fdc756d392800957b596d70ec0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/fa-times-circle-trigger.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/radio.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/radio.png new file mode 100644 index 0000000000000000000000000000000000000000..7c06a2c820229bf2a3944ccfe3fa2386ab8d9c06 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/radio.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/search-trigger-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/search-trigger-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ad1cee9d777a547bcff1159da71bb28f23585080 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/search-trigger-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/search-trigger.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/search-trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..ad1cee9d777a547bcff1159da71bb28f23585080 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/search-trigger.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/spinner-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/spinner-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..e6958fe275a81e70defdc8c618018c87d40c444e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/spinner-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/spinner.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/spinner.png new file mode 100644 index 0000000000000000000000000000000000000000..e6958fe275a81e70defdc8c618018c87d40c444e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/spinner.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/trigger-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/trigger-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..a1fbe0973fd78001fb8eba6e61f09a563246dcd9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/trigger-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/trigger.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..a1fbe0973fd78001fb8eba6e61f09a563246dcd9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/form/trigger.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..42fd0e28f3faad58e3a5232838dfdceb54419b27 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..50a641d6715b5bb5af1f2ee057ae5eab3e4dacf1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid-row-editor-buttons/grid-row-editor-buttons-default-bottom-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..ddcf16b98dcc142b2f035b25c7ada187d7294d78 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..50a641d6715b5bb5af1f2ee057ae5eab3e4dacf1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid-row-editor-buttons/grid-row-editor-buttons-default-top-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/col-move-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/col-move-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..38a244d4c60b4ba3e0c616dbcd62da98b4388908 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/col-move-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/col-move-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/col-move-top.png new file mode 100644 index 0000000000000000000000000000000000000000..9c6fa851ef07741c4f66afc216b617e226372616 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/col-move-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/columns.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/columns.png new file mode 100644 index 0000000000000000000000000000000000000000..bb256e7eab44594a19d9731719b7ff75cc1bf99a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/columns.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/dd-insert-arrow-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/dd-insert-arrow-left.png new file mode 100644 index 0000000000000000000000000000000000000000..846bc8d4808cd8fc363e61a7a657120da93c31fb Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/dd-insert-arrow-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/dd-insert-arrow-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/dd-insert-arrow-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c039a2b324a1c4d0c577151eb1f0a6c5ef81d559 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/dd-insert-arrow-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/dirty-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/dirty-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..7fc4374404bf1aa064c3a30c52e8ee08cf4f2294 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/dirty-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/dirty.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/dirty.png new file mode 100644 index 0000000000000000000000000000000000000000..3f181ed16518dd2a107a7f450cc114c376f457c7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/dirty.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/drop-no.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/drop-no.png new file mode 100644 index 0000000000000000000000000000000000000000..9a0f571821171d95ec44d9953aec867de9530f3a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/drop-no.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/drop-yes.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/drop-yes.png new file mode 100644 index 0000000000000000000000000000000000000000..9f66b4709474a66fad9def9c410b5aae715ba291 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/drop-yes.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/group-by.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/group-by.png new file mode 100644 index 0000000000000000000000000000000000000000..e941346df4528d4c9418d7fac835872dc88c3a0c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/group-by.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/group-collapse.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/group-collapse.png new file mode 100644 index 0000000000000000000000000000000000000000..2244563a7c966d4607931eda328dde7dc9b35ed9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/group-collapse.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/group-expand-sprite.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/group-expand-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..1e2b3939b7d25e4557feb24e2c1408dbe3c31e28 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/group-expand-sprite.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/group-expand.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/group-expand.png new file mode 100644 index 0000000000000000000000000000000000000000..d1e315ead43c0dbbc82e563b5f7fb8ed3fdccd79 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/group-expand.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hd-pop.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hd-pop.png new file mode 100644 index 0000000000000000000000000000000000000000..51355a5f483a46e30a886ff5be1b1125e3749063 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hd-pop.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hmenu-asc.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hmenu-asc.png new file mode 100644 index 0000000000000000000000000000000000000000..b8e2ba271fb60bdf30ca95c9333fcfcef7ab994d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hmenu-asc.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hmenu-desc.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hmenu-desc.png new file mode 100644 index 0000000000000000000000000000000000000000..c8074541dd543168920fb4989334434e82dadc66 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hmenu-desc.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hmenu-lock.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hmenu-lock.png new file mode 100644 index 0000000000000000000000000000000000000000..ca8602415204560d7a40e54560f2c2a0a4356c7d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hmenu-lock.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hmenu-unlock.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hmenu-unlock.png new file mode 100644 index 0000000000000000000000000000000000000000..cf3ffd2f792f46b5a2c536560d8b0fa3b50fa692 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/hmenu-unlock.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/loading.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..81b0f125790bc94311f43e08dee4bb851729adc2 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/loading.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-first.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-first.png new file mode 100644 index 0000000000000000000000000000000000000000..f5e047b7d6944318c48b48ab139ca3b3564cb376 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-first.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-first.psd b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-first.psd new file mode 100644 index 0000000000000000000000000000000000000000..52180a75bcf867b704f5d77e55ae61222e3b637c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-first.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-last.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-last.png new file mode 100644 index 0000000000000000000000000000000000000000..e267a17834949591f85384cc70bd7b10628e3f62 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-last.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-last.psd b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-last.psd new file mode 100644 index 0000000000000000000000000000000000000000..c25e5b840e77b1b9410aa033295d25d7711e7841 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-last.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-next.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-next.png new file mode 100644 index 0000000000000000000000000000000000000000..444d320f296483607ff07d236b497bd2406aacbf Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-next.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-next.psd b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-next.psd new file mode 100644 index 0000000000000000000000000000000000000000..fa5802cd45e66f20a2a8199c91362b95b819c54b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-next.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-prev.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-prev.png new file mode 100644 index 0000000000000000000000000000000000000000..ad680da71dc41816813e4c370f8fc0214443b12e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-prev.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-prev.psd b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-prev.psd new file mode 100644 index 0000000000000000000000000000000000000000..73cc7fa14d6309672d3680962099bfb06c8249fd Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/page-prev.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/pick-button.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/pick-button.png new file mode 100644 index 0000000000000000000000000000000000000000..077ca114954eceff79ea30baaeb18387e7cb77e1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/pick-button.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/refresh.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..9c124654b28fc8b62c2ffc0f21e6e848021add15 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/refresh.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/refresh.psd b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/refresh.psd new file mode 100644 index 0000000000000000000000000000000000000000..af33d503eff0f97f9492449d62aa914aa0d141e6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/refresh.psd differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/sort_asc.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/sort_asc.png new file mode 100644 index 0000000000000000000000000000000000000000..b8e2ba271fb60bdf30ca95c9333fcfcef7ab994d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/sort_asc.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/sort_desc.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/sort_desc.png new file mode 100644 index 0000000000000000000000000000000000000000..c8074541dd543168920fb4989334434e82dadc66 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/grid/sort_desc.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loading-background.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loading-background.png new file mode 100644 index 0000000000000000000000000000000000000000..f91aba28dab3ebd4e89ec3a183cbcf99a01d98f1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loading-background.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loading-logo.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loading-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..55a7f6cb52443e6877822f30cff28259fb291211 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loading-logo.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loading-product.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loading-product.png new file mode 100644 index 0000000000000000000000000000000000000000..2ad94c45a69ba0523aa7f4407f09c53bb2191bbb Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loading-product.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loading-spinner.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loading-spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..55f59960f8065f1043eaddf807e1cd0403239ac4 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loading-spinner.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loadmask/loading.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loadmask/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..8471b4f0b2570c79d4139779176012357865045a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/loadmask/loading.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/checked.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/checked.png new file mode 100644 index 0000000000000000000000000000000000000000..7dc0d1317ec2a2e75011858be29c31370521cf92 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/checked.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/group-checked.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/group-checked.png new file mode 100644 index 0000000000000000000000000000000000000000..c51fc306abf1191b38ba2a957ee07fe66d113853 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/group-checked.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/menu-item-active-bg.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/menu-item-active-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..01cf2fb1fbdc684d61dc9f5bb2fd95741636bf99 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/menu-item-active-bg.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/menu-parent-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/menu-parent-left.png new file mode 100644 index 0000000000000000000000000000000000000000..11351fc4691e109f2073728c7fae16ba2e8f1d23 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/menu-parent-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/menu-parent.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/menu-parent.png new file mode 100644 index 0000000000000000000000000000000000000000..74069eeceef97704ee335cf3ecb631a6068fe39c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/menu-parent.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..46f26f12d8f8693e9cb9624d35cd1d7e6950ca65 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..7f4a51dd53afa9f48c3164f26c95140b7432c69f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/unchecked.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/unchecked.png new file mode 100644 index 0000000000000000000000000000000000000000..73665913ff9f9eca24693608891b2d68e89cbb7e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/menu/unchecked.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-bottom-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-bottom-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..12b4afd887e9c8dbe741d6c6490be87fa63cd5b0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-bottom-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-bottom-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-bottom-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..d86fe1d5c28a4e68f0b785a99e9632402c034848 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-bottom-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-bottom-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-bottom-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5c3b41b71c6da06d1fb1838bd86a7b04b7bc4c8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-bottom-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-bottom-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-bottom-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..d86fe1d5c28a4e68f0b785a99e9632402c034848 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-bottom-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-left-corners-rtl.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-left-corners-rtl.gif new file mode 100644 index 0000000000000000000000000000000000000000..9a0b7e79028fc7df55752e175790e1cde52f2cc4 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-left-corners-rtl.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-left-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-left-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5c3b41b71c6da06d1fb1838bd86a7b04b7bc4c8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-left-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-left-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-left-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..00a0f6a8ef42decaecc619183645800f66790624 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-left-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-right-corners-rtl.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-right-corners-rtl.gif new file mode 100644 index 0000000000000000000000000000000000000000..9a0b7e79028fc7df55752e175790e1cde52f2cc4 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-right-corners-rtl.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-right-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-right-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5c3b41b71c6da06d1fb1838bd86a7b04b7bc4c8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-right-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-right-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-right-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..00a0f6a8ef42decaecc619183645800f66790624 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-right-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-top-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-top-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5c3b41b71c6da06d1fb1838bd86a7b04b7bc4c8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-top-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-top-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-top-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..d86fe1d5c28a4e68f0b785a99e9632402c034848 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-collapsed-top-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-left-corners-rtl.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-left-corners-rtl.gif new file mode 100644 index 0000000000000000000000000000000000000000..f6270fbd90a8b7cd5fb77de5da1cc6b33f598ae4 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-left-corners-rtl.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-left-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-left-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..05d3682db9bf7c3cd958e2f2ff7ce698ff3db0a7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-left-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-left-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-left-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..00a0f6a8ef42decaecc619183645800f66790624 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-left-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-right-corners-rtl.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-right-corners-rtl.gif new file mode 100644 index 0000000000000000000000000000000000000000..35fabef27d5ada8739e479dee3214895c2d5cfe6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-right-corners-rtl.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-right-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-right-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..5e4a52c67fcff274c1f7673bb4615eb3c69e9e79 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-right-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-right-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-right-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..00a0f6a8ef42decaecc619183645800f66790624 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-right-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-top-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-top-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..ce2c63374f942194574538b76beadd5aab16e018 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-top-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-top-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-top-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..d86fe1d5c28a4e68f0b785a99e9632402c034848 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel-header/panel-header-default-framed-top-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel/panel-default-framed-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel/panel-default-framed-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..c0471fe8b8d91c6ff97fda10adc2e5defc189bd3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel/panel-default-framed-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel/panel-default-framed-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel/panel-default-framed-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..2383011ea3d95f664c1ac40683938a21d55d2096 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/panel/panel-default-framed-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/s.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/s.gif new file mode 100644 index 0000000000000000000000000000000000000000..1d11fa9ada9e93505b3d736acb204083f45d5fbf Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/s.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/shared/icon-error.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/shared/icon-error.png new file mode 100644 index 0000000000000000000000000000000000000000..3ba9b069b92c086b3cfbf1270b8d10ff6c4c1ad4 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/shared/icon-error.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/shared/icon-info.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/shared/icon-info.png new file mode 100644 index 0000000000000000000000000000000000000000..3360eac6c4556ed6b69170d6d21c7b3512662677 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/shared/icon-info.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/shared/icon-question.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/shared/icon-question.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0551d1299e7116a4015de6bac182a3c8227dc1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/shared/icon-question.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/shared/icon-warning.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/shared/icon-warning.png new file mode 100644 index 0000000000000000000000000000000000000000..96b21e43a29cc1f2997406df6e0cb90e501fac25 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/shared/icon-warning.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/e-handle.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/e-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..8876d59a61eb479193ea406361cc5f24bf3f7f90 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/e-handle.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/ne-handle.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/ne-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..48399a6b08f567e4d0286ce210b0d60d99eef68d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/ne-handle.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/nw-handle.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/nw-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..b3a1f5926c64e6c7bfd98ebf95f74852296d8dd2 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/nw-handle.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/s-handle.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/s-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..d0f300fe4958b7ee586dc382804c700ce6cfc50d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/s-handle.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/se-handle.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/se-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..56a95e4a19427d81b51363fdb4059b21fff8e87a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/se-handle.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/sw-handle.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/sw-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..580239c2aebb842f533a68666cb50a010e4b8bfb Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/sizer/sw-handle.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/slider/slider-bg.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/slider/slider-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..c3de7ebd30160d3707bbee6cbe6f065965509c34 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/slider/slider-bg.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/slider/slider-thumb.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/slider/slider-thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..9840c4e88613d5fa9dd34c72a9cbd62c6567fb18 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/slider/slider-thumb.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/slider/slider-v-bg.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/slider/slider-v-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..55361b351eb8ddc95deba6558b133b2e1a5a1552 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/slider/slider-v-bg.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/slider/slider-v-thumb.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/slider/slider-v-thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..9840c4e88613d5fa9dd34c72a9cbd62c6567fb18 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/slider/slider-v-thumb.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-plain-scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-plain-scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..725b5fa395eb5e8cb23c02796777e36bc9c21701 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-plain-scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-plain-scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-plain-scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..98215df606775d3227717ad71b659977e3e5cff0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-plain-scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-plain-scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-plain-scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d8481df01837165eba4ea0deb93b37191783dca3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-plain-scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-plain-scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-plain-scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..cda4bc58ac692d2c586f6e3ec10fa5fe0821e361 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-plain-scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..5259c5eba332fc47c1eee747936cbbb44ff3d1ab Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..5c9844265730fbf168020e3e5b062c1882f11aad Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..8d1ef6c37e952219a89f4afc583b6190b7c63603 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..f2831ed4fd6fe3f390663e95b3dc23c5b116b970 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/default-scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-plain-scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-plain-scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..725b5fa395eb5e8cb23c02796777e36bc9c21701 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-plain-scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-plain-scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-plain-scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..98215df606775d3227717ad71b659977e3e5cff0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-plain-scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-plain-scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-plain-scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d8481df01837165eba4ea0deb93b37191783dca3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-plain-scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-plain-scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-plain-scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..cda4bc58ac692d2c586f6e3ec10fa5fe0821e361 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-plain-scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..725b5fa395eb5e8cb23c02796777e36bc9c21701 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..98215df606775d3227717ad71b659977e3e5cff0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d8481df01837165eba4ea0deb93b37191783dca3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..cda4bc58ac692d2c586f6e3ec10fa5fe0821e361 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/light-scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-plain-scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-plain-scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..725b5fa395eb5e8cb23c02796777e36bc9c21701 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-plain-scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-plain-scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-plain-scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..98215df606775d3227717ad71b659977e3e5cff0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-plain-scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-plain-scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-plain-scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d8481df01837165eba4ea0deb93b37191783dca3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-plain-scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-plain-scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-plain-scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..cda4bc58ac692d2c586f6e3ec10fa5fe0821e361 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-plain-scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-scroll-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-scroll-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..725b5fa395eb5e8cb23c02796777e36bc9c21701 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-scroll-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..98215df606775d3227717ad71b659977e3e5cff0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d8481df01837165eba4ea0deb93b37191783dca3 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-scroll-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-scroll-top.png new file mode 100644 index 0000000000000000000000000000000000000000..cda4bc58ac692d2c586f6e3ec10fa5fe0821e361 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab-bar/nx-light-scroll-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-active-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-active-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..914b8c1d765e50d4d150e35ed9f5a0b1030c85be Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-active-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-active-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-active-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..b0c133bfbc75bab6e1f00432d67079cf4779cd12 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-active-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..88c35ef652c67df4da468d2d33796791c0f90710 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-disabled-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-disabled-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..88c35ef652c67df4da468d2d33796791c0f90710 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-disabled-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-disabled-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-disabled-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..bedde718f01daa3685c5bd8b10a6909f194b1282 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-disabled-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-over-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-over-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..3e60dafde964c7ae2264db5263c75345c6f313e1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-over-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-over-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-over-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..1b91aef719764eca70f60c4b5289374c8fc3a6eb Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-over-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..8da957ed5dde37e7ad7aab5bf14d234fed09af23 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-bottom-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-close.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-close.png new file mode 100644 index 0000000000000000000000000000000000000000..320caa48a5a93434158fb1e933dc5cfd31345107 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-close.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-active-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-active-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..1d2eb8cabe4dae446f40026613b51d14b557f3ed Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-active-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-active-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-active-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..b0c133bfbc75bab6e1f00432d67079cf4779cd12 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-active-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..bc162b52d56bf7e16ef8617af104f1eb5a9d60ee Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-disabled-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-disabled-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..4753faf40157677438b1d371b42ae3568fda0f3e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-disabled-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-disabled-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-disabled-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..bedde718f01daa3685c5bd8b10a6909f194b1282 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-disabled-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-over-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-over-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..f91765f8771d976832747b5a9acf8f4ec30b07e2 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-over-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-over-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-over-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..1b91aef719764eca70f60c4b5289374c8fc3a6eb Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-over-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..8da957ed5dde37e7ad7aab5bf14d234fed09af23 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-default-top-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-light-close.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-light-close.png new file mode 100644 index 0000000000000000000000000000000000000000..320caa48a5a93434158fb1e933dc5cfd31345107 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-light-close.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-nx-light-close.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-nx-light-close.png new file mode 100644 index 0000000000000000000000000000000000000000..320caa48a5a93434158fb1e933dc5cfd31345107 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tab/tab-nx-light-close.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tip/tip-default-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tip/tip-default-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..c7802f11da563e6a03f6f50f68dc70a27bae1657 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tip/tip-default-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tip/tip-default-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tip/tip-default-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..6fd765146a7d1c2a0591889f935e9a2570cde124 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tip/tip-default-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tip/tip-form-invalid-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tip/tip-form-invalid-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..c7802f11da563e6a03f6f50f68dc70a27bae1657 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tip/tip-form-invalid-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tip/tip-form-invalid-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tip/tip-form-invalid-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..6fd765146a7d1c2a0591889f935e9a2570cde124 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tip/tip-form-invalid-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/toolbar/more.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/toolbar/more.png new file mode 100644 index 0000000000000000000000000000000000000000..4c884a9edd1d8920a426b5fa18bec1cba13621df Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/toolbar/more.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/toolbar/scroll-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/toolbar/scroll-left.png new file mode 100644 index 0000000000000000000000000000000000000000..ce5f0c0cd3ce36d6766bc25c66cc868ccf5eeed7 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/toolbar/scroll-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/toolbar/scroll-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/toolbar/scroll-right.png new file mode 100644 index 0000000000000000000000000000000000000000..721a683a0e8186434c43099e2fd7ccd69fc6b1ce Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/toolbar/scroll-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tools/tool-sprites-dark.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tools/tool-sprites-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..4e7fb18784f24c337867b7ce0fcc3db03e3a8254 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tools/tool-sprites-dark.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tools/tool-sprites.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tools/tool-sprites.png new file mode 100644 index 0000000000000000000000000000000000000000..4c21701adcc3f1268ac631b23a4c2a8b72ed333e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tools/tool-sprites.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/arrows-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/arrows-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..df41161335c114f0675a7e95edff67b1411d768f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/arrows-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/arrows.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/arrows.png new file mode 100644 index 0000000000000000000000000000000000000000..0e2b8b1f20e6cd0840d51b1f0894a5d507c21e4c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/arrows.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-above.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-above.png new file mode 100644 index 0000000000000000000000000000000000000000..ca89bfa626b94ae008aa088fc608d4e55693d928 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-above.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-add.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-add.gif new file mode 100644 index 0000000000000000000000000000000000000000..b22cd1448efa13c47ad6d3b75bdea8b4031c31e9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-add.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-append.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-append.png new file mode 100644 index 0000000000000000000000000000000000000000..ca89bfa626b94ae008aa088fc608d4e55693d928 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-append.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-below.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-below.png new file mode 100644 index 0000000000000000000000000000000000000000..ca89bfa626b94ae008aa088fc608d4e55693d928 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-below.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-between.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-between.png new file mode 100644 index 0000000000000000000000000000000000000000..ca89bfa626b94ae008aa088fc608d4e55693d928 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-between.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-no.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-no.gif new file mode 100644 index 0000000000000000000000000000000000000000..9d9c6a9ce1307c5ba072f08bf77d998bb1b716cb Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-no.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-over.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-over.gif new file mode 100644 index 0000000000000000000000000000000000000000..30d1ca7107816233884d23239dd76fce79237fe5 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-over.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-under.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-under.gif new file mode 100644 index 0000000000000000000000000000000000000000..85f66b1e584aece5a5d6d4cf062b8c1f63edce97 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-under.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-yes.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-yes.gif new file mode 100644 index 0000000000000000000000000000000000000000..8aacb307e89d690f46853e01f5c4726bd5d94e31 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/drop-yes.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-minus-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-minus-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..c8b7ff57fa9471098646f1bb0e5e84f0639f02a8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-minus-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-minus.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-minus.png new file mode 100644 index 0000000000000000000000000000000000000000..0902284a27d8be55431da8a4e76ef3bf1eded246 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-minus.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-plus-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-plus-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..a0d0e36cc63f061b757969ceb54548fd86c73c34 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-plus-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-plus.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-plus.png new file mode 100644 index 0000000000000000000000000000000000000000..19a3faadc815a526a7f9e5d5299e58a6ea611ed8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-plus.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..1e51148e17c6b15ee1a65f5ae8072358ccdf1c37 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end.png new file mode 100644 index 0000000000000000000000000000000000000000..80a897f775d6bc1bb733e5f4a47bf63dcacaa4ba Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-end.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-line-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-line-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ee273b6502615db08de372ed2035fa41526d3863 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-line-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-line.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-line.png new file mode 100644 index 0000000000000000000000000000000000000000..b655cbe1cc4a1eca9f4309db0507246a62cde2ab Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-line.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-minus-nl-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-minus-nl-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..fdc2afeb58ad97917c3098348e853efe462197ab Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-minus-nl-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-minus-nl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-minus-nl.png new file mode 100644 index 0000000000000000000000000000000000000000..2057568dcc890f7884428cfc27afa5208a1f4f5a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-minus-nl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-minus-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-minus-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..9ce1e710c242570a8d9b66cd94afb108469fbefe Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-minus-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-minus.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-minus.png new file mode 100644 index 0000000000000000000000000000000000000000..7d95b21efca23b054df1faef765b53f606e6163a Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-minus.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-plus-nl-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-plus-nl-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..c1100898e602cb4b2e6819e345cc20842a82a685 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-plus-nl-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-plus-nl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-plus-nl.png new file mode 100644 index 0000000000000000000000000000000000000000..860a688c26c4f6b78e98b680d70ba81819534d2b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-plus-nl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-plus-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-plus-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..c6d638b6e4976d384a1146c6281230414cc120b1 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-plus-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-plus.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-plus.png new file mode 100644 index 0000000000000000000000000000000000000000..7d32dba1714e5ea434784b14e981ce29f81e57bf Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-plus.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..52571b124c4d8ae21336fd3417264692d5980fe5 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow.png new file mode 100644 index 0000000000000000000000000000000000000000..ea022f4bdbc36884a1b93289850a178d1c7038a0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/elbow.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/folder-open-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/folder-open-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..ff5e878b21d061eb72465b8358d6f4996615b637 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/folder-open-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/folder-open.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/folder-open.png new file mode 100644 index 0000000000000000000000000000000000000000..d94c2751ad2bebcbb3dad76a20e7e4fdc6695ee5 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/folder-open.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/folder-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/folder-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..3b4f117e64567dbb2c2647e3da65b9add59d5445 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/folder-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/folder.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..3b4f117e64567dbb2c2647e3da65b9add59d5445 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/folder.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/leaf-rtl.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/leaf-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..2e4420c07abaf552181e2c3119b97e2020fb8d3e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/leaf-rtl.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/leaf.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/leaf.png new file mode 100644 index 0000000000000000000000000000000000000000..34528c5125908d81530934df20a2eafd27514b92 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/leaf.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/loading.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..81b0f125790bc94311f43e08dee4bb851729adc2 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/tree/loading.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/util/splitter/mini-bottom.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/util/splitter/mini-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..5144896cb9d2b9c0a0c1a10b65808bfd5831177b Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/util/splitter/mini-bottom.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/util/splitter/mini-left.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/util/splitter/mini-left.png new file mode 100644 index 0000000000000000000000000000000000000000..17faaed459a163a91f79901e1e612a92125b52e6 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/util/splitter/mini-left.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/util/splitter/mini-right.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/util/splitter/mini-right.png new file mode 100644 index 0000000000000000000000000000000000000000..94320746677d41d55dfe4d658972267e1365049f Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/util/splitter/mini-right.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/util/splitter/mini-top.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/util/splitter/mini-top.png new file mode 100644 index 0000000000000000000000000000000000000000..61f1c6b2a3cac3b4bc63c7b20830a6276ce1844e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/util/splitter/mini-top.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-bottom-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-bottom-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..0453c037e71d293aa5c14400cbe0afb3f3c9041c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-bottom-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-bottom-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-bottom-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..89790f21c114b226cad9f408352fd94d255051a9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-bottom-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-bottom-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-bottom-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..e048f70a410f961aa1a2b6441ea7d8affd10ab62 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-bottom-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-bottom-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-bottom-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..aa7614cc2a9cb51c568e2af2b8e8fb6faeb00493 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-bottom-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-left-corners-rtl.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-left-corners-rtl.gif new file mode 100644 index 0000000000000000000000000000000000000000..fad4570ffb3038e4229e230d5f1786e91719b485 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-left-corners-rtl.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-left-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-left-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..e048f70a410f961aa1a2b6441ea7d8affd10ab62 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-left-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-left-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-left-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..2df8b96358faa1bb9d15ea58ad92e6e0dded9913 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-left-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-right-corners-rtl.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-right-corners-rtl.gif new file mode 100644 index 0000000000000000000000000000000000000000..ed8ee39122453efd45bddb19cfcdbafd91e4025d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-right-corners-rtl.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-right-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-right-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..994c4e5d59bcdd5c38c963233ca7549c91377723 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-right-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-right-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-right-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..2df8b96358faa1bb9d15ea58ad92e6e0dded9913 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-right-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-top-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-top-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..e048f70a410f961aa1a2b6441ea7d8affd10ab62 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-top-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-top-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-top-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..aa7614cc2a9cb51c568e2af2b8e8fb6faeb00493 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-collapsed-top-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-left-corners-rtl.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-left-corners-rtl.gif new file mode 100644 index 0000000000000000000000000000000000000000..848e4c3e84c52acd4a49bbba5debc0709979a8a4 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-left-corners-rtl.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-left-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-left-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..0aacc8bcb27c8b3c0d7379d2ac3510d5e16a71f8 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-left-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-left-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-left-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..48d25b92eba36e89b6b12d0f876824cf1fc8f7db Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-left-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-right-corners-rtl.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-right-corners-rtl.gif new file mode 100644 index 0000000000000000000000000000000000000000..f608759dfb18cda02ee1207d62b3e076c3384f07 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-right-corners-rtl.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-right-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-right-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..ae00e38a37e97a25086f866d47110e9983761861 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-right-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-right-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-right-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..c8a59f6f8650b657485e131b8e2e97920f2a425c Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-right-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-top-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-top-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..5961e92fe5c76448948f5a6768186a02330920d0 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-top-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-top-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-top-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..89790f21c114b226cad9f408352fd94d255051a9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window-header/window-header-default-top-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window/window-default-corners.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window/window-default-corners.gif new file mode 100644 index 0000000000000000000000000000000000000000..c27f91071718bb93d7a30da8683bf4bb7336e055 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window/window-default-corners.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window/window-default-sides.gif b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window/window-default-sides.gif new file mode 100644 index 0000000000000000000000000000000000000000..05f56ff61fa211993f15e009a0b3d7ade1427bef Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/images/window/window-default-sides.gif differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/loading-debug.css b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/loading-debug.css new file mode 100644 index 0000000000000000000000000000000000000000..8fd51ebcba79a58b6df950927bf0c951720d85c7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/loading-debug.css @@ -0,0 +1,78 @@ +/** + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +body { + margin: 0; +} + +#loading-mask { + width: 100%; + height: 100%; + position: absolute; + left: 0; + top: 0; + background-color: black; + z-index: 20000; +} + +#loading { + width: 100%; + height: 100%; + position: absolute; + background-color: black; + margin: auto; + z-index: 20001; +} + +#loading-background { + width: 1024px; + height: 768px; + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 30%; + margin: auto; + background-image: url('images/loading-background.png'); + background-repeat: no-repeat; + background-position: center center; +} + +#loading-logo { + position: absolute; + bottom: 100px; + left: 120px; +} + +#loading-product { + position: absolute; + bottom: 150px; + left: 260px; +} + +#loading .loading-indicator { + position: absolute; + bottom: 110px; + left: 260px; + display: table; +} + +#loading-spinner { + vertical-align: text-bottom; +} + +#loading-msg { + color: white; + font: normal 18px "Helvetica Neue", Helvetica, Arial, sans-serif; + margin-left: 10px; +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-144x144.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..6ca07ef3a6c61e33d4ae649e9d66277d2cb706a9 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-144x144.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-150x150.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..bd2be50b81a8803b9f645428fa97aa2955db077e Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-150x150.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-310x150.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-310x150.png new file mode 100644 index 0000000000000000000000000000000000000000..ea359bd9c0ecafc6550e5572e52b30457bbaea36 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-310x150.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-310x310.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-310x310.png new file mode 100644 index 0000000000000000000000000000000000000000..f114ac2e5354b73d6285da4d7caad9c7036e6099 Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-310x310.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-70x70.png b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-70x70.png new file mode 100644 index 0000000000000000000000000000000000000000..9d6fbbbfda308c33dc15c47d1656b0c5e026a48d Binary files /dev/null and b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/mstile-70x70.png differ diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/nexus-rapture-debug.css b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/nexus-rapture-debug.css new file mode 100644 index 0000000000000000000000000000000000000000..a8d1849c1862728ddee8481621f189e1d02ce821 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/nexus-rapture-debug.css @@ -0,0 +1,26 @@ +/** + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Custom Ext.form.field.Trigger art. + * + * Leaving these here vs. moving to baseapp since art exists here for clarity. + */ + +.nx-form-fa-minus-circle-trigger { + background-image: url(images/form/fa-minus-circle-trigger.png); +} + +.nx-form-fa-times-circle-trigger { + background-image: url(images/form/fa-times-circle-trigger.png); +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/safari-pinned-tab.svg b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/safari-pinned-tab.svg new file mode 100644 index 0000000000000000000000000000000000000000..8921cc629b227dcd0715d742d92c284edfce7209 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/resources/static/rapture/resources/safari-pinned-tab.svg @@ -0,0 +1,36 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + + + + + + + + + + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/main/script/driver.groovy b/thirdparty-bundles/components/nexus-rapture/src/main/script/driver.groovy new file mode 100644 index 0000000000000000000000000000000000000000..7c4568e14d199cca97b712c42ae4f240cfecea15 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/main/script/driver.groovy @@ -0,0 +1,155 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * gmavenplus-plugin doesn't set System properties on project.properties, but they are available to the AntBuilder + */ +String systemProperty(String key) { + ant.properties.project.properties[key] +} + +String mode = systemProperty('mode') +assert mode: 'Missing property: mode' +log.info "Mode: $mode" + +// Sanity check sencha cmd +def senchaExe = 'sencha' +ant.exec(executable: senchaExe, failonerror: true) { + arg(line: 'which') +} + +def baseappDir = new File(project.basedir, 'src/main/baseapp') +def outputDir = new File(project.basedir, 'src/main/resources/static/rapture') + +/** + * Convert flavor to environment name. + */ +def flavorToEnv = {flavor -> + switch (flavor) { + case 'debug': + return 'testing' + case 'prod': + return 'production' + default: + throw Exception("Unknown flavor: $flavor") + } +} + +// +// mode=ext +// + +def do_ext = { + def extDir = new File(project.build.directory, 'ext') + if (extDir.exists()) { + return + } + + def extZip = project.artifactMap['com.sencha:ext'].file + ant.mkdir(dir: extDir) + ant.unzip(src: extZip, dest: extDir) { + cutdirsmapper(dirs: 1) + patternset { + exclude(name: 'ext-*/docs/**') + exclude(name: 'ext-*/welcome/**') + } + } + + // HACK: Ext 4.2 needs special handling for Ext.ux sources, as the ext-ux package is 5+ + ant.copy(todir: "$extDir/src") { + fileset(dir: "$extDir/examples/ux") { + include(name: '**/*') + } + } +} + +// +// mode=clobber +// + +def do_clobber = { + ant.delete { + fileset(dir: outputDir) { + include(name: 'baseapp-*.js') + include(name: 'ignore.html') + include(name: 'resources/baseapp-*.css') + include(name: 'resources/Readme.md') + include(name: 'resources/font-awesome/**') + } + + fileset(dir: "$outputDir/resources/images") { + exclude(name: '*') + exclude(name: 'form/fa-*') + } + } +} + +// +// mode=build +// + +def do_build = { + def flavors = systemProperty('flavors') + if (flavors) { + flavors = flavors.split(',') + } + else { + flavors = ['debug', 'prod'] + } + log.info "Flavors: $flavors" + + do_ext() + + flavors.each { flavor -> + def env = flavorToEnv flavor + ant.exec(executable: senchaExe, dir: baseappDir, failonerror: true) { + arg(line: "app build $env") + } + } +} + +// +// mode=watch +// + +def do_watch = { + do_ext() + + ant.exec(executable: senchaExe, dir: baseappDir, failonerror: true) { + arg(line: "app watch testing") + } +} + +// switch mode +switch (mode) { + case 'ext': + do_ext() + break + + case 'clobber': + do_clobber() + break + + case 'build': + do_build() + break + + case 'watch': + do_watch() + break + + default: + throw new Exception("Unknown mode: $mode") +} + + diff --git a/thirdparty-bundles/components/nexus-rapture/src/test/java/org/sonatype/nexus/rapture/HashingTrial.groovy b/thirdparty-bundles/components/nexus-rapture/src/test/java/org/sonatype/nexus/rapture/HashingTrial.groovy new file mode 100644 index 0000000000000000000000000000000000000000..52f4c3b56e3bc078a50d25bd172b709149703a46 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/test/java/org/sonatype/nexus/rapture/HashingTrial.groovy @@ -0,0 +1,45 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture + +import java.nio.charset.StandardCharsets + +import org.sonatype.goodies.testsupport.TestSupport + +import com.google.common.hash.Hashing +import com.google.gson.GsonBuilder +import org.junit.Test + +/** + * Hashing trials. + */ +class HashingTrial + extends TestSupport +{ + @Test + void 'test gson hash'() { + def gson = new GsonBuilder().create() + def data = ['a', 'b', 'c'] + def hash = Hashing.sha1().hashString(gson.toJson(data), StandardCharsets.UTF_8).toString(); + log hash + assert hash == 'e13460afb1e68af030bb9bee8344c274494661fa' + } + + @Test + void 'test object hash'() { + def data = ['a', 'b', 'c'] + log data.hashCode() + data << 'd' + log data.hashCode() + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/test/java/org/sonatype/nexus/rapture/internal/state/DatabaseStateContributorTest.java b/thirdparty-bundles/components/nexus-rapture/src/test/java/org/sonatype/nexus/rapture/internal/state/DatabaseStateContributorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..808207481cabe3665c93ce36104738ecf147bb39 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/test/java/org/sonatype/nexus/rapture/internal/state/DatabaseStateContributorTest.java @@ -0,0 +1,79 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rapture.internal.state; + +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.orient.freeze.DatabaseFreezeService; +import org.sonatype.nexus.orient.freeze.ReadOnlyState; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static com.google.common.collect.ImmutableMap.of; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.when; + +public class DatabaseStateContributorTest + extends TestSupport +{ + + DatabaseStateContributor underTest; + + @Mock + DatabaseFreezeService databaseFreezeService; + + @Before + public void setup() { + underTest = new DatabaseStateContributor(databaseFreezeService); + } + + @Test + public void testGetState() { + when(databaseFreezeService.getReadOnlyState()).thenReturn(state(true, "", false)); + assertThat(underTest.getState().get("db"), is(expected(true,"", false))); + + when(databaseFreezeService.getReadOnlyState()).thenReturn(state(false, "", false)); + assertThat(underTest.getState().get("db"), is(expected(false,"", false))); + } + + ReadOnlyState state(boolean frozen, String message, boolean system) { + return new ReadOnlyState() + { + @Override + public boolean isFrozen() { + return frozen; + } + + @Override + public String getSummaryReason() { + return message; + } + + @Override + public boolean isSystemInitiated() { + return system; + } + }; + } + + Map expected(boolean frozen, String message, boolean system) { + return of("dbFrozen", frozen, + "system", system, + "reason", message + ); + } +} diff --git a/thirdparty-bundles/components/nexus-rapture/src/test/resources/jasmine/NX-test.js b/thirdparty-bundles/components/nexus-rapture/src/test/resources/jasmine/NX-test.js new file mode 100644 index 0000000000000000000000000000000000000000..27c8d5f0ca64d6c98eb9cded8286a0449eaebe3e --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/test/resources/jasmine/NX-test.js @@ -0,0 +1,31 @@ +Ext.ns('NX'); + +NX.global = (function() { + if (window !== undefined) { + return window; + } + if (global !== undefined) { + return global; + } + Ext.Error.raise('Unable to determine global object'); +}()); + +Ext.Loader.setConfig({ + enabled: true, + paths: { + NX: './src/NX' + } +}); + +(function() { + var method = Ext.Loader.getPath; + Ext.Loader.getPath = function() { + var path = method.apply(this, arguments); + if (path.indexOf('static/rapture/NX') === 0) { + return path.replace('static/rapture/NX', './src/NX'); + } + else { + return path; + } + }; +})(); diff --git a/thirdparty-bundles/components/nexus-rapture/src/test/resources/jasmine/static/rapture/NX/Assert-spec.js b/thirdparty-bundles/components/nexus-rapture/src/test/resources/jasmine/static/rapture/NX/Assert-spec.js new file mode 100644 index 0000000000000000000000000000000000000000..619f2732428b316c8dccdf0cf191a210738240f6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/test/resources/jasmine/static/rapture/NX/Assert-spec.js @@ -0,0 +1,23 @@ +describe('Assert', function() { + beforeAll(function(done) { + Ext.onReady(done); + }); + + describe('assert', function() { + beforeEach(function() { + spyOn(NX.Console, 'error'); + }); + + it('does nothing when disabled', function() { + NX.Assert.disable = true; + NX.Assert.assert(1 !== 1, '1 is not 1!?'); + NX.Assert.disable = false; + expect(NX.Console.error).not.toHaveBeenCalled(); + }); + + it('logs an error when the expression is false', function() { + NX.Assert.assert(1 !== 1, '1 is not 1!?'); + expect(NX.Console.error).toHaveBeenCalledWith('Assertion failure:', '1 is not 1!?'); + }); + }); +}); diff --git a/thirdparty-bundles/components/nexus-rapture/src/test/resources/jasmine/static/rapture/NX/Il8n-spec.js b/thirdparty-bundles/components/nexus-rapture/src/test/resources/jasmine/static/rapture/NX/Il8n-spec.js new file mode 100644 index 0000000000000000000000000000000000000000..bc5de667980e9098410b3ea0b424931333a30c17 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/test/resources/jasmine/static/rapture/NX/Il8n-spec.js @@ -0,0 +1,69 @@ +describe('I18n', function() { + beforeAll(function(done) { + Ext.onReady(function() { + NX.I18n.register({ + keys: { + _test_key: 'value', + _test_reference: '@_test_key', + _test_missing_reference: '@_test_missing_key', + _test_formatted: '{0}' + }, + bundles: { + _test_bundle: { + _test_key: 'value', + _test_reference: '@_test_bundle:_test_key', + _test_missing_reference: '@_test_bundle:_test_missing_reference_target' + } + } + }); + + done(); + }); + }); + + describe('get', function() { + it('by key', function() { + expect(NX.I18n.get('_test_key')).toBe('value'); + }); + + it('by reference', function() { + expect(NX.I18n.get('_test_reference')).toBe('value'); + }); + + it('missing by key', function() { + expect(NX.I18n.get('_test_missing')).toBe('MISSING_I18N:_test_missing'); + }); + + it('missing by reference', function() { + expect(NX.I18n.get('_test_missing_reference')).toBe('MISSING_I18N:_test_missing_key'); + }); + }); + + describe('format', function() { + it('by key', function() { + expect(NX.I18n.format('_test_formatted', 'test')).toBe('test'); + }); + + it('missing by key', function() { + expect(NX.I18n.format('_test_missing', 'test')).toBe('MISSING_I18N:_test_missing'); + }); + }); + + describe('render', function() { + it('matching key', function() { + expect(NX.I18n.render('_test_bundle', '_test_key')).toBe('value'); + }); + + it('matching reference', function() { + expect(NX.I18n.render('_test_bundle', '_test_reference')).toBe('value'); + }); + + it('missing by key', function() { + expect(NX.I18n.render('_test_bundle', '_test_missing_key')).toBe('MISSING_I18N:_test_bundle:_test_missing_key'); + }); + + it('missing by reference', function() { + expect(NX.I18n.render('_test_bundle', '_test_missing_reference')).toBe('MISSING_I18N:_test_bundle:_test_missing_reference_target'); + }); + }); +}); \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rapture/src/test/resources/jasmine/static/rapture/NX/controller/Permissions-spec.js b/thirdparty-bundles/components/nexus-rapture/src/test/resources/jasmine/static/rapture/NX/controller/Permissions-spec.js new file mode 100644 index 0000000000000000000000000000000000000000..9df3d8a571c55470142fc23011114a9ca538b53d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rapture/src/test/resources/jasmine/static/rapture/NX/controller/Permissions-spec.js @@ -0,0 +1,32 @@ +describe('NX.controller.Permissions', function() { + var data = [ + { + id: 'permission1', + permitted: 'false' + } + ]; + + beforeAll(function(done) { + Ext.onReady(function() { + NX.store.Permission.addMembers({ + proxy: { + type: 'memory', + data: data, + reader: { + type: 'json' + } + } + }); + + done(); + }); + }); + + describe('fetchPermissions', function() { + it('loads data from the memory proxy', function() { + var permissions = Ext.create('NX.controller.Permissions'); + permissions.fetchPermissions(); + expect(permissions.getStore('Permission').getById('permission1').get('permitted')).toBe(false); + }); + }); +}); diff --git a/thirdparty-bundles/components/nexus-repository/pom.xml b/thirdparty-bundles/components/nexus-repository/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..a4a6d23384aaf6b430ce2703eb390085be56abfa --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/pom.xml @@ -0,0 +1,214 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.8.0-02 + + + nexus-repository + ${project.groupId}:${project.artifactId} + bundle + + + + org.sonatype.nexus + nexus-common + + + + org.sonatype.nexus + nexus-audit + + + + org.sonatype.nexus + nexus-capability + + + + org.sonatype.nexus + nexus-jmx + + + + org.sonatype.nexus + nexus-mime + + + + com.fasterxml.jackson.core + jackson-databind + + + + org.sonatype.nexus + nexus-validation + + + + org.sonatype.nexus + nexus-orient + + + + org.sonatype.nexus + nexus-blobstore + + + + org.sonatype.nexus + nexus-blobstore-api + + + + org.sonatype.nexus + nexus-blobstore-file + + + + org.sonatype.nexus + nexus-httpclient + + + + org.sonatype.nexus + nexus-security + + + + org.sonatype.nexus + nexus-elasticsearch + + + + org.sonatype.nexus + nexus-transaction + + + + org.sonatype.nexus + nexus-scheduling + + + + org.sonatype.nexus + nexus-selector + + + + org.sonatype.nexus + nexus-commands + + + + org.sonatype.nexus + nexus-webhooks + + + + org.sonatype.nexus + nexus-rest + provided + + + + org.sonatype.nexus + nexus-swagger + provided + + + + io.swagger + swagger-annotations + + + + org.jboss.resteasy + resteasy-multipart-provider + + + + org.sonatype.goodies + goodies-testsupport + test + + + + org.spockframework + spock-core + test + + + + cglib + cglib-nodep + test + + + + org.objenesis + objenesis + test + + + + org.slf4j + jul-to-slf4j + test + + + + javax.transaction + javax.transaction-api + test + + + + org.jboss.resteasy + resteasy-jaxrs + test + + + + com.jayway.awaitility + awaitility + test + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + + + diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/AttributeChange.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/AttributeChange.java new file mode 100644 index 0000000000000000000000000000000000000000..bb7ce41e38825a3c2a6bbe52c67c8f7396aa7549 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/AttributeChange.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.repository.attributes.AttributesFacet; + +/** + * A command that modifies a {@link Repository}'s {@link AttributesFacet#getAttributes() attributes}. + * + * @since 3.0 + */ +public interface AttributeChange +{ + /** + * Operations must have no side effects as they may be retried by the transaction infrastructure. + */ + void apply(NestedAttributesMap attributes); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/BadRequestException.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/BadRequestException.java new file mode 100644 index 0000000000000000000000000000000000000000..a05e97aee006f3130fefb69b0a3fddb9e583c83c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/BadRequestException.java @@ -0,0 +1,24 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +/** + * @since 3.3 + */ +public class BadRequestException + extends RuntimeException +{ + public BadRequestException(final String message) { + super(message); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Facet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Facet.java new file mode 100644 index 0000000000000000000000000000000000000000..cca7ef0c772064696f475fd68dac6daad86da564 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Facet.java @@ -0,0 +1,100 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import org.sonatype.nexus.repository.config.Configuration; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Repository facet. + * + * @since 3.0 + */ +public interface Facet +{ + /** + * Attach repository to facet. + * + * @see Repository#attach(Facet) + */ + void attach(Repository repository) throws Exception; + + /** + * Validate configuration. + */ + void validate(Configuration configuration) throws Exception; + + /** + * Initialize facet. + */ + void init() throws Exception; + + /** + * Update facet. + * + * Called when repository configuration has changed. + */ + void update() throws Exception; + + /** + * Start facet. + * + * Facet has been previously initialized or updated. + */ + void start() throws Exception; + + /** + * Stop facet. + * + * Facet was previously started. Facet is stopped before applying {@link #update}. + */ + void stop() throws Exception; + + /** + * Delete facet. + * + * Allows facet to cope with contained repository being deleted and clean up persistent knowledge about the + * repository or its contents. + * + * Facet must have been previously stopped. + */ + void delete() throws Exception; + + /** + * Destroy facet. + * + * Allows facet to clean up resources. This is different than {@link #delete}. + * + * Facet is stopped before destruction. + */ + void destroy() throws Exception; + + /** + * Marks {@link Facet} interface and/or implementation as exposed. + * + * Exposed types can be accessed via {@link Repository#facet}. + */ + @Documented + @Target(TYPE) + @Retention(RUNTIME) + @interface Exposed + { + // marker + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/FacetSupport.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/FacetSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..11b8adc175056c8a77b7d60c9bd62b32882e7d00 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/FacetSupport.java @@ -0,0 +1,235 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +import java.util.Optional; + +import javax.annotation.Nonnull; +import javax.inject.Inject; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.event.EventBus; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuard; +import org.sonatype.nexus.common.stateguard.StateGuardAware; +import org.sonatype.nexus.common.stateguard.Transitions; +import org.sonatype.nexus.repository.config.Configuration; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.FacetSupport.State.ATTACHED; +import static org.sonatype.nexus.repository.FacetSupport.State.DELETED; +import static org.sonatype.nexus.repository.FacetSupport.State.DESTROYED; +import static org.sonatype.nexus.repository.FacetSupport.State.FAILED; +import static org.sonatype.nexus.repository.FacetSupport.State.INITIALISED; +import static org.sonatype.nexus.repository.FacetSupport.State.NEW; +import static org.sonatype.nexus.repository.FacetSupport.State.STARTED; +import static org.sonatype.nexus.repository.FacetSupport.State.STOPPED; + +/** + * Support for {@link Facet} implementations. + * + * @since 3.0 + */ +public abstract class FacetSupport + extends ComponentSupport + implements Facet, StateGuardAware +{ + private EventManager eventManager; + + @Inject + public void installDependencies(final EventManager eventManager) { + this.eventManager = checkNotNull(eventManager); + } + + protected EventManager getEventManager() { + return checkNotNull(eventManager); + } + + private Repository repository; + + protected Repository getRepository() { + return checkNotNull(repository); + } + + // + // State + // + + public static final class State + { + public static final String NEW = "NEW"; + + public static final String ATTACHED = "ATTACHED"; + + public static final String INITIALISED = "INITIALISED"; + + public static final String STARTED = "STARTED"; + + public static final String STOPPED = "STOPPED"; + + public static final String DELETED = "DELETED"; + + public static final String DESTROYED = "DESTROYED"; + + public static final String FAILED = "FAILED"; + } + + protected final StateGuard states = new StateGuard.Builder() + .logger(createLogger()) + .initial(NEW) + .failure(FAILED) + .create(); + + @Override + @Nonnull + public StateGuard getStateGuard() { + return states; + } + + // + // Lifecycle + // + + @Override + @Transitions(from = NEW, to = ATTACHED) + public void attach(final Repository repository) throws Exception { + this.repository = checkNotNull(repository); + } + + @Override + @Guarded(by = {ATTACHED, STARTED, STOPPED}) + public void validate(final Configuration configuration) throws Exception { + doValidate(configuration); + } + + /** + * Sub-class should override to provide configuration validation logic. + */ + protected void doValidate(final Configuration configuration) throws Exception { + // nop + } + + /** + * Common init/update configuration extension-point. + * + * By default this is called on {@link #init} and {@link #update} + * unless sub-class overrides {@link #doInit} or {@link #doUpdate}. + */ + protected void doConfigure(final Configuration configuration) throws Exception { + // nop + } + + @Override + @Transitions(from = ATTACHED, to = INITIALISED) + public void init() throws Exception { + doInit(getRepository().getConfiguration()); + } + + protected void doInit(final Configuration configuration) throws Exception { + doConfigure(configuration); + } + + @Override + @Guarded(by = STOPPED) + public void update() throws Exception { + doUpdate(getRepository().getConfiguration()); + } + + protected void doUpdate(final Configuration configuration) throws Exception { + doConfigure(configuration); + } + + @Override + @Transitions(from = {INITIALISED, STOPPED}, to = STARTED) + public void start() throws Exception { + doStart(); + eventManager.register(this); + } + + protected void doStart() throws Exception { + // nop + } + + @Override + @Transitions(from = STARTED, to = STOPPED) + public void stop() throws Exception { + eventManager.unregister(this); + doStop(); + } + + protected void doStop() throws Exception { + // nop + } + + @Override + @Transitions(from = STOPPED, to = DELETED) + public void delete() throws Exception { + doDelete(); + } + + protected void doDelete() throws Exception { + // nop + } + + @Override + @Transitions(to = DESTROYED) + public void destroy() throws Exception { + if (states.is(STARTED)) { + stop(); + } + + doDestroy(); + this.repository = null; + } + + protected void doDestroy() throws Exception { + // nop + } + + // + // Helpers + // + + /** + * Lookup a facet on attached repository. + * + * Reduce some verbosity for commonly used repository operation. + * + * @see Repository#facet(Class) + */ + @Nonnull + protected T facet(final Class type) throws MissingFacetException { + return getRepository().facet(type); + } + + /** + * Lookup an {@link Optional} facet on attached repository. + * + * Reduce some verbosity for commonly used repository operation. + * + * @see Repository#optionalFacet(Class) + */ + @Nonnull + protected Optional optionalFacet(final Class type) { + return getRepository().optionalFacet(type); + } + + /** + * @deprecated use {@link EventManager} instead + */ + @Deprecated + protected EventBus getEventBus() { + return eventManager; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Format.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Format.java new file mode 100644 index 0000000000000000000000000000000000000000..15a78b3913cc8c7e408e6329b5f59c5f74d6d71c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Format.java @@ -0,0 +1,61 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Repository format symbol. + * + * @since 3.0 + */ +public abstract class Format +{ + private final String value; + + public Format(final String value) { + this.value = checkNotNull(value); + } + + public String getValue() { + return value; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Format format = (Format) o; + + if (!value.equals(format.value)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + return value; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/HasFacet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/HasFacet.java new file mode 100644 index 0000000000000000000000000000000000000000..7f405ad446db37c6f4b90a5bedfb6d67cc016246 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/HasFacet.java @@ -0,0 +1,44 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +import java.util.function.Predicate; + +/** + * {@link Predicate} that filters repositories according to whether they have a given facet. + * + * @since 3.0 + */ +public class HasFacet + implements Predicate, com.google.common.base.Predicate +{ + private final Class type; + + public HasFacet(final Class type) { + this.type = type; + } + + @Override + public boolean apply(final Repository input) { + return test(input); + } + + @Override + public boolean test(final Repository input) { + return input.optionalFacet(type).isPresent(); + } + + public static HasFacet hasFacet(final Class type) { + return new HasFacet(type); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/IllegalOperationException.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/IllegalOperationException.java new file mode 100644 index 0000000000000000000000000000000000000000..82fde5b093aaeb45e926d2bfdb20d07d84222b2b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/IllegalOperationException.java @@ -0,0 +1,30 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +/** + * Thrown when an illegal operation has been encountered. + * + * @since 3.0 + */ +public class IllegalOperationException + extends RuntimeException +{ + public IllegalOperationException(final String message) { + super(message); + } + + public IllegalOperationException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/InvalidContentException.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/InvalidContentException.java new file mode 100644 index 0000000000000000000000000000000000000000..1ad20ce318255addbb0635bc6e93a54330896c57 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/InvalidContentException.java @@ -0,0 +1,34 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +/** + * Content is of an incorrect or indeterminate type. + * + * @since 3.0 + */ +public class InvalidContentException + extends RuntimeException +{ + public InvalidContentException(final String message) { + super(message); + } + + public InvalidContentException(final String message, final Throwable cause) { + super(message, cause); + } + + public InvalidContentException(final Throwable cause) { + super(cause); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/MissingFacetException.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/MissingFacetException.java new file mode 100644 index 0000000000000000000000000000000000000000..5de60956505798af86de30881593335cf9de875d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/MissingFacetException.java @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +/** + * Thrown when requested {@link Facet} does not exist. + * + * @since 3.0 + */ +public class MissingFacetException + extends RuntimeException +{ + public MissingFacetException(final Repository repository, final Class type) { + super(String.format("No facet of type %s attached to repository %s", type.getSimpleName(), repository.getName())); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Recipe.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Recipe.java new file mode 100644 index 0000000000000000000000000000000000000000..aed2f80aedb63555f8bba4532640ad665c27aa1e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Recipe.java @@ -0,0 +1,31 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +import javax.annotation.Nonnull; + +/** + * Repository recipe. + * + * @since 3.0 + */ +public interface Recipe +{ + Type getType(); + + Format getFormat(); + + void apply(@Nonnull Repository repository) throws Exception; + + boolean isFeatureEnabled(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RecipeSupport.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RecipeSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..31c4f0d0ebb56baf779967ccacde3bb650af5997 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RecipeSupport.java @@ -0,0 +1,78 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +import javax.inject.Inject; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.view.Router; +import org.sonatype.nexus.repository.view.handlers.BrowseUnsupportedHandler; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Support for {@link Recipe} implementations. + * + * @since 3.0 + */ +public abstract class RecipeSupport + extends ComponentSupport + implements Recipe +{ + private final Format format; + + private final Type type; + + private BrowseUnsupportedHandler browseUnsupportedHandler; + + protected RecipeSupport(final Type type, final Format format) { + this.type = checkNotNull(type); + this.format = checkNotNull(format); + } + + @Override + public Format getFormat() { + return format; + } + + @Override + public Type getType() { + return type; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "format=" + format + + ", type=" + type + + '}'; + } + + @Inject + public void setBrowseUnsupportedHandler(final BrowseUnsupportedHandler browseUnsupportedHandler) { + this.browseUnsupportedHandler = checkNotNull(browseUnsupportedHandler); + } + + /** + * Adds route to redirect access directly with a browser to a handler with links to the repo's components and + * assets. + */ + protected void addBrowseUnsupportedRoute(Router.Builder builder) { + builder.route(browseUnsupportedHandler.getRoute()); + } + + @Override + public boolean isFeatureEnabled() { + return true; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Repository.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Repository.java new file mode 100644 index 0000000000000000000000000000000000000000..6a43d2d51c3016b5856efe553c5600668ea61a90 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Repository.java @@ -0,0 +1,124 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +import java.util.Optional; + +import javax.annotation.Nonnull; + +import org.sonatype.nexus.repository.config.Configuration; + +/** + * Repository. + * + * @since 3.0 + */ +public interface Repository +{ + /** + * Returns the type of the repository. + */ + Type getType(); + + /** + * Returns the format of the repository. + */ + Format getFormat(); + + /** + * Returns the unique name of the repository. + */ + String getName(); + + /** + * Validate repository configuration. + */ + void validate(Configuration configuration) throws Exception; + + /** + * Initialize the repository. + * + * Called when a new repository is created or a repository is restored from persistent storage on startup. + */ + void init(Configuration configuration) throws Exception; + + /** + * Update the repository. + * + * Called when the repository configuration changes. Repository has already been initialized. + */ + void update(Configuration configuration) throws Exception; + + /** + * Start the repository. + * + * Repository has already been initialized or updated. + */ + void start() throws Exception; + + /** + * Stop the repository. + * + * Repository must have been previously started. Repository is stopped before applying {@link #update}. + */ + void stop() throws Exception; + + /** + * Delete the repository and remove all persistent knowledge about repository and its contents. + * + * Repository must have been previously stopped. + */ + void delete() throws Exception; + + /** + * Destroy the repository. + * + * Allows repository to clean up resources. This is different than {@link #delete}. + * + * Repository is stopped before destruction. + */ + void destroy() throws Exception; + + /** + * Returns the configuration entity for the repository. + * + * Only available after {@link #init(Configuration)} lifecycle-operation. + */ + Configuration getConfiguration(); + + /** + * Attach a facet to the repository. + * + * Only allowed before repository {@link #init(Configuration)} lifecycle-operation. + */ + void attach(Facet facet) throws Exception; + + /** + * Returns a facet instance for the given type. + * + * @throws MissingFacetException Request facet type was not previously attached. + */ + @Nonnull + T facet(Class type) throws MissingFacetException; + + /** + * Returns an {@link Optional} facet instance for the given type. + */ + @Nonnull + Optional optionalFacet(Class type); + + /** + * Returns the URL for the root of the repository, without its trailing slash. + */ + String getUrl(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryDestroyedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryDestroyedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..45faf7a7beafa0f6d258b1077c4bad406eb29946 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryDestroyedEvent.java @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +/** + * Emitted when a repository has been destroyed. + * + * @since 3.0 + */ +public class RepositoryDestroyedEvent + extends RepositoryEvent +{ + public RepositoryDestroyedEvent(final Repository repository) { + super(repository); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..624ff034249434c0f80ac051e7f98efbd0524e45 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryEvent.java @@ -0,0 +1,40 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Repository event. + * + * @since 3.0 + */ +public abstract class RepositoryEvent +{ + private final Repository repository; + + public RepositoryEvent(final Repository repository) { + this.repository = checkNotNull(repository); + } + + public Repository getRepository() { + return repository; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "repository=" + repository + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryStartedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryStartedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..bad1fb215774ffd909086e1552a2a55f8231e14b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryStartedEvent.java @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +/** + * Emitted when a repository has been started. + * + * @since 3.0 + */ +public class RepositoryStartedEvent + extends RepositoryEvent +{ + public RepositoryStartedEvent(final Repository repository) { + super(repository); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryStoppedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryStoppedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..8efed75f5cf51c61379963fb43681a8b953621b8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryStoppedEvent.java @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +/** + * Emitted when a repository has been stopped. + * + * @since 3.0 + */ +public class RepositoryStoppedEvent + extends RepositoryEvent +{ + public RepositoryStoppedEvent(final Repository repository) { + super(repository); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryTaskSupport.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryTaskSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..ae2abc514c1ba1236655c86f7efa121965d34b42 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/RepositoryTaskSupport.java @@ -0,0 +1,150 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.goodies.common.MultipleFailures; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.types.GroupType; +import org.sonatype.nexus.scheduling.TaskInterruptedException; +import org.sonatype.nexus.scheduling.TaskSupport; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * Support for tasks that applies to repositories. + * + * If task is configured to run against a repository group (or all repositories) the repository list will be exploded + * such that the task is run against all repositories referenced in all groups including the group repository itself. + * Thus, task business logic should not need to process group members themselves + * + * @since 3.0 + */ +@Named +public abstract class RepositoryTaskSupport + extends TaskSupport +{ + public static final String REPOSITORY_NAME_FIELD_ID = "repositoryName"; + + private RepositoryManager repositoryManager; + + private Type groupType; + + private Set processedRepositories; + + @Inject + public void install(final RepositoryManager repositoryManager, @Named(GroupType.NAME) final Type groupType) { + this.repositoryManager = checkNotNull(repositoryManager); + this.groupType = checkNotNull(groupType, "repository group type required"); + } + + @Override + protected Object execute() throws Exception { + processedRepositories = Sets.newHashSet(); + MultipleFailures failures = new MultipleFailures(); + for (Repository repository : findRepositories()) { + if (isCanceled()) { + break; + } + + try { + execute(repository); + } + catch (TaskInterruptedException e) { // NOSONAR + cancel(); // potentially redundant call to ensure loop exits + break; + } + catch (Exception e) { + log.error("Failed to run task '{}' on repository '{}'", getMessage(), repository.getName(), e); + failures.add(e); + } + } + + if (isCanceled()) { + log.warn("Task '{}' was canceled", getMessage()); + } + + failures.maybePropagate(String.format("Failed to run task '%s'", getMessage())); + return null; + } + + @Nonnull + private Iterable findRepositories() { + final String repositoryName = getRepositoryField(); + checkArgument(!Strings.isNullOrEmpty(repositoryName)); + if ("*".equals(repositoryName)) { + return Iterables.filter(repositoryManager.browse(), this::appliesTo); + } + else { + Repository repository = checkNotNull(repositoryManager.get(repositoryName)); + checkState(appliesTo(repository)); + return ImmutableList.of(repository); + } + } + + /** + * Extract repository field out of configuration. + */ + protected String getRepositoryField() { + return getConfiguration().getString(REPOSITORY_NAME_FIELD_ID); + } + + /** + * Returns true if the repository is of type {@link GroupType} + * + * @since 3.6.1 + */ + protected boolean isGroupRepository(final Repository repository) { + return groupType.equals(repository.getType()); + } + + /** + * Tracks processed repositories + * + * @since 3.6.1 + */ + protected void markProcessed(final Repository repository) { + processedRepositories.add(repository); + } + + /** + * Returns true if the specified repository has already been processed during task execution + * + * @since 3.6.1 + */ + protected boolean hasBeenProcessed(final Repository repository) { + return processedRepositories.contains(repository); + } + + /** + * Execute against specified repository. + */ + protected abstract void execute(final Repository repository); + + /** + * Return true if the task should be run against specified repository. + */ + protected abstract boolean appliesTo(final Repository repository); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Type.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Type.java new file mode 100644 index 0000000000000000000000000000000000000000..8593018dbd4ada4a38db4ebdbc6bf1cf2a27ec07 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/Type.java @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Repository type symbol. + * + * @since 3.0 + */ +public abstract class Type +{ + private final String value; + + public Type(final String value) { + this.value = checkNotNull(value); + } + + public String getValue() { + return value; + } + + public abstract Class getValidationGroup(); + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Type type = (Type) o; + + if (!value.equals(type.value)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + return value; + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadCountStore.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadCountStore.java new file mode 100644 index 0000000000000000000000000000000000000000..1cedab8913bd2cd015a82570f8b653c83f6cd206 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadCountStore.java @@ -0,0 +1,82 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount; + +import org.joda.time.DateTime; + +/** + * Store maintaining counts of downloads of assets, per repository + * + * @since 3.3 + */ +public interface AssetDownloadCountStore +{ + /** + * Get the count of downloads of an asset for the specified day + */ + long getDailyCount(final String repositoryName, final String assetName, final DateTime date); + + /** + * Get the total number of downloads in the last thirty days + */ + long getLastThirtyDays(final String repositoryName, final String assetName); + + /** + * Get the count of downloads of an asset for the specified month + */ + long getMonthlyCount(final String repositoryName, final String assetName, final DateTime date); + + /** + * Get the counts for the last X days + */ + long[] getDailyCounts(final String repositoryName, final String assetName); + + /** + * Get the counts for the last X months + */ + long[] getMonthlyCounts(final String repositoryName, final String assetName); + + /** + * Increment the daily/monthly count of an asset + */ + void incrementCount(final String repositoryName, final String assetName); + + /** + * Set vulnerable count data for a specified date + */ + void setMonthlyVulnerableCount(final String repositoryName, + final DateTime date, + final long count); + + /** + * Set count data for a specified date + */ + void setMonthlyCount(final String repositoryName, + final DateTime date, + final long count); + + /** + * Get vulnerable download count data for each of the last 12 months + */ + long[] getMonthlyVulnerableCounts(final String repositoryName); + + /** + * Get download count data for each of the last 12 months + */ + long[] getMonthlyCounts(final String repositoryName); + + /** + * Check if the feature is enabled + */ + boolean isEnabled(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/DateType.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/DateType.java new file mode 100644 index 0000000000000000000000000000000000000000..b3373040a35f8f4ca933ffe56ea0301b8d75448a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/DateType.java @@ -0,0 +1,86 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount; + +import org.sonatype.nexus.repository.assetdownloadcount.internal.DateUtils; + +import com.google.common.annotations.VisibleForTesting; +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * @since 3.3 + */ +@VisibleForTesting +public enum DateType +{ + DAY(30), //we keep the last 30 days of DAY type records + MONTH(14), //we keep the last 14 months of MONTH type records + MONTH_WHOLE_REPO(14), //these records are sums for the whole repository + MONTH_WHOLE_REPO_VULNERABLE(14); //these records are sums for the whole repository + + private static final String UNIMPLEMENTED_DATE_TYPE_ENCOUNTERED = "Unimplemented DateType encountered "; + + private final int numberToKeep; + + DateType(final int numberToKeep) { + this.numberToKeep = numberToKeep; + } + + public int getNumberToKeep() { + return numberToKeep; + } + + public DateTime standardizeDate(final DateTime date) { + checkNotNull(date); + switch (this) { + case DAY: + return DateUtils.clearTime(date); + case MONTH: + case MONTH_WHOLE_REPO: + case MONTH_WHOLE_REPO_VULNERABLE: + return DateUtils.clearDayAndTime(date); + default: + throw new IllegalArgumentException(UNIMPLEMENTED_DATE_TYPE_ENCOUNTERED + this.name()); + } + } + + public DateTime toOldestDate(final DateTime date) { + checkNotNull(date); + switch (this) { + case DAY: + return date.minusDays(getNumberToKeep()); + case MONTH: + case MONTH_WHOLE_REPO: + case MONTH_WHOLE_REPO_VULNERABLE: + return date.minusMonths(getNumberToKeep()); + default: + throw new IllegalArgumentException(UNIMPLEMENTED_DATE_TYPE_ENCOUNTERED + this.name()); + } + } + + public DateTime decrement(final DateTime date) { + checkNotNull(date); + switch (this) { + case DAY: + return date.minusDays(1); + case MONTH: + case MONTH_WHOLE_REPO: + case MONTH_WHOLE_REPO_VULNERABLE: + return date.minusMonths(1); + default: + throw new IllegalArgumentException(UNIMPLEMENTED_DATE_TYPE_ENCOUNTERED + this.name()); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadCount.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadCount.java new file mode 100644 index 0000000000000000000000000000000000000000..acf26290cf4af000dd725e94a87199945d4fdebe --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadCount.java @@ -0,0 +1,100 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount.internal; + +import org.sonatype.nexus.common.entity.AbstractEntity; +import org.sonatype.nexus.repository.assetdownloadcount.DateType; + +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Count of asset downloads at specified time frame. + * + * @since 3.3 + */ +public class AssetDownloadCount + extends AbstractEntity +{ + private String repositoryName; + + private String assetName = ""; + + private String nodeId; + + private long count; + + private DateType dateType; + + private DateTime date; + + public AssetDownloadCount withRepositoryName(final String repositoryName) { + checkNotNull(repositoryName); + this.repositoryName = repositoryName; + return this; + } + + public AssetDownloadCount withAssetName(final String assetName) { + checkNotNull(assetName); + this.assetName = assetName; + return this; + } + + public AssetDownloadCount withNodeId(final String nodeId) { + checkNotNull(nodeId); + this.nodeId = nodeId; + return this; + } + + public AssetDownloadCount withCount(final long count) { + this.count = count; + return this; + } + + public AssetDownloadCount withDateType(final DateType dateType) { + checkNotNull(dateType); + this.dateType = dateType; + return this; + } + + public AssetDownloadCount withDate(final DateTime date) { + checkNotNull(date); + this.date = date; + return this; + } + + public String getRepositoryName() { + return repositoryName; + } + + public String getAssetName() { + return assetName; + } + + public String getNodeId() { + return nodeId; + } + + public long getCount() { + return count; + } + + public DateType getDateType() { + return dateType; + } + + public DateTime getDate() { + return date; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadCountContributedHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadCountContributedHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..8f90c8bda978ec72bb127acdaf7fe19564d50bd4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadCountContributedHandler.java @@ -0,0 +1,78 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.assetdownloadcount.AssetDownloadCountStore; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Payload; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.repository.view.Response; +import org.sonatype.nexus.repository.view.handlers.ContributedHandler; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.http.HttpMethods.GET; + +/** + * A {@link ContributedHandler} that will record asset downloads. + * + * @since 3.3 + */ +@Named +@Singleton +public class AssetDownloadCountContributedHandler + extends ComponentSupport + implements ContributedHandler +{ + private final AssetDownloadCountStore assetDownloadCountStore; + + @Inject + public AssetDownloadCountContributedHandler(final AssetDownloadCountStore assetDownloadCountStore) + { + this.assetDownloadCountStore = checkNotNull(assetDownloadCountStore); + } + + @Override + public Response handle(final Context context) throws Exception { + Response response = context.proceed(); + + if (assetDownloadCountStore.isEnabled() && + response != null && + response.getStatus().isSuccessful() && + isGetRequest(context.getRequest())) { + Asset asset = getAssetFromPayload(response.getPayload()); + if (asset != null) { + assetDownloadCountStore.incrementCount(context.getRepository().getName(), asset.name()); + } + } + + return response; + } + + private boolean isGetRequest(final Request request) { + return GET.equals(request.getAction()); + } + + private Asset getAssetFromPayload(final Payload payload) { + if (payload instanceof Content) { + return ((Content) payload).getAttributes().get(Asset.class); + } + return null; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadCountEntityAdapter.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadCountEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..b1b6bcca389f13353afe745d3d7781a3832adc59 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadCountEntityAdapter.java @@ -0,0 +1,429 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount.internal; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.OIndexNameBuilder; +import org.sonatype.nexus.orient.entity.IterableEntityAdapter; +import org.sonatype.nexus.repository.assetdownloadcount.DateType; + +import com.google.common.collect.Iterables; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.sql.OCommandSQL; +import org.joda.time.DateTime; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * {@link AssetDownloadCount} entity-adapter. + * + * @since 3.3 + */ +@Named +@Singleton +public class AssetDownloadCountEntityAdapter + extends IterableEntityAdapter +{ + private static final String DB_CLASS = new OClassNameBuilder() + .type("assetdownloadcount") + .build(); + + public static final String P_REPOSITORY_NAME = "repository_name"; + + public static final String P_ASSET_NAME = "asset_name"; + + public static final String P_NODE_ID = "node_id"; + + public static final String P_COUNT = "count"; + + public static final String P_DATE_TYPE = "date_type"; + + public static final String P_DATE = "date"; + + private static final String DATED_COUNT_QUERY = String + .format("select sum(%s) from %s where %s = :repositoryName and %s = :assetName and %s = :dateType and %s = :date", + P_COUNT, DB_CLASS, P_REPOSITORY_NAME, P_ASSET_NAME, P_DATE_TYPE, P_DATE); + + private static final String DATED_COUNT_ALL_OF_REPO_QUERY = String + .format("select sum(%s) from %s where %s = :repositoryName and %s = :assetName and %s = :dateType and %s = :date", + P_COUNT, DB_CLASS, P_REPOSITORY_NAME, P_ASSET_NAME, P_DATE_TYPE, P_DATE); + + private static final String TOTAL_COUNT_QUERY = String + .format("select sum(%s) from %s where %s = :repositoryName and %s = :assetName and %s = :dateType", P_COUNT, + DB_CLASS, P_REPOSITORY_NAME, P_ASSET_NAME, P_DATE_TYPE); + + private static final String UPDATE_COUNT_QUERY = String.format( + "update %s set count = :count, node_id = :nodeId, repository_name = :repositoryName, asset_name = :assetName, " + + "date_type = :dateType, date = :date upsert where %s = :nodeId and %s = :repositoryName and " + + "%s = :assetName and %s = :dateType and %s = :date", + DB_CLASS, P_NODE_ID, P_REPOSITORY_NAME, P_ASSET_NAME, P_DATE_TYPE, P_DATE); + + private static final String REMOVE_OLD_QUERY = String + .format("delete from %s where %s = :nodeId and %s = :dateType and %s < :date limit :limit", DB_CLASS, P_NODE_ID, + P_DATE_TYPE, P_DATE); + + private static final String ASSET_TOTAL_FOR_DATE_TYPE_QUERY = String + .format("select from %s where %s = :repositoryName and %s = :assetName and %s = :dateType", DB_CLASS, + P_REPOSITORY_NAME, P_ASSET_NAME, P_DATE_TYPE); + + private static final String REPO_TOTAL_FOR_DATE_TYPE_QUERY = String + .format("select from %s where %s = :repositoryName and %s = :dateType", DB_CLASS, + P_REPOSITORY_NAME, P_DATE_TYPE); + + private static final String I_REPO_NAME_ASSET_NAME_DATE_TYPE_DATE = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_REPOSITORY_NAME) + .property(P_ASSET_NAME) + .property(P_DATE_TYPE) + .property(P_DATE) + .build(); + + private static final String I_NODE_ID_REPO_NAME_ASSET_NAME_DATE_TYPE_DATE = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_NODE_ID) + .property(P_REPOSITORY_NAME) + .property(P_ASSET_NAME) + .property(P_DATE_TYPE) + .property(P_DATE) + .build(); + + private static final String I_NODE_ID_DATE_TYPE_DATE = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_NODE_ID) + .property(P_DATE_TYPE) + .property(P_DATE) + .build(); + + private static final String I_REPO_NAME_DATE_TYPE_DATE = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_REPOSITORY_NAME) + .property(P_DATE_TYPE) + .build(); + + private final String nodeId; + + private final int maxDeleteSize; + + @Inject + public AssetDownloadCountEntityAdapter(final NodeAccess nodeAccess, + @Named("${nexus.assetdownloads.max.delete.size:-1000}") final int maxDeleteSize) + { + super(DB_CLASS); + this.nodeId = nodeAccess.getId(); + this.maxDeleteSize = maxDeleteSize; + } + + @Override + protected void defineType(final OClass type) { + type.createProperty(P_REPOSITORY_NAME, OType.STRING).setMandatory(true).setNotNull(true); + type.createProperty(P_ASSET_NAME, OType.STRING).setMandatory(true).setNotNull(true); + type.createProperty(P_NODE_ID, OType.STRING).setMandatory(true).setNotNull(true); + type.createProperty(P_COUNT, OType.LONG).setMandatory(true).setNotNull(true); + type.createProperty(P_DATE_TYPE, OType.STRING).setMandatory(true).setNotNull(true); + type.createProperty(P_DATE, OType.DATETIME).setMandatory(true).setNotNull(true); + + type.createIndex(I_REPO_NAME_ASSET_NAME_DATE_TYPE_DATE, INDEX_TYPE.NOTUNIQUE, P_REPOSITORY_NAME, P_ASSET_NAME, + P_DATE_TYPE, P_DATE); + type.createIndex(I_NODE_ID_REPO_NAME_ASSET_NAME_DATE_TYPE_DATE, INDEX_TYPE.NOTUNIQUE, P_NODE_ID, P_REPOSITORY_NAME, + P_ASSET_NAME, P_DATE_TYPE, P_DATE); + type.createIndex(I_NODE_ID_DATE_TYPE_DATE, INDEX_TYPE.NOTUNIQUE, P_NODE_ID, P_DATE_TYPE, P_DATE); + type.createIndex(I_REPO_NAME_DATE_TYPE_DATE, INDEX_TYPE.NOTUNIQUE, P_REPOSITORY_NAME, P_DATE_TYPE); + } + + @Override + protected AssetDownloadCount newEntity() { + return new AssetDownloadCount(); + } + + @Override + protected void readFields(final ODocument document, final AssetDownloadCount entity) { + String repositoryName = document.field(P_REPOSITORY_NAME, OType.STRING); + String assetName = document.field(P_ASSET_NAME, OType.STRING); + String nodeId = document.field(P_NODE_ID, OType.STRING); + Long count = document.field(P_COUNT, OType.LONG); + DateType dateType = DateType.valueOf(document.field(P_DATE_TYPE, OType.STRING)); + Date date = document.field(P_DATE, OType.DATETIME); + + entity.withRepositoryName(repositoryName).withNodeId(nodeId).withDateType(dateType) + .withDate(new DateTime(date)).withCount(count); + + if (assetName != null) { + entity.withAssetName(assetName); + } + } + + @Override + protected void writeFields(final ODocument document, final AssetDownloadCount entity) { + document.field(P_REPOSITORY_NAME, entity.getRepositoryName()); + document.field(P_ASSET_NAME, entity.getAssetName()); + document.field(P_NODE_ID, entity.getNodeId()); + document.field(P_COUNT, entity.getCount()); + document.field(P_DATE_TYPE, entity.getDateType()); + document.field(P_DATE, entity.getDate().toDate()); + } + + public long getCount(final ODatabaseDocumentTx db, final String repositoryName, final String assetName) { + checkNotNull(db); + checkNotNull(repositoryName); + checkNotNull(assetName); + + Map parameters = buildQueryParameters(repositoryName, assetName, DateType.DAY, null, null); + Iterable docs = db.command(new OCommandSQL(TOTAL_COUNT_QUERY)).execute(parameters); + + ODocument result = Iterables.getFirst(docs, null); + + if (result != null) { + return result.field("sum", OType.LONG); + } + + return 0; + } + + public long getCount(final ODatabaseDocumentTx db, + final String repositoryName, + final String assetName, + final DateType dateType) + { + checkNotNull(db); + checkNotNull(repositoryName); + checkNotNull(assetName); + checkNotNull(dateType); + + Map parameters = buildQueryParameters(repositoryName, assetName, dateType, null, null); + String query = String.format( + "select sum(%s) from %s where %s = :repositoryName and %s = :assetName and %s = :dateType", + P_COUNT, DB_CLASS, P_REPOSITORY_NAME, P_ASSET_NAME, P_DATE_TYPE); + Iterable docs = db.command(new OCommandSQL(query)).execute(parameters); + + ODocument result = Iterables.getFirst(docs, null); + + if (result != null) { + return result.field("sum", OType.LONG); + } + + return 0; + } + + public long getCount(final ODatabaseDocumentTx db, + final String repositoryName, + final DateType dateType, + final DateTime date) + { + checkNotNull(db); + checkNotNull(repositoryName); + checkNotNull(dateType); + checkNotNull(date); + + Map parameters = buildQueryParameters(repositoryName, "", dateType, date, null); + Iterable docs = db.command(new OCommandSQL(DATED_COUNT_ALL_OF_REPO_QUERY)).execute(parameters); + + ODocument result = Iterables.getFirst(docs, null); + + if (result != null) { + return result.field("sum", OType.LONG); + } + + return 0; + } + + public long getCount(final ODatabaseDocumentTx db, + final String repositoryName, + final String assetName, + final DateType dateType, + final DateTime date) + { + checkNotNull(db); + checkNotNull(repositoryName); + checkNotNull(assetName); + checkNotNull(dateType); + checkNotNull(date); + + Map parameters = buildQueryParameters(repositoryName, assetName, dateType, date, null); + Iterable docs = db.command(new OCommandSQL(DATED_COUNT_QUERY)).execute(parameters); + + ODocument result = Iterables.getFirst(docs, null); + + if (result != null) { + return result.field("sum", OType.LONG); + } + + return 0; + } + + public void incrementCount(final ODatabaseDocumentTx db, + final String repositoryName, + final String assetName, + final long count) + { + checkNotNull(db); + checkNotNull(repositoryName); + checkNotNull(assetName); + + DateTime now = new DateTime(); + + incrementCount(db, repositoryName, assetName, nodeId, DateType.DAY, now, count); + incrementCount(db, repositoryName, assetName, nodeId, DateType.MONTH, now, count); + } + + public long[] getCounts(final ODatabaseDocumentTx db, + final String repositoryName, + final String assetName, + final DateType dateType) + { + checkNotNull(db); + checkNotNull(repositoryName); + checkNotNull(assetName); + checkNotNull(dateType); + + Map parameters = buildQueryParameters(repositoryName, assetName, dateType, null, null); + Iterable docs = db.command(new OCommandSQL(ASSET_TOTAL_FOR_DATE_TYPE_QUERY)).execute(parameters); + + return getCounts(docs, dateType); + } + + public void setCount(final ODatabaseDocumentTx db, + final String repositoryName, + final DateType dateType, + final DateTime date, + final long count) + { + checkNotNull(db); + checkNotNull(repositoryName); + checkNotNull(date); + + Map parameters = buildQueryParameters(repositoryName, "", dateType, date, nodeId); + parameters.put("count", count); + + db.command(new OCommandSQL(UPDATE_COUNT_QUERY)).execute(parameters); + } + + public long[] getCounts(final ODatabaseDocumentTx db, + final String repositoryName, + final DateType dateType) + { + checkNotNull(db); + checkNotNull(repositoryName); + checkNotNull(dateType); + + Map parameters = buildQueryParameters(repositoryName, "", dateType, null, null); + Iterable docs = db.command(new OCommandSQL(REPO_TOTAL_FOR_DATE_TYPE_QUERY)).execute(parameters); + + return getCounts(docs, dateType); + } + + public int removeOldRecords(final ODatabaseDocumentTx db, final DateType dateType) { + checkNotNull(db); + checkNotNull(dateType); + + DateTime date = dateType.toOldestDate(new DateTime()); + + Map parameters = buildQueryParameters(null, "", dateType, date, nodeId); + parameters.put("limit", maxDeleteSize); + + return db.command(new OCommandSQL(REMOVE_OLD_QUERY)).execute(parameters); + } + + public int getMaxDeleteSize() { + return maxDeleteSize; + } + + private Map buildQueryParameters(final String repositoryName, + final String assetName, + final DateType dateType, + final DateTime date, + final String nodeId) + { + Map params = new HashMap<>(); + + if (repositoryName != null) { + params.put("repositoryName", repositoryName); + } + + if (assetName != null) { + params.put("assetName", assetName); + } + + if (dateType != null) { + params.put("dateType", dateType); + } + + if (date != null) { + params.put("date", dateType.standardizeDate(date).toDate()); + } + + if (nodeId != null) { + params.put("nodeId", nodeId); + } + + return params; + } + + private void incrementCount(final ODatabaseDocumentTx db, + final String repositoryName, + final String assetName, + final String nodeId, + final DateType dateType, + final DateTime date, + final long count) + { + Map parameters = buildQueryParameters(repositoryName, assetName, dateType, date, nodeId); + + String query = String.format("update %s increment count = %s where %s = :nodeId and %s = :repositoryName " + + "and %s = :assetName and %s = :dateType and %s = :date", DB_CLASS, count, P_NODE_ID, P_REPOSITORY_NAME, + P_ASSET_NAME, P_DATE_TYPE, P_DATE); + + Integer updateCount = db.command(new OCommandSQL(query)).execute(parameters); + if (updateCount < 1) { + addEntity(db, newEntity().withRepositoryName(repositoryName).withAssetName(assetName).withNodeId(nodeId) + .withDateType(dateType).withDate(new DateTime(dateType.standardizeDate(date))).withCount(count)); + } + } + + private long[] getCounts(final Iterable docs, final DateType dateType) { + int numberToKeep = dateType.getNumberToKeep(); + long[] counts = new long[numberToKeep]; + + DateTime current = dateType.standardizeDate(DateTime.now()); + DateTime oldest = dateType.toOldestDate(current); + Map dateCountMap = new HashMap<>(); + + //build map of date -> count, will take multiple records (in case of HA) into account + for (ODocument doc : docs) { + AssetDownloadCount assetDownloadCount = readEntity(doc); + + dateCountMap.put(assetDownloadCount.getDate(), + dateCountMap.getOrDefault(assetDownloadCount.getDate(), 0L) + assetDownloadCount.getCount()); + } + + // iterate over the last X dates and pull any counts out of the map, + // using 0 otherwise + for (int i = 0; i < numberToKeep && oldest.isBefore(current); i++) { + counts[i] = dateCountMap.getOrDefault(current, 0L); + current = dateType.decrement(current); + } + + return counts; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadCountStoreImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadCountStoreImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..cf571b8a8412c24cef37564674ab12959c4e80cc --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadCountStoreImpl.java @@ -0,0 +1,227 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount.internal; + +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.lifecycle.Lifecycle; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.repository.assetdownloadcount.AssetDownloadCountStore; +import org.sonatype.nexus.repository.assetdownloadcount.DateType; +import org.sonatype.nexus.repository.storage.ComponentDatabase; +import org.sonatype.nexus.thread.NexusExecutorService; +import org.sonatype.nexus.thread.NexusThreadFactory; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.cache.RemovalListeners; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import org.joda.time.DateTime; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SCHEMAS; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTx; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; + +/** + * Manages shuffling counts data to from the database, as well as a cache of counts to not overwhelm the request + * handling with these db updates + * + * @since 3.4 + */ +@Named +@Singleton +@ManagedLifecycle(phase = SCHEMAS) +public class AssetDownloadCountStoreImpl + extends StateGuardLifecycleSupport + implements AssetDownloadCountStore, Lifecycle +{ + private final Provider databaseInstance; + + private final AssetDownloadCountEntityAdapter entityAdapter; + + private final boolean enabled; + + private final AssetDownloadHistoricDataCleaner historicDataCleaner; + + private final LoadingCache cache; + + @Inject + public AssetDownloadCountStoreImpl(@Named(ComponentDatabase.NAME) final Provider databaseInstance, + @Named("${nexus.assetdownloads.enabled:-true}") final boolean enabled, + @Named("${nexus.assetdownloads.cache.size:-10000}") final int cacheSize, + @Named("${nexus.assetdownloads.cache.duration:-3600}") final int cacheDuration, + final AssetDownloadCountEntityAdapter entityAdapter, + final AssetDownloadHistoricDataCleaner historicDataCleaner, + final CacheRemovalListener cacheRemovalListener) + { + this.databaseInstance = checkNotNull(databaseInstance); + this.entityAdapter = checkNotNull(entityAdapter); + this.historicDataCleaner = checkNotNull(historicDataCleaner); + this.enabled = enabled; + + cache = CacheBuilder.newBuilder() + .maximumSize(cacheSize) + .expireAfterWrite(cacheDuration, TimeUnit.SECONDS) + .removalListener(RemovalListeners.asynchronous(cacheRemovalListener, NexusExecutorService + .forCurrentSubject(Executors.newSingleThreadExecutor( + new NexusThreadFactory("assetdownloads-count", "Asset Downloads Count"))))) + .build(new CacheLoader() + { + @Override + public AtomicLong load(final CacheEntryKey cacheEntryKey) throws Exception { + return new AtomicLong(0); + } + }); + } + + @Override + protected void doStart() throws Exception { + try (ODatabaseDocumentTx db = databaseInstance.get().connect()) { + entityAdapter.register(db); + } + } + + @Override + protected void doStop() throws Exception { + historicDataCleaner.stop(); + } + + @Override + @Guarded(by = STARTED) + public long getDailyCount(final String repositoryName, + final String assetName, + final DateTime date) + { + long count = inTx(databaseInstance) + .call(db -> entityAdapter.getCount(db, repositoryName, assetName, DateType.DAY, date)); + log.debug("Get daily count for {} {} {} return {}", repositoryName, assetName, date, count); + return count; + } + + @Override + @Guarded(by = STARTED) + public long getMonthlyCount(final String repositoryName, + final String assetName, + final DateTime date) + { + long count = inTx(databaseInstance) + .call(db -> entityAdapter.getCount(db, repositoryName, assetName, DateType.MONTH, date)); + log.debug("Get monthly count for {} {} {} return {}", repositoryName, assetName, date, count); + return count; + } + + @Override + @Guarded(by = STARTED) + public long[] getDailyCounts(final String repositoryName, + final String assetName) + { + long[] counts = inTx(databaseInstance) + .call(db -> entityAdapter.getCounts(db, repositoryName, assetName, DateType.DAY)); + log.debug("Get daily counts for {} {} {}", repositoryName, assetName, counts); + return counts; + } + + @Override + @Guarded(by = STARTED) + public long[] getMonthlyCounts(final String repositoryName, + final String assetName) + { + long[] counts = inTx(databaseInstance) + .call(db -> entityAdapter.getCounts(db, repositoryName, assetName, DateType.MONTH)); + log.debug("Get monthly counts for {} {} {}", repositoryName, assetName, counts); + return counts; + } + + @Override + @Guarded(by = STARTED) + public void incrementCount(final String repositoryName, final String assetName) { + log.debug("Incremented count(CACHE) {} {} by {}", repositoryName, assetName, 1); + + //with the dead simple impl of the cache loader, i dont have concerns doing the getUnchecked over get method + cache.getUnchecked(new CacheEntryKey(repositoryName, assetName)).incrementAndGet(); + + historicDataCleaner.start(); + } + + @Override + @Guarded(by = STARTED) + public void setMonthlyVulnerableCount(final String repositoryName, + final DateTime date, + final long count) + { + inTxRetry(databaseInstance).run(db -> { + entityAdapter.setCount(db, repositoryName, DateType.MONTH_WHOLE_REPO_VULNERABLE, date, count); + log.debug("Setting monthly vulnerability count {} {} {}", repositoryName, date, count); + }); + } + + @Override + @Guarded(by = STARTED) + public void setMonthlyCount(final String repositoryName, + final DateTime date, + final long count) + { + inTxRetry(databaseInstance).run(db -> { + entityAdapter.setCount(db, repositoryName, DateType.MONTH_WHOLE_REPO, date, count); + log.debug("Setting monthly count {} {} {}", repositoryName, date, count); + }); + } + + @Override + @Guarded(by = STARTED) + public long[] getMonthlyCounts(final String repositoryName) { + return inTx(databaseInstance).call(db -> { + long[] counts = entityAdapter.getCounts(db, repositoryName, DateType.MONTH_WHOLE_REPO); + log.debug("Get monthly counts for {} {}", repositoryName, counts); + return counts; + }); + } + + @Override + @Guarded(by = STARTED) + public long[] getMonthlyVulnerableCounts(final String repositoryName) { + return inTx(databaseInstance).call(db -> { + long[] counts = entityAdapter.getCounts(db, repositoryName, DateType.MONTH_WHOLE_REPO_VULNERABLE); + log.debug("Get monthly vulnerable counts for {} {}", repositoryName, counts); + return counts; + }); + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + @Guarded(by = STARTED) + public long getLastThirtyDays(final String repositoryName, final String assetName) { + long lastThirty = 0; + for (long day : getDailyCounts(repositoryName, assetName)) { + lastThirty += day; + } + lastThirty += cache.getUnchecked(new CacheEntryKey(repositoryName, assetName)).get(); + return lastThirty; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadHistoricDataCleaner.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadHistoricDataCleaner.java new file mode 100644 index 0000000000000000000000000000000000000000..e86989a2479be643e54db070abe6a2f33cddf32b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/AssetDownloadHistoricDataCleaner.java @@ -0,0 +1,118 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount.internal; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.repository.assetdownloadcount.DateType; +import org.sonatype.nexus.repository.storage.ComponentDatabase; +import org.sonatype.nexus.thread.NexusExecutorService; +import org.sonatype.nexus.thread.NexusThreadFactory; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; + +/** + * Will periodically remove old download count records + * + * @since 3.4 + */ +@Named +@Singleton +public class AssetDownloadHistoricDataCleaner + extends ComponentSupport + implements Runnable +{ + private final Provider databaseInstance; + + private final AssetDownloadCountEntityAdapter assetDownloadCountEntityAdapter; + + private final long interval; + + private final ExecutorService executorService; + + private final AtomicBoolean running = new AtomicBoolean(); + + private static final String ERROR_MSG = "will restart process on next download count increment request"; + + @Inject + public AssetDownloadHistoricDataCleaner(@Named(ComponentDatabase.NAME) final Provider databaseInstance, + final AssetDownloadCountEntityAdapter assetDownloadCountEntityAdapter, + @Named("${nexus.assetdownloads.historicdata.cleaner.interval:-86400}") final long interval) + { + this.databaseInstance = checkNotNull(databaseInstance); + this.assetDownloadCountEntityAdapter = checkNotNull(assetDownloadCountEntityAdapter); + this.interval = interval; + executorService = NexusExecutorService.forCurrentSubject(Executors.newSingleThreadExecutor( + new NexusThreadFactory("assetdownloads-cleaner", "Asset Downloads Historic Data Cleaner", + Thread.MIN_PRIORITY))); + } + + public void start() { + if (!running.getAndSet(true)) { + executorService.submit(this); + } + } + + public void stop() { + running.set(false); + executorService.shutdownNow(); + } + + public boolean isRunning() { + return running.get(); + } + + @Override + public void run() { + do { + try { + doDelete(); + + Thread.sleep(interval * 1000); + } + catch (InterruptedException e) { // NOSONAR + log.debug("Periodic checks interrupted, {}", ERROR_MSG); + running.set(false); + } + catch (Exception e) { + log.debug("Periodic checks failed, {}", ERROR_MSG, e); + running.set(false); + } + } + while (running.get()); + } + + private void doDelete() throws Exception { + int removedCount; + for (DateType dateType : DateType.values()) { + do { + removedCount = inTxRetry(databaseInstance) + .call(db -> assetDownloadCountEntityAdapter.removeOldRecords(db, dateType)); + log.debug("Removed {} old records of type {}", removedCount, dateType.name()); + Thread.yield(); + } + //keep repeating the delete until there are none left + while (removedCount > 0 && removedCount == assetDownloadCountEntityAdapter.getMaxDeleteSize()); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/CacheEntryKey.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/CacheEntryKey.java new file mode 100644 index 0000000000000000000000000000000000000000..e9a481a3eeaa4d13380c8a3f697ccc82c5243a45 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/CacheEntryKey.java @@ -0,0 +1,57 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount.internal; + +import java.util.Objects; + +/** + * Simple key consisting of a reponame and assetname + * + * @since 3.4 + */ +public class CacheEntryKey +{ + private final String repositoryName; + + private final String assetName; + + public CacheEntryKey(final String repositoryName, final String assetName) { + this.repositoryName = repositoryName; + this.assetName = assetName; + } + + public String getAssetName() { + return assetName; + } + + public String getRepositoryName() { + return repositoryName; + } + + @Override + public int hashCode() { + return Objects.hash(repositoryName, assetName); + } + + @Override + public boolean equals(final Object obj) { + if (!(obj instanceof CacheEntryKey)) { + return false; + } + CacheEntryKey other = (CacheEntryKey) obj; + if (other == this) { + return true; + } + return Objects.equals(repositoryName, other.repositoryName) && Objects.equals(assetName, other.assetName); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/CacheRemovalListener.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/CacheRemovalListener.java new file mode 100644 index 0000000000000000000000000000000000000000..8f4f302126697072ca6d7dc730ed9cd8f6d74ee2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/CacheRemovalListener.java @@ -0,0 +1,78 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount.internal; + +import java.util.concurrent.atomic.AtomicLong; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.event.EventAware; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.freeze.DatabaseFreezeChangeEvent; +import org.sonatype.nexus.repository.storage.ComponentDatabase; + +import com.google.common.cache.RemovalListener; +import com.google.common.cache.RemovalNotification; +import com.google.common.eventbus.Subscribe; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; + +/** + * Google cache removal listener that will stuff the data into the database + * + * @since 3.4 + */ +@Named +@Singleton +public class CacheRemovalListener + extends ComponentSupport + implements RemovalListener, EventAware, EventAware.Asynchronous +{ + private final AssetDownloadCountEntityAdapter entityAdapter; + + private final Provider databaseInstance; + + private volatile boolean frozen; + + @Inject + public CacheRemovalListener(@Named(ComponentDatabase.NAME) final Provider databaseInstance, + final AssetDownloadCountEntityAdapter entityAdapter) + { + this.entityAdapter = checkNotNull(entityAdapter); + this.databaseInstance = checkNotNull(databaseInstance); + } + + @Override + public void onRemoval(final RemovalNotification removalNotification) { + if (!frozen) { + inTxRetry(databaseInstance).run( + db -> { + final CacheEntryKey key = removalNotification.getKey(); + entityAdapter.incrementCount(db, key.getRepositoryName(), key.getAssetName(), removalNotification + .getValue().get()); + log.debug("Incremented count(DB) {} {} by {}", key.getRepositoryName(), key.getAssetName(), + removalNotification.getValue()); + }); + } + } + + @Subscribe + public void onDatabaseFreezeChangeEvent(final DatabaseFreezeChangeEvent databaseFreezeChangeEvent) { + frozen = databaseFreezeChangeEvent.isFrozen(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/ComponentDatabaseUpgrade_1_6.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/ComponentDatabaseUpgrade_1_6.java new file mode 100644 index 0000000000000000000000000000000000000000..962dac8d1d0fec56c70bda1074ccd77805cfcb7e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/ComponentDatabaseUpgrade_1_6.java @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.upgrade.Upgrades; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseInstanceNames; +import org.sonatype.nexus.orient.DatabaseUpgradeSupport; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.sql.OCommandSQL; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Upgrade step to set empty assetName rather than null for assetdownloadcount records. + * + * @since 3.4 + */ +@Named +@Singleton +@Upgrades(model = DatabaseInstanceNames.COMPONENT, from = "1.5", to = "1.6") +public class ComponentDatabaseUpgrade_1_6 // NOSONAR + extends DatabaseUpgradeSupport +{ + private static final String UPDATE_ASSET_NAME = + "update assetdownloadcount set asset_name = '' where asset_name is null"; + + private final Provider componentDatabaseInstance; + + @Inject + public ComponentDatabaseUpgrade_1_6( + @Named(DatabaseInstanceNames.COMPONENT) final Provider componentDatabaseInstance) + { + this.componentDatabaseInstance = checkNotNull(componentDatabaseInstance); + } + + @Override + public void apply() throws Exception { + try (ODatabaseDocumentTx db = componentDatabaseInstance.get().connect()) { + if(db.getMetadata().getSchema().existsClass("assetdownloadcount")){ + int updates = db.command(new OCommandSQL(UPDATE_ASSET_NAME)).execute(); + if (updates > 0) { + log.info("Updated {} records with null assetIds", updates); + } + } + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/DateUtils.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/DateUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..d70d1168a3a5c9e2a327f2ed0b63eedf05a3e4cd --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/assetdownloadcount/internal/DateUtils.java @@ -0,0 +1,33 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount.internal; + +import org.joda.time.DateTime; + +/** + * @since 3.3 + */ +public final class DateUtils +{ + private DateUtils() { + //noop + } + + public static DateTime clearTime(final DateTime date) { + return date.withTimeAtStartOfDay(); + } + + public static DateTime clearDayAndTime(final DateTime date) { + return date.withTimeAtStartOfDay().withDayOfMonth(1); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/attributes/AttributesFacet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/attributes/AttributesFacet.java new file mode 100644 index 0000000000000000000000000000000000000000..3ba26c84be6b96abf2e4a9c6c5160cef84d39910 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/attributes/AttributesFacet.java @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.attributes; + +import org.sonatype.nexus.common.collect.ImmutableNestedAttributesMap; +import org.sonatype.nexus.repository.AttributeChange; +import org.sonatype.nexus.repository.Facet; + +/** + * Services for accessing and manipulating repository-level metadata attributes. + * + * @since 3.0 + */ +@Facet.Exposed +public interface AttributesFacet + extends Facet +{ + /** + * Returns an immutable view of the Repository's attributes. + */ + ImmutableNestedAttributesMap getAttributes(); + + /** + * Modifies the Repository's attributes. + */ + void modifyAttributes(AttributeChange change); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/attributes/internal/AttributesFacetImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/attributes/internal/AttributesFacetImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..250845c4293759afcd5f90377a0296262646528f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/attributes/internal/AttributesFacetImpl.java @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.attributes.internal; + +import javax.inject.Named; + +import org.sonatype.nexus.common.collect.ImmutableNestedAttributesMap; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.repository.AttributeChange; +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.attributes.AttributesFacet; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.repository.storage.StorageTx; +import org.sonatype.nexus.repository.transaction.TransactionalStoreMetadata; +import org.sonatype.nexus.transaction.Transactional; +import org.sonatype.nexus.transaction.UnitOfWork; + +/** + * Persists repository attributes in the repository's corresponding {@link Bucket}. + * + * @since 3.0 + */ +@Named +public class AttributesFacetImpl + extends FacetSupport + implements AttributesFacet +{ + @Override + public ImmutableNestedAttributesMap getAttributes() { + return Transactional.operation.withDb(facet(StorageFacet.class).txSupplier()).call(() -> { + final StorageTx tx = UnitOfWork.currentTx(); + final NestedAttributesMap attributes = tx.findBucket(getRepository()).attributes(); + return new ImmutableNestedAttributesMap(null, attributes.getKey(), attributes.backing()); + }); + } + + @Override + public void modifyAttributes(final AttributeChange change) { + TransactionalStoreMetadata.operation.withDb(facet(StorageFacet.class).txSupplier()).call(() -> { + final StorageTx tx = UnitOfWork.currentTx(); + + final Bucket bucket = tx.findBucket(getRepository()); + change.apply(bucket.attributes()); + tx.saveBucket(bucket); + + return null; + }); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/AssetPathBrowseNodeGenerator.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/AssetPathBrowseNodeGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..21b3832f1dfa2fac211aac64ee437896bef9db1d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/AssetPathBrowseNodeGenerator.java @@ -0,0 +1,50 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse; + +import java.util.List; + +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; + +import com.google.common.base.Splitter; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Lists.newArrayList; + +/** + * Asset-led layout that assumes the asset name is its path and components have the same path as their assets. + * + * @since 3.7 + */ +public abstract class AssetPathBrowseNodeGenerator + implements BrowseNodeGenerator +{ + /** + * Construct the asset path by splitting the asset name on the `/` character. + */ + @Override + public List computeAssetPath(final Asset asset, final Component component) { + checkNotNull(asset); + + return newArrayList(Splitter.on('/').omitEmptyStrings().split(asset.name())); + } + + /** + * Component path is same as the asset path. + */ + @Override + public List computeComponentPath(final Asset asset, final Component component) { + return computeAssetPath(asset, component); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseNodeConfiguration.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseNodeConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..ee4751c5b8ed107ff00759432b556c8e4a9a7b70 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseNodeConfiguration.java @@ -0,0 +1,129 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.goodies.common.Time; + +import com.google.common.annotations.VisibleForTesting; + +import static org.sonatype.goodies.common.Time.seconds; + +/** + * Configuration options for browse tree + * + * @since 3.6 + */ +@Named +public class BrowseNodeConfiguration +{ + /** + * Configuration property for enabling browse trees + */ + public static final String ENABLED = "nexus.browse.component.tree.enabled"; + + private final boolean enabled; + + private final boolean automaticRebuild; + + private final int rebuildPageSize; + + private final int deletePageSize; + + private final int maxNodes; + + private final int maxHtmlNodes; + + private final Time queryTimeout; + + @Inject + public BrowseNodeConfiguration(@Named("${nexus.browse.component.tree.enabled:-true}") final boolean enabled, + @Named("${nexus.browse.component.tree.automaticRebuild:-true}") final boolean automaticRebuild, + @Named("${nexus.browse.component.tree.rebuildPageSize:-1000}") final int rebuildPageSize, + @Named("${nexus.browse.component.tree.deletePageSize:-1000}") final int deletePageSize, + @Named("${nexus.browse.component.tree.maxNodes:-10000}") final int maxNodes, + @Named("${nexus.browse.component.tree.maxHtmlNodes:-10000}") final int maxHtmlNodes, + @Named("${nexus.browse.component.tree.queryTimeout:-59s}") final Time queryTimeout) + { + this.enabled = enabled; + this.automaticRebuild = automaticRebuild; + this.rebuildPageSize = rebuildPageSize; + this.deletePageSize = deletePageSize; + this.maxNodes = maxNodes; + this.maxHtmlNodes = maxHtmlNodes; + this.queryTimeout = queryTimeout; + } + + @VisibleForTesting + public BrowseNodeConfiguration() { + this(true, true, 1000, 1000, 10_000, 10_000, seconds(0)); + } + + /** + * The number of assets to retrieve at a time while rebuilding the browse tree + */ + public int getRebuildPageSize() { + return rebuildPageSize; + } + + /** + * The number of nodes to delete at a time while truncating the browse tree + * + * @since 3.7 + */ + public int getDeletePageSize() { + return deletePageSize; + } + + /** + * The maximum number of nodes to display on a given level of a tree + */ + public int getMaxNodes() { + return maxNodes; + } + + /** + * The maximum number of nodes to display in the browse html view (for a given level of the tree) + * + * @since 3.6.1 + */ + public int getMaxHtmlNodes() { + return maxHtmlNodes; + } + + /** + * How long to wait for filtered subtree queries to complete before returning a potentially truncated set of results + * + * @since 3.7 + */ + public Time getQueryTimeout() { + return queryTimeout; + } + + /** + * Whether the tree should be automatically rebuilt if the number of assets is different from the number of leaves in + * the browse tree on startup + */ + public boolean isAutomaticRebuildEnabled() { + return enabled && automaticRebuild; + } + + /** + * Whether the browse tree is enabled + */ + public boolean isEnabled() { + return enabled; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseNodeGenerator.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseNodeGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..b408e3d19d585d563ee4d964e0215eeb52c09cf0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseNodeGenerator.java @@ -0,0 +1,52 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse; + +import java.util.List; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; + +/** + * Defines the browse node layout for components and assets of the same format. + * + * @since 3.6 + */ +public interface BrowseNodeGenerator +{ + /** + * @return the path to the asset + */ + List computeAssetPath(Asset asset, @Nullable Component component); + + /** + * @return the path to the component + */ + List computeComponentPath(Asset asset, Component component); + + /** + * @return last segment of the given path string + * + * @since 3.7 + */ + default String lastSegment(final String path) { + int lastNonSlash = path.length() - 1; + while (lastNonSlash >= 0 && path.charAt(lastNonSlash) == '/') { + lastNonSlash--; + } + int precedingSlash = path.lastIndexOf('/', lastNonSlash - 1); + return path.substring(precedingSlash + 1, lastNonSlash + 1); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseResult.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseResult.java new file mode 100644 index 0000000000000000000000000000000000000000..0b9e8044227bf1cb811bf0439b0fb8756db0ddd0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseResult.java @@ -0,0 +1,69 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse; + +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Data carrier for {@link BrowseService} results, containing the results of a particular query. + * + * @since 3.1 + */ +public class BrowseResult +{ + private long total; + + private List results; + + /** + * @param total The total result count. + * @param results The results returned. + */ + public BrowseResult(final long total, final List results) { + this.total = total; + this.results = checkNotNull(results); + } + + public BrowseResult(final QueryOptions queryOptions, final List results) { + this(estimateCount(queryOptions, results), results); + } + + /** + * Returns the total count of entries available, not just those returned by this particular query. + */ + public long getTotal() { + return total; + } + + /** + * Returns the results from this particular query, which may be a subset or page of all possible results. + */ + public List getResults() { + return results; + } + + private static long estimateCount(final QueryOptions queryOptions, final List items) { + long count = items.size(); + if (queryOptions.getStart() != null && queryOptions.getLimit() != null) { + count += queryOptions.getStart(); + // estimate an additional page if the number of items returned was limited + if (items.size() == queryOptions.getLimit()) { + count += queryOptions.getLimit(); + } + } + return count; + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseService.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseService.java new file mode 100644 index 0000000000000000000000000000000000000000..aeaa53e6475d493dc4e665871ec4306f372e09d4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseService.java @@ -0,0 +1,85 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse; + +import java.util.List; +import java.util.Map; + +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.security.RepositorySelector; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; + +import com.orientechnologies.orient.core.id.ORID; + +/** + * Consolidates code for user browsing of repositories, specifically for the user interface. + * + * @since 3.1 + */ +public interface BrowseService +{ + /** + * Returns a {@link BrowseResult} for browsing the specified repository and query options. + */ + BrowseResult browseComponents(final Repository repository, + final QueryOptions queryOptions); + + /** + * Returns a {@link BrowseResult} of assets for the specified component. Note that the Repository passed in is not + * necessarily the Repository where the component resides (in the case of a group Repository). + */ + BrowseResult browseComponentAssets(final Repository repository, final String componentId); + + /** + * Returns a {@link BrowseResult} of assets based on the specified information. + */ + BrowseResult browseAssets(final Repository repository, + final QueryOptions queryOptions); + + /** + * Returns a {@link BrowseResult} for previewing the specified repository based on an arbitrary content selector. + */ + BrowseResult previewAssets(final RepositorySelector selectedRepository, + final List repositories, + final String jexlExpression, + final QueryOptions queryOptions); + + /** + * Returns an asset based on the supplied id and repository. + */ + Asset getAssetById(ORID assetId, final Repository repository); + + /** + * Returns an asset based on the supplied id and repository. + * + * @since 3.6.1 + */ + Asset getAssetById(EntityId assetId, final Repository repository); + + /** + * Returns a component based on the supplied id and repository. + */ + Component getComponentById(final ORID componentId, final Repository repository); + + /** + * Returns a map of bucket IDs to repository names for any buckets that could be referenced by the repository. + */ + Map getRepositoryBucketNames(final Repository repository); + + /** + * Returns the number of downloads in the last 30-days (if enabled) + */ + long getLastThirtyDays(Asset asset); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseSqlBuilderSupport.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseSqlBuilderSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..2c147d10e118b62e3c7d2a109f5883b181090c33 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/BrowseSqlBuilderSupport.java @@ -0,0 +1,50 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.orient.entity.EntityAdapter; + +import static org.sonatype.nexus.repository.browse.internal.SuffixSqlBuilder.buildSuffix; + +/** + * @since 3.7 + */ +public abstract class BrowseSqlBuilderSupport + extends ComponentSupport +{ + protected abstract EntityAdapter getEntityAdapter(); + + protected abstract String getBrowseIndex(); + + protected StringBuilder buildBase(QueryOptions queryOptions) { + StringBuilder queryBuilder = new StringBuilder("SELECT FROM "); + + if ("id".equals(queryOptions.getSortProperty())) { + queryBuilder.append(getEntityAdapter().getTypeName()); + } + else { + queryBuilder.append("INDEXVALUES"); + if (queryOptions.getSortDirection() != null) { + queryBuilder.append(queryOptions.getSortDirection()); + } + queryBuilder.append(":").append(getBrowseIndex()); + } + + return queryBuilder; + } + + protected String buildQuerySuffix(final QueryOptions queryOptions) { + return buildSuffix(queryOptions); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/ComponentPathBrowseNodeGenerator.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/ComponentPathBrowseNodeGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..f001149749850916981bca5d2c761db19585a3b8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/ComponentPathBrowseNodeGenerator.java @@ -0,0 +1,36 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse; + +import java.util.List; + +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; + +/** + * Component-led layout that places components one level above their assets. + * + * @since 3.7 + */ +public abstract class ComponentPathBrowseNodeGenerator + extends AssetPathBrowseNodeGenerator +{ + /** + * Component path is one level above the asset path. + */ + @Override + public List computeComponentPath(final Asset asset, final Component component) { + List assetPath = computeAssetPath(asset, component); + return assetPath.subList(0, assetPath.size() - 1); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/QueryOptions.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/QueryOptions.java new file mode 100644 index 0000000000000000000000000000000000000000..16d0fbbcc8fb6c4e89312378beeb51c6bae5ef9f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/QueryOptions.java @@ -0,0 +1,120 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * Data carrier with fields commonly used by SQL builders for the {@link BrowseService} implementation. Also does + * a quick check on the sortProperty and sortDirection fields as defensive programming against SQL injection. + * + * @since 3.1 + */ +public class QueryOptions +{ + private static final List SORT_PROPERTIES = Arrays.asList("name", "group", "version", "id"); + + private static final List SORT_DIRECTIONS = Arrays.asList("asc", "desc"); + + private final String filter; + + private final String sortProperty; + + private final String sortDirection; + + private final Integer start; + + private final Integer limit; + + private final String lastId; + + private final boolean contentAuth; + + public QueryOptions(@Nullable final String filter, + @Nullable final String sortProperty, + @Nullable final String sortDirection, + @Nullable final Integer start, + @Nullable final Integer limit) + { + this(filter, sortProperty, sortDirection, start, limit, null, true); + } + + public QueryOptions(@Nullable final String filter, + @Nullable final String sortProperty, + @Nullable final String sortDirection, + @Nullable final Integer start, + @Nullable final Integer limit, + @Nullable final String lastId) + { + this(filter, sortProperty, sortDirection, start, limit, lastId, true); + } + + public QueryOptions(@Nullable final String filter, + @Nullable final String sortProperty, + @Nullable final String sortDirection, + @Nullable final Integer start, + @Nullable final Integer limit, + @Nullable final String lastId, + final boolean contentAuth) + { + this.lastId = lastId; + checkArgument(sortProperty == null || SORT_PROPERTIES.contains(sortProperty.toLowerCase(Locale.ENGLISH))); + checkArgument(sortDirection == null || SORT_DIRECTIONS.contains(sortDirection.toLowerCase(Locale.ENGLISH))); + this.filter = filter; + this.sortProperty = sortProperty; + this.sortDirection = sortDirection; + this.start = start; + this.limit = limit; + this.contentAuth = contentAuth; + } + + @Nullable + public String getFilter() { + return filter; + } + + @Nullable + public String getSortProperty() { + return sortProperty; + } + + @Nullable + public String getSortDirection() { + return sortDirection; + } + + @Nullable + public Integer getStart() { + return start; + } + + @Nullable + public Integer getLimit() { + return limit; + } + + @Nullable + public String getLastId() { + return lastId; + } + + public boolean getContentAuth() { + return contentAuth; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/AssetWhereClauseBuilder.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/AssetWhereClauseBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..bf6cf5410929d361d712738d02add78801fa0ee5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/AssetWhereClauseBuilder.java @@ -0,0 +1,62 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.List; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.text.Strings2; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; + +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_NAME; + +/** + * Build where clause for asset select queries. + * + * @since 3.3 + */ +public class AssetWhereClauseBuilder +{ + private AssetWhereClauseBuilder() { + } + + @Nullable + public static String whereClause(@Nullable final String content, final boolean includeFilter, + final boolean includeLastId) + { + List clauses = Lists.newArrayList(); + if (!Strings2.isBlank(content)) { + clauses.add(content); + } + if (includeFilter) { + clauses.add(P_NAME + " LIKE :nameFilter"); + } + if (includeLastId) { + clauses.add("@RID > :rid"); + } + if (!clauses.isEmpty()) { + return Joiner.on(" AND ").join(clauses); + } + else { + return null; + } + } + + @Nullable + public static String whereClause(final String content, final boolean includeFilter) { + return whereClause(content, includeFilter, false); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseAssetsSqlBuilder.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseAssetsSqlBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..a645eb25b74fca1b880eea508554effe39d19715 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseAssetsSqlBuilder.java @@ -0,0 +1,95 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.browse.QueryOptions; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; +import org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter; + +import com.google.common.base.Joiner; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.browse.internal.AssetWhereClauseBuilder.whereClause; + +/** + * Class that encapsulates building the SQL queries for browsing assets in the {@link BrowseServiceImpl}. + * + * @since 3.1 + */ +@Named +@Singleton +public class BrowseAssetsSqlBuilder + extends BrowseMetadataNodeSqlBuilderSupport +{ + private final AssetEntityAdapter assetEntityAdapter; + + @Inject + BrowseAssetsSqlBuilder(final AssetEntityAdapter assetEntityAdapter) + { + this.assetEntityAdapter = checkNotNull(assetEntityAdapter); + } + + @Override + protected MetadataNodeEntityAdapter getEntityAdapter() { + return assetEntityAdapter; + } + + @Override + protected String getBrowseIndex() { + return AssetEntityAdapter.I_NAME_CASEINSENSITIVE; + } + + @Override + protected String buildWhereClause(final List bucketIds, final QueryOptions queryOptions) { + List whereClauses = new ArrayList<>(); + if (!bucketIds.isEmpty()) { + whereClauses.add("(" + bucketIds.stream() + .map((bucket) -> MetadataNodeEntityAdapter.P_BUCKET + " = " + bucket) + .collect(Collectors.joining(" OR ")) + ")"); + } + if (queryOptions.getContentAuth()) { + whereClauses.add("contentAuth(@this, :browsedRepository) == true"); + } + + return whereClause(Joiner.on(" AND ").join(whereClauses), queryOptions.getFilter() != null, + queryOptions.getLastId() != null); + } + + @Override + Map buildSqlParams(final String repositoryName, final QueryOptions queryOptions) { + Map params = new HashMap<>(); + if (queryOptions.getContentAuth()) { + params.put("browsedRepository", repositoryName); + } + String filter = queryOptions.getFilter(); + if (filter != null) { + params.put("nameFilter", "%" + filter + "%"); + } + + String lastId = queryOptions.getLastId(); + if (lastId != null) { + params.put("rid", lastId); + } + return params; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseComponentsSqlBuilder.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseComponentsSqlBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..3566048b25197c7ba7f3981ca1f43b63e7b65293 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseComponentsSqlBuilder.java @@ -0,0 +1,101 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.browse.QueryOptions; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; +import org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Class that encapsulates building the SQL queries for browsing components in the {@link BrowseServiceImpl}. + * + * @since 3.1 + */ +@Named +@Singleton +public class BrowseComponentsSqlBuilder + extends BrowseMetadataNodeSqlBuilderSupport +{ + private final MetadataNodeEntityAdapter componentEntityAdapter; + + @Inject + BrowseComponentsSqlBuilder(final ComponentEntityAdapter componentEntityAdapter) + { + this.componentEntityAdapter = checkNotNull(componentEntityAdapter); + } + + @Override + protected MetadataNodeEntityAdapter getEntityAdapter() { + return componentEntityAdapter; + } + + @Override + protected String getBrowseIndex() { + return ComponentEntityAdapter.I_GROUP_NAME_VERSION_INSENSITIVE; + } + + @Override + Map buildSqlParams(final String repositoryName, final QueryOptions queryOptions) { + Map params = new HashMap<>(); + params.put("browsedRepository", repositoryName); + + String filter = queryOptions.getFilter(); + if (filter != null) { + String filterValue = "%" + filter + "%"; + params.put("nameFilter", filterValue); + params.put("groupFilter", filterValue); + params.put("versionFilter", filterValue); + } + + String lastId = queryOptions.getLastId(); + if (lastId != null) { + params.put("rid", lastId); + } + + return params; + } + + @Override + protected String buildWhereClause(final List bucketIds, final QueryOptions queryOptions) { + List whereClauses = new ArrayList<>(); + whereClauses.add(bucketIds.stream() + .map((bucket) -> MetadataNodeEntityAdapter.P_BUCKET + " = " + bucket) + .collect(Collectors.joining(" OR "))); + if (queryOptions.getContentAuth()) { + whereClauses.add("contentAuth(@this, :browsedRepository) == true"); + } + if (queryOptions.getFilter() != null) { + whereClauses.add( + MetadataNodeEntityAdapter.P_NAME + " LIKE :nameFilter OR " + + ComponentEntityAdapter.P_GROUP + " LIKE :groupFilter OR " + + ComponentEntityAdapter.P_VERSION + " LIKE :versionFilter"); + } + if (queryOptions.getLastId() != null) { + whereClauses.add("@RID > :rid"); + } + return whereClauses.stream().map(clause -> "(" + clause + ")").collect(Collectors.joining(" AND ")); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseMetadataNodeSqlBuilderSupport.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseMetadataNodeSqlBuilderSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..7226f7bc0bb1f61ef91d3df799de7081d22ac473 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseMetadataNodeSqlBuilderSupport.java @@ -0,0 +1,55 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.List; +import java.util.Map; + +import org.sonatype.nexus.repository.browse.BrowseSqlBuilderSupport; +import org.sonatype.nexus.repository.browse.QueryOptions; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.MetadataNode; + +/** + * {@link BrowseSqlBuilderSupport} extension for classes that extend {@link MetadataNode} in order to provide additional + * support for repositories/buckets (namely {@link Component} and {@link Asset}) + * + * @since 3.4 + */ +public abstract class BrowseMetadataNodeSqlBuilderSupport + extends BrowseSqlBuilderSupport +{ + /** + * Returns the SQL for performing the build query. + */ + String buildBrowseSql(final List bucketIds, final QueryOptions queryOptions) { + if (bucketIds.isEmpty()) { + return ""; + } + + StringBuilder queryBuilder = buildBase(queryOptions); + + queryBuilder.append(" WHERE ").append(buildWhereClause(bucketIds, queryOptions)).append(' ') + .append(buildQuerySuffix(queryOptions)); + + return queryBuilder.toString(); + } + + protected abstract String buildWhereClause(final List bucketIds, final QueryOptions queryOptions); + + /** + * Returns the SQL parameters for performing the browse query. + */ + abstract Map buildSqlParams(final String repositoryName, final QueryOptions queryOptions); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseNodeEventHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseNodeEventHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..6009cbcb6f155beab82ff050f93ceb578e824bdc --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseNodeEventHandler.java @@ -0,0 +1,90 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.entity.EntityEvent; +import org.sonatype.nexus.common.event.EventAware; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; +import org.sonatype.nexus.repository.config.internal.ConfigurationDeletedEvent; +import org.sonatype.nexus.repository.storage.AssetCreatedEvent; +import org.sonatype.nexus.repository.storage.AssetDeletedEvent; +import org.sonatype.nexus.repository.storage.ComponentDeletedEvent; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.Subscribe; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Listens to any events that require managing folder data and calls the format-specific handler. + * + * @since 3.6 + */ +@Singleton +@Named +public class BrowseNodeEventHandler + implements EventAware, EventAware.Asynchronous +{ + private final boolean enabled; + + private final BrowseNodeManager browseNodeManager; + + @Inject + public BrowseNodeEventHandler(final BrowseNodeConfiguration configuration, + final BrowseNodeManager browseNodeManager) + { + this.enabled = checkNotNull(configuration).isEnabled(); + this.browseNodeManager = checkNotNull(browseNodeManager); + } + + @Subscribe + @AllowConcurrentEvents + public void on(final AssetCreatedEvent event) { + if (shouldProcess(event)) { + browseNodeManager.createFromAsset(event.getRepositoryName(), event.getAsset()); + } + } + + @Subscribe + @AllowConcurrentEvents + public void on(final AssetDeletedEvent event) { + if (shouldProcess(event)) { + browseNodeManager.deleteAssetNode(event.getAssetId()); + } + } + + @Subscribe + @AllowConcurrentEvents + public void on(final ComponentDeletedEvent event) { + if (shouldProcess(event)) { + browseNodeManager.deleteComponentNode(event.getComponentId()); + } + } + + @Subscribe + @AllowConcurrentEvents + public void on(final ConfigurationDeletedEvent event) { + if (shouldProcess(event)) { + browseNodeManager.deleteByRepository(event.getRepositoryName()); + } + } + + private boolean shouldProcess(final EntityEvent event) { + checkNotNull(event); + return enabled && event.isLocal(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseNodeManager.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseNodeManager.java new file mode 100644 index 0000000000000000000000000000000000000000..4ec2601f0dcfb96ac161700ed75e422de4c43764 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseNodeManager.java @@ -0,0 +1,141 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.BrowseNodeGenerator; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.BrowseNodeStore; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.ComponentStore; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Manages format specific behaviour for browse nodes + * + * @since 3.7 + */ +@Named +@Singleton +public class BrowseNodeManager + extends ComponentSupport +{ + private static final String DEFAULT_PATH_HANDLER = "default"; + + private final BrowseNodeStore browseNodeStore; + + private final ComponentStore componentStore; + + private final Map pathGenerators; + + private final BrowseNodeGenerator defaultGenerator; + + @Inject + public BrowseNodeManager(final BrowseNodeStore browseNodeStore, + final ComponentStore componentStore, + final Map pathGenerators) + { + this.browseNodeStore = checkNotNull(browseNodeStore); + this.componentStore = checkNotNull(componentStore); + this.pathGenerators = checkNotNull(pathGenerators); + this.defaultGenerator = checkNotNull(pathGenerators.get(DEFAULT_PATH_HANDLER)); + } + + /** + * Creates the browse nodes used to access an asset and its component (if it has one). + * + * @param repositoryName of the repository that the asset is stored in + * @param asset that needs to be accessible from the browse nodes + * @see BrowseNodeGenerator#computeAssetPath(Asset, Component) for details on the default behavior used to compute the asset path + * @see BrowseNodeGenerator#computeComponentPath(Asset, Component) for details on the default behavior used to compute the component path + */ + public void createFromAsset(final String repositoryName, final Asset asset) { + checkNotNull(repositoryName); + checkNotNull(asset); + + BrowseNodeGenerator generator = pathGenerators.getOrDefault(asset.format(), defaultGenerator); + createBrowseNodes(repositoryName, generator, asset); + } + + /** + * Creates the browse nodes used to access a collection of assets and their components (if they have one). + * + * @param repository storing the assets + * @param assets which need to be accessible from the browse nodes + * @see BrowseNodeGenerator#computeAssetPath(Asset, Component) for details on the default behavior used to compute the asset path + * @see BrowseNodeGenerator#computeComponentPath(Asset, Component) for details on the default behavior used to compute the component path + */ + public void createFromAssets(final Repository repository, final Iterable assets) { + checkNotNull(repository); + checkNotNull(assets); + + String repositoryName = repository.getName(); + BrowseNodeGenerator generator = pathGenerators.getOrDefault(repository.getFormat().getValue(), defaultGenerator); + assets.forEach(asset -> createBrowseNodes(repositoryName, generator, asset)); + } + + /** + * Creates an asset browse node and optional component browse node if the asset has a component. + */ + private void createBrowseNodes(final String repositoryName, final BrowseNodeGenerator generator, final Asset asset) { + try { + Component component = asset.componentId() != null ? componentStore.read(asset.componentId()) : null; + + List assetPath = generator.computeAssetPath(asset, component); + if (!assetPath.isEmpty()) { + browseNodeStore.createAssetNode(repositoryName, assetPath, asset); + } + + if (component != null) { + List componentPath = generator.computeComponentPath(asset, component); + if (!componentPath.isEmpty()) { + browseNodeStore.createComponentNode(repositoryName, componentPath, component); + } + } + } + catch (RuntimeException e) { + log.warn("Problem generating browse nodes for {}", asset, e); + } + } + + /** + * Deletes the asset's browse node. + */ + public void deleteAssetNode(final EntityId assetId) { + browseNodeStore.deleteAssetNode(assetId); + } + + /** + * Deletes the component's browse node. + */ + public void deleteComponentNode(final EntityId componentId) { + browseNodeStore.deleteComponentNode(componentId); + } + + /** + * Deletes all browse nodes belonging to the given repository. + */ + public void deleteByRepository(final String repositoryName) { + browseNodeStore.deleteByRepository(repositoryName); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseServiceImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..ab5f78a34f87152db8e0fa8c9125d6eea54c2b7a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/BrowseServiceImpl.java @@ -0,0 +1,365 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.entity.DetachedEntityId; +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.orient.entity.AttachedEntityHelper; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.Type; +import org.sonatype.nexus.repository.assetdownloadcount.AssetDownloadCountStore; +import org.sonatype.nexus.repository.browse.BrowseResult; +import org.sonatype.nexus.repository.browse.BrowseService; +import org.sonatype.nexus.repository.browse.QueryOptions; +import org.sonatype.nexus.repository.group.GroupFacet; +import org.sonatype.nexus.repository.security.ContentPermissionChecker; +import org.sonatype.nexus.repository.security.RepositorySelector; +import org.sonatype.nexus.repository.security.VariableResolverAdapter; +import org.sonatype.nexus.repository.security.VariableResolverAdapterManager; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.BucketStore; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; +import org.sonatype.nexus.repository.storage.MetadataNode; +import org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.repository.storage.StorageTx; +import org.sonatype.nexus.repository.types.GroupType; +import org.sonatype.nexus.security.BreadActions; +import org.sonatype.nexus.transaction.Transactional; +import org.sonatype.nexus.transaction.UnitOfWork; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.record.impl.ODocument; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.transform; +import static java.lang.String.format; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.StreamSupport.stream; + +/** + * Implementation of {@link BrowseService}. + * + * @since 3.1 + */ +@Named +@Singleton +public class BrowseServiceImpl + extends ComponentSupport + implements BrowseService +{ + private final Type groupType; + + private final ComponentEntityAdapter componentEntityAdapter; + + private final AssetDownloadCountStore assetDownloadCountStore; + + private final AssetEntityAdapter assetEntityAdapter; + + private final VariableResolverAdapterManager variableResolverAdapterManager; + + private final ContentPermissionChecker contentPermissionChecker; + + private final BrowseAssetsSqlBuilder browseAssetsSqlBuilder; + + private final BrowseComponentsSqlBuilder browseComponentsSqlBuilder; + + private final BucketStore bucketStore; + + @Inject + public BrowseServiceImpl(@Named(GroupType.NAME) final Type groupType, + final ComponentEntityAdapter componentEntityAdapter, + final VariableResolverAdapterManager variableResolverAdapterManager, + final ContentPermissionChecker contentPermissionChecker, + final AssetDownloadCountStore assetDownloadCountStore, + final AssetEntityAdapter assetEntityAdapter, + final BrowseAssetsSqlBuilder browseAssetsSqlBuilder, + final BrowseComponentsSqlBuilder browseComponentsSqlBuilder, + final BucketStore bucketStore) + { + this.groupType = checkNotNull(groupType); + this.componentEntityAdapter = checkNotNull(componentEntityAdapter); + this.variableResolverAdapterManager = checkNotNull(variableResolverAdapterManager); + this.contentPermissionChecker = checkNotNull(contentPermissionChecker); + this.assetDownloadCountStore = checkNotNull(assetDownloadCountStore); + this.assetEntityAdapter = checkNotNull(assetEntityAdapter); + this.browseAssetsSqlBuilder = checkNotNull(browseAssetsSqlBuilder); + this.browseComponentsSqlBuilder = checkNotNull(browseComponentsSqlBuilder); + this.bucketStore = checkNotNull(bucketStore); + } + + @Override + public BrowseResult browseComponents(final Repository repository, + final QueryOptions queryOptions) + { + checkNotNull(repository); + final List repositories = getRepositories(repository); + try (StorageTx storageTx = repository.facet(StorageFacet.class).txSupplier().get()) { + storageTx.begin(); + List bucketIds = getBucketIds(storageTx, repositories); + List components = Collections.emptyList(); + // ensure there are components before incurring contentAuth overhead + if (hasComponents(storageTx, repository, bucketIds, queryOptions)) { + components = getComponents(storageTx.browse( + browseComponentsSqlBuilder.buildBrowseSql(bucketIds, queryOptions), + browseComponentsSqlBuilder.buildSqlParams(repository.getName(), queryOptions))); + } + return new BrowseResult<>(queryOptions, components); + } + } + + private boolean hasComponents(final StorageTx storageTx, final Repository repository, final List bucketIds, + final QueryOptions queryOptions) + { + QueryOptions adjustedOptions = new QueryOptions(queryOptions.getFilter(), null, null, 0, 1, null, false); + Iterable docs = storageTx.browse(browseComponentsSqlBuilder.buildBrowseSql(bucketIds, adjustedOptions), + browseComponentsSqlBuilder.buildSqlParams(repository.getName(), adjustedOptions)); + return docs.iterator().hasNext(); + } + + @Override + public BrowseResult browseComponentAssets(final Repository repository, final String componentId) + { + checkNotNull(repository); + checkNotNull(componentId); + try (StorageTx storageTx = repository.facet(StorageFacet.class).txSupplier().get()) { + storageTx.begin(); + Component component = storageTx.findComponent(new DetachedEntityId(componentId)); + if (component == null) { + return new BrowseResult<>(0, Collections.emptyList()); + } + VariableResolverAdapter variableResolverAdapter = variableResolverAdapterManager.get(component.format()); + List assets = StreamSupport.stream(storageTx.browseAssets(component).spliterator(), false) + .filter( + (Asset asset) -> contentPermissionChecker.isPermitted( + repository.getName(), + asset.format(), + BreadActions.BROWSE, + variableResolverAdapter.fromAsset(asset)) + ).collect(Collectors.toList()); + return new BrowseResult<>(assets.size(), assets); + } + } + + @Override + public BrowseResult browseAssets(final Repository repository, + final QueryOptions queryOptions) + { + checkNotNull(repository); + final List repositories = getRepositories(repository); + try (StorageTx storageTx = repository.facet(StorageFacet.class).txSupplier().get()) { + storageTx.begin(); + List bucketIds = getBucketIds(storageTx, repositories); + List assets = Collections.emptyList(); + // ensure there are assets before incurring contentAuth overhead + if (hasAssets(storageTx, repository, bucketIds, queryOptions)) { + assets = getAssets(storageTx.browse( + browseAssetsSqlBuilder.buildBrowseSql(bucketIds, queryOptions), + browseAssetsSqlBuilder.buildSqlParams(repository.getName(), queryOptions))); + } + return new BrowseResult<>(queryOptions, assets); + } + } + + private boolean hasAssets(final StorageTx storageTx, final Repository repository, final List bucketIds, + final QueryOptions queryOptions) + { + QueryOptions adjustedOptions = new QueryOptions(queryOptions.getFilter(), null, null, 0, 1, null, false); + Iterable docs = storageTx.browse(browseAssetsSqlBuilder.buildBrowseSql(bucketIds, adjustedOptions), + browseAssetsSqlBuilder.buildSqlParams(repository.getName(), adjustedOptions)); + return docs.iterator().hasNext(); + } + + @Override + public BrowseResult previewAssets(final RepositorySelector repositorySelector, + final List repositories, + final String jexlExpression, + final QueryOptions queryOptions) + { + checkNotNull(repositories); + checkNotNull(jexlExpression); + final Repository repository = repositories.get(0); + try (StorageTx storageTx = repository.facet(StorageFacet.class).txSupplier().get()) { + storageTx.begin(); + List previewRepositories; + if (repositories.size() == 1 && groupType.equals(repository.getType())) { + previewRepositories = repository.facet(GroupFacet.class).leafMembers(); + } + else { + previewRepositories = repositories; + } + + PreviewAssetsSqlBuilder builder = new PreviewAssetsSqlBuilder( + repositorySelector, + jexlExpression, + queryOptions, + getRepoToContainedGroupMap(repositories)); + return new BrowseResult<>( + storageTx.countAssets(builder.buildWhereClause(), builder.buildSqlParams(), previewRepositories, null), + Lists.newArrayList(storageTx.findAssets(builder.buildWhereClause(), builder.buildSqlParams(), + previewRepositories, builder.buildQuerySuffix())) + ); + } + } + + @Override + public Asset getAssetById(final ORID assetId, final Repository repository) { + checkNotNull(repository); + checkNotNull(assetId); + + return getById(assetId, repository, "asset", assetEntityAdapter); + } + + @Override + public Component getComponentById(final ORID componentId, final Repository repository) { + checkNotNull(repository); + checkNotNull(componentId); + + return getById(componentId, repository, "component", componentEntityAdapter); + } + + @Override + public Map getRepositoryBucketNames(final Repository repository) { + checkNotNull(repository); + try (StorageTx storageTx = repository.facet(StorageFacet.class).txSupplier().get()) { + storageTx.begin(); + List buckets = getBuckets(storageTx, getRepositories(repository)); + return buckets.stream().collect(toMap(EntityHelper::id, Bucket::getRepositoryName)); + } + } + + private > T getById(final ORID orid, + final Repository repository, + final String tableName, + final MetadataNodeEntityAdapter adapter) + { + String sql = format("SELECT * FROM %s WHERE contentAuth(@this, :browsedRepository) == true AND @RID == :rid", + tableName); + + Map params = ImmutableMap + .of("browsedRepository", repository.getName(), "rid", orid.toString()); + + try (StorageTx storageTx = repository.facet(StorageFacet.class).txSupplier().get()) { + storageTx.begin(); + return stream(storageTx.browse(sql, params).spliterator(), false) + .map(adapter::readEntity).findFirst().orElse(null); + } + } + + @VisibleForTesting + Map> getRepoToContainedGroupMap(List repositories) { + Map> repoToContainedGroupMap = new HashMap<>(); + for (Repository repository : repositories) { + List groupNames = new ArrayList<>(); + groupNames.add(repository.getName()); + groupNames.addAll(repositories.stream().filter(groupRepository -> { + Optional groupFacet = groupRepository.optionalFacet(GroupFacet.class); + return groupFacet.isPresent() && groupFacet.get().leafMembers().stream() + .anyMatch(leafMember -> repository.getName().equals(leafMember.getName())); + }).map(groupRepository -> groupRepository.getName()).collect(Collectors.toSet())); + repoToContainedGroupMap.put(repository.getName(), groupNames); + } + return repoToContainedGroupMap; + } + + private List getRepositories(final Repository repository) { + checkNotNull(repository); + if (groupType.equals(repository.getType())) { + return repository.facet(GroupFacet.class).leafMembers(); + } + return ImmutableList.of(repository); + } + + private List getBuckets(final StorageTx storageTx, final Iterable repositories) { + checkNotNull(storageTx); + checkNotNull(repositories); + Iterable buckets = storageTx.findBuckets(repositories); + if (buckets == null) { + return Collections.emptyList(); + } + return Lists.newArrayList(buckets); + } + + @VisibleForTesting + List getBucketIds(final StorageTx storageTx, final Iterable repositories) { + return Lists.newArrayList(transform(getBuckets(storageTx, repositories), + bucket -> AttachedEntityHelper.id(bucket).toString())); + } + + private List getComponents(final Iterable results) { + checkNotNull(results); + return Lists.newArrayList(transform(results, componentEntityAdapter::readEntity)); + } + + @VisibleForTesting + List getAssets(final Iterable results) { + checkNotNull(results); + return Lists.newArrayList(transform(results, assetEntityAdapter::readEntity)); + } + + @Override + public Asset getAssetById(final EntityId assetId, final Repository repository) { + List members = allMembers(repository); + + return Transactional.operation.withDb(repository.facet(StorageFacet.class).txSupplier()).call(() -> { + StorageTx tx = UnitOfWork.currentTx(); + Asset candidate = tx.findAsset(assetId); + if (candidate != null) { + final String assetBucketRepositoryName = bucketStore.getById(candidate.bucketId()).getRepositoryName(); + if (members.stream().anyMatch(repo -> repo.getName().equals(assetBucketRepositoryName))) { + return candidate; + } + } + return null; + }); + } + + private List allMembers(final Repository repository) { + if (groupType.equals(repository.getType())) { + return repository.facet(GroupFacet.class).allMembers(); + } + else { + return Collections.singletonList(repository); + } + } + + @Override + public long getLastThirtyDays(Asset asset) { + checkNotNull(asset); + String repositoryName = bucketStore.getById(asset.bucketId()).getRepositoryName(); + + return assetDownloadCountStore.getLastThirtyDays(repositoryName, asset.name()); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/DefaultBrowseNodeGenerator.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/DefaultBrowseNodeGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..6f575a70fae15447cfadf53e306326afe4c89965 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/DefaultBrowseNodeGenerator.java @@ -0,0 +1,79 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nullable; +import javax.inject.Named; + +import org.sonatype.nexus.common.text.Strings2; +import org.sonatype.nexus.repository.browse.ComponentPathBrowseNodeGenerator; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; + +import com.google.inject.Singleton; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Component-led layout based on group, name, and version; places assets one level below their components. + * + * @since 3.6 + */ +@Singleton +@Named +public class DefaultBrowseNodeGenerator + extends ComponentPathBrowseNodeGenerator +{ + /** + * @return componentPath/lastSegment(assetPath) if the component was not null, otherwise assetPath + */ + @Override + public List computeAssetPath(final Asset asset, @Nullable final Component component) { + checkNotNull(asset); + + if (component != null) { + List path = computeComponentPath(asset, component); + + // place asset just below component + path.add(lastSegment(asset.name())); + + return path; + } + else { + return super.computeAssetPath(asset, null); + } + } + + /** + * @return [componentGroup]/componentName/[componentVersion] + */ + @Override + public List computeComponentPath(final Asset asset, final Component component) { + List path = new ArrayList<>(); + + if (!Strings2.isBlank(component.group())) { + path.add(component.group()); + } + + path.add(component.name()); + + if (!Strings2.isBlank(component.version())) { + path.add(component.version()); + } + + return path; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/PreviewAssetsSqlBuilder.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/PreviewAssetsSqlBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..6f9545d91abafb905621b98c40449cfc42332540 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/PreviewAssetsSqlBuilder.java @@ -0,0 +1,77 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.sonatype.nexus.repository.browse.QueryOptions; +import org.sonatype.nexus.repository.security.RepositorySelector; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.browse.internal.AssetWhereClauseBuilder.whereClause; +import static org.sonatype.nexus.repository.browse.internal.SuffixSqlBuilder.buildSuffix; + +/** + * Class that encapsulates building the SQL queries for previewing assets in the {@link BrowseServiceImpl}. + * + * @since 3.1 + */ +public class PreviewAssetsSqlBuilder +{ + private final RepositorySelector repositorySelector; + + private final String jexlExpression; + + private final QueryOptions queryOptions; + + private final Map> repoToContainedGroupMap; + + public PreviewAssetsSqlBuilder(final RepositorySelector repositorySelector, + final String jexlExpression, + final QueryOptions queryOptions, + final Map> repoToContainedGroupMap) { + this.repositorySelector = checkNotNull(repositorySelector); + this.jexlExpression = checkNotNull(jexlExpression); + this.queryOptions = checkNotNull(queryOptions); + this.repoToContainedGroupMap = checkNotNull(repoToContainedGroupMap); + } + + public String buildWhereClause() { + return whereClause("contentExpression(@this, :jexlExpression, :repositorySelector, " + + ":repoToContainedGroupMap) == true", queryOptions.getFilter() != null); + } + + public String buildQuerySuffix() { + return buildSuffix(queryOptions); + } + + public Map buildSqlParams() { + Map params = new HashMap<>(); + params.put("repositorySelector", repositorySelector.toSelector()); + params.put("jexlExpression", buildJexlExpression()); + params.put("repoToContainedGroupMap", repoToContainedGroupMap); + + String filter = queryOptions.getFilter(); + if (filter != null) { + params.put("nameFilter", "%" + filter + "%"); + } + return params; + } + + private String buildJexlExpression() { + //posted question here, http://www.prjhub.com/#/issues/7476 as why we can't just have orients bulit in escaping for double quotes + return jexlExpression.replaceAll("\"", "'").replaceAll("\\s", " "); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesManager.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesManager.java new file mode 100644 index 0000000000000000000000000000000000000000..568d8757f3cf8a37f780936c13d405ca59d5b1ff --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesManager.java @@ -0,0 +1,161 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.BucketEntityAdapter; +import org.sonatype.nexus.repository.storage.ComponentDatabase; +import org.sonatype.nexus.scheduling.TaskConfiguration; +import org.sonatype.nexus.scheduling.TaskInfo; +import org.sonatype.nexus.scheduling.TaskRemovedException; +import org.sonatype.nexus.scheduling.TaskScheduler; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Stopwatch; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.sql.OCommandSQL; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Streams.stream; +import static java.util.Collections.singletonMap; +import static java.util.stream.Collectors.toList; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.TASKS; +import static org.sonatype.nexus.orient.entity.AttachedEntityHelper.id; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTx; + +/** + * @since 3.6 + */ +@Named +@ManagedLifecycle(phase = TASKS) +@Singleton +public class RebuildBrowseNodesManager + extends StateGuardLifecycleSupport +{ + @VisibleForTesting + static final String SELECT_ANY_ASSET_BY_BUCKET = "select @rid from asset where bucket = :bucket limit 1"; + + @VisibleForTesting + static final String SELECT_ANY_BROWSE_NODE_BY_BUCKET = "select @rid from browse_node where repository_name = :repositoryName limit 1"; + + private final Provider componentDatabaseInstanceProvider; + + private final TaskScheduler taskScheduler; + + private final boolean automaticRebuildEnabled; + + private final BucketEntityAdapter bucketEntityAdapter; + + @Inject + public RebuildBrowseNodesManager(@Named(ComponentDatabase.NAME) + final Provider componentDatabaseInstanceProvider, + final TaskScheduler taskScheduler, + final BrowseNodeConfiguration configuration, + final BucketEntityAdapter bucketEntityAdapter) + { + this.componentDatabaseInstanceProvider = checkNotNull(componentDatabaseInstanceProvider); + this.taskScheduler = checkNotNull(taskScheduler); + this.automaticRebuildEnabled = checkNotNull(configuration).isAutomaticRebuildEnabled(); + this.bucketEntityAdapter = bucketEntityAdapter; + } + + @Override + protected void doStart() { // NOSONAR + if (!automaticRebuildEnabled) { + return; + } + + Stopwatch sw = Stopwatch.createStarted(); + try { + Collection buckets = inTx(componentDatabaseInstanceProvider).call(db -> { + return stream(bucketEntityAdapter.browse(db)).filter(bucket -> { + boolean hasAssets = !execute(db, SELECT_ANY_ASSET_BY_BUCKET, singletonMap("bucket", id(bucket))) + .isEmpty(); + boolean hasBrowseNodes = !execute(db, SELECT_ANY_BROWSE_NODE_BY_BUCKET, + singletonMap("repositoryName", bucket.getRepositoryName())) + .isEmpty(); + + if (hasAssets ^ hasBrowseNodes) { + log.debug("browse_node table will be rebuilt for bucket={}", id(bucket)); + return true; + } + else if (!hasAssets) { + log.debug("browse_node table won't be populated as there are no assets for bucketId={}", id(bucket)); + } + else { + log.debug("browse_node table already populated for bucketId={}", id(bucket)); + } + return false; + }).collect(toList()); + }); + + for (Bucket bucket : buckets) { + if (!launchExistingTask(bucket.getRepositoryName())) { + launchNewTask(bucket.getRepositoryName()); + } + } + } + catch (Exception e) { + log.error("Failed to determine if the browse nodes need to be rebuilt for any repositories", e); + } + log.debug("scheduling rebuild browse nodes tasks took {} ms", sw.elapsed(TimeUnit.MILLISECONDS)); + } + + private boolean launchExistingTask(final String repositoryName) throws TaskRemovedException { + for (TaskInfo taskInfo : taskScheduler.listsTasks()) { + if (isRebuildTask(repositoryName, taskInfo)) { + if (!TaskInfo.State.RUNNING.equals(taskInfo.getCurrentState().getState())) { + taskInfo.runNow(); + } + return true; + } + } + + return false; + } + + private void launchNewTask(final String repositoryName) { + TaskConfiguration configuration = taskScheduler + .createTaskConfigurationInstance(RebuildBrowseNodesTaskDescriptor.TYPE_ID); + configuration.setString(RebuildBrowseNodesTaskDescriptor.REPOSITORY_NAME_FIELD_ID, repositoryName); + configuration.setName("Rebuild repository browse tree - (" + repositoryName + ")"); + taskScheduler.submit(configuration); + } + + private boolean isRebuildTask(final String repositoryName, final TaskInfo taskInfo) { + return RebuildBrowseNodesTaskDescriptor.TYPE_ID.equals(taskInfo.getConfiguration().getTypeId()) && repositoryName + .equals(taskInfo.getConfiguration().getString(RebuildBrowseNodesTaskDescriptor.REPOSITORY_NAME_FIELD_ID)); + } + + private List execute(final ODatabaseDocumentTx db, // NOSONAR + final String query, + final Map parameters) + { + return db.command(new OCommandSQL(query)).execute(parameters); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesTask.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesTask.java new file mode 100644 index 0000000000000000000000000000000000000000..498d60851ae296486a0a35c78cdedb57a07ed234 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesTask.java @@ -0,0 +1,143 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.logging.task.ProgressLogIntervalHelper; +import org.sonatype.nexus.orient.entity.AttachedEntityHelper; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryTaskSupport; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; +import org.sonatype.nexus.repository.storage.AssetStore; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.BucketStore; +import org.sonatype.nexus.scheduling.TaskInterruptedException; + +import com.google.common.base.Stopwatch; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.index.OCompositeKey; +import com.orientechnologies.orient.core.index.OIndexCursor; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Browse nodes rebuild task. + * + * @since 3.6 + */ +@Named +public class RebuildBrowseNodesTask + extends RepositoryTaskSupport +{ + private static final int BUCKET_KEY_ID = 0; + + private final AssetStore assetStore; + + private final BucketStore bucketStore; + + private final BrowseNodeManager browseNodeManager; + + private final int rebuildPageSize; + + @Inject + public RebuildBrowseNodesTask(final AssetStore assetStore, + final BucketStore bucketStore, + final BrowseNodeManager browseNodeManager, + final BrowseNodeConfiguration configuration) + { + this.assetStore = checkNotNull(assetStore); + this.bucketStore = checkNotNull(bucketStore); + this.browseNodeManager = checkNotNull(browseNodeManager); + this.rebuildPageSize = checkNotNull(configuration).getRebuildPageSize(); + } + + @Override + public String getMessage() { + return "Rebuilding browse tree for " + getRepositoryField(); + } + + @Override + protected void execute(final Repository repo) { + + browseNodeManager.deleteByRepository(repo.getName()); + + Bucket bucket = bucketStore.read(repo.getName()); + ORID bucketId = AttachedEntityHelper.id(bucket); + + try { + long processed = 0; + long total = assetStore.countAssets(ImmutableList.of(bucket)); + + if (total > 0) { + ProgressLogIntervalHelper progressLogger = new ProgressLogIntervalHelper(log, 60); + Stopwatch sw = Stopwatch.createStarted(); + long lastTime = sw.elapsed(TimeUnit.MILLISECONDS); + + OIndexCursor cursor = assetStore.getIndex(AssetEntityAdapter.I_BUCKET_COMPONENT_NAME).cursor(); + List> nextPage = assetStore.getNextPage(cursor, rebuildPageSize); + while (!Iterables.isEmpty(nextPage)) { + checkContinuation(repo); + + List assets = new ArrayList<>(rebuildPageSize); + for (Entry indexEntry : nextPage) { + if (bucketId.equals(indexEntry.getKey().getKeys().get(BUCKET_KEY_ID))) { + assets.add(assetStore.getById(indexEntry.getValue())); + } + } + + int assetsSize = Iterables.size(assets); + + browseNodeManager.createFromAssets(repo, assets); + + processed += assetsSize; + + long elapsed = sw.elapsed(TimeUnit.MILLISECONDS); + progressLogger.info("rebuilding tree for {} assets took {} ms of {} ms, {} / {} assets processed", assetsSize, + elapsed - lastTime, elapsed, processed, total); + + lastTime = sw.elapsed(TimeUnit.MILLISECONDS); + + nextPage = assetStore.getNextPage(cursor, rebuildPageSize); + } + progressLogger.flush(); // ensure final rebuild message is flushed + } + } + catch (Exception e) { + log.error("Could not re-create browse nodes for repository: {}", repo, e); + } + } + + @Override + protected boolean appliesTo(final Repository repository) { + return repository != null; + } + + private void checkContinuation(final Repository repo) { + if (isCanceled()) { + throw new TaskInterruptedException(String.format("Rebuilding browse nodes was cancelled for %s", repo.getName()), + true); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesTaskDescriptor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesTaskDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..f4f89da2f5706ab7df7b8ccb47f75fd53dcf874d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesTaskDescriptor.java @@ -0,0 +1,47 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.formfields.RepositoryCombobox; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; +import org.sonatype.nexus.repository.types.GroupType; +import org.sonatype.nexus.scheduling.TaskDescriptorSupport; + +/** + * Task descriptor for {@link RebuildBrowseNodesTask}. + * + * @since 3.6 + */ +@Named +@Singleton +public class RebuildBrowseNodesTaskDescriptor + extends TaskDescriptorSupport +{ + public static final String TYPE_ID = "create.browse.nodes"; + + public static final String REPOSITORY_NAME_FIELD_ID = "repositoryName"; + + @Inject + public RebuildBrowseNodesTaskDescriptor(final NodeAccess nodeAccess, BrowseNodeConfiguration configuration) { + super(TYPE_ID, RebuildBrowseNodesTask.class, "Rebuild repository browse tree", + configuration.isEnabled() ? VISIBLE : NOT_VISIBLE, configuration.isEnabled() ? EXPOSED : NOT_EXPOSED, + new RepositoryCombobox(REPOSITORY_NAME_FIELD_ID, "Repository", "Select the repository to rebuild browse tree", + true).excludingAnyOfTypes(GroupType.NAME).includeAnEntryForAllRepositories(), + nodeAccess.isClustered() ? newLimitNodeFormField() : null); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/SuffixSqlBuilder.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/SuffixSqlBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..88e62450614927f8bdd9a51853b3fa2a33e570fd --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/SuffixSqlBuilder.java @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import org.sonatype.nexus.repository.browse.QueryOptions; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Builds SQL for sorting, limiting and setting the start record. + * + * @since 3.3 + */ +public class SuffixSqlBuilder +{ + private final QueryOptions queryOptions; + + private SuffixSqlBuilder(final QueryOptions queryOptions) { + this.queryOptions = checkNotNull(queryOptions); + } + + public static String buildSuffix(final QueryOptions queryOptions) { + return new SuffixSqlBuilder(queryOptions).build(); + } + + private String build() { + return sort() + start() + limit(); + } + + private String sort() { + String sortProperty = queryOptions.getSortProperty(); + String sortDirection = queryOptions.getSortDirection(); + if (sortProperty != null && sortDirection != null && "id".equals(sortProperty)) { + return " ORDER BY @RID " + sortDirection; + } + return ""; + } + + private String start() { + Integer start = queryOptions.getStart(); + if (start != null) { + return " SKIP " + start; + } + return ""; + } + + private String limit() { + Integer limit = queryOptions.getLimit(); + if (limit != null) { + return " LIMIT " + limit; + } + return ""; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/model/BrowseListItem.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/model/BrowseListItem.java new file mode 100644 index 0000000000000000000000000000000000000000..7299f623900cdde8cbd39262b7657969082499ca --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/model/BrowseListItem.java @@ -0,0 +1,70 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal.model; + +/** + * @since 3.6 + */ +public class BrowseListItem +{ + private final String resourceUri; + + private final String name; + + private final boolean collection; + + private final String lastModified; + + private final String size; + + private final String description; + + public BrowseListItem(final String resourceUri, + final String name, + final boolean collection, + final String lastModified, + final String size, + final String description) + { + this.resourceUri = resourceUri; + this.name = name; + this.collection = collection; + this.lastModified = lastModified; + this.size = size; + this.description = description; + } + + public String getResourceUri() { + return resourceUri; + } + + public String getName() { + return name; + } + + public boolean isCollection() { + return collection; + } + + public String getLastModified() { + return lastModified; + } + + public String getSize() { + return size; + } + + public String getDescription() { + return description; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/resources/RepositoryBrowseResource.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/resources/RepositoryBrowseResource.java new file mode 100644 index 0000000000000000000000000000000000000000..3647a70d8ca5518a4b7eea168dd876a7f11cbfa3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/browse/internal/resources/RepositoryBrowseResource.java @@ -0,0 +1,290 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal.resources; + +import java.net.URL; +import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.encoding.EncodingUtil; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.template.TemplateHelper; +import org.sonatype.nexus.common.template.TemplateParameters; +import org.sonatype.nexus.common.text.Strings2; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; +import org.sonatype.nexus.repository.browse.internal.model.BrowseListItem; +import org.sonatype.nexus.repository.group.GroupFacet; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.security.RepositoryViewPermission; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.BrowseNode; +import org.sonatype.nexus.repository.storage.BrowseNodeStore; +import org.sonatype.nexus.repository.storage.BucketStore; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.repository.storage.StorageTx; +import org.sonatype.nexus.rest.Resource; +import org.sonatype.nexus.security.SecurityHelper; +import org.sonatype.nexus.transaction.Transactional; +import org.sonatype.nexus.transaction.UnitOfWork; + +import com.google.common.collect.Iterables; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Arrays.asList; +import static javax.ws.rs.core.MediaType.TEXT_HTML; +import static javax.ws.rs.core.Response.Status.NOT_FOUND; +import static org.sonatype.nexus.common.encoding.EncodingUtil.urlEncode; +import static org.sonatype.nexus.security.BreadActions.BROWSE; + +/** + * @since 3.6 + */ +@Named +@Singleton +@Path(RepositoryBrowseResource.RESOURCE_URI) +@Produces(TEXT_HTML) +public class RepositoryBrowseResource + extends ComponentSupport + implements Resource +{ + private static final String REPOSITORY_PATH_SEGMENT = "{noop: (/)?}{repositoryPath: ((?<=/).*)?}"; + + public static final String RESOURCE_URI = "/repository/browse/{repositoryName}" + REPOSITORY_PATH_SEGMENT; + + private static final String TEMPLATE_RESOURCE = "browseContentHtml.vm"; + + private final RepositoryManager repositoryManager; + + private final BrowseNodeStore browseNodeStore; + + private final BrowseNodeConfiguration configuration; + + private final TemplateHelper templateHelper; + + private final SecurityHelper securityHelper; + + private final URL template; + + private final BucketStore bucketStore; + + @Inject + public RepositoryBrowseResource(final RepositoryManager repositoryManager, + final BrowseNodeStore browseNodeStore, + final BrowseNodeConfiguration configuration, + final BucketStore bucketStore, + final TemplateHelper templateHelper, + final SecurityHelper securityHelper) + { + this.repositoryManager = checkNotNull(repositoryManager); + this.browseNodeStore = checkNotNull(browseNodeStore); + this.configuration = checkNotNull(configuration); + this.templateHelper = checkNotNull(templateHelper); + this.securityHelper = checkNotNull(securityHelper); + this.bucketStore = checkNotNull(bucketStore); + this.template = getClass().getResource(TEMPLATE_RESOURCE); + checkNotNull(template); + } + + @GET + public Response getHtml(@PathParam("repositoryName") final String repositoryName, + @PathParam("repositoryPath") final String repositoryPath, + @QueryParam("filter") final String filter, + @Context final UriInfo uriInfo) + { + log.debug("Get HTML directory listing for repository {} on path {}", repositoryName, repositoryPath); + + if (!uriInfo.getAbsolutePath().toString().endsWith("/")) { + log.debug("Request does include a trailing slash, redirecting to include it"); + return Response.seeOther(UriBuilder.fromUri(uriInfo.getAbsolutePath()).path("/").build()).build(); + } + + Repository repository = repositoryManager.get(repositoryName); + + if (repository == null) { + throw createNotFoundException(repositoryName, null); + } + + List pathSegments = new ArrayList<>(); + + if (!isRoot(repositoryPath)) { + pathSegments = asList(EncodingUtil.urlDecode(repositoryPath.split("/"))); + } + + Iterable browseNodes = browseNodeStore + .getByPath(repository, pathSegments, configuration.getMaxHtmlNodes(), filter); + + final boolean permitted = securityHelper.allPermitted(new RepositoryViewPermission(repository, BROWSE)); + final boolean hasChildren = browseNodes != null && !Iterables.isEmpty(browseNodes); + final List listItems = hasChildren ? + toListItems(browseNodes, repository, repositoryPath, filter) : + Collections.emptyList(); + + //if there are visible children return them, or if we are at the root node and permitted to browse the repo + if (hasChildren || (isRoot(repositoryPath) && permitted)) { + return Response + .ok(templateHelper.render(template, initializeTemplateParameters(repositoryName, repositoryPath, listItems))) + .build(); + } + + throw createNotFoundException(repositoryName, permitted ? repositoryPath : null); + } + + private WebApplicationException createNotFoundException(final String repositoryName, final String repositoryPath) { + if (repositoryPath == null) { + log.debug("Requested repository could not be located or user does not have permission: {} ", repositoryName); + return new WebApplicationException("Repository not found", NOT_FOUND); + } + else { + log.debug("Requested path {} could not be located in repository {}", repositoryPath, repositoryName); + return new WebApplicationException("Path not found", NOT_FOUND); + } + } + + private List toListItems(final Iterable browseNodes, + final Repository repository, + final String path, + final String filter) + { + List listItems = new ArrayList<>(); + + if (browseNodes != null) { + SimpleDateFormat format = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy"); + for (BrowseNode browseNode : sort(browseNodes)) { + String size = null; + String lastModified = null; + String listItemPath; + if (browseNode.isLeaf()) { + Asset asset = getAssetById(repository, browseNode.getAssetId()); + + if (asset == null) { + log.error("Could not find expected asset (id): {}/{} ({}) in repository: {}", path, browseNode.getName(), + browseNode.getAssetId().toString(), repository.getName()); + //something bad going on here, move along to the next + continue; + } + + size = String.valueOf(asset.size()); + lastModified = Optional.ofNullable(asset.blobUpdated()).map(dateTime -> format.format(dateTime.toDate())) + .orElse(""); + listItemPath = getListItemPath(repository, browseNode, asset, filter); + } + else { + listItemPath = getListItemPath(repository, browseNode, null, filter); + } + + listItems.add( + new BrowseListItem(listItemPath, browseNode.getName(), !browseNode.isLeaf(), lastModified, size, + "")); + } + } + + return listItems; + } + + private Asset getAssetById(final Repository repository, final EntityId assetId) { + Optional optionalGroupFacet = repository.optionalFacet(GroupFacet.class); + List members = optionalGroupFacet.isPresent() ? optionalGroupFacet.get().allMembers() + : Collections.singletonList(repository); + + return Transactional.operation.withDb(repository.facet(StorageFacet.class).txSupplier()).call(() -> { + StorageTx tx = UnitOfWork.currentTx(); + Asset candidate = tx.findAsset(assetId); + if (candidate != null) { + final String asssetBucketRepositoryName = bucketStore.getById(candidate.bucketId()).getRepositoryName(); + if (members.stream().anyMatch(repo -> repo.getName().equals(asssetBucketRepositoryName))) { + return candidate; + } + } + + return null; + }); + } + + private Iterable sort(final Iterable nodes) { + List sortedBrowseNodes = new ArrayList<>(); + nodes.forEach(sortedBrowseNodes::add); + + sortedBrowseNodes.sort((o1, o2) -> { + if (o1.getAssetId() == null && o2.getAssetId() != null) { + return -1; + } + else if (o2.getAssetId() == null && o1.getAssetId() != null) { + return 1; + } + return Strings2.lower(o1.getName()).compareTo(Strings2.lower(o2.getName())); + }); + + return sortedBrowseNodes; + } + + private TemplateParameters initializeTemplateParameters(final String repositoryName, final String path, final List listItems) { + TemplateParameters templateParameters = templateHelper.parameters(); + + if (isRoot(path)) { + templateParameters.set("root", true); + } + + templateParameters.set("requestPath", "/" + path); + templateParameters.set("listItems", listItems); + + templateParameters.set("showMoreContentWarning", configuration.getMaxHtmlNodes() == listItems.size()); + if (Strings2.isBlank(path)) { + templateParameters.set("encodedPath", String.format("/#browse/browse:%s", repositoryName)); + } + else { + String encodedPath = urlEncode("/" + path + "/"); + templateParameters.set("encodedPath", String.format("/#browse/browse:%s:%s", repositoryName, encodedPath)); + } + templateParameters.set("searchUrl", "/#browse/search"); + + return templateParameters; + } + + private String getListItemPath(final Repository repository, + final BrowseNode browseNode, + final Asset asset, + final String filter) + { + String filterParam = filter == null ? "" : "?filter=" + URLEncoder.encode(filter); + + if (asset == null) { + return urlEncode(browseNode.getName()) + "/" + filterParam; + } + + return repository.getUrl() + "/" + asset.name(); + } + + private boolean isRoot(final String path) { + return Strings2.isBlank(path); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/CacheController.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/CacheController.java new file mode 100644 index 0000000000000000000000000000000000000000..782030139e4d9a64da81b3bf50ad4a89aa97b74a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/CacheController.java @@ -0,0 +1,82 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.common.ComponentSupport; + +import com.google.common.annotations.VisibleForTesting; +import org.joda.time.DateTime; + +/** + * A support class which implements basic cache-control logic. + * + * @since 3.0 + */ +public class CacheController + extends ComponentSupport +{ + public static String newCacheToken() { + return Long.toString(System.nanoTime()); + } + + private final int contentMaxAgeSeconds; + + private volatile String cacheToken; + + public CacheController(final int contentMaxAgeSeconds, @Nullable final String cacheToken) { + this.contentMaxAgeSeconds = contentMaxAgeSeconds; + this.cacheToken = cacheToken; + } + + /** + * After invoking this method, all {@link #isStale(CacheInfo)} checks will return true that has {@link CacheInfo} not + * carrying same token as created in this method. + */ + public void invalidateCache() { + this.cacheToken = newCacheToken(); + } + + /** + * Returns the currently effective {@link CacheInfo} with "now" timestamp. + */ + public CacheInfo current() { + return new CacheInfo(DateTime.now(), cacheToken); + } + + /** + * Returns {@code true} if passed in cache info carries stale information, detected either by cache token or + * age of the info. + */ + public boolean isStale(final CacheInfo cacheInfo) { + if (cacheToken != null && !cacheToken.equals(cacheInfo.getCacheToken())) { + log.debug("Content expired (cacheToken)"); + return true; + } + if (contentMaxAgeSeconds < 0) { + log.trace("Content max age checking disabled"); + return false; + } + if (cacheInfo.getLastVerified().isBefore(new DateTime().minusSeconds(contentMaxAgeSeconds))) { + log.debug("Content expired (age)"); + return true; + } + return false; + } + + @VisibleForTesting + public int getContentMaxAgeSeconds() { + return contentMaxAgeSeconds; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/CacheControllerHolder.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/CacheControllerHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..57b0b989a370c60e789e729f82070dc79ee606b9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/CacheControllerHolder.java @@ -0,0 +1,117 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache; + +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Holds controllers for the various standard or format specific cache types. + * + * @since 3.0 + */ +public class CacheControllerHolder +{ + public static class CacheType + { + private final String typeName; + + public CacheType(final String typeName) { + this.typeName = checkNotNull(typeName); + } + + public String value() { + return typeName; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CacheType)) { + return false; + } + + CacheType cacheType = (CacheType) o; + + return value().equals(cacheType.value()); + + } + + @Override + public int hashCode() { + return value().hashCode(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "typeName='" + typeName + '\'' + + '}'; + } + } + + public static final CacheType CONTENT = new CacheType("CONTENT"); + + public static final CacheType METADATA = new CacheType("METADATA"); + + private final Map controllers; + + public CacheControllerHolder(final CacheController contentCacheController, + final CacheController metadataCacheController) + { + controllers = new HashMap<>(); + controllers.put(CONTENT, checkNotNull(contentCacheController)); + controllers.put(METADATA, checkNotNull(metadataCacheController)); + } + + @Nonnull + public CacheController getContentCacheController() { + return checkNotNull(controllers.get(CONTENT)); + } + + @Nonnull + public CacheController getMetadataCacheController() { + return checkNotNull(controllers.get(METADATA)); + } + + @Nullable + public CacheController get(final CacheType cacheType) { + return controllers.get(checkNotNull(cacheType)); + } + + public CacheController require(final CacheType cacheType) { + return checkNotNull(get(cacheType), "Unexpected cache type: " + cacheType); + } + + @Nullable + public CacheController set(final CacheType cacheType, @Nullable final CacheController cacheController) { + checkNotNull(cacheType); + if (cacheController == null) { + return controllers.remove(cacheType); + } + else { + return controllers.put(cacheType, cacheController); + } + } + + public void invalidateCaches() { + controllers.values().forEach(CacheController::invalidateCache); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/CacheInfo.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/CacheInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..59b4bb6ceb3fde1764d14daea76adcba3b534d10 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/CacheInfo.java @@ -0,0 +1,115 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache; + +import java.util.Date; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.repository.storage.Asset; + +import com.google.common.annotations.VisibleForTesting; +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.time.DateHelper.toDate; +import static org.sonatype.nexus.common.time.DateHelper.toDateTime; + +/** + * Maintains cache details a cached resource. {@link CacheController} relies on information provided by this + * class to implement "aging" and cache invalidation. + * + * @since 3.0 + */ +public class CacheInfo +{ + /** + * Key of {@link Asset} nested map of cache related properties. + * + * @see CacheInfo + */ + @VisibleForTesting + static final String CACHE = "cache"; + + /** + * Cache token {@link String}. + */ + @VisibleForTesting + static final String CACHE_TOKEN = "cache_token"; + + /** + * Last verified {@link Date}. + */ + @VisibleForTesting + static final String LAST_VERIFIED = "last_verified"; + + private final DateTime lastVerified; + + @Nullable + private final String cacheToken; + + public CacheInfo(final DateTime lastVerified, @Nullable final String cacheToken) { + this.lastVerified = checkNotNull(lastVerified); + this.cacheToken = cacheToken; + } + + /** + * Returns the {@link DateTime} when this item was last verified and detected as "fresh". + */ + public DateTime getLastVerified() { + return lastVerified; + } + + /** + * Returns the "cache token" that was in effect when this item was last verified. + */ + @Nullable + public String getCacheToken() { + return cacheToken; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "lastVerified=" + lastVerified + + ", cacheToken='" + cacheToken + '\'' + + '}'; + } + + /** + * Extracts non-format specific cache info from passed in {@link Asset}. + */ + @Nullable + public static CacheInfo extractFromAsset(final Asset asset) { + checkNotNull(asset); + final NestedAttributesMap proxyCache = asset.attributes().child(CACHE); + final DateTime lastVerified = toDateTime(proxyCache.get(LAST_VERIFIED, Date.class)); + if (lastVerified == null) { + return null; + } + final String cacheToken = proxyCache.get(CACHE_TOKEN, String.class); + return new CacheInfo(lastVerified, cacheToken); + } + + /** + * Applies non-format specific cache info attributes onto passed in {@link Asset}. + */ + public static void applyToAsset(final Asset asset, final CacheInfo cacheInfo) { + checkNotNull(asset); + checkNotNull(cacheInfo); + final NestedAttributesMap proxyCache = asset.attributes().child(CACHE); + proxyCache.set(LAST_VERIFIED, toDate(cacheInfo.getLastVerified())); + proxyCache.set(CACHE_TOKEN, cacheInfo.getCacheToken()); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/NegativeCacheFacet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/NegativeCacheFacet.java new file mode 100644 index 0000000000000000000000000000000000000000..bc325894646f39c03c6ab75a148dfc02c1a4af74 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/NegativeCacheFacet.java @@ -0,0 +1,73 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.Facet; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Status; + +/** + * Negative cache management {@link Facet}. + * + * @since 3.0 + */ +@Facet.Exposed +public interface NegativeCacheFacet + extends Facet +{ + /** + * Retrieve an entry from negative cache. + * + * @param key cache key + * @return cached {@link Status} or null if no cache entry found + */ + @Nullable + Status get(NegativeCacheKey key); + + /** + * Add an entry to negative cache + * + * @param key cache key + * @param status (404) status to be cached + */ + void put(NegativeCacheKey key, Status status); + + /** + * Removes an entry from negative cache. + * + * @param key cache key + */ + void invalidate(NegativeCacheKey key); + + /** + * Removes entry for passed in parent key and all is children (using {@link NegativeCacheKey#isParentOf(NegativeCacheKey)}). + * + * @param key parent cache key + */ + void invalidateSubset(NegativeCacheKey key); + + /** + * Removes all entries from negative cache. + */ + void invalidate(); + + /** + * Retrieves the cache key based on context. + * + * @param context view context + * @return cache key + */ + NegativeCacheKey getCacheKey(Context context); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/NegativeCacheHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/NegativeCacheHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..6e9e146a5d88578fb23f7f8e1e874f02d5552904 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/NegativeCacheHandler.java @@ -0,0 +1,76 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache; + +import javax.annotation.Nonnull; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.http.HttpMethods; +import org.sonatype.nexus.repository.http.HttpStatus; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Response; +import org.sonatype.nexus.repository.view.Status; + +/** + * Handler that caches 404 responses. + * + * When context invocation returns 404, it caches the 404 status to avoid future invocations (if cached status is + * present). + * + * @since 3.0 + */ +public class NegativeCacheHandler + extends ComponentSupport + implements Handler +{ + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { + String action = context.getRequest().getAction(); + if (!HttpMethods.GET.equals(action)) { + return context.proceed(); + } + NegativeCacheFacet negativeCache = context.getRepository().facet(NegativeCacheFacet.class); + NegativeCacheKey key = negativeCache.getCacheKey(context); + + Response response; + Status status = negativeCache.get(key); + if (status == null) { + response = context.proceed(); + if (isNotFound(response)) { + negativeCache.put(key, response.getStatus()); + } + else if (response.getStatus().isSuccessful()) { + negativeCache.invalidate(key); + } + } + else { + response = buildResponse(status, context); + + log.debug("Found {} in negative cache, returning {}", key, response); + } + return response; + } + + protected Response buildResponse(final Status status, final Context context) { + return new Response.Builder() + .status(status) + .build(); + } + + private boolean isNotFound(final Response response) { + return HttpStatus.NOT_FOUND == response.getStatus().getCode(); + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/NegativeCacheKey.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/NegativeCacheKey.java new file mode 100644 index 0000000000000000000000000000000000000000..e286ba305ecbfcf41e3613582048abe1e698517c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/NegativeCacheKey.java @@ -0,0 +1,30 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache; + +import java.io.Serializable; + +/** + * A key for the {@link NegativeCacheFacet} negative cache. + * + * @since 3.0 + */ +public interface NegativeCacheKey + extends Serializable +{ + /** + * @param key child key + * @return true if this key is a parent of passed in key. + */ + boolean isParentOf(NegativeCacheKey key); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/RepositoryCacheUtils.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/RepositoryCacheUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..daba6cdcd4570c5f6b8fdce5fe403390a3c2f94a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/RepositoryCacheUtils.java @@ -0,0 +1,70 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.group.GroupFacet; +import org.sonatype.nexus.repository.proxy.ProxyFacet; +import org.sonatype.nexus.repository.types.GroupType; +import org.sonatype.nexus.repository.types.ProxyType; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Utility class for consolidating repeated cache-related logic not exclusive to individual facets and components. + * + * @since 3.0 + */ +public final class RepositoryCacheUtils +{ + private RepositoryCacheUtils() { + // empty + } + + /** + * Invalidates the group or proxy caches of the specified repository based on type. + * + * This is a no-op for hosted repositories. + */ + public static void invalidateCaches(final Repository repository) { + checkNotNull(repository); + if (GroupType.NAME.equals(repository.getType().getValue())) { + invalidateGroupCaches(repository); + } else if (ProxyType.NAME.equals(repository.getType().getValue())) { + invalidateProxyAndNegativeCaches(repository); + } + } + + /** + * Invalidates the group caches for given repository. + */ + public static void invalidateGroupCaches(final Repository repository) { + checkNotNull(repository); + checkArgument(GroupType.NAME.equals(repository.getType().getValue())); + GroupFacet groupFacet = repository.facet(GroupFacet.class); + groupFacet.invalidateGroupCaches(); + } + + /** + * Invalidates the proxy and negative caches for given repository. + */ + public static void invalidateProxyAndNegativeCaches(final Repository repository) { + checkNotNull(repository); + checkArgument(ProxyType.NAME.equals(repository.getType().getValue())); + ProxyFacet proxyFacet = repository.facet(ProxyFacet.class); + proxyFacet.invalidateProxyCaches(); + NegativeCacheFacet negativeCacheFacet = repository.facet(NegativeCacheFacet.class); + negativeCacheFacet.invalidate(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/internal/NegativeCacheFacetImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/internal/NegativeCacheFacetImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..e7e1223232e6e91366f4a335b86ba4d46537ba71 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/internal/NegativeCacheFacetImpl.java @@ -0,0 +1,213 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache.internal; + +import java.util.concurrent.TimeUnit; + +import javax.cache.Cache; +import javax.cache.Cache.Entry; +import javax.cache.expiry.CreatedExpiryPolicy; +import javax.cache.expiry.Duration; +import javax.inject.Inject; +import javax.inject.Named; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +import org.sonatype.goodies.common.Time; +import org.sonatype.nexus.cache.CacheHelper; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.cache.NegativeCacheFacet; +import org.sonatype.nexus.repository.cache.NegativeCacheKey; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.config.ConfigurationFacet; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Status; + +import com.google.common.annotations.VisibleForTesting; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.FacetSupport.State.STARTED; + +/** + * Default {@link NegativeCacheFacet} implementation. + * + * @since 3.0 + */ +@Named +public class NegativeCacheFacetImpl + extends FacetSupport + implements NegativeCacheFacet +{ + private final CacheHelper cacheHelper; + + @VisibleForTesting + static final String CONFIG_KEY = "negativeCache"; + + @VisibleForTesting + static class Config + { + @NotNull + public Boolean enabled = Boolean.TRUE; + + /** + * Time-to-live seconds. + */ + @NotNull + @Min(0) + public Integer timeToLive = Time.hours(24).toSecondsI(); + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "enabled=" + enabled + + ", timeToLive=" + timeToLive + + '}'; + } + } + + private Config config; + + private Cache cache; + + @Inject + public NegativeCacheFacetImpl(final CacheHelper cacheHelper) { + this.cacheHelper = checkNotNull(cacheHelper); + } + + @Override + protected void doValidate(final Configuration configuration) throws Exception { + facet(ConfigurationFacet.class).validateSection(configuration, CONFIG_KEY, Config.class); + } + + @Override + protected void doConfigure(final Configuration configuration) throws Exception { + config = facet(ConfigurationFacet.class).readSection(configuration, CONFIG_KEY, Config.class); + log.debug("Config: {}", config); + } + + @Override + protected void doInit(final Configuration configuration) throws Exception { + super.doInit(configuration); + + // create cache if enabled + if (config.enabled) { + maybeCreateCache(); + } + } + + @Override + protected void doUpdate(final Configuration configuration) throws Exception { + Config previous = config; + super.doUpdate(configuration); + + // re-create cache if enabled or cache settings changed + if (config.enabled) { + if (!previous.enabled || !config.timeToLive.equals(previous.timeToLive)) { + maybeDestroyCache(); + maybeCreateCache(); + } + } + else { + // else destroy cache if disabled + maybeDestroyCache(); + } + } + + @Override + protected void doDelete() throws Exception { + maybeDestroyCache(); + config = null; + } + + @Override + protected void doDestroy() throws Exception { + cache = null; + config = null; + } + + private void maybeCreateCache() { + if (cache == null) { + log.debug("Creating negative-cache for: {}", getRepository()); + cache = cacheHelper.maybeCreateCache(getCacheName(), NegativeCacheKey.class, Status.class, + CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.MINUTES, config.timeToLive))); + log.debug("Created negative-cache: {}", cache); + } + } + + private void maybeDestroyCache() { + log.debug("Destroying negative-cache for: {}", getRepository()); + cacheHelper.maybeDestroyCache(getCacheName()); + cache = null; + } + + @Override + @Guarded(by = STARTED) + public Status get(final NegativeCacheKey key) { + checkNotNull(key); + if (cache != null) { + return cache.get(key); + } + return null; + } + + @Override + @Guarded(by = STARTED) + public void put(final NegativeCacheKey key, final Status status) { + checkNotNull(key); + checkNotNull(status); + if (cache != null) { + log.debug("Adding {}={} to negative-cache of {}", key, status, getRepository()); + cache.put(key, status); + } + } + + @Override + @Guarded(by = STARTED) + public void invalidate(final NegativeCacheKey key) { + checkNotNull(key); + if (cache != null && cache.remove(key)) { + log.debug("Removing {} from negative-cache of {}", key, getRepository()); + } + } + + @Override + public void invalidateSubset(final NegativeCacheKey key) { + if (cache != null) { + invalidate(key); + for (final Entry entry : cache) { + if (!key.equals(entry.getKey()) && key.isParentOf(entry.getKey())) { + invalidate(entry.getKey()); + } + } + } + } + + @Override + @Guarded(by = STARTED) + public void invalidate() { + if (cache != null) { + log.debug("Removing all from negative-cache of {}", getRepository()); + cache.removeAll(); + } + } + + @Override + public NegativeCacheKey getCacheKey(final Context context) { + return new PathNegativeCacheKey(context.getRequest().getPath()); + } + + public String getCacheName() { + return getRepository().getName() + "#negative-cache"; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/internal/PathNegativeCacheKey.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/internal/PathNegativeCacheKey.java new file mode 100644 index 0000000000000000000000000000000000000000..84561fe575852dfe1cf702d9063dbd80c981d21d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/internal/PathNegativeCacheKey.java @@ -0,0 +1,73 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache.internal; + +import org.sonatype.nexus.repository.cache.NegativeCacheKey; + +import static com.google.common.base.Preconditions.checkNotNull; + +// TODO: implement Externalizable + +/** + * A path based {@link NegativeCacheKey}. + * + * @since 3.0 + */ +public class PathNegativeCacheKey + implements NegativeCacheKey +{ + private final String path; + + public PathNegativeCacheKey(final String path) { + this.path = checkNotNull(path); + } + + /** + * @param key child key + * @return true if child key path starts with this key path + */ + @Override + public boolean isParentOf(final NegativeCacheKey key) { + checkNotNull(key); + return path.endsWith("/") + && key instanceof PathNegativeCacheKey + && ((PathNegativeCacheKey) key).path.startsWith(path); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + PathNegativeCacheKey that = (PathNegativeCacheKey) o; + + return path.equals(that.path); + } + + @Override + public int hashCode() { + return path.hashCode(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "path='" + path + '\'' + + '}'; + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/internal/ValueNegativeCacheKey.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/internal/ValueNegativeCacheKey.java new file mode 100644 index 0000000000000000000000000000000000000000..639d3ae382ad5f4f99fd44adcfd19f17c29a8bf9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/internal/ValueNegativeCacheKey.java @@ -0,0 +1,71 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache.internal; + +import org.sonatype.nexus.repository.cache.NegativeCacheKey; + +import static com.google.common.base.Preconditions.checkNotNull; + +// TODO: implement Externalizable + +/** + * A simple value based {@link NegativeCacheKey}. + * + * @since 3.0 + */ +public class ValueNegativeCacheKey + implements NegativeCacheKey +{ + private final String value; + + public ValueNegativeCacheKey(final String value) { + this.value = checkNotNull(value); + } + + /** + * @param key child key + * @return false + */ + @Override + public boolean isParentOf(final NegativeCacheKey key) { + checkNotNull(key); + return false; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ValueNegativeCacheKey that = (ValueNegativeCacheKey) o; + + return value.equals(that.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "value='" + value + '\'' + + '}'; + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..65fbc6d708cc1d62d371f7307ada9cb066bcc5f0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/cache/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Repository cache support. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.cache; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/RepositoryConditions.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/RepositoryConditions.java new file mode 100644 index 0000000000000000000000000000000000000000..410ac46520c4f518ec1564b66532adae32c4c718 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/RepositoryConditions.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.capability; + +import java.util.function.Supplier; + +import org.sonatype.nexus.capability.Condition; + +/** + * Factory of {@link Condition}s related to repositories. + * + * @since 3.0 + */ +public interface RepositoryConditions +{ + /** + * Creates a new condition that is satisfied when a repository is in service. + * + * @param repositoryName getter for repository name (usually condition specific property) + * @return created condition + */ + Condition repositoryIsOnline(Supplier repositoryName); + + /** + * Creates a new condition that is satisfied when a repository exists. + * + * @param repositoryName getter for repository name (usually condition specific property) + * @return created condition + */ + Condition repositoryExists(Supplier repositoryName); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/internal/RepositoryConditionSupport.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/internal/RepositoryConditionSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..10eb08c9c37eb51c96f37a5a15ad53fcddd2a181 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/internal/RepositoryConditionSupport.java @@ -0,0 +1,152 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.capability.internal; + +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Supplier; + +import org.sonatype.nexus.capability.CapabilityContext; +import org.sonatype.nexus.capability.CapabilityContextAware; +import org.sonatype.nexus.capability.CapabilityEvent; +import org.sonatype.nexus.capability.CapabilityIdentity; +import org.sonatype.nexus.capability.condition.ConditionSupport; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.manager.RepositoryCreatedEvent; +import org.sonatype.nexus.repository.manager.RepositoryLoadedEvent; +import org.sonatype.nexus.repository.manager.RepositoryManager; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.Subscribe; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * Support class for repository conditions. + * + * @since 3.0 + */ +public abstract class RepositoryConditionSupport + extends ConditionSupport + implements CapabilityContextAware +{ + + private final RepositoryManager repositoryManager; + + private final Supplier repositoryName; + + private final ReentrantReadWriteLock bindLock; + + private CapabilityIdentity capabilityIdentity; + + private String repositoryBeforeLastUpdate; + + public RepositoryConditionSupport(final EventManager eventManager, + final RepositoryManager repositoryManager, + final Supplier repositoryName) + { + super(eventManager, false); + this.repositoryManager = checkNotNull(repositoryManager); + this.repositoryName = checkNotNull(repositoryName); + bindLock = new ReentrantReadWriteLock(); + } + + @Override + public RepositoryConditionSupport setContext(final CapabilityContext context) { + checkState(!isActive(), "Cannot contextualize when already bounded"); + checkState(capabilityIdentity == null, "Already contextualized with id '" + capabilityIdentity + "'"); + capabilityIdentity = context.id(); + + return this; + } + + @Override + protected void doBind() { + try { + bindLock.writeLock().lock(); + for (final Repository repository : repositoryManager.browse()) { + handle(new RepositoryCreatedEvent(repository)); + } + } + finally { + bindLock.writeLock().unlock(); + } + getEventManager().register(this); + } + + @Override + public void doRelease() { + getEventManager().unregister(this); + } + + public abstract void handle(final RepositoryCreatedEvent event); + + @Override + protected void setSatisfied(final boolean satisfied) { + try { + bindLock.readLock().lock(); + super.setSatisfied(satisfied); + } + finally { + bindLock.readLock().unlock(); + } + } + + @AllowConcurrentEvents + @Subscribe + public void handle(final RepositoryLoadedEvent event) { + handle(new RepositoryCreatedEvent(event.getRepository())); + } + + @AllowConcurrentEvents + @Subscribe + public void handle(final CapabilityEvent.BeforeUpdate event) { + if (event.getReference().context().id().equals(capabilityIdentity)) { + repositoryBeforeLastUpdate = getRepositoryName(); + } + } + + @AllowConcurrentEvents + @Subscribe + public void handle(final CapabilityEvent.AfterUpdate event) { + if (event.getReference().context().id().equals(capabilityIdentity)) { + if (!sameRepositoryAs(repositoryBeforeLastUpdate)) { + try { + bindLock.writeLock().lock(); + for (final Repository repository : repositoryManager.browse()) { + handle(new RepositoryCreatedEvent(repository)); + } + } + finally { + bindLock.writeLock().unlock(); + } + } + } + } + + /** + * Checks that condition is about the passed in repository name. + * + * @param repositoryName to check + * @return true, if condition repository matches the specified repository name + */ + protected boolean sameRepositoryAs(final String repositoryName) { + return repositoryName != null && repositoryName.equals(getRepositoryName()); + } + + protected String getRepositoryName() { + return repositoryName.get(); + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/internal/RepositoryConditionsImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/internal/RepositoryConditionsImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..1eb5cd6ab9b9e9e3aa5c481ee962fd64fcf12d2e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/internal/RepositoryConditionsImpl.java @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.capability.internal; + +import java.util.function.Supplier; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.capability.Condition; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.repository.capability.RepositoryConditions; +import org.sonatype.nexus.repository.capability.internal.RepositoryExistsCondition; +import org.sonatype.nexus.repository.capability.internal.RepositoryOnlineCondition; +import org.sonatype.nexus.repository.manager.RepositoryManager; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default implementation of {@link RepositoryConditions}. + * + * @since 3.0 + */ +@Named +@Singleton +public class RepositoryConditionsImpl + implements RepositoryConditions +{ + private final EventManager eventManager; + + private final RepositoryManager repositoryManager; + + @Inject + public RepositoryConditionsImpl(final EventManager eventManager, + final RepositoryManager repositoryManager) { + this.eventManager = checkNotNull(eventManager); + this.repositoryManager = checkNotNull(repositoryManager); + } + + @Override + public Condition repositoryIsOnline(final Supplier repositoryName) { + return new RepositoryOnlineCondition(eventManager, repositoryManager, repositoryName); + } + + @Override + public Condition repositoryExists(final Supplier repositoryName) { + return new RepositoryExistsCondition(eventManager, repositoryManager, repositoryName); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/internal/RepositoryExistsCondition.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/internal/RepositoryExistsCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..85ecec2b3831c32f5b481bfe82478be7ac3b7edd --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/internal/RepositoryExistsCondition.java @@ -0,0 +1,91 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.capability.internal; + +import java.util.function.Supplier; + +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.repository.manager.RepositoryCreatedEvent; +import org.sonatype.nexus.repository.manager.RepositoryDeletedEvent; +import org.sonatype.nexus.repository.manager.RepositoryManager; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.Subscribe; + +/** + * A condition that is satisfied when a repository exists. + * + * @since 3.0 + */ +public class RepositoryExistsCondition + extends RepositoryConditionSupport +{ + + public RepositoryExistsCondition(final EventManager eventManager, + final RepositoryManager repositoryManager, + final Supplier repositoryName) + { + super(eventManager, repositoryManager, repositoryName); + } + + @Override + @AllowConcurrentEvents + @Subscribe + public void handle(final RepositoryCreatedEvent event) { + if (sameRepositoryAs(event.getRepository().getName())) { + setSatisfied(true); + } + } + + @AllowConcurrentEvents + @Subscribe + public void handle(final RepositoryDeletedEvent event) { + if (sameRepositoryAs(event.getRepository().getName())) { + setSatisfied(false); + } + } + + @Override + public String toString() { + try { + final String repositoryName = getRepositoryName(); + return String.format("Repository '%s' exists", repositoryName); + } + catch (Exception ignore) { + return "Repository '(could not be evaluated)' exists"; + } + } + + @Override + public String explainSatisfied() { + try { + final String repositoryName = getRepositoryName(); + return String.format("Repository '%s' exists", repositoryName); + } + catch (Exception ignore) { + return "Repository '(could not be evaluated)' exists"; + } + } + + @Override + public String explainUnsatisfied() { + try { + final String repositoryName = getRepositoryName(); + return String.format("Repository '%s' does not exist", repositoryName); + } + catch (Exception ignore) { + return "Repository '(could not be evaluated)' does not exist"; + } + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/internal/RepositoryOnlineCondition.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/internal/RepositoryOnlineCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..1ffd25bb5ebcf8c37c2f01712bbe351eb1245738 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/internal/RepositoryOnlineCondition.java @@ -0,0 +1,100 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.capability.internal; + +import java.util.function.Supplier; + +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.repository.manager.RepositoryCreatedEvent; +import org.sonatype.nexus.repository.manager.RepositoryDeletedEvent; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.manager.RepositoryUpdatedEvent; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.Subscribe; + +/** + * A condition that is satisfied when a repository is online. + * + * @since 3.0 + */ +public class RepositoryOnlineCondition + extends RepositoryConditionSupport +{ + + public RepositoryOnlineCondition(final EventManager eventManager, + final RepositoryManager repositoryManager, + final Supplier repositoryName) + { + super(eventManager, repositoryManager, repositoryName); + } + + @Override + @AllowConcurrentEvents + @Subscribe + public void handle(final RepositoryCreatedEvent event) { + if (sameRepositoryAs(event.getRepository().getName())) { + setSatisfied(event.getRepository().getConfiguration().isOnline()); + } + } + + @AllowConcurrentEvents + @Subscribe + public void handle(final RepositoryUpdatedEvent event) { + if (sameRepositoryAs(event.getRepository().getName())) { + setSatisfied(event.getRepository().getConfiguration().isOnline()); + } + } + + @AllowConcurrentEvents + @Subscribe + public void handle(final RepositoryDeletedEvent event) { + if (sameRepositoryAs(event.getRepository().getName())) { + setSatisfied(false); + } + } + + @Override + public String toString() { + try { + final String repositoryName = getRepositoryName(); + return String.format("Repository '%s' is online", repositoryName); + } + catch (Exception ignore) { + return "Repository '(could not be evaluated)' is online"; + } + } + + @Override + public String explainSatisfied() { + try { + final String repositoryName = getRepositoryName(); + return String.format("Repository '%s' is online", repositoryName); + } + catch (Exception ignore) { + return "Repository '(could not be evaluated)' is online"; + } + } + + @Override + public String explainUnsatisfied() { + try { + final String repositoryName = getRepositoryName(); + return String.format("Repository '%s' is offline", repositoryName); + } + catch (Exception ignore) { + return "Repository '(could not be evaluated)' is offline"; + } + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..7479730a64e0d7d00f372d437b72eb07cd1488b4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/capability/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Repository capability integration. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.capability; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/Configuration.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/Configuration.java new file mode 100644 index 0000000000000000000000000000000000000000..b209928bb924cc0adc699d0cbe61f59a38e8f54e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/Configuration.java @@ -0,0 +1,188 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.collect.DetachingList; +import org.sonatype.nexus.common.collect.DetachingMap; +import org.sonatype.nexus.common.collect.DetachingSet; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.AbstractEntity; +import org.sonatype.nexus.common.io.SanitizingJsonOutputStream; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.google.common.collect.Maps; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Lists.newArrayList; +import static java.lang.String.format; +import static org.sonatype.nexus.common.text.Strings2.mask; + +/** + * Repository configuration. + * + * @since 3.0 + */ +public class Configuration + extends AbstractEntity + implements Cloneable +{ + private static final List SENSITIVE_FIELD_NAMES = newArrayList("applicationPassword", "password", + "systemPassword", "secret"); + + private static final TypeReference>> ATTRIBUTES_TYPE_REF = new TypeReference>>() { }; + + private static final ObjectWriter ATTRIBUTES_JSON_WRITER = new ObjectMapper().writerFor(ATTRIBUTES_TYPE_REF); + + private static final ObjectReader ATTRIBUTES_JSON_READER = new ObjectMapper().readerFor(ATTRIBUTES_TYPE_REF); + + private Logger log = LoggerFactory.getLogger(Configuration.class); + + private String repositoryName; + + private String recipeName; + + private boolean online; + + private Map> attributes; + + public String getRepositoryName() { + return repositoryName; + } + + public void setRepositoryName(final String repositoryName) { + this.repositoryName = checkNotNull(repositoryName); + } + + public String getRecipeName() { + return recipeName; + } + + public void setRecipeName(final String recipeName) { + this.recipeName = checkNotNull(recipeName); + } + + /** + * @return true, if repository should serve inbound requests + */ + public boolean isOnline() { + return online; + } + + /** + * @param online true, if repository should serve inbound requests + */ + public void setOnline(final boolean online) { + this.online = online; + } + + @Nullable + public Map> getAttributes() { + return attributes; + } + + public void setAttributes(@Nullable final Map> attributes) { + this.attributes = attributes; + } + + public NestedAttributesMap attributes(final String key) { + checkNotNull(key); + + if (attributes == null) { + attributes = Maps.newHashMap(); + } + + Map map = attributes.get(key); + if (map == null) { + map = Maps.newHashMap(); + attributes.put(key, map); + } + + return new NestedAttributesMap(key, map); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "repositoryName='" + repositoryName + '\'' + + ", recipeName='" + recipeName + '\'' + + ", attributes=" + obfuscatedAttributes() + + '}'; + } + + /** + * @return string representation with sensitive data (passwords) obfuscated + * @since 3.7 + */ + private String obfuscatedAttributes() { + try (ByteArrayOutputStream obfuscatedAttrs = new ByteArrayOutputStream()) { + try (SanitizingJsonOutputStream sanitizer = new SanitizingJsonOutputStream(obfuscatedAttrs, + SENSITIVE_FIELD_NAMES, + mask("password"))) { + sanitizer.write(ATTRIBUTES_JSON_WRITER.writeValueAsBytes(attributes)); + } + + Object result = ATTRIBUTES_JSON_READER.readValue(obfuscatedAttrs.toByteArray()); + return result != null ? result.toString() : ""; + } + catch (IOException e) { + log.error("Error obfuscating attributes", e); + return format("<>", e.getMessage()); + } + } + + /** + * Returns a deeply cloned copy. Note that Entity.entityMetadata is not deep-copied. + */ + public Configuration copy() { + try { + Configuration c = (Configuration) clone(); + c.attributes = copy(attributes); + return c; + } + catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + /** + * Returns a lazy copy; for collections the copy is only taken as the content is touched. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + private V copy(final V value) { + Object copy = value; + if (value instanceof Map) { + copy = new DetachingMap((Map) value, this::copy); + } + else if (value instanceof List) { + copy = new DetachingList((List) value, this::copy); + } + else if (value instanceof Set) { + copy = new DetachingSet((Set) value, this::copy); + } + return (V) copy; + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/ConfigurationFacet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/ConfigurationFacet.java new file mode 100644 index 0000000000000000000000000000000000000000..643fca739ee25a4efee69e806a25dd4bd6576bf3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/ConfigurationFacet.java @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config; + +import javax.validation.ValidationException; + +import org.sonatype.nexus.repository.Facet; + +/** + * Configuration {@link Facet}. + * + * @since 3.0 + */ +@Facet.Exposed +public interface ConfigurationFacet + extends Facet +{ + /** + * Persist configuration. + */ + void save() throws Exception; + + /** + * Convert value to given type. + */ + T convert(Object value, Class type); + + /** + * Read object of given type from named configuration section. + */ + T readSection(Configuration configuration, String section, Class type); + + /** + * Validate given object. + * + * @throws ValidationException + */ + void validate(Object value, Class... groups); + + /** + * Read and validate object from named configuration section. + */ + T validateSection(Configuration configuration, String section, Class type, Class... groups); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/ConfigurationObjectMapperCustomizer.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/ConfigurationObjectMapperCustomizer.java new file mode 100644 index 0000000000000000000000000000000000000000..acea30aa0779f5657887a18b9ddeec6920ad4d89 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/ConfigurationObjectMapperCustomizer.java @@ -0,0 +1,31 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config; + +import org.sonatype.nexus.repository.config.internal.ConfigurationObjectMapperProvider; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Customizes {@link ObjectMapper} for repository configuration. + * + * @since 3.0 + * @see ConfigurationObjectMapperProvider + */ +public interface ConfigurationObjectMapperCustomizer +{ + /** + * Applies any customization to the passed in {@link ObjectMapper}, is never {@code null}. + */ + void customize(ObjectMapper objectMapper); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/UniqueRepositoryName.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/UniqueRepositoryName.java new file mode 100644 index 0000000000000000000000000000000000000000..d5a3afbe16dc93a43a25db6dc0d10c8981560020 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/UniqueRepositoryName.java @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Validate a name as unique across all Repositories. + * + * @since 3.0 + */ +@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE}) +@Retention(RUNTIME) +@Constraint(validatedBy = UniqueRepositoryNameValidator.class) +public @interface UniqueRepositoryName +{ + String message() default "Name is already used, must be unique (ignoring case)"; + + Class[] groups() default { }; + + Class[] payload() default { }; +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/UniqueRepositoryNameValidator.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/UniqueRepositoryNameValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..5ceb32d68fb49589d5ebdc083a684d4d7303d20f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/UniqueRepositoryNameValidator.java @@ -0,0 +1,44 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.validation.ConstraintValidatorContext; + +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.validation.ConstraintValidatorSupport; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Ensure that Repository names are unique case-insensitively. + * + * @since 3.0 + */ +@Named +public class UniqueRepositoryNameValidator + extends ConstraintValidatorSupport +{ + private final RepositoryManager repositoryManager; + + @Inject + public UniqueRepositoryNameValidator(final RepositoryManager repositoryManager) { + this.repositoryManager = checkNotNull(repositoryManager); + } + + @Override + public boolean isValid(final String value, final ConstraintValidatorContext context) { + return !repositoryManager.exists(value); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationCreatedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationCreatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..de0ab7d17fba4cbaa4f96899b0c452d840b578d5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationCreatedEvent.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config.internal; + +import org.sonatype.nexus.common.entity.EntityCreatedEvent; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.repository.config.Configuration; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Repository {@link Configuration} created event. + * + * @since 3.1 + */ +public class ConfigurationCreatedEvent + extends EntityCreatedEvent + implements ConfigurationEvent +{ + private final String repositoryName; + + public ConfigurationCreatedEvent(final EntityMetadata metadata, final String repositoryName) { + super(metadata); + this.repositoryName = checkNotNull(repositoryName); + } + + @Override + public String getRepositoryName() { + return repositoryName; + } + + @Override + public Configuration getConfiguration() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationDeletedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationDeletedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..e4a6e74b785a1584afa87a236ebe4129212bda82 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationDeletedEvent.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config.internal; + +import org.sonatype.nexus.common.entity.EntityDeletedEvent; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.repository.config.Configuration; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Repository {@link Configuration} deleted event. + * + * @since 3.1 + */ +public class ConfigurationDeletedEvent + extends EntityDeletedEvent + implements ConfigurationEvent +{ + private final String repositoryName; + + public ConfigurationDeletedEvent(final EntityMetadata metadata, final String repositoryName) { + super(metadata); + this.repositoryName = checkNotNull(repositoryName); + } + + @Override + public String getRepositoryName() { + return repositoryName; + } + + @Override + public Configuration getConfiguration() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationEntityAdapter.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..88d5a62d37bf396a8f16c3510394786067ebe4de --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationEntityAdapter.java @@ -0,0 +1,189 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config.internal; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.Function; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.entity.EntityEvent; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.OIndexNameBuilder; +import org.sonatype.nexus.orient.entity.AttachedEntityMetadata; +import org.sonatype.nexus.orient.entity.IterableEntityAdapter; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.security.PasswordHelper; + +import com.orientechnologies.orient.core.collate.OCaseInsensitiveCollate; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * {@link Configuration} entity-adapter. + * + * @since 3.0 + */ +@Named +@Singleton +public class ConfigurationEntityAdapter + extends IterableEntityAdapter +{ + private static final String DB_CLASS = new OClassNameBuilder() + .type("repository") + .build(); + + private static final String P_REPOSITORY_NAME = "repository_name"; + + private static final String P_RECIPE_NAME = "recipe_name"; + + private static final String P_ONLINE = "online"; + + private static final String P_ATTRIBUTES = "attributes"; + + private static final String I_REPOSITORY_NAME = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_REPOSITORY_NAME) + .build(); + + private final PasswordHelper passwordHelper; + + @Inject + public ConfigurationEntityAdapter(final PasswordHelper passwordHelper) { + super(DB_CLASS); + this.passwordHelper = checkNotNull(passwordHelper); + } + + @Override + protected void defineType(final OClass type) { + type.createProperty(P_REPOSITORY_NAME, OType.STRING) + .setCollate(new OCaseInsensitiveCollate()) + .setMandatory(true) + .setNotNull(true); + type.createProperty(P_RECIPE_NAME, OType.STRING) + .setMandatory(true) + .setNotNull(true); + type.createProperty(P_ONLINE, OType.BOOLEAN) + .setMandatory(true) + .setNotNull(true); + type.createProperty(P_ATTRIBUTES, OType.EMBEDDEDMAP); + type.createIndex(I_REPOSITORY_NAME, INDEX_TYPE.UNIQUE, P_REPOSITORY_NAME); + } + + @Override + protected Configuration newEntity() { + return new Configuration(); + } + + @Override + protected void readFields(final ODocument document, final Configuration entity) { + String recipeName = document.field(P_RECIPE_NAME, OType.STRING); + String repositoryName = document.field(P_REPOSITORY_NAME, OType.STRING); + Boolean online = document.field(P_ONLINE, OType.BOOLEAN); + Map> attributes = document.field(P_ATTRIBUTES, OType.EMBEDDEDMAP); + + entity.setRecipeName(recipeName); + entity.setRepositoryName(repositoryName); + entity.setOnline(online); + entity.setAttributes(decrypt(attributes)); + } + + /** + * Returns a copy of the attributes with sensitive data decrypted. + */ + private Map> decrypt(Map> attributes) { + return process(attributes, passwordHelper::tryDecrypt); + } + + @Override + protected void writeFields(final ODocument document, final Configuration entity) { + Map> attributes = entity.getAttributes(); + + document.field(P_RECIPE_NAME, entity.getRecipeName()); + document.field(P_REPOSITORY_NAME, entity.getRepositoryName()); + document.field(P_ONLINE, entity.isOnline()); + document.field(P_ATTRIBUTES, encrypt(attributes)); + } + + /** + * Returns a copy of the attributes with sensitive data encrypted. + */ + private Map> encrypt(final Map> attributes) { + return process(attributes, passwordHelper::encrypt); + } + + /** + * Returns a copy of the attributes with sensitive data transformed using the given function. + */ + @SuppressWarnings("unchecked") + @Nullable + private Map process(@Nullable final Map map, final Function transform) { + if (map == null) { + return null; + } + Map processed = new HashMap<>(map.size()); + for (Entry entry : map.entrySet()) { + Object value = entry.getValue(); + if (entry.getValue() instanceof Map) { + value = process((Map) value, transform); + } + else if (isSensitiveEntry(entry)) { + value = transform.apply((String) value); + } + processed.put(entry.getKey(), (V) value); + } + return processed; + } + + /** + * Returns {@code true} if entry carries sensitive data, that should be encrypted/decrypted on externalization. + */ + private boolean isSensitiveEntry(final Entry entry) { + return entry.getKey().toLowerCase(Locale.ENGLISH).endsWith("password") && entry.getValue() instanceof String; + } + + @Override + public boolean sendEvents() { + return true; + } + + @Nullable + @Override + public EntityEvent newEvent(final ODocument document, final EventKind eventKind) { + final EntityMetadata metadata = new AttachedEntityMetadata(this, document); + final String repositoryName = document.field(P_REPOSITORY_NAME); + + log.trace("newEvent: eventKind: {}, repositoryName: {}, metadata: {}", eventKind, repositoryName, metadata); + switch (eventKind) { + case CREATE: + return new ConfigurationCreatedEvent(metadata, repositoryName); + case UPDATE: + return new ConfigurationUpdatedEvent(metadata, repositoryName); + case DELETE: + return new ConfigurationDeletedEvent(metadata, repositoryName); + default: + return null; + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..40c320096d719f21078d423dc044f07aeb7f33ca --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationEvent.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config.internal; + +import org.sonatype.nexus.repository.config.Configuration; + +/** + * Repository {@link Configuration} event. + * + * @since 3.1 + */ +public interface ConfigurationEvent +{ + boolean isLocal(); + + String getRepositoryName(); + + Configuration getConfiguration(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationFacetImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationFacetImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..a89e1d7e535ec6fc3ea2e1e8c8eb1dd71a21fcef --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationFacetImpl.java @@ -0,0 +1,125 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config.internal; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validator; + +import org.sonatype.nexus.common.collect.AttributesMap; +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.config.ConfigurationFacet; +import org.sonatype.nexus.validation.ConstraintViolations; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default {@link ConfigurationFacet} implementation. + * + * @since 3.0 + */ +@Named +public class ConfigurationFacetImpl + extends FacetSupport + implements ConfigurationFacet +{ + private final ConfigurationStore store; + + private final ObjectMapper objectMapper; + + private final Provider validatorProvider; + + @Inject + public ConfigurationFacetImpl(final ConfigurationStore store, + @Named(ConfigurationObjectMapperProvider.NAME) final ObjectMapper objectMapper, + final Provider validatorProvider) + { + this.store = checkNotNull(store); + this.objectMapper = checkNotNull(objectMapper); + this.validatorProvider = checkNotNull(validatorProvider); + } + + @Override + public void save() throws Exception { + store.update(getRepository().getConfiguration()); + log.debug("Saved"); + } + + @Override + public T convert(final Object value, final Class type) { + checkNotNull(value); + checkNotNull(type); + log.trace("Converting value: {} to type: {}", value, type); + return objectMapper.convertValue(value, type); + } + + @Override + public T readSection(final Configuration configuration, final String section, final Class type) { + checkNotNull(configuration); + checkNotNull(section); + log.trace("Reading section: {}", section); + AttributesMap attributes = configuration.attributes(section); + return convert(attributes.backing(), type); + } + + @Override + public void validate(final Object value, final Class... groups) { + checkNotNull(value); + checkNotNull(groups); + + if (log.isTraceEnabled()) { + log.trace("Validating: {} in groups: {}", value, Arrays.asList(groups)); + } + + Validator validator = validatorProvider.get(); + Set> violations = validator.validate(value, groups); + ConstraintViolations.maybePropagate(violations, log); + } + + /** + * Wrap attribute section value to allow configuration hierarchy to be encoded into property path. + */ + private static class SectionWrapper + { + @SuppressWarnings("unused") + @Valid + private Map attributes; + + public SectionWrapper(final String name, final Object value) { + this.attributes = Collections.singletonMap(name, value); + } + } + + @Override + public T validateSection(final Configuration configuration, + final String section, + final Class type, + final Class... groups) + { + T value = readSection(configuration, section, type); + SectionWrapper wrapper = new SectionWrapper(section, value); + validate(wrapper, groups); + return value; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationObjectMapperProvider.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationObjectMapperProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..b6efd475aeca8154fb4e09266fbd15baa72dd928 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationObjectMapperProvider.java @@ -0,0 +1,62 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config.internal; + +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.config.ConfigurationObjectMapperCustomizer; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Provides {@link ObjectMapper} for repository configuration. + * + * @since 3.0 + */ +@Named(ConfigurationObjectMapperProvider.NAME) +@Singleton +public class ConfigurationObjectMapperProvider + extends ComponentSupport + implements Provider +{ + public static final String NAME = "repository-configuration"; + + private final Map customizers; + + @Inject + public ConfigurationObjectMapperProvider(final Map customizers) + { + this.customizers = checkNotNull(customizers); + } + + @Override + public ObjectMapper get() { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + for (ConfigurationObjectMapperCustomizer customizer : customizers.values()) { + customizer.customize(mapper); + } + // TODO: ISO-8601, joda + // TODO: null handling + return mapper; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationStore.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationStore.java new file mode 100644 index 0000000000000000000000000000000000000000..d43d1a07b49b58661430012e00e38c27358ef208 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationStore.java @@ -0,0 +1,35 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config.internal; + +import java.util.List; + +import org.sonatype.goodies.lifecycle.Lifecycle; +import org.sonatype.nexus.repository.config.Configuration; + +/** + * {@link Configuration} store. + * + * @since 3.0 + */ +public interface ConfigurationStore + extends Lifecycle +{ + List list(); + + void create(Configuration configuration); + + void update(Configuration configuration); + + void delete(Configuration configuration); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationStoreImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationStoreImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..cf6b0a74f5d69df83ff59b0d4a3c2c59e2c79b02 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationStoreImpl.java @@ -0,0 +1,98 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config.internal; + +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseInstanceNames; +import org.sonatype.nexus.repository.config.Configuration; + +import com.google.common.collect.ImmutableList; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SCHEMAS; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTx; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; + +/** + * Orient {@link ConfigurationStore} implementation. + * + * @since 3.0 + */ +@Named +@ManagedLifecycle(phase = SCHEMAS) +@Singleton +public class ConfigurationStoreImpl + extends StateGuardLifecycleSupport + implements ConfigurationStore +{ + private final Provider databaseInstance; + + private final ConfigurationEntityAdapter entityAdapter; + + @Inject + public ConfigurationStoreImpl(@Named(DatabaseInstanceNames.CONFIG) final Provider databaseInstance, + final ConfigurationEntityAdapter entityAdapter) + { + this.databaseInstance = checkNotNull(databaseInstance); + this.entityAdapter = checkNotNull(entityAdapter); + } + + @Override + protected void doStart() throws Exception { + try (ODatabaseDocumentTx db = databaseInstance.get().connect()) { + entityAdapter.register(db); + } + } + + @Override + @Guarded(by = STARTED) + public List list() { + return inTx(databaseInstance).call(db -> ImmutableList.copyOf(entityAdapter.browse(db))); + } + + @Override + @Guarded(by = STARTED) + public void create(final Configuration configuration) { + checkNotNull(configuration); + + inTxRetry(databaseInstance).run(db -> entityAdapter.addEntity(db, configuration)); + } + + @Override + @Guarded(by = STARTED) + public void update(final Configuration configuration) { + checkNotNull(configuration); + + inTxRetry(databaseInstance).run(db -> entityAdapter.editEntity(db, configuration)); + } + + @Override + @Guarded(by = STARTED) + public void delete(final Configuration configuration) { + checkNotNull(configuration); + + inTxRetry(databaseInstance).run(db -> entityAdapter.deleteEntity(db, configuration)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationUpdatedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationUpdatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..661e93b8b94df35cf8469c2594ba01c5dbace952 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/internal/ConfigurationUpdatedEvent.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config.internal; + +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.common.entity.EntityUpdatedEvent; +import org.sonatype.nexus.repository.config.Configuration; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Repository {@link Configuration} updated event. + * + * @since 3.1 + */ +public class ConfigurationUpdatedEvent + extends EntityUpdatedEvent + implements ConfigurationEvent +{ + private final String repositoryName; + + public ConfigurationUpdatedEvent(final EntityMetadata metadata, final String repositoryName) { + super(metadata); + this.repositoryName = checkNotNull(repositoryName); + } + + @Override + public String getRepositoryName() { + return repositoryName; + } + + @Override + public Configuration getConfiguration() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..0f880453a18af591daa2de9976454ebab3587e56 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/config/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Repository configuration-facet and support. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.config; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/group/GroupFacet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/group/GroupFacet.java new file mode 100644 index 0000000000000000000000000000000000000000..6f5713ff9181e370b0564840c2a0715b7d5a3f47 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/group/GroupFacet.java @@ -0,0 +1,61 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.group; + +import java.util.List; + +import org.sonatype.nexus.repository.Facet; +import org.sonatype.nexus.repository.Repository; + +/** + * Group facet. + * + * @since 3.0 + */ +@Facet.Exposed +public interface GroupFacet + extends Facet +{ + /** + * Check if given repository is a member of the group. + */ + boolean member(String repositoryName); + + /** + * Check if given repository is a member of the group. + */ + boolean member(Repository repository); + + /** + * Return list of all member repositories including transitive + * + * @since 3.6.1 + */ + List allMembers(); + + /** + * Return list of (non-transitive) member repositories. + */ + List members(); + + /** + * Return the full list of members, including the members of groups, but excluding groups. + */ + List leafMembers(); + + /** + * Removes all entries from the group cache and the member caches. + */ + void invalidateGroupCaches(); + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/group/GroupFacetImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/group/GroupFacetImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..1954e058c12b46b7fe8add72c095e5baaeea6a35 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/group/GroupFacetImpl.java @@ -0,0 +1,252 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.group; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.validation.ConstraintViolation; +import javax.validation.constraints.NotNull; + +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.Type; +import org.sonatype.nexus.repository.cache.CacheController; +import org.sonatype.nexus.repository.cache.CacheInfo; +import org.sonatype.nexus.repository.cache.RepositoryCacheUtils; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.config.ConfigurationFacet; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.types.GroupType; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.validation.ConstraintViolationFactory; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Iterables; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.FacetSupport.State.STARTED; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; +import static org.sonatype.nexus.validation.ConstraintViolations.maybeAdd; +import static org.sonatype.nexus.validation.ConstraintViolations.maybePropagate; + +/** + * Default {@link GroupFacet} implementation. + * + * @since 3.0 + */ +@Named("default") +public class GroupFacetImpl + extends FacetSupport + implements GroupFacet +{ + private final RepositoryManager repositoryManager; + + private final Type groupType; + + private final ConstraintViolationFactory constraintViolationFactory; + + @VisibleForTesting + static final String CONFIG_KEY = "group"; + + @VisibleForTesting + static class Config + { + @NotNull + @JsonDeserialize(as = LinkedHashSet.class) // retain order + public Set memberNames; + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "memberNames=" + memberNames + + '}'; + } + } + + private Config config; + + protected CacheController cacheController; + + @Inject + public GroupFacetImpl(final RepositoryManager repositoryManager, + final ConstraintViolationFactory constraintViolationFactory, + @Named(GroupType.NAME) final Type groupType) + { + this.repositoryManager = checkNotNull(repositoryManager); + this.groupType = checkNotNull(groupType); + this.constraintViolationFactory = checkNotNull(constraintViolationFactory); + } + + @Override + protected void doValidate(final Configuration configuration) throws Exception { + facet(ConfigurationFacet.class).validateSection(configuration, CONFIG_KEY, Config.class); + + if (getStateGuard().is(STARTED)) { + Config configToValidate = facet(ConfigurationFacet.class).readSection(configuration, CONFIG_KEY, Config.class); + + Set> violations = new HashSet<>(); + maybeAdd(violations, validateGroupDoesNotContainItself(configuration.getRepositoryName(), configToValidate)); + maybePropagate(violations, log); + } + } + + private boolean containsGroup(Repository root, String repositoryName, Set checkedGroups) { + return root.facet(GroupFacet.class).members().stream().anyMatch((repository) -> { + return checkedGroups.add(repository) && + (repository.getName().equals(repositoryName) || + (groupType.equals(repository.getType()) && containsGroup(repository, repositoryName, checkedGroups))); + }); + } + + ConstraintViolation validateGroupDoesNotContainItself(String repositoryName, Config config) { + Set checkedGroups = new HashSet<>(); + for (String memberName : config.memberNames) { + Repository repository = repositoryManager.get(memberName); + if (repository.getName().equals(repositoryName) || + (groupType.equals(repository.getType()) && containsGroup(repository, repositoryName, checkedGroups))) { + return constraintViolationFactory.createViolation(P_ATTRIBUTES + "." + CONFIG_KEY + ".memberNames", + "Group '" + repository.getName() + "' has a member repository '" + repositoryName + + "' and cannot be added to this list."); + } + } + + return null; + } + + @Override + protected void doConfigure(final Configuration configuration) throws Exception { + config = facet(ConfigurationFacet.class).readSection(configuration, CONFIG_KEY, Config.class); + + cacheController = new CacheController(-1, null); + + log.debug("Config: {}", config); + } + + @Override + protected void doUpdate(final Configuration configuration) throws Exception { + // detect member changes + Set previousMemberNames = config.memberNames; + super.doUpdate(configuration); + + // check whether any members or their ordering have changed + if (!Iterables.elementsEqual(config.memberNames, previousMemberNames)) { + cacheController.invalidateCache(); + } + } + + @Override + protected void doDestroy() throws Exception { + config = null; + } + + @Override + @Guarded(by = STARTED) + public boolean member(final String repositoryName) { + checkNotNull(repositoryName); + return config.memberNames.contains(repositoryName); + } + + @Override + @Guarded(by = STARTED) + public boolean member(final Repository repository) { + checkNotNull(repository); + return config.memberNames.contains(repository.getName()); + } + + @Override + @Guarded(by = STARTED) + public List members() { + final Repository repository = getRepository(); + + List members = new ArrayList<>(config.memberNames.size()); + for (String name : config.memberNames) { + Repository member = repositoryManager.get(name); + if (member == null) { + log.warn("Ignoring missing member repository: {}", name); + } + else if (!repository.getFormat().equals(member.getFormat())) { + log.warn("Group {} includes an incompatible-format member: {} with format {}", + repository.getName(), name, member.getFormat()); + } + else { + members.add(member); + } + } + return members; + } + + @Override + public List leafMembers() { + Set leafMembers = new LinkedHashSet<>(); + + for (Repository repository : members()) { + if (groupType.equals(repository.getType())) { + leafMembers.addAll(repository.facet(GroupFacet.class).leafMembers()); + } + else { + leafMembers.add(repository); + } + } + + return new ArrayList<>(leafMembers); + } + + @Override + public List allMembers() { + return allMembers(new ArrayList<>(), getRepository()); + } + + private static List allMembers(final List members, final Repository root) { + members.add(root); + List groupMembers = root.optionalFacet(GroupFacet.class).map(GroupFacet::members) + .orElseGet(Collections::emptyList); + for (Repository child : groupMembers) { + allMembers(members, child); + } + return members; + } + + @Override + public void invalidateGroupCaches() { + log.info("Invalidating group caches of {}", getRepository().getName()); + cacheController.invalidateCache(); + for (Repository repository : members()) { + RepositoryCacheUtils.invalidateCaches(repository); + } + } + + /** + * Returns {@code true} if the content is considered stale; otherwise {@code false}. + */ + protected boolean isStale(@Nullable final Content content) { + return content == null || cacheController.isStale(content.getAttributes().require(CacheInfo.class)); + } + + /** + * Maintains the latest cache information in the given content's attributes. + */ + protected Content maintainCacheInfo(final Content content) { + content.getAttributes().set(CacheInfo.class, cacheController.current()); + return content; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/group/GroupHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/group/GroupHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..4ea7a751bd196cb2d0c786718152b76b4138032c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/group/GroupHandler.java @@ -0,0 +1,163 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.group; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.http.HttpResponses; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.repository.view.Response; +import org.sonatype.nexus.repository.view.ViewFacet; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import static org.sonatype.nexus.repository.http.HttpMethods.GET; +import static org.sonatype.nexus.repository.http.HttpMethods.HEAD; + +/** + * Group handler. + * + * @since 3.0 + */ +@Named +@Singleton +public class GroupHandler + extends ComponentSupport + implements Handler +{ + /** + * Request-context state container for set of repositories already dispatched to. + */ + @VisibleForTesting + public static class DispatchedRepositories + { + private final Set dispatched = Sets.newLinkedHashSet(); + + public void add(final Repository repository) { + dispatched.add(repository.getName()); + } + + public boolean contains(final Repository repository) { + return dispatched.contains(repository.getName()); + } + + @Override + public String toString() { + return dispatched.toString(); + } + } + + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { + final String method = context.getRequest().getAction(); + switch (method) { + case GET: + case HEAD: { + final DispatchedRepositories dispatched = context.getRequest().getAttributes() + .getOrCreate(DispatchedRepositories.class); + return doGet(context, dispatched); + } + + default: + return HttpResponses.methodNotAllowed(method, GET, HEAD); + } + } + + /** + * Method that actually performs group GET. Override if needed. + */ + protected Response doGet(@Nonnull final Context context, + @Nonnull final DispatchedRepositories dispatched) + throws Exception + { + final GroupFacet groupFacet = context.getRepository().facet(GroupFacet.class); + return getFirst(context, groupFacet.members(), dispatched); + } + + /** + * Returns the first OK response from member repositories or {@link HttpResponses#notFound()} if none of the members + * responded with OK. + */ + protected Response getFirst(@Nonnull final Context context, + @Nonnull final List members, + @Nonnull final DispatchedRepositories dispatched) + throws Exception + { + final Request request = context.getRequest(); + for (Repository member : members) { + log.trace("Trying member: {}", member); + // track repositories we have dispatched to, prevent circular dispatch for nested groups + if (dispatched.contains(member)) { + log.trace("Skipping already dispatched member: {}", member); + continue; + } + dispatched.add(member); + + final ViewFacet view = member.facet(ViewFacet.class); + final Response response = view.dispatch(request, context); + log.trace("Member {} response {}", member, response.getStatus()); + if (response.getStatus().isSuccessful()) { + return response; + } + } + return notFoundResponse(context); + } + + /** + * Returns all responses from all members as a linked map, where order is group member order. + */ + protected LinkedHashMap getAll(@Nonnull final Context context, + @Nonnull final Iterable members, + @Nonnull final DispatchedRepositories dispatched) + throws Exception + { + final Request request = context.getRequest(); + final LinkedHashMap responses = Maps.newLinkedHashMap(); + for (Repository member : members) { + log.trace("Trying member: {}", member); + // track repositories we have dispatched to, prevent circular dispatch for nested groups + if (dispatched.contains(member)) { + log.trace("Skipping already dispatched member: {}", member); + continue; + } + dispatched.add(member); + + final ViewFacet view = member.facet(ViewFacet.class); + final Response response = view.dispatch(request, context); + log.trace("Member {} response {}", member, response.getStatus()); + + responses.put(member, response); + } + return responses; + } + + /** + * Returns standard 404 with no message. Override for format specific messaging. + */ + protected Response notFoundResponse(final Context context) { + return HttpResponses.notFound(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/group/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/group/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..d1202f0b920c4172f34b0d8f4ae447df130abc1e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/group/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Repository group-facet and support. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.group; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpConditions.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpConditions.java new file mode 100644 index 0000000000000000000000000000000000000000..d3e1b1398ac8b29a12150341a4adc432e386c322 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpConditions.java @@ -0,0 +1,235 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.http; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map.Entry; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.view.Headers; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.repository.view.Response; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.base.Strings; +import com.google.common.net.HttpHeaders; +import org.apache.http.client.utils.DateUtils; +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Helper defining common HTTP conditions. + * + * @since 3.0 + */ +public class HttpConditions +{ + /** + * Request attribute containing the stashed conditions. + */ + private static final String HTTP_CONDITIONS = HttpConditions.class.getName() + ".conditions"; + + /** + * List of supported headers. This list must be in sync with headers supported in {@link #requestPredicate(Request)}. + */ + private static final List SUPPORTED_HEADERS = Arrays.asList( + HttpHeaders.IF_MODIFIED_SINCE, + HttpHeaders.IF_UNMODIFIED_SINCE, + HttpHeaders.IF_MATCH, + HttpHeaders.IF_NONE_MATCH + ); + + private HttpConditions() { + // empty + } + + /** + * Stashes the conditions of the passed in request, making it non-conditional request. To reverse this change, + * use {@link #makeConditional(Request)} method. + */ + @Nonnull + public static Request makeUnconditional(@Nonnull final Request request) { + checkNotNull(request); + final Headers stashedHeaders = new Headers(); + for (String httpHeader : SUPPORTED_HEADERS) { + List headerValues = request.getHeaders().getAll(httpHeader); + if (headerValues != null) { + stashedHeaders.set(httpHeader, headerValues); + } + request.getHeaders().remove(httpHeader); + } + request.getAttributes().set(HTTP_CONDITIONS, stashedHeaders); + return request; + } + + /** + * Un-stash the conditions originally found in {@link Request}. This method accepts only requests returned by {@link + * #makeUnconditional(Request)} method, otherwise will throw {@link IllegalStateException}. This method must be used + * in pair with the method above. + */ + @Nonnull + public static Request makeConditional(@Nonnull final Request request) { + checkNotNull(request); + final Headers stashedHeaders = request.getAttributes().require(HTTP_CONDITIONS, Headers.class); + for (Entry entry : stashedHeaders.entries()) { + request.getHeaders().set(entry.getKey(), stashedHeaders.getAll(entry.getKey())); + } + return request; + } + + /** + * Builds a {@link Predicate} that contains conditions in passed in {@link Request} or {@code null} if + * request does not contains any condition. The predicate applies to {@link Response} if it meets all the conditions + * found (they all are logically AND bound). + */ + @Nullable + public static Predicate requestPredicate(@Nonnull final Request request) { + checkNotNull(request); + final List> predicates = new ArrayList<>(); + final Predicate ifModifiedSince = ifModifiedSince(request); + if (ifModifiedSince != null) { + predicates.add(ifModifiedSince); + } + final Predicate ifUnmodifiedSince = ifUnmodifiedSince(request); + if (ifUnmodifiedSince != null) { + predicates.add(ifUnmodifiedSince); + } + final Predicate ifMatch = ifMatch(request); + if (ifMatch != null) { + predicates.add(ifMatch); + } + final Predicate ifNoneMatch = ifNoneMatch(request); + if (ifNoneMatch != null) { + predicates.add(ifNoneMatch); + } + + if (!predicates.isEmpty()) { + return Predicates.and(predicates); + } + return null; + } + + @Nullable + private static Predicate ifModifiedSince(final Request request) { + final DateTime date = parseDateHeader(request.getHeaders().get(HttpHeaders.IF_MODIFIED_SINCE)); + if (date != null) { + return new Predicate() + { + @Override + public boolean apply(final Response response) { + final DateTime lastModified = parseDateHeader(response.getHeaders().get(HttpHeaders.LAST_MODIFIED)); + if (lastModified != null) { + return lastModified.isAfter(date); + } + return true; + } + + @Override + public String toString() { + return HttpConditions.class.getSimpleName() + ".ifModifiedSince(" + date + ")"; + } + }; + } + return null; + } + + @Nullable + private static Predicate ifUnmodifiedSince(final Request request) { + final DateTime date = parseDateHeader(request.getHeaders().get(HttpHeaders.IF_UNMODIFIED_SINCE)); + if (date != null) { + return new Predicate() + { + @Override + public boolean apply(final Response response) { + final DateTime lastModified = parseDateHeader(response.getHeaders().get(HttpHeaders.LAST_MODIFIED)); + if (lastModified != null) { + return !lastModified.isAfter(date); + } + return true; + } + + @Override + public String toString() { + return HttpConditions.class.getSimpleName() + ".ifUnmodifiedSince(" + date + ")"; + } + }; + } + return null; + } + + @Nullable + private static Predicate ifMatch(final Request request) { + final String match = request.getHeaders().get(HttpHeaders.IF_MATCH); + if (match != null && !"*".equals(match)) { + return new Predicate() + { + @Override + public boolean apply(final Response response) { + final String etag = response.getHeaders().get(HttpHeaders.ETAG); + if (etag != null) { + return match.contains(etag); + } + return true; + } + + @Override + public String toString() { + return HttpConditions.class.getSimpleName() + ".ifMatch(" + match + ")"; + } + }; + } + return null; + } + + @Nullable + private static Predicate ifNoneMatch(final Request request) { + final String match = request.getHeaders().get(HttpHeaders.IF_NONE_MATCH); + if (match != null && !"*".equals(match)) { + return new Predicate() + { + @Override + public boolean apply(final Response response) { + final String etag = response.getHeaders().get(HttpHeaders.ETAG); + if (etag != null) { + return !match.contains(etag); + } + return true; + } + + @Override + public String toString() { + return HttpConditions.class.getSimpleName() + ".ifNoneMatch(" + match + ")"; + } + }; + } + return null; + } + + @Nullable + private static DateTime parseDateHeader(@Nullable final String httpDate) { + if (!Strings.isNullOrEmpty(httpDate)) { + final Date date = DateUtils.parseDate(httpDate); + if (date != null) { + return new DateTime(date.getTime()); + } + } + return null; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpHandlers.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpHandlers.java new file mode 100644 index 0000000000000000000000000000000000000000..5b1a95a08a0daf38445f83eaea7e24984b1b9167 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpHandlers.java @@ -0,0 +1,52 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.http; + +import javax.annotation.Nonnull; + +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Response; + +/** + * Helper to create common HTTP {@link Handler} instances. + * + * @since 3.0 + */ +public class HttpHandlers +{ + private HttpHandlers() { + // empty + } + + public static Handler notFound() { + return new Handler() + { + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { + return HttpResponses.notFound(); + } + }; + } + + public static Handler badRequest() { + return new Handler() { + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { + return HttpResponses.badRequest(); + } + }; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpMethods.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpMethods.java new file mode 100644 index 0000000000000000000000000000000000000000..f779b41037b51e8e37de3e25661b61795a1ddb04 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpMethods.java @@ -0,0 +1,99 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.http; + +/** + * Helper defining common HTTP methods names. + * + * @since 3.0 + */ +public class HttpMethods +{ + private HttpMethods() { + // empty + } + + // http://ietf.org/rfc/rfc2616 + + public static final String OPTIONS = "OPTIONS"; + + public static final String GET = "GET"; + + public static final String HEAD = "HEAD"; + + public static final String POST = "POST"; + + public static final String PUT = "PUT"; + + public static final String DELETE = "DELETE"; + + public static final String TRACE = "TRACE"; + + public static final String CONNECT = "CONNECT"; + + // http://ietf.org/rfc/rfc2518 + + public static final String PROPFIND = "PROPFIND"; + + public static final String PROPPATCH = "PROPPATCH"; + + public static final String MKCOL = "MKCOL"; + + public static final String COPY = "COPY"; + + public static final String MOVE = "MOVE"; + + public static final String LOCK = "LOCK"; + + public static final String UNLOCK = "UNLOCK"; + + // http://ietf.org/rfc/rfc3253 + + public static final String VERSION_CONTROL = "VERSION-CONTROL"; + + public static final String REPORT = "REPORT"; + + public static final String CHECKOUT = "CHECKOUT"; + + public static final String CHECKIN = "CHECKIN"; + + public static final String UNCHECKOUT = "UNCHECKOUT"; + + public static final String MKWORKSPACE = "MKWORKSPACE"; + + public static final String UPDATE = "UPDATE"; + + public static final String LABEL = "LABEL"; + + public static final String MERGE = "MERGE"; + + public static final String BASELINE_CONTROL = "BASELINE-CONTROL"; + + public static final String MKACTIVITY = "MKACTIVITY"; + + // http://ietf.org/rfc/rfc3648 + + public static final String ORDERPATCH = "ORDERPATCH"; + + // http://ietf.org/rfc/rfc3744 + + public static final String ACL = "ACL"; + + // https://datatracker.ietf.org/drafts/draft-dusseault-http-patch/ + + public static final String PATCH = "PATCH"; + + // https://datatracker.ietf.org/drafts/draft-reschke-webdav-search/ + + public static final String SEARCH = "SEARCH"; +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpResponses.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpResponses.java new file mode 100644 index 0000000000000000000000000000000000000000..918c9bd69af65c6e7c6cbfc732c0136a3954c11b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpResponses.java @@ -0,0 +1,215 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.http; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.view.Payload; +import org.sonatype.nexus.repository.view.Response; +import org.sonatype.nexus.repository.view.Status; + +import com.google.common.base.Joiner; +import com.google.common.net.HttpHeaders; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.http.HttpStatus.*; + +/** + * Convenience methods for constructing various commonly used HTTP responses. + * + * @since 3.0 + */ +public class HttpResponses +{ + private HttpResponses() { + // empty + } + + // Ok: 200 + + public static Response ok(@Nullable final String message) { + return new Response.Builder() + .status(Status.success(OK, message)) + .build(); + } + + public static Response ok() { + return ok((String) null); + } + + public static Response ok(final Payload payload) { + return new Response.Builder() + .status(Status.success(OK)) + .payload(payload) + .build(); + } + + // Created: 201 + + public static Response created(@Nullable final String message) { + return new Response.Builder() + .status(Status.success(CREATED, message)) + .build(); + } + + public static Response created() { + return created((String) null); + } + + public static Response created(final Payload payload) { + return new Response.Builder() + .status(Status.success(CREATED)) + .payload(payload) + .build(); + } + + // Accepted: 202 + + public static Response accepted() { + return new Response.Builder() + .status(Status.success(ACCEPTED)) + .build(); + } + + // No Content: 204 + + public static Response noContent(@Nullable final String message) { + return new Response.Builder() + .status(Status.success(NO_CONTENT, message)) + .build(); + } + + public static Response noContent() { + return noContent(null); + } + + // Not Found: 404 + + public static Response notFound(@Nullable final String message) { + return new Response.Builder() + .status(Status.failure(NOT_FOUND, message)) + .build(); + } + + public static Response notFound() { + return notFound(null); + } + + // Bad request: 400 + + public static Response badRequest(@Nullable final String message) { + return new Response.Builder() + .status(Status.failure(BAD_REQUEST, message)) + .build(); + } + + public static Response badRequest() { + return badRequest(null); + } + + // Unauthorized: 401 + + public static Response unauthorized(@Nullable final String message) { + return new Response.Builder() + .status(Status.failure(UNAUTHORIZED, message)) + .build(); + } + + public static Response unauthorized() { + return unauthorized(null); + } + + /** + * Builds the challenge for authorization by setting a HTTP 401 (Unauthorized) status as well as the + * response's WWW-Authenticate header. + *

    + * The header value constructed is equal to: + *

    + * String.format("%s realm=\"%s\"", authenticationScheme, realmName) + * + * @param message to be shown next to the status code + * @param authenticationScheme authentication scheme used in the authentication challenge header + * @param realmName realm name used in the authentication challenge header + * @return a 401 authentication challenge response + */ + public static Response unauthorized(final String message, final String authenticationScheme, + final String realmName) + { + checkNotNull(message); + checkNotNull(authenticationScheme); + checkNotNull(realmName); + return new Response.Builder() + .status(Status.failure(UNAUTHORIZED, message)) + .header(HttpHeaders.WWW_AUTHENTICATE, String.format("%s realm=\"%s\"", authenticationScheme, realmName)) + .build(); + } + + // Forbidden: 403 + + public static Response forbidden(@Nullable final String message) { + return new Response.Builder() + .status(Status.failure(FORBIDDEN, message)) + .build(); + } + + public static Response forbidden() { + return forbidden(null); + } + + // Method not allowed: 405 + + public static Response methodNotAllowed(final String methodName, final String... allowedMethods) { + checkNotNull(methodName); + checkNotNull(allowedMethods); + checkArgument(allowedMethods.length != 0); + return new Response.Builder() + .status(Status.failure(METHOD_NOT_ALLOWED, methodName)) + .header(HttpHeaders.ALLOW, Joiner.on(',').join(allowedMethods)) + .build(); + } + + public static Response serviceUnavailable(@Nullable final String message) { + return new Response.Builder() + .status(Status.failure(SERVICE_UNAVAILABLE, message)) + .build(); + } + + public static Response serviceUnavailable() { + return serviceUnavailable(null); + } + + public static Response badGateway(@Nullable final String message) { + return new Response.Builder() + .status(Status.failure(BAD_GATEWAY, message)) + .build(); + } + + public static Response badGateway() { + return badGateway(null); + } + + public static Response notImplemented(@Nullable final String message) { + return new Response.Builder() + .status(Status.failure(NOT_IMPLEMENTED, message)) + .build(); + } + + public static Response rangeNotSatisfiable(final long contentSize) { + return new Response.Builder() + .status(Status.failure(REQUESTED_RANGE_NOT_SATISFIABLE)) + .header(HttpHeaders.CONTENT_LENGTH, "0") + .header(HttpHeaders.CONTENT_RANGE, "bytes */" + contentSize) + .build(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpStatus.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..a5a006823a38fbe9efe1ec1eb19daeb9709fa36b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/HttpStatus.java @@ -0,0 +1,112 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.http; + +/** + * Helper defining common HTTP status codes. + * + * @since 3.0 + */ +public class HttpStatus +{ + private HttpStatus() { + // empty + } + + public static final int CONTINUE = 100; + + public static final int SWITCHING_PROTOCOLS = 101; + + public static final int OK = 200; + + public static final int CREATED = 201; + + public static final int ACCEPTED = 202; + + public static final int NON_AUTHORITATIVE_INFORMATION = 203; + + public static final int NO_CONTENT = 204; + + public static final int RESET_CONTENT = 205; + + public static final int PARTIAL_CONTENT = 206; + + public static final int MULTIPLE_CHOICES = 300; + + public static final int MOVED_PERMANENTLY = 301; + + public static final int MOVED_TEMPORARILY = 302; + + public static final int FOUND = 302; + + public static final int SEE_OTHER = 303; + + public static final int NOT_MODIFIED = 304; + + public static final int USE_PROXY = 305; + + public static final int TEMPORARY_REDIRECT = 307; + + public static final int BAD_REQUEST = 400; + + public static final int UNAUTHORIZED = 401; + + public static final int PAYMENT_REQUIRED = 402; + + public static final int FORBIDDEN = 403; + + public static final int NOT_FOUND = 404; + + public static final int METHOD_NOT_ALLOWED = 405; + + public static final int NOT_ACCEPTABLE = 406; + + public static final int PROXY_AUTHENTICATION_REQUIRED = 407; + + public static final int REQUEST_TIMEOUT = 408; + + public static final int CONFLICT = 409; + + public static final int GONE = 410; + + public static final int LENGTH_REQUIRED = 411; + + public static final int PRECONDITION_FAILED = 412; + + public static final int REQUEST_ENTITY_TOO_LARGE = 413; + + public static final int REQUEST_URI_TOO_LONG = 414; + + public static final int UNSUPPORTED_MEDIA_TYPE = 415; + + public static final int REQUESTED_RANGE_NOT_SATISFIABLE = 416; + + public static final int EXPECTATION_FAILED = 417; + + /** + * @since 3.3 + */ + public static final int UNPROCESSABLE_ENTITY = 422; + + public static final int INTERNAL_SERVER_ERROR = 500; + + public static final int NOT_IMPLEMENTED = 501; + + public static final int BAD_GATEWAY = 502; + + public static final int SERVICE_UNAVAILABLE = 503; + + public static final int GATEWAY_TIMEOUT = 504; + + public static final int HTTP_VERSION_NOT_SUPPORTED = 505; +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/PartialFetchHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/PartialFetchHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..e727970dffb94e102bdb8dd72a554dd9efddb4f1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/PartialFetchHandler.java @@ -0,0 +1,129 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.http; + +import java.util.List; + +import javax.annotation.Nonnull; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Payload; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.repository.view.Response; +import org.sonatype.nexus.repository.view.Status; + +import com.google.common.collect.Range; +import com.google.common.net.HttpHeaders; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Implements partial-fetch semantics (as per RFC 2616) for {@link Status#isSuccessful() successful} + * responses with payloads. + * + * @since 3.0 + */ +@Named +@Singleton +public class PartialFetchHandler + implements Handler +{ + private final RangeParser rangeParser; + + @Inject + public PartialFetchHandler(final RangeParser rangeParser) { + this.rangeParser = checkNotNull(rangeParser); + } + + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { + final Response response = context.proceed(); + + // Range requests only apply to GET + if (!HttpMethods.GET.equals(context.getRequest().getAction())) { + return response; + } + + if (response.getStatus().getCode() != HttpStatus.OK) { + // We don't interfere with any non-200 responses + return response; + } + + final Payload payload = response.getPayload(); + if (payload == null) { + return response; + } + + if (payload.getSize() == Payload.UNKNOWN_SIZE) { + // We can't do much if we don't know how big the payload is + return response; + } + + final String rangeHeader = getRangeHeader(context); + if (rangeHeader == null) { + return response; + } + + final List> ranges = rangeParser.parseRangeSpec(rangeHeader, payload.getSize()); + + if (ranges == null) { + // The ranges were not satisfiable + return HttpResponses.rangeNotSatisfiable(payload.getSize()); + } + + if (ranges.isEmpty()) { + // No ranges were specified, or they could not be parsed + return response; + } + + if (ranges.size() > 1) { + return HttpResponses.notImplemented("Multiple ranges not supported."); + } + + Range requestedRange = ranges.get(0); + + // Mutate the response + return partialResponse(response, payload, requestedRange); + } + + /** + * Mutate the response into one that returns part of the payload. + */ + private Response partialResponse(final Response response, + final Payload payload, + final Range requestedRange) + { + Response.Builder builder = new Response.Builder() + .copy(response) + .status(Status.success(HttpStatus.PARTIAL_CONTENT)); + + Payload partialPayload = new PartialPayload(payload, requestedRange); + builder.payload(partialPayload); + + // ResponseSender takes care of Content-Length header, via payload.size + builder.header(HttpHeaders.CONTENT_RANGE, + requestedRange.lowerEndpoint() + "-" + requestedRange.upperEndpoint() + "/" + payload.getSize()); + + return builder.build(); + } + + private String getRangeHeader(final Context context) { + final Request request = context.getRequest(); + return request.getHeaders().get(HttpHeaders.RANGE); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/PartialPayload.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/PartialPayload.java new file mode 100644 index 0000000000000000000000000000000000000000..d0e781134b95ad239c29379b1b6228a745bd56de --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/PartialPayload.java @@ -0,0 +1,66 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.http; + +import java.io.IOException; +import java.io.InputStream; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.view.Payload; + +import com.google.common.collect.Range; + +import static com.google.common.io.ByteStreams.limit; + +/** + * A wrapper {@link Payload} that returns only a portion of the original payload. + * + * @since 3.0 + */ +class PartialPayload + implements Payload +{ + private final Payload payload; + + private final Range rangeToSend; + + private final long partialSize; + + /** + * The endpoints of the Range are interpreted as the first and last byte positions to send. + */ + public PartialPayload(final Payload payload, final Range rangeToSend) { + this.payload = payload; + this.rangeToSend = rangeToSend; + this.partialSize = 1 + rangeToSend.upperEndpoint() - rangeToSend.lowerEndpoint(); + } + + @Override + public InputStream openInputStream() throws IOException { + final InputStream payloadStream = payload.openInputStream(); + payloadStream.skip(rangeToSend.lowerEndpoint()); + return limit(payloadStream, partialSize); + } + + @Override + public long getSize() { + return partialSize; + } + + @Nullable + @Override + public String getContentType() { + return payload.getContentType(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/RangeParser.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/RangeParser.java new file mode 100644 index 0000000000000000000000000000000000000000..7fb9fae0915999e012d64aebff9bd1e7d44a9f0b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/RangeParser.java @@ -0,0 +1,112 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.http; + +import java.util.Collections; +import java.util.List; + +import org.sonatype.goodies.common.ComponentSupport; + +import com.google.common.base.Strings; +import com.google.common.collect.Range; + +import static java.util.Collections.singletonList; + +/** + * Parses the "Range" request header. + * + * Defined by RFC 2616 14.35 + * + * @since 3.0 + */ +class RangeParser + extends ComponentSupport +{ + public static final List> UNSATISFIABLE = null; + + public static final List> WHOLE_RANGE = Collections.emptyList(); + + /** + * Returns a list of {@link Range}s, each indicating a range of byte indices (inclusive). + * + * Range: bytes=0-10 (from byte 0 to byte 10) + * Range: bytes=500-999 (from byte 500 to byte 999) + * Range: bytes=500- (from byte 500 to the end) + * Range: bytes=-500 (the last 500 bytes, per the RFC) + * + * @return {@code null} if the requested range cannot be satisfied given the size of the content, or an empty list in + * the case of parsing errors + */ + public List> parseRangeSpec(final String rangeHeader, long size) { + Range content = Range.closed(0L, size - 1L); + + // TODO: Current limitation: only one Range of bytes supported in forms of "-X", "X-Y" (where X size) { + return UNSATISFIABLE; + } + final Range suffix = Range.atLeast(size - byteCount); + return ensureSatisfiable(suffix, content); + } + else if (rangeSpec.endsWith("-")) { + final Range requested = Range.atLeast(Long.parseLong(rangeSpec.substring(0, rangeSpec.length() - 1))); + return ensureSatisfiable(requested, content); + } + else if (rangeSpec.contains("-")) { + final String[] parts = rangeSpec.split("-"); + return ensureSatisfiable(Range.closed(Long.parseLong(parts[0]), Long.parseLong(parts[1])), content); + } + else { + log.warn("Malformed HTTP Range value: {}, ignoring it", rangeHeader); + } + } + else { + log.warn("Unsupported non-byte or multiple HTTP Ranges: {}; sending complete content", rangeHeader); + } + } + catch (Exception e) { + if (log.isDebugEnabled()) { + log.debug("Problem parsing Range value: {}, ignoring", rangeHeader, e); + } + else { + log.warn("Problem parsing Range value: {}, ignoring: {}", rangeHeader, e.toString()); + } + } + } + + return WHOLE_RANGE; + } + + private List> ensureSatisfiable(Range requested, Range content) { + if (requested.isConnected(content)) { + return singletonList(requested.intersection(content)); + } + else { + return UNSATISFIABLE; + } + } + + private boolean isSatisfiable(final Range range, final long contentSize) { + if (!range.hasLowerBound()) { + return true; + } + // Per RFC 2616, a requested range is satisfiable as long as its lower bound is within the content size. + // Requests for ranges that extend beyond the content size are okay. + return range.lowerEndpoint() < contentSize - 1; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..db1e55fdda463fa1258bbee40b0bed270fe2b99c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/http/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Repository HTTP protocol helpers and constants. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.http; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/FilteredHttpClientSupport.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/FilteredHttpClientSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..3594ae042f40e63579b9943af746b4a58e428985 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/FilteredHttpClientSupport.java @@ -0,0 +1,159 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.httpclient; + +import java.io.Closeable; +import java.io.IOException; +import java.net.URI; + +import org.sonatype.goodies.common.ComponentSupport; + +import com.google.common.annotations.VisibleForTesting; +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.utils.URIUtils; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.HttpContext; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Support for wrapping {@link HttpClient}s. + * + * @since 3.1 + */ +public abstract class FilteredHttpClientSupport + extends ComponentSupport + implements HttpClient, Closeable +{ + private final HttpClient delegate; + + public FilteredHttpClientSupport(final HttpClient delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + public HttpParams getParams() { + return delegate.getParams(); + } + + @Override + public ClientConnectionManager getConnectionManager() { + return delegate.getConnectionManager(); + } + + @Override + public void close() throws IOException { + if (delegate instanceof Closeable) { + ((Closeable) delegate).close(); + } + } + + protected abstract T filter(final HttpHost target, final Filterable filterable) throws IOException; + + @Override + public HttpResponse execute(final HttpUriRequest request) throws IOException { + return filter(determineTarget(request), () -> delegate.execute(request)); + } + + @Override + public HttpResponse execute(final HttpUriRequest request, + final HttpContext context) + throws IOException + { + return filter(determineTarget(request), () -> delegate.execute(request, context)); + } + + @Override + public HttpResponse execute(final HttpHost target, + final HttpRequest request) + throws IOException + { + return filter(target, () -> delegate.execute(target, request)); + } + + @Override + public HttpResponse execute(final HttpHost target, + final HttpRequest request, + final HttpContext context) + throws IOException + { + return filter(target, () -> delegate.execute(target, request, context)); + } + + @Override + public T execute(final HttpUriRequest request, + final ResponseHandler responseHandler) + throws IOException + { + return filter(determineTarget(request), () -> delegate.execute(request, responseHandler)); + } + + @Override + public T execute(final HttpUriRequest request, + final ResponseHandler responseHandler, + final HttpContext context) + throws IOException + { + return filter(determineTarget(request), () -> delegate.execute(request, responseHandler, context)); + } + + @Override + public T execute(final HttpHost target, + final HttpRequest request, + final ResponseHandler responseHandler) + throws IOException + { + return filter(target, () -> delegate.execute(target, request, responseHandler)); + } + + @Override + public T execute(final HttpHost target, + final HttpRequest request, + final ResponseHandler responseHandler, + final HttpContext context) throws IOException + { + return filter(target, () -> delegate.execute(target, request, responseHandler, context)); + } + + private static HttpHost determineTarget(final HttpUriRequest request) throws ClientProtocolException { + HttpHost target = null; + final URI requestURI = request.getURI(); + if (requestURI.isAbsolute()) { + target = URIUtils.extractHost(requestURI); + if (target == null) { + throw new ClientProtocolException("URI does not specify a valid host name: " + requestURI); + } + } + return target; + } + + @VisibleForTesting + public interface Filterable + { + T call() throws IOException; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "delegate=" + delegate + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/HttpClientFacet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/HttpClientFacet.java new file mode 100644 index 0000000000000000000000000000000000000000..086f177ef5ea59e3d99ff1a7b4b0cc2cca1b8690 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/HttpClientFacet.java @@ -0,0 +1,34 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.httpclient; + +import org.sonatype.nexus.repository.Facet; + +import org.apache.http.Header; +import org.apache.http.client.HttpClient; + +/** + * HTTP client facet. + * + * @since 3.0 + */ +@Facet.Exposed +public interface HttpClientFacet + extends Facet +{ + HttpClient getHttpClient(); + + RemoteConnectionStatus getStatus(); + + Header createBasicAuthHeader(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteBlockedIOException.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteBlockedIOException.java new file mode 100644 index 0000000000000000000000000000000000000000..081e392b99e68ec67c1d98a1ec84b641b5bd60d2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteBlockedIOException.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.httpclient; + +import java.io.IOException; + +/** + * Thrown when a remote is manually or automatically blocked. + * + * @since 3.3 + */ +public class RemoteBlockedIOException + extends IOException +{ + public RemoteBlockedIOException(final String message) { + super(message); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteConnectionStatus.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteConnectionStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..8727ff7dcb96b825bff981f17928a3bd02f9f01b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteConnectionStatus.java @@ -0,0 +1,101 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.httpclient; + +import javax.annotation.Nullable; + +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Remote connection status. + * + * @since 3.0 + */ +public class RemoteConnectionStatus +{ + private final RemoteConnectionStatusType type; + + private final String reason; + + private DateTime blockedUntil; + + private String requestUrl; + + public RemoteConnectionStatus(final RemoteConnectionStatusType type) { + this(type, null); + } + + public RemoteConnectionStatus(final RemoteConnectionStatusType type, @Nullable final String reason) { + this.type = checkNotNull(type); + this.reason = reason; + } + + public String getDescription() { + return type.getDescription(); + } + + /** + * @since 3.3 + */ + public RemoteConnectionStatusType getType() { + return type; + } + + @Nullable + public String getReason() { + return reason; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(type.getDescription()); + if (reason != null) { + sb.append(" - ").append(reason); + } + return sb.toString(); + } + + /** + * @since 3.3 + */ + @Nullable + public DateTime getBlockedUntil() { + return blockedUntil; + } + + /** + * @since 3.3 + */ + public RemoteConnectionStatus setBlockedUntil(@Nullable final DateTime blockedUntil) { + this.blockedUntil = blockedUntil; + return this; + } + + /** + * @since 3.3 + */ + @Nullable + public String getRequestUrl() { + return requestUrl; + } + + /** + * @since 3.3 + */ + public RemoteConnectionStatus setRequestUrl(@Nullable final String requestUrl) { + this.requestUrl = requestUrl; + return this; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteConnectionStatusEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteConnectionStatusEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..7373a59fb5d705c79ff0de4ce140ede50a009343 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteConnectionStatusEvent.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.httpclient; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryEvent; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Represents a change in repository status. + * + * @since 3.3 + */ +public class RemoteConnectionStatusEvent + extends RepositoryEvent +{ + private final RemoteConnectionStatus status; + + public RemoteConnectionStatusEvent(final RemoteConnectionStatus status, final Repository repository) { + super(repository); + this.status = checkNotNull(status); + } + + public RemoteConnectionStatus getStatus() { + return status; + } + + @Override + public String toString() { + return "RemoteConnectionStatusEvent{" + + "status=" + getStatus() + + ", repository=" + getRepository() + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteConnectionStatusObserver.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteConnectionStatusObserver.java new file mode 100644 index 0000000000000000000000000000000000000000..b6d6995c44b86fc5a482d124751f0b5896c1bba8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteConnectionStatusObserver.java @@ -0,0 +1,23 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.httpclient; + +/** + * Listener interface for changes to a repositories connection status. + * + * @since 3.3 + */ +public interface RemoteConnectionStatusObserver +{ + void onStatusChanged(RemoteConnectionStatus oldStatus, RemoteConnectionStatus newStatus); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteConnectionStatusType.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteConnectionStatusType.java new file mode 100644 index 0000000000000000000000000000000000000000..4aaece3256b16b97456045d2e99ceb786aae207f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/RemoteConnectionStatusType.java @@ -0,0 +1,39 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.httpclient; + +/** + * Connection status of a remote repository + * + * @since 3.3 + */ +public enum RemoteConnectionStatusType +{ + UNINITIALISED("Uninitialised"), + READY("Ready to Connect"), + AVAILABLE("Remote Available"), + BLOCKED("Remote Manually Blocked"), + AUTO_BLOCKED_UNAVAILABLE("Remote Auto Blocked and Unavailable"), + UNAVAILABLE("Remote Unavailable"), + OFFLINE("Repository Offline"); + + private final String description; + + RemoteConnectionStatusType(final String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/internal/BlockingHttpClient.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/internal/BlockingHttpClient.java new file mode 100644 index 0000000000000000000000000000000000000000..d03ac8894f75307073cc95dbf6ee881e052937d3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/internal/BlockingHttpClient.java @@ -0,0 +1,242 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.httpclient.internal; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Optional; + +import javax.annotation.Nullable; +import javax.net.ssl.SSLPeerUnverifiedException; + +import org.sonatype.goodies.common.Time; +import org.sonatype.nexus.common.sequence.FibonacciNumberSequence; +import org.sonatype.nexus.common.sequence.NumberSequence; +import org.sonatype.nexus.repository.httpclient.FilteredHttpClientSupport; +import org.sonatype.nexus.repository.httpclient.RemoteBlockedIOException; +import org.sonatype.nexus.repository.httpclient.RemoteConnectionStatus; +import org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusObserver; +import org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType; + +import org.apache.http.HttpHost; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.conn.ConnectionPoolTimeoutException; +import org.joda.time.DateTime; +import org.joda.time.Duration; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.AUTO_BLOCKED_UNAVAILABLE; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.AVAILABLE; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.BLOCKED; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.OFFLINE; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.READY; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.UNAVAILABLE; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.UNINITIALISED; + +/** + * Wraps an {@link HttpClient} with manual and automatic blocking functionality. + * + * @since 3.0 + */ +public class BlockingHttpClient + extends FilteredHttpClientSupport + implements HttpClient, Closeable +{ + private final boolean blocked; + + private HttpHost mainTarget; + + private DateTime blockedUntil; + + private Thread checkThread; + + private final boolean autoBlock; + + private final NumberSequence autoBlockSequence; + + private final RemoteConnectionStatusObserver statusObserver; + + private RemoteConnectionStatus status = new RemoteConnectionStatus(UNINITIALISED); + + public BlockingHttpClient(final HttpClient delegate, + final HttpClientFacetImpl.Config config, + final RemoteConnectionStatusObserver statusObserver, + final boolean repositoryOnline) + { + super(delegate); + checkNotNull(config); + this.statusObserver = checkNotNull(statusObserver); + blocked = config.blocked != null ? config.blocked : false; + autoBlock = config.autoBlock != null ? config.autoBlock : false; + if (repositoryOnline) { + updateStatus(blocked ? BLOCKED : READY); + } + else { + updateStatus(OFFLINE); + } + // TODO shall we use config.getConnectionConfig().getTimeout() * 2 as in NX2? + autoBlockSequence = new FibonacciNumberSequence(Time.seconds(40).toMillis()); + } + + protected T filter(final HttpHost target, final Filterable filterable) throws IOException { + // main target is the first accessed target + if (mainTarget == null) { + mainTarget = target; + } + // we only filter requests to our main target + if (!target.equals(mainTarget)) { + return filterable.call(); + } + if (blocked) { + throw new RemoteBlockedIOException("Remote Manually Blocked"); + } + DateTime blockedUntilCopy = this.blockedUntil; + if (autoBlock && blockedUntilCopy != null && blockedUntilCopy.isAfterNow()) { + throw new RemoteBlockedIOException("Remote Auto Blocked until " + blockedUntilCopy); + } + + Optional exception = Optional.empty(); + T result = null; + try { + result = filterable.call(); + } + catch (IOException e) { + exception = Optional.of(e); + } + + synchronized (this) { + if (!exception.isPresent()) { + if (autoBlock && blockedUntil != null) { + blockedUntil = null; + checkThread.interrupt(); + checkThread = null; + autoBlockSequence.reset(); + } + updateStatus(AVAILABLE); + } + else { + IOException e = exception.get(); + if (isRemoteUnavailable(e)) { + if (autoBlock) { + // avoid some other thread already increased the sequence + if (blockedUntil == null || blockedUntil.isBeforeNow()) { + blockedUntil = DateTime.now().plus(autoBlockSequence.next()); + if (checkThread != null) { + checkThread.interrupt(); + } + String uri = target.toURI(); + // TODO maybe find different means to schedule status checking + checkThread = new Thread(new CheckStatus(uri, blockedUntil), "Check Status " + uri); + checkThread.setDaemon(true); + checkThread.start(); + } + updateStatus(AUTO_BLOCKED_UNAVAILABLE, getReason(e), target.toURI(), + blockedUntil.isAfter(status.getBlockedUntil())); + } + else { + updateStatus(UNAVAILABLE, getReason(e), target.toURI(), false); + } + } + } + } + + if (exception.isPresent()) { + throw exception.get(); + } + else { + return result; + } + } + + private void updateStatus(final RemoteConnectionStatusType type, + final String reason, + @Nullable final String url, + final boolean autoBlockTimeIncrease) + { + if (type != status.getType() || autoBlockTimeIncrease) { + RemoteConnectionStatus oldStatus = status; + status = new RemoteConnectionStatus(type, reason) + .setBlockedUntil(blockedUntil) + .setRequestUrl(url); + statusObserver.onStatusChanged(oldStatus, status); + } + } + + private void updateStatus(final RemoteConnectionStatusType type) { + updateStatus(type, null, null, false); + } + + public RemoteConnectionStatus getStatus() { + return status; + } + + private boolean isRemoteUnavailable(final Exception e) { + if (e instanceof ConnectionPoolTimeoutException) { + return false; + } + return true; + } + + private String getReason(final Exception e) { + if (e instanceof SSLPeerUnverifiedException) { + return "Untrusted Remote"; + } + return e.getClass().getName() + ": " + e.getMessage(); + } + + @Override + public void close() throws IOException { + if (checkThread != null) { + checkThread.interrupt(); + } + super.close(); + } + + private class CheckStatus + implements Runnable + { + + private final String uri; + + private final DateTime fireAt; + + private CheckStatus(final String uri, final DateTime fireAt) { + this.uri = uri; + this.fireAt = fireAt; + } + + @Override + public void run() { + if (fireAt.isAfterNow()) { + try { + long durationTillFire = new Duration(DateTime.now(), fireAt).getMillis(); + if (durationTillFire > 0) { + log.debug("Wait until {} to check status of {}", fireAt, uri); + Thread.sleep(durationTillFire); + log.debug("Time is up. Checking status of {}", uri); + execute(new HttpHead(uri)); + } + } + catch (InterruptedException e) { + log.debug("Stopped checking status of {}", uri); + } + catch (IOException e) { + // ignore as we just want to access the host + } + } + } + + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/internal/HttpClientFacetImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/internal/HttpClientFacetImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..cb4d1694564e501d4937a3ce61e84faf6f9a7410 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/internal/HttpClientFacetImpl.java @@ -0,0 +1,239 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.httpclient.internal; + +import java.io.IOException; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.validation.Valid; + +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.httpclient.HttpClientManager; +import org.sonatype.nexus.httpclient.config.AuthenticationConfiguration; +import org.sonatype.nexus.httpclient.config.ConfigurationCustomizer; +import org.sonatype.nexus.httpclient.config.ConnectionConfiguration; +import org.sonatype.nexus.httpclient.config.HttpClientConfiguration; +import org.sonatype.nexus.httpclient.config.HttpClientConfigurationChangedEvent; +import org.sonatype.nexus.httpclient.config.UsernameAuthenticationConfiguration; +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.config.ConfigurationFacet; +import org.sonatype.nexus.repository.httpclient.HttpClientFacet; +import org.sonatype.nexus.repository.httpclient.RemoteConnectionStatus; +import org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusEvent; +import org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusObserver; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.eventbus.Subscribe; +import org.apache.http.Header; +import org.apache.http.client.HttpClient; +import org.apache.http.message.BasicHeader; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static org.apache.commons.codec.binary.Base64.*; +import static org.apache.http.HttpHeaders.AUTHORIZATION; +import static org.sonatype.nexus.repository.FacetSupport.State.STARTED; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.AUTO_BLOCKED_UNAVAILABLE; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.UNINITIALISED; + +/** + * Default {@link HttpClientFacet} implementation. + * + * @since 3.0 + */ +@Named +public class HttpClientFacetImpl + extends FacetSupport + implements HttpClientFacet, RemoteConnectionStatusObserver +{ + private final HttpClientManager httpClientManager; + + @VisibleForTesting + static final String CONFIG_KEY = "httpclient"; + + @VisibleForTesting + static class Config + { + @Valid + @Nullable + public ConnectionConfiguration connection; + + @Valid + @Nullable + public AuthenticationConfiguration authentication; + + @Nullable + public Boolean blocked; + + @Nullable + public Boolean autoBlock; + } + + private Config config; + + private BlockingHttpClient httpClient; + + @Inject + public HttpClientFacetImpl(final HttpClientManager httpClientManager) { + this.httpClientManager = checkNotNull(httpClientManager); + } + + @VisibleForTesting + HttpClientFacetImpl(final HttpClientManager httpClientManager, final Config config) { + this(httpClientManager); + this.config = checkNotNull(config); + } + + @Override + protected void doValidate(final Configuration configuration) throws Exception { + facet(ConfigurationFacet.class).validateSection(configuration, CONFIG_KEY, Config.class); + } + + @Override + protected void doConfigure(final Configuration configuration) throws Exception { + config = facet(ConfigurationFacet.class).readSection(configuration, CONFIG_KEY, Config.class); + log.debug("Config: {}", config); + + createHttpClient(); + } + + @Override + protected void doDestroy() throws Exception { + config = null; + } + + @Override + protected void doStop() throws Exception { + closeHttpClient(); + } + + @Override + @Guarded(by = STARTED) + public HttpClient getHttpClient() { + return checkNotNull(httpClient); + } + + @Nullable + @Override + @Guarded(by = STARTED) + public Header createBasicAuthHeader() { + if (config.authentication instanceof UsernameAuthenticationConfiguration) { + UsernameAuthenticationConfiguration userAuth = (UsernameAuthenticationConfiguration) config.authentication; + + String auth = format("%1$s:%2$s", userAuth.getUsername(), userAuth.getPassword()); + + byte[] encodedAuth = encodeBase64(auth.getBytes(ISO_8859_1)); + + String authHeader = "Basic " + new String(encodedAuth, ISO_8859_1); + + return new BasicHeader(AUTHORIZATION, authHeader); + } + else { + log.debug("Basic auth header cannot be created for auth config of {}", config.authentication); + return null; + } + } + + @Override + @Guarded(by = STARTED) + public RemoteConnectionStatus getStatus() { + return httpClient.getStatus(); + } + + @Subscribe + public void on(final HttpClientConfigurationChangedEvent event) throws IOException { + closeHttpClient(); + createHttpClient(); + } + + @Override + public void onStatusChanged(final RemoteConnectionStatus oldStatus, final RemoteConnectionStatus newStatus) { + logStatusChange(oldStatus, newStatus); + getEventManager().post(new RemoteConnectionStatusEvent(newStatus, getRepository())); + } + + private void logStatusChange(final RemoteConnectionStatus oldStatus, final RemoteConnectionStatus newStatus) { + if (log.isInfoEnabled()) { + if (oldStatus.getType() == AUTO_BLOCKED_UNAVAILABLE && newStatus.getType() == AUTO_BLOCKED_UNAVAILABLE) { + logAutoBlockTimeIncreased(oldStatus, newStatus); + } + else if (oldStatus.getType() == UNINITIALISED) { + log.info("Remote connection status of repository {} set to {}.", getRepository().getName(), + newStatus.getDescription()); + } + else { + logStatusUpdated(oldStatus, newStatus); + } + } + } + + private void logAutoBlockTimeIncreased(final RemoteConnectionStatus oldStatus, final RemoteConnectionStatus newStatus) + { + String message = "Repository status for {} continued as {} until {} - reason {} (previous reason was {})"; + log.info(message, + getRepository().getName(), + newStatus.getType(), + newStatus.getBlockedUntil(), + statusReason(newStatus), + statusReason(oldStatus) + ); + } + + private void logStatusUpdated(final RemoteConnectionStatus oldStatus, final RemoteConnectionStatus newStatus) { + String message = "Repository status for {} changed from {} to {}{} - reason {}"; + log.info(message, + getRepository().getName(), + oldStatus.getType(), + newStatus.getType(), + statusBlockedUntil(newStatus), + statusReason(newStatus) + ); + } + + private static String statusBlockedUntil(final RemoteConnectionStatus status) { + if (status.getBlockedUntil() != null) { + return format(" until %s", status.getBlockedUntil()); + } + return ""; + } + + private static String statusReason(final RemoteConnectionStatus status) { + return format("%s for %s", + status.getReason() != null ? status.getReason() : "n/a", + status.getRequestUrl() != null ? status.getRequestUrl() : "n/a" + ); + } + + private void createHttpClient() { + // construct http client delegate + HttpClientConfiguration delegateConfig = new HttpClientConfiguration(); + delegateConfig.setConnection(config.connection); + delegateConfig.setAuthentication(config.authentication); + HttpClient delegate = httpClientManager.create(new ConfigurationCustomizer(delegateConfig)); + + boolean online = getRepository().getConfiguration().isOnline(); + // wrap delegate with auto-block aware client + httpClient = new BlockingHttpClient(delegate, config, this, online); + log.debug("Created HTTP client: {}", httpClient); + } + + private void closeHttpClient() throws IOException { + log.debug("Closing HTTP client: {}", httpClient); + httpClient.close(); + httpClient = null; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..2bf040e3318f79eab9b4d94da96a9ef896865eb9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/httpclient/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Repository HTTP-client-facet and support. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.httpclient; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/RepositoryModule.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/RepositoryModule.java new file mode 100644 index 0000000000000000000000000000000000000000..25f83bf50982853e61df3ae6a5a472f654c53979 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/RepositoryModule.java @@ -0,0 +1,40 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal; + +import javax.inject.Named; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.manager.internal.RepositoryFactory; +import org.sonatype.nexus.repository.manager.internal.RepositoryImpl; + +import com.google.inject.AbstractModule; +import com.google.inject.assistedinject.FactoryModuleBuilder; + +/** + * Repository module. + * + * @since 3.0 + */ +@Named +public class RepositoryModule + extends AbstractModule +{ + @Override + protected void configure() { + install(new FactoryModuleBuilder() + .implement(Repository.class, RepositoryImpl.class) + .build(RepositoryFactory.class) + ); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/RepositoryViewSecurityContributor.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/RepositoryViewSecurityContributor.groovy new file mode 100644 index 0000000000000000000000000000000000000000..4afedc8b63d108ab44718c7aa3ca43a5b20db734 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/RepositoryViewSecurityContributor.groovy @@ -0,0 +1,104 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal + +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.nexus.security.config.CPrivilege +import org.sonatype.nexus.security.config.MemorySecurityConfiguration +import org.sonatype.nexus.security.config.SecurityContributor + +/** + * Repository view security configuration. + * + * @since 3.0 + */ +@Named +@Singleton +class RepositoryViewSecurityContributor + implements SecurityContributor +{ + @Override + MemorySecurityConfiguration getContribution() { + return new MemorySecurityConfiguration( + privileges: [ + // + // nexus:repository-view + // + + new CPrivilege( + id: 'nx-repository-view-*-*-*', + description: 'All permissions for all repository views', + type: 'repository-view', + properties: [ + format: '*', + repository: '*', + actions: '*' + ] + ), + new CPrivilege( + id: 'nx-repository-view-*-*-browse', + description: 'Browse permission for all repository views', + type: 'repository-view', + properties: [ + format: '*', + repository: '*', + actions: 'browse' + ] + ), + new CPrivilege( + id: 'nx-repository-view-*-*-read', + description: 'Read permission for all repository views', + type: 'repository-view', + properties: [ + format: '*', + repository: '*', + actions: 'read' + ] + ), + new CPrivilege( + id: 'nx-repository-view-*-*-edit', + description: 'Edit permission for all repository views', + type: 'repository-view', + properties: [ + format: '*', + repository: '*', + actions: 'edit' + ] + ), + new CPrivilege( + id: 'nx-repository-view-*-*-add', + description: 'Add permission for all repository views', + type: 'repository-view', + properties: [ + format: '*', + repository: '*', + actions: 'add' + ] + ), + new CPrivilege( + id: 'nx-repository-view-*-*-delete', + description: 'Delete permission for all repository views', + type: 'repository-view', + properties: [ + format: '*', + repository: '*', + actions: 'delete' + ] + ), + ] + ) + } +} + diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreAuditor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreAuditor.java new file mode 100644 index 0000000000000000000000000000000000000000..2caa8f916e7430d74c7bd403f629959ada835ba6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreAuditor.java @@ -0,0 +1,69 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal.blobstore; + +import java.util.Map; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.audit.AuditData; +import org.sonatype.nexus.audit.AuditorSupport; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration; +import org.sonatype.nexus.blobstore.api.BlobStoreCreatedEvent; +import org.sonatype.nexus.blobstore.api.BlobStoreDeletedEvent; +import org.sonatype.nexus.blobstore.api.BlobStoreEvent; +import org.sonatype.nexus.common.event.EventAware; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.Subscribe; + +/** + * {@link BlobStore} auditor. + * + * @since 3.1 + */ +@Named +@Singleton +public class BlobStoreAuditor + extends AuditorSupport + implements EventAware +{ + public static final String DOMAIN = "blobstore"; + + public BlobStoreAuditor() { + registerType(BlobStoreCreatedEvent.class, CREATED_TYPE); + registerType(BlobStoreDeletedEvent.class, DELETED_TYPE); + } + + @Subscribe + @AllowConcurrentEvents + public void on(final BlobStoreEvent event) { + if (isRecording()) { + BlobStore blobStore = event.getBlobStore(); + BlobStoreConfiguration configuration = blobStore.getBlobStoreConfiguration(); + + AuditData data = new AuditData(); + data.setDomain(DOMAIN); + data.setType(type(event.getClass())); + data.setContext(configuration.getName()); + + Map attributes = data.getAttributes(); + attributes.put("name", configuration.getName()); + attributes.put("type", configuration.getType()); + + record(data); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationCreatedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationCreatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..720829817816afbc74727f6137edb37ca899cb47 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationCreatedEvent.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal.blobstore; + +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration; +import org.sonatype.nexus.common.entity.EntityCreatedEvent; +import org.sonatype.nexus.common.entity.EntityMetadata; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * {@link BlobStoreConfiguration} created event. + * + * @since 3.1 + */ +public class BlobStoreConfigurationCreatedEvent + extends EntityCreatedEvent + implements BlobStoreConfigurationEvent +{ + private final String name; + + public BlobStoreConfigurationCreatedEvent(final EntityMetadata metadata, final String name) { + super(metadata); + this.name = checkNotNull(name); + } + + @Override + public String getName() { + return name; + } + + @Override + public BlobStoreConfiguration getConfiguration() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationDeletedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationDeletedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..19c1a236b2ce58d87aae1a1c1797264a46e19c95 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationDeletedEvent.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal.blobstore; + +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration; +import org.sonatype.nexus.common.entity.EntityDeletedEvent; +import org.sonatype.nexus.common.entity.EntityMetadata; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * {@link BlobStoreConfiguration} deleted event. + * + * @since 3.1 + */ +public class BlobStoreConfigurationDeletedEvent + extends EntityDeletedEvent + implements BlobStoreConfigurationEvent +{ + private final String name; + + public BlobStoreConfigurationDeletedEvent(final EntityMetadata metadata, final String name) { + super(metadata); + this.name = checkNotNull(name); + } + + @Override + public String getName() { + return name; + } + + @Override + public BlobStoreConfiguration getConfiguration() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationEntityAdapter.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..5d93e7955bf3f2664b047e3d160cc3772c29ac61 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationEntityAdapter.java @@ -0,0 +1,126 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal.blobstore; + +import java.util.Map; + +import javax.annotation.Nullable; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration; +import org.sonatype.nexus.common.entity.EntityEvent; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.OIndexNameBuilder; +import org.sonatype.nexus.orient.entity.AttachedEntityMetadata; +import org.sonatype.nexus.orient.entity.IterableEntityAdapter; + +import com.google.common.annotations.VisibleForTesting; +import com.orientechnologies.orient.core.collate.OCaseInsensitiveCollate; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; + +/** + * {@link BlobStoreConfiguration} entity-adapter. + * + * since 3.0 + */ +@Named +@Singleton +public class BlobStoreConfigurationEntityAdapter + extends IterableEntityAdapter +{ + private static final String DB_CLASS = new OClassNameBuilder() + .prefix("repository") + .type("blobstore") + .build(); + + private static final String P_NAME = "name"; + + private static final String P_TYPE = "type"; + + private static final String P_ATTRIBUTES = "attributes"; + + @VisibleForTesting + static final String I_NAME = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_NAME) + .build(); + + public BlobStoreConfigurationEntityAdapter() { + super(DB_CLASS); + } + + @Override + protected void defineType(final OClass type) { + type.createProperty(P_NAME, OType.STRING) + .setCollate(new OCaseInsensitiveCollate()) + .setMandatory(true) + .setNotNull(true); + type.createProperty(P_TYPE, OType.STRING) + .setMandatory(true) + .setNotNull(true); + type.createProperty(P_ATTRIBUTES, OType.EMBEDDEDMAP) + .setMandatory(true) + .setNotNull(true); + type.createIndex(I_NAME, INDEX_TYPE.UNIQUE, P_NAME); + } + + @Override + protected BlobStoreConfiguration newEntity() { + return new BlobStoreConfiguration(); + } + + @Override + protected void readFields(final ODocument document, final BlobStoreConfiguration entity) { + String name = document.field(P_NAME, OType.STRING); + String type = document.field(P_TYPE, OType.STRING); + Map> attributes = document.field(P_ATTRIBUTES, OType.EMBEDDEDMAP); + + entity.setName(name); + entity.setType(type); + entity.setAttributes(detachable(attributes)); + } + + @Override + protected void writeFields(final ODocument document, final BlobStoreConfiguration entity) { + document.field(P_NAME, entity.getName()); + document.field(P_TYPE, entity.getType()); + document.field(P_ATTRIBUTES, entity.getAttributes()); + } + + @Override + public boolean sendEvents() { + return true; + } + + @Nullable + @Override + public EntityEvent newEvent(final ODocument document, final EventKind eventKind) { + final EntityMetadata metadata = new AttachedEntityMetadata(this, document); + final String name = document.field(P_NAME); + + log.trace("newEvent: eventKind: {}, name: {}, metadata: {}", eventKind, name, metadata); + switch (eventKind) { + case CREATE: + return new BlobStoreConfigurationCreatedEvent(metadata, name); + case DELETE: + return new BlobStoreConfigurationDeletedEvent(metadata, name); + default: + return null; + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..7d9ef043ea33d0f6da5820a48680ee4823aa973a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationEvent.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal.blobstore; + +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration; + +/** + * {@link BlobStoreConfiguration} event. + * + * @since 3.1 + */ +public interface BlobStoreConfigurationEvent +{ + boolean isLocal(); + + String getName(); + + BlobStoreConfiguration getConfiguration(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationStore.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationStore.java new file mode 100644 index 0000000000000000000000000000000000000000..39597ab1fa47499b415a1a616023ee542c1ff84b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationStore.java @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal.blobstore; + +import java.util.List; + +import org.sonatype.goodies.lifecycle.Lifecycle; +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration; + +/** + * {@link BlobStoreConfiguration} store. + * + * since 3.0 + */ +public interface BlobStoreConfigurationStore + extends Lifecycle +{ + /** + * @return all BlobStoreConfigurations + */ + List list(); + + /** + * Persist a new BlobStoreConfiguration. + */ + void create(BlobStoreConfiguration configuration); + + /** + * Delete an existing BlobStoreConfiguration. + */ + void delete(BlobStoreConfiguration configuration); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationStoreImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationStoreImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..3d2782ec74a912f1bdd800ef512b6c94dd453978 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationStoreImpl.java @@ -0,0 +1,90 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal.blobstore; + +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseInstanceNames; + +import com.google.common.collect.ImmutableList; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SCHEMAS; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTx; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; + +/** + * Default {@link BlobStoreConfigurationStore} implementation. + * + * @since 3.0 + */ +@Named +@ManagedLifecycle(phase = SCHEMAS) +@Singleton +public class BlobStoreConfigurationStoreImpl + extends StateGuardLifecycleSupport + implements BlobStoreConfigurationStore +{ + private final Provider databaseInstance; + + private final BlobStoreConfigurationEntityAdapter entityAdapter; + + @Inject + public BlobStoreConfigurationStoreImpl(@Named(DatabaseInstanceNames.CONFIG) final Provider databaseInstance, + final BlobStoreConfigurationEntityAdapter entityAdapter) + { + this.databaseInstance = databaseInstance; + this.entityAdapter = entityAdapter; + } + + @Override + protected void doStart() throws Exception { + try (ODatabaseDocumentTx db = databaseInstance.get().connect()) { + entityAdapter.register(db); + } + } + + @Override + @Guarded(by = STARTED) + public List list() { + return inTx(databaseInstance).call(db -> ImmutableList.copyOf(entityAdapter.browse(db))); + } + + @Override + @Guarded(by = STARTED) + public void create(final BlobStoreConfiguration configuration) { + checkNotNull(configuration); + + inTxRetry(databaseInstance).run(db -> entityAdapter.addEntity(db, configuration)); + } + + @Override + @Guarded(by = STARTED) + public void delete(final BlobStoreConfiguration configuration) { + checkNotNull(configuration); + + inTxRetry(databaseInstance).run(db -> entityAdapter.deleteEntity(db, configuration)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreManagerImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreManagerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..95caccc3f13f0cb1e304d950560be7bbe0d112b4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreManagerImpl.java @@ -0,0 +1,273 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal.blobstore; + +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration; +import org.sonatype.nexus.blobstore.api.BlobStoreCreatedEvent; +import org.sonatype.nexus.blobstore.api.BlobStoreDeletedEvent; +import org.sonatype.nexus.blobstore.api.BlobStoreManager; +import org.sonatype.nexus.blobstore.file.FileBlobStoreConfigurationBuilder; +import org.sonatype.nexus.common.event.EventAware; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.jmx.reflect.ManagedObject; +import org.sonatype.nexus.orient.freeze.DatabaseFreezeService; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; +import com.google.common.eventbus.Subscribe; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; + +/** + * Default {@link BlobStoreManager} implementation. + * + * @since 3.0 + */ +@Named +@Singleton +@ManagedObject +public class BlobStoreManagerImpl + extends StateGuardLifecycleSupport + implements BlobStoreManager, EventAware +{ + private final EventManager eventManager; + + private final Map stores = Maps.newConcurrentMap(); + + private final BlobStoreConfigurationStore store; + + private final Map> blobstorePrototypes; + + private final DatabaseFreezeService databaseFreezeService; + + private final NodeAccess nodeAccess; + + private final boolean provisionDefaults; + + @Inject + public BlobStoreManagerImpl(final EventManager eventManager, + final BlobStoreConfigurationStore store, + Map> blobstorePrototypes, + final DatabaseFreezeService databaseFreezeService, + final NodeAccess nodeAccess, + @Named("${nexus.blobstore.provisionDefaults:-false}") final boolean provisionDefaults) + { + this.eventManager = checkNotNull(eventManager); + this.store = checkNotNull(store); + this.blobstorePrototypes = checkNotNull(blobstorePrototypes); + this.databaseFreezeService = checkNotNull(databaseFreezeService); + this.nodeAccess = checkNotNull(nodeAccess); + this.provisionDefaults = provisionDefaults; + } + + @Override + protected void doStart() throws Exception { + List configurations = store.list(); + if (configurations.isEmpty() && (provisionDefaults || !nodeAccess.isClustered())) { + log.debug("No BlobStores configured; provisioning default BlobStore"); + store.create(new FileBlobStoreConfigurationBuilder(DEFAULT_BLOBSTORE_NAME).build()); + configurations = store.list(); + } + + log.debug("Restoring {} BlobStores", configurations.size()); + for (BlobStoreConfiguration configuration : configurations) { + log.debug("Restoring BlobStore: {}", configuration); + BlobStore blobStore = newBlobStore(configuration); + track(configuration.getName(), blobStore); + + // TODO - event publishing + } + + log.debug("Starting {} BlobStores", stores.size()); + for (BlobStore blobStore : stores.values()) { + log.debug("Starting BlobStore: {}", blobStore); + blobStore.start(); + + // TODO - event publishing + } + } + + @Override + protected void doStop() throws Exception { + if (stores.isEmpty()) { + log.debug("No BlobStores defined"); + return; + } + + log.debug("Stopping {} BlobStores", stores.size()); + for (Map.Entry entry : stores.entrySet()) { + String name = entry.getKey(); + BlobStore store = entry.getValue(); + log.debug("Stopping blob-store: {}", name); + store.stop(); + + // TODO - event publishing + } + + stores.clear(); + } + + @Override + @Guarded(by = STARTED) + public Iterable browse() { + return ImmutableList.copyOf(stores.values()); + } + + @Override + @Guarded(by = STARTED) + public BlobStore create(final BlobStoreConfiguration configuration) throws Exception { + checkNotNull(configuration); + log.debug("Creating BlobStore: {} with attributes: {}", configuration.getName(), + configuration.getAttributes()); + + BlobStore blobStore = newBlobStore(configuration); + + try { + store.create(configuration); + } + catch (Exception e) { + blobStore.remove(); + throw e; + } + + track(configuration.getName(), blobStore); + + blobStore.start(); + + eventManager.post(new BlobStoreCreatedEvent(blobStore)); + + return blobStore; + } + + @Override + @Guarded(by = STARTED) + @Nullable + public BlobStore get(final String name) { + checkNotNull(name); + + return stores.get(name); + } + + @Override + @Guarded(by = STARTED) + public void delete(final String name) throws Exception { + checkNotNull(name); + databaseFreezeService.checkUnfrozen("Unable to delete a BlobStore while database is frozen."); + + BlobStore blobStore = blobStore(name); + log.debug("Deleting BlobStore: {}", name); + blobStore.stop(); + blobStore.remove(); + untrack(name); + store.delete(blobStore.getBlobStoreConfiguration()); + + eventManager.post(new BlobStoreDeletedEvent(blobStore)); + } + + @Override + public boolean exists(final String name) { + return stores.keySet().stream().anyMatch(key -> key.equalsIgnoreCase(name)); + } + + private BlobStore newBlobStore(final BlobStoreConfiguration blobStoreConfiguration) throws Exception { + BlobStore blobStore = blobstorePrototypes.get(blobStoreConfiguration.getType()).get(); + blobStore.init(blobStoreConfiguration); + return blobStore; + } + + @VisibleForTesting + BlobStore blobStore(final String name) { + BlobStore blobStore = stores.get(name); + checkState(blobStore != null, "Missing BlobStore: %s", name); + return blobStore; + } + + private void track(final String name, final BlobStore blobStore) { + log.debug("Tracking: {}", name); + stores.put(name, blobStore); + } + + private void untrack(final String name) { + log.debug("Untracking: {}", name); + stores.remove(name); + } + + @Subscribe + public void on(final BlobStoreConfigurationCreatedEvent event) { + handleRemoteOnly(event, evt -> { + // only create if not tracked + String name = evt.getName(); + if (!stores.containsKey(name)) { + store.list().stream() + .filter(c -> c.getName().equals(name)) + .findFirst() + .ifPresent(c -> { + try { + BlobStore blobStore = newBlobStore(c); + track(name, blobStore); + blobStore.start(); + } + catch (Exception e) { + log.warn("create blob store from remote event failed: {}", name, e); + } + }); + } + }); + } + + @Subscribe + public void on(final BlobStoreConfigurationDeletedEvent event) { + handleRemoteOnly(event, evt -> { + try { + // only delete if tracked + String name = evt.getName(); + if (stores.containsKey(name)) { + BlobStore blobStore = blobStore(name); + blobStore.stop(); + blobStore.remove(); + untrack(name); + } + } + catch (Exception e) { + log.warn("delete blob store from remote event failed: {}", evt.getName(), e); + } + }); + } + + private void handleRemoteOnly(final BlobStoreConfigurationEvent event, + final Consumer consumer) + { + log.trace("handling: {}", event); + // skip local events + if (!event.isLocal()) { + consumer.accept(event); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreSecurityContributor.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreSecurityContributor.groovy new file mode 100644 index 0000000000000000000000000000000000000000..813f32c5c004243cd974f2f863c7022670f6707f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreSecurityContributor.groovy @@ -0,0 +1,75 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal.blobstore + +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.nexus.security.config.CPrivilege +import org.sonatype.nexus.security.config.MemorySecurityConfiguration +import org.sonatype.nexus.security.config.SecurityContributor + +/** + * BlobStore security configuration. + * + * @since 3.0 + */ +@Named +@Singleton +class BlobStoreSecurityContributor + implements SecurityContributor +{ + @Override + MemorySecurityConfiguration getContribution() { + return new MemorySecurityConfiguration( + privileges: [ + new CPrivilege( + id: 'nx-blobstores-all', + description: 'All permissions for Blob Stores', + type: 'application', + properties: [ + domain : 'blobstores', + actions: '*' + ] + ), + new CPrivilege( + id: 'nx-blobstores-create', + description: 'Create permission for Blob Stores', + type: 'application', + properties: [ + domain : 'blobstores', + actions: 'create,read' + ] + ), + new CPrivilege( + id: 'nx-blobstores-read', + description: 'Read permission for Blob Stores', + type: 'application', + properties: [ + domain : 'blobstores', + actions: 'read' + ] + ), + new CPrivilege( + id: 'nx-blobstores-delete', + description: 'Delete permission for Blob Stores', + type: 'application', + properties: [ + domain : 'blobstores', + actions: 'delete,read' + ] + ) + ] + ) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/LogConfigurationCustomizerImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/LogConfigurationCustomizerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..f705325303ecc80c94a6ff4b0069c8733c572bda --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/internal/blobstore/LogConfigurationCustomizerImpl.java @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal.blobstore; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.log.LogConfigurationCustomizer; + +import static org.sonatype.nexus.common.log.LoggerLevel.DEFAULT; + +/** + * Blob-store {@link LogConfigurationCustomizer}. + * + * @since 3.0 + */ +@Named +@Singleton +public class LogConfigurationCustomizerImpl + implements LogConfigurationCustomizer +{ + @Override + public void customize(final Configuration config) { + config.setLoggerLevel("org.sonatype.nexus.blobstore", DEFAULT); + config.setLoggerLevel("org.sonatype.nexus.repository.internal.blobstore", DEFAULT); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/maintenance/MaintenanceService.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/maintenance/MaintenanceService.java new file mode 100644 index 0000000000000000000000000000000000000000..2b738e2c470e076a7fa5810fa0f601c9aa00368e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/maintenance/MaintenanceService.java @@ -0,0 +1,35 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.maintenance; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; + +/** + * A service for executing maintenance operations (such as a 'delete') on assets in a repository. + * + * @since 3.3 + */ +public interface MaintenanceService +{ + /** + * Delete an asset in the specified repository. + */ + void deleteAsset(Repository repository, Asset asset); + + /** + * Delete a component in the specified repository. + */ + void deleteComponent(Repository repository, Component component); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/maintenance/internal/MaintenanceServiceImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/maintenance/internal/MaintenanceServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..a17e39da9a3a906c00bbc3594b5c1ce697159be7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/maintenance/internal/MaintenanceServiceImpl.java @@ -0,0 +1,113 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.maintenance.internal; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.nexus.repository.IllegalOperationException; +import org.sonatype.nexus.repository.MissingFacetException; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.maintenance.MaintenanceService; +import org.sonatype.nexus.repository.security.ContentPermissionChecker; +import org.sonatype.nexus.repository.security.VariableResolverAdapter; +import org.sonatype.nexus.repository.security.VariableResolverAdapterManager; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.ComponentMaintenance; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.repository.storage.StorageTx; +import org.sonatype.nexus.security.BreadActions; + +import org.apache.shiro.authz.AuthorizationException; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.String.format; + +/** + * @since 3.3 + */ +@Named +public class MaintenanceServiceImpl + implements MaintenanceService +{ + private final ContentPermissionChecker contentPermissionChecker; + + private final VariableResolverAdapterManager variableResolverAdapterManager; + + @Inject + public MaintenanceServiceImpl(final ContentPermissionChecker contentPermissionChecker, + final VariableResolverAdapterManager variableResolverAdapterManager) + { + this.contentPermissionChecker = checkNotNull(contentPermissionChecker); + this.variableResolverAdapterManager = checkNotNull(variableResolverAdapterManager); + } + + @Override + public void deleteAsset(final Repository repository, final Asset asset) { + checkNotNull(repository); + checkNotNull(asset); + + String repositoryFormat = repository.getFormat().toString(); + if (!canDeleteAssetInRepository(repository, repositoryFormat, variableResolverAdapterManager.get(repositoryFormat), + asset)) { + throw new AuthorizationException(); + } + + getComponentMaintenanceFacet(repository).deleteAsset(asset.getEntityMetadata().getId()); + } + + @Override + public void deleteComponent(final Repository repository, final Component component) { + checkNotNull(repository); + checkNotNull(component); + + String repositoryFormat = repository.getFormat().toString(); + VariableResolverAdapter variableResolverAdapter = variableResolverAdapterManager.get(repositoryFormat); + + StorageTx storageTx = repository.facet(StorageFacet.class).txSupplier().get(); + + try { + storageTx.begin(); + for (Asset asset : storageTx.browseAssets(component)) { + if (!canDeleteAssetInRepository(repository, repositoryFormat, variableResolverAdapter, asset)) { + throw new AuthorizationException(); + } + } + } + finally { + storageTx.close(); + } + + getComponentMaintenanceFacet(repository).deleteComponent(component.getEntityMetadata().getId()); + } + + private boolean canDeleteAssetInRepository(final Repository repository, + final String repositoryFormat, + final VariableResolverAdapter variableResolverAdapter, final Asset asset) + { + return contentPermissionChecker.isPermitted(repository.getName(), repositoryFormat, BreadActions.DELETE, + variableResolverAdapter.fromAsset(asset)); + } + + private ComponentMaintenance getComponentMaintenanceFacet(final Repository repository) { + try { + return repository.facet(ComponentMaintenance.class); + } + catch (MissingFacetException e) { + throw new IllegalOperationException( + format("Deleting from repository %s of type %s is not supported", repository.getName(), + repository.getFormat()), e); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/DefaultRepositoriesContributor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/DefaultRepositoriesContributor.java new file mode 100644 index 0000000000000000000000000000000000000000..6d9d8610067197074d7baee29fca65cf7bc7c453 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/DefaultRepositoriesContributor.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager; + +import java.util.List; + +import org.sonatype.nexus.repository.config.Configuration; + +/** + * Provides configurations for repositories that should be provisioned by default on first system startup. + * @since 3.0 + */ +public interface DefaultRepositoriesContributor +{ + /** + * Provides Configurations that should be be initially provisioned. + */ + List getRepositoryConfigurations(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryCreatedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryCreatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..4beb82b7c6d6b20b22cc7cdc52cb2003ad86aa8d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryCreatedEvent.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryEvent; + +/** + * Emitted when a repository has been created. + * + * @since 3.0 + */ +public class RepositoryCreatedEvent + extends RepositoryEvent +{ + public RepositoryCreatedEvent(final Repository repository) { + super(repository); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryDeletedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryDeletedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..3ff3ea46c2f798c057c91f344586b2a07307ae69 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryDeletedEvent.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryEvent; + +/** + * Emitted when a repository has been deleted. + * + * @since 3.0 + */ +public class RepositoryDeletedEvent + extends RepositoryEvent +{ + public RepositoryDeletedEvent(final Repository repository) { + super(repository); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryLoadedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryLoadedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..f0d39cb5d93c6146d874ddcf1b73c3a6ca49c875 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryLoadedEvent.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryEvent; + +/** + * Emitted when a repository has been loaded. + * + * @since 3.0 + */ +public class RepositoryLoadedEvent + extends RepositoryEvent +{ + public RepositoryLoadedEvent(final Repository repository) { + super(repository); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryManager.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryManager.java new file mode 100644 index 0000000000000000000000000000000000000000..3299a267185b44fcbb3635c86fbdf630e9b7032f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryManager.java @@ -0,0 +1,50 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.lifecycle.Lifecycle; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.config.Configuration; + +/** + * Repository manager. + * + * @since 3.0 + */ +public interface RepositoryManager + extends Lifecycle +{ + Iterable browse(); + + /** + * @since 3.6.1 + */ + Iterable browseForBlobStore(String blobStoreId); + + boolean exists(String name); + + @Nullable + Repository get(String name); + + Repository create(Configuration configuration) throws Exception; + + Repository update(Configuration configuration) throws Exception; + + void delete(String name) throws Exception; + + boolean isBlobstoreUsed(String blobStoreName); + + long blobstoreUsageCount(String blobStoreName); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryMetadataUpdatedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryMetadataUpdatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..84591c5e523574a21946f12b728bf30068a405e3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryMetadataUpdatedEvent.java @@ -0,0 +1,31 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryEvent; +import org.sonatype.nexus.repository.attributes.AttributesFacet; + +/** + * Emitted when a repository's metadata attributes have been updated. + * + * @since 3.6.1 + * @see AttributesFacet + */ +public class RepositoryMetadataUpdatedEvent + extends RepositoryEvent +{ + public RepositoryMetadataUpdatedEvent(final Repository repository) { + super(repository); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryRestoredEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryRestoredEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..69a7e97d72f7d68ab85b23e32744664dc8c61f15 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryRestoredEvent.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryEvent; + +/** + * Emitted when a repository has been restored. + * + * @since 3.0 + */ +public class RepositoryRestoredEvent + extends RepositoryEvent +{ + public RepositoryRestoredEvent(final Repository repository) { + super(repository); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryUpdatedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryUpdatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..f60db5b209b0ccc4cb5ec2fcef677c861e4ad057 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/RepositoryUpdatedEvent.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryEvent; + +/** + * Emitted when a repository has been updated. + * + * @since 3.0 + */ +public class RepositoryUpdatedEvent + extends RepositoryEvent +{ + public RepositoryUpdatedEvent(final Repository repository) { + super(repository); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/FacetLookup.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/FacetLookup.java new file mode 100644 index 0000000000000000000000000000000000000000..2673ecd419384cf153d6212498713470cedb4933 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/FacetLookup.java @@ -0,0 +1,134 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager.internal; + +import java.lang.annotation.Annotation; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.Facet; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.reflect.TypeToken; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * Helper to provide {@link Facet} lookup. + * + * @since 3.0 + */ +public class FacetLookup + extends ComponentSupport + implements Iterable +{ + /** + * All facet instances. Retain order. + */ + private final Set facets = Sets.newLinkedHashSet(); + + /** + * Exposed type to facet instance map. + */ + private final Map, Facet> exposed = Maps.newHashMap(); + + /** + * Add a facet and bind exposed types. + */ + public void add(final Facet facet) { + checkNotNull(facet); + + // discover all types to expose facet + Class root = facet.getClass(); + List> types = exposedTypes(root); + checkState(!types.isEmpty(), "No exposed facets: %s", root); + + log.trace("Adding facet: {}, exposed as: {}", facet, types); + + // verify we are not clobbering existing facet types + for (Class type : types) { + checkState(!exposed.containsKey(type), "Duplicate exposed facet type: %s, root type: %s", type, root); + } + + facets.add(facet); + + // map all exposed types to given facet + for (Class type : types) { + exposed.put(type, facet); + } + } + + /** + * Returns list of all types of given root type which implement {@link Facet} + * and are marked with {@link Facet.Exposed}. + */ + @SuppressWarnings("unchecked") + private List> exposedTypes(final Class root) { + List> exposed = Lists.newArrayList(); + + for (Class type : TypeToken.of(root).getTypes().rawTypes()) { + if (Facet.class.isAssignableFrom(type)) { + for (Annotation annotation : type.getDeclaredAnnotations()) { + if (annotation.annotationType() == Facet.Exposed.class) { + exposed.add((Class) type); + } + } + } + } + + return exposed; + } + + /** + * Get a facet instance by exposed type. + */ + @SuppressWarnings("unchecked") + @Nullable + public T get(final Class type) { + checkNotNull(type); + return (T) exposed.get(type); + } + + /** + * Clear all facet bindings. + */ + public void clear() { + facets.clear(); + exposed.clear(); + } + + /** + * List all facet instances. + */ + @Override + public Iterator iterator() { + return Iterators.unmodifiableIterator(facets.iterator()); + } + + /** + * Returns reversed list of all facet instances. + */ + public Iterable reverse() { + return ImmutableList.copyOf(facets).reverse(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryAdminSecurityContributor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryAdminSecurityContributor.java new file mode 100644 index 0000000000000000000000000000000000000000..d18b353f554b4ace1242e3509d3ead5a65db9b19 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryAdminSecurityContributor.java @@ -0,0 +1,98 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager.internal; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.security.config.MutableSecurityContributor; +import org.sonatype.nexus.security.config.SecurityConfiguration; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.security.BreadActions.ADD; +import static org.sonatype.nexus.security.BreadActions.BROWSE; +import static org.sonatype.nexus.security.BreadActions.DELETE; +import static org.sonatype.nexus.security.BreadActions.EDIT; +import static org.sonatype.nexus.security.BreadActions.READ; +import static org.sonatype.nexus.repository.security.RepositoryAdminPrivilegeDescriptor.id; +import static org.sonatype.nexus.repository.security.RepositoryAdminPrivilegeDescriptor.privilege; +import static org.sonatype.nexus.security.privilege.PrivilegeDescriptorSupport.ALL; + +/** + * Repository administration security contributor. + * + * @since 3.0 + */ +@Named +@Singleton +public class RepositoryAdminSecurityContributor + extends MutableSecurityContributor +{ + // TODO: Sort out role[-naming] scheme + + /** + * Initial (static) security configuration. + */ + @Override + protected void initial(final SecurityConfiguration model) { + model.addPrivilege(privilege(ALL, ALL, ALL)); + model.addPrivilege(privilege(ALL, ALL, BROWSE)); + model.addPrivilege(privilege(ALL, ALL, READ)); + model.addPrivilege(privilege(ALL, ALL, EDIT)); + model.addPrivilege(privilege(ALL, ALL, ADD)); + model.addPrivilege(privilege(ALL, ALL, DELETE)); + } + + /** + * Add security configuration for given repository. + */ + public void add(final Repository repository) { + checkNotNull(repository); + final String format = repository.getFormat().getValue(); + final String name = repository.getName(); + apply(new Mutator() + { + @Override + public void apply(final SecurityConfiguration model) { + // no per-repo repository-admin ADD action + model.addPrivilege(privilege(format, name, ALL)); + model.addPrivilege(privilege(format, name, BROWSE)); + model.addPrivilege(privilege(format, name, READ)); + model.addPrivilege(privilege(format, name, EDIT)); + model.addPrivilege(privilege(format, name, DELETE)); + } + }); + } + + /** + * Remove security configuration for given repository. + */ + public void remove(final Repository repository) { + checkNotNull(repository); + final String format = repository.getFormat().getValue(); + final String name = repository.getName(); + apply(new Mutator() + { + @Override + public void apply(final SecurityConfiguration model) { + // no per-repo repository-admin ADD action + model.removePrivilege(id(format, name, ALL)); + model.removePrivilege(id(format, name, BROWSE)); + model.removePrivilege(id(format, name, READ)); + model.removePrivilege(id(format, name, EDIT)); + model.removePrivilege(id(format, name, DELETE)); + } + }); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryAuditor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryAuditor.java new file mode 100644 index 0000000000000000000000000000000000000000..1ca7f524e171f86ea71456040853d9dde97c803b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryAuditor.java @@ -0,0 +1,79 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager.internal; + +import java.util.Map; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.audit.AuditData; +import org.sonatype.nexus.audit.AuditorSupport; +import org.sonatype.nexus.common.event.EventAware; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryDestroyedEvent; +import org.sonatype.nexus.repository.RepositoryEvent; +import org.sonatype.nexus.repository.RepositoryStartedEvent; +import org.sonatype.nexus.repository.RepositoryStoppedEvent; +import org.sonatype.nexus.repository.manager.RepositoryCreatedEvent; +import org.sonatype.nexus.repository.manager.RepositoryDeletedEvent; +import org.sonatype.nexus.repository.manager.RepositoryLoadedEvent; +import org.sonatype.nexus.repository.manager.RepositoryRestoredEvent; +import org.sonatype.nexus.repository.manager.RepositoryUpdatedEvent; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.Subscribe; + +/** + * Repository auditor. + * + * @since 3.1 + */ +@Named +@Singleton +public class RepositoryAuditor + extends AuditorSupport + implements EventAware +{ + public static final String DOMAIN = "repository"; + + public RepositoryAuditor() { + registerType(RepositoryCreatedEvent.class, CREATED_TYPE); + registerType(RepositoryRestoredEvent.class, "restored"); + registerType(RepositoryUpdatedEvent.class, UPDATED_TYPE); + registerType(RepositoryDestroyedEvent.class, "destroyed"); + registerType(RepositoryDeletedEvent.class, DELETED_TYPE); + registerType(RepositoryLoadedEvent.class, "loaded"); + registerType(RepositoryStartedEvent.class, "started"); + registerType(RepositoryStoppedEvent.class, "stopped"); + } + + @Subscribe + @AllowConcurrentEvents + public void on(final RepositoryEvent event) { + if (isRecording()) { + Repository repository = event.getRepository(); + AuditData data = new AuditData(); + data.setDomain(DOMAIN); + data.setType(type(event.getClass())); + data.setContext(repository.getName()); + + Map attributes = data.getAttributes(); + attributes.put("name", repository.getName()); + attributes.put("type", repository.getType().getValue()); + attributes.put("format", repository.getFormat().getValue()); + + record(data); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryFactory.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..7629d950f92465a7c12ce50193d317bd76a75001 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryFactory.java @@ -0,0 +1,27 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager.internal; + +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.Type; + +/** + * Repository factory. + * + * @since 3.0 + */ +public interface RepositoryFactory +{ + Repository create(Type type, Format format); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..77a7873f3f091efb1054476903c6da60e85ca07d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryImpl.java @@ -0,0 +1,338 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager.internal; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.inject.Inject; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.goodies.common.MultipleFailures; +import org.sonatype.nexus.common.app.BaseUrlHolder; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuard; +import org.sonatype.nexus.common.stateguard.StateGuardAware; +import org.sonatype.nexus.common.stateguard.Transitions; +import org.sonatype.nexus.repository.Facet; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.MissingFacetException; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryDestroyedEvent; +import org.sonatype.nexus.repository.RepositoryStartedEvent; +import org.sonatype.nexus.repository.RepositoryStoppedEvent; +import org.sonatype.nexus.repository.Type; +import org.sonatype.nexus.repository.config.Configuration; + +import com.google.inject.assistedinject.Assisted; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.manager.internal.RepositoryImpl.State.DELETED; +import static org.sonatype.nexus.repository.manager.internal.RepositoryImpl.State.DESTROYED; +import static org.sonatype.nexus.repository.manager.internal.RepositoryImpl.State.FAILED; +import static org.sonatype.nexus.repository.manager.internal.RepositoryImpl.State.INITIALISED; +import static org.sonatype.nexus.repository.manager.internal.RepositoryImpl.State.NEW; +import static org.sonatype.nexus.repository.manager.internal.RepositoryImpl.State.STARTED; +import static org.sonatype.nexus.repository.manager.internal.RepositoryImpl.State.STOPPED; + +/** + * {@link Repository} implementation. + * + * @since 3.0 + */ +public class RepositoryImpl + extends ComponentSupport + implements Repository, StateGuardAware +{ + private final EventManager eventManager; + + private final Type type; + + private final Format format; + + private final FacetLookup facets = new FacetLookup(); + + private Configuration configuration; + + private String name; + + @Inject + public RepositoryImpl(final EventManager eventManager, + @Assisted final Type type, + @Assisted final Format format) + { + this.eventManager = checkNotNull(eventManager); + this.type = checkNotNull(type); + this.format = checkNotNull(format); + } + + @Override + public Type getType() { + return type; + } + + @Override + public Format getFormat() { + return format; + } + + @Override + public String getName() { + return checkNotNull(name); + } + + @Override + public Configuration getConfiguration() { + return checkNotNull(configuration); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "type=" + type + + ", format=" + format + + ", name='" + name + '\'' + + '}'; + } + + // + // State + // + + static final class State + { + public static final String NEW = "NEW"; + + public static final String INITIALISED = "INITIALISED"; + + public static final String STARTED = "STARTED"; + + public static final String STOPPED = "STOPPED"; + + public static final String DELETED = "DELETED"; + + public static final String DESTROYED = "DESTROYED"; + + public static final String FAILED = "FAILED"; + } + + private final StateGuard states = new StateGuard.Builder() + .logger(createLogger()) + .initial(NEW) + .failure(FAILED) + .create(); + + @Override + @Nonnull + public StateGuard getStateGuard() { + return states; + } + + // + // Lifecycle + // + + @Override + public void validate(final Configuration configuration) throws Exception { + checkNotNull(configuration); + + Set> violations = new HashSet<>(); + MultipleFailures failures = new MultipleFailures(); + + for (Facet facet : facets) { + log.debug("Validating facet: {}", facet); + try { + facet.validate(configuration); + } + catch (ConstraintViolationException e) { + log.debug("Facet validation produced violations: {}", facet, e); + violations.addAll(e.getConstraintViolations()); + } + catch (Throwable t) { + log.error("Failed to validate facet: {}", facet, t); + failures.add(t); + } + } + failures.maybePropagate("Failed to validate facets"); + + if (!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } + } + + @Override + @Transitions(from = NEW, to = INITIALISED) + public void init(final Configuration configuration) throws Exception { + this.configuration = checkNotNull(configuration); + this.name = configuration.getRepositoryName(); + + MultipleFailures failures = new MultipleFailures(); + for (Facet facet : facets) { + try { + log.debug("Initializing facet: {}", facet); + facet.init(); + } + catch (Throwable t) { + log.error("Failed to initialize facet: {}", facet, t); + failures.add(t); + } + } + failures.maybePropagate("Failed to initialize facets"); + } + + @Override + @Guarded(by = STOPPED) + public void update(final Configuration configuration) throws Exception { + checkNotNull(configuration); + + // Ensure configuration sanity + validate(configuration); + this.configuration = configuration; + + MultipleFailures failures = new MultipleFailures(); + for (Facet facet : facets) { + try { + log.debug("Updating facet: {}", facet); + facet.update(); + } + catch (Throwable t) { + log.error("Failed to update facet: {}", facet, t); + failures.add(t); + } + } + failures.maybePropagate("Failed to update facets"); + } + + @Override + @Transitions(from = {INITIALISED, STOPPED}, to = STARTED) + public void start() throws Exception { + MultipleFailures failures = new MultipleFailures(); + for (Facet facet : facets) { + try { + log.debug("Starting facet: {}", facet); + facet.start(); + } + catch (Throwable t) { + log.error("Failed to start facet: {}", facet, t); + failures.add(t); + } + } + failures.maybePropagate("Failed to start facets"); + + eventManager.post(new RepositoryStartedEvent(this)); + } + + @Override + @Transitions(from = STARTED, to = STOPPED) + public void stop() throws Exception { + MultipleFailures failures = new MultipleFailures(); + + for (Facet facet : facets.reverse()) { + try { + log.debug("Stopping facet: {}", facet); + facet.stop(); + } + catch (Throwable t) { + log.error("Failed to stop facet: {}", facet, t); + failures.add(t); + } + } + failures.maybePropagate("Failed to stop facets"); + + eventManager.post(new RepositoryStoppedEvent(this)); + } + + @Override + @Transitions(from = STOPPED, to = DELETED) + public void delete() throws Exception { + MultipleFailures failures = new MultipleFailures(); + + for (Facet facet : facets.reverse()) { + try { + log.debug("Deleting facet: {}", facet); + facet.delete(); + } + catch (Throwable t) { + log.error("Failed to delete facet: {}", facet, t); + failures.add(t); + } + } + failures.maybePropagate("Failed to delete facets"); + } + + @Override + @Transitions(to = DESTROYED) + public void destroy() throws Exception { + if (states.is(STARTED)) { + stop(); + } + + MultipleFailures failures = new MultipleFailures(); + for (Facet facet : facets.reverse()) { + try { + log.debug("Destroying facet: {}", facet); + facet.destroy(); + } + catch (Throwable t) { + log.error("Failed to destroy facet: {}", facet, t); + failures.add(t); + } + } + failures.maybePropagate("Failed to destroy facets"); + + facets.clear(); + configuration = null; + + eventManager.post(new RepositoryDestroyedEvent(this)); + } + + // + // Facets + // + + @Override + @Guarded(by = NEW) + public void attach(final Facet facet) throws Exception { + checkNotNull(facet); + log.debug("Attaching facet: {}", facet); + facet.attach(this); + facets.add(facet); + } + + @Override + @Nonnull + public T facet(final Class type) throws MissingFacetException { + T facet = facets.get(type); + if (facet == null) { + throw new MissingFacetException(this, type); + } + + return facet; + } + + @Override + @Nonnull + public Optional optionalFacet(final Class type) { + return Optional.ofNullable(facets.get(type)); + } + + @Override + public String getUrl() { + return BaseUrlHolder.get() + "/repository/" + getName(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryManagerImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryManagerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..f2ab2c81e31c6d60d96387160328d1089ed4f1cc --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/internal/RepositoryManagerImpl.java @@ -0,0 +1,456 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager.internal; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.blobstore.api.BlobStoreManager; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.event.EventAware; +import org.sonatype.nexus.common.event.EventConsumer; +import org.sonatype.nexus.common.event.EventHelper; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.jmx.reflect.ManagedObject; +import org.sonatype.nexus.orient.freeze.DatabaseFreezeService; +import org.sonatype.nexus.repository.Recipe; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.config.ConfigurationFacet; +import org.sonatype.nexus.repository.config.internal.ConfigurationCreatedEvent; +import org.sonatype.nexus.repository.config.internal.ConfigurationDeletedEvent; +import org.sonatype.nexus.repository.config.internal.ConfigurationEvent; +import org.sonatype.nexus.repository.config.internal.ConfigurationStore; +import org.sonatype.nexus.repository.config.internal.ConfigurationUpdatedEvent; +import org.sonatype.nexus.repository.group.GroupFacet; +import org.sonatype.nexus.repository.manager.DefaultRepositoriesContributor; +import org.sonatype.nexus.repository.manager.RepositoryCreatedEvent; +import org.sonatype.nexus.repository.manager.RepositoryDeletedEvent; +import org.sonatype.nexus.repository.manager.RepositoryLoadedEvent; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.manager.RepositoryMetadataUpdatedEvent; +import org.sonatype.nexus.repository.manager.RepositoryRestoredEvent; +import org.sonatype.nexus.repository.manager.RepositoryUpdatedEvent; +import org.sonatype.nexus.repository.storage.internal.BucketUpdatedEvent; +import org.sonatype.nexus.repository.view.ViewFacet; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; +import com.google.common.eventbus.Subscribe; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static java.util.stream.StreamSupport.stream; +import static org.sonatype.nexus.blobstore.api.BlobStoreManager.DEFAULT_BLOBSTORE_NAME; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SERVICES; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; + +/** + * Default {@link RepositoryManager} implementation. + * + * @since 3.0 + */ +@Named +@Singleton +@ManagedLifecycle(phase = SERVICES) +@ManagedObject( + domain = "org.sonatype.nexus.repository.manager", + typeClass = RepositoryManager.class, + description = "Repository manager" +) +public class RepositoryManagerImpl + extends StateGuardLifecycleSupport + implements RepositoryManager, EventAware +{ + private final DatabaseFreezeService databaseFreezeService; + + private final EventManager eventManager; + + private final ConfigurationStore store; + + private final Map recipes; + + private final RepositoryFactory factory; + + private final Provider configFacet; + + private final RepositoryAdminSecurityContributor securityContributor; + + private final List defaultRepositoriesContributors; + + private final Map repositories = Maps.newConcurrentMap(); + + private final boolean skipDefaultRepositories; + + private final BlobStoreManager blobStoreManager; + + @Inject + public RepositoryManagerImpl(final EventManager eventManager, + final ConfigurationStore store, + final RepositoryFactory factory, + final Provider configFacet, + final Map recipes, + final RepositoryAdminSecurityContributor securityContributor, + final List defaultRepositoriesContributors, + final DatabaseFreezeService databaseFreezeService, + @Named("${nexus.skipDefaultRepositories:-false}") final boolean skipDefaultRepositories, + final BlobStoreManager blobStoreManager) + { + this.eventManager = checkNotNull(eventManager); + this.store = checkNotNull(store); + this.factory = checkNotNull(factory); + this.configFacet = checkNotNull(configFacet); + this.recipes = checkNotNull(recipes); + this.securityContributor = checkNotNull(securityContributor); + this.defaultRepositoriesContributors = checkNotNull(defaultRepositoriesContributors); + this.databaseFreezeService = checkNotNull(databaseFreezeService); + this.skipDefaultRepositories = skipDefaultRepositories; + this.blobStoreManager = checkNotNull(blobStoreManager); + } + + /** + * Lookup a recipe by name. + */ + private Recipe recipe(final String name) { + Recipe recipe = recipes.get(name); + checkState(recipe != null, "Missing recipe: %s", name); + return recipe; + } + + /** + * Lookup a repository by name. + */ + private Repository repository(final String name) { + Repository repository = repositories.get(name); + checkState(repository != null, "Missing repository: %s", name); + return repository; + } + + /** + * Construct a new repository from configuration. + */ + private Repository newRepository(final Configuration configuration) throws Exception { + String recipeName = configuration.getRecipeName(); + Recipe recipe = recipe(recipeName); + log.debug("Using recipe: [{}] {}", recipeName, recipe); + + Repository repository = factory.create(recipe.getType(), recipe.getFormat()); + + // attach mandatory facets + repository.attach(configFacet.get()); + + // apply recipe to repository + recipe.apply(repository); + + // verify required facets + repository.facet(ViewFacet.class); + + // ensure configuration sanity, once all facets are attached + repository.validate(configuration); + + // initialize repository + repository.init(configuration); + + return repository; + } + + /** + * Track repository. + */ + private void track(final Repository repository) { + // configure security + securityContributor.add(repository); + + log.debug("Tracking: {}", repository); + repositories.put(repository.getName(), repository); + } + + /** + * Untrack repository. + */ + private void untrack(final Repository repository) { + log.debug("Untracking: {}", repository); + repositories.remove(repository.getName()); + + // tear down security + securityContributor.remove(repository); + } + + // TODO: Generally need to consider exception handling to ensure proper state is maintained always + + @Override + protected void doStart() throws Exception { + blobStoreManager.start(); + + List configurations = store.list(); + + // attempt to provision default repositories if allowed + if (configurations.isEmpty()) { + if (skipDefaultRepositories || !blobStoreManager.exists(DEFAULT_BLOBSTORE_NAME)) { + log.debug("Skipping provisioning of default repositories"); + return; + } + + // attempt to discover default repository configurations + log.debug("No repositories configured; provisioning default repositories"); + provisionDefaultRepositories(); + configurations = store.list(); + + // if we still have no repository configurations, complain and stop + if (configurations.isEmpty()) { + log.debug("No default repositories to provision"); + return; + } + } + + restoreRepositories(configurations); + + startRepositories(); + } + + private void provisionDefaultRepositories() { + for (DefaultRepositoriesContributor contributor : defaultRepositoriesContributors) { + for (Configuration configuration : contributor.getRepositoryConfigurations()) { + log.debug("Provisioning default repository: {}", configuration); + store.create(configuration); + } + } + } + + private void restoreRepositories(final List configurations) throws Exception { + log.debug("Restoring {} repositories", configurations.size()); + for (Configuration configuration : configurations) { + log.debug("Restoring repository: {}", configuration); + Repository repository = newRepository(configuration); + track(repository); + + eventManager.post(new RepositoryLoadedEvent(repository)); + } + } + + private void startRepositories() throws Exception { + log.debug("Starting {} repositories", repositories.size()); + for (Repository repository : repositories.values()) { + log.debug("Starting repository: {}", repository); + repository.start(); + + eventManager.post(new RepositoryRestoredEvent(repository)); + } + } + + @Override + protected void doStop() throws Exception { + + log.debug("Stopping {} repositories", repositories.size()); + for (Repository repository : repositories.values()) { + log.debug("Stopping repository: {}", repository); + repository.stop(); + } + + log.debug("Destroying {} repositories", repositories.size()); + for (Repository repository : repositories.values()) { + log.debug("Destroying repository: {}", repository); + repository.destroy(); + } + + repositories.clear(); + + blobStoreManager.stop(); + } + + @Override + @Guarded(by = STARTED) + public Iterable browse() { + return ImmutableList.copyOf(repositories.values()); + } + + @Override + @Guarded(by = STARTED) + public Iterable browseForBlobStore(String blobStoreId) { + return stream(browse().spliterator(), true) + .filter(r -> blobStoreId.equals(r.getConfiguration().attributes("storage").get("blobStoreName"))) + ::iterator; + } + + @Override + public boolean exists(final String name) { + return stream(browse().spliterator(), false).anyMatch(repository -> repository.getName().equalsIgnoreCase(name)); + } + + @Nullable + @Override + @Guarded(by = STARTED) + public Repository get(final String name) { + checkNotNull(name); + + return repositories.get(name); + } + + @Override + @Guarded(by = STARTED) + public Repository create(final Configuration configuration) throws Exception { + checkNotNull(configuration); + String repositoryName = checkNotNull(configuration.getRepositoryName()); + + log.info("Creating repository: {} -> {}", repositoryName, configuration); + + Repository repository = newRepository(configuration); + + if (!EventHelper.isReplicating()) { + store.create(configuration); + } + + track(repository); + + repository.start(); + + eventManager.post(new RepositoryCreatedEvent(repository)); + + return repository; + } + + @Override + @Guarded(by = STARTED) + public Repository update(final Configuration configuration) throws Exception { + checkNotNull(configuration); + String repositoryName = checkNotNull(configuration.getRepositoryName()); + + log.info("Updating repository: {} -> {}", repositoryName, configuration); + + Repository repository = repository(repositoryName); + + // ensure configuration sanity + repository.validate(configuration); + + if (!EventHelper.isReplicating()) { + store.update(configuration); + } + + repository.stop(); + repository.update(configuration); + repository.start(); + + eventManager.post(new RepositoryUpdatedEvent(repository)); + + return repository; + } + + @Override + @Guarded(by = STARTED) + public void delete(final String name) throws Exception { + checkNotNull(name); + databaseFreezeService.checkUnfrozen("Unable to delete repository when database is frozen."); + + log.info("Deleting repository: {}", name); + + Repository repository = repository(name); + Configuration configuration = repository.getConfiguration(); + + removeRepositoryFromAllGroups(repository); + + repository.stop(); + repository.delete(); + repository.destroy(); + + if (!EventHelper.isReplicating()) { + store.delete(configuration); + } + + untrack(repository); + + eventManager.post(new RepositoryDeletedEvent(repository)); + } + + private void removeRepositoryFromAllGroups(final Repository repositoryToRemove) throws Exception { + for (Repository group : repositories.values()) { + Optional groupFacet = group.optionalFacet(GroupFacet.class); + if (groupFacet.isPresent() && groupFacet.get().member(repositoryToRemove)) { + removeRepositoryFromGroup(repositoryToRemove, group); + } + } + } + + private void removeRepositoryFromGroup(final Repository repositoryToRemove, final Repository group) throws Exception { + NestedAttributesMap groupAttributes = group.getConfiguration().attributes("group"); + groupAttributes.get("memberNames", Collection.class).remove(repositoryToRemove.getName()); + update(group.getConfiguration()); + } + + private Stream blobstoreUsageStream(final String blobStoreName) { + return stream(browse().spliterator(), false) + .map(Repository::getConfiguration) + .map(Configuration::getAttributes) + .map(a -> a.get("storage")) + .map(s -> s.get("blobStoreName")) + .filter(blobStoreName::equals); + } + + @Override + public boolean isBlobstoreUsed(final String blobStoreName) { + return blobstoreUsageStream(blobStoreName).findAny().isPresent(); + } + + @Override + public long blobstoreUsageCount(final String blobStoreName) { + return blobstoreUsageStream(blobStoreName).count(); + } + + @Subscribe + public void on(final ConfigurationCreatedEvent event) { + handleReplication(event, e -> create(e.getConfiguration())); + } + + @Subscribe + public void on(final ConfigurationUpdatedEvent event) { + handleReplication(event, e -> update(e.getConfiguration())); + } + + @Subscribe + public void on(final ConfigurationDeletedEvent event) { + handleReplication(event, e -> delete(e.getRepositoryName())); + } + + private void handleReplication(final ConfigurationEvent event, final EventConsumer consumer) { + if (!event.isLocal()) { + try { + consumer.accept(event); + } + catch (Exception e) { + log.error("Failed to replicate: {}", event, e); + } + } + } + + @Subscribe + public void onBucketUpdated(final BucketUpdatedEvent event) { + Repository repository = repositories.get(event.getRepositoryName()); + if (repository != null) { + eventManager.post(new RepositoryMetadataUpdatedEvent(repository)); + } + else { + log.debug("Not posting metadata update event for deleted repository {}", event.getRepositoryName()); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..19bed8db1ff3de35a398f84354e33eca392452e3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/manager/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Repository manager services. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.manager; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..c63629e834eb3d68ea62e42f53a1cf082e73aff9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Repository framework. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/Cooperation.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/Cooperation.java new file mode 100644 index 0000000000000000000000000000000000000000..41063a2a7791283a836036c15ac3ce683408e533 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/Cooperation.java @@ -0,0 +1,269 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.proxy; + +import java.io.IOException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import org.sonatype.goodies.common.Time; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Throwables.propagateIfPossible; +import static java.lang.Boolean.TRUE; + +/** + * Manages cooperation between multiple threads to avoid duplicated I/O. + * + * @since 3.4 + */ +public class Cooperation +{ + private static final Logger log = LoggerFactory.getLogger(Cooperation.class); + + private static final ThreadLocal isDownloading = new ThreadLocal<>(); + + private final ConcurrentMap> futureValues = new ConcurrentHashMap<>(); + + private final Time passiveTimeout; + + private final Time activeTimeout; + + private final int threadsPerKey; + + @FunctionalInterface + public interface IOCall + { + T call(boolean checkCache) throws IOException; + } + + /** + * @param passiveTimeout used when passively waiting for an initial download + * @param activeTimeout used when actively waiting for a download dependency + * @param threadsPerKey maximum threads that can wait for the same download + */ + public Cooperation(final Time passiveTimeout, final Time activeTimeout, final int threadsPerKey) { + this.passiveTimeout = checkNotNull(passiveTimeout); + this.activeTimeout = checkNotNull(activeTimeout); + this.threadsPerKey = threadsPerKey; + } + + /** + * Requests cooperation with other threads that might already be downloading the same remote content. + * + * @param key uniquely identifies the content to be downloaded + * @param fetch function that will download the remote content + * + * @return remote content + * + * @throws IOException when download fails + * @throws CooperationException when cooperation fails + */ + public T cooperate(final String key, final IOCall fetch) throws IOException { + + // try and declare our interest in downloading the content + CooperatingFuture myFuture = new CooperatingFuture<>(key); + CooperatingFuture theirFuture = futureValues.putIfAbsent(key, myFuture); + + if (theirFuture == null) { + // no-one else is downloading the content, go-ahead + try { + return download(myFuture, fetch, false); + } + catch (Exception | Error e) { // NOSONAR report all download errors to cooperating threads + myFuture.completeExceptionally(e); + throw e; + } + finally { + futureValues.remove(key, myFuture); + } + } + + theirFuture.increaseCooperation(threadsPerKey); + try { + if (currentThreadAlreadyDownloading()) { + // this is a dependency; use shorter timeout and be prepared to download in parallel + // (just in case the thread we're waiting on ends up waiting on our primary content) + return waitForDownload(theirFuture, fetch, activeTimeout, true); + } + else { + // waiting for primary content; use longer timeout and avoid downloading in parallel + return waitForDownload(theirFuture, fetch, passiveTimeout, false); + } + } + finally { + theirFuture.decreaseCooperation(); + } + } + + /** + * Cooperatively waits for the download thread; may resort to downloading in parallel if allowed. + * + * @param future shared future that request threads cooperate on + * @param fetch function that will download the remote content + * @param initialTimeout how long to wait for the download if we were the only thread waiting + * @param downloadOnTimeout whether to download in parallel if original thread takes too long + * + * @return remote content + * + * @throws IOException when download fails + * @throws CooperationException when cooperation fails + */ + private T waitForDownload(final CooperatingFuture future, // NOSONAR + final IOCall fetch, + final Time initialTimeout, + final boolean downloadOnTimeout) + throws IOException + { + try { + if (initialTimeout.value() <= 0) { + log.debug("Attempt cooperative wait on {}", future); + return future.get(); // wait indefinitely + } + + Time timeout = initialTimeout; + if (downloadOnTimeout) { + timeout = future.staggerTimeout(timeout); // preserve minimum gap between parallel downloads + } + + log.debug("Attempt cooperative wait on {} for {}", future, timeout); + return future.get(timeout.value(), timeout.unit()); + } + catch (ExecutionException e) { // NOSONAR unwrap and report download errors + log.debug("Cooperative wait failed on {}", future, e.getCause()); + propagateIfPossible(e.getCause(), IOException.class); + throw new IOException("Cooperative wait failed on " + future, e.getCause()); + } + catch (CancellationException | InterruptedException e) { + log.debug("Cooperative wait cancelled on {}", future, e); + throw new CooperationException("Cooperative wait cancelled on " + future); + } + catch (TimeoutException e) { + log.debug("Cooperative wait timed out on {}", future, e); + + if (downloadOnTimeout) { + return download(future, fetch, true); // go remote in case original download thread is stuck + } + + throw new CooperationException("Cooperative wait timed out on " + future); + } + } + + /** + * Is the current thread flagged as already downloading some other content? + * + * This can happen when a download dependency, such as an index file, is needed to complete the initial request. + */ + private static boolean currentThreadAlreadyDownloading() { + return TRUE.equals(isDownloading.get()); + } + + /** + * Attempt to download remote content, optionally checking local cache, storing any result in the given future. + */ + private T download(final CooperatingFuture future, final IOCall fetch, final boolean checkCache) + throws IOException + { + if (currentThreadAlreadyDownloading()) { + return future.download(fetch, checkCache); + } + try { + isDownloading.set(TRUE); + return future.download(fetch, checkCache); + } + finally { + isDownloading.remove(); + } + } + + /** + * {@link CompletableFuture} that keeps track of the last staggered time to + * preserve a minimum gap between download requests for the same content. + */ + static class CooperatingFuture + extends CompletableFuture + { + private final AtomicLong staggerTimeMillis = new AtomicLong(System.currentTimeMillis()); + + private final AtomicInteger cooperationCount = new AtomicInteger(1); + + private final String key; + + CooperatingFuture(final String key) { + this.key = checkNotNull(key); + } + + /** + * Increases the cooperation count by one. + * + * @throws CooperationException if increasing the count would breach the given limit. + */ + public void increaseCooperation(final int limit) { + // try to avoid depleting entire request pool with waiting threads + cooperationCount.getAndUpdate(count -> { + if (count >= limit) { + log.debug("Thread cooperation maxed for {}", this); + throw new CooperationException("Thread cooperation maxed for " + this); + } + return count + 1; + }); + } + + /** + * Decreases the cooperation count by one. + */ + public void decreaseCooperation() { + cooperationCount.decrementAndGet(); + } + + /** + * @return staggered timeout that makes sure waiting threads don't all wake-up at the same time + */ + public Time staggerTimeout(final Time gap) { + long currentTimeMillis = System.currentTimeMillis(); + + // atomically progress the staggered time + long prevTimeMillis, nextTimeMillis; + do { + prevTimeMillis = staggerTimeMillis.get(); + nextTimeMillis = Math.max(prevTimeMillis + gap.toMillis(), currentTimeMillis); + } + while (!staggerTimeMillis.compareAndSet(prevTimeMillis, nextTimeMillis)); + + return Time.millis(nextTimeMillis - currentTimeMillis); + } + + /** + * Fluent method that downloads content, stores it in this future, before finally returning it. + */ + public T download(final IOCall fetch, final boolean checkCache) throws IOException { + T value = fetch.call(checkCache); + complete(value); + return value; + } + + @Override + public String toString() { + return key + " (" + cooperationCount.get() + " threads cooperating)"; + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/CooperationException.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/CooperationException.java new file mode 100644 index 0000000000000000000000000000000000000000..424d14f5b3026c1e1223577a5ba3f8ac55ffbce2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/CooperationException.java @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.proxy; + +/** + * Thrown when the current thread is not able to cooperate on a download. + * + * @since 3.6 + */ +public class CooperationException + extends RuntimeException +{ + public CooperationException(final String message) { + super(message); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacet.java new file mode 100644 index 0000000000000000000000000000000000000000..b629da06922a89e0b7ab068146c8b2ba52a82afe --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacet.java @@ -0,0 +1,50 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.proxy; + +import java.io.IOException; +import java.net.URI; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.Facet; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.repository.view.Context; + +/** + * A format neutral proxy facet. + * + * @since 3.0 + */ +@Facet.Exposed +public interface ProxyFacet + extends Facet +{ + /** + * Obtain the content which the user has requested, either by retrieving cached content, or by fetching new or + * updated content from the upstream repository. + */ + @Nullable + Content get(Context context) throws IOException; + + /** + * Returns the root of the remote repository. + */ + URI getRemoteUrl(); + + /** + * Invalidates all entries from proxy cache, causing all subsequent requests to attempt to fetch new or updated + * content, if any. + */ + void invalidateProxyCaches(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupport.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..6fa3cb970b1d2f519979263be18f35f1b6175131 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupport.java @@ -0,0 +1,511 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.proxy; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URI; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.validation.constraints.NotNull; + +import org.sonatype.goodies.common.Time; +import org.sonatype.nexus.repository.BadRequestException; +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.InvalidContentException; +import org.sonatype.nexus.repository.cache.CacheController; +import org.sonatype.nexus.repository.cache.CacheControllerHolder; +import org.sonatype.nexus.repository.cache.CacheInfo; +import org.sonatype.nexus.repository.cache.NegativeCacheFacet; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.config.ConfigurationFacet; +import org.sonatype.nexus.repository.httpclient.HttpClientFacet; +import org.sonatype.nexus.repository.storage.MissingBlobException; +import org.sonatype.nexus.repository.storage.RetryDeniedException; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.payloads.HttpEntityPayload; +import org.sonatype.nexus.validation.constraint.Url; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; +import com.google.common.io.Closeables; +import com.google.common.net.HttpHeaders; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.utils.DateUtils; +import org.apache.http.client.utils.HttpClientUtils; +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * A support class which implements basic payload logic; subclasses provide format-specific operations. + * + * @since 3.0 + */ +public abstract class ProxyFacetSupport + extends FacetSupport + implements ProxyFacet +{ + @VisibleForTesting + static final String CONFIG_KEY = "proxy"; + + @VisibleForTesting + public static class Config + { + @Url + @NotNull + public URI remoteUrl; + + /** + * Content max-age minutes. + */ + @NotNull + public Integer contentMaxAge = Time.hours(24).toMinutesI(); + + /** + * Metadata max-age minutes. + */ + @NotNull + public Integer metadataMaxAge = Time.hours(24).toMinutesI(); + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "remoteUrl=" + remoteUrl + + ", contentMaxAge=" + contentMaxAge + + '}'; + } + } + + private Config config; + + private HttpClientFacet httpClient; + + private boolean remoteUrlChanged; + + protected CacheControllerHolder cacheControllerHolder; + + private Cooperation contentCooperation; + + /** + * Configures content {@link Cooperation} for this proxy; a timeout of 0 means wait indefinitely. + * + * @param enabled should threads attempt to cooperate when downloading resources + * @param passiveTimeout used when passively cooperating on an initial download + * @param activeTimeout used when actively cooperating on a download dependency + * @param threadsPerKey maximum threads that can cooperate on the same download + * + * @since 3.4 + */ + @Inject + protected void configureCooperation( + @Named("${nexus.proxy.cooperation.enabled:-true}") final boolean cooperationEnabled, + @Named("${nexus.proxy.cooperation.passiveTimeout:-0s}") final Time passiveTimeout, + @Named("${nexus.proxy.cooperation.activeTimeout:-30s}") final Time activeTimeout, + @Named("${nexus.proxy.cooperation.threadsPerKey:-100}") final int threadsPerKey) + { + if (cooperationEnabled) { + this.contentCooperation = new Cooperation<>(passiveTimeout, activeTimeout, threadsPerKey); + } + } + + @Override + protected void doValidate(final Configuration configuration) throws Exception { + facet(ConfigurationFacet.class).validateSection(configuration, CONFIG_KEY, Config.class); + } + + @Override + protected void doConfigure(final Configuration configuration) throws Exception { + config = facet(ConfigurationFacet.class).readSection(configuration, CONFIG_KEY, Config.class); + + cacheControllerHolder = new CacheControllerHolder( + new CacheController(Time.minutes(config.contentMaxAge).toSecondsI(), null), + new CacheController(Time.minutes(config.metadataMaxAge).toSecondsI(), null) + ); + + // normalize URL path to contain trailing slash + if (!config.remoteUrl.getPath().endsWith("/")) { + config.remoteUrl = config.remoteUrl.resolve(config.remoteUrl.getPath() + "/"); + } + + log.debug("Config: {}", config); + } + + @Override + protected void doUpdate(final Configuration configuration) throws Exception { + // detect URL changes + URI previousUrl = config.remoteUrl; + super.doUpdate(configuration); + remoteUrlChanged = !config.remoteUrl.equals(previousUrl); + } + + @Override + protected void doDestroy() throws Exception { + config = null; + } + + @Override + protected void doStart() throws Exception { + httpClient = facet(HttpClientFacet.class); + + if (remoteUrlChanged) { + remoteUrlChanged = false; + + optionalFacet(NegativeCacheFacet.class).ifPresent((nfc) -> nfc.invalidate()); + } + } + + @Override + protected void doStop() throws Exception { + httpClient = null; + } + + public URI getRemoteUrl() { + return config.remoteUrl; + } + + @Override + public Content get(final Context context) throws IOException { + checkNotNull(context); + + Content content = maybeGetCachedContent(context); + if (!isStale(context, content)) { + return content; + } + if (contentCooperation == null) { + return doGet(context, content); + } + return contentCooperation.cooperate(getRequestKey(context), (checkCache) -> { + Content latestContent = content; + if (checkCache) { + latestContent = maybeGetCachedContent(context); + if (!isStale(context, latestContent)) { + return latestContent; + } + } + return doGet(context, latestContent); + }); + } + + /** + * @since 3.4 + */ + protected Content doGet(final Context context, @Nullable final Content staleContent) throws IOException { + Content remote = null, content = staleContent; + + try { + remote = fetch(context, content); + if (remote != null) { + content = store(context, remote); + if (contentCooperation != null && remote.equals(content)) { + // remote wasn't stored; make reusable copy for cooperation + content = new TempContent(remote); + } + } + } + catch (ProxyServiceException e) { + logContentOrThrow(content, context, e.getHttpResponse().getStatusLine(), e); + } + catch (IOException e) { + logContentOrThrow(content, context, null, e); // note this also takes care of RemoteBlockedIOException + } + catch (UncheckedIOException e) { + logContentOrThrow(content, context, null, e.getCause()); // "special" path (for now) for npm and similar peculiar formats + } + finally { + if (remote != null && !remote.equals(content)) { + Closeables.close(remote, true); + } + } + + return content; + } + + /** + * Path + query parameters provide a unique enough request key for known formats. + * If a format needs to add more context then they should customize this method. + * + * @return key that uniquely identifies this upstream request from other contexts + * + * @since 3.4 + */ + protected String getRequestKey(final Context context) { + return context.getRequest().getPath() + '?' + context.getRequest().getParameters(); + } + + private void logContentOrThrow(@Nullable final Content content, + final Context context, + @Nullable final StatusLine statusLine, + final X exception) throws X + { + String logMessage = buildLogContentMessage(content, statusLine); + String repositoryName = context.getRepository().getName(); + String contextUrl = getUrl(context); + + if (content != null) { + log.debug(logMessage, exception, repositoryName, contextUrl, statusLine); + } + else { + if (log.isDebugEnabled()) { + log.warn(logMessage, exception, repositoryName, contextUrl, statusLine, exception); + } + else { + log.warn(logMessage, exception, repositoryName, contextUrl, statusLine); + } + throw exception; + } + } + + @VisibleForTesting + String buildLogContentMessage(@Nullable final Content content, + @Nullable final StatusLine statusLine) + { + StringBuilder message = new StringBuilder("Exception {} checking remote for update"); + + if (statusLine == null) { + message.append(", proxy repo {} failed to fetch {}"); + } + else { + message.append(", proxy repo {} failed to fetch {} with status line {}"); + } + + if (content == null) { + message.append(", content not in cache."); + } + else { + message.append(", returning content from cache."); + } + + return message.toString(); + } + + @Override + public void invalidateProxyCaches() { + log.info("Invalidating proxy caches of {}", getRepository().getName()); + cacheControllerHolder.invalidateCaches(); + } + + private Content maybeGetCachedContent(Context context) throws IOException { + try { + return getCachedContent(context); + } + catch (RetryDeniedException e) { + if (e.getCause() instanceof MissingBlobException) { + log.warn("Unable to find blob {} for {}, will check remote", ((MissingBlobException) e.getCause()).getBlobRef(), + getUrl(context)); + return null; + } + else { + throw e; + } + } + } + + /** + * If we have the content cached locally already, return that along with applicable cache controller - otherwise + * {@code null}. + */ + @Nullable + protected abstract Content getCachedContent(final Context context) throws IOException; + + /** + * Store a new Payload, freshly fetched from the remote URL. + * + * The Context indicates which component was being requested. + * + * @throws IOException + * @throws InvalidContentException + */ + protected abstract Content store(final Context context, final Content content) throws IOException; + + @Nullable + protected Content fetch(final Context context, Content stale) throws IOException { + return fetch(getUrl(context), context, stale); + } + + protected Content fetch(String url, Context context, @Nullable Content stale) throws IOException { + HttpClient client = httpClient.getHttpClient(); + + checkState(config.remoteUrl.isAbsolute(), + "Invalid remote URL '%s' for proxy repository %s, please fix your configuration", config.remoteUrl, + getRepository().getName()); + URI uri; + try { + uri = config.remoteUrl.resolve(url); + } + catch (IllegalArgumentException e) { // NOSONAR + log.warn("Unable to resolve url. Reason: {}", e.getMessage()); + throw new BadRequestException("Invalid repository path"); + } + HttpRequestBase request = buildFetchHttpRequest(uri, context); + if (stale != null) { + final DateTime lastModified = stale.getAttributes().get(Content.CONTENT_LAST_MODIFIED, DateTime.class); + if (lastModified != null) { + request.addHeader(HttpHeaders.IF_MODIFIED_SINCE, DateUtils.formatDate(lastModified.toDate())); + } + final String etag = stale.getAttributes().get(Content.CONTENT_ETAG, String.class); + if (etag != null) { + request.addHeader(HttpHeaders.IF_NONE_MATCH, "\"" + etag + "\""); + } + } + log.debug("Fetching: {}", request); + + HttpResponse response = execute(context, client, request); + log.debug("Response: {}", response); + + StatusLine status = response.getStatusLine(); + log.debug("Status: {}", status); + + final CacheInfo cacheInfo = getCacheController(context).current(); + + if (status.getStatusCode() == HttpStatus.SC_OK) { + HttpEntity entity = response.getEntity(); + log.debug("Entity: {}", entity); + + final Content result = createContent(context, response); + result.getAttributes().set(Content.CONTENT_LAST_MODIFIED, extractLastModified(request, response)); + result.getAttributes().set(Content.CONTENT_ETAG, extractETag(response)); + result.getAttributes().set(CacheInfo.class, cacheInfo); + return result; + } + + try { + if (status.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { + checkState(stale != null, "Received 304 without conditional GET (bad server?) from %s", uri); + indicateVerified(context, stale, cacheInfo); + } + mayThrowProxyServiceException(response); + } + finally { + HttpClientUtils.closeQuietly(response); + } + + return null; + } + + /** + * Create {@link Content} out of HTTP response. + */ + protected Content createContent(final Context context, final HttpResponse response) + { + return new Content(new HttpEntityPayload(response, response.getEntity())); + } + + /** + * May throw {@link ProxyServiceException} based on response statuses. + */ + private void mayThrowProxyServiceException(final HttpResponse httpResponse) { + final StatusLine status = httpResponse.getStatusLine(); + if (HttpStatus.SC_UNAUTHORIZED == status.getStatusCode() + || HttpStatus.SC_PAYMENT_REQUIRED == status.getStatusCode() + || HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED == status.getStatusCode() + || HttpStatus.SC_INTERNAL_SERVER_ERROR <= status.getStatusCode()) { + throw new ProxyServiceException(httpResponse); + } + } + + /** + * Execute http client request. + */ + protected HttpResponse execute(final Context context, final HttpClient client, final HttpRequestBase request) + throws IOException + { + return client.execute(request); + } + + /** + * Builds the {@link HttpRequestBase} for a particular set of parameters (mapping to GET by default). + */ + protected HttpRequestBase buildFetchHttpRequest(URI uri, Context context) { + return new HttpGet(uri); + } + + /** + * Extract Last-Modified date from response if possible, or {@code null}. + */ + @Nullable + private DateTime extractLastModified(final HttpRequestBase request, final HttpResponse response) { + final Header lastModifiedHeader = response.getLastHeader(HttpHeaders.LAST_MODIFIED); + if (lastModifiedHeader != null) { + try { + return new DateTime(DateUtils.parseDate(lastModifiedHeader.getValue()).getTime()); + } + catch (Exception ex) { + log.warn("Could not parse date '{}' received from {}; using system current time as item creation time", + lastModifiedHeader, request.getURI()); + } + } + return null; + } + + /** + * Extract ETag from response if possible, or {@code null}. + */ + @Nullable + private String extractETag(final HttpResponse response) { + final Header etagHeader = response.getLastHeader(HttpHeaders.ETAG); + if (etagHeader != null) { + final String etag = etagHeader.getValue(); + if (!Strings.isNullOrEmpty(etag)) { + if (etag.startsWith("\"") && etag.endsWith("\"")) { + return etag.substring(1, etag.length() - 1); + } + else { + return etag; + } + } + } + return null; + } + + /** + * For whatever component/asset + */ + protected abstract void indicateVerified(final Context context, final Content content, final CacheInfo cacheInfo) + throws IOException; + + /** + * Provide the URL of the content relative to the repository root. + */ + protected abstract String getUrl(@Nonnull final Context context); + + + /** + * Get the appropriate cache controller for the type of content being requested. Must never return {@code null}. + */ + @Nonnull + protected CacheController getCacheController(@Nonnull final Context context) { + return cacheControllerHolder.getContentCacheController(); + } + + private boolean isStale(final Context context, final Content content) { + if (content == null) { + // not in cache, consider it stale + return true; + } + final CacheInfo cacheInfo = content.getAttributes().get(CacheInfo.class); + return cacheInfo == null || getCacheController(context).isStale(cacheInfo); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..ae42645213b5f64014391506d2c13add2475334f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyHandler.java @@ -0,0 +1,89 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.proxy; + +import java.io.IOException; +import java.io.UncheckedIOException; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.http.HttpResponses; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Payload; +import org.sonatype.nexus.repository.view.Response; + +import static org.sonatype.nexus.repository.http.HttpMethods.GET; +import static org.sonatype.nexus.repository.http.HttpMethods.HEAD; + +/** + * A format-neutral proxy handler which delegates to an instance of {@link ProxyFacet} for content. + * + * @since 3.0 + */ +public class ProxyHandler + extends ComponentSupport + implements Handler +{ + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { // NOSONAR + final Response response = buildMethodNotAllowedResponse(context); + if (response != null) { + return response; + } + + try { + Payload payload = proxyFacet(context).get(context); + if (payload != null) { + return buildPayloadResponse(context, payload); + } + return buildNotFoundResponse(context); + } + catch (ProxyServiceException e) { + return HttpResponses.serviceUnavailable(); + } + catch (CooperationException e) { // NOSONAR + return HttpResponses.serviceUnavailable(e.getMessage()); + } + catch (IOException | UncheckedIOException e) { + return HttpResponses.badGateway(); + } + } + + /** + * Builds a not-allowed response if the specified method is unsupported under the specified context, null otherwise. + */ + @Nullable + protected Response buildMethodNotAllowedResponse(final Context context) { + final String action = context.getRequest().getAction(); + if (!GET.equals(action) && !HEAD.equals(action)) { + return HttpResponses.methodNotAllowed(action, GET, HEAD); + } + return null; + } + + protected Response buildPayloadResponse(final Context context, final Payload payload) { + return HttpResponses.ok(payload); + } + + protected Response buildNotFoundResponse(final Context context) { + return HttpResponses.notFound(); + } + + private ProxyFacet proxyFacet(final Context context) { + return context.getRepository().facet(ProxyFacet.class); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyServiceException.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyServiceException.java new file mode 100644 index 0000000000000000000000000000000000000000..592058adf31c16a9bde55b4f8596161a89350fd6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyServiceException.java @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.proxy; + +import javax.annotation.Nonnull; + +import org.apache.http.HttpResponse; + +/** + * A format-neutral proxy service exception thrown in cases like proxy with misconfiguration or remote down. + * + * @since 3.0 + */ +public class ProxyServiceException + extends RuntimeException +{ + private final HttpResponse httpResponse; + + public ProxyServiceException(final HttpResponse httpResponse) { + super(httpResponse.getStatusLine().toString()); + this.httpResponse = httpResponse; + } + + /** + * Returns the {@link HttpResponse} but with a consumed entity, to be able to inspect response status and + * headers, if needed. + */ + @Nonnull + public HttpResponse getHttpResponse() { + return httpResponse; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/TempContent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/TempContent.java new file mode 100644 index 0000000000000000000000000000000000000000..7d7056f9acd93776953bbed2a8714460e4b40994 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/TempContent.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.proxy; + +import java.io.IOException; +import java.io.InputStream; + +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.repository.view.Payload; +import org.sonatype.nexus.repository.view.payloads.BytesPayload; + +import static com.google.common.io.ByteStreams.toByteArray; + +/** + * Temporary reusable {@link Content}; caches the original remote content in-memory. + * + * @since 3.4 + */ +class TempContent + extends Content +{ + public TempContent(final Content remote) throws IOException { + super(cachePayload(remote), remote.getAttributes()); + } + + private static Payload cachePayload(final Content remote) throws IOException { + try (InputStream in = remote.openInputStream()) { + return new BytesPayload(toByteArray(in), remote.getContentType()); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..6f7e75d8ea971f656f79998b7ff7c446f1234ad7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Repository proxy-facet and support. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.proxy; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedFacet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedFacet.java new file mode 100644 index 0000000000000000000000000000000000000000..50561c633bcbb89094c7e5571f58a1f2651b7472 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedFacet.java @@ -0,0 +1,32 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.purge; + +import org.sonatype.nexus.repository.Facet; + +/** + * Find & delete components and assets that were not used/accessed for a number of days. + * + * @since 3.0 + */ +@Facet.Exposed +public interface PurgeUnusedFacet + extends Facet +{ + /** + * Find & delete components and assets that were not used/accessed for a number of days. + * + * @param numberOfDays number of days from the moment the method is invoked. Must be > 0. + */ + void purgeUnused(int numberOfDays); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedFacetImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedFacetImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..39411b260cb6bd0ec6d0d722ea724a87bd333ee2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedFacetImpl.java @@ -0,0 +1,142 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.purge; + +import java.util.Date; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.orient.entity.AttachedEntityHelper; +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.repository.storage.StorageTx; +import org.sonatype.nexus.repository.transaction.TransactionalDeleteBlob; +import org.sonatype.nexus.transaction.UnitOfWork; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.FacetSupport.State.STARTED; +import static org.sonatype.nexus.repository.storage.AssetEntityAdapter.P_COMPONENT; +import static org.sonatype.nexus.repository.storage.AssetEntityAdapter.P_LAST_DOWNLOADED; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_BUCKET; +import static org.sonatype.nexus.scheduling.CancelableHelper.checkCancellation; + +/** + * {@link PurgeUnusedFacet} implementation. + * + * @since 3.0 + */ +@Named +public class PurgeUnusedFacetImpl + extends FacetSupport + implements PurgeUnusedFacet +{ + private final ComponentEntityAdapter componentEntityAdapter; + + @Inject + public PurgeUnusedFacetImpl(final ComponentEntityAdapter componentEntityAdapter) + { + this.componentEntityAdapter = checkNotNull(componentEntityAdapter); + } + + @Override + @Guarded(by = STARTED) + public void purgeUnused(final int numberOfDays) { + checkArgument(numberOfDays > 0, "Number of days must be greater then zero"); + log.info("Purging unused components from repository {}", getRepository().getName()); + + Date olderThan = DateTime.now().minusDays(numberOfDays).withTimeAtStartOfDay().toDate(); + + UnitOfWork.beginBatch(facet(StorageFacet.class).txSupplier()); + try { + deleteUnusedComponents(olderThan); + deleteUnusedAssets(olderThan); + } + finally { + UnitOfWork.end(); + } + } + + /** + * Delete all unused components. + */ + @TransactionalDeleteBlob + protected void deleteUnusedComponents(final Date olderThan) { + StorageTx tx = UnitOfWork.currentTx(); + + for (Component component : findUnusedComponents(tx, olderThan)) { + checkCancellation(); + log.debug("Deleting unused component {}", component); + tx.deleteComponent(component); // TODO: commit in batches + } + } + + /** + * Delete all unused assets. + */ + @TransactionalDeleteBlob + protected void deleteUnusedAssets(final Date olderThan) { + StorageTx tx = UnitOfWork.currentTx(); + + for (Asset asset : findUnusedAssets(tx, olderThan)) { + checkCancellation(); + log.debug("Deleting unused asset {}", asset); + tx.deleteAsset(asset); // TODO: commit in batches + } + } + + /** + * Find all components that were last accessed before specified date. Date when a component was last accessed is the + * last time an asset of that component was last accessed. + */ + private Iterable findUnusedComponents(final StorageTx tx, final Date olderThan) { + final Bucket bucket = tx.findBucket(getRepository()); + + String sql = String.format( + "SELECT FROM (SELECT %s, MAX(%s) AS lastDownloaded FROM asset WHERE %s=:bucket AND %s IS NOT NULL GROUP BY %s) WHERE lastDownloaded < :olderThan", + P_COMPONENT, P_LAST_DOWNLOADED, P_BUCKET, P_COMPONENT, P_COMPONENT + ); + + Map sqlParams = ImmutableMap.of( + "bucket", AttachedEntityHelper.id(bucket), + "olderThan", olderThan + ); + + checkCancellation(); + return Iterables.transform(tx.browse(sql, sqlParams), + (doc) -> componentEntityAdapter.readEntity(doc.field(P_COMPONENT))); + } + + /** + * Find all assets without component that were last accessed before specified date. + */ + private Iterable findUnusedAssets(final StorageTx tx, final Date olderThan) { + String whereClause = String.format("%s IS NULL AND %s < :olderThan", P_COMPONENT, P_LAST_DOWNLOADED); + Map sqlParams = ImmutableMap.of("olderThan", olderThan); + + checkCancellation(); + return tx.findAssets(whereClause, sqlParams, ImmutableList.of(getRepository()), null); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedTask.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedTask.java new file mode 100644 index 0000000000000000000000000000000000000000..53249d6cd3fd6cc7c6f668d893fc1a894f7b6691 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedTask.java @@ -0,0 +1,47 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.purge; + +import javax.inject.Named; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryTaskSupport; +import org.sonatype.nexus.scheduling.Cancelable; + +/** + * Task to purge unused components/assets of given repository. + * + * @since 3.0 + */ +@Named +public class PurgeUnusedTask + extends RepositoryTaskSupport + implements Cancelable +{ + public static final String LAST_USED_FIELD_ID = "lastUsed"; + + @Override + protected void execute(final Repository repository) { + repository.facet(PurgeUnusedFacet.class).purgeUnused(getConfiguration().getInteger(LAST_USED_FIELD_ID, -1)); + } + + @Override + protected boolean appliesTo(final Repository repository) { + return repository.optionalFacet(PurgeUnusedFacet.class).isPresent(); + } + + @Override + public String getMessage() { + return "Purge unused components and assets from " + getRepositoryField(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedTaskDescriptor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedTaskDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..4f22e7dbf027b9eaa3e2447bf9b56bf39a3b1e0f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/purge/PurgeUnusedTaskDescriptor.java @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.purge; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.formfields.FormField; +import org.sonatype.nexus.formfields.NumberTextFormField; +import org.sonatype.nexus.formfields.RepositoryCombobox; +import org.sonatype.nexus.scheduling.TaskDescriptorSupport; + +import static org.sonatype.nexus.repository.purge.PurgeUnusedTask.LAST_USED_FIELD_ID; +import static org.sonatype.nexus.repository.purge.PurgeUnusedTask.REPOSITORY_NAME_FIELD_ID; + +/** + * Task descriptor for {@link PurgeUnusedTask}. + * + * @since 3.0 + */ +@Named +@Singleton +public class PurgeUnusedTaskDescriptor + extends TaskDescriptorSupport +{ + public static final String TASK_NAME = "Purge unused components and assets"; + + public static final String TYPE_ID = "repository.purge-unused"; + + public static final Number LAST_USED_INIT_VALUE = 1; + + public static final Number LAST_USED_MIN_VALUE = 1; + + public PurgeUnusedTaskDescriptor() { + super(TYPE_ID, + PurgeUnusedTask.class, + TASK_NAME, + VISIBLE, + EXPOSED, + new RepositoryCombobox( + REPOSITORY_NAME_FIELD_ID, + "Repository", + "Select the repository to purge components/assets from", + FormField.MANDATORY + ).includingAnyOfFacets(PurgeUnusedFacet.class).includeAnEntryForAllRepositories(), + new NumberTextFormField( + LAST_USED_FIELD_ID, + "Last used in days", + "Purge all components and assets that were last used before given number of days", + FormField.MANDATORY + ).withInitialValue(LAST_USED_INIT_VALUE).withMinimumValue(LAST_USED_MIN_VALUE) + ); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/ComponentsResourceExtension.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/ComponentsResourceExtension.java new file mode 100644 index 0000000000000000000000000000000000000000..06f969b0f9407003fc1a8834e45e8f10da092ed5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/ComponentsResourceExtension.java @@ -0,0 +1,30 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest; + +import org.sonatype.nexus.repository.rest.api.ComponentXO; +import org.sonatype.nexus.repository.rest.internal.resources.ComponentsResource; +import org.sonatype.nexus.repository.storage.Component; + +/** + * Extension point for the {@link ComponentsResource} class + * + * @since 3.8 + */ +public interface ComponentsResourceExtension +{ + /** + * Update the {@link ComponentXO} with the data in the {@link Component} + */ + ComponentXO updateComponentXO(ComponentXO componentXO, Component component); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchMapping.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchMapping.java new file mode 100644 index 0000000000000000000000000000000000000000..6832d028cf13074dffcd71dbc3d92bd080be2256 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchMapping.java @@ -0,0 +1,85 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Describes the mapping of an Elasticsearch attribute to an alias and also provides a description of the + * attribute. + * + * @since 3.7 + */ +public class SearchMapping +{ + private final String attribute; + + private final String alias; + + private final String description; + + public SearchMapping(final String alias, final String attribute, final String description) { + this.alias = checkNotNull(alias); + this.attribute = checkNotNull(attribute); + this.description = checkNotNull(description); + } + + public String getAttribute() { + return attribute; + } + + public String getAlias() { + return alias; + } + + public String getDescription() { + return description; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + SearchMapping that = (SearchMapping) o; + + if (!attribute.equals(that.attribute)) { + return false; + } + if (!alias.equals(that.alias)) { + return false; + } + return description.equals(that.description); + } + + @Override + public int hashCode() { + int result = attribute.hashCode(); + result = 31 * result + alias.hashCode(); + result = 31 * result + description.hashCode(); + return result; + } + + @Override + public String toString() { + return "SearchMapping{" + + "attribute='" + attribute + '\'' + + ", alias='" + alias + '\'' + + ", description='" + description + '\'' + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchMappings.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchMappings.java new file mode 100644 index 0000000000000000000000000000000000000000..839463fb953503ec1a3d28f4e05200b8a78114e5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchMappings.java @@ -0,0 +1,23 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest; + +/** + * Provide a set of {@link SearchMapping}s. + * + * @since 3.7 + */ +public interface SearchMappings +{ + Iterable get(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchMappingsService.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchMappingsService.java new file mode 100644 index 0000000000000000000000000000000000000000..a62453ef5b44deff06d545f8ad3a42f79955f579 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchMappingsService.java @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest; + +/** + * Provide a listing of all {@link SearchMapping}s that have been contributed via {@link SearchMappings}. + * + * @since 3.7 + */ +public interface SearchMappingsService +{ + /** + * Get all {@link SearchMapping}s. + */ + Iterable getAllMappings(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchResourceExtension.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchResourceExtension.java new file mode 100644 index 0000000000000000000000000000000000000000..89a0dae9f231ecc4d530d212e0b44fbd3a851fc7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchResourceExtension.java @@ -0,0 +1,31 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest; + +import org.sonatype.nexus.repository.rest.api.ComponentXO; +import org.sonatype.nexus.repository.rest.internal.resources.SearchResource; + +import org.elasticsearch.search.SearchHit; + +/** + * Extension point for the {@link SearchResource} class + * + * @since 3.8 + */ +public interface SearchResourceExtension +{ + /** + * Update the {@link ComponentXO} with data from the {@link SearchHit} + */ + ComponentXO updateComponentXO(ComponentXO componentXO, SearchHit hit); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchUtils.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..24c1e67e0bd08933f4f38d98d2d328647649f4df --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/SearchUtils.java @@ -0,0 +1,143 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest; + +import java.util.Map; +import java.util.Map.Entry; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriInfo; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.rest.internal.resources.RepositoryManagerRESTAdapter; +import org.sonatype.nexus.repository.group.GroupFacet; +import org.sonatype.nexus.repository.types.GroupType; + +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; + +import static java.util.stream.Collectors.toMap; +import static java.util.stream.StreamSupport.stream; +import static jline.internal.Preconditions.checkNotNull; +import static org.elasticsearch.index.query.QueryBuilders.boolQuery; +import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; +import static org.elasticsearch.index.query.QueryBuilders.termQuery; + +/** + * @since 3.7 + */ +@Named +@Singleton +public class SearchUtils + extends ComponentSupport +{ + private static final String Q = "q"; + + private static final String CONTINUATION_TOKEN = "continuationToken"; + + private static final String ASSET_PREFIX = "assets."; + + private final RepositoryManagerRESTAdapter repoAdapter; + + private final Map searchParams; + + private final Map assetSearchParams; + + @Inject + public SearchUtils(final RepositoryManagerRESTAdapter repoAdapter, + final Map searchMappings) + { + this.repoAdapter = checkNotNull(repoAdapter); + this.searchParams = checkNotNull(searchMappings).entrySet().stream() + .flatMap(e -> stream(e.getValue().get().spliterator(), true)) + .collect(toMap(SearchMapping::getAlias, SearchMapping::getAttribute)); + this.assetSearchParams = searchParams.entrySet().stream() + .filter(e -> e.getValue().startsWith(ASSET_PREFIX)) + .collect(toMap(Entry::getKey, Entry::getValue)); + } + + public Map getSearchParameters() { + return searchParams; + } + + public Map getAssetSearchParameters() { + return assetSearchParams; + } + + public Repository getRepository(final String repository) { + return repoAdapter.getRepository(repository); + } + + /** + * Builds a {@link QueryBuilder} based on configured search parameters. + * + * @param uriInfo {@link UriInfo} to extract query parameters from + */ + public QueryBuilder buildQuery(final UriInfo uriInfo) { // NOSONAR + BoolQueryBuilder query = boolQuery(); + + MultivaluedMap queryParams = uriInfo.getQueryParameters(); + + if (queryParams.containsKey(Q)) { + query.must(queryStringQuery(queryParams.getFirst(Q))); + } + + queryParams.forEach((key, value) -> { + if (value.isEmpty() || value.get(0).isEmpty()) { + // no value sent + return; + } + if (Q.equals(key)) { + // skip the keyword search + return; + } + if (CONTINUATION_TOKEN.equals(key)) { + // skip the continuation token + return; + } + if ("repository".equals(key)) { + Repository repository = repoAdapter.getRepository(value.get(0)); + if (isGroup(repository)) { + repository.facet(GroupFacet.class).leafMembers().forEach(r -> + query.should(termQuery(searchParams.get(key), r.getName()))); + query.minimumNumberShouldMatch(1); + return; + } + } + query.filter(termQuery(searchParams.getOrDefault(key, key), value.get(0))); + }); + + log.debug("Query: {}", query); + return query; + } + + private boolean isGroup(final Repository repository) { + return GroupType.NAME.equals(repository.getType().getValue()); + } + + public boolean isAssetSearchParam(final String assetSearchParam) { + return assetSearchParams.containsKey(assetSearchParam) || isFullAssetAttributeName(assetSearchParam); + } + + public boolean isFullAssetAttributeName(final String assetSearchParam) { + return assetSearchParam.startsWith(ASSET_PREFIX); + } + + public String getFullAssetAttributeName(final String key) { + return isFullAssetAttributeName(key) ? key : getAssetSearchParameters().get(key); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/AssetXO.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/AssetXO.groovy new file mode 100644 index 0000000000000000000000000000000000000000..1c7b876a287c6b6b34023190d9e2a380b1fe87e4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/AssetXO.groovy @@ -0,0 +1,82 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.api + +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.rest.internal.api.RepositoryItemIDXO +import org.sonatype.nexus.repository.storage.Asset + +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import groovy.transform.builder.Builder + +import static org.sonatype.nexus.common.entity.EntityHelper.id +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.ID +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.NAME +import static org.sonatype.nexus.repository.storage.Asset.CHECKSUM +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES + +/** + * Asset transfer object for REST APIs. + * + * @since 3.3 + */ +@CompileStatic +@Builder +@ToString(includePackage = false, includeNames = true) +@EqualsAndHashCode(includes = ['id']) +class AssetXO +{ + String downloadUrl + + String path + + String id + + String repository + + String format + + Map checksum + + static AssetXO fromAsset(final Asset asset, final Repository repository) { + String internalId = id(asset).getValue() + + Map checksum = asset.attributes().child(CHECKSUM).backing() + + return builder() + .path(asset.name()) + .downloadUrl(repository.url + '/' + asset.name()) + .id(new RepositoryItemIDXO(repository.name, internalId).value) + .repository(repository.name) + .checksum(checksum) + .format(repository.format.value) + .build() + } + + static AssetXO fromElasticSearchMap(final Map map, final Repository repository) { + String internalId = (String) map.get(ID) + + Map checksum = (Map) map.get(P_ATTRIBUTES, [:])[CHECKSUM] + + return builder() + .path((String) map.get(NAME)) + .downloadUrl(repository.url + '/' + (String) map.get(NAME)) + .id(new RepositoryItemIDXO(repository.name, internalId).value) + .repository(repository.name) + .checksum(checksum) + .format(repository.format.value) + .build() + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXO.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXO.groovy new file mode 100644 index 0000000000000000000000000000000000000000..250910974b873c5497fc721c3cd4dbb47bb71f9f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXO.groovy @@ -0,0 +1,58 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.api + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonPropertyOrder + +/** + * @since 3.8 + */ +@JsonPropertyOrder(["id", "repository", "format", "group", "name", "version", "assets"]) +interface ComponentXO +{ + String getId() + + void setId(String id) + + String getGroup() + + void setGroup(String group) + + String getName() + + void setName(String name) + + String getVersion() + + void setVersion(String version) + + String getRepository() + + void setRepository(String repository) + + String getFormat() + + void setFormat(String format) + + List getAssets() + + void setAssets(List assets) + + /** + * Attributes to add the JSON payload + * @return + */ + @JsonAnyGetter + Map getExtraJsonAttributes() +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXODecorator.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXODecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..08cdcb88522653502c47c49bb70794726f0f27a4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXODecorator.java @@ -0,0 +1,23 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.api; + +/** + * Decorator interface for the {@link ComponentXO} class + * + * @since 3.8 + */ +public interface ComponentXODecorator +{ + ComponentXO decorate(ComponentXO componentXO); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXODeserializer.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXODeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..31e7bd9a71ac009b12f7d5795c7ae94e29c3db57 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXODeserializer.java @@ -0,0 +1,66 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.api; + +import java.io.IOException; +import java.util.Set; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Custom {@link JsonDeserializer} for the {@link ComponentXO} to handle the decorator approach + * + * @since 3.8 + */ +public class ComponentXODeserializer + extends JsonDeserializer +{ + private final ComponentXOFactory componentXOFactory; + + private final ObjectMapper objectMapper; + + private final Set componentXODeserializerExtensions; + + public ComponentXODeserializer(final ComponentXOFactory componentXOFactory, + final ObjectMapper objectMapper, + final Set componentXODeserializerExtensions) + { + this.componentXOFactory = checkNotNull(componentXOFactory); + this.objectMapper = checkNotNull(objectMapper); + this.componentXODeserializerExtensions = checkNotNull(componentXODeserializerExtensions); + } + + @Override + public ComponentXO deserialize(final JsonParser jsonParser, final DeserializationContext ctxt) throws IOException + { + JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser); + + ComponentXO componentXO = componentXOFactory.createComponentXO(); + + // update the fresh ComponentXO with its own properties + objectMapper.readerForUpdating(componentXO).readValue(jsonNode); + + // allow each extension to update its properties + for (ComponentXODeserializerExtension componentXODeserializerExtension : componentXODeserializerExtensions) { + componentXO = componentXODeserializerExtension.updateComponentXO(componentXO, jsonNode); + } + + return componentXO; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXODeserializerExtension.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXODeserializerExtension.java new file mode 100644 index 0000000000000000000000000000000000000000..cfe440b68b2263f6ccf0c93d8456436268b01246 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXODeserializerExtension.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.api; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Extension point for the {@link ComponentXODeserializer} + * + * @since 3.8 + */ +public interface ComponentXODeserializerExtension +{ + /** + * Update the given {@link ComponentXO} with the data in the {@link JsonNode} + */ + ComponentXO updateComponentXO(final ComponentXO componentXO, final JsonNode jsonNode); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXOFactory.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXOFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3a2e382f1572d952c911a3759aa895c37c8af034 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/ComponentXOFactory.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.api; + +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Factory for creating {@link ComponentXO} instances. + * + * @since 3.8 + */ +@Singleton +@Named +public class ComponentXOFactory +{ + private final Set componentXODecorators; + + @Inject + public ComponentXOFactory(final Set componentXODecorators) { + this.componentXODecorators = checkNotNull(componentXODecorators); + } + + public ComponentXO createComponentXO() { + ComponentXO componentXO = new DefaultComponentXO(); + for (ComponentXODecorator componentXODecorator : componentXODecorators) { + componentXO = componentXODecorator.decorate(componentXO); + } + return componentXO; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/DecoratedComponentXO.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/DecoratedComponentXO.groovy new file mode 100644 index 0000000000000000000000000000000000000000..0b3d3f8ac2fd24e892030768ce0c0cfc58a5450c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/DecoratedComponentXO.groovy @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.api + +import org.sonatype.nexus.common.decorator.DecoratedObject + +import com.fasterxml.jackson.annotation.JsonIgnore +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import groovy.transform.builder.Builder + +/** + * Base abstract decorator for the {@link ComponentXO} class + * + * @since 3.8 + */ +@CompileStatic +@Builder +@ToString(includePackage = false, includeNames = true) +@EqualsAndHashCode(includes = ['id']) +abstract class DecoratedComponentXO + implements ComponentXO, DecoratedObject +{ + @Delegate + protected final ComponentXO componentXO + + DecoratedComponentXO(ComponentXO componentXO) { + this.componentXO = componentXO + } + + @Override + @JsonIgnore + ComponentXO getWrappedObject() { + return componentXO + } + + /** + * Get the additional attributes from this decorated object to add to the json payload. Variables in the extending + * class should have the @JsonIgnore annotation and expose the data via this method. + * @return + */ + @JsonIgnore + abstract Map getDecoratedExtraJsonAttributes() + + @Override + final Map getExtraJsonAttributes() { + return componentXO.getExtraJsonAttributes() + getDecoratedExtraJsonAttributes() + } +} diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/RepositoryXO.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/DefaultComponentXO.groovy similarity index 69% rename from components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/RepositoryXO.groovy rename to thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/DefaultComponentXO.groovy index 3fc95d49835ff8f17011282a8f5e4f8949a74fc4..24953267b89c89ac93fb13b3d27693fb1f44c5f0 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/RepositoryXO.groovy +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/api/DefaultComponentXO.groovy @@ -12,38 +12,45 @@ */ package org.sonatype.nexus.repository.rest.api -import org.sonatype.nexus.repository.Repository - import groovy.transform.CompileStatic import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import groovy.transform.builder.Builder +import static java.util.Collections.emptyMap + /** - * Repository transfer object for REST APIs. + * Component transfer object for REST APIs. * - * @since 3.next + * @since 3.8 */ @CompileStatic @Builder @ToString(includePackage = false, includeNames = true) -@EqualsAndHashCode -class RepositoryXO +@EqualsAndHashCode(includes = ['id']) +class DefaultComponentXO + implements ComponentXO { + String id + + String group + String name - String format + String version + + String repository - String type + String format - String url + List assets - static RepositoryXO fromRepository(final Repository repository) { - return builder() - .name(repository.getName()) - .format(repository.getFormat().getValue()) - .type(repository.getType().getValue()) - .url(repository.getUrl()) - .build() + /** + * Provides extra attributes for the JSON payload. Implementers must use @JsonAnyGetter. + * @since 3.8 + */ + @Override + Map getExtraJsonAttributes() { + return emptyMap() } } diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/ComponentUploadParameterContributor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/ComponentUploadParameterContributor.java new file mode 100644 index 0000000000000000000000000000000000000000..4ce193fba5752084a5fe9ae7468c877070c52c12 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/ComponentUploadParameterContributor.java @@ -0,0 +1,76 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.rest.internal.resources.ComponentsResource; +import org.sonatype.nexus.repository.upload.UploadDefinition; +import org.sonatype.nexus.repository.upload.UploadManager; +import org.sonatype.nexus.swagger.ParameterContributor; + +import com.google.common.collect.ImmutableList; +import io.swagger.models.parameters.FormParameter; + +import static io.swagger.models.HttpMethod.POST; + +/** + * @since 3.8 + */ +@Named +@Singleton +public class ComponentUploadParameterContributor + extends ParameterContributor +{ + private static final List PATHS = ImmutableList.of(ComponentsResource.RESOURCE_URI); + + @Inject + public ComponentUploadParameterContributor(final UploadManager uploadManager) { + super(POST, PATHS, transformUploadDefinitions(uploadManager.getAvailableDefinitions())); + } + + private static Collection transformUploadDefinitions(final Collection uploadDefinitions) { + Collection parameters = new ArrayList<>(); + + for (UploadDefinition uploadDefinition : uploadDefinitions) { + uploadDefinition.getComponentFields().forEach(uploadFieldDefinition -> parameters.add(new FormParameter() + .name(uploadDefinition.getFormat() + "." + uploadFieldDefinition.getName()) + .type(uploadFieldDefinition.getType().name().toLowerCase()) + .description(uploadDefinition.getFormat() + " " + uploadFieldDefinition.getDisplayName()))); + + for (int i = 1; i <= (uploadDefinition.isMultipleUpload() ? 3 : 1); i++) { + String assetIndex = uploadDefinition.isMultipleUpload() ? Integer.toString(i) : ""; + String assetName = uploadDefinition.getFormat() + ".asset" + assetIndex; + String assetDisplayName = uploadDefinition.getFormat() + " Asset " + assetIndex; + + parameters.add(new FormParameter() + .name(assetName) + .type("file") + .description(assetDisplayName)); + + uploadDefinition.getAssetFields().forEach(uploadFieldDefinition -> parameters.add(new FormParameter() + .name(assetName + "." + uploadFieldDefinition.getName()) + .type(uploadFieldDefinition.getType().name().toLowerCase()) + .description(assetDisplayName + " " + uploadFieldDefinition.getDisplayName()))); + } + } + + return parameters; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/DefaultSearchMappings.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/DefaultSearchMappings.java new file mode 100644 index 0000000000000000000000000000000000000000..b001f2f9a3ac321f4180f4f80a1a115ced685c23 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/DefaultSearchMappings.java @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal; + +import java.util.List; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.rest.SearchMapping; +import org.sonatype.nexus.repository.rest.SearchMappings; + +import com.google.common.collect.ImmutableList; + +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.REPOSITORY_NAME; + +/** + * @since 3.7 + */ +@Named("default") +@Singleton +public class DefaultSearchMappings + extends ComponentSupport + implements SearchMappings +{ + private static final List MAPPINGS = ImmutableList.of( + new SearchMapping("q", "q", "Query by keyword"), + new SearchMapping("repository", REPOSITORY_NAME, "Repository name"), + new SearchMapping("format", "format", "Query by format"), + new SearchMapping("group", "group.raw", "Component group"), + new SearchMapping("name", "name.raw", "Component name"), + new SearchMapping("version", "version", "Component version"), + new SearchMapping("md5", "assets.attributes.checksum.md5", "Specific MD5 hash of component's asset"), + new SearchMapping("sha1", "assets.attributes.checksum.sha1", "Specific SHA-1 hash of component's asset"), + new SearchMapping("sha256", "assets.attributes.checksum.sha256", "Specific SHA-256 hash of component's asset"), + new SearchMapping("sha512", "assets.attributes.checksum.sha512", "Specific SHA-512 hash of component's asset") + ); + + @Override + public Iterable get() { + return MAPPINGS; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/SearchMappingsServiceImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/SearchMappingsServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..9062d8bf45d178572a42f9848289248b3776a9b1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/SearchMappingsServiceImpl.java @@ -0,0 +1,69 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal; + +import java.util.Collection; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.rest.SearchMapping; +import org.sonatype.nexus.repository.rest.SearchMappings; +import org.sonatype.nexus.repository.rest.SearchMappingsService; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; + +import static com.google.common.base.Preconditions.checkNotNull; + +@Named +@Singleton +public class SearchMappingsServiceImpl + extends ComponentSupport + implements SearchMappingsService +{ + private static final String DEFAULT = "default"; + + private final Collection searchMappings; + + @Inject + public SearchMappingsServiceImpl(final Map searchMappings) { + this.searchMappings = collectMappings(checkNotNull(searchMappings)); + } + + private static Collection collectMappings(final Map searchMappings) { + final Builder builder = ImmutableList.builder(); + + // put the default mappings in first + final SearchMappings defaultMappings = searchMappings.get(DEFAULT); + if (defaultMappings != null) { + builder.addAll(defaultMappings.get()); + } + + // add the rest of the mappings + searchMappings.keySet().stream() + .filter(key -> !DEFAULT.equals(key)) + .sorted() + .forEach(key -> builder.addAll(searchMappings.get(key).get())); + + return builder.build(); + } + + @Override + public Iterable getAllMappings() { + return searchMappings; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/SearchParameterContributor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/SearchParameterContributor.java new file mode 100644 index 0000000000000000000000000000000000000000..019947e11dc3366159b739ef801cafe3d0219443 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/SearchParameterContributor.java @@ -0,0 +1,58 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal; + +import java.util.Collection; +import java.util.List; +import java.util.stream.StreamSupport; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.rest.SearchMapping; +import org.sonatype.nexus.repository.rest.SearchMappingsService; +import org.sonatype.nexus.repository.rest.internal.resources.SearchResource; +import org.sonatype.nexus.swagger.ParameterContributor; + +import com.google.common.collect.ImmutableList; +import io.swagger.models.parameters.QueryParameter; + +import static io.swagger.models.HttpMethod.GET; +import static java.util.stream.Collectors.toList; + +/** + * @since 3.7 + */ +@Named +@Singleton +public class SearchParameterContributor + extends ParameterContributor +{ + private static final List PATHS = ImmutableList.of( + SearchResource.RESOURCE_URI, + SearchResource.RESOURCE_URI + SearchResource.SEARCH_ASSET_URI, + SearchResource.RESOURCE_URI + SearchResource.SEARCH_AND_DOWNLOAD_URI + ); + + @Inject + public SearchParameterContributor(final SearchMappingsService searchMappings) { + super(GET, PATHS, transformMappings(searchMappings.getAllMappings())); + } + + private static Collection transformMappings(final Iterable searchMappings) { // NOSONAR + return StreamSupport.stream(searchMappings.spliterator(), false) + .map(m -> new QueryParameter().name(m.getAlias()).type("string").description(m.getDescription())) + .collect(toList()); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/api/RepositoryItemIDXO.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/api/RepositoryItemIDXO.groovy new file mode 100644 index 0000000000000000000000000000000000000000..8f080c2354abbe3a4b0aac34ca9150c29c614c23 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/api/RepositoryItemIDXO.groovy @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.api + +import javax.ws.rs.NotFoundException +import javax.ws.rs.WebApplicationException + +import org.sonatype.goodies.common.Loggers + +import org.slf4j.Logger + +import static com.google.common.base.Preconditions.checkNotNull +import static org.sonatype.nexus.repository.http.HttpStatus.UNPROCESSABLE_ENTITY + +/** + * An object to hold the id and repository id of an asset in the format repository-id:asset-id, encoded. + * @since 3.3 + */ +class RepositoryItemIDXO +{ + public static final Logger log = Loggers.getLogger(RepositoryItemIDXO.class) + + String repositoryId + + String id + + RepositoryItemIDXO(final String repositoryId, final String id) { + this.repositoryId = checkNotNull(repositoryId) + this.id = checkNotNull(id) + } + + public static RepositoryItemIDXO fromString(String encoded) { + try { + String decoded = new String(Base64.getUrlDecoder().decode(encoded)) + String[] parts = decoded.split(":") + if (parts.length != 2) { + throw new WebApplicationException("Unable to parse RepositoryItemIDXO " + encoded, UNPROCESSABLE_ENTITY) + } + return new RepositoryItemIDXO(parts[0], parts[1]) + } + catch (IllegalArgumentException e) { + log.debug("Unable to parse id: {}, returning 404.", encoded, e); + throw new NotFoundException("Unable to locate asset with id " + encoded); + } + } + + public String getValue() { + return new String(Base64.getUrlEncoder().withoutPadding().encode("$repositoryId:$id".bytes)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/AssetDownloadResponseProcessor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/AssetDownloadResponseProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..6a41bc73d9a2e62822441fce9f98110bafdd23d3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/AssetDownloadResponseProcessor.java @@ -0,0 +1,77 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import java.net.URI; +import java.util.List; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriBuilder; + +import org.sonatype.nexus.repository.rest.api.AssetXO; +import org.sonatype.nexus.rest.WebApplicationMessageException; + +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; +import static javax.ws.rs.core.Response.Status.NOT_FOUND; + +/** + * A helper class to produce an appropriate HTTP Response for asset download requests. + * + * @since 3.7 + */ +public class AssetDownloadResponseProcessor +{ + public static final String NO_SEARCH_RESULTS_FOUND = "Asset search returned no results"; + + public static final String SEARCH_RETURNED_MULTIPLE_ASSETS = "Search returned multiple assets, please refine search criteria to find a single asset"; + + private final List assetXOs; + + AssetDownloadResponseProcessor(final List assetXOs) { + this.assetXOs = assetXOs; + } + + /** + * Produces the appropriate http response based upon the assets list. Initial + * implementation is returning a BAD_REQUEST when collection has more than one asset + * as there is no means for determining the appropriate asset in the absence of a + * selection strategy (future work). + * @return response the appropriate {@link Response} based on the input asset list + */ + Response process() { + if (assetXOs.isEmpty()) { + throw new WebApplicationMessageException(NOT_FOUND, NO_SEARCH_RESULTS_FOUND); + } + + if (assetXOs.size() > 1) { + throw new WebApplicationMessageException(BAD_REQUEST, SEARCH_RETURNED_MULTIPLE_ASSETS); + } + + //Case when only a single assetXO is present + return getResponse(assetXOs.get(0)); + } + + /** + * Builds the response with the provided assetXO object + * @param assetXO the {@link AssetXO} to be downloaded + * @return response the redirect {@link Response} containing the download url + */ + private Response getResponse(final AssetXO assetXO) { + String redirectUrl = assetXO.getDownloadUrl(); + URI uri = UriBuilder.fromPath(redirectUrl).build(); + ResponseBuilder builder = Response.status(Status.FOUND).location(uri); + return builder.build(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/AssetMapUtils.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/AssetMapUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..43f4d115acd8b6f12a4e7bc194e160e8bcff2681 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/AssetMapUtils.java @@ -0,0 +1,184 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.ws.rs.core.MultivaluedMap; + +import org.sonatype.nexus.repository.rest.SearchUtils; + +import com.google.common.annotations.VisibleForTesting; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Strings.isNullOrEmpty; +import static com.google.common.collect.Lists.newArrayList; +import static java.util.stream.Collectors.toList; + +/** + * Utility for working with asset maps that come out of Elastic + * + * @since 3.6.1 + */ +@Named +@Singleton +class AssetMapUtils +{ + private static final String EMPTY_PARAM = ""; + + private final SearchUtils searchUtils; + + @Inject + public AssetMapUtils(final SearchUtils searchUtils) { + this.searchUtils = checkNotNull(searchUtils); + } + + /** + * Get a value from an asset map that has come out of Elastic Search + * + * Out of Elastic comes a component map which contains an 'assets' entry, which itself is a map containing other + * maps. In the REST API we have query parameters like 'assets.attributes.checksum.sha1'. Given the 'assets' map and + * an identifier such as this, this method will attempt to pull the nested value out. + * + * @param assetMap the Map containing all the asset attributes + * @param identifier an attribute identifier + * @return Value, if found + */ + @SuppressWarnings("unchecked") + static Optional getValueFromAssetMap(final Map assetMap, final String identifier) { + if (isNullOrEmpty(identifier) || assetMap.isEmpty()) { + return Optional.empty(); + } + + List keys = newArrayList(identifier.split("\\.")); + + if ("assets".equals(keys.get(0))) { + keys.remove(0); + } + + Object value = assetMap; + for (String key : keys) { + if (value == null) { + return Optional.empty(); + } + value = ((Map) value).get(key); + } + return Optional.ofNullable(value); + } + + /** + * The convention of providing a parameter without a value can be used + * with the rest calls to filter out assets that have a value when it's not desired. + * A scenario where this is applicable is with maven jar artifacts where one jar has + * a classifier and one jar is without, yet they share the same GAV coordinates. + * + * For example, the following set of query parameters: + * + *
    maven.artifactId=foo&maven.baseVersion=2.7.3&maven.extension=jar&maven.classifier
    + * + * means search for and return assets that match on these values + * artifactId=foo, baseVersion=2.7.3, extension=jar and no classifier defined. + * + * Alternatively, the following + * + *
    maven.artifactId=foo&maven.baseVersion=2.7.3&maven.extension=jar&maven.classifier=sources
    + * + * means search for and return assets that match on these values + * artifactId=foo, baseVersion=2.7.3, extension=jar and classifier=sources + * + * @return boolean to indicate if the response should include (true) or exclude (false) this asset + */ + @VisibleForTesting + boolean filterAsset(final Map assetMap, final MultivaluedMap assetParams) { + // short circuit if the assetMap contains an assetAttribute found in the list of empty asset params + if (excludeAsset(assetMap, getEmptyAssetParams(assetParams))) { + return false; + } + + // fetch only the set of assetParams that have values + final Map assetParamsWithValues = getNonEmptyAssetParams(assetParams); + + // if no asset parameters were sent, we'll count that as return all assets + if (assetParamsWithValues.isEmpty()) { + return true; + } + + // loop each asset specific http query parameter to filter out assets that do not apply + return assetParamsWithValues.entrySet().stream() + .map(entry -> keepAsset(assetMap, entry.getKey(), entry.getValue())) + .filter(b->b) + .findFirst() + .orElse(false); + } + + /** + * Checks the assetMap to see if it has values for any attribute in the paramFilters list. If 'true', this indicates + * the asset should be excluded from the response. + * + * @return boolean indicating if the asset for which this method was called should be excluded from the response + */ + static boolean excludeAsset(final Map assetMap, final List paramFilters) { + return paramFilters.stream() + .map(filter -> getValueFromAssetMap(assetMap, filter).isPresent()) + .filter(b -> b) + .findFirst() + .orElse(false); + } + + /** + * Helper that simply checks to see if the provided param key is in the asset map and if so, does it's value + * equal the param value. + * + * @return boolean indicating if the asset contains the param key and matches the provided param value + */ + static boolean keepAsset(final Map assetMap, final String paramKey, final String paramValue) { + return getValueFromAssetMap(assetMap, paramKey) + .map(result -> result.equals(paramValue)) + .orElse(false); + } + + /** + * Traverses the query parameters to find any parameters without a corresponding + * value. + * + * @return a list of parameter names that have empty values + */ + @VisibleForTesting + List getEmptyAssetParams(final MultivaluedMap assetParams) { + return assetParams.entrySet() + .stream() + .filter(entry -> EMPTY_PARAM.equals(entry.getValue().get(0))) + .map(e -> searchUtils.getFullAssetAttributeName(e.getKey())) + .collect(toList()); + } + + /** + * Traverses the query parameters to find only parameters with a value. + * + * @return a map of parameter names that have values + */ + @VisibleForTesting + Map getNonEmptyAssetParams(final MultivaluedMap assetParams) { + return assetParams.entrySet() + .stream() + .filter(entry -> !EMPTY_PARAM.equals(entry.getValue().get(0))) + .collect(Collectors + .toMap(entry -> searchUtils.getFullAssetAttributeName(entry.getKey()), entry -> entry.getValue().get(0))); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/AssetsResource.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/AssetsResource.java new file mode 100644 index 0000000000000000000000000000000000000000..d534a911c8c31a2b9359c77b844cbacbe3210277 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/AssetsResource.java @@ -0,0 +1,164 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import java.util.List; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.entity.ContinuationTokenHelper; +import org.sonatype.nexus.common.entity.ContinuationTokenHelper.ContinuationTokenException; +import org.sonatype.nexus.common.entity.DetachedEntityId; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.BrowseResult; +import org.sonatype.nexus.repository.browse.BrowseService; +import org.sonatype.nexus.repository.browse.QueryOptions; +import org.sonatype.nexus.repository.rest.api.AssetXO; +import org.sonatype.nexus.repository.rest.internal.api.RepositoryItemIDXO; +import org.sonatype.nexus.repository.rest.internal.resources.doc.AssetsResourceDoc; +import org.sonatype.nexus.repository.maintenance.MaintenanceService; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; +import org.sonatype.nexus.rest.Page; +import org.sonatype.nexus.rest.Resource; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.getLast; +import static java.lang.String.format; +import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.toList; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.sonatype.nexus.repository.rest.api.AssetXO.fromAsset; +import static org.sonatype.nexus.repository.rest.internal.api.RepositoryItemIDXO.fromString; +import static org.sonatype.nexus.repository.http.HttpStatus.NOT_ACCEPTABLE; +import static org.sonatype.nexus.repository.http.HttpStatus.UNPROCESSABLE_ENTITY; +import static org.sonatype.nexus.rest.APIConstants.BETA_API_PREFIX; + +/** + * @since 3.3 + */ +@Named +@Singleton +@Path(AssetsResource.RESOURCE_URI) +@Produces(APPLICATION_JSON) +@Consumes(APPLICATION_JSON) +public class AssetsResource + extends ComponentSupport + implements Resource, AssetsResourceDoc +{ + public static final String RESOURCE_URI = BETA_API_PREFIX + "/assets"; + + private final BrowseService browseService; + + private final RepositoryManagerRESTAdapter repositoryManagerRESTAdapter; + + private final AssetEntityAdapter assetEntityAdapter; + + private final MaintenanceService maintenanceService; + + private final ContinuationTokenHelper continuationTokenHelper; + + @Inject + public AssetsResource(final BrowseService browseService, + final RepositoryManagerRESTAdapter repositoryManagerRESTAdapter, + final AssetEntityAdapter assetEntityAdapter, + final MaintenanceService maintenanceService, + @Named("asset") final ContinuationTokenHelper continuationTokenHelper) + { + this.browseService = checkNotNull(browseService); + this.repositoryManagerRESTAdapter = checkNotNull(repositoryManagerRESTAdapter); + this.assetEntityAdapter = checkNotNull(assetEntityAdapter); + this.maintenanceService = checkNotNull(maintenanceService); + this.continuationTokenHelper = checkNotNull(continuationTokenHelper); + } + + + @GET + public Page getAssets(@QueryParam("continuationToken") final String continuationToken, + @QueryParam("repository") final String repositoryId) + { + Repository repository = repositoryManagerRESTAdapter.getRepository(repositoryId); + + BrowseResult assetBrowseResult = browseService.browseAssets( + repository, + new QueryOptions(null, "id", "asc", 0, 10, lastIdFromContinuationToken(continuationToken))); + + List assetXOs = assetBrowseResult.getResults().stream() + .map(asset -> fromAsset(asset, repository)) + .collect(toList()); + return new Page<>(assetXOs, assetBrowseResult.getTotal() > assetBrowseResult.getResults().size() ? + continuationTokenHelper.getTokenFromId(getLast(assetBrowseResult.getResults())) : null); + } + + @Nullable + private String lastIdFromContinuationToken(final String continuationToken) { + try { + return continuationTokenHelper.getIdFromToken(continuationToken); + } + catch (ContinuationTokenException e) { + log.debug(e.getMessage(), e); + throw new WebApplicationException(NOT_ACCEPTABLE); + } + } + + @GET + @Path("/{id}") + public AssetXO getAssetById(@PathParam("id") final String id) + { + RepositoryItemIDXO repositoryItemIDXO = fromString(id); + Repository repository = repositoryManagerRESTAdapter.getRepository(repositoryItemIDXO.getRepositoryId()); + + Asset asset = getAsset(id, repository, new DetachedEntityId(repositoryItemIDXO.getId())); + return fromAsset(asset, repository); + } + + @DELETE + @Path("/{id}") + public void deleteAsset(@PathParam("id") + final String id) + { + RepositoryItemIDXO repositoryItemIDXO = fromString(id); + Repository repository = repositoryManagerRESTAdapter.getRepository(repositoryItemIDXO.getRepositoryId()); + + DetachedEntityId entityId = new DetachedEntityId(repositoryItemIDXO.getId()); + Asset asset = getAsset(id, repository, entityId); + + maintenanceService.deleteAsset(repository, asset); + } + + private Asset getAsset(final String id, final Repository repository, final DetachedEntityId entityId) + { + try { + return ofNullable(browseService + .getAssetById(assetEntityAdapter.recordIdentity(entityId), repository)) + .orElseThrow(() -> new NotFoundException("Unable to locate asset with id " + id)); + } + catch (IllegalArgumentException e) { + log.debug("IllegalArgumentException caught retrieving asset with id {}", entityId, e); + throw new WebApplicationException(format("Unable to process asset with id %s", entityId), UNPROCESSABLE_ENTITY); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/ComponentUploadUtils.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/ComponentUploadUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..d33526e72180bae7d5d1b9abd97210937347703e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/ComponentUploadUtils.java @@ -0,0 +1,243 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.upload.AssetUpload; +import org.sonatype.nexus.repository.upload.ComponentUpload; +import org.sonatype.nexus.repository.view.PartPayload; + +import org.jboss.resteasy.plugins.providers.multipart.InputPart; +import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; + +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; +import static javax.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE; + +/** + * Utility for processing component upload data. + * + * @since 3.8 + */ +class ComponentUploadUtils +{ + private static final Pattern FIELD_NAME_PATTERN = Pattern.compile(".*\\sname\\s*=[\'\"\\s]?([^\'\";]+).*"); + + private static final Pattern FILENAME_PATTERN = Pattern.compile(".*\\sfilename\\s*=[\'\"\\s]?([^\'\";]+).*"); + + private ComponentUploadUtils() { + // empty + } + + /** + * Extract the value from using given pattern from the Content Disposition header + * Example: Content-Disposition: form-data; name="fieldName"; filename="filename.jpg" + * with a pattern of .*\sfilename\s*=['"\s]?([^'";]+).* will return filename.jpg + * + * @param header Content Disposition header + * @param fieldPattern Regex pattern to find the field + * @param formatPrefix Format name used as a prefix that shall be removed + * @return value of the 'name' parameter + */ + private static Optional extractValue(final String header, final Pattern fieldPattern, final Optional formatPrefix) { + Matcher matcher = fieldPattern.matcher(header); + if (matcher.matches()) { + String value = matcher.group(1); + if (formatPrefix.isPresent()) { + value = value.replaceFirst("^" + formatPrefix.get() + "\\.", ""); + } + return Optional.of(value); + } + else { + return Optional.empty(); + } + } + + private static Optional extractFieldName(final String format, final String header) { + return extractValue(header, FIELD_NAME_PATTERN, Optional.of(format)); + } + + private static Optional extractFilename(final String header) { + return extractValue(header, FILENAME_PATTERN, Optional.empty()); + } + + static ComponentUpload createComponentUpload(final String format, final MultipartInput multipartInput) + throws IOException + { + Map assetsPayloads = getAssetsPayloads(format, multipartInput); + Map formFields = getTextFormFields(format, multipartInput, assetsPayloads.keySet()); + + List assetUploads = assetsPayloads.entrySet().stream() + .map(asset -> createAssetUpload(asset.getKey(), asset.getValue(), formFields)) + .collect(Collectors.toList()); + + ComponentUpload componentUpload = new ComponentUpload(); + componentUpload.setFields(getComponentFields(formFields, assetUploads)); + componentUpload.setAssetUploads(assetUploads); + return componentUpload; + } + + /** + * Computes component fields by filtering out asset fields out of all form fields. + */ + private static Map getComponentFields(final Map formFields, + final List assetUploads) + { + Set assetFieldNames = assetUploads.stream() + .flatMap(asset -> asset.getFields().keySet().stream() + .map(assetField -> String.format("%s.%s", asset.getPayload().getFieldName(), assetField))) + .collect(toSet()); + + return formFields.entrySet().stream() + .filter(field -> !assetFieldNames.contains(field.getKey())) + .collect(toMap(Entry::getKey, Entry::getValue)); + } + + /** + * Creates an {@link AssetUpload} object with provided {@link InputStreamPartPayload} and fields extracted from the + * multi-part form. The asset fields must be prefixed with the assetName followed by any delimiter character. + * + * @param assetName Name of the asset + * @param assetPayload PartPayload of the asset + * @param formFields All text fields of the form + * @return {@link AssetUpload} to be passed to the {@link org.sonatype.nexus.repository.upload.UploadManager} + */ + private static AssetUpload createAssetUpload(final String assetName, + final InputStreamPartPayload assetPayload, + final Map formFields) + { + Map assetFields = formFields.entrySet().stream() + .filter(field -> field.getKey().startsWith(assetName) && (field.getKey().length() > assetName.length() + 2)) + .collect(Collectors.toMap(field -> field.getKey().substring(assetName.length() + 1), Entry::getValue)); + + AssetUpload assetUpload = new AssetUpload(); + assetUpload.setPayload(assetPayload); + assetUpload.setFields(assetFields); + return assetUpload; + } + + private static Map getTextFormFields(final String format, final MultipartInput multipartInput, final Set assetNames) + throws IOException + { + Map fields = new HashMap<>(); + List fieldsParts = multipartInput.getParts().stream() + .filter(part -> TEXT_PLAIN_TYPE.isCompatible(part.getMediaType())) + .collect(toList()); + + for (InputPart inputPart : fieldsParts) { + Optional maybeFieldName = extractFieldName(format, inputPart.getHeaders().getFirst(CONTENT_DISPOSITION)); + if (maybeFieldName.isPresent() && !assetNames.contains(maybeFieldName.get())) { + fields.put(maybeFieldName.get(), inputPart.getBodyAsString()); + } + } + return fields; + } + + private static Map getAssetsPayloads(final String format, + final MultipartInput multipartInput) + throws IOException + { + Map payloads = new HashMap<>(); + for (InputPart inputPart : multipartInput.getParts()) { + String contentDisposition = inputPart.getHeaders().getFirst(CONTENT_DISPOSITION); + Optional filename = extractFilename(contentDisposition); + if (filename.isPresent()) { + String name = extractFieldName(format, inputPart.getHeaders().getFirst(CONTENT_DISPOSITION)).orElse(filename.get()); + InputStream inputStream = inputPart.getBody(InputStream.class, null); + payloads.put(name, new InputStreamPartPayload(name, name, inputStream, inputPart.getMediaType().toString())); + } + } + return payloads; + } + + private static class InputStreamPartPayload + implements PartPayload + { + + final String name; + + final String fieldName; + + final InputStream inputStream; + + final String contentType; + + InputStreamPartPayload(final String name, + final String fieldName, + final InputStream inputStream, + final String contentType) + { + this.name = name; + this.fieldName = fieldName; + this.inputStream = inputStream; + this.contentType = contentType; + } + + @Nullable + @Override + public String getName() { + return name; + } + + @Override + public String getFieldName() { + return fieldName; + } + + @Override + public boolean isFormField() { + return false; + } + + @Override + public InputStream openInputStream() throws IOException { + return inputStream; + } + + @Override + public long getSize() { + try { + return inputStream.available(); + } + catch (IOException ignored) { + return 0; + } + } + + @Nullable + @Override + public String getContentType() { + return contentType; + } + + @Override + public void close() throws IOException { + inputStream.close(); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/ComponentsResource.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/ComponentsResource.java new file mode 100644 index 0000000000000000000000000000000000000000..3f14809cb50589a6cc46e17399b46f7d65958f27 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/ComponentsResource.java @@ -0,0 +1,251 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import java.io.IOException; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response.Status; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.entity.ContinuationTokenHelper; +import org.sonatype.nexus.common.entity.ContinuationTokenHelper.ContinuationTokenException; +import org.sonatype.nexus.common.entity.DetachedEntityId; +import org.sonatype.nexus.repository.IllegalOperationException; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.BrowseResult; +import org.sonatype.nexus.repository.browse.BrowseService; +import org.sonatype.nexus.repository.browse.QueryOptions; +import org.sonatype.nexus.repository.maintenance.MaintenanceService; +import org.sonatype.nexus.repository.rest.ComponentsResourceExtension; +import org.sonatype.nexus.repository.rest.api.ComponentXO; +import org.sonatype.nexus.repository.rest.api.ComponentXOFactory; +import org.sonatype.nexus.repository.rest.internal.api.RepositoryItemIDXO; +import org.sonatype.nexus.repository.rest.internal.resources.doc.ComponentsResourceDoc; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; +import org.sonatype.nexus.repository.upload.ComponentUpload; +import org.sonatype.nexus.repository.upload.UploadConfiguration; +import org.sonatype.nexus.repository.upload.UploadManager; +import org.sonatype.nexus.rest.Page; +import org.sonatype.nexus.rest.Resource; +import org.sonatype.nexus.rest.WebApplicationMessageException; + +import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.getLast; +import static java.lang.String.format; +import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.toList; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.sonatype.nexus.common.entity.EntityHelper.id; +import static org.sonatype.nexus.repository.http.HttpStatus.NOT_ACCEPTABLE; +import static org.sonatype.nexus.repository.http.HttpStatus.NOT_FOUND; +import static org.sonatype.nexus.repository.http.HttpStatus.UNPROCESSABLE_ENTITY; +import static org.sonatype.nexus.repository.rest.api.AssetXO.fromAsset; +import static org.sonatype.nexus.repository.rest.internal.api.RepositoryItemIDXO.fromString; +import static org.sonatype.nexus.repository.rest.internal.resources.ComponentUploadUtils.createComponentUpload; +import static org.sonatype.nexus.repository.rest.internal.resources.ComponentsResource.RESOURCE_URI; +import static org.sonatype.nexus.rest.APIConstants.BETA_API_PREFIX; + +/** + * @since 3.4 + */ +@Named +@Singleton +@Path(RESOURCE_URI) +@Produces(APPLICATION_JSON) +@Consumes(APPLICATION_JSON) +public class ComponentsResource + extends ComponentSupport + implements Resource, ComponentsResourceDoc +{ + + public static final String RESOURCE_URI = BETA_API_PREFIX + "/components"; + + private final RepositoryManagerRESTAdapter repositoryManagerRESTAdapter; + + private final BrowseService browseService; + + private final ComponentEntityAdapter componentEntityAdapter; + + private final MaintenanceService maintenanceService; + + private final ContinuationTokenHelper continuationTokenHelper; + + private final UploadManager uploadManager; + + private final UploadConfiguration uploadConfiguration; + + private final ComponentXOFactory componentXOFactory; + + private final Set componentsResourceExtensions; + + @Inject + public ComponentsResource(final RepositoryManagerRESTAdapter repositoryManagerRESTAdapter, + final BrowseService browseService, + final ComponentEntityAdapter componentEntityAdapter, + final MaintenanceService maintenanceService, + @Named("component") final ContinuationTokenHelper continuationTokenHelper, + final UploadManager uploadManager, + final UploadConfiguration uploadConfiguration, + final ComponentXOFactory componentXOFactory, + final Set componentsResourceExtensions + ) + { + this.repositoryManagerRESTAdapter = checkNotNull(repositoryManagerRESTAdapter); + this.browseService = checkNotNull(browseService); + this.componentEntityAdapter = checkNotNull(componentEntityAdapter); + this.maintenanceService = checkNotNull(maintenanceService); + this.continuationTokenHelper = checkNotNull(continuationTokenHelper); + this.uploadManager = checkNotNull(uploadManager); + this.uploadConfiguration = checkNotNull(uploadConfiguration); + this.componentXOFactory = checkNotNull(componentXOFactory); + this.componentsResourceExtensions = checkNotNull(componentsResourceExtensions); + } + + @GET + public Page getComponents(@QueryParam("continuationToken") final String continuationToken, + @QueryParam("repository") final String repositoryId) + { + Repository repository = repositoryManagerRESTAdapter.getRepository(repositoryId); + + //must explicitly order by id or the generate sql will automatically order on group/name/version. (see BrowseComponentsSqlBuider) + BrowseResult componentBrowseResult = browseService + .browseComponents(repository, + new QueryOptions(null, "id", "asc", 0, 10, lastIdFromContinuationToken(continuationToken))); + + List componentXOs = componentBrowseResult.getResults().stream() + .map(component -> fromComponent(component, repository)) + .collect(toList()); + + return new Page<>(componentXOs, componentBrowseResult.getTotal() > componentBrowseResult.getResults().size() ? + continuationTokenHelper.getTokenFromId(getLast(componentBrowseResult.getResults())) : null); + } + + @Nullable + private String lastIdFromContinuationToken(final String continuationToken) { + try { + return continuationTokenHelper.getIdFromToken(continuationToken); + } + catch (ContinuationTokenException e) { + log.debug(e.getMessage(), e); + throw new WebApplicationException(NOT_ACCEPTABLE); + } + } + + private ComponentXO fromComponent(Component component, Repository repository) { + String internalId = id(component).getValue(); + + ComponentXO componentXO = componentXOFactory.createComponentXO(); + + componentXO + .setAssets(browseService.browseComponentAssets(repository, component.getEntityMetadata().getId().getValue()) + .getResults() + .stream() + .map(asset -> fromAsset(asset, repository)) + .collect(toList())); + + componentXO.setGroup(component.group()); + componentXO.setName(component.name()); + componentXO.setVersion(component.version()); + componentXO.setId(new RepositoryItemIDXO(repository.getName(), internalId).getValue()); + componentXO.setRepository(repository.getName()); + componentXO.setFormat(repository.getFormat().getValue()); + + for (ComponentsResourceExtension componentsResourceExtension : componentsResourceExtensions) { + componentXO = componentsResourceExtension.updateComponentXO(componentXO, component); + } + + return componentXO; + } + + @GET + @Path("/{id}") + public ComponentXO getComponentById(@PathParam("id") final String id) + { + RepositoryItemIDXO repositoryItemXOID = fromString(id); + Repository repository = repositoryManagerRESTAdapter.getRepository(repositoryItemXOID.getRepositoryId()); + + Component component = getComponent(repositoryItemXOID, repository); + + return fromComponent(component, repository); + } + + private Component getComponent(final RepositoryItemIDXO repositoryItemIDXO, final Repository repository) + { + try { + return ofNullable(browseService + .getComponentById(componentEntityAdapter.recordIdentity(new DetachedEntityId(repositoryItemIDXO.getId())), + repository)).orElseThrow( + () -> new NotFoundException("Unable to locate component with id " + repositoryItemIDXO.getValue())); + } + catch (IllegalArgumentException e) { + log.debug("IllegalArgumentException caught retrieving component with id {}", repositoryItemIDXO.getId(), e); + throw new WebApplicationException(format("Unable to process component with id %s", repositoryItemIDXO.getId()), + UNPROCESSABLE_ENTITY); + } + } + + @DELETE + @Path("/{id}") + public void deleteComponent(@PathParam("id") final String id) + { + RepositoryItemIDXO repositoryItemIdXO = fromString(id); + Repository repository = repositoryManagerRESTAdapter.getRepository(repositoryItemIdXO.getRepositoryId()); + + Component component = getComponent(repositoryItemIdXO, repository); + + maintenanceService.deleteComponent(repository, component); + } + + /** + * @since 3.8 + */ + @POST + @Consumes(MediaType.MULTIPART_FORM_DATA) + public void uploadComponent(@QueryParam("repository") final String repositoryId, final MultipartInput multipartInput) + throws IOException + { + if (!uploadConfiguration.isEnabled()) { + throw new WebApplicationException(NOT_FOUND); + } + + Repository repository = repositoryManagerRESTAdapter.getRepository(repositoryId); + String format = repository.getFormat().getValue(); + ComponentUpload componentUpload = createComponentUpload(format, multipartInput); + + try { + uploadManager.handle(repository, componentUpload); + } catch (IllegalOperationException e) { + throw new WebApplicationMessageException(Status.BAD_REQUEST, e.getMessage()); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapter.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..85f13278e4d6e575db7aeea708ff26f2280fa017 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapter.java @@ -0,0 +1,32 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import org.sonatype.nexus.repository.Repository; + +/** + * A component to share common functionality for interacting with {@link + * org.sonatype.nexus.repository.manager.RepositoryManager}. + * between API resources + * + * @since 3.4 + */ +public interface RepositoryManagerRESTAdapter +{ + /** + * Retrieve a repository. Will throw a {@link javax.ws.rs.WebApplicationException} with status code 422 if the + * supplied repository id is null, and throws a {@link javax.ws.rs.NotFoundException} if no repository with the + * supplied id exists. + */ + Repository getRepository(String repositoryId); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..164ae02f402e54718cf90ce3cade7561a34eed2b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImpl.java @@ -0,0 +1,105 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.WebApplicationException; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.security.RepositoryContentSelectorPermission; +import org.sonatype.nexus.repository.security.RepositoryViewPermission; +import org.sonatype.nexus.security.SecurityHelper; +import org.sonatype.nexus.selector.SelectorManager; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Collections.singletonList; +import static java.util.Optional.ofNullable; +import static org.sonatype.nexus.repository.http.HttpStatus.FORBIDDEN; +import static org.sonatype.nexus.repository.http.HttpStatus.UNPROCESSABLE_ENTITY; +import static org.sonatype.nexus.security.BreadActions.BROWSE; +import static org.sonatype.nexus.security.BreadActions.READ; + +/** + * An implementation of the {@link RepositoryManagerRESTAdapter} + * + * @since 3.4 + */ +@Named +public class RepositoryManagerRESTAdapterImpl + implements RepositoryManagerRESTAdapter +{ + private final RepositoryManager repositoryManager; + + private final SecurityHelper securityHelper; + + private final SelectorManager selectorManager; + + @Inject + public RepositoryManagerRESTAdapterImpl(final RepositoryManager repositoryManager, + final SecurityHelper securityHelper, + final SelectorManager selectorManager) + { + this.repositoryManager = checkNotNull(repositoryManager); + this.securityHelper = checkNotNull(securityHelper); + this.selectorManager = checkNotNull(selectorManager); + } + + @Override + public Repository getRepository(final String repositoryId) { + if (repositoryId == null) { + throw new WebApplicationException("repositoryId is required.", UNPROCESSABLE_ENTITY); + } + Repository repository = ofNullable(repositoryManager.get(repositoryId)) + .orElseThrow(() -> new NotFoundException("Unable to locate repository with id " + repositoryId)); + + if (userCanBrowseRepository(repository)) { + //browse implies complete access to the repository. + return repository; + } + else if (userCanViewRepository(repository)) { + //user knows the repository exists but does not have the appropriate permission to browse, return a 403 + throw new WebApplicationException(FORBIDDEN); + } + else { + //User does not know the repository exists because they can not VIEW or BROWSE, return a 404 + throw new NotFoundException("Unable to locate repository with id " + repositoryId); + } + } + + private boolean userCanViewRepository(final Repository repository) { + return userHasReadPermission(repository) || userHasAnyContentSelectorAccess(repository); + } + + private boolean userCanBrowseRepository(final Repository repository) { + return userHasBrowsePermissions(repository) || userHasAnyContentSelectorAccess(repository); + } + + private boolean userHasBrowsePermissions(final Repository repository) { + return securityHelper.anyPermitted( + new RepositoryViewPermission(repository.getFormat().getValue(), repository.getName(), BROWSE)); + } + + private boolean userHasReadPermission(final Repository repository) { + return securityHelper.anyPermitted( + new RepositoryViewPermission(repository.getFormat().getValue(), repository.getName(), READ)); + } + + private boolean userHasAnyContentSelectorAccess(final Repository repository) { + return selectorManager.browse().stream().anyMatch(sc -> securityHelper.anyPermitted( + new RepositoryContentSelectorPermission(sc.getName(), repository.getFormat().getValue(), repository.getName(), + singletonList(BROWSE)))); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/SearchResource.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/SearchResource.java new file mode 100644 index 0000000000000000000000000000000000000000..c60d18ebb7d369fdde1d977700180def63e815d0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/SearchResource.java @@ -0,0 +1,241 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Stream; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.BrowseService; +import org.sonatype.nexus.repository.rest.SearchResourceExtension; +import org.sonatype.nexus.repository.rest.SearchUtils; +import org.sonatype.nexus.repository.rest.api.AssetXO; +import org.sonatype.nexus.repository.rest.api.ComponentXO; +import org.sonatype.nexus.repository.rest.api.ComponentXOFactory; +import org.sonatype.nexus.repository.rest.internal.api.RepositoryItemIDXO; +import org.sonatype.nexus.repository.rest.internal.resources.doc.SearchResourceDoc; +import org.sonatype.nexus.repository.search.SearchService; +import org.sonatype.nexus.rest.Page; +import org.sonatype.nexus.rest.Resource; + +import com.google.common.annotations.VisibleForTesting; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.search.SearchHit; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.String.format; +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.sonatype.nexus.repository.rest.api.AssetXO.fromAsset; +import static org.sonatype.nexus.repository.rest.api.AssetXO.fromElasticSearchMap; +import static org.sonatype.nexus.repository.rest.internal.resources.SearchResource.RESOURCE_URI; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.GROUP; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.NAME; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.REPOSITORY_NAME; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.VERSION; +import static org.sonatype.nexus.rest.APIConstants.BETA_API_PREFIX; + +/** + * @since 3.4 + */ +@Named +@Singleton +@Path(RESOURCE_URI) +@Produces(APPLICATION_JSON) +@Consumes(APPLICATION_JSON) +public class SearchResource + extends ComponentSupport + implements Resource, SearchResourceDoc +{ + public static final String RESOURCE_URI = BETA_API_PREFIX + "/search"; + + public static final String SEARCH_ASSET_URI = "/assets"; + + public static final String SEARCH_AND_DOWNLOAD_URI = "/assets/download"; + + private final SearchUtils searchUtils; + + private final AssetMapUtils assetMapUtils; + + private final BrowseService browseService; + + private final SearchService searchService; + + private static final int PAGE_SIZE = 50; + + private final TokenEncoder tokenEncoder; + + private final ComponentXOFactory componentXOFactory; + + private final Set searchResourceExtensions; + + @Inject + public SearchResource(final SearchUtils searchUtils, + final AssetMapUtils assetMapUtils, + final BrowseService browseService, + final SearchService searchService, + final TokenEncoder tokenEncoder, + final ComponentXOFactory componentXOFactory, + final Set searchResourceExtensions) + { + this.searchUtils = checkNotNull(searchUtils); + this.assetMapUtils = checkNotNull(assetMapUtils); + this.browseService = checkNotNull(browseService); + this.searchService = checkNotNull(searchService); + this.tokenEncoder = checkNotNull(tokenEncoder); + this.componentXOFactory = checkNotNull(componentXOFactory); + this.searchResourceExtensions = checkNotNull(searchResourceExtensions); + } + + @GET + public Page search( + @QueryParam("continuationToken") final String continuationToken, + @Context final UriInfo uriInfo) + { + QueryBuilder query = searchUtils.buildQuery(uriInfo); + + int from = tokenEncoder.decode(continuationToken, query); + SearchResponse response = searchService.search(query, emptyList(), from, PAGE_SIZE); + + List componentXOs = Arrays.stream(response.getHits().hits()) + .map(this::toComponent) + .collect(toList()); + + return new Page<>(componentXOs, componentXOs.size() == PAGE_SIZE ? + tokenEncoder.encode(from, PAGE_SIZE, query) : null); + } + + private ComponentXO toComponent(final SearchHit hit) { + Map source = checkNotNull(hit.getSource()); + Repository repository = searchUtils.getRepository((String) source.get(REPOSITORY_NAME)); + + ComponentXO componentXO = componentXOFactory.createComponentXO(); + + componentXO + .setAssets(browseService.browseComponentAssets(repository, hit.getId()) + .getResults() + .stream() + .map(asset -> fromAsset(asset, repository)) + .collect(toList())); + + componentXO.setGroup((String) source.get(GROUP)); + componentXO.setName((String) source.get(NAME)); + componentXO.setVersion((String) source.get(VERSION)); + componentXO.setId(new RepositoryItemIDXO(repository.getName(), hit.getId()).getValue()); + componentXO.setRepository(repository.getName()); + componentXO.setFormat(repository.getFormat().getValue()); + + for (SearchResourceExtension searchResourceExtension : searchResourceExtensions) { + componentXO = searchResourceExtension.updateComponentXO(componentXO, hit); + } + + return componentXO; + } + + /** + * @since 3.6.1 + */ + @GET + @Path(SEARCH_ASSET_URI) + public Page searchAssets( + @QueryParam("continuationToken") final String continuationToken, + @Context final UriInfo uriInfo) + { + QueryBuilder query = searchUtils.buildQuery(uriInfo); + + int from = tokenEncoder.decode(continuationToken, query); + + List assetXOs = retrieveAssets(query, uriInfo, from); + return new Page<>(assetXOs, assetXOs.size() == PAGE_SIZE ? + tokenEncoder.encode(from, PAGE_SIZE, query) : null); + } + + /** + * @since 3.7 + */ + @GET + @Path(SEARCH_AND_DOWNLOAD_URI) + public Response searchAndDownloadAssets(@Context final UriInfo uriInfo) + { + QueryBuilder query = searchUtils.buildQuery(uriInfo); + + List assetXOs = retrieveAssets(query, uriInfo); + + return new AssetDownloadResponseProcessor(assetXOs).process(); + } + + private List retrieveAssets(final QueryBuilder query, final UriInfo uriInfo, final int from) { + SearchResponse response = searchService.search(query, emptyList(), from, PAGE_SIZE); + + // get the asset specific parameters + MultivaluedMap assetParams = getAssetParams(uriInfo); + + return Arrays.stream(response.getHits().hits()) + .flatMap(hit -> extractAssets(hit, assetParams)) + .collect(toList()); + } + + private List retrieveAssets(final QueryBuilder query, final UriInfo uriInfo) { + return this.retrieveAssets(query, uriInfo, 0); + } + + @SuppressWarnings("unchecked") + private Stream extractAssets(final SearchHit componentHit, + final MultivaluedMap assetParams) + { + Map componentMap = checkNotNull(componentHit.getSource()); + Repository repository = searchUtils.getRepository((String) componentMap.get(REPOSITORY_NAME)); + + List> assets = (List>) componentMap.get("assets"); + if (assets == null) { + return Stream.empty(); + } + + return assets.stream() + .filter(assetMap -> assetMapUtils.filterAsset(assetMap, assetParams)) + .map(asset -> fromElasticSearchMap(asset, repository)); + } + + @VisibleForTesting + MultivaluedMap getAssetParams(final UriInfo uriInfo) { + return uriInfo.getQueryParameters() + .entrySet().stream() + .filter(t -> searchUtils.isAssetSearchParam(t.getKey())) + .collect(toMap(Entry::getKey, Entry::getValue, (u, v) -> { + throw new IllegalStateException(format("Duplicate key %s", u)); + }, MultivaluedHashMap::new)); + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/TokenEncoder.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/TokenEncoder.java new file mode 100644 index 0000000000000000000000000000000000000000..42e7fadf2a9934304f1cbfe5b2e669e7d0131c6c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/TokenEncoder.java @@ -0,0 +1,65 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import javax.annotation.Nullable; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.ws.rs.WebApplicationException; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.io.Hex; + +import org.elasticsearch.index.query.QueryBuilder; + +import static java.lang.Integer.parseInt; +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.sonatype.nexus.common.hash.HashAlgorithm.MD5; +import static org.sonatype.nexus.repository.http.HttpStatus.NOT_ACCEPTABLE; + +/** + * @since 3.4 + */ +@Singleton +@Named +public class TokenEncoder + extends ComponentSupport +{ + int decode(@Nullable final String continuationToken, final QueryBuilder query) { + if (continuationToken == null) { + return 0; + } + else { + String decoded = new String(Hex.decode(continuationToken), UTF_8); + String[] decodedParts = decoded.split(":"); + if (decodedParts.length != 2) { + throw new WebApplicationException(format("Unable to parse token %s", continuationToken), NOT_ACCEPTABLE); + } + if (!decodedParts[1].equals(getHashCode(query))) { + throw new WebApplicationException( + format("Continuation token %s does not match this query", continuationToken), NOT_ACCEPTABLE); + } + return parseInt(decodedParts[0]); + } + } + + String encode(final int lastFrom, final int pageSize, final QueryBuilder query) { + int index = lastFrom + pageSize; + return Hex.encode(format("%s:%s", Integer.toString(index), getHashCode(query)).getBytes(UTF_8)); + } + + private String getHashCode(final QueryBuilder query) { + return MD5.function().hashString(query.toString(), UTF_8).toString(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/doc/AssetsResourceDoc.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/doc/AssetsResourceDoc.java new file mode 100644 index 0000000000000000000000000000000000000000..41d50804be3f0a53f14c0ebdf04f0948c9b5e772 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/doc/AssetsResourceDoc.java @@ -0,0 +1,62 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources.doc; + +import org.sonatype.nexus.repository.rest.api.AssetXO; +import org.sonatype.nexus.repository.rest.internal.resources.AssetsResource; +import org.sonatype.nexus.rest.Page; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + + +/** + * Swagger documentation for {@link AssetsResource} + * + * @since 3.4.0 + */ +@Api(value = "assets", description = "Operations to get and delete assets") +public interface AssetsResourceDoc +{ + @ApiOperation("List assets") + @ApiResponses(value = { + @ApiResponse(code = 403, message = "Insufficient permissions to list assets"), + @ApiResponse(code = 422, message = "Parameter 'repository' is required") + }) + Page getAssets( + @ApiParam(value = "A token returned by a prior request. If present, the next page of results are returned") + final String continuationToken, + + @ApiParam(value = "Repository from which you would like to retrieve assets.", required = true) + final String repository); + + @ApiOperation("Get a single asset") + @ApiResponses(value = { + @ApiResponse(code = 403, message = "Insufficient permissions to get asset"), + @ApiResponse(code = 404, message = "Asset not found"), + @ApiResponse(code = 422, message = "Malformed ID") + }) + AssetXO getAssetById(@ApiParam(value = "Id of the asset to get") final String id); + + @ApiOperation(value = "Delete a single asset") + @ApiResponses(value = { + @ApiResponse(code = 204, message = "Asset was successfully deleted"), + @ApiResponse(code = 403, message = "Insufficient permissions to delete asset"), + @ApiResponse(code = 404, message = "Asset not found"), + @ApiResponse(code = 422, message = "Malformed ID") + }) + void deleteAsset(@ApiParam(value = "Id of the asset to delete") final String id); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/doc/ComponentsResourceDoc.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/doc/ComponentsResourceDoc.java new file mode 100644 index 0000000000000000000000000000000000000000..d5d14bc5002f1cd215e6b0e51050ca1b2531c9f8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/doc/ComponentsResourceDoc.java @@ -0,0 +1,79 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources.doc; + +import java.io.IOException; + +import org.sonatype.nexus.repository.rest.api.ComponentXO; +import org.sonatype.nexus.repository.rest.internal.resources.ComponentsResource; +import org.sonatype.nexus.rest.Page; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import org.jboss.resteasy.annotations.providers.multipart.MultipartForm; +import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; + +/** + * Swagger documentation for {@link ComponentsResource} + * + * @since 3.4.0 + */ +@Api(value = "components") +public interface ComponentsResourceDoc +{ + @ApiOperation("List components") + @ApiResponses(value = { + @ApiResponse(code = 403, message = "Insufficient permissions to list components"), + @ApiResponse(code = 422, message = "Parameter 'repository' is required") + }) + Page getComponents( + @ApiParam(value = "A token returned by a prior request. If present, the next page of results are returned") + final String continuationToken, + + @ApiParam(value = "Repository from which you would like to retrieve components", required = true) + final String repository); + + @ApiOperation("Get a single component") + @ApiResponses(value = { + @ApiResponse(code = 403, message = "Insufficient permissions to get component"), + @ApiResponse(code = 404, message = "Component not found"), + @ApiResponse(code = 422, message = "Malformed ID") + }) + ComponentXO getComponentById(@ApiParam(value = "ID of the component to retrieve") final String id); + + @ApiOperation(value = "Delete a single component") + @ApiResponses(value = { + @ApiResponse(code = 204, message = "Component was successfully deleted"), + @ApiResponse(code = 403, message = "Insufficient permissions to delete component"), + @ApiResponse(code = 404, message = "Component not found"), + @ApiResponse(code = 422, message = "Malformed ID") + }) + void deleteComponent(@ApiParam(value = "ID of the component to delete") final String id); + + @ApiOperation(value = "Upload a single component", + //TODO: NEXUS-15443 remove this parameter when ready to ship with ui upload enabled + hidden = true + ) + @ApiResponses(value = { + @ApiResponse(code = 403, message = "Insufficient permissions to upload a component"), + @ApiResponse(code = 422, message = "Parameter 'repository' is required") + }) + void uploadComponent( + @ApiParam(value = "Name of the repository to which you would like to upload the component", required = true) + final String repository, + @ApiParam(hidden = true) @MultipartForm MultipartInput multipartInput) + throws IOException; +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/doc/SearchResourceDoc.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/doc/SearchResourceDoc.java new file mode 100644 index 0000000000000000000000000000000000000000..783ad1a858952588297b0ffc8870a38b77a36e8b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/rest/internal/resources/doc/SearchResourceDoc.java @@ -0,0 +1,61 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources.doc; + +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.sonatype.nexus.repository.rest.api.AssetXO; +import org.sonatype.nexus.repository.rest.api.ComponentXO; +import org.sonatype.nexus.repository.rest.internal.resources.SearchResource; +import org.sonatype.nexus.rest.Page; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import static org.sonatype.nexus.repository.rest.internal.resources.AssetDownloadResponseProcessor.*; + +/** + * Swagger documentation for {@link SearchResource} + * + * @since 3.4 + */ +@Api(value = "search") +public interface SearchResourceDoc +{ + @ApiOperation("Search components") + Page search( + @ApiParam(value = "A token returned by a prior request. If present, the next page of results are returned") + final String continuationToken, + @Context final UriInfo uriInfo); + + @ApiOperation("Search assets") + Page searchAssets( + @ApiParam(value = "A token returned by a prior request. If present, the next page of results are returned") + final String continuationToken, + @Context final UriInfo uriInfo); + + @ApiOperation(value = "Search and download asset", + notes = "Returns a 302 Found with location header field set to download URL. " + + "Search must return a single asset to receive download URL.") + @ApiResponses(value = { + @ApiResponse(code = 400, message = SEARCH_RETURNED_MULTIPLE_ASSETS), + @ApiResponse(code = 404, message = NO_SEARCH_RESULTS_FOUND) + }) + Response searchAndDownloadAssets( + @Context final UriInfo uriInfo); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/BulkIndexUpdateListener.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/BulkIndexUpdateListener.java new file mode 100644 index 0000000000000000000000000000000000000000..2893558b5ef9dc505c2f76a8f43e1c38cf366810 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/BulkIndexUpdateListener.java @@ -0,0 +1,56 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import org.sonatype.goodies.common.ComponentSupport; + +import org.elasticsearch.action.bulk.BulkProcessor; +import org.elasticsearch.action.bulk.BulkRequest; +import org.elasticsearch.action.bulk.BulkResponse; + +/** + * {@link BulkProcessor.Listener} that logs. + */ +class BulkIndexUpdateListener + extends ComponentSupport + implements BulkProcessor.Listener +{ + @Override + public void beforeBulk(final long executionId, final BulkRequest request) { + log.debug("index update starting, executionId: {}, request count: {}, request size (bytes): {}, ", + executionId, + request.requests().size(), + request.estimatedSizeInBytes() + ); + } + + @Override + public void afterBulk(final long executionId, final BulkRequest request, final BulkResponse response) { + log.debug("index update success, executionId: {}, request count: {}, request size (bytes): {}, " + + "response took: {}, response hasFailures: {}", + executionId, + request.requests().size(), + request.estimatedSizeInBytes(), + response.getTook(), + response.hasFailures()); + } + + @Override + public void afterBulk(final long executionId, final BulkRequest request, final Throwable failure) { + log.error("index update failure, executionId: {}, request count: {}, request size (bytes): {}; " + + "this may indicate that not enough CPU is available to effectively index repository content", + executionId, + request.requests().size(), + request.estimatedSizeInBytes(), failure); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/ComponentMetadata.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/ComponentMetadata.java new file mode 100644 index 0000000000000000000000000000000000000000..c3197344e9cd2fd2a259a0a763ec2b6eebfb91d3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/ComponentMetadata.java @@ -0,0 +1,33 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +/** + * Metadata (to be indexed) about a component. + * + * @since 3.0 + */ +public interface ComponentMetadata +{ + + /** + * Retrieves component Metadata id. + */ + String getId(); + + /** + * Converts component metadata to JSON format. + */ + String toJson(); + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/ComponentMetadataProducer.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/ComponentMetadataProducer.java new file mode 100644 index 0000000000000000000000000000000000000000..c1ea2997b53d558d74d0a95eb69c2a53182c9bf6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/ComponentMetadataProducer.java @@ -0,0 +1,35 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.util.Map; + +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; + +/** + * Producer of metadata to be indexed in JSON format. + * + * @since 3.0 + */ +public interface ComponentMetadataProducer +{ + + /** + * Retrieves data to be indexed for a component. + * + * @return data to be indexed in json format + */ + String getMetadata(Component component, Iterable assets, Map additional); + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/ComponentMetadataProducerExtension.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/ComponentMetadataProducerExtension.java new file mode 100644 index 0000000000000000000000000000000000000000..e367a6cdfa4cb16332c8a7a251a3d0d49cb04a1a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/ComponentMetadataProducerExtension.java @@ -0,0 +1,31 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.util.Map; + +import org.sonatype.nexus.repository.storage.Component; + +/** + * Extension point for {@link ComponentMetadataProducer} to add additional fields to the Elasticsearch index + * + * @since 3.8 + * + */ +public interface ComponentMetadataProducerExtension +{ + /** + * Get any data for the ES {@link ComponentMetadataProducer} from the {@link Component} + */ + Map getComponentMetadata(Component component); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/DefaultComponentMetadataProducer.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/DefaultComponentMetadataProducer.java new file mode 100644 index 0000000000000000000000000000000000000000..ef33d809b4be609ca4bebd58ba6775ea408b8eb5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/DefaultComponentMetadataProducer.java @@ -0,0 +1,123 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default {@link ComponentMetadataProducer} implementation that uses all properties of a component & its assets as + * metadata. + * + * @since 3.0 + */ +@Named +@Singleton +public class DefaultComponentMetadataProducer + implements ComponentMetadataProducer +{ + + public static final String ATTRIBUTES = "attributes"; + + public static final String CONTENT_TYPE = "content_type"; + + public static final String FORMAT = "format"; + + public static final String NAME = "name"; + + public static final String GROUP = "group"; + + public static final String REPOSITORY_NAME = "repository_name"; + + public static final String VERSION = "version"; + + public static final String ASSETS = "assets"; + + public static final String ID = "id"; + + private final Set componentMetadataProducerExtensions; + + @Inject + public DefaultComponentMetadataProducer(final Set componentMetadataProducerExtensions) { + this.componentMetadataProducerExtensions = checkNotNull(componentMetadataProducerExtensions); + } + + @Override + public String getMetadata(final Component component, + final Iterable assets, + final Map additional) + { + checkNotNull(component); + checkNotNull(assets); + checkNotNull(additional); + + Map metadata = new HashMap<>(); + put(metadata, FORMAT, component.format()); + put(metadata, GROUP, component.group()); + put(metadata, NAME, component.name()); + put(metadata, VERSION, component.version()); + put(metadata, ATTRIBUTES, component.attributes().backing()); + + List> allAssetMetadata = new ArrayList<>(); + for (Asset asset : assets) { + Map assetMetadata = new HashMap<>(); + put(assetMetadata, ID, documentId(asset)); + put(assetMetadata, NAME, asset.name()); + put(assetMetadata, CONTENT_TYPE, asset.contentType()); + put(assetMetadata, ATTRIBUTES, asset.attributes().backing()); + + allAssetMetadata.add(assetMetadata); + } + if (!allAssetMetadata.isEmpty()) { + metadata.put(ASSETS, allAssetMetadata); + } + + for (ComponentMetadataProducerExtension componentMetadataProducerExtension : componentMetadataProducerExtensions) { + metadata.putAll(componentMetadataProducerExtension.getComponentMetadata(component)); + } + + metadata.putAll(additional); + + try { + return JsonUtils.from(metadata); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + private String documentId(final Asset asset) { + return EntityHelper.id(asset).getValue(); + } + + private static void put(final Map metadata, final String key, final Object value) { + if (value != null) { + metadata.put(key, value); + } + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexBatchRequest.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexBatchRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..788f74ab0d3a63004e01b2335a4f72584868a19c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexBatchRequest.java @@ -0,0 +1,129 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.entity.EntityBatchEvent; +import org.sonatype.nexus.common.entity.EntityEvent; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.repository.storage.AssetEvent; +import org.sonatype.nexus.repository.storage.ComponentDeletedEvent; +import org.sonatype.nexus.repository.storage.ComponentEvent; + +/** + * Requests indexing of one or more components across one or more repositories. + * + * @since 3.4 + */ +public final class IndexBatchRequest +{ + /** + * Repository-scoped index requests. + */ + private final Map requests = new HashMap<>(); + + /** + * Tracks components that were deleted; this may include components also tracked under {@link IndexRequest}s. + */ + private final Set pendingDeletes = new HashSet<>(); + + /** + * Populates index requests based on the given event. + */ + public IndexBatchRequest(final EntityBatchEvent batchEvent) { + for (final EntityEvent event : batchEvent.getEvents()) { + if (event instanceof ComponentEvent) { + consume((ComponentEvent) event); + } + else if (event instanceof AssetEvent) { + consume((AssetEvent) event); + } + } + } + + /** + * Creates a blank index request for manual building. + */ + public IndexBatchRequest() { + // no-op + } + + /** + * Requests an update of the given component's index under the given repository. + */ + public void update(final String repositoryName, final EntityId componentId) { + request(repositoryName).update(componentId); + } + + /** + * Requests a delete of the given component's index under the given repository (if known). + * + * If the repository is unknown then we'll query Elasticsearch later on to find the index. + */ + public void delete(@Nullable final String repositoryName, final EntityId componentId) { + if (repositoryName != null) { + request(repositoryName).update(componentId); // scope delete under specific repository + } + pendingDeletes.add(componentId); + } + + /** + * Applies the index requests to the given consumer and returns any remaining pending deletes. + * + * These deletes aren't directly associated with a repository and require a delete-by-query step. + */ + Set apply(final BiConsumer consumer) { + requests.forEach(consumer); + return pendingDeletes; + } + + /** + * Marks the component's index as needing an update or deletion. + */ + private void consume(final ComponentEvent event) { + if (event instanceof ComponentDeletedEvent) { + delete(event.getRepositoryName(), event.getComponentId()); + } + else { + update(event.getRepositoryName(), event.getComponentId()); + } + } + + /** + * Marks the owning component's index as needing an update. + */ + private void consume(final AssetEvent event) { + if (event.getComponentId() != null) { + update(event.getRepositoryName(), event.getComponentId()); + } + } + + /** + * Returns an {@link IndexRequest} for the given repository. + */ + private IndexRequest request(final String repositoryName) { + IndexRequest request = requests.get(repositoryName); + if (request == null) { + request = new IndexRequest(pendingDeletes); + requests.put(repositoryName, request); + } + return request; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexRequest.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..a8c2728a8a2ba79aa76c8e67db8d6c2390d5ceb9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexRequest.java @@ -0,0 +1,95 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.sonatype.nexus.common.entity.EntityId; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Requests indexing of one or more components in a particular repository. + * + * @since 3.4 + */ +final class IndexRequest +{ + /** + * All pending deletes as tracked by {@link IndexBatchRequest}. + */ + private final Set pendingDeletes; + + /** + * Components that need their index either updated or possibly removed. + */ + private final Set updatedIds = new HashSet<>(); + + IndexRequest(final Set pendingDeletes) { + this.pendingDeletes = checkNotNull(pendingDeletes); + } + + /** + * Marks the given component as needing their index updated or possibly removed. + */ + void update(final EntityId componentId) { + updatedIds.add(componentId); + } + + /** + * Applies the index request to the repository's {@link SearchFacet} one-by-one. + * + * Has side-effect of removing local deletions from {@link #pendingDeletes}. + */ + void apply(final SearchFacet searchFacet) { + updatedIds.forEach(id -> { + if (pendingDeletes.remove(id)) { + searchFacet.delete(id); + } + else { + searchFacet.put(id); + } + }); + } + + /** + * Applies the index request to the repository's {@link SearchFacet} in bulk. + * + * Has side-effect of removing local deletions from {@link #pendingDeletes}. + */ + void bulkApply(final SearchFacet searchFacet) { + + if (!pendingDeletes.isEmpty()) { + Set deletedIds = new HashSet<>(); + + // move ids over from bulk-update to bulk-delete as appropriate + for (Iterator itr = updatedIds.iterator(); itr.hasNext();) { + EntityId id = itr.next(); + if (pendingDeletes.remove(id)) { + deletedIds.add(id); + itr.remove(); + } + } + + if (!deletedIds.isEmpty()) { + searchFacet.bulkDelete(deletedIds); + } + } + + if (!updatedIds.isEmpty()) { + searchFacet.bulkPut(updatedIds); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexRequestProcessor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexRequestProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..fcd9e6f822b358be658097f109e8c680eabd500f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexRequestProcessor.java @@ -0,0 +1,138 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.lifecycle.LifecycleSupport; +import org.sonatype.nexus.common.entity.EntityBatchEvent; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.event.EventAware.Asynchronous; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.common.stateguard.InvalidStateException; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.transaction.UnitOfWork; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.Subscribe; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.transform; +import static org.sonatype.nexus.repository.FacetSupport.State.DELETED; +import static org.sonatype.nexus.repository.FacetSupport.State.DESTROYED; +import static org.sonatype.nexus.repository.FacetSupport.State.STOPPED; + +/** + * Async processor of {@link IndexBatchRequest}s, which are used to trigger search updates. + * + * @since 3.0 + */ +@Named +@Singleton +public class IndexRequestProcessor + extends LifecycleSupport + implements Asynchronous +{ + private final RepositoryManager repositoryManager; + + private final EventManager eventManager; + + private final SearchService searchService; + + private final boolean bulkProcessing; + + @Inject + public IndexRequestProcessor(final RepositoryManager repositoryManager, + final EventManager eventManager, + final SearchService searchService, + @Named("${nexus.elasticsearch.bulkProcessing:-true}") final boolean bulkProcessing) + { + this.repositoryManager = checkNotNull(repositoryManager); + this.eventManager = checkNotNull(eventManager); + this.searchService = checkNotNull(searchService); + this.bulkProcessing = bulkProcessing; + } + + @Override + protected void doStart() { + eventManager.register(this); + } + + @Override + protected void doStop() { + eventManager.unregister(this); + + searchService.flush(); + } + + @Subscribe + @AllowConcurrentEvents + public void on(final EntityBatchEvent batchEvent) { + process(new IndexBatchRequest(batchEvent)); + } + + public void process(final IndexBatchRequest request) { + Set pendingDeletes = request.apply(this::maybeUpdateSearchIndex); + if (!pendingDeletes.isEmpty()) { + // IndexSyncService can request deletes that have no associated repository, + // in which case we need to attempt a special bulk delete as the last step + searchService.bulkDelete(null, transform(pendingDeletes, EntityId::getValue)); + } + } + + private void maybeUpdateSearchIndex(final String repositoryName, final IndexRequest indexRequest) { + final Repository repository = repositoryManager.get(repositoryName); + if (repository != null) { + try { + doUpdateSearchIndex(repository, indexRequest); + } + catch (InvalidStateException e) { + switch (e.getInvalidState()) { + case STOPPED: + case DELETED: + case DESTROYED: + log.debug("Ignoring async search update for {} repository {}", e.getInvalidState(), repositoryName, e); + break; + default: + throw e; + } + } + } + else { + log.debug("Ignoring async search update for missing repository {}", repositoryName); + } + } + + private void doUpdateSearchIndex(final Repository repository, final IndexRequest indexRequest) { + repository.optionalFacet(SearchFacet.class).ifPresent(searchFacet -> { + UnitOfWork.begin(repository.facet(StorageFacet.class).txSupplier()); + try { + if (bulkProcessing) { + indexRequest.bulkApply(searchFacet); + } + else { + indexRequest.apply(searchFacet); + } + } + finally { + UnitOfWork.end(); + } + }); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexSettingsContributor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexSettingsContributor.java new file mode 100644 index 0000000000000000000000000000000000000000..d2d2c95ec861bbc06d9770630de7f0b0bf169c6d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexSettingsContributor.java @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.net.URL; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.Repository; + +/** + * Contributor to ES index settings. + * + * Index settings from all contributors are merged before index is created. + * + * @since 3.0 + */ +public interface IndexSettingsContributor +{ + /** + * Retrieves index settings for specific repository. + * + * @return ES index settings in json format or null if this contributor has no settings for repository + */ + @Nullable + URL getIndexSettings(Repository repository); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexSettingsContributorSupport.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexSettingsContributorSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..8ae4c685e86674788b060f630e8ec85ec3669bdb --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexSettingsContributorSupport.java @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.net.URL; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; + +import com.google.common.io.Resources; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.search.SearchServiceImpl.MAPPING_JSON; + +// TODO: Can probably simplify and avoid intf/impl here for such simple configurator + +/** + * Support for {@link IndexSettingsContributor} implementations. + * + * @since 3.0 + */ +public class IndexSettingsContributorSupport + extends ComponentSupport + implements IndexSettingsContributor +{ + private final Format format; + + public IndexSettingsContributorSupport(final Format format) { + this.format = checkNotNull(format); + } + + @Override + @Nullable + public URL getIndexSettings(final Repository repository) { + checkNotNull(repository); + if (format.equals(repository.getFormat())) { + return Resources.getResource(getClass(), MAPPING_JSON); + } + return null; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexSyncService.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexSyncService.java new file mode 100644 index 0000000000000000000000000000000000000000..9dd8120c8f5f064a572ec8fca4bf9e481954fb7d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/IndexSyncService.java @@ -0,0 +1,225 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Map; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.lifecycle.LifecycleSupport; +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.entity.AttachedEntityId; +import org.sonatype.nexus.orient.entity.EntityAdapter; +import org.sonatype.nexus.orient.entity.EntityLog; +import org.sonatype.nexus.orient.entity.EntityLog.UnknownDeltaException; +import org.sonatype.nexus.repository.RepositoryTaskSupport; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; +import org.sonatype.nexus.scheduling.TaskConfiguration; +import org.sonatype.nexus.scheduling.TaskScheduler; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.io.ByteStreams; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.storage.impl.local.paginated.wal.OLogSequenceNumber; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.TASKS; +import static org.sonatype.nexus.orient.DatabaseInstanceNames.COMPONENT; +import static org.sonatype.nexus.repository.storage.AssetEntityAdapter.P_COMPONENT; +import static org.sonatype.nexus.repository.storage.BucketEntityAdapter.P_REPOSITORY_NAME; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_BUCKET; + +/** + * Service that keeps the Elasticsearch index in-sync with off-process database changes. + * + * @since 3.4 + */ +@Named +@Singleton +@ManagedLifecycle(phase = TASKS) +public class IndexSyncService + extends LifecycleSupport +{ + public static final String INDEX_UPGRADE_MARKER = "INDEX_UPGRADE_MARKER"; + + private final Provider componentDatabase; + + private final ComponentEntityAdapter componentEntityAdapter; + + private final NodeAccess nodeAccess; + + private final IndexRequestProcessor indexRequestProcessor; + + private final TaskScheduler taskScheduler; + + private final EntityLog entityLog; + + private final File checkpointFile; + + @Inject + public IndexSyncService(@Named(COMPONENT) final Provider componentDatabase, + final ComponentEntityAdapter componentEntityAdapter, + final AssetEntityAdapter assetEntityAdapter, + final ApplicationDirectories directories, + final NodeAccess nodeAccess, + final IndexRequestProcessor indexRequestProcessor, + final TaskScheduler taskScheduler) + { + this.componentDatabase = checkNotNull(componentDatabase); + this.componentEntityAdapter = checkNotNull(componentEntityAdapter); + this.nodeAccess = checkNotNull(nodeAccess); + this.indexRequestProcessor = checkNotNull(indexRequestProcessor); + this.taskScheduler = checkNotNull(taskScheduler); + + this.entityLog = new EntityLog(componentDatabase, componentEntityAdapter, assetEntityAdapter); + this.checkpointFile = new File(directories.getWorkDirectory("elasticsearch"), "nexus.lsn"); + } + + @Override + protected void doStart() throws Exception { + indexRequestProcessor.start(); + + try { + Map changes = entityLog.since(loadCheckpoint()); + if (!changes.isEmpty()) { + log.info("Applying {} incremental search updates", changes.size()); + syncIndex(changes); + } + } + catch (UnknownDeltaException e) { + logReason("Rebuilding search indexes because database has diverged", e); + rebuildIndex(); + } + catch (FileNotFoundException e) { + if (!nodeAccess.isOldestNode()) { + logReason("Rebuilding search indexes to match joining cluster", e); + rebuildIndex(); + } + } + catch (Exception e) { + log.warn("Unexpected error, skipping index sync", e); + } + } + + @Override + protected void doStop() throws Exception { + try { + saveCheckpoint(entityLog.mark()); + } + catch (IOException e) { + log.warn("Problem saving {}", checkpointFile, e); + } + + indexRequestProcessor.stop(); + } + + private void logReason(final String message, final Throwable cause) { + if (log.isDebugEnabled()) { + log.info(message, cause); + } + else { + log.info(message); + } + } + + private void syncIndex(final Map changes) { + IndexBatchRequest batch = new IndexBatchRequest(); + try (ODatabaseDocumentTx db = componentDatabase.get().acquire()) { + changes.forEach((rid, adapter) -> { + ODocument document = db.load(rid); + if (document != null) { + EntityId componentId = findComponentId(document); + if (componentId != null) { + batch.update(findRepositoryName(document), componentId); + } + } + else if (adapter instanceof ComponentEntityAdapter) { + batch.delete(null, componentId(rid)); + } + }); + } + indexRequestProcessor.process(batch); + } + + private String findRepositoryName(final ODocument document) { + return ((ODocument) document.field(P_BUCKET)).field(P_REPOSITORY_NAME); + } + + @Nullable + private EntityId findComponentId(final ODocument document) { + if (document.containsField(P_COMPONENT)) { + // get the owning component from the asset, might be null if asset is standalone + return componentId(document.field(P_COMPONENT, ORID.class)); + } + else { + // not an asset, must be a component itself + return componentId(document.getIdentity()); + } + } + + @Nullable + private EntityId componentId(@Nullable final ORID rid) { + return rid != null ? new AttachedEntityId(componentEntityAdapter, rid) : null; + } + + /** + * Schedule one-off background task to rebuild the indexes of all repositories. + */ + private void rebuildIndex() { + TaskConfiguration taskConfig = taskScheduler.createTaskConfigurationInstance(RebuildIndexTaskDescriptor.TYPE_ID); + taskConfig.setString(RepositoryTaskSupport.REPOSITORY_NAME_FIELD_ID, "*"); + try { + taskScheduler.submit(taskConfig); + } + catch (RuntimeException e) { + log.warn("Problem scheduling rebuild of repository indexes", e); + } + } + + @VisibleForTesting + OLogSequenceNumber loadCheckpoint() throws IOException { + try (InputStream in = new FileInputStream(checkpointFile)) { + byte[] nexusLsnBytes = ByteStreams.toByteArray(in); + if (Arrays.equals(nexusLsnBytes, INDEX_UPGRADE_MARKER.getBytes(StandardCharsets.UTF_8))) { + throw new UnknownDeltaException("Index upgrade indicator found in Elasticsearch marker file"); + } + return new OLogSequenceNumber(ByteStreams.newDataInput(nexusLsnBytes)); + } + } + + private void saveCheckpoint(final OLogSequenceNumber checkpoint) throws IOException { + try (DataOutputStream out = new DataOutputStream(new FileOutputStream(checkpointFile))) { + checkpoint.toStream(out); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/JsonUtils.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/JsonUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..0a8ce41425c898769f8eb08b5190900f484f0f89 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/JsonUtils.java @@ -0,0 +1,78 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.io.IOException; +import java.util.Iterator; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * JSON related utilities. + * + * @since 3.0 + */ +class JsonUtils +{ + private JsonUtils() { + // empty + } + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private static final ObjectWriter objectWriter = objectMapper.writerWithDefaultPrettyPrinter(); + + /** + * Converts any object to JSON. + */ + public static String from(final Object value) throws IOException { + return objectWriter.writeValueAsString(value); + } + + /** + * Merges json objects. + */ + public static String merge(final String... jsons) throws IOException { + JsonNode merged = objectMapper.createObjectNode(); + for (String json : jsons) { + merged = merge(merged, objectMapper.readTree(json)); + } + return objectWriter.writeValueAsString(merged); + } + + /** + * Merges json objects. + */ + public static JsonNode merge(final JsonNode mainNode, final JsonNode updateNode) { + Iterator fieldNames = updateNode.fieldNames(); + while (fieldNames.hasNext()) { + String fieldName = fieldNames.next(); + JsonNode jsonNode = mainNode.get(fieldName); + // if field exists and is an embedded object + if (jsonNode != null && jsonNode.isObject()) { + merge(jsonNode, updateNode.get(fieldName)); + } + else { + if (mainNode instanceof ObjectNode) { + // Overwrite field + JsonNode value = updateNode.get(fieldName); + ((ObjectNode) mainNode).set(fieldName, value); + } + } + } + return mainNode; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTask.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTask.java new file mode 100644 index 0000000000000000000000000000000000000000..7d5aa8db738ef82295517e399f14edf31fd3627c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTask.java @@ -0,0 +1,45 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import javax.inject.Named; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryTaskSupport; + +/** + * Internal task to rebuild index of given repository. + * + * @since 3.0 + */ +@Named +public class RebuildIndexTask + extends RepositoryTaskSupport +{ + + @Override + protected void execute(final Repository repository) { + repository.facet(SearchFacet.class).rebuildIndex(); + } + + @Override + protected boolean appliesTo(final Repository repository) { + return repository.optionalFacet(SearchFacet.class).isPresent(); + } + + @Override + public String getMessage() { + return "Rebuilding index of " + getRepositoryField(); + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTaskDescriptor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTaskDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..f2bf54c2bbab209de7d5c90e7b96406dcb59f712 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/RebuildIndexTaskDescriptor.java @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.formfields.RepositoryCombobox; +import org.sonatype.nexus.scheduling.TaskDescriptorSupport; + +/** + * Task descriptor for {@link RebuildIndexTask}. + * + * @since 3.0 + */ +@Named +@Singleton +public class RebuildIndexTaskDescriptor + extends TaskDescriptorSupport +{ + public static final String TYPE_ID = "repository.rebuild-index"; + + public static final String REPOSITORY_NAME_FIELD_ID = "repositoryName"; + + @Inject + public RebuildIndexTaskDescriptor(final NodeAccess nodeAccess) { + super(TYPE_ID, + RebuildIndexTask.class, + "Rebuild repository index", + VISIBLE, + EXPOSED, + new RepositoryCombobox( + REPOSITORY_NAME_FIELD_ID, + "Repository", + "Select the repository to rebuild index", + true + ).includingAnyOfFacets(SearchFacet.class).includeAnEntryForAllRepositories(), + + nodeAccess.isClustered() ? newMultinodeFormField().withInitialValue(true) : null + ); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchFacet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchFacet.java new file mode 100644 index 0000000000000000000000000000000000000000..9f6cb92db45d977c26d777cd557a4769b388a0db --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchFacet.java @@ -0,0 +1,58 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.repository.Facet; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.transaction.UnitOfWork; + +/** + * Search {@link Facet}, that index/de-index component metadata. + * + * @since 3.0 + */ +@Facet.Exposed +public interface SearchFacet + extends Facet +{ + /** + * Indexes the metadata of the given component, requires an active {@link UnitOfWork}. + */ + void put(EntityId componentId); + + /** + * Indexes the metadata of the given components, requires an active {@link UnitOfWork}. + * + * @since 3.4 + */ + void bulkPut(Iterable componentIds); + + /** + * De-indexes the metadata of the given component. + */ + void delete(EntityId componentId); + + /** + * De-indexes the metadata of the given components. + * + * @since 3.4 + */ + void bulkDelete(Iterable componentIds); + + /** + * Forcefully rebuilds index of the {@link Repository} this facet is attached to. Rebuild happens by dropping current + * index and recreating it from scratch. + */ + void rebuildIndex(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchFacetImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchFacetImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..273348c28ab79322b01019eee839623a6db907d9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchFacetImpl.java @@ -0,0 +1,207 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.util.Map; +import java.util.Objects; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.orient.entity.AttachedEntityId; +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.BucketEntityAdapter; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.repository.storage.StorageTx; +import org.sonatype.nexus.transaction.Transactional; +import org.sonatype.nexus.transaction.UnitOfWork; + +import com.google.common.collect.ImmutableMap; +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.record.impl.ODocument; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.transform; +import static org.sonatype.nexus.repository.FacetSupport.State.STARTED; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.REPOSITORY_NAME; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_BUCKET; + +/** + * Default {@link SearchFacet} implementation. + * + * Depends on presence of a {@link StorageFacet} attached to {@link Repository}. + * + * @since 3.0 + */ +@Named +public class SearchFacetImpl + extends FacetSupport + implements SearchFacet +{ + private static final String COMPONENTS_IN_BUCKET = String.format( + "select from %s where %s = :bucket", ComponentEntityAdapter.DB_CLASS, P_BUCKET); + + private final SearchService searchService; + + private final Map componentMetadataProducers; + + private final ComponentEntityAdapter componentEntityAdapter; + + private final BucketEntityAdapter bucketEntityAdapter; + + private Map repositoryMetadata; + + @Inject + public SearchFacetImpl(final SearchService searchService, + final Map componentMetadataProducers, + final ComponentEntityAdapter componentEntityAdapter, + final BucketEntityAdapter bucketEntityAdapter) + { + this.searchService = checkNotNull(searchService); + this.componentMetadataProducers = checkNotNull(componentMetadataProducers); + this.componentEntityAdapter = checkNotNull(componentEntityAdapter); + this.bucketEntityAdapter = checkNotNull(bucketEntityAdapter); + } + + @Override + protected void doInit(Configuration configuration) throws Exception { + repositoryMetadata = ImmutableMap.of(REPOSITORY_NAME, getRepository().getName()); + super.doInit(configuration); + } + + @Override + @Guarded(by = STARTED) + public void rebuildIndex() { + log.info("Rebuilding index of repository {}", getRepository().getName()); + searchService.rebuildIndex(getRepository()); + UnitOfWork.begin(facet(StorageFacet.class).txSupplier()); + try { + rebuildComponentIndex(); + } + finally { + UnitOfWork.end(); + } + } + + @Transactional + protected void rebuildComponentIndex() { + StorageTx tx = UnitOfWork.currentTx(); + Bucket bucket = tx.findBucket(getRepository()); + if (bucket != null) { + ORID bucketId = bucketEntityAdapter.recordIdentity(bucket); + Iterable docs = tx.browse(COMPONENTS_IN_BUCKET, ImmutableMap.of(P_BUCKET, bucketId)); + bulkPut(transform(filter(docs, Objects::nonNull), this::componentId)); + } + } + + @Override + @Guarded(by = STARTED) + public void put(final EntityId componentId) { + checkNotNull(componentId); + String json = json(componentId); + if (json != null) { + searchService.put(getRepository(), identifier(componentId), json); + } + } + + @Override + @Guarded(by = STARTED) + public void bulkPut(final Iterable componentIds) { + checkNotNull(componentIds); + searchService.bulkPut(getRepository(), componentIds, this::identifier, this::json); + } + + @Override + @Guarded(by = STARTED) + public void delete(final EntityId componentId) { + checkNotNull(componentId); + searchService.delete(getRepository(), identifier(componentId)); + } + + @Override + @Guarded(by = STARTED) + public void bulkDelete(final Iterable componentIds) { + checkNotNull(componentIds); + searchService.bulkDelete(getRepository(), transform(componentIds, this::identifier)); + } + + @Override + protected void doStart() throws Exception { + searchService.createIndex(getRepository()); + } + + @Override + protected void doDelete() { + searchService.deleteIndex(getRepository()); + } + + /** + * Converts a component's Orient record id to its {@link EntityId}. + */ + @Nullable + private EntityId componentId(@Nullable final ODocument doc) { + return doc != null ? new AttachedEntityId(componentEntityAdapter, doc.getIdentity()) : null; + } + + /** + * Looks for a {@link ComponentMetadataProducer} specific to the component {@link Format}. + * If one is not available will use a default one ({@link DefaultComponentMetadataProducer}). + */ + private ComponentMetadataProducer producer(final Component component) { + checkNotNull(component); + String format = component.format(); + ComponentMetadataProducer producer = componentMetadataProducers.get(format); + if (producer == null) { + producer = componentMetadataProducers.get("default"); + } + checkState(producer != null, "Could not find a component metadata producer for format: %s", format); + return producer; + } + + /** + * Returns the document identifier in the repository's index for the given component. + */ + @Nullable + private String identifier(@Nullable final EntityId componentId) { + return componentId != null ? componentId.getValue() : null; + } + + /** + * Returns the JSON document representing the given component in the repository's index. + */ + @Nullable + @Transactional + protected String json(@Nullable final EntityId componentId) { + if (componentId != null) { + StorageTx tx = UnitOfWork.currentTx(); + Component component = componentEntityAdapter.read(tx.getDb(), componentId); + if (component != null) { + Iterable assets = tx.browseAssets(component); + return producer(component).getMetadata(component, assets, repositoryMetadata); + } + } + return null; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchService.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchService.java new file mode 100644 index 0000000000000000000000000000000000000000..19ef4fdefde6f3c04183598efeefc68f82f28e0a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchService.java @@ -0,0 +1,137 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.util.Collection; +import java.util.List; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.Repository; + +import com.google.common.base.Function; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.aggregations.AggregationBuilder; +import org.elasticsearch.search.sort.SortBuilder; + +/** + * Search service: maintains indexes for repositories and performs indexing/deindexing of data, along with search. + * + * @since 3.0 + */ +public interface SearchService +{ + + /** + * Create index for specified repository, if does not already exits. + */ + void createIndex(Repository repository); + + /** + * Deletes index for specified repository. + */ + void deleteIndex(Repository repository); + + /** + * Rebuilds index for specific reppsitory. + */ + void rebuildIndex(Repository repository); + + /** + * Puts data with given identifier into index of given repository. + */ + void put(Repository repository, String identifier, String json); + + /** + * Operation used for bulk updating of component index. + * + * @param repository the source repository + * @param components an {@link Iterable} of components to index + * @param identifierProducer a function producing an identifier for a component (never returning null) + * @param jsonDocumentProducer a function producing a json document for the component (never returning null) + * + * @since 3.4 + */ + void bulkPut(Repository repository, Iterable components, + Function identifierProducer, + Function jsonDocumentProducer); + + /** + * Removes data with given identifier from index of given repository. + */ + void delete(Repository repository, String identifier); + + /** + * Operation used for bulk removal of data from index of given repository. + * + * @param repository the source repository (if known) + * @param identifiers the ids of documents to remove + * + * @since 3.4 + */ + void bulkDelete(@Nullable Repository repository, Iterable identifiers); + + /** + * Search component metadata and browse results, without the effect of content selectors. + * + * @since 3.1 + */ + Iterable browseUnrestricted(QueryBuilder query); + + /** + * Search component metadata in a specified repository and browse results, without the effect of content selectors + * + * @since 3.4 + */ + Iterable browseUnrestrictedInRepos(QueryBuilder query, Collection repoNames); + + /** + * Search component metadata and browse results (paged), without the effect of content selectors. + * + * @since 3.1 + */ + SearchResponse searchUnrestricted(QueryBuilder query, @Nullable List sort, int from, int size); + + /** + * Search component metadata and browse results (paged) with content selectors applied. + * + * @since 3.1 + */ + SearchResponse search(QueryBuilder query, @Nullable List sort, int from, int size); + + /** + * Search component metadata and browse results using aggregations with content selectors applied. + * + * @since 3.7 + */ + SearchResponse searchInReposWithAggregations(QueryBuilder query, + List aggregations, + Collection repoNames); + + /** + * Count the number of results for a given query, without the effect of content selectors. + * + * @since 3.1 + */ + long countUnrestricted(QueryBuilder query); + + /** + * Flush any pending bulk index requests. + * + * @since 3.4 + */ + void flush(); + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchServiceImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..248f998111246f7855601a3f513fce52a8ce0e8d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchServiceImpl.java @@ -0,0 +1,651 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; +import java.util.stream.StreamSupport; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.search.SearchSubjectHelper.SubjectRegistration; +import org.sonatype.nexus.repository.security.RepositoryViewPermission; +import org.sonatype.nexus.repository.selector.internal.ContentAuthPluginScriptFactory; +import org.sonatype.nexus.security.SecurityHelper; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.collect.Collections2; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.io.Resources; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.validate.query.QueryExplanation; +import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryResponse; +import org.elasticsearch.action.bulk.BulkProcessor; +import org.elasticsearch.action.delete.DeleteResponse; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.search.SearchRequestBuilder; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.search.ShardSearchFailure; +import org.elasticsearch.client.Client; +import org.elasticsearch.client.IndicesAdminClient; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.IndexNotFoundException; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.indices.IndexAlreadyExistsException; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.aggregations.AggregationBuilder; +import org.elasticsearch.search.internal.InternalSearchResponse; +import org.elasticsearch.search.profile.ProfileShardResult; +import org.elasticsearch.search.sort.SortBuilder; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.elasticsearch.index.query.QueryBuilders.idsQuery; +import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA1; +import static org.sonatype.nexus.security.BreadActions.BROWSE; + +/** + * Default {@link SearchService} implementation. It does not expects that {@link Repository} have storage facet + * attached. + * + * @since 3.0 + */ +@Named +@Singleton +public class SearchServiceImpl + extends ComponentSupport + implements SearchService +{ + private static final String TYPE = "component"; + + /** + * Resource name of ElasticSearch mapping configuration. + */ + public static final String MAPPING_JSON = "elasticsearch-mapping.json"; + + private static final SearchResponse EMPTY_SEARCH_RESPONSE = new SearchResponse(InternalSearchResponse.empty(), null, 0, + 0, 0, new ShardSearchFailure[]{}); + + private final Provider client; + + private final RepositoryManager repositoryManager; + + private final SecurityHelper securityHelper; + + private final SearchSubjectHelper searchSubjectHelper; + + private final List indexSettingsContributors; + + private final ConcurrentMap repositoryNameMapping; + + private final boolean profile; + + private final boolean periodicFlush; + + private BulkProcessor bulkProcessor; + + /** + * @param client source for a {@link Client} + * @param repositoryManager the repositoryManager + * @param securityHelper the securityHelper + * @param searchSubjectHelper the searchSubjectHelper + * @param indexSettingsContributors the indexSetttingsContributors + * @param profile whether or not to profile elasticsearch queries (default: false) + * @param bulkCapacity how many bulk requests to batch before they're automatically flushed (default: 1000) + * @param concurrentRequests how many bulk requests to execute concurrently (default: 1; 0 means execute synchronously) + * @param flushInterval how long to wait in milliseconds between flushing bulk requests (default: 0, instantaneous) + */ + @Inject + public SearchServiceImpl(final Provider client, //NOSONAR + final RepositoryManager repositoryManager, + final SecurityHelper securityHelper, + final SearchSubjectHelper searchSubjectHelper, + final List indexSettingsContributors, + @Named("${nexus.elasticsearch.profile:-false}") final boolean profile, + @Named("${nexus.elasticsearch.bulkCapacity:-1000}") final int bulkCapacity, + @Named("${nexus.elasticsearch.concurrentRequests:-1}") final int concurrentRequests, + @Named("${nexus.elasticsearch.flushInterval:-0}") final int flushInterval) + { + this.client = checkNotNull(client); + this.repositoryManager = checkNotNull(repositoryManager); + this.securityHelper = checkNotNull(securityHelper); + this.searchSubjectHelper = checkNotNull(searchSubjectHelper); + this.indexSettingsContributors = checkNotNull(indexSettingsContributors); + this.repositoryNameMapping = Maps.newConcurrentMap(); + this.profile = profile; + this.periodicFlush = flushInterval > 0; + + this.bulkProcessor = BulkProcessor + .builder(client.get(), new BulkIndexUpdateListener()) + .setBulkActions(bulkCapacity) + .setBulkSize(new ByteSizeValue(-1)) // turn off automatic flush based on size in bytes + .setConcurrentRequests(concurrentRequests) + .setFlushInterval(periodicFlush ? TimeValue.timeValueMillis(flushInterval) : null) + .build(); + } + + @Override + public void flush() { + log.debug("Flushing index requests"); + bulkProcessor.flush(); + } + + @Override + public void createIndex(final Repository repository) { + checkNotNull(repository); + final String safeIndexName = SHA1.function().hashUnencodedChars(repository.getName()).toString(); + log.debug("Creating index for {}", repository); + createIndex(repository, safeIndexName); + } + + private void createIndex(final Repository repository, final String indexName) { + // TODO we should calculate the checksum of index settings and compare it with a value stored in index _meta tags + // in case that they not match (settings changed) we should drop the index, recreate it and re-index all components + IndicesAdminClient indices = indicesAdminClient(); + if (!indices.prepareExists(indexName).execute().actionGet().isExists()) { + // determine list of mapping configuration urls + List urls = Lists.newArrayListWithExpectedSize(indexSettingsContributors.size() + 1); + urls.add(Resources.getResource(getClass(), MAPPING_JSON)); // core mapping + for (IndexSettingsContributor contributor : indexSettingsContributors) { + URL url = contributor.getIndexSettings(repository); + if (url != null) { + urls.add(url); + } + } + + try { + // merge all mapping configuration + String source = "{}"; + for (URL url : urls) { + log.debug("Merging ElasticSearch mapping: {}", url); + String contributed = Resources.toString(url, StandardCharsets.UTF_8); + log.trace("Contributed ElasticSearch mapping: {}", contributed); + source = JsonUtils.merge(source, contributed); + } + // update runtime configuration + log.trace("ElasticSearch mapping: {}", source); + indices.prepareCreate(indexName) + .setSource(source) + .execute() + .actionGet(); + } + catch (IndexAlreadyExistsException e) { + log.debug("Using existing index for {}", repository, e); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + repositoryNameMapping.put(repository.getName(), indexName); + } + + @Override + public void deleteIndex(final Repository repository) { + checkNotNull(repository); + String indexName = repositoryNameMapping.remove(repository.getName()); + if (indexName != null) { + log.debug("Removing index of {}", repository); + deleteIndex(indexName); + } + } + + private void deleteIndex(final String indexName) { + bulkProcessor.flush(); // make sure dangling requests don't resurrect this index + IndicesAdminClient indices = indicesAdminClient(); + if (indices.prepareExists(indexName).execute().actionGet().isExists()) { + indices.prepareDelete(indexName).execute().actionGet(); + } + } + + public void rebuildIndex(final Repository repository) { + checkNotNull(repository); + String indexName = repositoryNameMapping.remove(repository.getName()); + if (indexName != null) { + log.debug("Rebuilding index for {}", repository); + deleteIndex(indexName); + createIndex(repository, indexName); + } + } + + @Override + public void put(final Repository repository, final String identifier, final String json) { + checkNotNull(repository); + checkNotNull(identifier); + checkNotNull(json); + String indexName = repositoryNameMapping.get(repository.getName()); + if (indexName == null) { + return; + } + log.debug("Adding to index document {} from {}: {}", identifier, repository, json); + client.get().prepareIndex(indexName, TYPE, identifier).setSource(json).execute( + new ActionListener() { + @Override + public void onResponse(final IndexResponse indexResponse) { + log.debug("successfully added {} {} to index {}", TYPE, identifier, indexName, indexResponse); + } + @Override + public void onFailure(final Throwable e) { + log.error( + "failed to add {} {} to index {}; this is a sign that the Elasticsearch index thread pool is overloaded", + TYPE, identifier, indexName, e); + } + }); + } + + @Override + public void bulkPut(final Repository repository, final Iterable components, + final Function identifierProducer, + final Function jsonDocumentProducer) { + checkNotNull(repository); + checkNotNull(components); + String indexName = repositoryNameMapping.get(repository.getName()); + if (indexName == null) { + return; + } + + components.forEach(component -> { + String identifier = identifierProducer.apply(component); + String json = jsonDocumentProducer.apply(component); + if (json != null) { + bulkProcessor.add( + client.get() + .prepareIndex(indexName, TYPE, identifier) + .setSource(json).request() + ); + } + }); + + if (!periodicFlush) { + bulkProcessor.flush(); + } + } + + @Override + public void delete(final Repository repository, final String identifier) { + checkNotNull(repository); + checkNotNull(identifier); + String indexName = repositoryNameMapping.get(repository.getName()); + if (indexName == null) { + return; + } + log.debug("Removing from index document {} from {}", identifier, repository); + client.get().prepareDelete(indexName, TYPE, identifier).execute(new ActionListener() { + @Override + public void onResponse(final DeleteResponse deleteResponse) { + log.debug("successfully removed {} {} from index {}", TYPE, identifier, indexName, deleteResponse); + } + @Override + public void onFailure(final Throwable e) { + log.error( + "failed to remove {} {} from index {}; this is a sign that the Elasticsearch index thread pool is overloaded", + TYPE, identifier, indexName, e); + } + }); + } + + @Override + public void bulkDelete(@Nullable final Repository repository, final Iterable identifiers) { + checkNotNull(identifiers); + + if (repository != null) { + String indexName = repositoryNameMapping.get(repository.getName()); + if (indexName == null) { + return; // index has gone, nothing to delete + } + + identifiers.forEach(id -> + bulkProcessor.add(client.get().prepareDelete(indexName, TYPE, id).request())); + } + else { + + // When bulk-deleting documents based on the write-ahead-log we won't have the owning index. + // Since delete is a single-index operation we need to discover the index for each identifier + // before we can delete its document. Chunking is used to keep queries to a reasonable size. + + Iterables.partition(identifiers, 100).forEach(chunk -> { + SearchResponse toDelete = client.get() + .prepareSearch("_all") + .setFetchSource(false) + .setQuery(idsQuery(TYPE).ids(chunk)) + .setSize(chunk.size()) + .execute() + .actionGet(); + + toDelete.getHits().forEach(hit -> + bulkProcessor.add(client.get().prepareDelete(hit.index(), TYPE, hit.getId()).request())); + }); + } + + if (!periodicFlush) { + bulkProcessor.flush(); + } + } + + @Override + public Iterable browseUnrestricted(final QueryBuilder query) { + return browseUnrestrictedInRepos(query, null); + } + + @Override + public Iterable browseUnrestrictedInRepos(final QueryBuilder query, + @Nullable final Collection repoNames) { + checkNotNull(query); + try { + if (!indicesAdminClient().prepareValidateQuery().setQuery(query).execute().actionGet().isValid()) { + throw new IllegalArgumentException("Invalid query"); + } + } + catch (IndexNotFoundException e) { + // no repositories were created yet, so there is no point in searching + return null; + } + final String[] searchableIndexes = getSearchableIndexes(false, repoNames); + if (searchableIndexes.length == 0) { + return Collections.emptyList(); + } + return new Iterable() + { + @Override + public Iterator iterator() { + return new Iterator() + { + private SearchResponse response; + + private Iterator iterator; + + private boolean noMoreHits = false; + + @Override + public boolean hasNext() { + if (noMoreHits) { + return false; + } + if (response == null) { + response = client.get().prepareSearch(searchableIndexes) + .setTypes(TYPE) + .setQuery(query) + .setScroll(new TimeValue(1, TimeUnit.MINUTES)) + .setSize(100) + .execute() + .actionGet(); + iterator = Arrays.asList(response.getHits().getHits()).iterator(); + noMoreHits = !iterator.hasNext(); + } + else if (!iterator.hasNext()) { + response = client.get().prepareSearchScroll(response.getScrollId()) + .setScroll(new TimeValue(1, TimeUnit.MINUTES)) + .execute() + .actionGet(); + iterator = Arrays.asList(response.getHits().getHits()).iterator(); + noMoreHits = !iterator.hasNext(); + } + return iterator.hasNext(); + } + + @Override + public SearchHit next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return iterator.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + @Override + public SearchResponse searchUnrestricted(final QueryBuilder query, + @Nullable final List sort, + final int from, + final int size) + { + if (!validateQuery(query)) { + return EMPTY_SEARCH_RESPONSE; + } + final String[] searchableIndexes = getSearchableIndexes(false); + if (searchableIndexes.length == 0) { + return EMPTY_SEARCH_RESPONSE; + } + + return executeSearch(query, searchableIndexes, from, size, sort, null); + } + + @Override + public SearchResponse search(final QueryBuilder query, + @Nullable final List sort, + final int from, + final int size) + { + if (!validateQuery(query)) { + return EMPTY_SEARCH_RESPONSE; + } + final String[] searchableIndexes = getSearchableIndexes(true); + if (searchableIndexes.length == 0) { + return EMPTY_SEARCH_RESPONSE; + } + + try (SubjectRegistration registration = searchSubjectHelper.register(securityHelper.subject())) { + return executeSearch(query, searchableIndexes, from, size, sort, + QueryBuilders.scriptQuery(ContentAuthPluginScriptFactory.newScript(registration.getId()))); + } + } + + @Override + public SearchResponse searchInReposWithAggregations(final QueryBuilder query, + final List aggregations, + final Collection repoNames) + { + if (!validateQuery(query)) { + return EMPTY_SEARCH_RESPONSE; + } + final String[] searchableIndexes = getSearchableIndexes(true, repoNames); + if (searchableIndexes.length == 0) { + return EMPTY_SEARCH_RESPONSE; + } + + try (SubjectRegistration registration = searchSubjectHelper.register(securityHelper.subject())) { + return executeSearch(query, aggregations, searchableIndexes, + QueryBuilders.scriptQuery(ContentAuthPluginScriptFactory.newScript(registration.getId()))); + } + } + + private SearchResponse executeSearch(final QueryBuilder query, + final String[] searchableIndexes, + final int from, + final int size, + @Nullable final List sort, + @Nullable final QueryBuilder postFilter) + { + checkNotNull(query); + checkNotNull(searchableIndexes); + SearchRequestBuilder searchRequestBuilder = client.get().prepareSearch(searchableIndexes) + .setTypes(TYPE) + .setQuery(query) + .setFrom(from) + .setSize(size) + .setProfile(profile); + if (postFilter != null) { + searchRequestBuilder.setPostFilter(postFilter); + } + if (sort != null) { + for (SortBuilder entry : sort) { + searchRequestBuilder.addSort(entry); + } + } + SearchResponse searchResponse = searchRequestBuilder.execute().actionGet(); + + if (profile) { + logProfileResults(searchResponse); + } + + return searchResponse; + } + + private SearchResponse executeSearch(final QueryBuilder query, + final List aggregations, + final String[] searchableIndexes, + @Nullable final QueryBuilder postFilter) + { + checkNotNull(query); + checkNotNull(aggregations); + checkNotNull(searchableIndexes); + + SearchRequestBuilder searchRequestBuilder = client.get().prepareSearch(searchableIndexes) + .setTypes(TYPE) + .setQuery(query) + .setFrom(0) + .setSize(0) + .setProfile(profile) + .setTrackScores(true); + + for (AggregationBuilder aggregation : aggregations) { + searchRequestBuilder.addAggregation(aggregation); + } + + if (postFilter != null) { + searchRequestBuilder.setPostFilter(postFilter); + } + + SearchResponse searchResponse = searchRequestBuilder.execute().actionGet(); + + if (profile) { + logProfileResults(searchResponse); + } + + return searchResponse; + } + + @Override + public long countUnrestricted(final QueryBuilder query) { + if(!validateQuery(query)) { + return 0; + } + final String[] searchableIndexes = getSearchableIndexes(false); + if (searchableIndexes.length == 0) { + return 0; + } + SearchRequestBuilder count = client.get().prepareSearch(searchableIndexes).setQuery(query).setSize(0); + SearchResponse response = count.execute().actionGet(); + return response.getHits().totalHits(); + } + + private boolean validateQuery(QueryBuilder query) { + checkNotNull(query); + try { + ValidateQueryResponse validateQueryResponse = indicesAdminClient().prepareValidateQuery() + .setQuery(query).setExplain(true).execute().actionGet(); + if (!validateQueryResponse.isValid()) { + if (log.isDebugEnabled()) { + Collection explanations = Collections2.transform(validateQueryResponse.getQueryExplanation(), + new Function() + { + @Nullable + @Override + public String apply(final QueryExplanation input) { + return input.getExplanation() != null ? input.getExplanation() : input.getError(); + } + }); + log.debug("Invalid query explanation: {}", explanations); + } + throw new IllegalArgumentException("Invalid query"); + } + } + catch (IndexNotFoundException e) { + // no repositories were created yet, so there is no point in searching + return false; + } + return true; + } + + private String[] getSearchableIndexes(final boolean skipPermissionCheck) { + return getSearchableIndexes(skipPermissionCheck, null); + } + + @VisibleForTesting + String[] getSearchableIndexes(final boolean skipPermissionCheck, final Collection repoChoices) { + Predicate repoChoicesFilter = repoChoices == null ? s -> true : repoChoices::contains; + return StreamSupport.stream(repositoryManager.browse().spliterator(), false) + .map(repo -> { + if (repoOnlineAndHasSearchFacet(repo) && (repositoryNameMapping.containsKey(repo.getName())) + && (skipPermissionCheck || securityHelper.allPermitted(new RepositoryViewPermission(repo, BROWSE)))) { + return repo.getName(); + } + return null; + }) + .filter(Objects::nonNull) + .filter(repoChoicesFilter) + .map(repositoryNameMapping::get) + .toArray(String[]::new); + } + + private static boolean repoOnlineAndHasSearchFacet(Repository repo) { + return repo.optionalFacet(SearchFacet.class).isPresent() && repo.getConfiguration().isOnline(); + } + + /** + * Returns the indixes admin client. + */ + private IndicesAdminClient indicesAdminClient() { + return client.get().admin().indices(); + } + + private void logProfileResults(final SearchResponse searchResponse) { + for (Entry> entry : searchResponse.getProfileResults().entrySet()) { + for (ProfileShardResult profileShardResult : entry.getValue()) { + try { + XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); + builder.startObject(); + profileShardResult.toXContent(builder, ToXContent.EMPTY_PARAMS); + builder.endObject(); + log.info("Elasticsearch profile for {} is: {}", entry.getKey(), builder.string()); + } + catch (IOException e) { + log.error("Error writing elasticsearch profile result", e); + } + } + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchSubjectHelper.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchSubjectHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..a4f2a37ef0df84814d83b656149cc5800fa86b1f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/SearchSubjectHelper.java @@ -0,0 +1,93 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; + +import com.google.common.annotations.VisibleForTesting; +import org.apache.shiro.subject.Subject; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Helper class used to temporarily associate a subject with a key for purposes of passing Shiro {@link Subject}s into + * Elasticsearch. The caller is provided with a {@link SubjectRegistration} that should typically be used as part of a + * try-with-resources block. + * + * @since 3.1 + */ +@Named +@Singleton +public class SearchSubjectHelper + extends ComponentSupport +{ + @VisibleForTesting + final Map subjects = new ConcurrentHashMap<>(); + + /** + * Registers the subject, returning a {@link SubjectRegistration}. + */ + public SubjectRegistration register(final Subject subject) { + checkNotNull(subject); + String uuid = UUID.randomUUID().toString(); + if (subjects.putIfAbsent(uuid, subject) != null) { + throw new IllegalStateException("Duplicate UUID: " + uuid); + } + return new SubjectRegistration(uuid); + } + + /** + * Gets the subject associated with the specified ID if present, throwing an exception if no subject exists. + */ + public Subject getSubject(final String subjectId) { + checkNotNull(subjectId); + return checkNotNull(subjects.get(subjectId), "No subject for ID %s", subjectId); + } + + /** + * Unregisters the subject with the specified ID. + */ + private void unregister(final String subjectId) { + checkNotNull(subjectId); + subjects.remove(subjectId); + } + + /** + * {@link AutoCloseable} class demarcating the registration and unregistration of a subject. + */ + public class SubjectRegistration + implements AutoCloseable + { + private final String id; + + public SubjectRegistration(final String id) { + this.id = checkNotNull(id); + } + + public String getId() { + return id; + } + + @Override + public void close() { + unregister(id); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/internal/ElasticSearchIndexCheckpoint.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/internal/ElasticSearchIndexCheckpoint.java new file mode 100644 index 0000000000000000000000000000000000000000..131dd880379bfc30e754ab5da1e0b6e8d12df89c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/internal/ElasticSearchIndexCheckpoint.java @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search.internal; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.upgrade.Checkpoint; +import org.sonatype.nexus.common.upgrade.Checkpoints; + +/** + * Upgrade checkpoint for elasticsearch index. It exists for the sole purpose of forcing a reindexing of all + * repositories to pull in new attributes/elements. + * + * @since 3.6.1 + */ +@Named +@Singleton +@Checkpoints(model = ElasticSearchIndexCheckpoint.MODEL, local = true) +public class ElasticSearchIndexCheckpoint + implements Checkpoint +{ + static final String MODEL = "elasticsearch"; + + @Override + public void begin(final String version) throws Exception { + //no-op + } + + @Override + public void commit() throws Exception { + // no-op + } + + @Override + public void rollback() throws Exception { + // no-op + } + + @Override + public void end() { + //no-op + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/internal/ElasticSearchIndexUpgrade.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/internal/ElasticSearchIndexUpgrade.java new file mode 100644 index 0000000000000000000000000000000000000000..187ab95f4301f6eb786f96207ae7559de6642909 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/internal/ElasticSearchIndexUpgrade.java @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search.internal; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.common.upgrade.Upgrade; +import org.sonatype.nexus.common.upgrade.Upgrades; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.search.IndexSyncService.INDEX_UPGRADE_MARKER; + +/** + * Updates the $data-dir/elasticsearch/nexus.lsn file with a reindex marker string to + * trigger the IndexSyncService service to update all indexes. + * + * @since 3.6.1 + */ +@Named +@Singleton +@Upgrades(model = ElasticSearchIndexCheckpoint.MODEL, from = "1.0", to = "1.1") +public class ElasticSearchIndexUpgrade + extends ComponentSupport + implements Upgrade +{ + private File nexusLsnFile; + + @Inject + public ElasticSearchIndexUpgrade(final ApplicationDirectories applicationDirectories) { + checkNotNull(applicationDirectories); + this.nexusLsnFile = new File(applicationDirectories.getWorkDirectory("elasticsearch"), "nexus.lsn"); + } + + @Override + public void apply() throws Exception { + writeReindexMarker(); + } + + private void writeReindexMarker() throws IOException { + log.info("Write reindex marker to Elasticsearch marker file"); + try (DataOutputStream out = new DataOutputStream(new FileOutputStream(nexusLsnFile))) { + out.writeBytes(INDEX_UPGRADE_MARKER); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..48264898f8850cd077798cba7b0d9d74fcff5b84 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/search/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Repository search-facet and support. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.search; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/ContentPermissionChecker.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/ContentPermissionChecker.java new file mode 100644 index 0000000000000000000000000000000000000000..7ac5f49b35b2ff4df7b82fe930317fe4d57a373d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/ContentPermissionChecker.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import org.sonatype.nexus.selector.VariableSource; + +/** + * Repository content permission checker + * + * @since 3.1 + */ +public interface ContentPermissionChecker +{ + /** + * Ensure that either the view permission or the content selector permission is permitted + */ + boolean isPermitted(String repositoryName, + String repositoryFormat, + String action, + VariableSource variableSource); + + /** + * Ensure that either the view permission or that a JEXL content selector permission is permitted + * + * @since 3.6 + */ + boolean isPermittedJexlOnly(String repositoryName, + String repositoryFormat, + String action, + VariableSource variableSource); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryAdminPermission.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryAdminPermission.java new file mode 100644 index 0000000000000000000000000000000000000000..99d32e4b28a9973e388f4ac48a84ed1df40e9aee --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryAdminPermission.java @@ -0,0 +1,66 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import java.util.List; + +import org.sonatype.nexus.security.authz.WildcardPermission2; + +import com.google.common.base.Joiner; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Repository administration permission. + * + * @since 3.0 + */ +public class RepositoryAdminPermission + extends WildcardPermission2 +{ + public static final String SYSTEM = "nexus"; + + public static final String DOMAIN = "repository-admin"; + + private final String format; + + private final String name; + + private final List actions; + + public RepositoryAdminPermission(final String format, final String name, final List actions) { + this.format = checkNotNull(format); + this.name = checkNotNull(name); + this.actions = checkNotNull(actions); + + setParts(Joiner.on(':').join( + SYSTEM, + DOMAIN, + format, + name, + Joiner.on(',').join(actions) + )); + } + + public String getFormat() { + return format; + } + + public String getName() { + return name; + } + + public List getActions() { + return actions; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryAdminPrivilegeDescriptor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryAdminPrivilegeDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..f8217638085db565c0636b960af7452626d2d8c4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryAdminPrivilegeDescriptor.java @@ -0,0 +1,146 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import java.util.List; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.i18n.I18N; +import org.sonatype.goodies.i18n.MessageBundle; +import org.sonatype.nexus.formfields.FormField; +import org.sonatype.nexus.formfields.RepositoryCombobox; +import org.sonatype.nexus.formfields.StringTextFormField; +import org.sonatype.nexus.security.config.CPrivilege; +import org.sonatype.nexus.security.config.CPrivilegeBuilder; +import org.sonatype.nexus.security.privilege.PrivilegeDescriptor; +import org.sonatype.nexus.security.privilege.PrivilegeDescriptorSupport; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import org.apache.shiro.authz.Permission; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * Repository admin {@link PrivilegeDescriptor}. + * + * @see RepositoryAdminPermission + * @since 3.0 + */ +@Named(RepositoryAdminPrivilegeDescriptor.TYPE) +@Singleton +public class RepositoryAdminPrivilegeDescriptor + extends PrivilegeDescriptorSupport +{ + public static final String TYPE = RepositoryAdminPermission.DOMAIN; + + public static final String P_FORMAT = "format"; + + public static final String P_REPOSITORY = "repository"; + + public static final String P_ACTIONS = "actions"; + + private interface Messages + extends MessageBundle + { + @DefaultMessage("Repository Admin") + String name(); + + @DefaultMessage("Format") + String format(); + + @DefaultMessage("The format(s) for the repository") + String formatHelp(); + + @DefaultMessage("Repository") + String repository(); + + @DefaultMessage("The repository name") + String repositoryHelp(); + + @DefaultMessage("Actions") + String actions(); + + @DefaultMessage("The comma-delimited list of actions") + String actionsHelp(); + } + + private static final Messages messages = I18N.create(Messages.class); + + private final List formFields; + + public RepositoryAdminPrivilegeDescriptor() { + super(TYPE); + this.formFields = ImmutableList.of( + new StringTextFormField( + P_FORMAT, + messages.format(), + messages.formatHelp(), + FormField.MANDATORY + ), + new RepositoryCombobox( + P_REPOSITORY, + messages.repository(), + messages.repositoryHelp(), + true + ).includeAnEntryForAllRepositories(), + new StringTextFormField( + P_ACTIONS, + messages.actions(), + messages.actionsHelp(), + FormField.MANDATORY + ) + ); + } + + @Override + public Permission createPermission(final CPrivilege privilege) { + assert privilege != null; + String format = readProperty(privilege, P_FORMAT, ALL); + String name = readProperty(privilege, P_REPOSITORY, ALL); + List actions = readListProperty(privilege, P_ACTIONS, ALL); + return new RepositoryAdminPermission(format, name, actions); + } + + @Override + public List getFormFields() { + return formFields; + } + + @Override + public String getName() { + return messages.name(); + } + + // + // Helpers + // + + public static String id(final String format, final String name, final String... actions) { + return String.format("nx-%s-%s-%s-%s", TYPE, format, name, Joiner.on(',').join(actions)); + } + + public static CPrivilege privilege(final String format, final String name, final String... actions) { + checkArgument(actions.length > 0); + return new CPrivilegeBuilder() + .type(TYPE) + .id(id(format, name, actions)) + .description(String.format("%s for %s repository administration", humanizeActions(actions), humanizeName(name, format))) + .property(P_FORMAT, format) + .property(P_REPOSITORY, name) + .property(P_ACTIONS, actions) + .create(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryContentSelectorPermission.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryContentSelectorPermission.java new file mode 100644 index 0000000000000000000000000000000000000000..b4ef7f16d137df1b1dee529f5b5631b47c0f140d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryContentSelectorPermission.java @@ -0,0 +1,74 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import java.util.List; + +import org.sonatype.nexus.security.authz.WildcardPermission2; + +import com.google.common.base.Joiner; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Repository content selector permission. + * + * @since 3.1 + */ +public class RepositoryContentSelectorPermission + extends WildcardPermission2 +{ + public static final String SYSTEM = "nexus"; + + public static final String DOMAIN = "repository-content-selector"; + + private final String selector; + + private final String format; + + private final String name; + + private final List actions; + + public RepositoryContentSelectorPermission(final String selector, + final String format, + final String name, + final List actions) + { + this.selector = checkNotNull(selector); + this.format = checkNotNull(format); + this.name = checkNotNull(name); + this.actions = checkNotNull(actions); + + setParts(Joiner.on(':').join( + SYSTEM, + DOMAIN, + selector, + format, + name, + Joiner.on(',').join(actions) + )); + } + + public String getFormat() { + return format; + } + + public String getName() { + return name; + } + + public List getActions() { + return actions; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryContentSelectorPrivilegeDescriptor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryContentSelectorPrivilegeDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..4d649c5b3c6a0816cd578c5e6afad2af8442c82b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryContentSelectorPrivilegeDescriptor.java @@ -0,0 +1,159 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import java.util.List; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.i18n.I18N; +import org.sonatype.goodies.i18n.MessageBundle; +import org.sonatype.nexus.formfields.FormField; +import org.sonatype.nexus.formfields.RepositoryCombobox; +import org.sonatype.nexus.formfields.SelectorComboFormField; +import org.sonatype.nexus.formfields.StringTextFormField; +import org.sonatype.nexus.security.config.CPrivilege; +import org.sonatype.nexus.security.config.CPrivilegeBuilder; +import org.sonatype.nexus.security.privilege.PrivilegeDescriptor; +import org.sonatype.nexus.security.privilege.PrivilegeDescriptorSupport; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import org.apache.shiro.authz.Permission; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * Repository selector {@link PrivilegeDescriptor}. + * + * @see RepositoryContentSelectorPermission + * @since 3.1 + */ +@Named(RepositoryContentSelectorPrivilegeDescriptor.TYPE) +@Singleton +public class RepositoryContentSelectorPrivilegeDescriptor + extends PrivilegeDescriptorSupport +{ + public static final String TYPE = RepositoryContentSelectorPermission.DOMAIN; + + public static final String P_CONTENT_SELECTOR = "contentSelector"; + + public static final String P_REPOSITORY = "repository"; + + public static final String P_ACTIONS = "actions"; + + private interface Messages + extends MessageBundle + { + @DefaultMessage("Repository Content Selector") + String name(); + + @DefaultMessage("Content Selector") + String contentSelector(); + + @DefaultMessage("The content selector for the repository") + String contentSelectorHelp(); + + @DefaultMessage("Repository") + String repository(); + + @DefaultMessage("The repository or repositories to grant access") + String repositoryHelp(); + + @DefaultMessage("Actions") + String actions(); + + @DefaultMessage("The comma-delimited list of actions") + String actionsHelp(); + } + + private static final Messages messages = I18N.create(Messages.class); + + private final List formFields; + + public RepositoryContentSelectorPrivilegeDescriptor() { + super(TYPE); + this.formFields = ImmutableList.of( + new SelectorComboFormField( + P_CONTENT_SELECTOR, + messages.contentSelector(), + messages.contentSelectorHelp(), + FormField.MANDATORY + ), + new RepositoryCombobox( + P_REPOSITORY, + messages.repository(), + messages.repositoryHelp(), + true + ).includeEntriesForAllFormats(), + new StringTextFormField( + P_ACTIONS, + messages.actions(), + messages.actionsHelp(), + FormField.MANDATORY + ) + ); + } + + @Override + public Permission createPermission(final CPrivilege privilege) { + assert privilege != null; + String contentSelector = readProperty(privilege, P_CONTENT_SELECTOR, ALL); + String name = readProperty(privilege, P_REPOSITORY, ALL); + List actions = readListProperty(privilege, P_ACTIONS, ALL); + RepositorySelector selector = RepositorySelector.fromSelector(name); + return new RepositoryContentSelectorPermission(contentSelector, selector.getFormat(), selector.getName(), actions); + } + + @Override + public List getFormFields() { + return formFields; + } + + @Override + public String getName() { + return messages.name(); + } + + // + // Helpers + // + + public static String id(final String contentSelector, + final String format, + final String name, + final String... actions) + { + return String.format("nx-%s-%s-%s-%s-%s", TYPE, contentSelector, format, name, Joiner.on(',').join(actions)); + } + + public static CPrivilege privilege(final String contentSelector, + final String format, + final String name, + final String... actions) + { + checkArgument(actions.length > 0); + RepositorySelector selector = RepositorySelector.fromNameAndFormat(name, format); + return new CPrivilegeBuilder() + .type(TYPE) + .id(id(contentSelector, format, name, actions)) + .description(String + .format("%s for %s repository content selector %s", humanizeActions(actions), selector.humanizeSelector(), + contentSelector)) + .property(P_CONTENT_SELECTOR, contentSelector) + .property(P_REPOSITORY, selector.toSelector()) + .property(P_ACTIONS, actions) + .create(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryFormatSecurityContributor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryFormatSecurityContributor.java new file mode 100644 index 0000000000000000000000000000000000000000..41fa9bef74e54497fa4599f793ad0fd0da50db5e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryFormatSecurityContributor.java @@ -0,0 +1,107 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.security.config.MutableSecurityContributor; +import org.sonatype.nexus.security.config.SecurityConfiguration; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.security.BreadActions.ADD; +import static org.sonatype.nexus.security.BreadActions.BROWSE; +import static org.sonatype.nexus.security.BreadActions.DELETE; +import static org.sonatype.nexus.security.BreadActions.EDIT; +import static org.sonatype.nexus.security.BreadActions.READ; +import static org.sonatype.nexus.repository.security.RepositoryViewPrivilegeDescriptor.id; +import static org.sonatype.nexus.repository.security.RepositoryViewPrivilegeDescriptor.privilege; +import static org.sonatype.nexus.security.privilege.PrivilegeDescriptorSupport.ALL; + +/** + * Repository format security contributor. + * + * @since 3.0 + */ +public class RepositoryFormatSecurityContributor + extends MutableSecurityContributor +{ + // NOTE: This is not ideal, but moving forward to allow sharing, refactor eventually + + private final Format format; + + public RepositoryFormatSecurityContributor(final Format format) { + this.format = checkNotNull(format); + } + + /** + * Initial (static) security configuration. + */ + @Override + protected void initial(final SecurityConfiguration model) { + String format = this.format.getValue(); + + // add repository-view ALL privileges + model.addPrivilege(privilege(format, ALL, ALL)); + model.addPrivilege(privilege(format, ALL, BROWSE)); + model.addPrivilege(privilege(format, ALL, READ)); + model.addPrivilege(privilege(format, ALL, EDIT)); + model.addPrivilege(privilege(format, ALL, ADD)); + model.addPrivilege(privilege(format, ALL, DELETE)); + } + + /** + * Add security configuration for given repository. + */ + public void add(final Repository repository) { + checkNotNull(repository); + final String format = repository.getFormat().getValue(); + final String name = repository.getName(); + + apply(new Mutator() + { + @Override + public void apply(final SecurityConfiguration model) { + // add repository-view privileges + model.addPrivilege(privilege(format, name, ALL)); + model.addPrivilege(privilege(format, name, BROWSE)); + model.addPrivilege(privilege(format, name, READ)); + model.addPrivilege(privilege(format, name, EDIT)); + model.addPrivilege(privilege(format, name, ADD)); + model.addPrivilege(privilege(format, name, DELETE)); + } + }); + } + + /** + * Remove security configuration for given repository. + */ + public void remove(final Repository repository) { + checkNotNull(repository); + final String format = repository.getFormat().getValue(); + final String name = repository.getName(); + + apply(new Mutator() + { + @Override + public void apply(final SecurityConfiguration model) { + // remove repository-view privileges + model.removePrivilege(id(format, name, ALL)); + model.removePrivilege(id(format, name, BROWSE)); + model.removePrivilege(id(format, name, READ)); + model.removePrivilege(id(format, name, EDIT)); + model.removePrivilege(id(format, name, ADD)); + model.removePrivilege(id(format, name, DELETE)); + } + }); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositorySelector.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositorySelector.java new file mode 100644 index 0000000000000000000000000000000000000000..4afd120e58be7d847410feea64f16452200ec36a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositorySelector.java @@ -0,0 +1,100 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import com.google.common.annotations.VisibleForTesting; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A selector util class that builds values that we need for storage and display of the privileges to humans based + * on the provided repo name and format + * + * @since 3.1 + */ +public class RepositorySelector +{ + @VisibleForTesting + public static final String ALL = "*"; + + private static final String ALL_OF_FORMAT_PREFIX = "*-"; + + private final String name; + + private final String format; + + private RepositorySelector(final String name, final String format) { + this.name = checkNotNull(name); + this.format = checkNotNull(format); + } + + public static RepositorySelector all() { + return new RepositorySelector(ALL, ALL); + } + + public static RepositorySelector allOfFormat(final String format) { + return new RepositorySelector(ALL, format); + } + + public static RepositorySelector fromSelector(final String selector) { + checkNotNull(selector); + + if (ALL.equals(selector)) { + return new RepositorySelector(ALL, ALL); + } + else if (selector.startsWith(ALL_OF_FORMAT_PREFIX)) { + return new RepositorySelector(ALL, selector.substring(ALL_OF_FORMAT_PREFIX.length())); + } + return new RepositorySelector(selector, ALL); + } + + public static RepositorySelector fromNameAndFormat(final String name, final String format) { + return new RepositorySelector(name, format); + } + + public String getName() { + return name; + } + + public String getFormat() { + return format; + } + + public String toSelector() { + if (ALL.equals(name)) { + if (ALL.equals(format)) { + return ALL; + } + return ALL_OF_FORMAT_PREFIX + format; + } + return name; + } + + public String humanizeSelector() { + if (ALL.equals(name)) { + if (ALL.equals(format)) { + return "all"; + } + return "all '" + format + "'-format"; + } + return name; + } + + public boolean isAllRepositories() { + return ALL.equals(name); + } + + public boolean isAllFormats() { + return ALL.equals(format); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryViewPermission.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryViewPermission.java new file mode 100644 index 0000000000000000000000000000000000000000..8832170a7cbd09f97ff8c0a1ae3f6865f815cdbb --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryViewPermission.java @@ -0,0 +1,76 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import java.util.Arrays; +import java.util.List; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.security.authz.WildcardPermission2; + +import com.google.common.base.Joiner; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Repository view permission. + * + * @since 3.0 + */ +public class RepositoryViewPermission + extends WildcardPermission2 +{ + public static final String SYSTEM = "nexus"; + + public static final String DOMAIN = "repository-view"; + + private final String format; + + private final String name; + + private final List actions; + + public RepositoryViewPermission(final String format, final String name, final List actions) { + this.format = checkNotNull(format); + this.name = checkNotNull(name); + this.actions = checkNotNull(actions); + + setParts(Joiner.on(':').join( + SYSTEM, + DOMAIN, + format, + name, + Joiner.on(',').join(actions) + )); + } + + public RepositoryViewPermission(final String format, final String name, final String... actions) { + this(format, name, Arrays.asList(actions)); + } + + public RepositoryViewPermission(final Repository repository, final String... actions) { + this(repository.getFormat().getValue(), repository.getName(), Arrays.asList(actions)); + } + + public String getFormat() { + return format; + } + + public String getName() { + return name; + } + + public List getActions() { + return actions; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryViewPrivilegeDescriptor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryViewPrivilegeDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..1b4391fcf12322ebe7f3904310d4a96d14089773 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/RepositoryViewPrivilegeDescriptor.java @@ -0,0 +1,146 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import java.util.List; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.i18n.I18N; +import org.sonatype.goodies.i18n.MessageBundle; +import org.sonatype.nexus.formfields.FormField; +import org.sonatype.nexus.formfields.RepositoryCombobox; +import org.sonatype.nexus.formfields.StringTextFormField; +import org.sonatype.nexus.security.config.CPrivilege; +import org.sonatype.nexus.security.config.CPrivilegeBuilder; +import org.sonatype.nexus.security.privilege.PrivilegeDescriptor; +import org.sonatype.nexus.security.privilege.PrivilegeDescriptorSupport; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import org.apache.shiro.authz.Permission; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * Repository view {@link PrivilegeDescriptor}. + * + * @see RepositoryViewPermission + * @since 3.0 + */ +@Named(RepositoryViewPrivilegeDescriptor.TYPE) +@Singleton +public class RepositoryViewPrivilegeDescriptor + extends PrivilegeDescriptorSupport +{ + public static final String TYPE = RepositoryViewPermission.DOMAIN; + + public static final String P_FORMAT = "format"; + + public static final String P_REPOSITORY = "repository"; + + public static final String P_ACTIONS = "actions"; + + private interface Messages + extends MessageBundle + { + @DefaultMessage("Repository View") + String name(); + + @DefaultMessage("Format") + String format(); + + @DefaultMessage("The format(s) for the repository") + String formatHelp(); + + @DefaultMessage("Repository") + String repository(); + + @DefaultMessage("The repository name") + String repositoryHelp(); + + @DefaultMessage("Actions") + String actions(); + + @DefaultMessage("The comma-delimited list of actions") + String actionsHelp(); + } + + private static final Messages messages = I18N.create(Messages.class); + + private final List formFields; + + public RepositoryViewPrivilegeDescriptor() { + super(TYPE); + this.formFields = ImmutableList.of( + new StringTextFormField( + P_FORMAT, + messages.format(), + messages.formatHelp(), + FormField.MANDATORY + ), + new RepositoryCombobox( + P_REPOSITORY, + messages.repository(), + messages.repositoryHelp(), + true + ).includeAnEntryForAllRepositories(), + new StringTextFormField( + P_ACTIONS, + messages.actions(), + messages.actionsHelp(), + FormField.MANDATORY + ) + ); + } + + @Override + public Permission createPermission(final CPrivilege privilege) { + assert privilege != null; + String format = readProperty(privilege, P_FORMAT, ALL); + String name = readProperty(privilege, P_REPOSITORY, ALL); + List actions = readListProperty(privilege, P_ACTIONS, ALL); + return new RepositoryViewPermission(format, name, actions); + } + + @Override + public List getFormFields() { + return formFields; + } + + @Override + public String getName() { + return messages.name(); + } + + // + // Helpers + // + + public static String id(final String format, final String name, final String... actions) { + return String.format("nx-%s-%s-%s-%s", TYPE, format, name, Joiner.on(',').join(actions)); + } + + public static CPrivilege privilege(final String format, final String name, final String... actions) { + checkArgument(actions.length > 0); + return new CPrivilegeBuilder() + .type(TYPE) + .id(id(format, name, actions)) + .description(String.format("%s for %s repository views", humanizeActions(actions), humanizeName(name, format))) + .property(P_FORMAT, format) + .property(P_REPOSITORY, name) + .property(P_ACTIONS, actions) + .create(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/SecurityFacet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/SecurityFacet.java new file mode 100644 index 0000000000000000000000000000000000000000..caa33e3cfc4a17b619de0503e9e3792940398a42 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/SecurityFacet.java @@ -0,0 +1,35 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import org.sonatype.nexus.repository.Facet; +import org.sonatype.nexus.repository.view.Request; + +import org.apache.shiro.authz.AuthorizationException; + +/** + * Security facet. + * + * @since 3.0 + */ +@Facet.Exposed +public interface SecurityFacet + extends Facet +{ + /** + * Ensure the given request is permitted on the repository. + * + * @throws AuthorizationException + */ + void ensurePermitted(Request request); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/SecurityFacetSupport.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/SecurityFacetSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..ca64534c170eda6b181b470a7cf6adebda6be607 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/SecurityFacetSupport.java @@ -0,0 +1,101 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.http.HttpMethods; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.security.BreadActions; +import org.sonatype.nexus.selector.VariableSource; + +import org.apache.shiro.authz.AuthorizationException; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Support for {@link SecurityFacet} implementations. + * + * @since 3.0 + */ +public abstract class SecurityFacetSupport + extends FacetSupport + implements SecurityFacet +{ + private final RepositoryFormatSecurityContributor securityContributor; + + private final VariableResolverAdapter variableResolverAdapter; + + private final ContentPermissionChecker contentPermissionChecker; + + public SecurityFacetSupport(final RepositoryFormatSecurityContributor securityContributor, + final VariableResolverAdapter variableResolverAdapter, + final ContentPermissionChecker contentPermissionChecker) + { + this.securityContributor = checkNotNull(securityContributor); + this.variableResolverAdapter = checkNotNull(variableResolverAdapter); + this.contentPermissionChecker = checkNotNull(contentPermissionChecker); + } + + @Override + protected void doInit(final Configuration configuration) throws Exception { + securityContributor.add(getRepository()); + } + + @Override + protected void doDestroy() throws Exception { + securityContributor.remove(getRepository()); + } + + @Override + public void ensurePermitted(final Request request) { + checkNotNull(request); + + // determine permission action from request + String action = action(request); + + Repository repo = getRepository(); + + VariableSource variableSource = variableResolverAdapter.fromRequest(request, getRepository()); + if (!contentPermissionChecker.isPermitted(repo.getName(), repo.getFormat().getValue(), action, variableSource)) { + throw new AuthorizationException(); + } + } + + /** + * Returns BREAD action for request action. + */ + protected String action(final Request request) { + switch (request.getAction()) { + case HttpMethods.OPTIONS: + case HttpMethods.GET: + case HttpMethods.HEAD: + case HttpMethods.TRACE: + return BreadActions.READ; + + case HttpMethods.POST: + case HttpMethods.MKCOL: + case HttpMethods.PATCH: + return BreadActions.ADD; + + case HttpMethods.PUT: + return BreadActions.EDIT; + + case HttpMethods.DELETE: + return BreadActions.DELETE; + } + + throw new RuntimeException("Unsupported action: " + request.getAction()); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/SecurityHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/SecurityHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..8cee4b945a27f8a53aec483c05a2c552e2ba1143 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/SecurityHandler.java @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import javax.annotation.Nonnull; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Response; + +import com.google.common.annotations.VisibleForTesting; + +/** + * Security handler. + * + * @since 3.0 + */ +@Named +@Singleton +public class SecurityHandler + extends ComponentSupport + implements Handler +{ + @VisibleForTesting + static final String AUTHORIZED_KEY = "security.authorized"; + + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { + SecurityFacet securityFacet = context.getRepository().facet(SecurityFacet.class); + + //we employ the model that one security check per request is all that is necessary, if this handler is in a nested + //repository (because this is a group repository), there is no need to check authz again + if (context.getAttributes().get(AUTHORIZED_KEY) == null) { + securityFacet.ensurePermitted(context.getRequest()); + context.getAttributes().set(AUTHORIZED_KEY, true); + } + + return context.proceed(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/VariableResolverAdapter.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/VariableResolverAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..11976642c0f49090b57f8ddde886cdb74161740d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/VariableResolverAdapter.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import java.util.Map; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.selector.VariableSource; + +import com.orientechnologies.orient.core.record.impl.ODocument; +import org.elasticsearch.search.lookup.SourceLookup; + +/** + * Generate a variable source from a context, to be used for content selector evaluation + * @since 3.1 + */ +public interface VariableResolverAdapter +{ + VariableSource fromRequest(Request request, Repository repository); + + VariableSource fromDocument(ODocument document); + + VariableSource fromAsset(Asset asset); + + /** + * Creates a {@link VariableSource} from an ES-indexed asset. + */ + VariableSource fromSourceLookup(SourceLookup sourceLookup, Map asset); + + /** + * @since 3.8 + */ + VariableSource fromCoordinates(String format, String path, Map coordinates); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/VariableResolverAdapterManager.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/VariableResolverAdapterManager.java new file mode 100644 index 0000000000000000000000000000000000000000..b05b7e2fea893d38c85541e8c12d29f978b183a2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/VariableResolverAdapterManager.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +/** + * Manages {@link VariableResolverAdapter} implementations. + * + * @since 3.1 + * + */ +public interface VariableResolverAdapterManager +{ + /** + * Gets a {@link VariableResolverAdapter} for the specified format. If no specialized implemention for the format + * exists, a default implementation will be used. + */ + VariableResolverAdapter get(String format); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/VariableResolverAdapterSupport.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/VariableResolverAdapterSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..c52bf0625d38858a5f460093c35c3c17e400cb09 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/VariableResolverAdapterSupport.java @@ -0,0 +1,111 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import java.util.Map; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.selector.ConstantVariableResolver; +import org.sonatype.nexus.selector.PropertiesResolver; +import org.sonatype.nexus.selector.VariableSource; +import org.sonatype.nexus.selector.VariableSourceBuilder; + +import com.orientechnologies.orient.core.record.impl.ODocument; +import org.elasticsearch.search.lookup.SourceLookup; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Adapts different contexts to variable resolvers + * + * @since 3.1 + */ +public abstract class VariableResolverAdapterSupport + implements VariableResolverAdapter +{ + protected static final String PATH = "path"; + protected static final String FORMAT = "format"; + + @Override + public VariableSource fromRequest(final Request request, final Repository repository) { + VariableSourceBuilder builder = new VariableSourceBuilder(); + builder.addResolver(new ConstantVariableResolver(request.getPath(), PATH)); + builder.addResolver(new ConstantVariableResolver(repository.getFormat().getValue(), FORMAT)); + addFromRequest(builder, request); + + return builder.build(); + } + + protected abstract void addFromRequest(VariableSourceBuilder builder, Request request); + + @Override + public VariableSource fromDocument(final ODocument document) { + String path = document.field(AssetEntityAdapter.P_NAME, String.class); + String format = document.field(AssetEntityAdapter.P_FORMAT, String.class); + + VariableSourceBuilder builder = new VariableSourceBuilder(); + builder.addResolver(new ConstantVariableResolver('/' + path, PATH)); + builder.addResolver(new ConstantVariableResolver(format, FORMAT)); + addFromDocument(builder, document); + + return builder.build(); + } + + protected abstract void addFromDocument(VariableSourceBuilder builder, ODocument document); + + @Override + public VariableSource fromAsset(final Asset asset) { + VariableSourceBuilder builder = new VariableSourceBuilder(); + builder.addResolver(new ConstantVariableResolver('/' + asset.name(), PATH)); + builder.addResolver(new ConstantVariableResolver(asset.format(), FORMAT)); + addFromAsset(builder, asset); + + return builder.build(); + } + + protected abstract void addFromAsset(VariableSourceBuilder builder, Asset asset); + + @Override + public VariableSource fromCoordinates(final String format, final String path, final Map coordinates) { + VariableSourceBuilder builder = new VariableSourceBuilder(); + builder.addResolver(new ConstantVariableResolver('/' + checkNotNull(path), PATH)); + builder.addResolver(new ConstantVariableResolver(checkNotNull(format), FORMAT)); + + addCoordinates(builder, coordinates); + return builder.build(); + } + + @Override + public VariableSource fromSourceLookup(final SourceLookup sourceLookup, final Map asset) { + VariableSourceBuilder builder = new VariableSourceBuilder(); + builder.addResolver( + new ConstantVariableResolver('/' + checkNotNull((String) asset.get(DefaultComponentMetadataProducer.NAME)), PATH)); + builder.addResolver( + new ConstantVariableResolver(checkNotNull(sourceLookup.get(DefaultComponentMetadataProducer.FORMAT)), FORMAT)); + addFromSourceLookup(builder, sourceLookup, asset); + + return builder.build(); + } + + protected abstract void addFromSourceLookup(VariableSourceBuilder builder, + SourceLookup sourceLookup, + Map asset); + + protected void addCoordinates(final VariableSourceBuilder builder, final Map coordinates) { + builder.addResolver(new PropertiesResolver<>("coordinate", coordinates)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/internal/ContentPermissionCheckerImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/internal/ContentPermissionCheckerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..782fff9eb0d934a0fc122962a2f9a257fe6922fb --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/internal/ContentPermissionCheckerImpl.java @@ -0,0 +1,116 @@ + +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security.internal; + +import java.util.Arrays; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.security.ContentPermissionChecker; +import org.sonatype.nexus.repository.security.RepositoryContentSelectorPermission; +import org.sonatype.nexus.repository.security.RepositoryViewPermission; +import org.sonatype.nexus.security.SecurityHelper; +import org.sonatype.nexus.selector.SelectorConfiguration; +import org.sonatype.nexus.selector.SelectorEvaluationException; +import org.sonatype.nexus.selector.SelectorManager; +import org.sonatype.nexus.selector.VariableSource; + +import com.google.common.annotations.VisibleForTesting; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * @since 3.1 + */ +@Named +@Singleton +public class ContentPermissionCheckerImpl + extends ComponentSupport + implements ContentPermissionChecker +{ + private final SecurityHelper securityHelper; + + private final SelectorManager selectorManager; + + @Inject + public ContentPermissionCheckerImpl(final SecurityHelper securityHelper, + final SelectorManager selectorManager) { + this.securityHelper = checkNotNull(securityHelper); + this.selectorManager = checkNotNull(selectorManager); + } + + @VisibleForTesting + public boolean isViewPermitted(final String repositoryName, final String repositoryFormat, final String action) { + return securityHelper.anyPermitted(new RepositoryViewPermission(repositoryFormat, repositoryName, action)); + } + + @VisibleForTesting + public boolean isContentPermitted(final String repositoryName, + final String repositoryFormat, + final String action, + final SelectorConfiguration selectorConfiguration, + final VariableSource variableSource) + { + RepositoryContentSelectorPermission perm = new RepositoryContentSelectorPermission( + selectorConfiguration.getName(), repositoryFormat, repositoryName, Arrays.asList(action)); + + try { + // make sure subject has the selector permission before evaluating it, because that's a cheaper/faster check + return securityHelper.anyPermitted(perm) && selectorManager.evaluate(selectorConfiguration, variableSource); + } + catch (SelectorEvaluationException e) { + if (log.isTraceEnabled()) { + log.debug(e.getMessage(), e); + } + else { + log.debug(e.getMessage()); + } + } + + return false; + } + + @Override + public boolean isPermitted(final String repositoryName, + final String repositoryFormat, + final String action, + final VariableSource variableSource) + { + //check view perm first, if applicable, grant access + if (isViewPermitted(repositoryName, repositoryFormat, action)) { + return true; + } + //otherwise check the content selector perms + return selectorManager.browse().stream() + .anyMatch(config -> isContentPermitted(repositoryName, repositoryFormat, action, config, variableSource)); + } + + @Override + public boolean isPermittedJexlOnly(final String repositoryName, + final String repositoryFormat, + final String action, + final VariableSource variableSource) + { + // check view perm first, if applicable, grant access + if (isViewPermitted(repositoryName, repositoryFormat, action)) { + return true; + } + // otherwise check the content selector perms + return selectorManager.browseJexl().stream() + .anyMatch(config -> isContentPermitted(repositoryName, repositoryFormat, action, config, variableSource)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/internal/SimpleVariableResolverAdapter.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/internal/SimpleVariableResolverAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..02c94a166898c6aa60f1a99d5810d0f8910ed976 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/internal/SimpleVariableResolverAdapter.java @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security.internal; + +import java.util.Map; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.security.VariableResolverAdapter; +import org.sonatype.nexus.repository.security.VariableResolverAdapterSupport; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.selector.VariableSourceBuilder; + +import com.orientechnologies.orient.core.record.impl.ODocument; +import org.elasticsearch.search.lookup.SourceLookup; + +/** + * Simple implementation that will expose the path/format variable resolvers + * + * @since 3.1 + */ +@Named(SimpleVariableResolverAdapter.NAME) +@Singleton +public class SimpleVariableResolverAdapter + extends VariableResolverAdapterSupport + implements VariableResolverAdapter +{ + static final String NAME = "simple"; + + @Override + protected void addFromRequest(final VariableSourceBuilder builder, final Request request) { + //no-op the simple impl just allows for the path/format variable resolvers in the support class + } + + @Override + protected void addFromDocument(final VariableSourceBuilder builder, final ODocument document) { + //no-op the simple impl just allows for the path/format variable resolvers in the support class + } + + @Override + protected void addFromAsset(final VariableSourceBuilder builder, final Asset asset) { + //no-op the simple impl just allows for the path/format variable resolvers in the support class + } + + @Override + protected void addFromSourceLookup(final VariableSourceBuilder builder, + final SourceLookup sourceLookup, + final Map asset) + { + //no-op the simple impl just allows for the path/format variable resolvers in the support class + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/internal/VariableResolverAdapterManagerImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/internal/VariableResolverAdapterManagerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..557ec962d981a8f3687703c159d5600216fdb4a0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/internal/VariableResolverAdapterManagerImpl.java @@ -0,0 +1,52 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security.internal; + +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.security.VariableResolverAdapter; +import org.sonatype.nexus.repository.security.VariableResolverAdapterManager; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default implementation of {@link VariableResolverAdapterManager}. + * + * @since 3.1 + */ +@Named +@Singleton +public class VariableResolverAdapterManagerImpl + extends ComponentSupport + implements VariableResolverAdapterManager +{ + private final VariableResolverAdapter defaultAdapter; + + private final Map adaptersByFormat; + + @Inject + public VariableResolverAdapterManagerImpl(final Map adaptersByFormat) { + this.adaptersByFormat = checkNotNull(adaptersByFormat); + this.defaultAdapter = checkNotNull(adaptersByFormat.get(SimpleVariableResolverAdapter.NAME)); + } + + @Override + public VariableResolverAdapter get(String format) { + return adaptersByFormat.getOrDefault(format, defaultAdapter); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..a521b6b48a600835edd1aecce1d253bbc60899b6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/security/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Repository security-facet and support. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.security; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuth.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuth.java new file mode 100644 index 0000000000000000000000000000000000000000..4e41114a64704a46a15893b6a8a4904f09df6c64 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuth.java @@ -0,0 +1,115 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.selector.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; + +import com.google.common.collect.ImmutableMap; +import com.orientechnologies.orient.core.command.OCommandContext; +import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal; +import com.orientechnologies.orient.core.db.record.OIdentifiable; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.sql.OCommandSQL; +import com.orientechnologies.orient.core.sql.functions.OSQLFunction; +import com.orientechnologies.orient.core.sql.functions.OSQLFunctionAbstract; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Custom {@link OSQLFunction} for applying content selectors to an individual asset as part of a database query. When + * applied to the asset, the asset itself is tested. When applied to a component, the assets under the component are + * tested to see if at least one is visible. + * + * @since 3.1 + */ +@Named +@Singleton +public class ContentAuth + extends OSQLFunctionAbstract +{ + public static final String NAME = "contentAuth"; + + private final ContentAuthHelper contentAuthHelper; + + @Inject + public ContentAuth(final ContentAuthHelper contentAuthHelper) + { + super(NAME, 2, 3); + this.contentAuthHelper = checkNotNull(contentAuthHelper); + } + + @Override + public Object execute(final Object iThis, + final OIdentifiable iCurrentRecord, + final Object iCurrentResult, + final Object[] iParams, + final OCommandContext iContext) + { + OIdentifiable identifiable = (OIdentifiable) iParams[0]; + ODocument document = identifiable.getRecord(); + String browsedRepositoryName = (String) iParams[1]; + boolean jexlOnly = iParams.length > 2 && (boolean) iParams[2]; + + switch (document.getClassName()) { + case "asset": + return checkAssetPermissions(document, browsedRepositoryName, jexlOnly); + case "component": + return checkComponentAssetPermissions(document, browsedRepositoryName, jexlOnly); + default: + return false; + } + } + + private boolean checkComponentAssetPermissions(final ODocument component, + final String sourceRepositoryName, + final boolean jexlOnly) + { + checkNotNull(component); + for (ODocument asset : browseComponentAssets(component)) { + if (checkAssetPermissions(asset, sourceRepositoryName, jexlOnly)) { + return true; + } + } + return false; + } + + private boolean checkAssetPermissions(final ODocument asset, final String repositoryName, final boolean jexlOnly) { + if (jexlOnly) { + return contentAuthHelper.checkAssetPermissionsJexlOnly(asset, repositoryName); + } + return contentAuthHelper.checkAssetPermissions(asset, repositoryName); + } + + @Override + public String getSyntax() { + return NAME + "(, [jexlSelectorsOnly])"; + } + + private Iterable browseComponentAssets(final ODocument component) { + checkNotNull(component); + OIdentifiable bucket = component.field(ComponentEntityAdapter.P_BUCKET, OIdentifiable.class); + ODatabaseDocumentInternal db = component.getDatabase(); + Iterable results = db + .command(new OCommandSQL("select from asset where bucket = :bucket and component = :component")).execute( + new ImmutableMap.Builder() + .put("bucket", bucket.getIdentity()) + .put("component", component.getIdentity()) + .build() + ); + return results; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthHelper.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..1158468c19b2ba0bffb2d31725d1d916ca1c50fb --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthHelper.java @@ -0,0 +1,77 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.selector.internal; + +import java.util.Arrays; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.security.ContentPermissionChecker; +import org.sonatype.nexus.repository.security.VariableResolverAdapter; +import org.sonatype.nexus.repository.security.VariableResolverAdapterManager; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; +import org.sonatype.nexus.selector.VariableSource; + +import com.orientechnologies.orient.core.record.impl.ODocument; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.storage.DatabaseThreadUtils.withOtherDatabase; +import static org.sonatype.nexus.security.BreadActions.BROWSE; + +/** + * Simple helper class that encapsulates the auth checks for reuse by the orientdb user defined functions + * @since 3.2.1 + */ +@Named +@Singleton +public class ContentAuthHelper +{ + private final VariableResolverAdapterManager variableResolverAdapterManager; + + private final ContentPermissionChecker contentPermissionChecker; + + @Inject + public ContentAuthHelper(final VariableResolverAdapterManager variableResolverAdapterManager, + final ContentPermissionChecker contentPermissionChecker) + { + this.variableResolverAdapterManager = checkNotNull(variableResolverAdapterManager); + this.contentPermissionChecker = checkNotNull(contentPermissionChecker); + } + + public boolean checkAssetPermissions(final ODocument asset, final String... repositoryNames) { + String format = asset.field(AssetEntityAdapter.P_FORMAT, String.class); + VariableResolverAdapter variableResolverAdapter = variableResolverAdapterManager.get(format); + VariableSource variableSource = variableResolverAdapter.fromDocument(asset); + return withOtherDatabase(() -> { + for (String repositoryName : repositoryNames) { + if (contentPermissionChecker.isPermitted(repositoryName, format, BROWSE, variableSource)) { + return true; + } + } + return false; + }); + } + + /** + * @since 3.6 + */ + public boolean checkAssetPermissionsJexlOnly(final ODocument asset, final String... repositoryNames) { + String format = asset.field(AssetEntityAdapter.P_FORMAT, String.class); + VariableResolverAdapter variableResolverAdapter = variableResolverAdapterManager.get(format); + VariableSource variableSource = variableResolverAdapter.fromDocument(asset); + return withOtherDatabase(() -> Arrays.stream(repositoryNames).anyMatch(repositoryName -> contentPermissionChecker + .isPermittedJexlOnly(repositoryName, format, BROWSE, variableSource))); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPlugin.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..e264e891a704153bd8dee28ef2e26037039c4359 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPlugin.java @@ -0,0 +1,78 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.selector.internal; + +import org.sonatype.nexus.repository.search.SearchSubjectHelper; +import org.sonatype.nexus.repository.security.ContentPermissionChecker; +import org.sonatype.nexus.repository.security.VariableResolverAdapterManager; + +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.script.ScriptModule; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Elasticsearch plugin that exposes a content auth function for working with content selectors in search. Also holds + * on to necessary instances as static variables that should be set by the matching {@link ContentAuthPluginLocator}, + * as this class is instantiated by ES, not by us. + * + * @since 3.1 + */ +public class ContentAuthPlugin + extends Plugin +{ + private static ContentPermissionChecker contentPermissionChecker; + private static VariableResolverAdapterManager variableResolverAdapterManager; + private static SearchSubjectHelper searchSubjectHelper; + + public ContentAuthPlugin() { + checkNotNull(contentPermissionChecker); + checkNotNull(variableResolverAdapterManager); + checkNotNull(searchSubjectHelper); + } + + @Override + public String name() { + return "content-auth-plugin"; + } + + @Override + public String description() { + return "ES plugin for working with content selectors"; + } + + public void onModule(final ScriptModule module) { + module.registerScript(ContentAuthPluginScript.NAME, ContentAuthPluginScriptFactory.class); + } + + public static void setDependencies(final ContentPermissionChecker contentPermissionChecker, + final VariableResolverAdapterManager variableResolverAdapterManager, + final SearchSubjectHelper searchSubjectHelper) + { + ContentAuthPlugin.contentPermissionChecker = checkNotNull(contentPermissionChecker); + ContentAuthPlugin.variableResolverAdapterManager = checkNotNull(variableResolverAdapterManager); + ContentAuthPlugin.searchSubjectHelper = checkNotNull(searchSubjectHelper); + } + + public static ContentPermissionChecker getContentPermissionChecker() { + return contentPermissionChecker; + } + + public static VariableResolverAdapterManager getVariableResolverAdapterManager() { + return variableResolverAdapterManager; + } + + public static SearchSubjectHelper getSearchSubjectHelper() { + return searchSubjectHelper; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPluginLocator.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPluginLocator.java new file mode 100644 index 0000000000000000000000000000000000000000..fd87c72e4f41ef319084ec2b1d4232f11472b450 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPluginLocator.java @@ -0,0 +1,50 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.selector.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.elasticsearch.PluginLocator; +import org.sonatype.nexus.repository.search.SearchSubjectHelper; +import org.sonatype.nexus.repository.security.ContentPermissionChecker; +import org.sonatype.nexus.repository.security.VariableResolverAdapterManager; + +import org.elasticsearch.plugins.Plugin; + +/** + * {@link PluginLocator} for {@link ContentAuthPlugin}. Also responsible for setting some required objects into static + * fields on {@link ContentAuthPlugin} as the instantiation of the latter occurs outside of our purview within ES. + * + * @since 3.1 + */ +@Named +@Singleton +public class ContentAuthPluginLocator + implements PluginLocator +{ + @Inject + public ContentAuthPluginLocator(final ContentPermissionChecker contentPermissionChecker, + final VariableResolverAdapterManager variableResolverAdapterManager, + final SearchSubjectHelper searchSubjectHelper) + { + ContentAuthPlugin.setDependencies(contentPermissionChecker, variableResolverAdapterManager, + searchSubjectHelper); + } + + @Override + public Class pluginClass() { + return ContentAuthPlugin.class; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPluginScript.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPluginScript.java new file mode 100644 index 0000000000000000000000000000000000000000..a4200bab9d5dcfa00ecd6d7c7f780d9a91dc0877 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPluginScript.java @@ -0,0 +1,93 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.selector.internal; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.sonatype.nexus.repository.security.ContentPermissionChecker; +import org.sonatype.nexus.repository.security.VariableResolverAdapter; +import org.sonatype.nexus.repository.security.VariableResolverAdapterManager; +import org.sonatype.nexus.selector.VariableSource; + +import com.google.common.annotations.VisibleForTesting; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.subject.support.SubjectThreadState; +import org.apache.shiro.util.ThreadState; +import org.elasticsearch.script.AbstractSearchScript; +import org.elasticsearch.search.lookup.SourceLookup; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.FORMAT; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.REPOSITORY_NAME; +import static org.sonatype.nexus.security.BreadActions.BROWSE; + +/** + * Native script to work with content selectors from within ES queries. + * + * @since 3.1 + */ +public class ContentAuthPluginScript + extends AbstractSearchScript +{ + public static final String NAME = "content_auth"; + + private final Subject subject; + + private final VariableResolverAdapterManager variableResolverAdapterManager; + + private final ContentPermissionChecker contentPermissionChecker; + + public ContentAuthPluginScript(final Subject subject, + final ContentPermissionChecker contentPermissionChecker, + final VariableResolverAdapterManager variableResolverAdapterManager) + { + this.subject = checkNotNull(subject); + this.contentPermissionChecker = checkNotNull(contentPermissionChecker); + this.variableResolverAdapterManager = checkNotNull(variableResolverAdapterManager); + } + + @Override + public Object run() { + ThreadState threadState = new SubjectThreadState(subject); + threadState.bind(); + try { + SourceLookup sourceLookup = getSourceLookup(); + String format = (String) checkNotNull(sourceLookup.get(FORMAT)); + String repositoryName = (String) checkNotNull(sourceLookup.get(REPOSITORY_NAME)); + VariableResolverAdapter variableResolverAdapter = variableResolverAdapterManager.get(format); + @SuppressWarnings("unchecked") + List> assets = (List>) sourceLookup + .getOrDefault("assets", Collections.emptyList()); + if (assets != null) { + for (Map asset : assets) { + VariableSource variableSource = variableResolverAdapter.fromSourceLookup(sourceLookup, asset); + return contentPermissionChecker.isPermitted(repositoryName, format, BROWSE, variableSource); + } + } + return false; + } + finally { + threadState.clear(); + } + } + + /** + * Delegates to {@link #source()}, only here to aid in unit testing. + */ + @VisibleForTesting + protected SourceLookup getSourceLookup() { + return source(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPluginScriptFactory.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPluginScriptFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..b643fef60318e72af8d363da04894bdc714eec33 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPluginScriptFactory.java @@ -0,0 +1,59 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.selector.internal; + +import java.util.Map; + +import org.apache.shiro.subject.Subject; +import org.elasticsearch.script.ExecutableScript; +import org.elasticsearch.script.NativeScriptFactory; +import org.elasticsearch.script.Script; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Collections.singletonMap; +import static org.elasticsearch.script.ScriptService.ScriptType.INLINE; + +/** + * Factory for {@link ContentAuthPluginScript}. + * + * @since 3.1 + */ +public class ContentAuthPluginScriptFactory + implements NativeScriptFactory +{ + private static final String SUBJECT_PARAM = "subject"; + + @Override + public ExecutableScript newScript(final Map params) { + checkNotNull(params); + String subjectId = (String) checkNotNull(params.get(SUBJECT_PARAM)); + Subject subject = ContentAuthPlugin.getSearchSubjectHelper().getSubject(subjectId); + return new ContentAuthPluginScript( + subject, + ContentAuthPlugin.getContentPermissionChecker(), + ContentAuthPlugin.getVariableResolverAdapterManager()); + } + + @Override + public boolean needsScores() { + return false; + } + + /** + * Returns a new {@link Script} instance for use in ES queries, configured for the {@link ContentAuthPluginScript}. + */ + public static Script newScript(final String subjectId) { + checkNotNull(subjectId); + return new Script(ContentAuthPluginScript.NAME, INLINE, "native", singletonMap(SUBJECT_PARAM, subjectId)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentExpressionFunction.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentExpressionFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..ae75bb265e38ec67c41352ce8d40d3f79219215f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/selector/internal/ContentExpressionFunction.java @@ -0,0 +1,166 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.selector.internal; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.security.RepositorySelector; +import org.sonatype.nexus.repository.security.VariableResolverAdapter; +import org.sonatype.nexus.repository.security.VariableResolverAdapterManager; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; +import org.sonatype.nexus.repository.storage.BucketEntityAdapter; +import org.sonatype.nexus.selector.JexlSelector; +import org.sonatype.nexus.selector.SelectorConfiguration; +import org.sonatype.nexus.selector.SelectorEvaluationException; +import org.sonatype.nexus.selector.SelectorManager; +import org.sonatype.nexus.selector.VariableSource; + +import com.google.common.collect.ImmutableMap; +import com.orientechnologies.orient.core.command.OCommandContext; +import com.orientechnologies.orient.core.db.record.OIdentifiable; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.sql.functions.OSQLFunction; +import com.orientechnologies.orient.core.sql.functions.OSQLFunctionAbstract; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Custom {@link OSQLFunction} for applying a jexl expression and repository(ies) to an individual asset as part of a + * database query. + * + * Required parameters for the functiona are + * - asset (typically provided as @this) + * - expression {a jexl expression string, i.e. "format == 'nuget'"} + * - repositorySelector (a RepositorySelector based string, i.e. * or *maven2 or repoName directly, etc.) + * - repoToContainedGroupMap a serialized map containing all repoIds in the system, with a list of any groups that may + * contain the repositories + * + * @since 3.1 + */ +@Named +@Singleton +public class ContentExpressionFunction + extends OSQLFunctionAbstract +{ + public static final String NAME = "contentExpression"; + + private static final Logger log = LoggerFactory.getLogger(ContentExpressionFunction.class); + + private final VariableResolverAdapterManager variableResolverAdapterManager; + + private final SelectorManager selectorManager; + + private final ContentAuthHelper contentAuthHelper; + + @Inject + public ContentExpressionFunction(final VariableResolverAdapterManager variableResolverAdapterManager, + final SelectorManager selectorManager, + final ContentAuthHelper contentAuthHelper) + { + super(NAME, 4, 4); + this.variableResolverAdapterManager = checkNotNull(variableResolverAdapterManager); + this.selectorManager = checkNotNull(selectorManager); + this.contentAuthHelper = checkNotNull(contentAuthHelper); + } + + @Override + public Object execute(final Object iThis, + final OIdentifiable iCurrentRecord, + final Object iCurrentResult, + final Object[] iParams, + final OCommandContext iContext) + { + OIdentifiable identifiable = (OIdentifiable) iParams[0]; + ODocument asset = identifiable.getRecord(); + RepositorySelector repositorySelector = RepositorySelector.fromSelector((String) iParams[2]); + String jexlExpression = (String) iParams[1]; + List membersForAuth; + + //if a single repo was selected, we want to auth against that member + if (!repositorySelector.isAllRepositories()) { + membersForAuth = Arrays.asList(repositorySelector.getName()); + } + //if all repos (or all of format) was selected, use the repository the asset was in, as well as any groups + //that may contain that repository + else { + @SuppressWarnings("unchecked") + Map> repoToContainedGroupMap = (Map>) iParams[3]; + + //find the repository that matches the asset + String assetRepository = getAssetRepository(asset); + + //if can't find it, just back out, nothing more to see here + if (assetRepository == null) { + log.error("Asset {} references no repository", getAssetName(asset)); + return false; + } + + membersForAuth = repoToContainedGroupMap.get(assetRepository); + + if (membersForAuth == null) { + log.error("Asset {} references an invalid repository: {}", getAssetName(asset), assetRepository); + return false; + } + } + + return contentAuthHelper.checkAssetPermissions(asset, membersForAuth.toArray(new String[membersForAuth.size()])) && + checkJexlExpression(asset, jexlExpression, asset.field(AssetEntityAdapter.P_FORMAT, String.class)); + } + + @Nullable + private String getAssetRepository(ODocument asset) { + OIdentifiable bucketId = asset.field(AssetEntityAdapter.P_BUCKET, OIdentifiable.class); + ODocument bucket = bucketId.getRecord(); + return bucket.field(BucketEntityAdapter.P_REPOSITORY_NAME, String.class); + } + + private String getAssetName(ODocument asset) { + return asset.field(AssetEntityAdapter.P_NAME, String.class); + } + + private boolean checkJexlExpression(final ODocument asset, + final String jexlExpression, + final String format) + { + VariableResolverAdapter variableResolverAdapter = variableResolverAdapterManager.get(format); + VariableSource variableSource = variableResolverAdapter.fromDocument(asset); + + SelectorConfiguration selectorConfiguration = new SelectorConfiguration(); + + selectorConfiguration.setAttributes(ImmutableMap.of("expression", jexlExpression)); + selectorConfiguration.setType(JexlSelector.TYPE); + selectorConfiguration.setName("preview"); + + try { + return selectorManager.evaluate(selectorConfiguration, variableSource); + } + catch (SelectorEvaluationException e) { + log.debug("Unable to evaluate expression {}.", jexlExpression, e); + return false; + } + } + + @Override + public String getSyntax() { + return NAME + "(, , , )"; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AbstractMetadataNode.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AbstractMetadataNode.java new file mode 100644 index 0000000000000000000000000000000000000000..d8849530717a73ae25a31fb186da4bf878d3f4ea --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AbstractMetadataNode.java @@ -0,0 +1,159 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.AbstractEntity; +import org.sonatype.nexus.common.entity.EntityId; + +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_BUCKET; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_FORMAT; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_LAST_UPDATED; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_NAME; + +/** + * @since 3.7 + */ +public abstract class AbstractMetadataNode + extends AbstractEntity + implements MetadataNode +{ + private boolean newEntity = true; + + private EntityId bucketId; + + private String name; + + private DateTime lastUpdated; + + private String format; + + private NestedAttributesMap attributes; + + /** + * Is this entity new as of this transaction? + */ + public boolean isNew() { + return newEntity; + } + + public T newEntity(final boolean newEntity) { + this.newEntity = newEntity; + return self(); + } + + /** + * Gets the bucket this is part of. + */ + public EntityId bucketId() { + return require(bucketId, P_BUCKET); + } + + public T bucketId(final EntityId bucketId) { + this.bucketId = checkNotNull(bucketId); + return self(); + } + + /** + * Gets the name. + */ + public String name() { + return require(name, P_NAME); + } + + /** + * Sets the name. + */ + public T name(final String name) { + this.name = checkNotNull(name); + return self(); + } + + /** + * Gets the last updated date or {@code null} if undefined (the node has never been saved). + */ + @Nullable + public DateTime lastUpdated() { + return lastUpdated; + } + + /** + * Gets the last updated date or throws a runtime exception if undefined. + */ + public DateTime requireLastUpdated() { + return require(lastUpdated, P_LAST_UPDATED); + } + + /** + * Sets the last updated date. + */ + public T lastUpdated(final DateTime lastUpdated) { + this.lastUpdated = lastUpdated; + return self(); + } + + /** + * Gets the format property, which is immutable. + */ + public String format() { + return require(format, P_FORMAT); + } + + /** + * Sets the format. + */ + public T format(final String format) { + this.format = format; + return self(); + } + + /** + * Gets the "attributes" property of this node, a map of maps that is possibly empty, but never {@code null}. + */ + public NestedAttributesMap attributes() { + return require(attributes, P_ATTRIBUTES); + } + + /** + * Sets the attributes. + */ + public T attributes(final NestedAttributesMap attributes) { + this.attributes = attributes; + return self(); + } + + /** + * Gets the format-specific attributes of this node ("attributes.formatName"). + */ + public NestedAttributesMap formatAttributes() { + return attributes().child(format()); + } + + protected V require(final V value, final String name) { + checkState(value != null, "Missing property: %s", name); + return value; + } + + @SuppressWarnings("unchecked") + private T self() { + return (T) this; + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/Asset.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/Asset.java new file mode 100644 index 0000000000000000000000000000000000000000..ff56bb0f326ef1375a587dedbab2a9a4142726bc --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/Asset.java @@ -0,0 +1,310 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Map; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.blobstore.api.BlobRef; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.hash.HashAlgorithm; + +import com.google.common.collect.Maps; +import com.google.common.hash.HashCode; +import org.joda.time.DateTime; + +import static org.sonatype.nexus.repository.storage.AssetEntityAdapter.P_BLOB_REF; +import static org.sonatype.nexus.repository.storage.AssetEntityAdapter.P_CONTENT_TYPE; +import static org.sonatype.nexus.repository.storage.AssetEntityAdapter.P_SIZE; + +/** + * Metadata about a file, which may or may not belong to a component. + * + * @since 3.0 + */ +public class Asset + extends AbstractMetadataNode +{ + /** + * Key of {@link Asset} nested map of blob content hashes (if asset has backing content). + * + * @see StorageTx#attachBlob(Asset, AssetBlob) + */ + public static final String CHECKSUM = "checksum"; + + /** + * Key of {@link Asset} nested map containing information regarding the provenance of the blob. + * + * @see StorageTx#attachBlob(Asset, AssetBlob) + * @since 3.1 + */ + public static final String PROVENANCE = "provenance"; + + /** + * Key of {@link Asset} under {@link Asset#PROVENANCE} indicating if the hashes were not verified. + * + * @since 3.1 + */ + public static final String HASHES_NOT_VERIFIED = "hashes_not_verified"; + + private EntityId componentId; + + private Long size; + + private String contentType; + + private BlobRef blobRef; + + private DateTime lastDownloaded; + + private DateTime blobCreated; + + private DateTime blobUpdated; + + private String createdBy; + + private String createdByIp; + + /** + * Gets the component's {@link EntityId} this asset is part of, or {@code null} if it's standalone. + */ + @Nullable + public EntityId componentId() { + return componentId; + } + + /** + * Sets the component id this asset is part of. + */ + public Asset componentId(final EntityId componentId) { + this.componentId = componentId; + return this; + } + + /** + * Gets the size of the file in bytes or {@code null} if undefined. + */ + @Nullable + public Long size() { + return size; + } + + /** + * Gets the size of the file in bytes or throws a runtime exception if undefined. + */ + public Long requireSize() { + return require(size, P_SIZE); + } + + /** + * Sets the size to the given value, or {@code null} to un-define it. + */ + public Asset size(@Nullable final Long size) { + this.size = size; + return this; + } + + /** + * Gets the user used to create this + * + * @since 3.6.1 + */ + @Nullable + public String createdBy() { + return createdBy; + } + + /** + * Gets the ip address used to create this + * + * @since 3.6.1 + */ + @Nullable + public String createdByIp() { + return createdByIp; + } + + /** + * Sets the user used to create this + * + * @since 3.6.1 + */ + public Asset createdBy(@Nullable final String createdBy) { + this.createdBy = createdBy; + return this; + } + + /** + * Sets the IP address used to create this + * + * @since 3.6.1 + */ + public Asset createdByIp(@Nullable final String createdByIp) { + this.createdByIp = createdByIp; + return this; + } + + /** + * Gets the content type or {@code null} if undefined. + */ + @Nullable + public String contentType() { + return contentType; + } + + /** + * Gets the content type or throws a runtime exception if undefined. + */ + public String requireContentType() { + return require(contentType, P_CONTENT_TYPE); + } + + /** + * Sets the content type to the given value, or {@code null} to un-define it. + */ + public Asset contentType(@Nullable final String contentType) { + this.contentType = contentType; + return this; + } + + /** + * Gets the blobRef or {@code null} if undefined. + */ + @Nullable + public BlobRef blobRef() { + return blobRef; + } + + /** + * Gets the blobRef or throws a runtime exception if undefined. + */ + public BlobRef requireBlobRef() { + return require(blobRef, P_BLOB_REF); + } + + /** + * Sets the blobRef to the given value, or {@code null} to un-define it. + */ + public Asset blobRef(@Nullable final BlobRef blobRef) { + this.blobRef = blobRef; + return this; + } + + /** + * Gets the last downloaded timestamp or {@code null} if undefined. Note that this can also reflect the timestamp for + * initial upload if the artifact has not yet been downloaded. + */ + @Nullable + public DateTime lastDownloaded() { + return lastDownloaded; + } + + /** + * Sets the last downloaded timestamp. + */ + Asset lastDownloaded(final DateTime lastDownloaded) { + this.lastDownloaded = lastDownloaded; + return this; + } + + /** + * Sets the last downloaded timestamp to now, if it has been more than a minute. + * + * @return {@code true} if the timestamp was changed, otherwise {@code false} + */ + public boolean markAsDownloaded() { + DateTime now = DateTime.now(); + if (lastDownloaded == null || lastDownloaded.isBefore(now.minusMinutes(1))) { + lastDownloaded(now); + return true; + } + return false; + } + + /** + * Extract checksums of asset blob if checksums are present in asset attributes. + */ + public Map getChecksums(final Iterable hashAlgorithms) + { + final NestedAttributesMap checksumAttributes = attributes().child(CHECKSUM); + final Map hashCodes = Maps.newHashMap(); + for (HashAlgorithm algorithm : hashAlgorithms) { + final HashCode hashCode = HashCode.fromString(checksumAttributes.require(algorithm.name(), String.class)); + hashCodes.put(algorithm, hashCode); + } + return hashCodes; + } + + /** + * Extract checksum of asset blob if checksum is present in asset attributes. + */ + @Nullable + public HashCode getChecksum(final HashAlgorithm hashAlgorithm) + { + String hashCode = attributes().child(CHECKSUM).get(hashAlgorithm.name(), String.class); + if (hashCode != null) { + return HashCode.fromString(hashCode); + } + return null; + } + + /** + * Gets the blob_created timestamp or null if undefined. + * + * @since 3.3 + */ + @Nullable + public DateTime blobCreated() { + return blobCreated; + } + + /** + * Sets the blob_created timestamp. + * + * @since 3.3 + */ + public Asset blobCreated(@Nullable final DateTime blobCreated) { + this.blobCreated = blobCreated; + return this; + } + + /** + * Gets the blob_updated timestamp or null if undefined. + * + * @since 3.3 + */ + @Nullable + public DateTime blobUpdated() { + return blobUpdated; + } + + /** + * Sets the blob_updated timestamp. + * + * @since 3.3 + */ + public Asset blobUpdated(@Nullable final DateTime blobUpdated) { + this.blobUpdated = blobUpdated; + return this; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "metadata=" + getEntityMetadata() + + ", name=" + name() + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetBlob.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetBlob.java new file mode 100644 index 0000000000000000000000000000000000000000..dfa0e0d48855b14d4cc5c553f388bb6bea6b4e70 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetBlob.java @@ -0,0 +1,185 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Map; +import java.util.function.Function; + +import javax.annotation.Nonnull; + +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobRef; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.common.hash.HashAlgorithm; +import org.sonatype.nexus.common.node.NodeAccess; + +import com.google.common.hash.HashCode; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Blob handle, that holds properties and reference of newly created blob that is about to be attached to an {@link + * Asset}. If this instance is not attached to any {@link Asset} during transaction, it is considered "orphan" and + * will be cleaned up (deleted). + * + * @since 3.0 + */ +public class AssetBlob +{ + private final NodeAccess nodeAccess; + + private final BlobStore blobStore; + + private final Function blobFunction; + + private final String contentType; + + private final Map hashes; + + private final boolean hashesVerified; + + private boolean attached; + + private Blob canonicalBlob; + + private Blob ingestedBlob; + + public AssetBlob(final NodeAccess nodeAccess, + final BlobStore blobStore, + final Function blobFunction, + final String contentType, + final Map hashes, + final boolean hashesVerified) + { + this.nodeAccess = checkNotNull(nodeAccess); + this.blobStore = checkNotNull(blobStore); + this.blobFunction = checkNotNull(blobFunction); + this.contentType = checkNotNull(contentType); + this.hashes = checkNotNull(hashes); + this.hashesVerified = hashesVerified; + } + + /** + * Returns {@code true} if this {@link AssetBlob} duplicates the old asset's blob. + * + * @since 3.4 + */ + public boolean isDuplicate() { + return canonicalBlob != null; + } + + /** + * Redirects this temporary {@link AssetBlob} to a canonical (de-duplicated) blob. + * + * @since 3.4 + */ + void setDuplicate(final Blob canonicalBlob) { + this.canonicalBlob = checkNotNull(canonicalBlob); + } + + /** + * Deletes this temporary {@link AssetBlob} by clearing any locally ingested blobs. + * + * Note this shouldn't stop the current response from serving back temporary content, + * it just makes sure non-persisted content is eventually cleaned up from the store. + * + * @since 3.4 + */ + void delete(final String reason) { + if (ingestedBlob != null) { + if (canonicalBlob != null) { + // canonical redirect is in place, so it's safe to hard-delete the temp blob + blobStore.deleteHard(ingestedBlob.getId()); + } + else { + // no redirect, so the temp blob is all we have - use soft-delete so the bytes + // will still be available on disk for streaming back in the current response, + // while making sure it gets cleaned up on the next compact + blobStore.delete(ingestedBlob.getId(), reason); + } + } + } + + /** + * Returns {@code true} if this instance is attached to an {@link Asset}. If {@code false} returned, the blob + * referenced by this instance is considered "orphan" and will be deleted at the end of TX (whatever outcome is, + * commit or rollback) if not attached until the end of TX. + */ + boolean isAttached() { + return attached; + } + + /** + * Sets the attached state or this instance. Only can be invoked once, while this instance is not attached. + */ + void setAttached(final boolean attached) { + checkArgument(!this.attached, "Already attached"); + this.attached = attached; + } + + /** + * The blob reference this instance is pointing to. + */ + @Nonnull + public BlobRef getBlobRef() { + return new BlobRef( + nodeAccess.getId(), + blobStore.getBlobStoreConfiguration().getName(), + getBlob().getId().asUniqueString()); + } + + @Nonnull + public Blob getBlob() { + if (canonicalBlob != null) { + return canonicalBlob; + } + if (ingestedBlob == null) { + ingestedBlob = checkNotNull(blobFunction.apply(blobStore)); + } + return ingestedBlob; + } + + /** + * The blob size in bytes. + */ + public long getSize() { + return getBlob().getMetrics().getContentSize(); + } + + /** + * The content-type that blob contains. + */ + @Nonnull + public String getContentType() { + return contentType; + } + + /** + * Exact hashes for the blob. Typically calculated by storage subsystem while blob was getting saved, but sometimes + * provided based on precalculated or known values. + */ + @Nonnull + public Map getHashes() { + return hashes; + } + + /** + * Returns a boolean indicating whether the hashes associated with this blob have been verified. + * + * @since 3.1 + */ + public boolean getHashesVerified() { + return hashesVerified; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetCreatedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetCreatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..7378bb2788772c5845d37ce1934941c666ddef74 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetCreatedEvent.java @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.entity.EntityCreatedEvent; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Asset created event. + * + * @since 3.0 + */ +public class AssetCreatedEvent + extends EntityCreatedEvent + implements AssetEvent +{ + private final String repositoryName; + + private final EntityId componentId; + + public AssetCreatedEvent(final EntityMetadata metadata, final String repositoryName, + @Nullable final EntityId componentId) + { + super(metadata); + this.repositoryName = checkNotNull(repositoryName); + this.componentId = componentId; + } + + public String getRepositoryName() { + return repositoryName; + } + + @Nullable + public EntityId getComponentId() { + return componentId; + } + + public EntityId getAssetId() { + return getId(); + } + + public Asset getAsset() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetDeletedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetDeletedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..34c3fe4a956c8bd9f7e5445e4f14df0eca556659 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetDeletedEvent.java @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.entity.EntityDeletedEvent; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Asset deleted event. + * + * @since 3.0 + */ +public class AssetDeletedEvent + extends EntityDeletedEvent + implements AssetEvent +{ + private final String repositoryName; + + private final EntityId componentId; + + public AssetDeletedEvent(final EntityMetadata metadata, final String repositoryName, + @Nullable final EntityId componentId) + { + super(metadata); + this.repositoryName = checkNotNull(repositoryName); + this.componentId = componentId; + } + + public String getRepositoryName() { + return repositoryName; + } + + @Nullable + public EntityId getComponentId() { + return componentId; + } + + public EntityId getAssetId() { + return getId(); + } + + public Asset getAsset() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetEntityAdapter.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..da0d64fec309602397b03fb211c7c4b63fd7e869 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetEntityAdapter.java @@ -0,0 +1,314 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Date; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.blobstore.api.BlobRef; +import org.sonatype.nexus.common.entity.EntityEvent; +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.OIndexBuilder; +import org.sonatype.nexus.orient.OIndexNameBuilder; +import org.sonatype.nexus.orient.entity.AttachedEntityId; +import org.sonatype.nexus.orient.entity.AttachedEntityMetadata; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.sql.OCommandSQL; +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static org.sonatype.nexus.repository.storage.BucketEntityAdapter.P_REPOSITORY_NAME; + +/** + * {@link Asset} entity-adapter. + * + * @since 3.0 + */ +@Named +@Singleton +public class AssetEntityAdapter + extends MetadataNodeEntityAdapter +{ + private static final String DB_CLASS = new OClassNameBuilder() + .type("asset") + .build(); + + /** + * Applied (optionally) to asset only, holds a value from format describing what current asset is. + */ + public static final String P_ASSET_KIND = "asset_kind"; + + /** + * Key of {@link Asset} blob ref attribute (if asset has backing content). + * + * @see StorageTx#attachBlob(Asset, AssetBlob) + */ + public static final String P_BLOB_REF = "blob_ref"; + + /** + * Key of {@link Asset} component reference attribute (if asset belongs to a component). + */ + public static final String P_COMPONENT = "component"; + + /** + * Key of {@link Asset} for content type attribute (if asset has backing content). + * + * @see StorageTx#attachBlob(Asset, AssetBlob) + */ + public static final String P_CONTENT_TYPE = "content_type"; + + /** + * Key of {@link Asset} attribute denoting when it was last downloaded. + */ + public static final String P_LAST_DOWNLOADED = "last_downloaded"; + + /** + * Key of {@link Asset} size attribute (if asset has backing content). + * + * @see StorageTx#attachBlob(Asset, AssetBlob) + */ + public static final String P_SIZE = "size"; + + /** + * Key of {@link Asset} created by attribute (if asset has backing content). + * + * @see StorageTx#attachBlob(Asset, AssetBlob) + */ + public static final String P_CREATED_BY = "created_by"; + + /** + * Key of {@link Asset} created by ip attribute (if asset has backing content). + * + * @see StorageTx#attachBlob(Asset, AssetBlob) + */ + public static final String P_CREATED_BY_IP = "created_by_ip"; + + /** + * Key of {@link Asset} attribute indicating when a blob was first attached to this asset. + */ + public static final String P_BLOB_CREATED = "blob_created"; + + /** + * Key of {@link Asset} attribute indicating when a blob was updated (on creation or attaching a different blob). + */ + public static final String P_BLOB_UPDATED = "blob_updated"; + + public static final String I_BUCKET_COMPONENT_NAME = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_BUCKET) + .property(P_COMPONENT) + .property(P_NAME) + .build(); + + private static final String I_BUCKET_NAME = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_BUCKET) + .property(P_NAME) + .build(); + + public static final String I_COMPONENT = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_COMPONENT) + .build(); + + public static final String I_NAME_CASEINSENSITIVE = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_NAME) + .caseInsensitive() + .build(); + + private final ComponentEntityAdapter componentEntityAdapter; + + @Inject + public AssetEntityAdapter(final BucketEntityAdapter bucketEntityAdapter, + final ComponentEntityAdapter componentEntityAdapter) + { + super(DB_CLASS, bucketEntityAdapter); + this.componentEntityAdapter = componentEntityAdapter; + } + + @Override + protected void defineType(final ODatabaseDocumentTx db, final OClass type) { + super.defineType(type); + type.createProperty(P_COMPONENT, OType.LINK, componentEntityAdapter.getSchemaType()); + type.createProperty(P_NAME, OType.STRING) + .setMandatory(true) + .setNotNull(true); + type.createProperty(P_SIZE, OType.LONG); + type.createProperty(P_CONTENT_TYPE, OType.STRING); + type.createProperty(P_BLOB_REF, OType.STRING); + type.createProperty(P_LAST_DOWNLOADED, OType.DATETIME); + type.createProperty(P_BLOB_CREATED, OType.DATETIME); + type.createProperty(P_BLOB_UPDATED, OType.DATETIME); + type.createProperty(P_CREATED_BY, OType.STRING); + type.createProperty(P_CREATED_BY_IP, OType.STRING); + + ODocument metadata = db.newInstance() + .field("ignoreNullValues", false) + .field("mergeKeys", false); + type.createIndex(I_BUCKET_COMPONENT_NAME, INDEX_TYPE.UNIQUE.name(), null, metadata, + new String[]{P_BUCKET, P_COMPONENT, P_NAME} + ); + type.createIndex(I_BUCKET_NAME, INDEX_TYPE.NOTUNIQUE, P_BUCKET, P_NAME); + type.createIndex(I_COMPONENT, INDEX_TYPE.NOTUNIQUE, P_COMPONENT); + + new OIndexBuilder(type, I_NAME_CASEINSENSITIVE, INDEX_TYPE.NOTUNIQUE) + .property(P_NAME, OType.STRING) + .caseInsensitive() + .build(db); + } + + @Override + protected Asset newEntity() { + return new Asset(); + } + + @Override + protected void readFields(final ODocument document, final Asset entity) { + super.readFields(document, entity); + + ORID componentId = document.field(P_COMPONENT, ORID.class); + String name = document.field(P_NAME, OType.STRING); + Long size = document.field(P_SIZE, OType.LONG); + String contentType = document.field(P_CONTENT_TYPE, OType.STRING); + String blobRef = document.field(P_BLOB_REF, OType.STRING); + Date lastDownloaded = document.field(P_LAST_DOWNLOADED, OType.DATETIME); + Date blobCreated = document.field(P_BLOB_CREATED, OType.DATETIME); + Date blobUpdated = document.field(P_BLOB_UPDATED, OType.DATETIME); + String createdBy = document.field(P_CREATED_BY, OType.STRING); + String createdByIp = document.field(P_CREATED_BY_IP, OType.STRING); + + if (componentId != null) { + entity.componentId(new AttachedEntityId(componentEntityAdapter, componentId)); + } + entity.name(name); + entity.size(size); + entity.contentType(contentType); + entity.createdBy(createdBy); + entity.createdByIp(createdByIp); + if (blobRef != null) { + entity.blobRef(BlobRef.parse(blobRef)); + } + if (lastDownloaded != null) { + entity.lastDownloaded(new DateTime(lastDownloaded)); + } + if (blobCreated != null) { + entity.blobCreated(new DateTime(blobCreated)); + } + if (blobUpdated != null) { + entity.blobUpdated(new DateTime(blobUpdated)); + } + } + + @Override + protected void writeFields(final ODocument document, final Asset entity) { + super.writeFields(document, entity); + + EntityId componentId = entity.componentId(); + document.field(P_COMPONENT, componentId != null ? componentEntityAdapter.recordIdentity(componentId) : null); + document.field(P_NAME, entity.name()); + document.field(P_SIZE, entity.size()); + document.field(P_CONTENT_TYPE, entity.contentType()); + document.field(P_CREATED_BY, entity.createdBy()); + document.field(P_CREATED_BY_IP, entity.createdByIp()); + BlobRef blobRef = entity.blobRef(); + document.field(P_BLOB_REF, blobRef != null ? blobRef.toString() : null); + DateTime lastDownloaded = entity.lastDownloaded(); + document.field(P_LAST_DOWNLOADED, lastDownloaded != null ? lastDownloaded.toDate() : null); + DateTime blobCreated = entity.blobCreated(); + document.field(P_BLOB_CREATED, blobCreated != null ? blobCreated.toDate() : null); + DateTime blobUpdated = entity.blobUpdated(); + document.field(P_BLOB_UPDATED, blobUpdated != null ? blobUpdated.toDate() : null); + } + + Asset findByProperty(final ODatabaseDocumentTx db, + final String propName, + final Object propValue, + final Component component) + { + checkNotNull(propName); + checkNotNull(propValue); + checkNotNull(component); + + Map parameters = ImmutableMap.of( + "bucket", bucketEntityAdapter.recordIdentity(component.bucketId()), + "component", componentEntityAdapter.recordIdentity(component), + "propValue", propValue + ); + String query = String.format( + "select from %s where %s = :bucket and %s = :component and %s = :propValue", + DB_CLASS, P_BUCKET, P_COMPONENT, propName + ); + Iterable docs = db.command(new OCommandSQL(query)).execute(parameters); + ODocument first = Iterables.getFirst(docs, null); + return first != null ? readEntity(first) : null; + } + + Iterable browseByComponent(final ODatabaseDocumentTx db, final Component component) { + checkNotNull(component); + checkState(EntityHelper.hasMetadata(component)); + + Map parameters = ImmutableMap.of( + "bucket", bucketEntityAdapter.recordIdentity(component.bucketId()), + "component", componentEntityAdapter.recordIdentity(component) + ); + String query = String.format( + "select from %s where %s = :bucket and %s = :component", + DB_CLASS, P_BUCKET, P_COMPONENT + ); + Iterable docs = db.command(new OCommandSQL(query)).execute(parameters); + return transform(docs); + } + + @Override + public boolean sendEvents() { + return true; + } + + @Override + public EntityEvent newEvent(final ODocument document, final EventKind eventKind) { + EntityMetadata metadata = new AttachedEntityMetadata(this, document); + + String repositoryName = ((ODocument) document.field(P_BUCKET)).field(P_REPOSITORY_NAME); + + ORID rid = document.field(P_COMPONENT, ORID.class); + EntityId componentId = rid != null ? new AttachedEntityId(componentEntityAdapter, rid) : null; + + switch (eventKind) { + case CREATE: + return new AssetCreatedEvent(metadata, repositoryName, componentId); + case UPDATE: + return new AssetUpdatedEvent(metadata, repositoryName, componentId); + case DELETE: + return new AssetDeletedEvent(metadata, repositoryName, componentId); + default: + return null; + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..6856f4068913449f34dac511443c572e4cc53ff2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetEvent.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityBatchEvent.Batchable; + +/** + * Asset event. + * + * @since 3.0 + */ +public interface AssetEvent + extends Batchable +{ + /** + * @since 3.1 + */ + boolean isLocal(); + + String getRepositoryName(); + + @Nullable + EntityId getComponentId(); + + EntityId getAssetId(); + + Asset getAsset(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetStore.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetStore.java new file mode 100644 index 0000000000000000000000000000000000000000..45707c31bcdc27aa2b5afc9c62c45124ddd8f103 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetStore.java @@ -0,0 +1,68 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.List; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.entity.EntityId; + +import com.orientechnologies.orient.core.index.OIndex; +import com.orientechnologies.orient.core.index.OIndexCursor; + +/** + * Store providing access to assets. + * + * @since 3.6 + */ +public interface AssetStore +{ + /** + * Get the asset matching the id or null + */ + Asset getById(EntityId id); + + /** + * Get the assets matching the ids or an empty iterable + */ + Iterable getByIds(Iterable id); + + /** + * Gets the number of assets matching the given {@link Query} clause. + */ + long countAssets(@Nullable Iterable buckets); + + /** + * Get an index to iterate over the assets in that index + */ + OIndex getIndex(String indexName); + + /** + * @param cursor to get the asset ids from + * @param limit the maximum number of records to return + * @return a page of assets in a bucket (up to limit number of assets or the end of the records available) + */ + List> getNextPage(OIndexCursor cursor, int limit); + + /** + * Save changes made to an asset + */ + Asset save(Asset asset); + + /** + * Save changes to multiple assets + */ + void save(Iterable assets); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetStoreImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetStoreImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..1fd92b872da24b06b1d91d9f85e512c8a8ee8c70 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetStoreImpl.java @@ -0,0 +1,145 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.lifecycle.Lifecycle; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.entity.AttachedEntityId; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.db.record.OIdentifiable; +import com.orientechnologies.orient.core.index.OIndex; +import com.orientechnologies.orient.core.index.OIndexCursor; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SCHEMAS; +import static org.sonatype.nexus.common.entity.EntityHelper.hasMetadata; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; + +/** + * @since 3.6 + */ +@Singleton +@ManagedLifecycle(phase = SCHEMAS) +@Named +public class AssetStoreImpl + extends StateGuardLifecycleSupport + implements AssetStore, Lifecycle +{ + private final Provider databaseInstance; + + private final AssetEntityAdapter entityAdapter; + + @Inject + public AssetStoreImpl(@Named("component") final Provider databaseInstance, + final AssetEntityAdapter entityAdapter) + { + this.databaseInstance = checkNotNull(databaseInstance); + this.entityAdapter = checkNotNull(entityAdapter); + } + + @Override + @Guarded(by = STARTED) + public Asset getById(final EntityId id) { + try (ODatabaseDocumentTx db = databaseInstance.get().acquire()) { + return entityAdapter.read(db, id); + } + } + + @Override + public Iterable getByIds(final Iterable ids) { + return inTxRetry(databaseInstance).call(db -> entityAdapter.transform(entityAdapter.documents(db, ids))); + } + + @Override + @Guarded(by = STARTED) + public long countAssets(@Nullable final Iterable buckets) { + try (ODatabaseDocumentTx db = databaseInstance.get().acquire()) { + return entityAdapter.countByQuery(db, null, null, buckets, null); + } + } + + @Override + @Guarded(by = STARTED) + public OIndex getIndex(final String indexName) { + try (ODatabaseDocumentTx db = databaseInstance.get().acquire()) { + return db.getMetadata().getIndexManager().getIndex(indexName); + } + } + + @Override + @Guarded(by = STARTED) + public List> getNextPage(final OIndexCursor cursor, final int limit) { + List> page = new ArrayList<>(limit); + + // For reasons unknown Orient needs the connection despite the code not using it + try (ODatabaseDocumentTx db = databaseInstance.get().acquire()) { + cursor.setPrefetchSize(limit); + while (page.size() < limit) { + Entry entry = cursor.nextEntry(); + if (entry == null) { + break; + } + + @SuppressWarnings("unchecked") + T key = (T) entry.getKey(); + EntityId value = new AttachedEntityId(entityAdapter, entry.getValue().getIdentity()); + page.add(new SimpleEntry<>(key, value)); + } + } + + return page; + } + + @Override + @Guarded(by = STARTED) + public Asset save(Asset asset) { + if (hasMetadata(asset)) { + inTxRetry(databaseInstance).run(db -> entityAdapter.editEntity(db, asset)); + return asset; + } + else { + return inTxRetry(databaseInstance).call(db -> entityAdapter.readEntity(entityAdapter.addEntity(db, asset))); + } + } + + @Override + public void save(final Iterable assets) { + inTxRetry(databaseInstance).run(db -> { + assets.forEach(asset -> { + if (hasMetadata(asset)) { + entityAdapter.editEntity(db, asset); + } + else { + entityAdapter.addEntity(db, asset); + } + }); + }); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetUpdatedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetUpdatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..f3de780d1c9ee98fb02bcec1fdb87aedbff5d7d1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/AssetUpdatedEvent.java @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.common.entity.EntityUpdatedEvent; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Asset updated event. + * + * @since 3.0 + */ +public class AssetUpdatedEvent + extends EntityUpdatedEvent + implements AssetEvent +{ + private final String repositoryName; + + private final EntityId componentId; + + public AssetUpdatedEvent(final EntityMetadata metadata, final String repositoryName, + @Nullable final EntityId componentId) + { + super(metadata); + this.repositoryName = checkNotNull(repositoryName); + this.componentId = componentId; + } + + public String getRepositoryName() { + return repositoryName; + } + + @Nullable + public EntityId getComponentId() { + return componentId; + } + + public EntityId getAssetId() { + return getId(); + } + + public Asset getAsset() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BlobTx.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BlobTx.java new file mode 100644 index 0000000000000000000000000000000000000000..c6eb81af56cb4ab9ca2b0e276cf1c98383dbc716 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BlobTx.java @@ -0,0 +1,191 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.io.InputStream; +import java.nio.file.Path; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.Function; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobId; +import org.sonatype.nexus.blobstore.api.BlobRef; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.common.hash.HashAlgorithm; +import org.sonatype.nexus.common.hash.MultiHashingInputStream; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.common.text.Strings2; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.hash.HashCode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA1; + +/** + * Keeps track of added and to-be-deleted blobs so they can be deleted as appropriate when the transaction ends, + * via commit or rollback. + * + * @since 3.0 + */ +class BlobTx +{ + private static final Logger log = LoggerFactory.getLogger(BlobTx.class); + + private final NodeAccess nodeAccess; + + private final BlobStore blobStore; + + private final Set newlyCreatedBlobs = Sets.newHashSet(); + + private final Map deletionRequests = Maps.newHashMap(); + + public BlobTx(final NodeAccess nodeAccess, final BlobStore blobStore) { + this.nodeAccess = checkNotNull(nodeAccess); + this.blobStore = checkNotNull(blobStore); + } + + public AssetBlob create(final InputStream inputStream, + final Map headers, + final Iterable hashAlgorithms, + final String contentType) + { + MultiHashingInputStream hashingStream = new MultiHashingInputStream(hashAlgorithms, inputStream); + Blob streamedBlob = blobStore.create(hashingStream, headers); // pre-fetch to populate hashes + return createAssetBlob( + store -> streamedBlob, + hashingStream.hashes(), + true, + contentType); + } + + /** + * Create an asset blob by hard linking to the {@code sourceFile}. + * + * @param sourceFile the file to be hard linked + * @param headers a map of headers to be applied to the resulting blob + * @param hashes the algorithms and precalculated hashes of the content + * @param contentType content type + * @param size precalculated size for the blob + * @return {@link AssetBlob} + */ + public AssetBlob createByHardLinking(final Path sourceFile, + final Map headers, + final Map hashes, + final String contentType, + final long size) + { + return createAssetBlob( + store -> store.create(sourceFile, headers, size, hashes.get(SHA1)), + hashes, + false, + contentType); + } + + /** + * Create an asset blob by copying the source blob. Throws an exception if the blob has already been deleted. + * + * @param blobId blobId of a blob already present in the blobstore + * @param headers a map of headers to be applied to the resulting blob + * @param hashes the algorithms and precalculated hashes of the content + * @return {@link AssetBlob} + * @since 3.1 + */ + public AssetBlob createByCopying(final BlobId blobId, + final Map headers, + final Map hashes, + final boolean hashesVerified) + { + checkArgument(!Strings2.isBlank(headers.get(BlobStore.CONTENT_TYPE_HEADER)), "Blob content type is required"); + // This might be a place where we might consider passing in a BlobRef instead of a BlobId, for a post-fabric world + // where repositories could be writing/reading from multiple blob stores. + return createAssetBlob( + store -> store.copy(blobId, headers), + hashes, + hashesVerified, + headers.get(BlobStore.CONTENT_TYPE_HEADER)); + } + + private AssetBlob createAssetBlob(final Function blobFunction, + final Map hashes, + final boolean hashesVerified, + final String contentType) + { + AssetBlob assetBlob = new AssetBlob( + nodeAccess, + blobStore, + blobFunction, + contentType, + hashes, + hashesVerified); + + newlyCreatedBlobs.add(assetBlob); + return assetBlob; + } + + @Nullable + public Blob get(BlobRef blobRef) { + return blobStore.get(blobRef.getBlobId()); + } + + public void delete(BlobRef blobRef, final String reason) { + deletionRequests.put(blobRef, reason); + } + + public void commit() { + for (Entry deletionRequestEntry : deletionRequests.entrySet()) { + try { + blobStore.delete(deletionRequestEntry.getKey().getBlobId(), deletionRequestEntry.getValue()); + } + catch (Throwable t) { + log.warn("Unable to delete old blob {} while committing transaction", deletionRequestEntry.getKey(), t); + } + } + for (AssetBlob assetBlob : newlyCreatedBlobs) { + try { + if (!assetBlob.isAttached()) { + assetBlob.delete("Removing unattached asset"); + } + } + catch (Throwable t) { + log.warn("Unable to delete new orphan blob {} while committing transaction", assetBlob.getBlobRef(), t); + } + } + clearState(); + } + + public void rollback() { + for (AssetBlob assetBlob : newlyCreatedBlobs) { + try { + assetBlob.delete("Rolling back new asset"); + } + catch (Throwable t) { + log.warn("Unable to delete new blob {} while rolling back transaction", assetBlob.getBlobRef(), t); + } + } + clearState(); + } + + private void clearState() { + newlyCreatedBlobs.clear(); + deletionRequests.clear(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BrowseNode.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BrowseNode.java new file mode 100644 index 0000000000000000000000000000000000000000..f246c16360f437809221c650d01b5cd74063dbb6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BrowseNode.java @@ -0,0 +1,148 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.entity.AbstractEntity; +import org.sonatype.nexus.common.entity.EntityId; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * Represents a path segment in a tree hierarchy. + * + * @since 3.6 + */ +public class BrowseNode + extends AbstractEntity +{ + private String repositoryName; + + private String parentPath; + + private String name; + + private boolean leaf; + + @Nullable + private EntityId componentId; + + @Nullable + private EntityId assetId; + + @Nullable + private String assetNameLowercase; + + public String getRepositoryName() { + return require(repositoryName, BrowseNodeEntityAdapter.P_REPOSITORY_NAME); + } + + public void setRepositoryName(final String repositoryName) { + this.repositoryName = checkNotNull(repositoryName); + } + + /** + * @since 3.7 + */ + public String getParentPath() { + return require(parentPath, BrowseNodeEntityAdapter.P_PARENT_PATH); + } + + /** + * @since 3.7 + */ + public void setParentPath(final String parentPath) { + this.parentPath = checkNotNull(parentPath); + } + + /** + * @since 3.7 + */ + public String getName() { + return require(name, BrowseNodeEntityAdapter.P_NAME); + } + + /** + * @since 3.7 + */ + public void setName(final String name) { + this.name = checkNotNull(name); + } + + /** + * @since 3.6.1 + */ + public boolean isLeaf() { + return leaf; + } + + /** + * @since 3.6.1 + */ + public void setLeaf(final boolean leaf) { + this.leaf = leaf; + } + + @Nullable + public EntityId getComponentId() { + return componentId; + } + + public void setComponentId(final EntityId componentId) { + this.componentId = checkNotNull(componentId); + } + + @Nullable + public EntityId getAssetId() { + return assetId; + } + + public void setAssetId(final EntityId assetId) { + this.assetId = checkNotNull(assetId); + } + + /** + * @since 3.6.1 + */ + @Nullable + public String getAssetNameLowercase() { + return assetNameLowercase; + } + + /** + * @since 3.6.1 + */ + public void setAssetNameLowercase(final String assetNameLowercase) { + this.assetNameLowercase = checkNotNull(assetNameLowercase); + } + + private V require(final V value, final String name) { + checkState(value != null, "Missing property: %s", name); + return value; + } + + @Override + public String toString() { + return "BrowseNode{" + + "repositoryName=" + repositoryName + + ", parentPath=" + parentPath + + ", name=" + name + + ", leaf=" + leaf + + ", componentId='" + componentId + '\'' + + ", assetId='" + assetId + '\'' + + ", assetNameLowercase='" + assetNameLowercase + '\'' + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BrowseNodeEntityAdapter.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BrowseNodeEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..4e837d68116ad7e7437cd41be9685d5773414326 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BrowseNodeEntityAdapter.java @@ -0,0 +1,502 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.UnaryOperator; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.OIndexNameBuilder; +import org.sonatype.nexus.orient.entity.AttachedEntityId; +import org.sonatype.nexus.orient.entity.IterableEntityAdapter; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; + +import com.google.common.collect.ImmutableMap; +import com.orientechnologies.common.concur.ONeedRetryException; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.sql.OCommandSQL; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.getFirst; +import static org.sonatype.nexus.common.text.Strings2.lower; + +/** + * {@link BrowseNode} entity-adapter. + * + * @since 3.6 + */ +@Named +@Singleton +public class BrowseNodeEntityAdapter + extends IterableEntityAdapter +{ + private static final String DB_CLASS = new OClassNameBuilder().type("browse_node").build(); + + public static final String P_REPOSITORY_NAME = "repository_name"; + + public static final String P_PARENT_PATH = "parent_path"; + + public static final String P_NAME = "name"; + + public static final String P_COMPONENT_ID = "component_id"; + + public static final String P_ASSET_ID = "asset_id"; + + public static final String P_ASSET_NAME_LOWERCASE = "asset_name_lowercase"; + + public static final String AUTHZ_REPOSITORY_NAME = "authz_repository_name"; + + private static final String BASE_PATH = "base_path"; + + private static final String BASE_BOUNDARY = "base_boundary"; + + private static final String SUBTREE_BOUNDARY = "subtree_boundary"; + + private static final String I_REPOSITORY_NAME_PARENT_PATH_NAME = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_REPOSITORY_NAME) + .property(P_PARENT_PATH) + .property(P_NAME) + .build(); + + private static final String I_COMPONENT_ID = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_COMPONENT_ID) + .build(); + + private static final String I_ASSET_ID = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_ASSET_ID) + .build(); + + private static final String FIND_BY_PATH = String.format( + "select expand(rid) from index:%s where key=[:%s,:%s,:%s] limit 1", + I_REPOSITORY_NAME_PARENT_PATH_NAME, P_REPOSITORY_NAME, P_PARENT_PATH, P_NAME); + + private static final String FIND_CHILDREN = String.format( + "select from %s where (%s=:%s and %s=:%s)", + DB_CLASS, P_REPOSITORY_NAME, P_REPOSITORY_NAME, P_PARENT_PATH, BASE_PATH); + + private static final String FIND_FIRST_SUBTREE = String.format( + "select from %s where (%s=:%s and %s>:%s and %s<:%s)", + DB_CLASS, P_REPOSITORY_NAME, P_REPOSITORY_NAME, P_PARENT_PATH, BASE_PATH, P_PARENT_PATH, BASE_BOUNDARY); + + private static final String FIND_NEXT_SUBTREE = String.format( + "select from %s where (%s=:%s and %s>=:%s and %s<:%s)", + DB_CLASS, P_REPOSITORY_NAME, P_REPOSITORY_NAME, P_PARENT_PATH, SUBTREE_BOUNDARY, P_PARENT_PATH, BASE_BOUNDARY); + + private static final String FIND_BY_COMPONENT = String.format( + "select from %s where %s=:%s", + DB_CLASS, P_COMPONENT_ID, P_COMPONENT_ID); + + private static final String FIND_BY_ASSET = String.format( + "select from %s where %s=:%s limit 1", + DB_CLASS, P_ASSET_ID, P_ASSET_ID); + + private static final String DELETE_BY_REPOSITORY = String.format( + "delete from %s where %s=:%s limit :limit", + DB_CLASS, P_REPOSITORY_NAME, P_REPOSITORY_NAME); + + private final ComponentEntityAdapter componentEntityAdapter; + + private final AssetEntityAdapter assetEntityAdapter; + + private final int timeoutMillis; + + @Inject + public BrowseNodeEntityAdapter(final ComponentEntityAdapter componentEntityAdapter, + final AssetEntityAdapter assetEntityAdapter, + final BrowseNodeConfiguration configuration) + { + super(DB_CLASS); + this.assetEntityAdapter = checkNotNull(assetEntityAdapter); + this.componentEntityAdapter = checkNotNull(componentEntityAdapter); + this.timeoutMillis = configuration.getQueryTimeout().toMillisI(); + } + + @Override + protected void defineType(final OClass type) { + type.createProperty(P_REPOSITORY_NAME, OType.STRING).setMandatory(true).setNotNull(true); + type.createProperty(P_PARENT_PATH, OType.STRING).setMandatory(true).setNotNull(true); + type.createProperty(P_NAME, OType.STRING).setMandatory(true).setNotNull(true); + type.createProperty(P_COMPONENT_ID, OType.LINK, componentEntityAdapter.getSchemaType()); + type.createProperty(P_ASSET_ID, OType.LINK, assetEntityAdapter.getSchemaType()); + type.createProperty(P_ASSET_NAME_LOWERCASE, OType.STRING); + } + + @Override + protected void defineType(final ODatabaseDocumentTx db, final OClass type) { + defineType(type); + + // primary index that guarantees path uniqueness for nodes in a given repository + type.createIndex(I_REPOSITORY_NAME_PARENT_PATH_NAME, INDEX_TYPE.UNIQUE, P_REPOSITORY_NAME, P_PARENT_PATH, P_NAME); + + // save space and ignore nulls because we'll never query on a null component/asset id + ODocument ignoreNullValues = db.newInstance().field("ignoreNullValues", true); + type.createIndex(I_COMPONENT_ID, INDEX_TYPE.NOTUNIQUE.name(), null, ignoreNullValues, new String[] { P_COMPONENT_ID }); + type.createIndex(I_ASSET_ID, INDEX_TYPE.UNIQUE.name(), null, ignoreNullValues, new String[] { P_ASSET_ID }); + } + + @Override + protected BrowseNode newEntity() { + return new BrowseNode(); + } + + @Override + protected void readFields(final ODocument document, final BrowseNode entity) throws Exception { + String repositoryName = document.field(P_REPOSITORY_NAME, OType.STRING); + String parentPath = document.field(P_PARENT_PATH, OType.STRING); + String name = document.field(P_NAME, OType.STRING); + + entity.setRepositoryName(repositoryName); + entity.setParentPath(parentPath); + entity.setName(name); + + ORID componentId = document.field(P_COMPONENT_ID, ORID.class); + if (componentId != null) { + entity.setComponentId(new AttachedEntityId(componentEntityAdapter, componentId)); + } + + ORID assetId = document.field(P_ASSET_ID, ORID.class); + if (assetId != null) { + entity.setAssetId(new AttachedEntityId(assetEntityAdapter, assetId)); + String assetNameLowercase = document.field(P_ASSET_NAME_LOWERCASE, OType.STRING); + entity.setAssetNameLowercase(assetNameLowercase); + } + } + + @Override + protected void writeFields(final ODocument document, final BrowseNode entity) throws Exception { + document.field(P_REPOSITORY_NAME, entity.getRepositoryName()); + document.field(P_PARENT_PATH, entity.getParentPath()); + document.field(P_NAME, entity.getName()); + + if (entity.getComponentId() != null) { + document.field(P_COMPONENT_ID, componentEntityAdapter.recordIdentity(entity.getComponentId())); + } + + if (entity.getAssetId() != null) { + document.field(P_ASSET_ID, assetEntityAdapter.recordIdentity(entity.getAssetId())); + document.field(P_ASSET_NAME_LOWERCASE, entity.getAssetNameLowercase()); + } + } + + /** + * Associates a {@link BrowseNode} with the given {@link Component}. + */ + public void createComponentNode(final ODatabaseDocumentTx db, + final String repositoryName, + final List path, + final Component component) + { + BrowseNode node = newNode(repositoryName, path); + ODocument document = findNodeRecord(db, node); + if (document == null) { + // complete the new entity before persisting + node.setComponentId(EntityHelper.id(component)); + addEntity(db, node); + } + else { + ORID oldComponentId = document.field(P_COMPONENT_ID, ORID.class); + ORID newComponentId = componentEntityAdapter.recordIdentity(component); + if (oldComponentId == null) { + // shortcut: merge new information directly into existing record + document.field(P_COMPONENT_ID, newComponentId); + document.save(); + } + else if (!oldComponentId.equals(newComponentId)) { + // retry in case this is due to an out-of-order delete event + throw new RetryUpsertException("Node already has a component"); + } + } + } + + /** + * Associates a {@link BrowseNode} with the given {@link Asset}. + */ + public void createAssetNode(final ODatabaseDocumentTx db, + final String repositoryName, + final List path, + final Asset asset) + { + BrowseNode node = newNode(repositoryName, path); + ODocument document = findNodeRecord(db, node); + if (document == null) { + // complete the new entity before persisting + node.setAssetId(EntityHelper.id(asset)); + node.setAssetNameLowercase(lower(asset.name())); + addEntity(db, node); + } + else { + ORID oldAssetId = document.field(P_ASSET_ID, ORID.class); + ORID newAssetId = assetEntityAdapter.recordIdentity(asset); + if (oldAssetId == null) { + // shortcut: merge new information directly into existing record + document.field(P_ASSET_ID, newAssetId); + document.field(P_ASSET_NAME_LOWERCASE, lower(asset.name())); + document.save(); + } + else if (!oldAssetId.equals(newAssetId)) { + // retry in case this is due to an out-of-order delete event + throw new RetryUpsertException("Node already has an asset"); + } + } + } + + /** + * Creates a basic {@link BrowseNode} for the given repository and path. + */ + private static BrowseNode newNode(final String repositoryName, final List path) { + BrowseNode node = new BrowseNode(); + node.setRepositoryName(repositoryName); + node.setParentPath(joinPath(path.subList(0, path.size() - 1))); + node.setName(path.get(path.size() - 1)); + return node; + } + + /** + * Returns the {@link BrowseNode} with the same coordinates as the sample node; {@code null} if no such node exists. + */ + @Nullable + private static ODocument findNodeRecord(final ODatabaseDocumentTx db, BrowseNode node) { + return getFirst( + db.command(new OCommandSQL(FIND_BY_PATH)).execute( + ImmutableMap.of( + P_REPOSITORY_NAME, node.getRepositoryName(), + P_PARENT_PATH, node.getParentPath(), + P_NAME, node.getName())), + null); + } + + /** + * Removes any {@link BrowseNode}s associated with the given component id. + */ + public void deleteComponentNode(final ODatabaseDocumentTx db, final EntityId componentId) { + // some formats have the same component appearing on different branches of the tree + Iterable documents = + db.command(new OCommandSQL(FIND_BY_COMPONENT)).execute( + ImmutableMap.of(P_COMPONENT_ID, recordIdentity(componentId))); + + documents.forEach(document -> { + if (document.containsField(P_ASSET_ID)) { + // asset still exists, just remove component details + document.removeField(P_COMPONENT_ID); + document.save(); + } + else { + document.delete(); + } + }); + } + + /** + * Removes the {@link BrowseNode} associated with the given asset id. + */ + public void deleteAssetNode(final ODatabaseDocumentTx db, final EntityId assetId) { + // a given asset will only appear once in the tree + ODocument document = getFirst( + db.command(new OCommandSQL(FIND_BY_ASSET)).execute( + ImmutableMap.of(P_ASSET_ID, recordIdentity(assetId))), null); + + if (document != null) { + if (document.containsField(P_COMPONENT_ID)) { + // component still exists, just remove asset details + document.removeField(P_ASSET_ID); + document.removeField(P_ASSET_NAME_LOWERCASE); + document.save(); + } + else { + document.delete(); + } + } + } + + /** + * Removes a number of {@link BrowseNode}s belonging to the given repository (so we can batch the deletes). + */ + public int deleteByRepository(final ODatabaseDocumentTx db, final String repositoryName, final int limit) { + return db.command(new OCommandSQL(DELETE_BY_REPOSITORY)).execute( + ImmutableMap.of(P_REPOSITORY_NAME, repositoryName, "limit", limit)); + } + + /** + * Returns the {@link BrowseNode}s directly visible under the given path, according to the given asset filter. + */ + public List getByPath(final ODatabaseDocumentTx db, + final String repositoryName, + final List path, + final int maxNodes, + final String assetFilter, + final Map filterParameters) + { + // timeout function which helps avoid runaway subtree queries when filtering assets + UnaryOperator timeoutFunction = configureTimeoutFunction(assetFilter); + + List listing = new ArrayList<>(); + + Map parameters = new HashMap<>(filterParameters); + parameters.put(P_REPOSITORY_NAME, repositoryName); + + // STEP 1: make a note of any direct child nodes with visible assets (or no asset) + + OCommandSQL sql = buildQuery(FIND_CHILDREN, true, assetFilter, maxNodes); + + String basePath = joinPath(path); + parameters.put(BASE_PATH, basePath); + + Map children = new HashMap<>(); + transform(db.command(sql).execute(parameters)).forEach(child -> { + children.put(child.getName(), child); + }); + + // STEP 2: search for the first subtree with at least one (indirect) visible asset + + sql = buildQuery(FIND_FIRST_SUBTREE, false, assetFilter, 1); + + // subtree nodes have paths greater than '/org/foo/base/' and less than '/org/foo/base0' + String baseBoundary = basePath.substring(0, basePath.length() - 1) + '0'; + parameters.put(BASE_BOUNDARY, baseBoundary); + + List subtree = db.command(timeoutFunction.apply(sql)).execute(parameters); + + sql = buildQuery(FIND_NEXT_SUBTREE, false, assetFilter, 1); + + while (!subtree.isEmpty() && listing.size() < maxNodes) { + + // STEP 3: build node from subtree path, using direct child nodes to fill in details + + // extract the name of the child folder directly under the base path + String childName = childName(basePath, subtree.get(0).field(P_PARENT_PATH)); + + // use direct child node if available, as it has the component/asset detail + BrowseNode child = children.remove(childName); + if (child == null) { + // otherwise create a placeholder/virtual node that leads to the subtree + child = new BrowseNode(); + child.setRepositoryName(repositoryName); + child.setParentPath(basePath); + child.setName(childName); + } + listing.add(child); + + // STEP 4: move on to the next subtree with at least one (indirect) visible asset + + // jump past the current subtree, for example if the last subtree was '/org/foo/base/wibble/' + // then we kick-off the next search with any paths greater or equal to '/org/foo/base/wibble0' + + String subtreeBoundary = basePath + childName + '0'; + parameters.put(SUBTREE_BOUNDARY, subtreeBoundary); + + subtree = db.command(timeoutFunction.apply(sql)).execute(parameters); + } + + // STEP 5: add any leftover direct child nodes with visible assets, and mark them as leaves + + for (BrowseNode child : children.values()) { + if (child.getAssetId() != null) { + child.setLeaf(true); // we know this is a leaf because we didn't find a matching subtree + listing.add(child); + } + } + + return listing; + } + + /** + * Function that applies a gradually reducing timeout across successive queries until a deadline. + */ + private UnaryOperator configureTimeoutFunction(final String assetFilter) { + // only apply if we're filtering assets as that's when subtree queries could take a while + if (timeoutMillis > 0 && !assetFilter.isEmpty()) { + long deadlineMillis = System.currentTimeMillis() + timeoutMillis; + return sql -> { + long remainingMillis = Math.max(1, deadlineMillis - System.currentTimeMillis()); + return new OCommandSQL(sql.getText() + " timeout " + remainingMillis + " return"); + }; + } + return UnaryOperator.identity(); // otherwise apply no timeout + } + + /** + * Joins segments into a path which always starts and ends with a single slash. + */ + private static String joinPath(final List path) { + StringBuilder buf = new StringBuilder("/"); + path.forEach(s -> buf.append(s).append('/')); + return buf.toString(); + } + + /** + * Extracts the name of the folder directly after the base path. + * + * Assumes basePath is a prefix of path, and basePath ends in a slash. + */ + private static String childName(final String basePath, final String path) { + return path.substring(basePath.length(), path.indexOf('/', basePath.length())); + } + + /** + * Builds a visible node query from the primary select clause, optional asset filter, and limit. + * + * Optionally include nodes which don't have assets (regardless of the filter) to allow their + * component details to be used in the final listing when they overlap with visible subtrees. + */ + private static OCommandSQL buildQuery(final String select, + final boolean includeNonAssetNodes, + final String assetFilter, + final int limit) + { + StringBuilder buf = new StringBuilder(select); + + if (!assetFilter.isEmpty()) { + buf.append(" and (").append(P_ASSET_ID); + if (includeNonAssetNodes) { + buf.append(" is null or "); + } + else { + buf.append(" is not null and "); + } + buf.append(assetFilter).append(')'); + } + + buf.append(" limit ").append(limit); + + return new OCommandSQL(buf.toString()); + } + + /** + * {@link ONeedRetryException} thrown when we want to retry upserting a {@link BrowseNode}. + */ + private static class RetryUpsertException + extends ONeedRetryException + { + RetryUpsertException(final String message) { + super(message); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BrowseNodeStore.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BrowseNodeStore.java new file mode 100644 index 0000000000000000000000000000000000000000..85501b500d54135e30ebeaf091c05843437645c7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BrowseNodeStore.java @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.List; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.repository.Repository; + +/** + * Store providing access to the browse tree for assets & components. + * + * @since 3.7 + */ +public interface BrowseNodeStore +{ + /** + * Creates a {@link BrowseNode} for the given asset. + */ + void createAssetNode(String repositoryName, List path, Asset asset); + + /** + * Creates a {@link BrowseNode} for the given component. + */ + void createComponentNode(String repositoryName, List path, Component component); + + /** + * Deletes the asset's {@link BrowseNode}. + */ + void deleteAssetNode(EntityId assetId); + + /** + * Deletes the component's {@link BrowseNode}. + */ + void deleteComponentNode(EntityId componentId); + + /** + * Deletes all {@link BrowseNode}s belonging to the given repository. + */ + void deleteByRepository(String repositoryName); + + /** + * Returns the {@link BrowseNode}s directly visible under the given path. + * + * An optional substring can be supplied to further filter the nodes by name. + */ + Iterable getByPath(Repository repository, List path, int maxNodes, @Nullable String filter); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BrowseNodeStoreImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BrowseNodeStoreImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..17224921d2e407f4afbd0eca48dbcfa89c341c08 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BrowseNodeStoreImpl.java @@ -0,0 +1,317 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.common.text.Strings2; +import org.sonatype.nexus.logging.task.ProgressLogIntervalHelper; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; +import org.sonatype.nexus.repository.group.GroupFacet; +import org.sonatype.nexus.repository.security.RepositoryViewPermission; +import org.sonatype.nexus.repository.types.GroupType; +import org.sonatype.nexus.security.BreadActions; +import org.sonatype.nexus.security.SecurityHelper; +import org.sonatype.nexus.selector.CselAssetSql; +import org.sonatype.nexus.selector.CselAssetSqlBuilder; +import org.sonatype.nexus.selector.CselSelector; +import org.sonatype.nexus.selector.SelectorConfiguration; +import org.sonatype.nexus.selector.SelectorManager; + +import com.orientechnologies.common.concur.ONeedRetryException; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.storage.ORecordDuplicatedException; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.toList; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SCHEMAS; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTx; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; +import static org.sonatype.nexus.repository.storage.BrowseNodeEntityAdapter.AUTHZ_REPOSITORY_NAME; +import static org.sonatype.nexus.repository.storage.BrowseNodeEntityAdapter.P_ASSET_ID; +import static org.sonatype.nexus.repository.storage.BrowseNodeEntityAdapter.P_ASSET_NAME_LOWERCASE; + +/** + * @since 3.7 + */ +@Singleton +@ManagedLifecycle(phase = SCHEMAS) +@Named +public class BrowseNodeStoreImpl + extends StateGuardLifecycleSupport + implements BrowseNodeStore +{ + private static final String ASSET_FIELD_PREFIX = P_ASSET_ID + '.'; + + private final Provider databaseInstance; + + private final BrowseNodeEntityAdapter entityAdapter; + + private final SecurityHelper securityHelper; + + private final SelectorManager selectorManager; + + private final CselAssetSqlBuilder cselAssetSqlBuilder; + + private final int deletePageSize; + + private final boolean enabled; + + @Inject + public BrowseNodeStoreImpl(@Named("component") final Provider databaseInstance, + final BrowseNodeEntityAdapter entityAdapter, + final SecurityHelper securityHelper, + final SelectorManager selectorManager, + final CselAssetSqlBuilder cselAssetSqlBuilder, + final BrowseNodeConfiguration configuration) + { + this.databaseInstance = checkNotNull(databaseInstance); + this.entityAdapter = checkNotNull(entityAdapter); + this.securityHelper = checkNotNull(securityHelper); + this.selectorManager = checkNotNull(selectorManager); + this.cselAssetSqlBuilder = checkNotNull(cselAssetSqlBuilder); + this.deletePageSize = configuration.getDeletePageSize(); + this.enabled = configuration.isEnabled(); + } + + @Override + protected void doStart() throws Exception { + if (enabled) { + try (ODatabaseDocumentTx db = databaseInstance.get().connect()) { + entityAdapter.register(db); + } + } + } + + @Override + @Guarded(by = STARTED) + public void createComponentNode(final String repositoryName, final List path, final Component component) { + inTxRetry(databaseInstance) + // handle case where assets try to create the exact same component-level path at once + .retryOn(ONeedRetryException.class, ORecordDuplicatedException.class) + .run(db -> entityAdapter.createComponentNode(db, repositoryName, path, component)); + } + + @Override + @Guarded(by = STARTED) + public void createAssetNode(final String repositoryName, final List path, final Asset asset) { + inTxRetry(databaseInstance) + // handle case where an asset and its component try to create the exact same path at once + .retryOn(ONeedRetryException.class, ORecordDuplicatedException.class) + .run(db -> entityAdapter.createAssetNode(db, repositoryName, path, asset)); + } + + @Override + @Guarded(by = STARTED) + public void deleteComponentNode(EntityId componentId) { + inTxRetry(databaseInstance).run(db -> entityAdapter.deleteComponentNode(db, componentId)); + } + + @Override + @Guarded(by = STARTED) + public void deleteAssetNode(final EntityId assetId) { + inTxRetry(databaseInstance).run(db -> entityAdapter.deleteAssetNode(db, assetId)); + } + + @Override + @Guarded(by = STARTED) + public void deleteByRepository(final String repositoryName) { + log.debug("Deleting all browse nodes for repository {}", repositoryName); + + ProgressLogIntervalHelper progressLogger = new ProgressLogIntervalHelper(log, 60); + + int deletedCount; + do { + deletedCount = inTxRetry(databaseInstance).call( + db -> entityAdapter.deleteByRepository(db, repositoryName, deletePageSize)); + + progressLogger.info("Deleted {} browse nodes for repository {} in {}", + deletedCount, repositoryName, progressLogger.getElapsed()); + } + while (deletedCount == deletePageSize); + + progressLogger.flush(); + + log.debug("All browse nodes deleted for repository {} in {}", repositoryName, progressLogger.getElapsed()); + } + + @Override + @Guarded(by = STARTED) + public Iterable getByPath(final Repository repository, + final List path, + final int maxNodes, + @Nullable final String keyword) + { + List selectors = emptyList(); + + String repositoryName = repository.getName(); + String format = repository.getFormat().getValue(); + if (!hasBrowsePermission(repositoryName, format)) { + // user doesn't have repository-wide access so need to apply content selection + selectors = selectorManager.browseActive(asList(repositoryName), asList(format)); + if (selectors.isEmpty()) { + return emptyList(); // no browse permission and no selectors -> no results + } + } + + Map filterParameters = new HashMap<>(); + String assetFilter = buildAssetFilter(repository, keyword, selectors, filterParameters); + + if (repository.getType() instanceof GroupType) { + // overlay member results, first-one-wins if there are any nodes with the same name + return members(repository) + .map(m -> getByPath(m.getName(), path, maxNodes, assetFilter, filterParameters)) + .flatMap(List::stream) + .filter(distinctByName()) + .limit(maxNodes) + .collect(toList()); + } + else { + return getByPath(repository.getName(), path, maxNodes, assetFilter, filterParameters); + } + } + + /** + * Returns a filter that discards nodes which have the same name as an earlier node. + * + * Warning: this method is not thread-safe, so don't use it with a parallel stream + */ + private static Predicate distinctByName() { + Set names = new HashSet<>(); + return node -> names.add(node.getName()); + } + + /** + * Returns stream of all non-group repositories reachable from the given repository. + */ + private static Stream members(final Repository repository) { + return repository.facet(GroupFacet.class).leafMembers().stream(); + } + + /** + * Returns the browse nodes directly visible under the path according to the given asset filter. + */ + private List getByPath(final String repositoryName, + final List path, + final int maxNodes, + @Nullable final String assetFilter, + @Nullable final Map filterParameters) + { + return inTx(databaseInstance).call( + db -> entityAdapter.getByPath(db, repositoryName, path, maxNodes, assetFilter, filterParameters)); + } + + /** + * Builds an asset filter in SQL for the current user. + */ + private String buildAssetFilter(final Repository repository, + @Nullable final String keyword, + final List selectors, + final Map filterParameters) + { + StringBuilder filterBuilder = new StringBuilder(); + if (keyword != null) { + appendKeywordFilter(filterBuilder, keyword); + } + if (!selectors.isEmpty()) { + if (filterBuilder.length() > 0) { + filterBuilder.append(" and "); + } + appendContentAuthFilter(filterBuilder, repository, selectors, filterParameters); + } + return filterBuilder.toString(); + } + + /** + * Does the current user have permission to browse the full repository? + */ + private boolean hasBrowsePermission(final String repositoryName, final String format) { + return securityHelper.anyPermitted(new RepositoryViewPermission(format, repositoryName, BreadActions.BROWSE)); + } + + /** + * Appends a keyword filter in SQL. + */ + private void appendKeywordFilter(final StringBuilder filterBuilder, final String keyword) { + filterBuilder.append(P_ASSET_NAME_LOWERCASE).append(" like '%").append(Strings2.lower(keyword)).append("%'"); + } + + /** + * Appends a content authentication filter in SQL for the current user. + */ + private void appendContentAuthFilter(final StringBuilder filterBuilder, + final Repository repository, + final List selectors, + final Map filterParameters) + { + String repositoryName = repository.getName(); + String format = repository.getFormat().getValue(); + + if (selectors.size() > 1) { + filterBuilder.append('('); + } + + int cselCount = 0; + + for (SelectorConfiguration selector : selectors) { + if (CselSelector.TYPE.equals(selector.getType())) { + if (cselCount > 0) { + filterBuilder.append(" or "); + } + + String expression = (String) selector.getAttributes().get("expression"); + CselAssetSql cselAssetSql = cselAssetSqlBuilder.buildWhereClause( + expression, format, "s" + (cselCount++) + "p", ASSET_FIELD_PREFIX); + filterBuilder.append('(').append(cselAssetSql.getSql()).append(')'); + + filterParameters.putAll(cselAssetSql.getSqlParameters()); + } + } + + if (selectors.size() > cselCount) { + if (cselCount > 0) { + filterBuilder.append(" or "); + } + + // call 'contentAuth' function if we need to evaluate any non-CSEL selectors (such as JEXL based selectors) + filterBuilder.append(String.format("contentAuth(@this.%s, :%s, true) = true", P_ASSET_ID, AUTHZ_REPOSITORY_NAME)); + + filterParameters.put(AUTHZ_REPOSITORY_NAME, repositoryName); + } + + if (selectors.size() > 1) { + filterBuilder.append(')'); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/Bucket.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/Bucket.java new file mode 100644 index 0000000000000000000000000000000000000000..e241212ea1bccb87ee293fa3a79f7c8aab3b13fb --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/Bucket.java @@ -0,0 +1,68 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.AbstractEntity; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; + +/** + * A logical container of components and assets. + * + * @since 3.0 + */ +public class Bucket + extends AbstractEntity +{ + /** + * An identifying name for disaster recovery purposes (which isn't required to be strictly unique) + */ + public static final String REPO_NAME_HEADER = "Bucket.repo-name"; + + private String repositoryName; + + private NestedAttributesMap attributes; + + /** + * Gets the repository name. + */ + public String getRepositoryName() { + return repositoryName; + } + + /** + * Sets the repository name. + */ + public void setRepositoryName(final String repositoryName) { + this.repositoryName = repositoryName; + } + + /** + * Gets the "attributes" property of this node, a map of maps that is possibly empty, but never {@code null}. + */ + public NestedAttributesMap attributes() { + checkState(attributes != null, "Missing attributes: %s", P_ATTRIBUTES); + return attributes; + } + + /** + * Sets the attributes. + */ + Bucket attributes(final NestedAttributesMap attributes) { + this.attributes = checkNotNull(attributes); + return this; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BucketDeleter.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BucketDeleter.java new file mode 100644 index 0000000000000000000000000000000000000000..75ce967e198803804e7befac096bca9f2acba7f0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BucketDeleter.java @@ -0,0 +1,192 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.blobstore.api.BlobRef; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.blobstore.api.BlobStoreManager; +import org.sonatype.nexus.common.stateguard.InvalidStateException; +import org.sonatype.nexus.orient.DatabaseInstance; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; + +/** + * Encapsulates the operation of deleting a bucket and its contents. Used by {@code StorageFacetManagerImpl}. + * + * @since 3.2.1 + */ +@Named +@Singleton +public class BucketDeleter + extends ComponentSupport +{ + private static final long DELETE_BATCH_SIZE = 100L; + + private final Provider databaseInstanceProvider; + + private final BucketEntityAdapter bucketEntityAdapter; + + private final ComponentEntityAdapter componentEntityAdapter; + + private final AssetEntityAdapter assetEntityAdapter; + + private final BlobStoreManager blobStoreManager; + + @Inject + public BucketDeleter(@Named(ComponentDatabase.NAME) final Provider databaseInstanceProvider, + final BucketEntityAdapter bucketEntityAdapter, + final ComponentEntityAdapter componentEntityAdapter, + final AssetEntityAdapter assetEntityAdapter, + final BlobStoreManager blobStoreManager) { + this.databaseInstanceProvider = checkNotNull(databaseInstanceProvider); + this.bucketEntityAdapter = checkNotNull(bucketEntityAdapter); + this.componentEntityAdapter = checkNotNull(componentEntityAdapter); + this.assetEntityAdapter = checkNotNull(assetEntityAdapter); + this.blobStoreManager = checkNotNull(blobStoreManager); + } + + /** + * Deletes an existing bucket and all components and assets within. + * + * NOTE: This is a potentially long-lived and non-atomic operation. Items within the bucket will be + * sequentially deleted in batches in order to keep memory use within reason. This method will automatically + * commit a transaction for each batch, and will return after committing the last batch. + */ + public void deleteBucket(final Bucket bucket) throws InterruptedException { + checkNotNull(bucket); + inTxRetry(databaseInstanceProvider).throwing(InterruptedException.class).run(db -> { + + long count = 0; + + List deletedBlobs = new ArrayList<>(); + Set deletedBlobStores = new HashSet<>(); + + // first delete all components and constituent assets + for (Component component : componentEntityAdapter.browseByBucket(db, bucket)) { + deleteComponent(db, deletedBlobs, component); + count++; + if (count == DELETE_BATCH_SIZE) { + commitBatch(db, deletedBlobs, deletedBlobStores); + count = 0; + if (Thread.interrupted()) { + throw new InterruptedException(); + } + } + } + commitBatch(db, deletedBlobs, deletedBlobStores); + + // then delete all standalone assets + for (Asset asset : assetEntityAdapter.browseByBucket(db, bucket)) { + deleteAsset(db, deletedBlobs, asset); + count++; + if (count == DELETE_BATCH_SIZE) { + commitBatch(db, deletedBlobs, deletedBlobStores); + count = 0; + if (Thread.interrupted()) { + throw new InterruptedException(); + } + } + } + commitBatch(db, deletedBlobs, deletedBlobStores); + + // finally, delete the bucket document + bucketEntityAdapter.deleteEntity(db, bucket); + db.commit(); + }); + } + + private void deleteAsset(final ODatabaseDocumentTx db, final List deletedBlobs, final Asset asset) { + checkNotNull(db); + checkNotNull(deletedBlobs); + checkNotNull(asset); + + BlobRef ref = asset.blobRef(); + if (ref != null) { + deletedBlobs.add(ref); + } + assetEntityAdapter.deleteEntity(db, asset); + } + + private void deleteComponent(final ODatabaseDocumentTx db, + final List deletedBlobs, + final Component component) + { + checkNotNull(db); + checkNotNull(deletedBlobs); + checkNotNull(component); + + for (Asset asset : assetEntityAdapter.browseByComponent(db, component)) { + deleteAsset(db, deletedBlobs, asset); + } + componentEntityAdapter.deleteEntity(db, component); + } + + private void commitBatch(final ODatabaseDocumentTx db, + final List deletedBlobs, + final Set deletedBlobStores) + { + checkNotNull(db); + checkNotNull(deletedBlobs); + checkNotNull(deletedBlobStores); + + db.commit(); + + for (BlobRef blobRef : deletedBlobs) { + deleteBlob(deletedBlobStores, blobRef); + } + + deletedBlobs.clear(); + } + + private void deleteBlob(final Set deletedBlobStores, final BlobRef blobRef) { + String blobStoreName = blobRef.getStore(); + if (deletedBlobStores.contains(blobStoreName)) { + return; + } + + BlobStore blobStore = blobStoreManager.get(blobRef.getStore()); + if (blobStore == null) { + if (deletedBlobStores.add(blobStoreName)) { + log.info("Not deleting blobs for blob store {}, blob store not found", blobStoreName); + } + return; + } + + try { + blobStore.delete(blobRef.getBlobId(), "Deleting Bucket"); + } + catch (InvalidStateException e) { + if (deletedBlobStores.add(blobStoreName)) { + log.info("Not deleting blobs for blob store {}, invalid state {}", blobStoreName, e.getInvalidState(), + log.isDebugEnabled() ? e : null); + } + } + catch (Exception e) { + log.warn("Error deleting blob {}, skipping", blobRef, e); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BucketEntityAdapter.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BucketEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..0c4fba01671fbcec1b855c2a79ab0106ab5dc6bd --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BucketEntityAdapter.java @@ -0,0 +1,139 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Map; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.EntityEvent; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.OIndexNameBuilder; +import org.sonatype.nexus.orient.entity.AttachedEntityMetadata; +import org.sonatype.nexus.orient.entity.IterableEntityAdapter; +import org.sonatype.nexus.orient.entity.action.ReadEntityByPropertyAction; +import org.sonatype.nexus.repository.storage.internal.BucketCreatedEvent; +import org.sonatype.nexus.repository.storage.internal.BucketDeletedEvent; +import org.sonatype.nexus.repository.storage.internal.BucketUpdatedEvent; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; + +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; + +/** + * {@link Bucket} entity-adapter. + * + * @since 3.0 + */ +@Named +@Singleton +public class BucketEntityAdapter + extends IterableEntityAdapter +{ + private static final String DB_CLASS = new OClassNameBuilder() + .type("bucket") + .build(); + + /** + * Key of {@link Bucket} repository name attribute. + */ + public static final String P_REPOSITORY_NAME = "repository_name"; + + private static final String I_REPOSITORY_NAME = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_REPOSITORY_NAME) + .build(); + + private final ReadEntityByPropertyAction read = new ReadEntityByPropertyAction<>(this, P_REPOSITORY_NAME); + + @Inject + public BucketEntityAdapter() { + super(DB_CLASS); + } + + @Override + protected void defineType(final OClass type) { + type.createProperty(P_REPOSITORY_NAME, OType.STRING) + .setMandatory(true) + .setNotNull(true); + type.createProperty(P_ATTRIBUTES, OType.EMBEDDEDMAP) + .setNotNull(true); + + type.createIndex(I_REPOSITORY_NAME, INDEX_TYPE.UNIQUE, P_REPOSITORY_NAME); + } + + @Override + protected Bucket newEntity() { + return new Bucket(); + } + + @Override + protected void readFields(final ODocument document, final Bucket entity) { + String repositoryName = document.field(P_REPOSITORY_NAME, OType.STRING); + Map attributes = document.field(P_ATTRIBUTES, OType.EMBEDDEDMAP); + + entity.setRepositoryName(repositoryName); + entity.attributes(new NestedAttributesMap(P_ATTRIBUTES, attributes)); + } + + @Override + protected void writeFields(final ODocument document, final Bucket entity) { + document.field(P_REPOSITORY_NAME, entity.getRepositoryName()); + document.field(P_ATTRIBUTES, entity.attributes().backing()); + } + + // + // Actions + // + + /** + * @since 3.1 + */ + @Nullable + public Bucket read(final ODatabaseDocumentTx db, final String name) { + return read.execute(db, name); + } + + @Override + public boolean sendEvents() { + return true; + } + + @Nullable + @Override + public EntityEvent newEvent(final ODocument document, final EventKind eventKind) { + final EntityMetadata metadata = new AttachedEntityMetadata(this, document); + final String repositoryName = document.field(P_REPOSITORY_NAME); + + log.debug("newEvent: eventKind: {}, repositoryName: {}, metadata: {}", eventKind, repositoryName, metadata); + switch (eventKind) { + case CREATE: + return new BucketCreatedEvent(metadata, repositoryName); + case UPDATE: + return new BucketUpdatedEvent(metadata, repositoryName); + case DELETE: + return new BucketDeletedEvent(metadata, repositoryName); + default: + return null; + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BucketStore.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BucketStore.java new file mode 100644 index 0000000000000000000000000000000000000000..0c200da809c2a10031f0073c2146fad7801ae909 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BucketStore.java @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.entity.EntityId; + +/** + * Store providing access to the buckets. + * + * @since 3.6 + */ +public interface BucketStore +{ + /** + * @param repositoryName + * @return the bucket for the repository name + */ + Bucket read(String repositoryName); + + /** + * Retrieve a bucket by its id + */ + @Nullable + Bucket getById(EntityId bucketId); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BucketStoreImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BucketStoreImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..b99326c6fc07c296373fb3a33576dc09775bb246 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/BucketStoreImpl.java @@ -0,0 +1,70 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.lifecycle.Lifecycle; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.orient.DatabaseInstance; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SCHEMAS; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; + +/** + * @since 3.6 + */ +@Singleton +@ManagedLifecycle(phase = SCHEMAS) +@Named +public class BucketStoreImpl + extends StateGuardLifecycleSupport + implements BucketStore, Lifecycle +{ + private final Provider databaseInstance; + + private final BucketEntityAdapter entityAdapter; + + @Inject + public BucketStoreImpl(@Named("component") final Provider databaseInstance, + final BucketEntityAdapter entityAdapter) + { + this.databaseInstance = checkNotNull(databaseInstance); + this.entityAdapter = checkNotNull(entityAdapter); + } + + @Override + @Guarded(by = STARTED) + public Bucket read(final String repositoryName) + { + try (ODatabaseDocumentTx db = databaseInstance.get().connect()) { + return entityAdapter.read(db, repositoryName); + } + } + + @Override + public Bucket getById(final EntityId bucketId) { + try (ODatabaseDocumentTx db = databaseInstance.get().acquire()) { + return entityAdapter.read(db, bucketId); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/Component.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/Component.java new file mode 100644 index 0000000000000000000000000000000000000000..e06c4ba7f2f7bd5173f03341d3808a9d31d071cd --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/Component.java @@ -0,0 +1,68 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.annotation.Nullable; + +/** + * Metadata about a software component. + * + * @since 3.7 + */ +public interface Component + extends MetadataNode +{ + /** + * Gets the group or {@code null} if undefined. + */ + @Nullable + String group(); + + /** + * Gets the group or throws a runtime exception if undefined. + */ + String requireGroup(); + + /** + * Sets the group to the given value, or {@code null} to un-define it. + */ + Component group(@Nullable final String group); + + /** + * Gets the version or {@code null} if undefined. + */ + @Nullable + String version(); + + /** + * Gets the version or throws a runtime exception if undefined. + */ + String requireVersion(); + + /** + * Sets the version to the given value, or {@code null} to un-define it. + */ + Component version(@Nullable final String version); + + /** + * The default {@link #toString()} includes the metadata which can leak out internal data when used for an external + * use case where it is exposed to the end user. For example, in a rest call. This method can be used for those cases + * instead to not leak out this data. + */ + default String toStringExternal() { + return "group=" + group() + + ", name=" + name() + + ", version=" + version() + + ", format=" + format(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentCreatedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentCreatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..d164da808a9473d3bd99451cc67adb7a66a4eb97 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentCreatedEvent.java @@ -0,0 +1,48 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import org.sonatype.nexus.common.entity.EntityCreatedEvent; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Component created event. + * + * @since 3.0 + */ +public class ComponentCreatedEvent + extends EntityCreatedEvent + implements ComponentEvent +{ + private final String repositoryName; + + public ComponentCreatedEvent(final EntityMetadata metadata, final String repositoryName) { + super(metadata); + this.repositoryName = checkNotNull(repositoryName); + } + + public String getRepositoryName() { + return repositoryName; + } + + public EntityId getComponentId() { + return getId(); + } + + public Component getComponent() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentDatabase.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentDatabase.java new file mode 100644 index 0000000000000000000000000000000000000000..6990a264ee3aeb0047737773dc1ac37d3907934d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentDatabase.java @@ -0,0 +1,58 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseManager; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Shared {@code component} database components. + * + * @since 3.0 + */ +public class ComponentDatabase +{ + private ComponentDatabase() { + // empty + } + + public static final String NAME = "component"; + + /** + * Shared {@code component} database instance provider. + */ + @Named(NAME) + @Singleton + public static class ProviderImpl + implements Provider + { + private final DatabaseManager databaseManager; + + @Inject + public ProviderImpl(final DatabaseManager databaseManager) { + this.databaseManager = checkNotNull(databaseManager); + } + + @Override + public DatabaseInstance get() { + return databaseManager.instance(NAME); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentDatabaseCheckpoint.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentDatabaseCheckpoint.java new file mode 100644 index 0000000000000000000000000000000000000000..3649283e4f8492cfac8a908db72f62876af137a0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentDatabaseCheckpoint.java @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.common.upgrade.Checkpoints; +import org.sonatype.nexus.orient.DatabaseCheckpointSupport; +import org.sonatype.nexus.orient.DatabaseInstance; + +/** + * Upgrade checkpoint for the "component" database. + * + * @since 3.1 + */ +@Named +@Singleton +@Checkpoints(model = ComponentDatabase.NAME) +public class ComponentDatabaseCheckpoint + extends DatabaseCheckpointSupport +{ + @Inject + public ComponentDatabaseCheckpoint(@Named(ComponentDatabase.NAME) final Provider databaseInstance, + final ApplicationDirectories appDirectories) + { + super(ComponentDatabase.NAME, databaseInstance, appDirectories); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentDecorator.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..c3ffd0f09d350783590af7dc505a699b9e704b38 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentDecorator.java @@ -0,0 +1,23 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +/** + * Decorator interface for the {@link Component} class + * + * @since 3.8 + */ +public interface ComponentDecorator +{ + Component decorate(Component component); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentDeletedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentDeletedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..47585271ea959d66d2f59f82f5e01d603a714aee --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentDeletedEvent.java @@ -0,0 +1,48 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import org.sonatype.nexus.common.entity.EntityDeletedEvent; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Component deleted event. + * + * @since 3.0 + */ +public class ComponentDeletedEvent + extends EntityDeletedEvent + implements ComponentEvent +{ + private final String repositoryName; + + public ComponentDeletedEvent(final EntityMetadata metadata, final String repositoryName) { + super(metadata); + this.repositoryName = checkNotNull(repositoryName); + } + + public String getRepositoryName() { + return repositoryName; + } + + public EntityId getComponentId() { + return getId(); + } + + public Component getComponent() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentEntityAdapter.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..b52f807e34bdab080274d8ade9e7e69092916963 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentEntityAdapter.java @@ -0,0 +1,261 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.entity.EntityEvent; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.OIndexBuilder; +import org.sonatype.nexus.orient.OIndexNameBuilder; +import org.sonatype.nexus.orient.entity.AttachedEntityMetadata; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.orientechnologies.orient.core.collate.OCaseInsensitiveCollate; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.sql.OCommandSQL; +import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.String.format; +import static org.sonatype.nexus.common.entity.EntityHelper.id; +import static org.sonatype.nexus.repository.storage.BucketEntityAdapter.P_REPOSITORY_NAME; + +/** + * {@link Component} entity-adapter. + * + * @since 3.0 + */ +@Named +@Singleton +public class ComponentEntityAdapter + extends MetadataNodeEntityAdapter +{ + public static final String DB_CLASS = new OClassNameBuilder() + .type("component") + .build(); + + /** + * Key of {@link Component} group coordinate. + */ + public static final String P_GROUP = "group"; + + /** + * Key of {@link Component} version coordinate. + */ + public static final String P_VERSION = "version"; + + /** + * Key of {@link Component} ci_name (case-insensitive name) field. + */ + public static final String P_CI_NAME = "ci_name"; + + private static final String I_BUCKET_GROUP_NAME_VERSION = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_BUCKET) + .property(P_GROUP) + .property(P_NAME) + .property(P_VERSION) + .build(); + + private static final String I_BUCKET_NAME_VERSION = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_BUCKET) + .property(P_NAME) + .property(P_VERSION) + .build(); + + private static final String I_CI_NAME_CASE_INSENSITIVE = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_CI_NAME) + .caseInsensitive() + .build(); + + public static final String I_GROUP_NAME_VERSION_INSENSITIVE = new OIndexNameBuilder() + .type(DB_CLASS) + .property(P_GROUP) + .property(P_NAME) + .property(P_VERSION) + .caseInsensitive() + .build(); + + private static final String EXISTS_QUERY_STRING = + format("select from index:%1$s where key = [:%2$s, :%3$s, :%4$s, :%5$s]", + I_BUCKET_GROUP_NAME_VERSION, P_BUCKET, P_GROUP, P_NAME, P_VERSION); + + + private static final OSQLSynchQuery EXISTS_QUERY = new OSQLSynchQuery<>(EXISTS_QUERY_STRING, 1); + + private final ComponentFactory componentFactory; + + private final Set componentEntityAdapterExtensions; + + @Inject + public ComponentEntityAdapter(final BucketEntityAdapter bucketEntityAdapter, + final ComponentFactory componentFactory, + final Set componentEntityAdapterExtensions) + { + super(DB_CLASS, bucketEntityAdapter); + this.componentFactory = componentFactory; + this.componentEntityAdapterExtensions = checkNotNull(componentEntityAdapterExtensions); + } + + @Override + protected void defineType(final ODatabaseDocumentTx db, final OClass type) { + super.defineType(type); + type.createProperty(P_GROUP, OType.STRING); + type.createProperty(P_NAME, OType.STRING) + .setMandatory(true) + .setNotNull(true); + type.createProperty(P_VERSION, OType.STRING); + type.createProperty(P_CI_NAME, OType.STRING) + .setCollate(new OCaseInsensitiveCollate()) + .setMandatory(true) + .setNotNull(true); + + ODocument metadata = db.newInstance() + .field("ignoreNullValues", false) + .field("mergeKeys", false); + type.createIndex(I_BUCKET_GROUP_NAME_VERSION, INDEX_TYPE.UNIQUE.name(), null, metadata, + new String[]{P_BUCKET, P_GROUP, P_NAME, P_VERSION}); + type.createIndex(I_BUCKET_NAME_VERSION, INDEX_TYPE.NOTUNIQUE.name(), null, metadata, + new String[]{P_BUCKET, P_NAME, P_VERSION}); + + new OIndexBuilder(type, I_GROUP_NAME_VERSION_INSENSITIVE, INDEX_TYPE.NOTUNIQUE) + .property(P_GROUP, OType.STRING) + .property(P_NAME, OType.STRING) + .property(P_VERSION, OType.STRING) + .caseInsensitive() + .build(db); + + new OIndexBuilder(type, I_CI_NAME_CASE_INSENSITIVE, INDEX_TYPE.NOTUNIQUE) + .property(P_CI_NAME, OType.STRING) + .caseInsensitive() + .build(db); + + componentEntityAdapterExtensions.forEach(d -> d.defineType(db, type)); + } + + public Iterable browseByNameCaseInsensitive(final ODatabaseDocumentTx db, + final String name, + @Nullable final Iterable buckets, + @Nullable final String querySuffix) + { + checkNotNull(name); + + String whereClause = " where (ci_name = :name)"; + StringBuilder query = new StringBuilder("select from " + DB_CLASS + whereClause); + + addBucketConstraints(whereClause, buckets, query); + + if (querySuffix != null) { + query.append(' ').append(querySuffix); + } + + Map parameters = ImmutableMap.of("name", name); + log.debug("Finding {}s with query: {}, parameters: {}", getTypeName(), query, parameters); + return transform(db.command(new OCommandSQL(query.toString())).execute(parameters)); + } + + /** + * Check for the existence of a component with {@code group}, {@code name}, and {@code version} in {@code bucket}. + * + * @since 3.8 + */ + public boolean exists(final ODatabaseDocumentTx db, + @Nullable final String group, + final String name, + @Nullable final String version, + final Bucket bucket) + { + Map params = Maps.newHashMap(); + params.put(P_GROUP, group); + params.put(P_NAME, checkNotNull(name)); + params.put(P_VERSION, version); + params.put(P_BUCKET, recordIdentity(id(checkNotNull(bucket)))); + return !Iterables.isEmpty(db.command(EXISTS_QUERY).>execute(params)); + } + + @Override + protected Component newEntity() { + return componentFactory.createComponent(); + } + + @Override + protected void readFields(final ODocument document, final Component entity) { + super.readFields(document, entity); + + String group = document.field(P_GROUP, OType.STRING); + String name = document.field(P_NAME, OType.STRING); + String version = document.field(P_VERSION, OType.STRING); + + entity.group(group); + entity.name(name); + entity.version(version); + + componentEntityAdapterExtensions.forEach(d -> d.readFields(document, entity)); + } + + @Override + protected void writeFields(final ODocument document, final Component entity) { + super.writeFields(document, entity); + + document.field(P_GROUP, entity.group()); + document.field(P_NAME, entity.name()); + document.field(P_VERSION, entity.version()); + + // need to lowercase ID value as workaround for https://www.prjhub.com/#/issues/8750 (case-insensitive querying) + // indirect queries against CI fields when another table is involved lose their case-insensitiveness, so we store + // as lowercase to permit those kinds of queries to query on lowercase as a workaround for now. + document.field(P_CI_NAME, entity.name().toLowerCase(Locale.ENGLISH)); + + componentEntityAdapterExtensions.forEach(d -> d.writeFields(document, entity)); + } + + @Override + public boolean sendEvents() { + return true; + } + + @Override + public EntityEvent newEvent(final ODocument document, final EventKind eventKind) { + EntityMetadata metadata = new AttachedEntityMetadata(this, document); + + String repositoryName = ((ODocument) document.field(P_BUCKET)).field(P_REPOSITORY_NAME); + + switch (eventKind) { + case CREATE: + return new ComponentCreatedEvent(metadata, repositoryName); + case UPDATE: + return new ComponentUpdatedEvent(metadata, repositoryName); + case DELETE: + return new ComponentDeletedEvent(metadata, repositoryName); + default: + return null; + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentEntityAdapterExtension.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentEntityAdapterExtension.java new file mode 100644 index 0000000000000000000000000000000000000000..03cca34362d8a90f6cc8bda36f148c03d167bd02 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentEntityAdapterExtension.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.record.impl.ODocument; + +/** + * Extension point for the {@link ComponentEntityAdapter} to define/read/write additional fields to Orient on a {@link + * Component} instance + * + * @since 3.8 + */ +public interface ComponentEntityAdapterExtension +{ + /** + * @see ComponentEntityAdapter#defineType(ODatabaseDocumentTx, OClass) + */ + void defineType(ODatabaseDocumentTx db, OClass type); + + /** + * @see ComponentEntityAdapter#readFields(ODocument, Component) + */ + void readFields(ODocument document, Component component); + + /** + * @see ComponentEntityAdapter#writeFields(ODocument, Component) + */ + void writeFields(ODocument document, Component component); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..070f23732acf199c4faf49c3fbeec347dfceff49 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentEvent.java @@ -0,0 +1,36 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityBatchEvent.Batchable; + +/** + * Component event. + * + * @since 3.0 + */ +public interface ComponentEvent + extends Batchable +{ + /** + * @since 3.1 + */ + boolean isLocal(); + + String getRepositoryName(); + + EntityId getComponentId(); + + Component getComponent(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentFactory.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..39ee987815f24a3b235cfe801e58777958ec3964 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentFactory.java @@ -0,0 +1,47 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Factory for creating {@link Component} instances. + * + * @since 3.8 + */ +@Singleton +@Named +public class ComponentFactory +{ + private final Set componentDecorators; + + @Inject + public ComponentFactory(final Set componentDecorators) + { + this.componentDecorators = checkNotNull(componentDecorators); + } + + public Component createComponent() { + Component component = new DefaultComponent(); + for (ComponentDecorator componentDecorator : componentDecorators) { + component = componentDecorator.decorate(component); + } + return component; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentMaintenance.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentMaintenance.java new file mode 100644 index 0000000000000000000000000000000000000000..59e0799d24b0495a0cd65ace7cbf9612cd15e76f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentMaintenance.java @@ -0,0 +1,36 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.repository.Facet; + +/** + * Exposes manual component maintenance operations. + * + * @since 3.0 + */ +@Facet.Exposed +public interface ComponentMaintenance + extends Facet +{ + /** + * Deletes a component from storage. + */ + void deleteComponent(EntityId componentId); + + /** + * Deletes an asset from storage. + */ + void deleteAsset(EntityId assetId); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentStore.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentStore.java new file mode 100644 index 0000000000000000000000000000000000000000..bd058557aca896dd8086404f616ebcccf0b409a6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentStore.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import org.sonatype.nexus.common.entity.EntityId; + +/** + * Store providing access to components. + * + * @since 3.6 + */ +public interface ComponentStore +{ + /** + * @param id + * @return the component for the id + */ + Component read(EntityId id); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentStoreImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentStoreImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..69fa0772c20fda09e9ef3f7d7d624e4239ec0275 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentStoreImpl.java @@ -0,0 +1,63 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.lifecycle.Lifecycle; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.orient.DatabaseInstance; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SCHEMAS; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; + +/** + * @since 3.6 + */ +@Singleton +@ManagedLifecycle(phase = SCHEMAS) +@Named +public class ComponentStoreImpl + extends StateGuardLifecycleSupport + implements ComponentStore, Lifecycle +{ + private final Provider databaseInstance; + + private final ComponentEntityAdapter entityAdapter; + + @Inject + public ComponentStoreImpl(@Named("component") final Provider databaseInstance, + final ComponentEntityAdapter entityAdapter) + { + this.databaseInstance = checkNotNull(databaseInstance); + this.entityAdapter = checkNotNull(entityAdapter); + } + + @Override + @Guarded(by = STARTED) + public Component read(final EntityId id) + { + try (ODatabaseDocumentTx db = databaseInstance.get().acquire()) { + return entityAdapter.read(db, id); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentUpdatedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentUpdatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..7bf0ffee227ecb23f4bf822a6cf3926bc7fb72f8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ComponentUpdatedEvent.java @@ -0,0 +1,48 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.common.entity.EntityUpdatedEvent; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Component updated event. + * + * @since 3.0 + */ +public class ComponentUpdatedEvent + extends EntityUpdatedEvent + implements ComponentEvent +{ + private final String repositoryName; + + public ComponentUpdatedEvent(final EntityMetadata metadata, final String repositoryName) { + super(metadata); + this.repositoryName = checkNotNull(repositoryName); + } + + public String getRepositoryName() { + return repositoryName; + } + + public EntityId getComponentId() { + return getId(); + } + + public Component getComponent() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ContentSelectorUpgradeManager.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ContentSelectorUpgradeManager.java new file mode 100644 index 0000000000000000000000000000000000000000..555b34370decaf64d9ba930e35f4d35336e63194 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ContentSelectorUpgradeManager.java @@ -0,0 +1,72 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.selector.CselSelector; +import org.sonatype.nexus.selector.CselValidator; +import org.sonatype.nexus.selector.SelectorManager; + +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.TASKS; + +/** + * @since 3.6 + */ +@Named +@ManagedLifecycle(phase = TASKS) +@Singleton +public class ContentSelectorUpgradeManager + extends StateGuardLifecycleSupport +{ + private final CselValidator cselValidator; + + private final SelectorManager selectorManager; + + @Inject + public ContentSelectorUpgradeManager(final CselValidator cselValidator, final SelectorManager selectorManager) { + this.cselValidator = cselValidator; + this.selectorManager = selectorManager; + } + + @Override + protected void doStart() throws Exception { + selectorManager.browseJexl().forEach(config -> { + String expression = (String) config.getAttributes().get("expression"); + String name = config.getName(); + + log.debug("Attempting to upgrade jexl content selector to csel, expression={}", expression); + + try { + if (cselValidator.validate(expression)) { + config.setType(CselSelector.TYPE); + selectorManager.update(config); + } + else { + log.warn( + "Could not convert deprecated jexl content selector into csel content selector with name={}, expression={}", + name, expression); + } + } + catch (Exception e) { + log.warn( + "Failed to parse jexl content selector for conversion to csel content selector with name={}, expression={}", + name, expression, log.isDebugEnabled() ? e : null); + } + }); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ContentValidator.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ContentValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..da2e0807ff0df20549f128e9604de46d431b83d9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ContentValidator.java @@ -0,0 +1,56 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.io.IOException; +import java.io.InputStream; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.sonatype.nexus.mime.MimeRulesSource; +import org.sonatype.nexus.repository.InvalidContentException; + +import com.google.common.base.Supplier; + +/** + * Content validator interface. + * + * @since 3.0 + */ +public interface ContentValidator +{ + /** + * Determines or confirms the content type for the given content, or throws {@link InvalidContentException} if it + * cannot. + * + * @param strictContentTypeValidation whether the check should be strict or not. + * @param contentSupplier the supplier of the content to determine or confirm content type. + * @param contentName blob name, usually a file path or file name or just extension + * (file extension is used to determine content type along with "magic" detection + * where actual content bits are used, like file headers or magic bytes). Is + * optional, but be aware that if present it improves content type detection + * reliability. + * @param mimeRulesSource if non-null, mime rules source to use. + * @param declaredContentType if non-null, the declared content type will be confirmed, if null, this method + * will attempt to determine the content type. + * @return the content type of the content. + * @throws InvalidContentException if type cannot be confirmed or detected. + */ + @Nonnull + String determineContentType(boolean strictContentTypeValidation, + Supplier contentSupplier, + @Nullable MimeRulesSource mimeRulesSource, + @Nullable String contentName, + @Nullable String declaredContentType) throws IOException; +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ContentValidatorSelector.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ContentValidatorSelector.java new file mode 100644 index 0000000000000000000000000000000000000000..638b69f8f8ed2b47e51a77816aee2486d3b663d3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/ContentValidatorSelector.java @@ -0,0 +1,66 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.Repository; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Content validator selector component. + * + * @since 3.0 + */ +@Singleton +@Named +public class ContentValidatorSelector + extends ComponentSupport +{ + private final Map contentValidators; + + private final DefaultContentValidator defaultContentValidator; + + @Inject + public ContentValidatorSelector(final Map contentValidators, + final DefaultContentValidator defaultContentValidator) + { + this.contentValidators = checkNotNull(contentValidators); + this.defaultContentValidator = checkNotNull(defaultContentValidator); + } + + /** + * Find content validator for given repository. If no format-specific validator is configured, the default is used. + * + * @param repository The repository for content validator is looked up. + * @return the repository specific content validator to be used, or the default content validator, never {@code null}. + */ + @Nonnull + public ContentValidator validator(final Repository repository) { + checkNotNull(repository); + String format = repository.getFormat().getValue(); + log.trace("Looking for content validator for format: {}", format); + ContentValidator contentValidator = contentValidators.get(format); + if (contentValidator == null) { + return defaultContentValidator; + } + return contentValidator; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DatabaseThreadUtils.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DatabaseThreadUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..5cbfdb982debf7b057befb15b1157abdd6f6232e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DatabaseThreadUtils.java @@ -0,0 +1,50 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.concurrent.Callable; + +import com.google.common.base.Throwables; +import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal; +import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal; + +/** + * Utility class for working with different databases in the same thread. If you can find a way to avoid using this, + * you probably should. + * + * @since 3.1 + */ +public final class DatabaseThreadUtils +{ + /** + * Utility function for working around "ODatabaseException: Database instance is not set in current thread" issues. + * The current database ThreadLocal is preserved and restored after calling the lambda. + */ + public static T withOtherDatabase(Callable function) { + final ODatabaseDocumentInternal db = ODatabaseRecordThreadLocal.INSTANCE.getIfDefined(); + try { + return function.call(); + } + catch (Exception e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + finally { + ODatabaseRecordThreadLocal.INSTANCE.set(db); + } + } + + private DatabaseThreadUtils() { + // empty + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DecoratedComponent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DecoratedComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..d8a87f90acdc4feb60bb8c669ac9cadf8133c5c1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DecoratedComponent.java @@ -0,0 +1,156 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.decorator.DecoratedObject; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; + +import org.joda.time.DateTime; + +/** + * Base abstract decorator for the {@link Component} class + * + * @since 3.8 + */ +public abstract class DecoratedComponent + implements Component, DecoratedObject +{ + protected final Component component; + + protected DecoratedComponent(final Component component) { + this.component = component; + } + + @Override + public Component getWrappedObject() { + return component; + } + + @Nullable + @Override + public String group() { + return component.group(); + } + + @Override + public String requireGroup() { + return component.requireGroup(); + } + + @Override + public Component group(@Nullable final String group) { + return component.group(group); + } + + @Nullable + @Override + public String version() { + return component.version(); + } + + @Override + public String requireVersion() { + return component.requireVersion(); + } + + @Override + public Component version(@Nullable final String version) { + return component.version(version); + } + + @Override + public boolean isNew() { + return component.isNew(); + } + + @Override + public Component newEntity(final boolean newEntity) { + return component.newEntity(newEntity); + } + + @Override + public EntityId bucketId() { + return component.bucketId(); + } + + @Override + public Component bucketId(final EntityId bucketId) { + return component.bucketId(bucketId); + } + + @Override + public String name() { + return component.name(); + } + + @Override + public Component name(final String name) { + return component.name(name); + } + + @Nullable + @Override + public DateTime lastUpdated() { + return component.lastUpdated(); + } + + @Override + public DateTime requireLastUpdated() { + return component.requireLastUpdated(); + } + + @Override + public Component lastUpdated(final DateTime lastUpdated) { + return component.lastUpdated(lastUpdated); + } + + @Override + public String format() { + return component.format(); + } + + @Override + public Component format(final String format) { + return component.format(format); + } + + @Override + public NestedAttributesMap attributes() { + return component.attributes(); + } + + @Override + public Component attributes(final NestedAttributesMap attributes) { + return component.attributes(attributes); + } + + @Override + public NestedAttributesMap formatAttributes() { + return component.formatAttributes(); + } + + @Nullable + @Override + public EntityMetadata getEntityMetadata() { + return component.getEntityMetadata(); + } + + @Override + public void setEntityMetadata(@Nullable final EntityMetadata metadata) { + component.setEntityMetadata(metadata); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DefaultComponent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DefaultComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..39362d334e02d4fe985d3ab7daa0fe9368a835c6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DefaultComponent.java @@ -0,0 +1,87 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.annotation.Nullable; + +import static org.sonatype.nexus.repository.storage.ComponentEntityAdapter.P_GROUP; +import static org.sonatype.nexus.repository.storage.ComponentEntityAdapter.P_VERSION; + +/** + * @since 3.7 + */ +public class DefaultComponent + extends AbstractMetadataNode + implements Component +{ + private String group; + + private String version; + + /** + * Gets the group or {@code null} if undefined. + */ + @Nullable + public String group() { + return group; + } + + /** + * Gets the group or throws a runtime exception if undefined. + */ + public String requireGroup() { + return require(group, P_GROUP); + } + + /** + * Sets the group to the given value, or {@code null} to un-define it. + */ + public DefaultComponent group(@Nullable final String group) { + this.group = group; + return this; + } + + /** + * Gets the version or {@code null} if undefined. + */ + @Nullable + public String version() { + return version; + } + + /** + * Gets the version or throws a runtime exception if undefined. + */ + public String requireVersion() { + return require(version, P_VERSION); + } + + /** + * Sets the version to the given value, or {@code null} to un-define it. + */ + public DefaultComponent version(@Nullable final String version) { + this.version = version; + return this; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "metadata=" + getEntityMetadata() + + ", name=" + name() + + ", version=" + version() + + ", group=" + group() + + '}'; + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DefaultComponentMaintenanceImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DefaultComponentMaintenanceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..bca4881c70c6fd6299adba447b524a5ae1880000 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DefaultComponentMaintenanceImpl.java @@ -0,0 +1,89 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.inject.Named; + +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.transaction.TransactionalDeleteBlob; +import org.sonatype.nexus.transaction.UnitOfWork; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; + +/** + * A default implementation of {@link ComponentMaintenance} for repository format that don't need + * additional bookkeeping. + * + * @since 3.0 + */ +@Named +public class DefaultComponentMaintenanceImpl + extends FacetSupport + implements ComponentMaintenance +{ + /** + * Deletes the component directly, with no additional bookkeeping. + */ + @Override + public void deleteComponent(final EntityId componentId) { + checkNotNull(componentId); + UnitOfWork.begin(getRepository().facet(StorageFacet.class).txSupplier()); + try { + deleteComponentTx(componentId); + } + finally { + UnitOfWork.end(); + } + } + + @TransactionalDeleteBlob + protected void deleteComponentTx(final EntityId componentId) { + StorageTx tx = UnitOfWork.currentTx(); + Component component = tx.findComponentInBucket(componentId, tx.findBucket(getRepository())); + if (component == null) { + return; + } + log.info("Deleting component: {}", component); + tx.deleteComponent(component); + } + + /** + * Deletes the asset directly, with no additional bookkeeping. + */ + @Override + @Guarded(by = STARTED) + public void deleteAsset(final EntityId assetId) { + checkNotNull(assetId); + UnitOfWork.begin(getRepository().facet(StorageFacet.class).txSupplier()); + try { + deleteAssetTx(assetId); + } + finally { + UnitOfWork.end(); + } + } + + @TransactionalDeleteBlob + protected void deleteAssetTx(final EntityId assetId) { + StorageTx tx = UnitOfWork.currentTx(); + Asset asset = tx.findAsset(assetId, tx.findBucket(getRepository())); + if (asset == null) { + return; + } + log.info("Deleting asset: {}", asset); + tx.deleteAsset(asset); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DefaultContentValidator.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DefaultContentValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..dc304f2b477bee82aec77d24befed28f04ddcb52 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/DefaultContentValidator.java @@ -0,0 +1,172 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.io.IOException; +import java.io.InputStream; +import java.util.LinkedHashSet; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.mime.MimeRulesSource; +import org.sonatype.nexus.mime.MimeSupport; +import org.sonatype.nexus.repository.InvalidContentException; +import org.sonatype.nexus.repository.view.ContentTypes; + +import com.google.common.base.Strings; +import com.google.common.base.Supplier; +import com.google.common.collect.Sets; +import com.google.common.net.MediaType; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default {@link ContentValidator}. + * + * @since 3.0 + */ +@Named(DefaultContentValidator.NAME) +@Singleton +public class DefaultContentValidator + extends ComponentSupport + implements ContentValidator +{ + public static final String NAME = "default"; + + private final MimeSupport mimeSupport; + + @Inject + public DefaultContentValidator(final MimeSupport mimeSupport) { + this.mimeSupport = checkNotNull(mimeSupport); + } + + @Nonnull + @Override + public String determineContentType(boolean strictContentTypeValidation, + Supplier contentSupplier, + @Nullable MimeRulesSource mimeRulesSource, + @Nullable String contentName, + @Nullable String declaredContentType) throws IOException + { + checkNotNull(contentSupplier); + final String declaredBaseContentType = mediaTypeWithoutParameters(declaredContentType); + + final LinkedHashSet contentDetectedMimeTypes = new LinkedHashSet<>(); + try (InputStream is = contentSupplier.get()) { + contentDetectedMimeTypes.addAll(mimeSupport.detectMimeTypes(is, contentName)); + } + adjustIfHtml(contentDetectedMimeTypes); + log.debug("Mime support detects {} as {}", contentName, contentDetectedMimeTypes); + + if (strictContentTypeValidation && isUnknown(contentDetectedMimeTypes)) { + throw new InvalidContentException("Content type could not be determined: " + contentName); + } + + final LinkedHashSet nameAssumedMimeTypes = new LinkedHashSet<>(); + if (contentName != null) { + nameAssumedMimeTypes.addAll( + mimeSupport.guessMimeTypesListFromPath( + contentName, + mimeRulesSource != null ? mimeRulesSource : MimeRulesSource.NOOP) + ); + adjustIfHtml(nameAssumedMimeTypes); + log.debug("Mime support assumes {} as {}", contentName, nameAssumedMimeTypes); + + if (!isUnknown(nameAssumedMimeTypes)) { + Set intersection = Sets.intersection(contentDetectedMimeTypes, nameAssumedMimeTypes); + log.debug("content/name types intersection {}", intersection); + if (strictContentTypeValidation && intersection.isEmpty()) { + throw new InvalidContentException( + String.format("Detected content type %s, but expected %s: %s", + contentDetectedMimeTypes, nameAssumedMimeTypes, contentName) + ); + } + } + } + + String finalContentType; + if (!isUnknown(nameAssumedMimeTypes)) { + // format implied type or known extension + finalContentType = nameAssumedMimeTypes.iterator().next(); + } + else if (!isUnknown(contentDetectedMimeTypes)) { + // use the content based one + finalContentType = contentDetectedMimeTypes.iterator().next(); + } + else if (!Strings.isNullOrEmpty(declaredBaseContentType)) { + // use the declared if declared at all + finalContentType = declaredBaseContentType; + } + else { + // give up + finalContentType = ContentTypes.APPLICATION_OCTET_STREAM; + } + + log.debug("Content {} declared as {}, determined as {}", contentName, declaredContentType, finalContentType); + + return finalContentType; + } + + /** + * Removes any parameter (like charset) for simpler matching. + */ + @Nullable + private String mediaTypeWithoutParameters(final String declaredMediaType) { + if (Strings.isNullOrEmpty(declaredMediaType)) { + return null; + } + + try { + MediaType mediaType = MediaType.parse(declaredMediaType); + return mediaType.withoutParameters().toString(); + } + catch (IllegalArgumentException e) { + //https://maven.oracle.com is sending out incomplete content-type header, so lets try to clean it up and reprocess + //i.e. 'Application/jar;charset=' + int idx = declaredMediaType.indexOf(';'); + if (idx >= 0) { + String parsedDeclaredMediaType = declaredMediaType.substring(0, idx); + log.debug("Invalid declared contentType {} will retry with {}", declaredMediaType, parsedDeclaredMediaType, e); + return mediaTypeWithoutParameters(parsedDeclaredMediaType); + } + + throw new InvalidContentException("Content type could not be determined: " + declaredMediaType, e); + } + } + + /** + * Returns {@code true} if list of mimeTypes is actually saying "unknown" content. + */ + private boolean isUnknown(final Set mimeTypes) { + return mimeTypes.isEmpty() || (mimeTypes.size() == 1 && mimeTypes.contains(ContentTypes.APPLICATION_OCTET_STREAM)); + } + + /** + * Circumvention if mime type covers HTML: Reason for exceptional handling HTML is due to XHTML (augmented with +xml, + * basically subtype of XML) and "plain" HTML. They are not related in any way (supertype or alias) in registry but + * still both represent "html" (as general term) in what we are interested to perform validation. + */ + private void adjustIfHtml(final Set mimeTypes) { + if (mimeTypes.contains(MediaType.HTML_UTF_8.withoutParameters().toString()) || + mimeTypes.contains(MediaType.XHTML_UTF_8.withoutParameters().toString())) { + // to circumvent on xhtml vs html, we will treat both as text/html which is okay for content validation purposes + mimeTypes.add(ContentTypes.TEXT_HTML); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/MetadataNode.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/MetadataNode.java new file mode 100644 index 0000000000000000000000000000000000000000..21e09e0ed91c6d751acf964ff8d6799a6f35b272 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/MetadataNode.java @@ -0,0 +1,95 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.Entity; +import org.sonatype.nexus.common.entity.EntityId; + +import org.joda.time.DateTime; + +/** + * Wraps an {@code ODocument} to provide a simpler API for working with stored component and asset metadata. + * + * @since 3.7 + */ +public interface MetadataNode + extends Entity +{ + /** + * Is this entity new as of this transaction? + */ + boolean isNew(); + + T newEntity(final boolean newEntity); + + /** + * Gets the bucket this is part of. + */ + EntityId bucketId(); + + T bucketId(final EntityId bucketId); + + /** + * Gets the name. + */ + String name(); + + /** + * Sets the name. + */ + T name(final String name); + + /** + * Gets the last updated date or {@code null} if undefined (the node has never been saved). + */ + @Nullable + DateTime lastUpdated(); + + /** + * Gets the last updated date or throws a runtime exception if undefined. + */ + DateTime requireLastUpdated(); + + /** + * Sets the last updated date. + */ + T lastUpdated(final DateTime lastUpdated); + + /** + * Gets the format property, which is immutable. + */ + String format(); + + /** + * Sets the format. + */ + T format(final String format); + + /** + * Gets the "attributes" property of this node, a map of maps that is possibly empty, but never {@code null}. + */ + NestedAttributesMap attributes(); + + /** + * Sets the attributes. + */ + T attributes(final NestedAttributesMap attributes); + + /** + * Gets the format-specific attributes of this node ("attributes.formatName"). + */ + NestedAttributesMap formatAttributes(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/MetadataNodeEntityAdapter.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/MetadataNodeEntityAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..33aeadaf6c25c96898db4fa1c33763d7ae0d891e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/MetadataNodeEntityAdapter.java @@ -0,0 +1,252 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.orient.entity.AttachedEntityId; +import org.sonatype.nexus.orient.entity.IterableEntityAdapter; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.sql.OCommandSQL; +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Iterables.isEmpty; +import static org.sonatype.nexus.common.text.Strings2.isBlank; + +/** + * {@link MetadataNode} entity-adapter. + * + * @since 3.0 + */ +public abstract class MetadataNodeEntityAdapter> + extends IterableEntityAdapter +{ + /** + * Key of {@link Bucket}, {@link Component} and {@link Asset} attributes nested map. + */ + public static final String P_ATTRIBUTES = "attributes"; + + /** + * Key of {@link Component} and {@link Asset} bucket reference attribute. + */ + public static final String P_BUCKET = "bucket"; + + /** + * Key of {@link Component} and {@link Asset} attribute for format reference. + */ + public static final String P_FORMAT = "format"; + + /** + * Key of {@link Component} name coordinate. + */ + public static final String P_NAME = "name"; + + /** + * Key of {@link Component} and {@link Asset} attribute denoting when the record was last updated. This denotes a + * timestamp when CMA last modified any attribute of the record, and has nothing to do with content change, it's age + * or it's last modified attributes. This property is present always on {@link Component} and {@link Asset}. + * + * @see MetadataNodeEntityAdapter#writeFields(ODocument, MetadataNode) + */ + static final String P_LAST_UPDATED = "last_updated"; + + protected final BucketEntityAdapter bucketEntityAdapter; + + public MetadataNodeEntityAdapter(final String typeName, final BucketEntityAdapter bucketEntityAdapter) { + super(typeName); + this.bucketEntityAdapter = bucketEntityAdapter; + } + + @Override + protected void defineType(final OClass type) { + type.createProperty(P_BUCKET, OType.LINK, bucketEntityAdapter.getSchemaType()).setMandatory(true).setNotNull(true); + type.createProperty(P_FORMAT, OType.STRING).setMandatory(true).setNotNull(true); + type.createProperty(P_LAST_UPDATED, OType.DATETIME); + type.createProperty(P_ATTRIBUTES, OType.EMBEDDEDMAP); + } + + @Override + protected void readFields(final ODocument document, final T entity) { + ORID bucketId = document.field(P_BUCKET, ORID.class); + String format = document.field(P_FORMAT, OType.STRING); + Date lastUpdated = document.field(P_LAST_UPDATED, OType.DATETIME); + Map attributes = document.field(P_ATTRIBUTES, OType.EMBEDDEDMAP); + + entity.bucketId(new AttachedEntityId(bucketEntityAdapter, bucketId)); + entity.format(format); + entity.lastUpdated(new DateTime(lastUpdated)); + entity.attributes(new NestedAttributesMap(P_ATTRIBUTES, detachable(attributes))); + entity.newEntity(document.getIdentity().isNew()); + } + + @Override + protected void writeFields(final ODocument document, final T entity) { + document.field(P_BUCKET, bucketEntityAdapter.recordIdentity(entity.bucketId())); + document.field(P_FORMAT, entity.format()); + document.field(P_LAST_UPDATED, new Date()); + document.field(P_ATTRIBUTES, entity.attributes().backing()); + } + + Iterable browseByBucket(final ODatabaseDocumentTx db, final Bucket bucket) { + checkNotNull(bucket); + checkState(EntityHelper.hasMetadata(bucket)); + + Map parameters = ImmutableMap.of( + P_BUCKET, bucketEntityAdapter.recordIdentity(bucket) + ); + String query = String.format( + "select from %s where %s = :bucket", + getTypeName(), P_BUCKET + ); + Iterable docs = OrientAsyncHelper.asyncIterable(db, query, parameters); + return transform(docs); + } + + T findByProperty(final ODatabaseDocumentTx db, + final String propName, final Object propValue, + final Bucket bucket) + { + checkNotNull(propName); + checkNotNull(propValue); + checkNotNull(bucket); + + Map parameters = ImmutableMap.of( + P_BUCKET, bucketEntityAdapter.recordIdentity(bucket), + "propValue", propValue + ); + String query = String.format( + "select from %s where %s = :bucket and %s = :propValue", + getTypeName(), P_BUCKET, propName + ); + Iterable docs = db.command(new OCommandSQL(query)).execute(parameters); + ODocument first = Iterables.getFirst(docs, null); + return first != null ? readEntity(first) : null; + } + + T findByProperty(final ODatabaseDocumentTx db, final String propName, final Object propValue) { + checkNotNull(propName); + checkNotNull(propValue); + + Map parameters = ImmutableMap.of("propValue", propValue); + String query = String.format("select from %s where %s = :propValue", getTypeName(), propName); + Iterable docs = db.command(new OCommandSQL(query)).execute(parameters); + ODocument first = Iterables.getFirst(docs, null); + return first != null ? readEntity(first) : null; + } + + Iterable browseByQuery(final ODatabaseDocumentTx db, + @Nullable final String whereClause, + @Nullable final Map parameters, + @Nullable final Iterable buckets, + @Nullable final String querySuffix) + { + String query = buildQuery(false, whereClause, buckets, querySuffix); + + if (isBlank(query)) { + log.debug("Skipped finding {}s as query is empty, parameters: {}", getTypeName(), parameters); + return Collections.emptyList(); + } + + log.debug("Finding {}s with query: {}, parameters: {}", getTypeName(), query, parameters); + Iterable docs = db.command(new OCommandSQL(query)).execute(parameters); + return transform(docs); + } + + long countByQuery(final ODatabaseDocumentTx db, + @Nullable final String whereClause, + @Nullable final Map parameters, + @Nullable final Iterable buckets, + @Nullable final String querySuffix) + { + String query = buildQuery(true, whereClause, buckets, querySuffix); + + if (isBlank(query)) { + log.debug("Skipped counting {}s as query is empty, parameters: {}", getTypeName(), parameters); + return 0; + } + + log.debug("Counting {}s with query: {}, parameters: {}", getTypeName(), query, parameters); + List results = db.command(new OCommandSQL(query)).execute(parameters); + return results.get(0).field("count"); + } + + private String buildQuery(final boolean isCount, + @Nullable final String whereClause, + @Nullable final Iterable buckets, + @Nullable final String querySuffix) + { + // constrained by buckets, but no buckets were provided + if (buckets != null && isEmpty(buckets)) { + return ""; + } + + StringBuilder query = new StringBuilder(); + query.append("select"); + if (isCount) { + query.append(" count(*)"); + } + query.append(" from ").append(getTypeName()); + if (whereClause != null) { + query.append(" where (").append(whereClause).append(")"); + } + + addBucketConstraints(whereClause, buckets, query); + + if (querySuffix != null) { + query.append(" ").append(querySuffix); + } + + return query.toString(); + } + + /** + * Constrain a query to certain buckets. + */ + protected void addBucketConstraints(@Nullable final String whereClause, + @Nullable final Iterable buckets, + final StringBuilder query) + { + if (buckets != null && !isEmpty(buckets)) { + if (whereClause == null) { + query.append(" where "); + } + else { + query.append(" and "); + } + + query.append('('); + Joiner.on(" or ").appendTo(query, + Iterables.transform(buckets, bucket -> + String.format("%s=%s", P_BUCKET, bucketEntityAdapter.recordIdentity(bucket).toString()) + )); + query.append(')'); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/MimeRulesSourceSelector.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/MimeRulesSourceSelector.java new file mode 100644 index 0000000000000000000000000000000000000000..206fa1a648e97e0c9e639937bddb0b13559b1095 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/MimeRulesSourceSelector.java @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.mime.MimeRulesSource; +import org.sonatype.nexus.repository.Repository; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * MIME rules source selector component. + * + * @since 3.0 + */ +@Singleton +@Named +public class MimeRulesSourceSelector + extends ComponentSupport +{ + private final Map mimeRulesSources; + + @Inject + public MimeRulesSourceSelector(final Map mimeRulesSources) + { + this.mimeRulesSources = checkNotNull(mimeRulesSources); + } + + /** + * Find MIME rule source for given repository. If no format-specific source is configured, {@link + * MimeRulesSource#NOOP} is returned. + * + * @param repository The repository for MIME rule source is looked up. + * @return the repository specific MIME rule source, or noop rule selector. + */ + @Nonnull + public MimeRulesSource ruleSource(final Repository repository) { + checkNotNull(repository); + String format = repository.getFormat().getValue(); + log.trace("Looking for MIME rule source for format: {}", format); + MimeRulesSource mimeRulesSource = mimeRulesSources.get(format); + if (mimeRulesSource != null) { + return mimeRulesSource; + } + return MimeRulesSource.NOOP; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/MissingBlobException.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/MissingBlobException.java new file mode 100644 index 0000000000000000000000000000000000000000..3eb2dda29b0742f3889ce455e0d8d27b3d0fc92b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/MissingBlobException.java @@ -0,0 +1,35 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import org.sonatype.nexus.blobstore.api.BlobRef; + +/** + * Thrown when attempting to access blob content which is now missing from the blobstore. + * + * @since 3.2 + */ +public class MissingBlobException + extends IllegalStateException +{ + private final BlobRef blobRef; + + public MissingBlobException(final BlobRef blobRef) { + super(String.format("Blob %s exists in metadata, but is missing from the blobstore", blobRef)); + this.blobRef = blobRef; + } + + public BlobRef getBlobRef() { + return blobRef; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/OrientAsyncHelper.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/OrientAsyncHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..57b28f5a70331b28d8c211510d387bda88f43225 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/OrientAsyncHelper.java @@ -0,0 +1,228 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.common.ComponentSupport; + +import com.google.common.annotations.VisibleForTesting; +import com.orientechnologies.orient.core.command.OCommandResultListener; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.sql.query.OSQLNonBlockingQuery; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Helper class to wrap Orient SQL SELECT queries into {@link Iterable}s. + * + * @since 3.0 + */ +class OrientAsyncHelper +{ + private static final int BUFFER_SIZE = 128; + + private static final long TIMEOUT_SECONDS = 60L; + + private OrientAsyncHelper() { + // private + } + + /** + * Executes with passed in {@link ODatabaseDocumentTx} connection the passed in {@link String} SELECT SQL command + * and returns a true asynchronous backed {@link Iterable} of {@link ODocument} instance, that can be later + * transformed into whatever caller needs. This means that this wrapper supports ANY SQL commands, even those + * performing aggregation on results, as long as iterator is not accumulated into a real array or list. Still, given + * the nature of non-blocking query, it's execution happens on a separate thread, hence, it will not see uncommited + * changes from the caller thread, if any. It uses default buffer size of 128 and timeout of 60 seconds. + * + * @param db The {@link ODatabaseDocumentTx} database instance + * @param selectQuery The SQL SELECT query in full + * @param parameters The SQL SELECT named parameters (optional) + */ + public static Iterable asyncIterable(final ODatabaseDocumentTx db, + final String selectQuery, + @Nullable final Map parameters) + { + return asyncIterable(db, selectQuery, parameters, BUFFER_SIZE, TIMEOUT_SECONDS); + } + + /** + * Executes with passed in {@link ODatabaseDocumentTx} connection the passed in {@link String} SELECT SQL command + * and returns a true asynchronous backed {@link Iterable} of {@link ODocument} instance, that can be later + * transformed into whatever caller needs. This means that this wrapper supports ANY SQL commands, even those + * performing aggregation on results, as long as iterator is not accumulated into a real array or list. Still, given + * the nature of non-blocking query, it's execution happens on a separate thread, hence, it will not see uncommited + * changes from the caller thread, if any. This method allows to tune buffer queue size and timeout. + * + * @param db The {@link ODatabaseDocumentTx} database instance + * @param selectQuery The SQL SELECT query in full + * @param parameters The SQL SELECT named parameters (optional) + * @param bufferSize The queue buffer size + * @param timeoutSeconds The timeout (in seconds) on queue handoff operations + */ + public static Iterable asyncIterable(final ODatabaseDocumentTx db, + final String selectQuery, + @Nullable final Map parameters, + final int bufferSize, + final long timeoutSeconds) + { + checkNotNull(db); + checkNotNull(selectQuery); + checkArgument(bufferSize > 0); + checkArgument(timeoutSeconds >= 0); + final BlockingQueue queue = new ArrayBlockingQueue<>(bufferSize); + + db.command( + new OSQLNonBlockingQuery( + selectQuery, + new QueueFeedingResultListener(timeoutSeconds, queue) + ) + ).execute(parameters); + + return new QueueConsumingIterable(timeoutSeconds, queue); + } + + /** + * A "sentinel" {@link ODocument} instance that marks non blocking result end. + */ + @VisibleForTesting + static final ODocument SENTINEL = new ODocument(); + + private static String queueIdentity(final BlockingQueue queue) { + // using the identity hash to denote the queue instance, not its contents as toString() would do + return Integer.toHexString(System.identityHashCode(queue)); + } + + /** + * The queue feeding {@link OCommandResultListener} implementation. + */ + @VisibleForTesting + static final class QueueFeedingResultListener + extends ComponentSupport + implements OCommandResultListener + { + private final long timeoutSeconds; + + private final BlockingQueue queue; + + public QueueFeedingResultListener(final long timeoutSeconds, + final BlockingQueue queue) + { + this.timeoutSeconds = timeoutSeconds; + this.queue = queue; + } + + @Override + public boolean result(final Object o) { + final ODocument doc = (ODocument) o; + try { + if (!queue.offer(doc, timeoutSeconds, TimeUnit.SECONDS)) { + log.warn("Timed out adding query result to queue {} after {} seconds, aborting query", queueIdentity(queue), + timeoutSeconds); + return false; + } + } + catch (InterruptedException e) { + log.warn("Interrupted result", e); + return false; + } + return true; + } + + @Override + public void end() { + try { + if (!queue.offer(SENTINEL, timeoutSeconds, TimeUnit.SECONDS)) { + log.warn("Timed out adding end marker to queue {} after {} seconds", queueIdentity(queue), timeoutSeconds); + } + } + catch (InterruptedException e) { + log.warn("Interrupted end", e); + } + } + + @Override + public Object getResult() { + return null; // unused + } + } + + /** + * The {@link Iterable} implementation returned to caller that is consuming the queue fed by SQL query result. + */ + @VisibleForTesting + static final class QueueConsumingIterable + extends ComponentSupport + implements Iterable, Iterator + { + private final long timeoutSeconds; + + private final BlockingQueue queue; + + private ODocument next; + + public QueueConsumingIterable(final long timeoutSeconds, final BlockingQueue queue) + { + this.timeoutSeconds = timeoutSeconds; + this.queue = queue; + } + + @Override + public Iterator iterator() { + return this; + } + + @Override + public boolean hasNext() { + if (next == null) { + try { + next = queue.poll(timeoutSeconds, TimeUnit.SECONDS); + if (next == null) { + throw new IllegalStateException("Timed out reading query result from queue " + queueIdentity(queue) + + " after " + timeoutSeconds + " seconds"); + } + } + catch (InterruptedException e) { + log.warn("Interrupted poll", e); + throw new RuntimeException(e); + } + } + return next != SENTINEL; + } + + @Override + public ODocument next() { + if (hasNext()) { + ODocument doc = next; + next = null; + return doc; + } + throw new NoSuchElementException("Iterator depleted"); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Method not supported"); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/Query.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/Query.java new file mode 100644 index 0000000000000000000000000000000000000000..a9c177f4a83011f94629436bb6e334eb7e6df33d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/Query.java @@ -0,0 +1,146 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Collections; +import java.util.Map; + +import com.google.common.base.Strings; +import com.google.common.collect.Maps; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * Helper for constructing orientDB queries. + * + * @since 3.0 + */ +public class Query +{ + /** + * Helper for creating query builder. + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder + { + private final StringBuilder where = new StringBuilder(); + + private final Map parameters = Maps.newHashMap(); + + private final StringBuilder suffix = new StringBuilder(); + + private int parameterNumber; + + private Builder() { + // nop + } + + /** + * Appends to the 'where' clause. + */ + public Builder where(String where) { + this.where.append(where); + return this; + } + + /** + * Appends an equals expression for a parameterized value. + */ + public Builder eq(Object value) { + checkNotNull(value); + checkState(hasWhere(), "Missing where statement"); + return this.where(" = ").param(value); + } + + public Builder and(String value) { + checkNotNull(value); + checkState(hasWhere(), "Missing where statement"); + return where(" AND ").where(value); + } + + public Builder or(String value) { + checkNotNull(value); + checkState(hasWhere(), "Missing where statement"); + return where(" OR ").where(value); + } + + public Builder isNull() { + checkState(hasWhere(), "Missing where statement"); + return where(" IS NULL "); + } + + public Builder isNotNull() { + checkState(hasWhere(), "Missing where statement"); + return where(" IS NOT NULL "); + } + + public boolean hasWhere() { + return clean(where) != null; + } + + /** + * Appends a parameterized value to the where clause. + */ + public Builder param(Object value) { + return param("p", value); + } + + private Builder param(String parameterName, Object value) { + final String mangledName = checkNotNull(parameterName) + parameterNumber++; + parameters.put(mangledName, value); + where(":" + mangledName); + return this; + } + + public Builder suffix(String suffix) { + this.suffix.append(suffix); + return this; + } + + public Query build() { + final StringBuilder str = where; + return new Query(clean(str), clean(suffix), Collections.unmodifiableMap(parameters) + ); + } + + private String clean(final StringBuilder str) { + return Strings.emptyToNull(str.toString().trim()); + } + } + + private final String where, suffix; + + private final Map parameters; + + private Query(final String where, final String suffix, final Map parameters) { + this.where = where; + this.suffix = suffix; + this.parameters = parameters; + } + + public String getWhere() { + return where; + } + + public Map getParameters() { + return parameters; + } + + public String getQuerySuffix() { + return suffix; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataConfiguration.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..32c54cf6a3394e8c5e2a71fb5c0f6265398d96a8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataConfiguration.java @@ -0,0 +1,45 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + + +/** + * @since 3.6 + */ +@Named +@Singleton +public class RebuildAssetUploadMetadataConfiguration +{ + private final boolean enabled; + + private final int pageSize; + + @Inject + public RebuildAssetUploadMetadataConfiguration(@Named("${nexus.asset.rebuildUploadMetadata.enabled:-true}") final boolean enabled, + @Named("${nexus.asset.rebuildUploadMetadata.pageSize:-1000}") final int pageSize) { + this.enabled = enabled; + this.pageSize = pageSize; + } + + public boolean isEnabled() { + return enabled; + } + + public int getPageSize() { + return pageSize; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTask.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTask.java new file mode 100644 index 0000000000000000000000000000000000000000..45e3f37a6b32a803ba2d690965da11b1fc60fbdd --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTask.java @@ -0,0 +1,120 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Collection; +import java.util.List; +import java.util.Map.Entry; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.blobstore.api.BlobStoreManager; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.text.Strings2; +import org.sonatype.nexus.logging.task.ProgressLogIntervalHelper; +import org.sonatype.nexus.scheduling.TaskInterruptedException; +import org.sonatype.nexus.scheduling.TaskSupport; + +import com.google.common.collect.Iterables; +import com.orientechnologies.orient.core.index.OCompositeKey; +import com.orientechnologies.orient.core.index.OIndexCursor; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.size; +import static com.google.common.collect.Streams.stream; +import static java.util.stream.Collectors.toList; + +/** + * Fix asset metadata, based on information from the associated blob. + * + * @since 3.6.1 + */ +@Named +public class RebuildAssetUploadMetadataTask + extends TaskSupport +{ + private final AssetStore assetStore; + + private final BlobStoreManager blobStoreManager; + + private final int limit; + + @Inject + public RebuildAssetUploadMetadataTask(final AssetStore assetStore, + final BlobStoreManager blobStoreManager, + final RebuildAssetUploadMetadataConfiguration configuration) + { + this.assetStore = checkNotNull(assetStore); + this.blobStoreManager = checkNotNull(blobStoreManager); + this.limit = checkNotNull(configuration).getPageSize(); + } + + @Override + public String getMessage() { + return "Rebuild asset upload metadata"; + } + + @Override + protected Object execute() { + long totalAssets = assetStore.countAssets(null); + long processedAssets = 0; + + OIndexCursor assetCursor = assetStore.getIndex(AssetEntityAdapter.I_BUCKET_COMPONENT_NAME).cursor(); + ProgressLogIntervalHelper progressLogger = new ProgressLogIntervalHelper(log, 60); + List> assets = assetStore.getNextPage(assetCursor, limit); + + if (!Iterables.isEmpty(assets) && !Strings2.isBlank(assetStore.getById(assets.get(0).getValue()).createdBy())) { + return null; + } + + while (assets != null && !assets.isEmpty()) { + checkContinuation(); + + Iterable assetIds = assets.stream().map(Entry::getValue).collect(toList()); + + Collection assetsToUpdate = stream(assetStore.getByIds(assetIds)) + .filter(asset -> Strings2.isEmpty(asset.createdBy())) + .filter(asset -> asset.blobRef() != null).map(asset -> { + BlobStore blobStore = blobStoreManager.get(asset.blobRef().getStore()); + Blob blob = blobStore.get(asset.blobRef().getBlobId()); + if (blob != null) { + asset.createdBy(blob.getHeaders().get(BlobStore.CREATED_BY_HEADER)); + asset.createdByIp(blob.getHeaders().get(BlobStore.CREATED_BY_IP_HEADER)); + asset.blobCreated(blob.getMetrics().getCreationTime()); + } + + return asset; + }).collect(toList()); + + assetStore.save(assetsToUpdate); + + processedAssets += size(assetsToUpdate); + progressLogger.info("{} / {} asset upload metadata processed in {} ms", processedAssets, totalAssets, progressLogger.getElapsed()); + + assets = assetStore.getNextPage(assetCursor, limit); + } + + progressLogger.flush(); + + return null; + } + + private void checkContinuation() { + if (isCanceled()) { + throw new TaskInterruptedException("Rebuilding asset upload metadata was cancelled", true); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskDescriptor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..0e1be50be6d8913e4968925b8451cca9f3687642 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskDescriptor.java @@ -0,0 +1,45 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.scheduling.TaskDescriptorSupport; + +/** + * Task descriptor for {@link RebuildAssetUploadMetadataTask}. + * + * @since 3.6 + */ +@Named +@Singleton +public class RebuildAssetUploadMetadataTaskDescriptor + extends TaskDescriptorSupport +{ + public static final String TYPE_ID = "rebuild.asset.uploadMetadata"; + + @Inject + public RebuildAssetUploadMetadataTaskDescriptor(final NodeAccess nodeAccess, + final RebuildAssetUploadMetadataConfiguration configuration) { + super( + TYPE_ID, + RebuildAssetUploadMetadataTask.class, + "Rebuild asset upload metadata", + configuration.isEnabled() ? VISIBLE : NOT_VISIBLE, + configuration.isEnabled() ? EXPOSED : NOT_EXPOSED, + nodeAccess.isClustered() ? newLimitNodeFormField() : null); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskManager.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskManager.java new file mode 100644 index 0000000000000000000000000000000000000000..fd0757c9da63be36e23272f485459ee243792e2c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskManager.java @@ -0,0 +1,53 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.lifecycle.LifecycleSupport; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.scheduling.TaskInfo; +import org.sonatype.nexus.scheduling.TaskScheduler; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.TASKS; + +/** + * @since 3.6 + */ +@Named +@ManagedLifecycle(phase = TASKS) +@Singleton +public class RebuildAssetUploadMetadataTaskManager + extends LifecycleSupport +{ + private final TaskScheduler taskScheduler; + + @Inject + public RebuildAssetUploadMetadataTaskManager(final TaskScheduler taskScheduler) { + this.taskScheduler = checkNotNull(taskScheduler); + } + + @Override + protected void doStart() { + removeOldRebuildTasks(); + } + + private void removeOldRebuildTasks() { + taskScheduler.listsTasks().stream() + .filter(task -> RebuildAssetUploadMetadataTaskDescriptor.TYPE_ID.equals(task.getConfiguration().getTypeId())) + .forEach(TaskInfo::remove); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RetryDeniedException.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RetryDeniedException.java new file mode 100644 index 0000000000000000000000000000000000000000..f014464fa9a983fee68be3559adf31b2e0f8b19f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/RetryDeniedException.java @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +/** + * Thrown when a storage operation fails and is not allowed to retry. + * + * @since 3.0 + */ +public class RetryDeniedException + extends RuntimeException +{ + public RetryDeniedException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/SingleAssetComponentMaintenance.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/SingleAssetComponentMaintenance.java new file mode 100644 index 0000000000000000000000000000000000000000..4702f6620fdce8e2d10332cb1303bbee33692116 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/SingleAssetComponentMaintenance.java @@ -0,0 +1,51 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.inject.Named; + +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.repository.transaction.TransactionalDeleteBlob; +import org.sonatype.nexus.transaction.UnitOfWork; + +/** + * A component maintenance facet that assumes that Components have the same lifecycle as their + * single Assets. + * + * @since 3.0 + */ +@Named +public class SingleAssetComponentMaintenance + extends DefaultComponentMaintenanceImpl +{ + /** + * Deletes both the asset and its component. + */ + @TransactionalDeleteBlob + protected void deleteAssetTx(final EntityId assetId) { + StorageTx tx = UnitOfWork.currentTx(); + final Asset asset = tx.findAsset(assetId, tx.findBucket(getRepository())); + if (asset == null) { + return; + } + final EntityId componentId = asset.componentId(); + if (componentId == null) { + // Assets without components should be deleted on their own + super.deleteAssetTx(assetId); + } + else { + // Otherwise, delete the component, which in turn cascades down to the asset + deleteComponentTx(componentId); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageFacet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageFacet.java new file mode 100644 index 0000000000000000000000000000000000000000..e5bdb5c443bf82473eeef31f3d281082b83363de --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageFacet.java @@ -0,0 +1,61 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.io.InputStream; + +import org.sonatype.nexus.common.hash.HashAlgorithm; +import org.sonatype.nexus.repository.Facet; +import org.sonatype.nexus.repository.view.Payload; +import org.sonatype.nexus.transaction.UnitOfWork; + +import com.google.common.base.Supplier; + +/** + * Storage {@link Facet}, providing component and asset storage for a repository. + * + * @since 3.0 + */ +@Facet.Exposed +public interface StorageFacet + extends Facet +{ + + /** + * Registers format specific selector for {@link WritePolicy}. If not set, the {@link + * WritePolicySelector#DEFAULT} is used which returns the configured write policy. + */ + void registerWritePolicySelector(WritePolicySelector writePolicySelector); + + /** + * Supplies transactions for use in {@link UnitOfWork}. + */ + Supplier txSupplier(); + + /** + * Creates a new temporary blob based using the contents of the input stream. Disposal of the temp blob must be + * managed by the caller, typically using a try-with-resources block. + * + * @since 3.1 + */ + TempBlob createTempBlob(InputStream inputStream, Iterable hashAlgorithms); + + /** + * Creates a new temporary blob based using the contents of the payload. Disposal of the temp blob must be + * managed by the caller, typically using a try-with-resources block. + * + * @since 3.1 + */ + TempBlob createTempBlob(Payload payload, Iterable hashAlgorithms); + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageFacetImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageFacetImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..903166c961ea4eb2a23b302510e7d4ff46d10e5b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageFacetImpl.java @@ -0,0 +1,282 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; + +import javax.annotation.Nonnull; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.validation.constraints.NotNull; +import javax.validation.groups.Default; + +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.blobstore.api.BlobStoreManager; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.event.EventHelper; +import org.sonatype.nexus.common.hash.HashAlgorithm; +import org.sonatype.nexus.common.hash.MultiHashingInputStream; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardAspect; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.config.ConfigurationFacet; +import org.sonatype.nexus.repository.types.HostedType; +import org.sonatype.nexus.repository.view.Payload; +import org.sonatype.nexus.security.ClientInfo; +import org.sonatype.nexus.security.ClientInfoProvider; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableMap; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import org.hibernate.validator.constraints.NotEmpty; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; +import static org.sonatype.nexus.repository.FacetSupport.State.ATTACHED; +import static org.sonatype.nexus.repository.FacetSupport.State.INITIALISED; +import static org.sonatype.nexus.repository.FacetSupport.State.STARTED; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; + +/** + * Default {@link StorageFacet} implementation. + * + * @since 3.0 + */ +@Named("default") +public class StorageFacetImpl + extends FacetSupport + implements StorageFacet +{ + private final NodeAccess nodeAccess; + + private final BlobStoreManager blobStoreManager; + + private final Provider databaseInstanceProvider; + + private final BucketEntityAdapter bucketEntityAdapter; + + private final ComponentEntityAdapter componentEntityAdapter; + + private final AssetEntityAdapter assetEntityAdapter; + + private final ClientInfoProvider clientInfoProvider; + + private final ContentValidatorSelector contentValidatorSelector; + + private final MimeRulesSourceSelector mimeRulesSourceSelector; + + private final Supplier txSupplier; + + private final StorageFacetManager storageFacetManager; + + private final ComponentFactory componentFactory; + + @VisibleForTesting + static final String CONFIG_KEY = "storage"; + + @VisibleForTesting + static class Config + { + @NotEmpty + public String blobStoreName; + + @NotNull(groups = HostedType.ValidationGroup.class) + public WritePolicy writePolicy; + + @NotNull + public Boolean strictContentTypeValidation = Boolean.TRUE; + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "blobStoreName='" + blobStoreName + '\'' + + ", writePolicy=" + writePolicy + + ", strictContentTypeValidation=" + strictContentTypeValidation + + '}'; + } + } + + private Config config; + + private WritePolicySelector writePolicySelector; + + @Inject + public StorageFacetImpl(final NodeAccess nodeAccess, + final BlobStoreManager blobStoreManager, + @Named(ComponentDatabase.NAME) final Provider databaseInstanceProvider, + final BucketEntityAdapter bucketEntityAdapter, + final ComponentEntityAdapter componentEntityAdapter, + final AssetEntityAdapter assetEntityAdapter, + final ClientInfoProvider clientInfoProvider, + final ContentValidatorSelector contentValidatorSelector, + final MimeRulesSourceSelector mimeRulesSourceSelector, + final StorageFacetManager storageFacetManager, + final ComponentFactory componentFactory) + { + this.nodeAccess = checkNotNull(nodeAccess); + this.blobStoreManager = checkNotNull(blobStoreManager); + this.databaseInstanceProvider = checkNotNull(databaseInstanceProvider); + + this.bucketEntityAdapter = checkNotNull(bucketEntityAdapter); + this.componentEntityAdapter = checkNotNull(componentEntityAdapter); + this.assetEntityAdapter = checkNotNull(assetEntityAdapter); + this.clientInfoProvider = checkNotNull(clientInfoProvider); + this.contentValidatorSelector = checkNotNull(contentValidatorSelector); + this.mimeRulesSourceSelector = checkNotNull(mimeRulesSourceSelector); + this.storageFacetManager = checkNotNull(storageFacetManager); + this.componentFactory = checkNotNull(componentFactory); + + this.txSupplier = () -> openStorageTx(databaseInstanceProvider.get().acquire()); + } + + @Override + protected void doValidate(final Configuration configuration) throws Exception { + facet(ConfigurationFacet.class).validateSection(configuration, CONFIG_KEY, Config.class, + Default.class, getRepository().getType().getValidationGroup() + ); + } + + @Override + protected void doConfigure(final Configuration configuration) throws Exception { + config = facet(ConfigurationFacet.class).readSection(configuration, CONFIG_KEY, Config.class); + log.debug("Config: {}", config); + } + + @Override + protected void doInit(final Configuration configuration) throws Exception { + initBucket(); + writePolicySelector = WritePolicySelector.DEFAULT; + super.doInit(configuration); + } + + private void initBucket() { + // create the bucket for the repository if it doesn't exist + inTxRetry(databaseInstanceProvider).run(db -> { + String repositoryName = getRepository().getName(); + Bucket bucket = bucketEntityAdapter.read(db, repositoryName); + if (bucket == null) { + bucket = new Bucket(); + bucket.setRepositoryName(repositoryName); + bucket.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + bucketEntityAdapter.addEntity(db, bucket); + } + }); + } + + @Override + protected void doDestroy() throws Exception { + config = null; + } + + @Override + protected void doDelete() throws Exception { + // skip when replicating, origin node will delete the bucket blobs + if (!EventHelper.isReplicating()) { + inTxRetry(databaseInstanceProvider).run(db -> { + Bucket bucket = bucketEntityAdapter.read(db, getRepository().getName()); + storageFacetManager.enqueueDeletion(getRepository(), blobStoreManager.get(config.blobStoreName), bucket); + }); + } + } + + @Override + @Guarded(by = {INITIALISED, ATTACHED} ) + public void registerWritePolicySelector(final WritePolicySelector writePolicySelector) { + checkNotNull(writePolicySelector); + this.writePolicySelector = writePolicySelector; + } + + @Override + @Guarded(by = STARTED) + public Supplier txSupplier() { + return txSupplier; + } + + @Override + public TempBlob createTempBlob(final InputStream inputStream, final Iterable hashAlgorithms) { + BlobStore blobStore = checkNotNull(blobStoreManager.get(config.blobStoreName)); + MultiHashingInputStream hashingStream = new MultiHashingInputStream(hashAlgorithms, inputStream); + Blob blob = blobStore.create(hashingStream, + ImmutableMap.of( + BlobStore.BLOB_NAME_HEADER, "temp", + BlobStore.CREATED_BY_HEADER, createdBy(), + BlobStore.CREATED_BY_IP_HEADER, createdByIp(), + BlobStore.TEMPORARY_BLOB_HEADER, "")); + return new TempBlob(blob, hashingStream.hashes(), true, blobStore); + } + + @Override + public TempBlob createTempBlob(final Payload payload, final Iterable hashAlgorithms) + { + try (InputStream inputStream = payload.openInputStream()) { + return createTempBlob(inputStream, hashAlgorithms); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Returns the "principal name" to be used with current instance of {@link StorageTx}. + */ + @Nonnull + private String createdBy() { + ClientInfo clientInfo = clientInfoProvider.getCurrentThreadClientInfo(); + if (clientInfo == null || clientInfo.getUserid() == null) { + return "system"; + } + return clientInfo.getUserid(); + } + + /** + * Returns the "principal name" to be used with current instance of {@link StorageTx}. + */ + @Nonnull + private String createdByIp() { + ClientInfo clientInfo = clientInfoProvider.getCurrentThreadClientInfo(); + if (clientInfo == null || clientInfo.getRemoteIP() == null) { + return "system"; + } + return clientInfo.getRemoteIP(); + } + + @Nonnull + private StorageTx openStorageTx(final ODatabaseDocumentTx db) { + BlobStore blobStore = blobStoreManager.get(config.blobStoreName); + return StateGuardAspect.around( + new StorageTxImpl( + createdBy(), + createdByIp(), + new BlobTx(nodeAccess, blobStore), + db, + getRepository().getName(), + config.writePolicy == null ? WritePolicy.ALLOW : config.writePolicy, + writePolicySelector, + bucketEntityAdapter, + componentEntityAdapter, + assetEntityAdapter, + config.strictContentTypeValidation, + contentValidatorSelector.validator(getRepository()), + mimeRulesSourceSelector.ruleSource(getRepository()), + componentFactory) + ); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageFacetManager.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageFacetManager.java new file mode 100644 index 0000000000000000000000000000000000000000..f2ab4526f6ac1fe0db8cbb45c7528ff9295ffbbb --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageFacetManager.java @@ -0,0 +1,38 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.storage.Bucket; + +/** + * Contains StorageFacet-related operations that cannot be represented in individual StorageFacet instances. + * + * @since 3.2.1 + */ +public interface StorageFacetManager +{ + /** + * Enqueues the contents (bucket) of a particular StorageFacet for deletion. Called by a StorageFacet on delete. + */ + void enqueueDeletion(Repository repository, BlobStore blobStore, Bucket bucket); + + /** + * Performs the actual deletions for any deleted storage facets, returning the number of deletions successfully + * performed. This can potentially be a long-running operation! + * + * @return the number of successful cleanups performed during the call + */ + long performDeletions(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTx.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTx.java new file mode 100644 index 0000000000000000000000000000000000000000..83eee92d515fa97507cbe47380c16fc81f7a5660 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTx.java @@ -0,0 +1,485 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.Map; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobRef; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.hash.HashAlgorithm; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.transaction.Transaction; + +import com.google.common.base.Supplier; +import com.google.common.hash.HashCode; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.record.impl.ODocument; + +/** + * A storage transaction. + * + * @since 3.0 + */ +public interface StorageTx + extends Transaction +{ + /** + * Begins the transaction. + */ + @Override + void begin(); + + /** + * Commits the transaction. + */ + @Override + void commit(); + + /** + * Rolls back the transaction. + */ + @Override + void rollback(); + + /** + * Closes the transaction. Uncommitted changes will be lost, and the object will be ineligible for further use. + */ + @Override + void close(); + + /** + * Provides the underlying document transaction. + * + * Note: The caller may use this to directly query or manipulate the Orient document DB, if needed, but should not + * directly commit, rollback, or close the underlying transaction. + */ + ODatabaseDocumentTx getDb(); + + /** + * A lower level but still safe access to SQL SELECT queries, that allows caller to "browse" potentially + * huge result sets without worry about paging or OOMs. This method will NOT see uncommited changes performed + * in this current TX, if any. The returned {@link Iterable} may throw {@link RuntimeException} if timeout to + * receive new elements is breached. + * + * @param selectSql The SELECT SQL in full, optionally with named parameters. + * @param params The map holding named parameters of SELECT SQL. + * @return Iterable of SELECTed {@link ODocument}s. + * @see OrientAsyncHelper + */ + Iterable browse(String selectSql, @Nullable Map params); + + /** + * Same as {@link #browse(String, Map)} but additionally exposes the bufferSize and timeoutSeconds options of the + * backing {@link OrientAsyncHelper#asyncIterable} method + * + * @see OrientAsyncHelper#asyncIterable(ODatabaseDocumentTx, String, Map, int, long) + */ + Iterable browse(final String selectSql, + @Nullable final Map params, + final int bufferSize, + final long timeoutSeconds); + + /** + * Finds a bucket based on the repository. + */ + @Nullable + Bucket findBucket(Repository repository); + + /** + * Finds buckets based on the repositories. + */ + @Nullable + Iterable findBuckets(final Iterable repositories); + + /** + * Gets all buckets. + */ + Iterable browseBuckets(); + + /** + * Gets all assets owned by the specified bucket. This method will NOT see unsommited changes performed in this + * same TX, if any. The returned {@link Iterable} may throw {@link RuntimeException} if timeout to + * receive new elements is breached. + * + * @see OrientAsyncHelper + */ + Iterable browseAssets(Bucket bucket); + + /** + * Gets all assets owned by the specified component. + */ + Iterable browseAssets(Component component); + + /** + * Gets first asset owned by the specified component. + */ + @Nullable + Asset firstAsset(Component component); + + /** + * Gets all components owned by the specified bucket. This method will NOT see unsommited changes performed in this + * same TX, if any. The returned {@link Iterable} may throw {@link RuntimeException} if timeout to + * receive new elements is breached. + * + * @see OrientAsyncHelper + */ + Iterable browseComponents(Bucket bucket); + + /** + * Gets an asset by id, owned by the specified bucket, or {@code null} if not found. + */ + @Nullable + Asset findAsset(EntityId id, Bucket bucket); + + /** + * Gets a asset by id, regardless of which bucket it resides in, or {@code null} if not found. + * + * @since 3.6 + */ + @Nullable + Asset findAsset(EntityId id); + + /** + * Gets an asset by some identifying property, owned by the specified bucket, or {@code null} if not found. + */ + @Nullable + Asset findAssetWithProperty(String propName, Object propValue, Bucket bucket); + + /** + * Gets an asset by some identifying property, or {@code null} if not found. + * + * @since 3.6.1 + */ + @Nullable + Asset findAssetWithProperty(String propName, Object propValue); + + /** + * Gets an asset by some identifying property, owned by the specified component, or {@code null} if not found. + */ + @Nullable + Asset findAssetWithProperty(String propName, Object propValue, Component component); + + /** + * Gets all assets in the specified repositories that match the given where clause. + * + * @param whereClause an OrientDB select query, minus the "select from X where " prefix. Rather than passing values + * in directly, they should be specified as :labeled portions of the where clause (e.g. a = + * :aVal). + * @param parameters the name-value pairs specifying the values for any :labeled portions of the where clause. + * @param repositories the repositories to limit the results to. If null or empty, results won't be limited + * by repository. + * @param querySuffix the part of the query after the where clause, which may by used for ordering and paging + * as per the OrientDB select query syntax. + * @see OrientDB SELECT Query Documentation + */ + Iterable findAssets(@Nullable String whereClause, + @Nullable Map parameters, + @Nullable Iterable repositories, + @Nullable String querySuffix); + + /** + * Gets all assets in the specified repositories that match the given where clause. + * + * @param query an {@link Query} query. + * @param repositories the repositories to limit the results to. If null or empty, results won't be limited + * by repository (unless passed in query limits already). + */ + Iterable findAssets(Query query, @Nullable Iterable repositories); + + /** + * Gets the number of assets matching the given where clause. + */ + long countAssets(@Nullable String whereClause, + @Nullable Map parameters, + @Nullable Iterable repositories, + @Nullable String querySuffix); + + /** + * Gets the number of assets matching the given {@link Query} clause. + */ + long countAssets(Query query, @Nullable Iterable repositories); + + /** + * Check for the existence of a component with {@code group}, {@code name}, and {@code version} in {@code repository}. + * + * @since 3.8 + */ + boolean componentExists(@Nullable final String group, + final String name, + @Nullable final String version, + final Repository repository); + /** + * Gets a component by id, owned by the specified bucket, or {@code null} if not found. + */ + @Nullable + Component findComponentInBucket(EntityId id, Bucket bucket); + + /** + * Gets a component by id, regardless of which bucket it resides in, or {@code null} if not found. + */ + @Nullable + Component findComponent(EntityId id); + + /** + * Gets a component by some identifying property, or {@code null} if not found. + */ + @Nullable + Component findComponentWithProperty(String propName, Object propValue, Bucket bucket); + + /** + * Gets all component in the specified repositories that match the given where clause. + * + * @param whereClause an OrientDB query, minus the "select from X where " prefix. Rather than passing values + * in directly, they should be specified as :labeled portions of the where clause (e.g. a = + * :aVal). + * @param parameters the name-value pairs specifying the values for any :labeled portions of the where clause. + * @param repositories the repositories to limit the results to. If null or empty, results won't be limited + * by repository. + * @param querySuffix the part of the query after the where clause, which may by used for ordering and paging + * as per the OrientDB select query syntax. + * @see OrientDB SELECT Query Documentation + */ + Iterable findComponents(@Nullable String whereClause, + @Nullable Map parameters, + @Nullable Iterable repositories, + @Nullable String querySuffix); + + /** + * Gets all component in the specified repositories that match the given where clause. + * + * @param query an {@link Query} query. + * @param repositories the repositories to limit the results to. If null or empty, results won't be limited + * by repository (unless passed in query limits already). + */ + Iterable findComponents(Query query, @Nullable Iterable repositories); + + /** + * Gets all components with a given name, compared case-insensitively. + * + * @param name the component name. + * @param repositories the repositories to limit the results to. If null or empty, results won't be limited + * by repository. + * @param querySuffix the part of the query after the where clause + * + * @since 3.3 + */ + Iterable findComponentsByNameCaseInsensitive(String name, + @Nullable Iterable repositories, + @Nullable String querySuffix); + + /** + * Gets the number of components matching the given where clause. + */ + long countComponents(@Nullable String whereClause, + @Nullable Map parameters, + @Nullable Iterable repositories, + @Nullable String querySuffix); + + /** + * Gets the number of components matching the given {@link Query} clause. + */ + long countComponents(Query query, @Nullable Iterable repositories); + + /** + * Creates a new standalone asset. + */ + Asset createAsset(Bucket bucket, Format format); + + /** + * Creates a new asset that belongs to a component. + */ + Asset createAsset(Bucket bucket, Component component); + + /** + * Creates a new component. + */ + Component createComponent(Bucket bucket, Format format); + + /** + * Updates an existing bucket. + */ + void saveBucket(Bucket bucket); + + /** + * Updates an existing component. + */ + void saveComponent(Component component); + + /** + * Updates an existing asset. + */ + void saveAsset(Asset asset); + + /** + * Deletes an existing component and all constituent assets. + */ + void deleteComponent(Component component); + + /** + * Deletes an existing asset and requests the blob to be deleted. + */ + void deleteAsset(Asset asset); + + /** + * Creates a new Blob and updates the given asset with a reference to it, hash metadata, size, and content type. + * The old blob, if any, will be deleted. + * + * @param asset the {@link Asset} that should reference newly created blob. + * @param blobName blob name (may not be unique), but it will be used also in content validation. + * See {@link ContentValidator}. + * @param streamSupplier the content being stored as a blob + * @param hashAlgorithms {@link HashAlgorithm}s to be applied while streaming to blob store, will be set + * in {@link Asset} attributes. + * @param headers optional, custom headers for blob, if any. + * @param declaredContentType optional, the declared MIME type of the blob. See {@link ContentValidator}. + * @param skipContentVerification if {@code false}, {@link ContentValidator} will be used to determine or verify + * the content type of content. If {@code true}, the {@code declaredContentType} + * will be used as-is and must be non-{@code null}. Use {@code true} in cases when + * content to be saved is known to be what it is declared to, ie. caller already + * parsed it or processed in any other way that proved it's format/type. + * @return Attached instance of {@link AssetBlob} of the newly created blob. As side effect, passed in {@link Asset} + * is modified too, but is unsaved. Caller must ensure {@link #saveAsset(Asset)} is invoked before this TX ends. + */ + AssetBlob setBlob(Asset asset, + String blobName, + final Supplier streamSupplier, + Iterable hashAlgorithms, + @Nullable Map headers, + @Nullable String declaredContentType, + boolean skipContentVerification) throws IOException; + + /** + * Creates a new Blob by hard linking to {@code sourceFile} and updates the given asset with a reference to it, + * hash metadata, size, and content type. The old blob, if any, will be deleted. + * + * @param asset the {@link Asset} that should reference newly created blob. + * @param blobName blob name (may not be unique), but it will be used also in content validation. + * See {@link ContentValidator}. + * @param sourceFile the source file path of the content being stored as a blob + * @param hashes precalculated {@link HashAlgorithm}s and {@link HashCode}s for {@link AssetBlob}. + * @param headers optional, custom headers for blob, if any. + * @param declaredContentType the declared MIME type of the blob. See {@link ContentValidator}. + * @param size precalculated size for the blob + * @return Attached instance of {@link AssetBlob} of the newly created blob. As side effect, passed in {@link Asset} + * is modified too, but is unsaved. Caller must ensure {@link #saveAsset(Asset)} is invoked before this TX ends. + */ + AssetBlob setBlob(Asset asset, + String blobName, + Path sourceFile, + Map hashes, + @Nullable Map headers, + String declaredContentType, + long size) throws IOException; + + /** + * Creates a new Blob by hard linking to a {@code TempBlob} and returns its {@link AssetBlob}. Otherwise behaves as + * the other {@code setBlob()} methods. + * + * @since 3.1 + */ + AssetBlob setBlob(Asset asset, + String blobName, + TempBlob originalBlob, + @Nullable Map headers, + @Nullable String declaredContentType, + boolean skipContentVerification) + throws IOException; + + /** + * Creates a new Blob and returns its {@link AssetBlob}. Blobs created but not attached in a scope of a TX to any + * asset are considered as "orphans", and they will be deleted from blob store at the end of a TX. + * + * @param blobName blob name (may not be unique), but it will be used also in content validation. See + * {@link ContentValidator}. + * @param streamSupplier the content to be streamed into blob store. + * @param hashAlgorithms {@link HashAlgorithm}s to be applied while streaming to blob store, returned in + * {@link + * AssetBlob}. + * @param headers optional, custom headers for blob, if any. + * @param declaredContentType optional, the declared MIME type of the blob. See {@link ContentValidator}. + * @param skipContentVerification if {@code false}, {@link ContentValidator} will be used to determine or verify + * the content type of content. If {@code true}, the {@code declaredContentType} + * will be used as-is and must be non-{@code null}. Use {@code true} in cases when + * content to be saved is known to be what it is declared to, ie. caller already + * parsed it or processed in any other way that proved it's format/type. + * @return Unattached instance of {@link AssetBlob} that should be attached to some {@link Asset} during this + * transaction using {@link #attachBlob(Asset, AssetBlob)} method. + */ + AssetBlob createBlob(String blobName, + final Supplier streamSupplier, + Iterable hashAlgorithms, + @Nullable Map headers, + @Nullable String declaredContentType, + boolean skipContentVerification) throws IOException; + + /** + * Creates a new Blob by hard linking to {@code sourceFile} and returns its {@link AssetBlob}. Blobs created but not + * attached in a scope of a TX to any asset are considered as "orphans", and they will be deleted from blob store at + * the end of a TX. + * + * @param blobName blob name (may not be unique), but it will be used also in content validation. See + * {@link ContentValidator}. + * @param sourceFile the source file path of the content to be included in blob store. + * @param hashes precalculated {@link HashAlgorithm}s and {@link HashCode}s for {@link AssetBlob}. + * @param headers optional, custom headers for blob, if any. + * @param declaredContentType the declared MIME type of the blob. See {@link ContentValidator}. + * @param size precalculated size for the blob + * @return Unattached instance of {@link AssetBlob} that should be attached to some {@link Asset} during this + * transaction using {@link #attachBlob(Asset, AssetBlob)} method. + */ + AssetBlob createBlob(String blobName, + Path sourceFile, + Map hashes, + @Nullable Map headers, + String declaredContentType, + long size) throws IOException; + + /** + * Creates a new Blob by hard linking to a {@code TempBlob} and returns its {@link AssetBlob}. Otherwise behaves as + * the other {@code createBlob()} methods. + * + * @since 3.1 + */ + AssetBlob createBlob(String blobName, + TempBlob originalBlob, + @Nullable Map headers, + @Nullable String declaredContentType, + boolean skipContentVerification) + throws IOException; + + /** + * Attaches a Blob to asset and updates the given asset with a reference to it, hash metadata, size, and content + * type. The asset's old blob, if any, will be deleted. + */ + void attachBlob(Asset asset, AssetBlob assetBlob); + + /** + * Gets a Blob, or {@code null if not found}. + */ + @Nullable + Blob getBlob(BlobRef blobRef); + + /** + * Requires that a Blob exists in the blobstore. + * + * @throws MissingBlobException if the blob is missing + */ + Blob requireBlob(BlobRef blobRef); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTxImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTxImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..0891515fc16b02b8ee6dd96d4c2d2b1d2db39e56 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTxImpl.java @@ -0,0 +1,962 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobRef; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.hash.HashAlgorithm; +import org.sonatype.nexus.common.property.SystemPropertiesHelper; +import org.sonatype.nexus.common.sequence.NumberSequence; +import org.sonatype.nexus.common.sequence.RandomExponentialSequence; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuard; +import org.sonatype.nexus.common.stateguard.StateGuardAware; +import org.sonatype.nexus.common.stateguard.Transitions; +import org.sonatype.nexus.common.text.Strings2; +import org.sonatype.nexus.mime.MimeRulesSource; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.IllegalOperationException; +import org.sonatype.nexus.repository.Repository; + +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.Iterables; +import com.google.common.hash.HashCode; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.tx.OTransaction.TXTYPE; +import org.joda.time.DateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.String.format; +import static org.sonatype.nexus.common.entity.EntityHelper.id; +import static org.sonatype.nexus.repository.storage.Asset.CHECKSUM; +import static org.sonatype.nexus.repository.storage.Asset.HASHES_NOT_VERIFIED; +import static org.sonatype.nexus.repository.storage.Asset.PROVENANCE; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; +import static org.sonatype.nexus.repository.storage.StorageTxImpl.State.ACTIVE; +import static org.sonatype.nexus.repository.storage.StorageTxImpl.State.CLOSED; +import static org.sonatype.nexus.repository.storage.StorageTxImpl.State.OPEN; + +/** + * Default {@link StorageTx} implementation. + * + * @since 3.0 + */ +public class StorageTxImpl + implements StorageTx, StateGuardAware +{ + private static final Logger log = LoggerFactory.getLogger(StorageTxImpl.class); + + private static final int INITIAL_DELAY_MS = SystemPropertiesHelper + .getInteger(StorageTxImpl.class.getName() + ".retrydelay.initial", 10); + + private static final int MAX_RETRIES = 8; + + private final String createdBy; + + private final String createdByIp; + + private final BlobTx blobTx; + + private final ODatabaseDocumentTx db; + + private final String repositoryName; + + private final WritePolicy writePolicy; + + private final WritePolicySelector writePolicySelector; + + private final StateGuard stateGuard = new StateGuard.Builder().initial(OPEN).logger(log).create(); + + private final BucketEntityAdapter bucketEntityAdapter; + + private final ComponentEntityAdapter componentEntityAdapter; + + private final AssetEntityAdapter assetEntityAdapter; + + private final boolean strictContentValidation; + + private final ContentValidator contentValidator; + + private final MimeRulesSource mimeRulesSource; + + private final ComponentFactory componentFactory; + + private int retries = 0; + + private NumberSequence retryDelay; + + public StorageTxImpl(final String createdBy, + final String createdByIp, + final BlobTx blobTx, + final ODatabaseDocumentTx db, + final String repositoryName, + final WritePolicy writePolicy, + final WritePolicySelector writePolicySelector, + final BucketEntityAdapter bucketEntityAdapter, + final ComponentEntityAdapter componentEntityAdapter, + final AssetEntityAdapter assetEntityAdapter, + final boolean strictContentValidation, + final ContentValidator contentValidator, + final MimeRulesSource mimeRulesSource, + final ComponentFactory componentFactory) + { + this.createdBy = checkNotNull(createdBy); + this.createdByIp = checkNotNull(createdByIp); + this.blobTx = checkNotNull(blobTx); + this.db = checkNotNull(db); + this.repositoryName = checkNotNull(repositoryName); + this.writePolicy = checkNotNull(writePolicy); + this.writePolicySelector = checkNotNull(writePolicySelector); + this.bucketEntityAdapter = checkNotNull(bucketEntityAdapter); + this.componentEntityAdapter = checkNotNull(componentEntityAdapter); + this.assetEntityAdapter = checkNotNull(assetEntityAdapter); + this.strictContentValidation = strictContentValidation; + this.contentValidator = checkNotNull(contentValidator); + this.mimeRulesSource = checkNotNull(mimeRulesSource); + this.componentFactory = checkNotNull(componentFactory); + + // This is only here for now to yell in case of nested TX + // To be discussed in future, or at the point when we will have need for nested TX + // Note: orient DB sports some rudimentary support for nested TXes + checkArgument(!db.getTransaction().isActive(), "Nested DB TX!"); + } + + public static final class State + { + public static final String OPEN = "OPEN"; + + public static final String ACTIVE = "ACTIVE"; + + public static final String CLOSED = "CLOSED"; + } + + @Override + @Nonnull + public StateGuard getStateGuard() { + return stateGuard; + } + + @Override + @Transitions(from = OPEN, to = ACTIVE) + public void begin() { + db.begin(TXTYPE.OPTIMISTIC); + } + + @Override + @Transitions(from = ACTIVE, to = OPEN, silent = true) + public void commit() { + db.commit(); + blobTx.commit(); + retries = 0; + } + + @Override + @Transitions(from = ACTIVE, to = OPEN) + public void rollback() { + db.rollback(); + blobTx.rollback(); + } + + @Override + public boolean isActive() { + return ACTIVE.equals(stateGuard.getCurrent()); + } + + /** + * Custom retry strategy that throws {@link RetryDeniedException} when retry limit is breached. + */ + @Override + public boolean allowRetry(final Exception cause) throws RetryDeniedException { + if (retries < MAX_RETRIES) { + try { + if (retryDelay == null) { + retryDelay = delaySequence(); + } + long delay = retryDelay.next(); + log.trace("Delaying tx retry for {}ms", delay); + Thread.sleep(delay); + } + catch (InterruptedException e) { + throw new RuntimeException(e); + } + + retries++; + log.debug("Retrying operation: {}/{}", retries, MAX_RETRIES); + return true; + } + + String message = format("Reached max retries: %d/%d", retries, MAX_RETRIES); + log.warn(message); + + throw new RetryDeniedException(message, cause); + } + + @Override + @Transitions(from = {OPEN, ACTIVE}, to = CLOSED) + public void close() { + // If the transaction has not been committed, then we roll back. + if (ACTIVE.equals(stateGuard.getCurrent())) { + rollback(); + } + + db.close(); // rolls back and releases ODatabaseDocumentTx to pool + } + + @Override + @Guarded(by = {ACTIVE}) + public ODatabaseDocumentTx getDb() { + return db; + } + + @Override + @Guarded(by = {ACTIVE}) + public Iterable browse(final String selectSql, @Nullable final Map params) { + if (Strings2.isBlank(selectSql)) { + return Collections.emptyList(); + } + else { + return OrientAsyncHelper.asyncIterable(db, selectSql, params); + } + } + + @Override + @Guarded(by = {ACTIVE}) + public Iterable browse(final String selectSql, + @Nullable final Map params, + final int bufferSize, + final long timeoutSeconds) + { + if (Strings2.isBlank(selectSql)) { + return Collections.emptyList(); + } + else { + return OrientAsyncHelper.asyncIterable(db, selectSql, params, bufferSize, timeoutSeconds); + } + } + + @Nullable + @Override + @Guarded(by = ACTIVE) + public Bucket findBucket(final Repository repository) { + return bucketOf(repository.getName()); + } + + @Nullable + @Override + @Guarded(by = ACTIVE) + public Iterable findBuckets(final Iterable repositories) { + return bucketsOf(repositories); + } + + @Override + @Guarded(by = ACTIVE) + public Iterable browseBuckets() { + return bucketEntityAdapter.browse(db); + } + + @Override + @Guarded(by = ACTIVE) + public Iterable browseAssets(final Bucket bucket) { + return assetEntityAdapter.browseByBucket(db, bucket); + } + + @Override + @Guarded(by = ACTIVE) + public Iterable browseAssets(final Component component) { + return assetEntityAdapter.browseByComponent(db, component); + } + + @Override + @Guarded(by = ACTIVE) + public Asset firstAsset(final Component component) { + return Iterables.getFirst(browseAssets(component), null); + } + + @Override + @Guarded(by = ACTIVE) + public Iterable browseComponents(final Bucket bucket) { + return componentEntityAdapter.browseByBucket(db, bucket); + } + + @Nullable + @Override + @Guarded(by = ACTIVE) + public Asset findAsset(final EntityId id, final Bucket bucket) { + checkNotNull(id); + checkNotNull(bucket); + Asset asset = assetEntityAdapter.read(db, id); + return bucketOwns(bucket, asset) ? asset : null; + } + + @Nullable + @Override + @Guarded(by = ACTIVE) + public Asset findAsset(final EntityId id) { + checkNotNull(id); + return assetEntityAdapter.read(db, id); + } + + private boolean bucketOwns(final Bucket bucket, @Nullable final MetadataNode item) { + return item != null && Objects.equals(id(bucket), item.bucketId()); + } + + @Nullable + @Override + @Guarded(by = ACTIVE) + public Asset findAssetWithProperty(final String propName, final Object propValue, final Bucket bucket) { + return assetEntityAdapter.findByProperty(db, propName, propValue, bucket); + } + + @Nullable + @Override + @Guarded(by = ACTIVE) + public Asset findAssetWithProperty(final String propName, final Object propValue) { + return assetEntityAdapter.findByProperty(db, propName, propValue); + } + + @Nullable + @Override + @Guarded(by = ACTIVE) + public Asset findAssetWithProperty(final String propName, final Object propValue, final Component component) { + return assetEntityAdapter.findByProperty(db, propName, propValue, component); + } + + @Override + @Guarded(by = ACTIVE) + public Iterable findAssets(@Nullable String whereClause, + @Nullable Map parameters, + @Nullable Iterable repositories, + @Nullable String querySuffix) + { + return assetEntityAdapter.browseByQuery(db, whereClause, parameters, bucketsOf(repositories), querySuffix); + } + + @Override + @Guarded(by = ACTIVE) + public Iterable findAssets(final Query query, @Nullable final Iterable repositories) { + return findAssets(query.getWhere(), query.getParameters(), repositories, query.getQuerySuffix()); + } + + @Override + @Guarded(by = ACTIVE) + public long countAssets(@Nullable String whereClause, + @Nullable Map parameters, + @Nullable Iterable repositories, + @Nullable String querySuffix) + { + return assetEntityAdapter.countByQuery(db, whereClause, parameters, bucketsOf(repositories), querySuffix); + } + + @Override + @Guarded(by = ACTIVE) + public long countAssets(final Query query, @Nullable final Iterable repositories) { + return countAssets(query.getWhere(), query.getParameters(), repositories, query.getQuerySuffix()); + } + + @Override + @Guarded(by = ACTIVE) + public boolean componentExists(@Nullable final String group, + final String name, + @Nullable final String version, + final Repository repository) + { + return componentEntityAdapter.exists(db, group, name, version, bucketOf(repository.getName())); + } + + @Nullable + @Override + @Guarded(by = ACTIVE) + public Component findComponentInBucket(final EntityId id, final Bucket bucket) { + checkNotNull(id); + checkNotNull(bucket); + Component component = findComponent(id); + return bucketOwns(bucket, component) ? component : null; + } + + @Nullable + @Override + @Guarded(by = ACTIVE) + public Component findComponent(final EntityId id) { + checkNotNull(id); + return componentEntityAdapter.read(db, id); + } + + @Nullable + @Override + @Guarded(by = ACTIVE) + public Component findComponentWithProperty(final String propName, final Object propValue, final Bucket bucket) { + return componentEntityAdapter.findByProperty(db, propName, propValue, bucket); + } + + @Override + @Guarded(by = ACTIVE) + public Iterable findComponents(@Nullable String whereClause, + @Nullable Map parameters, + @Nullable Iterable repositories, + @Nullable String querySuffix) + { + return componentEntityAdapter.browseByQuery(db, whereClause, parameters, bucketsOf(repositories), querySuffix); + } + + @Override + @Guarded(by = ACTIVE) + public Iterable findComponents(final Query query, @Nullable final Iterable repositories) { + return findComponents(query.getWhere(), query.getParameters(), repositories, query.getQuerySuffix()); + } + + @Override + @Guarded(by = ACTIVE) + public Iterable findComponentsByNameCaseInsensitive(final String name, + @Nullable final Iterable repositories, + @Nullable final String querySuffix) + { + return componentEntityAdapter.browseByNameCaseInsensitive(db, name, bucketsOf(repositories), querySuffix); + } + + @Override + @Guarded(by = ACTIVE) + public long countComponents(@Nullable String whereClause, + @Nullable Map parameters, + @Nullable Iterable repositories, + @Nullable String querySuffix) + { + return componentEntityAdapter.countByQuery(db, whereClause, parameters, bucketsOf(repositories), querySuffix); + } + + @Override + @Guarded(by = ACTIVE) + public long countComponents(final Query query, @Nullable final Iterable repositories) { + return countComponents(query.getWhere(), query.getParameters(), repositories, query.getQuerySuffix()); + } + + @Override + @Guarded(by = ACTIVE) + public Asset createAsset(final Bucket bucket, final Format format) { + checkNotNull(format); + return createAsset(bucket, format.toString()); + } + + private Asset createAsset(final Bucket bucket, final String format) { + checkNotNull(bucket); + Asset asset = new Asset(); + asset.bucketId(id(bucket)); + asset.format(format); + asset.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + return asset; + } + + @Override + @Guarded(by = ACTIVE) + public Asset createAsset(final Bucket bucket, final Component component) { + checkNotNull(component); + Asset asset = createAsset(bucket, component.format()); + asset.componentId(id(component)); + return asset; + } + + @Override + @Guarded(by = ACTIVE) + public Component createComponent(final Bucket bucket, final Format format) { + checkNotNull(bucket); + checkNotNull(format); + + Component component = componentFactory.createComponent(); + component.bucketId(id(bucket)); + component.format(format.toString()); + component.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + return component; + } + + @Override + @Guarded(by = ACTIVE) + public void saveBucket(final Bucket bucket) { + bucketEntityAdapter.editEntity(db, bucket); + } + + @Override + @Guarded(by = ACTIVE) + public void saveComponent(final Component component) { + if (EntityHelper.hasMetadata(component)) { + componentEntityAdapter.editEntity(db, component); + } + else { + componentEntityAdapter.addEntity(db, component); + } + } + + @Override + @Guarded(by = ACTIVE) + public void saveAsset(final Asset asset) { + if (EntityHelper.hasMetadata(asset)) { + assetEntityAdapter.editEntity(db, asset); + } + else { + assetEntityAdapter.addEntity(db, asset); + } + } + + @Override + @Guarded(by = ACTIVE) + public void deleteComponent(Component component) { + deleteComponent(component, true); + } + + private void deleteComponent(final Component component, final boolean checkWritePolicy) { + checkNotNull(component); + + for (Asset asset : browseAssets(component)) { + deleteAsset(asset, checkWritePolicy ? writePolicySelector.select(asset, writePolicy) : null); + } + componentEntityAdapter.deleteEntity(db, component); + } + + @Override + @Guarded(by = ACTIVE) + public void deleteAsset(Asset asset) { + deleteAsset(asset, writePolicySelector.select(asset, writePolicy)); + } + + private void deleteAsset(final Asset asset, @Nullable final WritePolicy effectiveWritePolicy) { + checkNotNull(asset); + + BlobRef blobRef = asset.blobRef(); + if (blobRef != null) { + deleteBlob(blobRef, effectiveWritePolicy, format("Deleting asset %s", EntityHelper.id(asset))); + } + assetEntityAdapter.deleteEntity(db, asset); + } + + @Override + @Guarded(by = ACTIVE) + public AssetBlob createBlob(final String blobName, + final Supplier streamSupplier, + final Iterable hashAlgorithms, + @Nullable final Map headers, + @Nullable final String declaredContentType, + final boolean skipContentVerification) throws IOException + { + checkNotNull(blobName); + checkNotNull(streamSupplier); + checkNotNull(hashAlgorithms); + + if (!writePolicy.checkCreateAllowed()) { + throw new IllegalOperationException("Repository is read only: " + repositoryName); + } + + Map storageHeadersMap = buildStorageHeaders(blobName, streamSupplier, headers, declaredContentType, + skipContentVerification); + return blobTx.create( + streamSupplier.get(), + storageHeadersMap, + hashAlgorithms, + storageHeadersMap.get(BlobStore.CONTENT_TYPE_HEADER) + ); + } + + @Override + @Guarded(by = ACTIVE) + public AssetBlob createBlob(final String blobName, + final Path sourceFile, + final Map hashes, + @Nullable final Map headers, + final String declaredContentType, + final long size) throws IOException + { + checkNotNull(blobName); + checkNotNull(sourceFile); + checkNotNull(hashes); + checkArgument(!Strings2.isBlank(declaredContentType), "no declaredContentType provided"); + + if (!writePolicy.checkCreateAllowed()) { + throw new IllegalOperationException("Repository is read only: " + repositoryName); + } + + Map storageHeaders = buildStorageHeaders(blobName, null, headers, declaredContentType, true); + return blobTx.createByHardLinking( + sourceFile, + storageHeaders, + hashes, + declaredContentType, + size + ); + } + + @Override + public AssetBlob createBlob(final String blobName, + final TempBlob originalBlob, + @Nullable final Map headers, + @Nullable final String declaredContentType, + boolean skipContentVerification) + throws IOException + { + checkNotNull(blobName); + checkNotNull(originalBlob); + + if (!writePolicy.checkCreateAllowed()) { + throw new IllegalOperationException("Repository is read only: " + repositoryName); + } + + Map storageHeadersMap = buildStorageHeaders(blobName, originalBlob, headers, declaredContentType, + skipContentVerification); + return blobTx.createByCopying( + originalBlob.getBlob().getId(), + storageHeadersMap, + originalBlob.getHashes(), + originalBlob.getHashesVerified() + ); + } + + private Map buildStorageHeaders(final String blobName, + @Nullable final Supplier streamSupplier, + @Nullable final Map headers, + @Nullable final String declaredContentType, + final boolean skipContentVerification) throws IOException + { + checkArgument( + !skipContentVerification || !Strings2.isBlank(declaredContentType), + "skipContentVerification set true but no declaredContentType provided" + ); + Builder storageHeaders = ImmutableMap.builder(); + storageHeaders.put(Bucket.REPO_NAME_HEADER, repositoryName); + storageHeaders.put(BlobStore.BLOB_NAME_HEADER, blobName); + storageHeaders.put(BlobStore.CREATED_BY_HEADER, createdBy); + storageHeaders.put(BlobStore.CREATED_BY_IP_HEADER, createdByIp); + if (!skipContentVerification) { + storageHeaders.put( + BlobStore.CONTENT_TYPE_HEADER, + determineContentType(streamSupplier, blobName, declaredContentType) + ); + } + else { + storageHeaders.put(BlobStore.CONTENT_TYPE_HEADER, declaredContentType); + } + if (headers != null) { + storageHeaders.putAll(headers); + } + return storageHeaders.build(); + } + + @Override + @Guarded(by = ACTIVE) + public void attachBlob(final Asset asset, final AssetBlob assetBlob) + { + checkNotNull(asset); + checkNotNull(assetBlob); + checkArgument(!assetBlob.isAttached(), "Blob is already attached to an asset"); + + final WritePolicy effectiveWritePolicy = writePolicySelector.select(asset, writePolicy); + if (!effectiveWritePolicy.checkCreateAllowed()) { + throw new IllegalOperationException("Repository is read only: " + repositoryName); + } + + NestedAttributesMap checksums = asset.attributes().child(CHECKSUM); + + if (!isDuplicateBlob(asset, assetBlob, effectiveWritePolicy, checksums)) { + maybeDeleteBlob(asset, effectiveWritePolicy); + + asset.blobRef(assetBlob.getBlobRef()); + asset.size(assetBlob.getSize()); + asset.contentType(assetBlob.getContentType()); + + // Set attributes map to contain computed checksum metadata + for (Entry entry : assetBlob.getHashes().entrySet()) { + HashAlgorithm algorithm = entry.getKey(); + HashCode checksum = entry.getValue(); + checksums.set(algorithm.name(), checksum.toString()); + } + + // Mark assets whose checksums were not verified locally, for possible later verification + NestedAttributesMap provenance = asset.attributes().child(PROVENANCE); + provenance.set(HASHES_NOT_VERIFIED, !assetBlob.getHashesVerified()); + + Map blobHeaders = assetBlob.getBlob().getHeaders(); + if (blobHeaders.containsKey(BlobStore.CREATED_BY_HEADER)) { + asset.createdBy(blobHeaders.get(BlobStore.CREATED_BY_HEADER)); + } + if (blobHeaders.containsKey(BlobStore.CREATED_BY_IP_HEADER)) { + asset.createdByIp(blobHeaders.get(BlobStore.CREATED_BY_IP_HEADER)); + } + + assetBlob.setAttached(true); + } + } + + /** + * Returns {@code true} when at least one incoming hash has the same algorithm as an existing checksum. + */ + private boolean checksumExists(final NestedAttributesMap checksums, final AssetBlob assetBlob) { + if (!checksums.isEmpty()) { + for (HashAlgorithm algorithm : assetBlob.getHashes().keySet()) { + if (checksums.contains(algorithm.name())) { + return true; + } + } + } + return false; + } + + /** + * Returns {@code true} when incoming hashes all match existing checksums. + */ + private boolean compareChecksums(final NestedAttributesMap checksums, final AssetBlob assetBlob) { + for (Entry entry : assetBlob.getHashes().entrySet()) { + HashAlgorithm algorithm = entry.getKey(); + HashCode checksum = entry.getValue(); + if (!checksum.toString().equals(checksums.get(algorithm.name()))) { + return false; + } + } + return true; + } + + /** + * Checks whether incoming blob is a duplicate of existing asset blob; if so it re-uses the old blob. + */ + private boolean isDuplicateBlob(final Asset asset, + final AssetBlob assetBlob, + final WritePolicy effectiveWritePolicy, + final NestedAttributesMap checksums) + { + if (asset.blobRef() != null) { + Blob oldBlob = blobTx.get(asset.blobRef()); + if (oldBlob != null) { + boolean checksumsMatch; + if (assetBlob.getHashesVerified() && checksumExists(checksums, assetBlob)) { + // we have verified hashes, use those to avoid touching blob metrics + checksumsMatch = compareChecksums(checksums, assetBlob); + } + else { + // fall back to blob metrics, which involves fetching the new blob + String oldBlobSha1 = oldBlob.getMetrics().getSha1Hash(); + String newBlobSha1 = assetBlob.getBlob().getMetrics().getSha1Hash(); + checksumsMatch = oldBlobSha1.equalsIgnoreCase(newBlobSha1); + } + if (checksumsMatch) { + // still respect write policy even when de-duplicating + if (!effectiveWritePolicy.checkUpdateAllowed()) { + throw new IllegalOperationException("Repository does not allow updating assets: " + repositoryName); + } + assetBlob.setDuplicate(oldBlob); + return true; + } + } + } + return false; + } + + /** + * Deletes the existing blob for the asset if one exists, updating the blob updated field if necessary. The + * write policy will be enforced for this operation and will throw an exception if updates are not supported. + */ + private void maybeDeleteBlob(final Asset asset, final WritePolicy effectiveWritePolicy) + { + DateTime now = DateTime.now(); + if (asset.blobRef() != null) { + // updating old blob + if (!effectiveWritePolicy.checkUpdateAllowed()) { + throw new IllegalOperationException("Repository does not allow updating assets: " + repositoryName); + } + asset.blobUpdated(now); + deleteBlob(asset.blobRef(), effectiveWritePolicy, format("Updating asset %s", EntityHelper.id(asset))); + } + else { + // creating new blob + asset.blobCreated(now); + asset.blobUpdated(now); + } + } + + @Override + @Guarded(by = ACTIVE) + public AssetBlob setBlob(final Asset asset, + final String blobName, + final Supplier streamSupplier, + final Iterable hashAlgorithms, + @Nullable final Map headers, + @Nullable final String declaredContentType, + final boolean skipContentVerification) throws IOException + { + checkNotNull(asset); + + // Enforce write policy ahead, as we have asset here + BlobRef oldBlobRef = asset.blobRef(); + if (oldBlobRef != null) { + if (!writePolicySelector.select(asset, writePolicy).checkUpdateAllowed()) { + throw new IllegalOperationException("Repository does not allow updating assets: " + repositoryName); + } + } + final AssetBlob assetBlob = createBlob( + blobName, + streamSupplier, + hashAlgorithms, + headers, + declaredContentType, + skipContentVerification + ); + attachBlob(asset, assetBlob); + return assetBlob; + } + + @Override + @Guarded(by = ACTIVE) + public AssetBlob setBlob(final Asset asset, + final String blobName, + final Path sourceFile, + final Map hashes, + @Nullable final Map headers, + final String declaredContentType, + final long size) throws IOException + { + checkNotNull(asset); + checkArgument(!Strings2.isBlank(declaredContentType), "no declaredContentType provided"); + + // Enforce write policy ahead, as we have asset here + BlobRef oldBlobRef = asset.blobRef(); + if (oldBlobRef != null) { + if (!writePolicySelector.select(asset, writePolicy).checkUpdateAllowed()) { + throw new IllegalOperationException( "Repository does not allow updating assets: " + repositoryName); + } + } + final AssetBlob assetBlob = createBlob( + blobName, + sourceFile, + hashes, + headers, + declaredContentType, + size + ); + attachBlob(asset, assetBlob); + return assetBlob; + } + + @Override + public AssetBlob setBlob(final Asset asset, + final String blobName, + final TempBlob originalBlob, + @Nullable final Map headers, + @Nullable String declaredContentType, + boolean skipContentVerification) + throws IOException + { + checkNotNull(blobName); + checkNotNull(originalBlob); + + // Enforce write policy ahead, as we have asset here + BlobRef oldBlobRef = asset.blobRef(); + if (oldBlobRef != null && !writePolicySelector.select(asset, writePolicy).checkUpdateAllowed()) { + throw new IllegalOperationException("Repository does not allow updating assets: " + repositoryName); + } + AssetBlob assetBlob = createBlob(blobName, originalBlob, headers, declaredContentType, skipContentVerification); + attachBlob(asset, assetBlob); + return assetBlob; + } + + @Nullable + @Override + @Guarded(by = ACTIVE) + public Blob getBlob(final BlobRef blobRef) { + checkNotNull(blobRef); + + return blobTx.get(blobRef); + } + + @Override + @Guarded(by = ACTIVE) + public Blob requireBlob(final BlobRef blobRef) { + Blob blob = getBlob(blobRef); + if (blob == null) { + throw new MissingBlobException(blobRef); + } + return blob; + } + + @Nonnull + private String determineContentType(final Supplier inputStreamSupplier, + final String blobName, + @Nullable final String declaredContentType) + throws IOException + { + return contentValidator.determineContentType( + strictContentValidation, + inputStreamSupplier, + mimeRulesSource, + blobName, + declaredContentType + ); + } + + /** + * Deletes a blob w/ enforcing {@link WritePolicy} if not {@code null}. otherwise write policy will NOT be checked. + */ + private void deleteBlob(final BlobRef blobRef, @Nullable WritePolicy effectiveWritePolicy, final String reason) { + checkNotNull(blobRef); + if (effectiveWritePolicy != null && !effectiveWritePolicy.checkDeleteAllowed()) { + throw new IllegalOperationException("Repository does not allow deleting assets: " + repositoryName); + } + blobTx.delete(blobRef, reason); + } + + /** + * Returns the {@link Bucket} associated with given repository. + */ + @Nullable + private Bucket bucketOf(final String repositoryName) { + return bucketEntityAdapter.read(db, repositoryName); + } + + /** + * Returns the {@link Bucket}s associated with the given repositories. + */ + @Nullable + private Iterable bucketsOf(@Nullable final Iterable repositories) { + if (repositories == null) { + return null; + } + ImmutableList.Builder bucketsBuilder = ImmutableList.builder(); + for (Repository repository : repositories) { + bucketsBuilder.add(bucketOf(repository.getName())); + } + return bucketsBuilder.build(); + } + + private NumberSequence delaySequence() { + return RandomExponentialSequence.builder() + .start(INITIAL_DELAY_MS) // start at 10ms + .factor(2) // delay an average of 100% longer, each time + .maxDeviation(.5) // ±50% + .build(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/TempBlob.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/TempBlob.java new file mode 100644 index 0000000000000000000000000000000000000000..35ed4adbe7feee637a247a213a08a6ed9578383e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/TempBlob.java @@ -0,0 +1,97 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.io.InputStream; +import java.util.Map; + +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.blobstore.api.BlobStoreException; +import org.sonatype.nexus.common.hash.HashAlgorithm; + +import com.google.common.base.Supplier; +import com.google.common.hash.HashCode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Blob handle that holds information for a temporary blob used in place of temporary files and streams. Instances must + * be closed by the caller. + * + * @since 3.1 + */ +public class TempBlob + implements AutoCloseable, Supplier +{ + private static final Logger log = LoggerFactory.getLogger(TempBlob.class); + + private final Blob blob; + + private final Map hashes; + + private final boolean hashesVerified; + + private final BlobStore blobStore; + + public TempBlob(final Blob blob, + final Map hashes, + final boolean hashesVerified, + final BlobStore blobStore) + { + this.blob = checkNotNull(blob); + this.hashes = checkNotNull(hashes); + this.hashesVerified = hashesVerified; + this.blobStore = checkNotNull(blobStore); + } + + /** + * The actual blob this instance is pointing to. + */ + public Blob getBlob() { + return blob; + } + + /** + * Exact hashes for the blob. Typically calculated by storage subsystem while blob was getting saved, but sometimes + * provided based on precalculated or known values. + */ + public Map getHashes() { + return hashes; + } + + /** + * Returns a boolean indicating whether the hashes associated with this blob have been verified. + */ + public boolean getHashesVerified() { + return hashesVerified; + } + + @Override + public void close() { + try { + blobStore.deleteHard(blob.getId()); + } + catch (BlobStoreException e) { + log.debug("Unable to delete blob {} in blob store {}", blob.getId(), + blobStore.getBlobStoreConfiguration().getName(), e); + } + } + + @Override + public InputStream get() { + return blob.getInputStream(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/UnitOfWorkHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/UnitOfWorkHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..97a8f7465dd74c83be2f1044c3145f9f8c6c10ec --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/UnitOfWorkHandler.java @@ -0,0 +1,45 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Response; +import org.sonatype.nexus.transaction.UnitOfWork; + +/** + * Invokes the remaining handler chain within the repository's unit-of-work. + * + * @since 3.0 + */ +@Named +@Singleton +public class UnitOfWorkHandler + extends ComponentSupport + implements Handler +{ + @Override + public Response handle(Context context) throws Exception { + UnitOfWork.begin(context.getRepository().facet(StorageFacet.class).txSupplier()); + try { + return context.proceed(); + } + finally { + UnitOfWork.end(); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/WritePolicy.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/WritePolicy.java new file mode 100644 index 0000000000000000000000000000000000000000..5e99f1b58fc207c38a59a8cd92d788d3ed054a34 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/WritePolicy.java @@ -0,0 +1,61 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +/** + * Write policy. + * + * @since 3.0 + */ +public enum WritePolicy +{ + /** + * Asset can be linked with a blob. + * Asset can be re-linked with another blob. + * Asset can be unlinked from a blob (blob can be deleted). + */ + ALLOW, + /** + * Asset can be linked with a blob. + * Asset cannot be re-linked with another blob. + * Asset can be unlinked from a blob (blob can be deleted). + */ + ALLOW_ONCE, + /** + * Asset cannot be linked with a blob. + * Asset cannot be re-linked with another blob. + * Asset cannot be unlinked from a blob. + */ + DENY; + + /** + * Returns {@code true} if Create allowed with this policy. + */ + public boolean checkCreateAllowed() { + return this != DENY; + } + + /** + * Returns {@code true} if Update allowed with this policy. + */ + public boolean checkUpdateAllowed() { + return this == ALLOW; + } + + /** + * Returns {@code true} if Delete allowed with this policy. + */ + public boolean checkDeleteAllowed() { + return this != DENY; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/WritePolicySelector.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/WritePolicySelector.java new file mode 100644 index 0000000000000000000000000000000000000000..0babf59aa9b0bdc21e5b8d6dd7176331981f3748 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/WritePolicySelector.java @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +/** + * Write policy selector. + * + * @since 3.0 + */ +public interface WritePolicySelector +{ + /** + * Default instance of {@link WritePolicy} selector, that returns the configured {@link WritePolicy} unchanged. + */ + WritePolicySelector DEFAULT = new WritePolicySelector() + { + @Override + public WritePolicy select(final Asset asset, final WritePolicy configured) { + return configured; + } + }; + + /** + * Returns the effective {@link WritePolicy} for given asset blob changes. + */ + WritePolicy select(final Asset asset, final WritePolicy configured); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/AssetAuditor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/AssetAuditor.java new file mode 100644 index 0000000000000000000000000000000000000000..0ec0e57410c3633b3c20d83d228c4af0e56781c7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/AssetAuditor.java @@ -0,0 +1,70 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import java.util.Map; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.audit.AuditData; +import org.sonatype.nexus.audit.AuditorSupport; +import org.sonatype.nexus.common.event.EventAware; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.AssetCreatedEvent; +import org.sonatype.nexus.repository.storage.AssetDeletedEvent; +import org.sonatype.nexus.repository.storage.AssetEvent; +import org.sonatype.nexus.repository.storage.AssetUpdatedEvent; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.Subscribe; + +/** + * Repository asset auditor. + * + * @since 3.1 + */ +@Named +@Singleton +public class AssetAuditor + extends AuditorSupport + implements EventAware +{ + public static final String DOMAIN = "repository.asset"; + + public AssetAuditor() { + registerType(AssetCreatedEvent.class, CREATED_TYPE); + registerType(AssetDeletedEvent.class, DELETED_TYPE); + registerType(AssetUpdatedEvent.class, UPDATED_TYPE); + } + + @Subscribe + @AllowConcurrentEvents + public void on(final AssetEvent event) { + if (isRecording() && event.isLocal()) { + Asset asset = event.getAsset(); + + AuditData data = new AuditData(); + data.setDomain(DOMAIN); + data.setType(type(event.getClass())); + data.setContext(asset.name()); + + Map attributes = data.getAttributes(); + attributes.put("repository.name", event.getRepositoryName()); + attributes.put("format", asset.format()); + attributes.put("name", asset.name()); + + record(data); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/AssetContinuationTokenHelper.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/AssetContinuationTokenHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..6944945471a70946bcb1ed58a20e397a9f9b3f08 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/AssetContinuationTokenHelper.java @@ -0,0 +1,36 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.orient.entity.OrientContinuationTokenHelper; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; + +/** + * {@link OrientContinuationTokenHelper} for the {@link AssetEntityAdapter} + * + * @since 3.7 + */ +@Named("asset") +@Singleton +public class AssetContinuationTokenHelper + extends OrientContinuationTokenHelper +{ + @Inject + public AssetContinuationTokenHelper(AssetEntityAdapter entityAdapter) { + super(entityAdapter); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/BucketCreatedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/BucketCreatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..ed5f0fbead1fb16ca7eea102c3b9a0372b6ed4e6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/BucketCreatedEvent.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import org.sonatype.nexus.common.entity.EntityCreatedEvent; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.repository.storage.Bucket; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * {@link Bucket} created event. + * + * @since 3.6.1 + */ +public class BucketCreatedEvent + extends EntityCreatedEvent + implements BucketEvent +{ + private final String repositoryName; + + public BucketCreatedEvent(final EntityMetadata metadata, final String repositoryName) { + super(metadata); + this.repositoryName = checkNotNull(repositoryName); + } + + @Override + public String getRepositoryName() { + return repositoryName; + } + + @Override + public Bucket getBucket() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/BucketDeletedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/BucketDeletedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..ad27cfa732e3f9faae90d275e3202571bb859155 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/BucketDeletedEvent.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import org.sonatype.nexus.common.entity.EntityDeletedEvent; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.repository.storage.Bucket; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * {@link Bucket} deleted event. + * + * @since 3.6.1 + */ +public class BucketDeletedEvent + extends EntityDeletedEvent + implements BucketEvent +{ + private final String repositoryName; + + public BucketDeletedEvent(final EntityMetadata metadata, final String repositoryName) { + super(metadata); + this.repositoryName = checkNotNull(repositoryName); + } + + @Override + public String getRepositoryName() { + return repositoryName; + } + + @Override + public Bucket getBucket() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/BucketEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/BucketEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..8b327c7ccfd838f80387c9dc0eeecb254a339795 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/BucketEvent.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import org.sonatype.nexus.repository.storage.Bucket; + +/** + * {@link Bucket} event. + * + * @since 3.6.1 + */ +public interface BucketEvent +{ + boolean isLocal(); + + String getRepositoryName(); + + Bucket getBucket(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/BucketUpdatedEvent.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/BucketUpdatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..7c40dcf1dbe2790a55131d19ae200eaa4b650e24 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/BucketUpdatedEvent.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.common.entity.EntityUpdatedEvent; +import org.sonatype.nexus.repository.storage.Bucket; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * {@link Bucket} updated event. + * + * @since 3.6.1 + */ +public class BucketUpdatedEvent + extends EntityUpdatedEvent + implements BucketEvent +{ + private final String repositoryName; + + public BucketUpdatedEvent(final EntityMetadata metadata, final String repositoryName) { + super(metadata); + this.repositoryName = checkNotNull(repositoryName); + } + + @Override + public String getRepositoryName() { + return repositoryName; + } + + @Override + public Bucket getBucket() { + return getEntity(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentAuditor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentAuditor.java new file mode 100644 index 0000000000000000000000000000000000000000..cde8fd6b1410b7aa67ffe0f6cd671f6c45189ebb --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentAuditor.java @@ -0,0 +1,72 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import java.util.Map; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.audit.AuditData; +import org.sonatype.nexus.audit.AuditorSupport; +import org.sonatype.nexus.common.event.EventAware; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.ComponentCreatedEvent; +import org.sonatype.nexus.repository.storage.ComponentDeletedEvent; +import org.sonatype.nexus.repository.storage.ComponentEvent; +import org.sonatype.nexus.repository.storage.ComponentUpdatedEvent; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.Subscribe; + +/** + * Repository component auditor. + * + * @since 3.1 + */ +@Named +@Singleton +public class ComponentAuditor + extends AuditorSupport + implements EventAware +{ + public static final String DOMAIN = "repository.component"; + + public ComponentAuditor() { + registerType(ComponentCreatedEvent.class, CREATED_TYPE); + registerType(ComponentDeletedEvent.class, DELETED_TYPE); + registerType(ComponentUpdatedEvent.class, UPDATED_TYPE); + } + + @Subscribe + @AllowConcurrentEvents + public void on(final ComponentEvent event) { + if (isRecording() && event.isLocal()) { + Component component = event.getComponent(); + + AuditData data = new AuditData(); + data.setDomain(DOMAIN); + data.setType(type(event.getClass())); + data.setContext(component.name()); + + Map attributes = data.getAttributes(); + attributes.put("repository.name", event.getRepositoryName()); + attributes.put("format", component.format()); + attributes.put("name", component.name()); + attributes.put("group", component.group()); + attributes.put("version", component.version()); + + record(data); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentContinuationTokenHelper.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentContinuationTokenHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..c27122c9b726ba0029ea3a0e0be8fc8e3f405789 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentContinuationTokenHelper.java @@ -0,0 +1,36 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.orient.entity.OrientContinuationTokenHelper; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; + +/** + * {@link OrientContinuationTokenHelper} for the {@link ComponentEntityAdapter} + * + * @since 3.7 + */ +@Named("component") +@Singleton +public class ComponentContinuationTokenHelper + extends OrientContinuationTokenHelper +{ + @Inject + public ComponentContinuationTokenHelper(ComponentEntityAdapter entityAdapter) { + super(entityAdapter); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_3.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_3.java new file mode 100644 index 0000000000000000000000000000000000000000..f91ea0df66bc808684ac611d05e657ace79aaab7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_3.java @@ -0,0 +1,103 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.upgrade.Upgrades; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseInstanceNames; +import org.sonatype.nexus.orient.DatabaseUpgradeSupport; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OSchema; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.sql.OCommandSQL; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Upgrade step to add blob_created and blob_updated fields to the asset class in the component database. + * + * @since 3.3 + */ +@Named +@Singleton +@Upgrades(model = DatabaseInstanceNames.COMPONENT, from = "1.2", to = "1.3") +public class ComponentDatabaseUpgrade_1_3 // NOSONAR + extends DatabaseUpgradeSupport +{ + static final String ASSET_CLASS = new OClassNameBuilder() + .type("asset") + .build(); + + static final String P_LAST_ACCESSED = "last_accessed"; + + static final String ALTER_ASSET_LAST_ACCESSED = "alter property asset.last_accessed name last_downloaded"; + + private final Provider componentDatabaseInstance; + + @Inject + public ComponentDatabaseUpgrade_1_3( + @Named(DatabaseInstanceNames.COMPONENT) final Provider componentDatabaseInstance) + { + this.componentDatabaseInstance = checkNotNull(componentDatabaseInstance); + } + + @Override + public void apply() throws Exception { + if (hasSchemaClass(componentDatabaseInstance, ASSET_CLASS)) { + createAssetBlobCreatedField(); + createAssetBlobUpdatedField(); + renameAssetLastAccessedField(); + } + } + + private void createAssetBlobCreatedField() { + try (ODatabaseDocumentTx db = componentDatabaseInstance.get().connect()) { + maybeCreateProperty(getAssetDbClass(db), AssetEntityAdapter.P_BLOB_CREATED, OType.DATETIME); + } + } + + private void createAssetBlobUpdatedField() { + try (ODatabaseDocumentTx db = componentDatabaseInstance.get().connect()) { + maybeCreateProperty(getAssetDbClass(db), AssetEntityAdapter.P_BLOB_UPDATED, OType.DATETIME); + } + } + + private void renameAssetLastAccessedField() { + try (ODatabaseDocumentTx db = componentDatabaseInstance.get().connect()) { + OClass assetClass = getAssetDbClass(db); + if (assetClass.existsProperty(P_LAST_ACCESSED)) { + db.command(new OCommandSQL(ALTER_ASSET_LAST_ACCESSED)).execute(); + } + } + } + + private OClass getAssetDbClass(final ODatabaseDocumentTx db) { + OSchema schema = db.getMetadata().getSchema(); + return schema.getClass(ASSET_CLASS); + } + + private void maybeCreateProperty(final OClass oClass, final String property, final OType oType) { + if (!oClass.existsProperty(property)) { + oClass.createProperty(property, oType); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_4.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_4.java new file mode 100644 index 0000000000000000000000000000000000000000..f447b796cf338c5cef8630155bd236ab3d484c50 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_4.java @@ -0,0 +1,140 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.upgrade.Upgrades; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseInstanceNames; +import org.sonatype.nexus.orient.DatabaseUpgradeSupport; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.OIndexBuilder; +import org.sonatype.nexus.orient.OIndexNameBuilder; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.index.OIndexManager; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE; +import com.orientechnologies.orient.core.metadata.schema.OType; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.storage.ComponentEntityAdapter.P_GROUP; +import static org.sonatype.nexus.repository.storage.ComponentEntityAdapter.P_VERSION; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_NAME; + +/** + * Upgrade step to add asset and component indices. + * + * @since 3.3 + */ +@Named +@Singleton +@Upgrades(model = DatabaseInstanceNames.COMPONENT, from = "1.3", to = "1.4") +public class ComponentDatabaseUpgrade_1_4 // NOSONAR + extends DatabaseUpgradeSupport +{ + public static final String COMPONENT_CLASS = new OClassNameBuilder() + .type("component") + .build(); + + public static final String ASSET_CLASS = new OClassNameBuilder() + .type("asset") + .build(); + + public static final String I_NAME_CASE_INSENSITIVE = new OIndexNameBuilder() + .type(COMPONENT_CLASS) + .property(P_NAME) + .caseInsensitive() + .build(); + + public static final String I_COMPONENT = new OIndexNameBuilder() + .type(ASSET_CLASS) + .property(AssetEntityAdapter.P_COMPONENT) + .build(); + + public static final String I_ASSET_NAME = new OIndexNameBuilder() + .type(ASSET_CLASS) + .property(P_NAME) + .caseInsensitive() + .build(); + + public static final String I_COMPONENT_GROUP_NAME_VERSION = new OIndexNameBuilder() + .type(COMPONENT_CLASS) + .property(P_GROUP) + .property(P_NAME) + .property(P_VERSION) + .caseInsensitive() + .build(); + + private final Provider componentDatabaseInstance; + + @Inject + public ComponentDatabaseUpgrade_1_4( + @Named(DatabaseInstanceNames.COMPONENT) final Provider componentDatabaseInstance) + { + this.componentDatabaseInstance = checkNotNull(componentDatabaseInstance); + } + + @Override + public void apply() throws Exception { + withDatabaseAndClass(componentDatabaseInstance, ASSET_CLASS, (db, type) -> { + createComponentIndex(db, type); + createAssetNameIdx(db, type); + }); + withDatabaseAndClass(componentDatabaseInstance, COMPONENT_CLASS, (db, type) -> { + createNameCaseInsensitiveIndex(db, type); + createComponentGroupNameVersionIdx(db, type); + }); + } + + private void createNameCaseInsensitiveIndex(final ODatabaseDocumentTx db, final OClass type) { + OIndexManager indexManager = db.getMetadata().getIndexManager(); + if (indexManager.getIndex(I_NAME_CASE_INSENSITIVE) == null) { + new OIndexBuilder(type, I_NAME_CASE_INSENSITIVE, INDEX_TYPE.NOTUNIQUE) + .property(P_NAME, OType.STRING) + .caseInsensitive() + .build(db); + } + } + + private void createComponentIndex(final ODatabaseDocumentTx db, final OClass type) { + if (db.getMetadata().getIndexManager().getIndex(I_COMPONENT) == null) { + type.createIndex(I_COMPONENT, INDEX_TYPE.NOTUNIQUE, AssetEntityAdapter.P_COMPONENT); + } + } + + private void createAssetNameIdx(final ODatabaseDocumentTx db, final OClass type) { + if (db.getMetadata().getIndexManager().getIndex(I_ASSET_NAME) == null) { + new OIndexBuilder(type, I_ASSET_NAME, INDEX_TYPE.NOTUNIQUE) + .property(P_NAME, OType.STRING) + .caseInsensitive() + .build(db); + } + } + + private void createComponentGroupNameVersionIdx(final ODatabaseDocumentTx db, final OClass type) { + if (db.getMetadata().getIndexManager().getIndex(I_COMPONENT_GROUP_NAME_VERSION) == null) { + new OIndexBuilder(type, I_COMPONENT_GROUP_NAME_VERSION, INDEX_TYPE.NOTUNIQUE) + .property(P_GROUP, OType.STRING) + .property(P_NAME, OType.STRING) + .property(P_VERSION, OType.STRING) + .caseInsensitive() + .build(db); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_5.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_5.java new file mode 100644 index 0000000000000000000000000000000000000000..cb9f8730eef1ba202fa39abc7f2b6a6f3f436431 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_5.java @@ -0,0 +1,172 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import java.util.List; +import java.util.Locale; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.upgrade.Upgrades; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseInstanceNames; +import org.sonatype.nexus.orient.DatabaseUpgradeSupport; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.OIndexBuilder; +import org.sonatype.nexus.orient.OIndexNameBuilder; + +import com.orientechnologies.orient.core.collate.OCaseInsensitiveCollate; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.index.OIndex; +import com.orientechnologies.orient.core.index.OIndexManager; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE; +import com.orientechnologies.orient.core.metadata.schema.OProperty; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.sql.query.OSQLQuery; +import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.storage.ComponentEntityAdapter.P_CI_NAME; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_NAME; + +/** + * Upgrade step to introduce ci_name field on component for faster queries. + * + * @since 3.4 + */ +@Named +@Singleton +@Upgrades(model = DatabaseInstanceNames.COMPONENT, from = "1.4", to = "1.5") +public class ComponentDatabaseUpgrade_1_5 + extends DatabaseUpgradeSupport +{ + private static final String COMPONENT_CLASS = new OClassNameBuilder() + .type("component") + .build(); + + private static final String I_CI_NAME_CASE_INSENSITIVE = new OIndexNameBuilder() + .type(COMPONENT_CLASS) + .property(P_CI_NAME) + .caseInsensitive() + .build(); + + private static final String I_NAME_CASE_INSENSITIVE = new OIndexNameBuilder() + .type(COMPONENT_CLASS) + .property(P_NAME) + .caseInsensitive() + .build(); + + private static final int BATCH_SIZE = 500; + + private static final String SELECT_COMPONENT_BATCH_SQL = String + .format("select from component where ci_name is null limit %d", BATCH_SIZE); + + private OSQLQuery SELECT_COMPONENT_BATCH = new OSQLSynchQuery<>(SELECT_COMPONENT_BATCH_SQL); + + private final Provider componentDatabaseInstance; + + @Inject + public ComponentDatabaseUpgrade_1_5( + @Named(DatabaseInstanceNames.COMPONENT) final Provider componentDatabaseInstance) + { + this.componentDatabaseInstance = checkNotNull(componentDatabaseInstance); + } + + @Override + public void apply() throws Exception { + withDatabaseAndClass(componentDatabaseInstance, COMPONENT_CLASS, (db, type) -> { + // note that the ci_name field is created as not-mandatory and nullable because we still have to populate its + // contents as part of the upgrade, once populated we go back and change the field to mandatory and non-null + createCaseInsensitiveNameField(type); + createCaseInsensitiveNameCaseInsensitiveIndex(db, type); + populateCaseInsensitiveNameField(db); + modifyCaseInsensitiveNameField(type); + deleteNameCaseInsensitiveIndex(db); + }); + } + + private void createCaseInsensitiveNameField(final OClass type) { + log.info("Creating case-insensitive name field on component"); + if (!type.existsProperty(P_CI_NAME)) { + type.createProperty(P_CI_NAME, OType.STRING) + .setCollate(new OCaseInsensitiveCollate()) + .setMandatory(false) + .setNotNull(false); + } + } + + private void populateCaseInsensitiveNameField(final ODatabaseDocumentTx db) { + log.info("Populating case-insensitive name field on component, this could be a long-running operation"); + try { + boolean hasRecords = true; + while (hasRecords) { + hasRecords = populateCaseInsensitiveNameFieldBatch(db); + } + } + finally { + db.rollback(); + } + } + + private boolean populateCaseInsensitiveNameFieldBatch(final ODatabaseDocumentTx db) { + log.trace("Processing batch of {} component records...", BATCH_SIZE); + db.begin(); + List components = db.query(SELECT_COMPONENT_BATCH); + if (components.isEmpty()) { + return false; + } + for (ODocument component : components) { + String name = component.field(P_NAME, String.class); + component.field(P_CI_NAME, name.toLowerCase(Locale.ENGLISH)); + component.save(); + } + db.commit(); + return true; + } + + private void modifyCaseInsensitiveNameField(final OClass type) { + log.info("Modifying case-insensitive name field on component"); + OProperty ciNameProperty = type.getProperty(P_CI_NAME); + if (!ciNameProperty.isMandatory()) { + ciNameProperty.setMandatory(true); + } + if (!ciNameProperty.isNotNull()) { + ciNameProperty.setNotNull(true); + } + } + + private void createCaseInsensitiveNameCaseInsensitiveIndex(final ODatabaseDocumentTx db, final OClass type) { + log.info("Creating case-insensitive index on case-insensitive name field on component"); + OIndexManager indexManager = db.getMetadata().getIndexManager(); + if (indexManager.getIndex(I_CI_NAME_CASE_INSENSITIVE) == null) { + new OIndexBuilder(type, I_CI_NAME_CASE_INSENSITIVE, INDEX_TYPE.NOTUNIQUE) + .property(P_CI_NAME, OType.STRING) + .caseInsensitive() + .build(db); + } + } + + private void deleteNameCaseInsensitiveIndex(final ODatabaseDocumentTx db) { + log.info("Deleting old case-insensitive name index on component"); + OIndexManager indexManager = db.getMetadata().getIndexManager(); + OIndex nameCaseInsensitiveIndex = indexManager.getIndex(I_NAME_CASE_INSENSITIVE); + if (nameCaseInsensitiveIndex != null) { + indexManager.dropIndex(I_NAME_CASE_INSENSITIVE); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_7.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_7.java new file mode 100644 index 0000000000000000000000000000000000000000..5a326dc19bb9d183d9b489206b5e20b2a5db00bc --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_7.java @@ -0,0 +1,70 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.upgrade.Upgrades; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseInstanceNames; +import org.sonatype.nexus.orient.DatabaseUpgradeSupport; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; + +import com.orientechnologies.orient.core.metadata.schema.OType; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Upgrade step to fix the browse_node table's properties and indices if it was defined without them via 3.6 + * + * @since 3.6.1 + */ +@Named +@Singleton +@Upgrades(model = DatabaseInstanceNames.COMPONENT, from = "1.6", to = "1.7") +public class ComponentDatabaseUpgrade_1_7 + extends DatabaseUpgradeSupport +{ + private static final String ASSET = new OClassNameBuilder().type("asset").build(); + + private static final String BROWSE_NODE = new OClassNameBuilder().type("browse_node").build(); + + private final Provider componentDatabaseInstance; + + @Inject + public ComponentDatabaseUpgrade_1_7( + @Named(DatabaseInstanceNames.COMPONENT) final Provider componentDatabaseInstance) + { + this.componentDatabaseInstance = checkNotNull(componentDatabaseInstance); + } + + @Override + public void apply() throws Exception { + withDatabaseAndClass(componentDatabaseInstance, BROWSE_NODE, (db, table) -> { + db.getMetadata().getSchema().dropClass(BROWSE_NODE); + }); + + withDatabaseAndClass(componentDatabaseInstance, ASSET, (db, table) -> { + if (!table.existsProperty(AssetEntityAdapter.P_CREATED_BY)) { + table.createProperty(AssetEntityAdapter.P_CREATED_BY, OType.STRING); + } + if (!table.existsProperty(AssetEntityAdapter.P_CREATED_BY_IP)) { + table.createProperty(AssetEntityAdapter.P_CREATED_BY_IP, OType.STRING); + } + }); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_8.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_8.java new file mode 100644 index 0000000000000000000000000000000000000000..abb79f332474a1a0aaa3a7d08fafdda6a077a5a7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_8.java @@ -0,0 +1,56 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.upgrade.Upgrades; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.DatabaseInstanceNames; +import org.sonatype.nexus.orient.DatabaseUpgradeSupport; +import org.sonatype.nexus.orient.OClassNameBuilder; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Upgrade step to drop the old browse_node schema and data so it can be replaced and rebuilt. + * + * @since 3.7 + */ +@Named +@Singleton +@Upgrades(model = DatabaseInstanceNames.COMPONENT, from = "1.7", to = "1.8") +public class ComponentDatabaseUpgrade_1_8 + extends DatabaseUpgradeSupport +{ + private static final String BROWSE_NODE = new OClassNameBuilder().type("browse_node").build(); + + private final Provider componentDatabaseInstance; + + @Inject + public ComponentDatabaseUpgrade_1_8( + @Named(DatabaseInstanceNames.COMPONENT) final Provider componentDatabaseInstance) + { + this.componentDatabaseInstance = checkNotNull(componentDatabaseInstance); + } + + @Override + public void apply() throws Exception { + withDatabaseAndClass(componentDatabaseInstance, BROWSE_NODE, (db, table) -> { + db.getMetadata().getSchema().dropClass(BROWSE_NODE); + }); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentSchemaRegistration.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentSchemaRegistration.java new file mode 100644 index 0000000000000000000000000000000000000000..4dc567b389ba5d600705717511abf2b0f42cfb94 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/ComponentSchemaRegistration.java @@ -0,0 +1,73 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.lifecycle.LifecycleSupport; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; +import org.sonatype.nexus.repository.storage.BucketEntityAdapter; +import org.sonatype.nexus.repository.storage.ComponentDatabase; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SCHEMAS; + +/** + * Registers schemas used for component storage. + * + * @since 3.2.1 + */ +@Named +@ManagedLifecycle(phase = SCHEMAS) +@Singleton +public class ComponentSchemaRegistration + extends LifecycleSupport +{ + private final Provider databaseInstance; + + private final BucketEntityAdapter bucketEntityAdapter; + + private final ComponentEntityAdapter componentEntityAdapter; + + private final AssetEntityAdapter assetEntityAdapter; + + @Inject + public ComponentSchemaRegistration(@Named(ComponentDatabase.NAME) final Provider databaseInstance, + final BucketEntityAdapter bucketEntityAdapter, + final ComponentEntityAdapter componentEntityAdapter, + final AssetEntityAdapter assetEntityAdapter) + { + this.databaseInstance = checkNotNull(databaseInstance); + + this.bucketEntityAdapter = checkNotNull(bucketEntityAdapter); + this.componentEntityAdapter = checkNotNull(componentEntityAdapter); + this.assetEntityAdapter = checkNotNull(assetEntityAdapter); + } + + @Override + protected void doStart() throws Exception { + try (ODatabaseDocumentTx db = databaseInstance.get().connect()) { + bucketEntityAdapter.register(db); + componentEntityAdapter.register(db); + assetEntityAdapter.register(db); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTask.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTask.java new file mode 100644 index 0000000000000000000000000000000000000000..0784a633db3f4ecea885cc877bb54e3d041e5872 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTask.java @@ -0,0 +1,70 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.nexus.logging.task.TaskLogging; +import org.sonatype.nexus.repository.storage.StorageFacetManager; +import org.sonatype.nexus.scheduling.Cancelable; +import org.sonatype.nexus.scheduling.TaskSupport; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.logging.task.TaskLogType.NEXUS_LOG_ONLY; + +/** + * Background task (hidden from users) that cleans up deleted storage facets. Triggered programmatically on repo delete + * or on Nexus startup. + * + * @since 3.2.1 + */ +@Named +@TaskLogging(NEXUS_LOG_ONLY) +public class StorageFacetCleanupTask + extends TaskSupport + implements Cancelable +{ + private final StorageFacetManager storageFacetManager; + + private volatile Thread thread; + + @Inject + public StorageFacetCleanupTask(final StorageFacetManager storageFacetManager) { + this.storageFacetManager = checkNotNull(storageFacetManager); + } + + @Override + protected Void execute() throws Exception { + thread = Thread.currentThread(); + long count; + do { + count = storageFacetManager.performDeletions(); + } + while (count > 0 && !isCanceled()); + return null; + } + + @Override + public void cancel() { + super.cancel(); + if (thread != null) { + thread.interrupt(); + } + } + + @Override + public String getMessage() { + return "Reclaim storage for deleted repositories"; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTaskDescriptor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTaskDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..97360d957cfc6161bdc9ed7e1af2b84086eae28b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTaskDescriptor.java @@ -0,0 +1,35 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.scheduling.TaskDescriptorSupport; + +/** + * @since 3.2.1 + */ +@Named +@Singleton +public class StorageFacetCleanupTaskDescriptor + extends TaskDescriptorSupport +{ + public static final String TYPE_ID = "repository.storage-facet-cleanup"; + + @Inject + public StorageFacetCleanupTaskDescriptor() { + super(TYPE_ID, StorageFacetCleanupTask.class, "Storage facet cleanup", NOT_VISIBLE, NOT_EXPOSED); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTaskManager.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTaskManager.java new file mode 100644 index 0000000000000000000000000000000000000000..57318a59bc0530ebe54ca7da4c9711385f94caa5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTaskManager.java @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import java.util.Date; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.scheduling.TaskConfiguration; +import org.sonatype.nexus.scheduling.TaskScheduler; +import org.sonatype.nexus.scheduling.schedule.Schedule; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.TASKS; +import static org.sonatype.nexus.repository.storage.internal.StorageFacetCleanupTaskDescriptor.TYPE_ID; + +/** + * @since 3.6 + */ +@Named +@ManagedLifecycle(phase = TASKS) +@Singleton +public class StorageFacetCleanupTaskManager + extends StateGuardLifecycleSupport +{ + private final TaskScheduler taskScheduler; + + private final String storageCleanupCron; + + @Inject + public StorageFacetCleanupTaskManager(final TaskScheduler taskScheduler, + @Named("${nexus.storageCleanup.cron:-0 */10 * * * ?}") final String storageCleanupCron) + { + this.taskScheduler = checkNotNull(taskScheduler); + this.storageCleanupCron = checkNotNull(storageCleanupCron); + } + + @Override + protected void doStart() throws Exception { + if (!taskScheduler.listsTasks().stream().anyMatch((info) -> TYPE_ID.equals(info.getConfiguration().getTypeId()))) { + TaskConfiguration configuration = taskScheduler.createTaskConfigurationInstance(TYPE_ID); + Schedule schedule = taskScheduler.getScheduleFactory().cron(new Date(), storageCleanupCron); + taskScheduler.scheduleTask(configuration, schedule); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/StorageFacetManagerImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/StorageFacetManagerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..3d4bbe4511b9b2858b813ee8f933cfe8ffcf03a4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/internal/StorageFacetManagerImpl.java @@ -0,0 +1,133 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.stateguard.Guarded; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.BucketDeleter; +import org.sonatype.nexus.repository.storage.BucketEntityAdapter; +import org.sonatype.nexus.repository.storage.ComponentDatabase; +import org.sonatype.nexus.repository.storage.StorageFacetManager; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SERVICES; +import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.STARTED; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTx; +import static org.sonatype.nexus.orient.transaction.OrientTransactional.inTxRetry; + +/** + * @since 3.2.1 + */ +@Named +@ManagedLifecycle(phase = SERVICES) +@Singleton +public class StorageFacetManagerImpl + extends StateGuardLifecycleSupport + implements StorageFacetManager +{ + private static final String BUCKET_PENDING_DELETION_KEY = "pendingDeletion"; + + private final Provider databaseInstanceProvider; + + private final BucketEntityAdapter bucketEntityAdapter; + + private final BucketDeleter bucketDeleter; + + @Inject + public StorageFacetManagerImpl(@Named(ComponentDatabase.NAME) final Provider databaseInstanceProvider, + final BucketEntityAdapter bucketEntityAdapter, + final BucketDeleter bucketDeleter) + { + this.databaseInstanceProvider = checkNotNull(databaseInstanceProvider); + this.bucketEntityAdapter = checkNotNull(bucketEntityAdapter); + this.bucketDeleter = checkNotNull(bucketDeleter); + } + + @Override + @Guarded(by = STARTED) + public void enqueueDeletion(final Repository repository, final BlobStore blobStore, final Bucket bucket) + { + checkNotNull(repository); + checkNotNull(blobStore); + checkNotNull(bucket); + + // The bucket associated with repository needs a new "repository name" created for it in order to avoid clashes. + // Consider what happens if the bucket is still being deleted and someone tries to reuse that repository name. + String generatedRepositoryName = repository.getName() + '$' + UUID.randomUUID(); + bucket.setRepositoryName(generatedRepositoryName); + bucket.attributes().set(BUCKET_PENDING_DELETION_KEY, true); + + updateBucket(bucket); + } + + @Override + @Guarded(by = STARTED) + public long performDeletions() { + List buckets = findBucketsForDeletion(); + return buckets.stream().filter((bucket) -> { + try { + log.info("Deleting bucket for repository {}", bucket.getRepositoryName()); + deleteBucket(bucket); + return true; + } + catch (Exception e) { + log.warn("Unable to delete bucket with repository name {}, will require manual cleanup", + bucket.getRepositoryName(), e); + return false; + } + }).count(); + } + + /** + * Returns a list of buckets queued for deletion. + */ + private List findBucketsForDeletion() { + return inTx(databaseInstanceProvider).call(db -> { + return StreamSupport + .stream(bucketEntityAdapter.browse(db).spliterator(), false) + .filter((bucket) -> bucket.attributes().contains(BUCKET_PENDING_DELETION_KEY)) + .collect(Collectors.toList()); + }); + } + + /** + * Deletes the specified bucket. + */ + protected void deleteBucket(final Bucket bucket) throws Exception { + bucketDeleter.deleteBucket(bucket); + } + + /** + * Updates the specified bucket. + */ + protected void updateBucket(final Bucket bucket) { + inTxRetry(databaseInstanceProvider).run(db -> { + bucketEntityAdapter.editEntity(db, bucket); + }); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..4380bd2d7ad52c60b92541d671010dbc1caf8c25 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Repository storage-facet and support. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.storage; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/BlobUnavilableException.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/BlobUnavilableException.groovy new file mode 100644 index 0000000000000000000000000000000000000000..d6d5e7b7bf5965e95bb76296fbfa374f75030aff --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/BlobUnavilableException.groovy @@ -0,0 +1,25 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.tools + +/** + * Thrown when Blob InputStream reports no available bytes. + * @since 3.3 + */ +class BlobUnavilableException + extends Exception +{ + BlobUnavilableException() { + super('Blob InputStream reports unavailable') + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/DeadBlobFinder.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/DeadBlobFinder.groovy new file mode 100644 index 0000000000000000000000000000000000000000..163156d1fc5e11ed414d8e8a1de38257a772828e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/DeadBlobFinder.groovy @@ -0,0 +1,224 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.tools + +import java.util.concurrent.TimeUnit + +import javax.inject.Inject +import javax.inject.Named +import javax.validation.constraints.NotNull + +import org.sonatype.goodies.common.ComponentSupport +import org.sonatype.nexus.blobstore.api.Blob +import org.sonatype.nexus.blobstore.api.BlobMetrics +import org.sonatype.nexus.blobstore.api.BlobStoreException +import org.sonatype.nexus.common.hash.HashAlgorithm +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.storage.Asset +import org.sonatype.nexus.repository.storage.AssetEntityAdapter +import org.sonatype.nexus.repository.storage.StorageFacet +import org.sonatype.nexus.repository.storage.StorageTx + +import com.google.common.base.Stopwatch +import groovy.transform.ToString + +import static com.google.common.base.Preconditions.checkNotNull +import static org.sonatype.nexus.repository.tools.ResultState.ASSET_DELETED +import static org.sonatype.nexus.repository.tools.ResultState.DELETED +import static org.sonatype.nexus.repository.tools.ResultState.MISSING_BLOB_REF +import static org.sonatype.nexus.repository.tools.ResultState.SHA1_DISAGREEMENT +import static org.sonatype.nexus.repository.tools.ResultState.UNAVAILABLE_BLOB +import static org.sonatype.nexus.repository.tools.ResultState.UNKNOWN +import static org.sonatype.nexus.repository.tools.ResultState.UNREADABLE_BLOB + +/** + * Examines Asset metadata and confirms the sha1 of all referenced blobs. Reports on any instances where + * Blob binary is missing or indicates a different sha1 than that stored in the DB. + * @since 3.3 + */ +@ToString(includePackage = false) +@Named +class DeadBlobFinder + extends ComponentSupport +{ + final AssetEntityAdapter assetEntityAdapter + + @Inject + DeadBlobFinder(final AssetEntityAdapter assetEntityAdapter) { + this.assetEntityAdapter = checkNotNull(assetEntityAdapter) + } + + /** + * Based on the db metadata, confirm that all Blobs exist and sha1 values match. Can optionally ignore any records + * that don't have a blobRef, which is expected for NuGet search results. + * @parem repository The Repository to inspect + * @param ignoreMissingBlobRefs (defaults to true) + */ + List find(@NotNull final Repository repository, boolean ignoreMissingBlobRefs = true) { + checkNotNull(repository) + + StorageTx tx = repository.facet(StorageFacet).txSupplier().get() + try { + tx.begin() + List deadBlobCandidates = identifySuspects(tx, repository, ignoreMissingBlobRefs) + return verifySuspects(tx, deadBlobCandidates, repository) + } + finally { + tx.close() + } + } + + /** + * Identify any potentially incorrect data by inspecting all Assets in the system. It is expected that + * in a highly concurrent system some of the blob related data may already be stale after being loaded in + * memory, so we collect those for later inspection. + */ + private List identifySuspects(StorageTx tx, Repository repository, + boolean ignoreMissingBlobRefs) + { + List deadBlobCandidates = [] + long blobsExamined = 0 + Stopwatch sw = Stopwatch.createStarted() + tx.browseAssets(tx.findBucket(repository)).each { Asset asset -> + blobsExamined++ + if (!asset.blobRef() && ignoreMissingBlobRefs) { + log.trace("Set to ignore missing blobRef on ${asset}") + } + else { + deadBlobCandidates << checkAsset(repository.name, tx, asset) + } + } + deadBlobCandidates = deadBlobCandidates - null // trim out all null results + log.debug( + "Inspecting repository ${repository.name} took ${sw.elapsed(TimeUnit.MILLISECONDS)}ms for $blobsExamined " + + "assets and identified ${deadBlobCandidates.size()} potentially incorrect Assets for followup assessment") + return deadBlobCandidates + } + + /** + * Load each potentially incorrect Asset individually to see if it is truly out of sync with its referenced Blob. + * If data still appears in an incorrect state, log all pertinent details, including whether or not the Asset + * remains in the same state as when originally inspected. + */ + private List verifySuspects(StorageTx tx, List deadBlobCandidates, Repository repository) + { + List deadBlobs = [] + if (!deadBlobCandidates.isEmpty()) { + Stopwatch sw = Stopwatch.createStarted() + deadBlobCandidates.each { DeadBlobResult candidateResult -> + DeadBlobResult deadBlobResult = + checkAsset(repository.name, tx, assetEntityAdapter.read(tx.db, candidateResult.asset.entityMetadata.id)) + if (deadBlobResult) { + logResults(candidateResult, deadBlobResult) + deadBlobs << deadBlobResult + } + else { + log.debug( + "Asset ${candidateResult.asset.name()} corrected from error state ${candidateResult.resultState} " + + "during inspection") + } + } + deadBlobs = deadBlobs - null // trim out all null results + log.info("Followup inspection of repository ${repository.name} took ${sw.elapsed(TimeUnit.MILLISECONDS)}ms for " + + "${deadBlobCandidates.size()} assets and identified ${deadBlobs.size()} incorrect Assets") + } + return deadBlobs + } + + /** + * Verify the Asset metadata against the attached Blob, categorizing any errors encountered. + */ + private DeadBlobResult checkAsset(final String repositoryName, final StorageTx tx, final Asset asset) { + if (!asset) { + return new DeadBlobResult(repositoryName, null, ASSET_DELETED, 'Asset was deleted during inspection') + } + try { + def blob = tx.requireBlob(asset.requireBlobRef()) + verifyBlob(blob, asset) + } + catch (IllegalStateException ise) { + return new DeadBlobResult(repositoryName, asset, MISSING_BLOB_REF, ise.message) + } + catch (BlobStoreException bse) { + if (bse.cause instanceof IOException) { + return new DeadBlobResult(repositoryName, asset, UNREADABLE_BLOB, bse.message) + } + return new DeadBlobResult(repositoryName, asset, DELETED, bse.message) // check for specific message? + } + catch (MismatchedSHA1Exception pae) { + return new DeadBlobResult(repositoryName, asset, SHA1_DISAGREEMENT, pae.message) + } + catch (BlobUnavilableException e) { + return new DeadBlobResult(repositoryName, asset, UNAVAILABLE_BLOB, e.message ?: 'Blob inputstream unavailable') + } + catch (Throwable e) { + return new DeadBlobResult(repositoryName, asset, UNKNOWN, e.message) + } + return null + } + + /** + * Verify that the Blob exists and is in agreement with the stored Asset metadata. + */ + private void verifyBlob(Blob blob, Asset asset) { + BlobMetrics metrics = blob.metrics + if (metrics.sha1Hash != asset.getChecksum(HashAlgorithm.SHA1).toString()) { + throw new MismatchedSHA1Exception() + } + InputStream blobstream + try { + blobstream = blob.inputStream + if (metrics.contentSize > 0) { + if (!blobstream.available()) { + throw new BlobUnavilableException() + } + } + } + finally { + if (blobstream) { + try { + blobstream.close() + } + catch (e) { + log.error('Unable to close stream', e) + } + } + } + } + + /** + * Log details about an incorrect result, including if state changed between inspections. + */ + private void logResults(final DeadBlobResult firstResult, final DeadBlobResult secondResult) { + log.info("Possible bad data found in Asset: ${secondResult.asset}") + if (lastUpdated(firstResult) != lastUpdated(secondResult)) { + log.info("Asset metadata was updated during inspection between ${lastUpdated(firstResult)} " + + "and ${lastUpdated(secondResult)}") + } + if (firstResult.resultState != secondResult.resultState) { + log.info("Error state changed from ${firstResult.resultState} to ${secondResult.resultState} during inspection") + } + if (blobUpdated(firstResult) != blobUpdated(secondResult)) { + log.info("Asset blob was updated during inspection between ${blobUpdated(firstResult)} " + + "and ${blobUpdated(secondResult)}") + } + } + + private static blobUpdated(DeadBlobResult deadBlobResult) { + deadBlobResult.asset?.blobUpdated() + } + + private static lastUpdated(DeadBlobResult deadBlobResult) { + deadBlobResult.asset?.lastUpdated() + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/DeadBlobResult.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/DeadBlobResult.groovy new file mode 100644 index 0000000000000000000000000000000000000000..58b64ae0b9aa5f1e025847515740bb6869d1d8aa --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/DeadBlobResult.groovy @@ -0,0 +1,45 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.tools + +import javax.annotation.Nullable + +import org.sonatype.nexus.repository.storage.Asset + +import groovy.transform.ToString + +import static com.google.common.base.Preconditions.checkNotNull + +/** + * @since 3.3 + */ +@ToString(includePackage = false) +class DeadBlobResult +{ + final String repositoryName + + final Asset asset + + final String errorMessage + + final ResultState resultState + + DeadBlobResult(final String repositoryName, @Nullable final Asset asset, final ResultState resultState, + final String errorMessage) + { + this.repositoryName = checkNotNull(repositoryName) + this.asset = asset + this.resultState = checkNotNull(resultState) + this.errorMessage = checkNotNull(errorMessage) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/MismatchedSHA1Exception.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/MismatchedSHA1Exception.groovy new file mode 100644 index 0000000000000000000000000000000000000000..52b9f399225833cad990c84fc5f01e86c1d8b471 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/MismatchedSHA1Exception.groovy @@ -0,0 +1,24 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.tools + +/** + * Exception thrown when Asset record metadata has a different SHA1 than the blob suggests. + * @since 3.3 + */ +class MismatchedSHA1Exception extends Exception +{ + MismatchedSHA1Exception() { + super('SHA1 values disagree between the DB and the BlobStore') + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/ResultState.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/ResultState.groovy new file mode 100644 index 0000000000000000000000000000000000000000..a7e8c4d3a4df29a160cabc20db3006930f98962d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/tools/ResultState.groovy @@ -0,0 +1,27 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.tools + +/** + * Possible error states that can be detected by {@link DeadBlobFinder}. + * @since 3.3 + */ +enum ResultState { + ASSET_DELETED, // DB record was deleted during inspection + DELETED, // DB record references blobRef which has since been deleted + MISSING_BLOB_REF, // DB record has no blobRef + SHA1_DISAGREEMENT, // DB record and blob have different SHA1 + UNAVAILABLE_BLOB, // blob has an inputstream that reports 0 when calling isAvailable() + UNKNOWN, + UNREADABLE_BLOB, // Unable to read blob from disk +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalDeleteBlob.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalDeleteBlob.java new file mode 100644 index 0000000000000000000000000000000000000000..e51251563f6f4864f6a7db95892bb3be8b98570c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalDeleteBlob.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.transaction; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import org.sonatype.nexus.repository.storage.MissingBlobException; +import org.sonatype.nexus.transaction.Operations; +import org.sonatype.nexus.transaction.Transactional; + +import com.orientechnologies.common.concur.ONeedRetryException; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Indicates the method will delete blobs; while doing this it may touch other blobs. + * + * @since 3.2 + */ +@Transactional(retryOn = { ONeedRetryException.class, MissingBlobException.class }) +@Target(METHOD) +@Retention(RUNTIME) +public @interface TransactionalDeleteBlob +{ + /** + * Helper to apply this transactional behaviour to lambdas. + */ + Operations operation = Transactional.operation.stereotype(TransactionalDeleteBlob.class); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalStoreBlob.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalStoreBlob.java new file mode 100644 index 0000000000000000000000000000000000000000..b5187720176229826bd19e44aba4c242fab6656f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalStoreBlob.java @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.transaction; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import org.sonatype.nexus.repository.storage.MissingBlobException; +import org.sonatype.nexus.transaction.Operations; +import org.sonatype.nexus.transaction.Transactional; + +import com.orientechnologies.common.concur.ONeedRetryException; +import com.orientechnologies.orient.core.storage.ORecordDuplicatedException; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Indicates the method will create or replace blobs. + * + * @since 3.2 + */ +@Transactional(retryOn = { ONeedRetryException.class, ORecordDuplicatedException.class, MissingBlobException.class }) +@Target(METHOD) +@Retention(RUNTIME) +public @interface TransactionalStoreBlob +{ + /** + * Helper to apply this transactional behaviour to lambdas. + */ + Operations operation = Transactional.operation.stereotype(TransactionalStoreBlob.class); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalStoreMetadata.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalStoreMetadata.java new file mode 100644 index 0000000000000000000000000000000000000000..20a164518b4525e06eedd6368e92058a4fdc1dca --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalStoreMetadata.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.transaction; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import org.sonatype.nexus.transaction.Operations; +import org.sonatype.nexus.transaction.Transactional; + +import com.orientechnologies.common.concur.ONeedRetryException; +import com.orientechnologies.orient.core.storage.ORecordDuplicatedException; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Indicates the method will create or update metadata. + * + * @since 3.2 + */ +@Transactional(retryOn = { ONeedRetryException.class, ORecordDuplicatedException.class }) +@Target(METHOD) +@Retention(RUNTIME) +public @interface TransactionalStoreMetadata +{ + /** + * Helper to apply this transactional behaviour to lambdas. + */ + Operations operation = Transactional.operation.stereotype(TransactionalStoreMetadata.class); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalTouchBlob.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalTouchBlob.java new file mode 100644 index 0000000000000000000000000000000000000000..166e210394b6c1feac25f7a06f88ac80488a910a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalTouchBlob.java @@ -0,0 +1,43 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.transaction; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import org.sonatype.nexus.repository.storage.MissingBlobException; +import org.sonatype.nexus.transaction.Operations; +import org.sonatype.nexus.transaction.Transactional; + +import com.orientechnologies.common.concur.ONeedRetryException; +import com.orientechnologies.common.concur.lock.OModificationOperationProhibitedException; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Indicates the method will access blobs and "touch" their last-downloaded times. + * + * @since 3.2 + */ +@Transactional(retryOn = MissingBlobException.class, swallow = { ONeedRetryException.class, + OModificationOperationProhibitedException.class }) +@Target(METHOD) +@Retention(RUNTIME) +public @interface TransactionalTouchBlob +{ + /** + * Helper to apply this transactional behaviour to lambdas. + */ + Operations operation = Transactional.operation.stereotype(TransactionalTouchBlob.class); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalTouchMetadata.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalTouchMetadata.java new file mode 100644 index 0000000000000000000000000000000000000000..7e9498946bb624d88bc0d59727d3a2be94ef6e69 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/transaction/TransactionalTouchMetadata.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.transaction; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import org.sonatype.nexus.transaction.Operations; +import org.sonatype.nexus.transaction.Transactional; + +import com.orientechnologies.common.concur.ONeedRetryException; +import com.orientechnologies.common.concur.lock.OModificationOperationProhibitedException; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Indicates the method will access metadata and "touch" their last-downloaded times. + * + * @since 3.2 + */ +@Transactional(swallow = { ONeedRetryException.class, OModificationOperationProhibitedException.class }) +@Target(METHOD) +@Retention(RUNTIME) +public @interface TransactionalTouchMetadata +{ + /** + * Helper to apply this transactional behaviour to lambdas. + */ + Operations operation = Transactional.operation.stereotype(TransactionalTouchMetadata.class); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/GroupType.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/GroupType.java new file mode 100644 index 0000000000000000000000000000000000000000..1739389103b1079578c5f53a748eda9c7f20d268 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/GroupType.java @@ -0,0 +1,47 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.types; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.Type; + +import com.google.common.annotations.VisibleForTesting; + +/** + * Group repository type. + * + * @since 3.0 + */ +@Named(GroupType.NAME) +@Singleton +public class GroupType + extends Type +{ + public static final String NAME = "group"; + + @VisibleForTesting + public GroupType() { + super(NAME); + } + + @Override + public Class getValidationGroup() { + return ValidationGroup.class; + } + + public interface ValidationGroup { + // empty + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/HostedType.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/HostedType.java new file mode 100644 index 0000000000000000000000000000000000000000..af4f1cae773665c1ef0a87be0e22cb786022600f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/HostedType.java @@ -0,0 +1,47 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.types; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.Type; + +import com.google.common.annotations.VisibleForTesting; + +/** + * Hosted repository type. + * + * @since 3.0 + */ +@Named(HostedType.NAME) +@Singleton +public class HostedType + extends Type +{ + public static final String NAME = "hosted"; + + @VisibleForTesting + public HostedType() { + super(NAME); + } + + @Override + public Class getValidationGroup() { + return ValidationGroup.class; + } + + public interface ValidationGroup { + // empty + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/ProxyType.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/ProxyType.java new file mode 100644 index 0000000000000000000000000000000000000000..dedc126980a4790e6d254dc0d3ca8f0f463396c5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/ProxyType.java @@ -0,0 +1,47 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.types; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.Type; + +import com.google.common.annotations.VisibleForTesting; + +/** + * Proxy repository type. + * + * @since 3.0 + */ +@Named(ProxyType.NAME) +@Singleton +public class ProxyType + extends Type +{ + public static final String NAME = "proxy"; + + @VisibleForTesting + public ProxyType() { + super(NAME); + } + + @Override + public Class getValidationGroup() { + return ValidationGroup.class; + } + + public interface ValidationGroup { + // empty + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/VirtualType.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/VirtualType.java new file mode 100644 index 0000000000000000000000000000000000000000..1aae5c9e6781b3b3808ce8b12e43f23d549f9e80 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/VirtualType.java @@ -0,0 +1,47 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.types; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.Type; + +import com.google.common.annotations.VisibleForTesting; + +/** + * Virtual repository type. + * + * @since 3.0 + */ +@Named(VirtualType.NAME) +@Singleton +public class VirtualType + extends Type +{ + public static final String NAME = "virtual"; + + @VisibleForTesting + public VirtualType() { + super(NAME); + } + + @Override + public Class getValidationGroup() { + return ValidationGroup.class; + } + + public interface ValidationGroup { + // empty + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..9ddfb4a967b3fcea5efd575daac83c99fdf09518 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/types/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Primary repository {@link org.sonatype.nexus.repository.Type} symbols. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.types; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/AssetUpload.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/AssetUpload.java new file mode 100644 index 0000000000000000000000000000000000000000..281f13e3584ab6b89e734d7a151182ff24b6b3fb --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/AssetUpload.java @@ -0,0 +1,48 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.upload; + +import java.util.HashMap; +import java.util.Map; + +import org.sonatype.nexus.repository.view.PartPayload; + +/** + * An asset being uploaded. + * + * @since 3.7 + */ +public class AssetUpload + implements WithUploadField +{ + private Map fields = new HashMap<>(); + + private PartPayload payload; + + @Override + public Map getFields() { + return fields; + } + + public PartPayload getPayload() { + return payload; + } + + public void setFields(final Map fields) { + this.fields = fields; + } + + public void setPayload(final PartPayload payload) { + this.payload = payload; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/ComponentUpload.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/ComponentUpload.java new file mode 100644 index 0000000000000000000000000000000000000000..d41e5070fa3fa30ebf096676a5ab16759e255752 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/ComponentUpload.java @@ -0,0 +1,48 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.upload; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A component being uploaded. + * + * @since 3.7 + */ +public class ComponentUpload + implements WithUploadField +{ + private List assetUploads = new ArrayList<>(); + + private Map fields = new HashMap<>(); + + public List getAssetUploads() { + return assetUploads; + } + + @Override + public Map getFields() { + return fields; + } + + public void setAssetUploads(final List assetUploads) { + this.assetUploads = assetUploads; + } + + public void setFields(final Map fields) { + this.fields = fields; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadConfiguration.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..712e6fe966de7fe9fdc72e27f3e207d9cb819c60 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadConfiguration.java @@ -0,0 +1,38 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.upload; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * @since 3.7 + */ +public class UploadConfiguration +{ + /** + * Configuration property for enabling the upload ui + */ + public static final String ENABLED = "nexus.upload.component.enabled"; + + private final boolean enabled; + + @Inject + public UploadConfiguration(@Named("${" + ENABLED + ":-false}") final boolean enabled) { + this.enabled = enabled; + } + + public boolean isEnabled() { + return enabled; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadDefinition.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadDefinition.java new file mode 100644 index 0000000000000000000000000000000000000000..534e91e1755b1a9d7096f9ca57dc9babeb6e8805 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadDefinition.java @@ -0,0 +1,99 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.upload; + +import java.util.Collections; +import java.util.List; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Describes the fields associated with a component, fields associated with an asset and whether the format supports + * multiple asset uploads. + * + * @since 3.7 + */ +public class UploadDefinition +{ + + private final boolean multipleUpload; + + private final String format; + + private final List componentFields; + + private final List assetFields; + + private final UploadRegexMap regexMap; + + public UploadDefinition(final String format, + final boolean multipleUpload, + final List componentFields, + final List assetFields, + final UploadRegexMap regexMap) + { + this.multipleUpload = multipleUpload; + this.format = checkNotNull(format); + this.componentFields = Collections.unmodifiableList(checkNotNull(componentFields)); + this.assetFields = Collections.unmodifiableList(checkNotNull(assetFields)); + this.regexMap = regexMap; + } + + public UploadDefinition(final String format, + final boolean multipleUpload, + final List componentFields, + final List assetFields) + { + this(format, multipleUpload, componentFields, assetFields, null); + } + + /** + * Whether multiple uploads are supported by the available handler. + */ + public boolean isMultipleUpload() { + return multipleUpload; + } + + /** + * The repository format + */ + public String getFormat() { + return format; + } + + /** + * The fields associated with the component for uploads of this format. + */ + public List getComponentFields() { + return componentFields; + } + + /** + * The fields associated with the asset for uploads of this format. + */ + public List getAssetFields() { + return assetFields; + } + + /** + * The mapper to use for file names. + * + * @since 3.8 + */ + @Nullable + public UploadRegexMap getRegexMap() { + return regexMap; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadFieldDefinition.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadFieldDefinition.java new file mode 100644 index 0000000000000000000000000000000000000000..df52072ab328cabe59a5f47753461c648e602a29 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadFieldDefinition.java @@ -0,0 +1,127 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.upload; + +import java.util.Objects; + +import org.sonatype.nexus.common.text.Strings2; + +/** + * Description of a field used when uploading a component. + * + * @since 3.7 + */ +public class UploadFieldDefinition +{ + public enum Type + { + BOOLEAN, STRING + } + + private String displayName; + + private String helpText; + + private String name; + + private boolean optional; + + private Type type; + + public UploadFieldDefinition(final String name, final boolean optional, final Type type) { + this(name, Strings2.capitalize(name), null, optional, type); + } + + public UploadFieldDefinition(final String name, final String helpText, final boolean optional, final Type type) { + this(name, Strings2.capitalize(name), helpText, optional, type); + } + + public UploadFieldDefinition(final String name, final String displayName, final String helpText, final boolean optional, final Type type) { + this.name = name; + this.displayName = displayName; + this.helpText = helpText; + this.optional = optional; + this.type = type; + } + + /** + * The name of the field + */ + public String getName() { + return name; + } + + /** + * The name to be displayed in UI + */ + public String getDisplayName() { + return this.displayName; + } + + /** + * The help text to be displayed in UI + */ + public String getHelpText() { + return this.helpText; + } + + /** + * The type of the field + */ + public Type getType() { + return type; + } + + /** + * Indicates whether this field is required + */ + public boolean isOptional() { + return optional; + } + + @Override + public int hashCode() { + return Objects.hash(name, displayName, helpText, type, optional); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { // Early return to speed up the equals checks for the same object + return true; + } + + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + UploadFieldDefinition other = (UploadFieldDefinition) obj; + + if (!Objects.equals(name, other.name)) { + return false; + } + + if (!Objects.equals(displayName, other.displayName)) { + return false; + } + + if (!Objects.equals(helpText, other.helpText)) { + return false; + } + + if (!Objects.equals(type, other.type)) { + return false; + } + + return optional == other.optional; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..06bf3e69d8bc6500482a3fa783fbfce4923100cd --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadHandler.java @@ -0,0 +1,87 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.upload; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.security.ContentPermissionChecker; +import org.sonatype.nexus.repository.security.VariableResolverAdapter; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.rest.ValidationErrorsException; +import org.sonatype.nexus.security.BreadActions; +import org.sonatype.nexus.selector.VariableSource; + +import static java.lang.String.format; + +/** + * @since 3.7 + */ +public interface UploadHandler +{ + + /** + * Adds a component to the repository at the described location. May fail or have unexpected behavior if the + * repository format does not match this upload instance. + * + * @param repository the {@link Repository} to add the component to + * @param upload the upload + * @return the {@link Asset Assets} created by the operation + */ + Collection handle(Repository repository, ComponentUpload upload) throws IOException; + + /** + * The {@link UploadDefinition} used by this format. + */ + UploadDefinition getDefinition(); + + /** + * The VariableResolverAdapter to use for ensurePermitted + */ + VariableResolverAdapter getVariableResolverAdapter(); + + /** + * The ContentPermissionChecker to use for ensurePermitted + */ + ContentPermissionChecker contentPermissionChecker(); + + /** + * Use the ContentPermissionChecker to verify the current user has EDIT permission for the repository, + * path and coordinates. An AuthorizationException will be thrown if the action is not permitted. + * + * @param repositoryName the name of the repository the asset is being uploaded to + * @param format the format name + * @param path the path within the repository that will represent the asset (should not be prefixed with a slash) + * @param coordinates a map containing the coordinate fields and their values + */ + default void ensurePermitted(final String repositoryName, + final String format, + final String path, + final Map coordinates) + { + VariableSource variableSource = getVariableResolverAdapter().fromCoordinates(format, path, coordinates); + if (!contentPermissionChecker().isPermitted(repositoryName, format, BreadActions.EDIT, variableSource)) { + throw new ValidationErrorsException(format("Not authorized for requested path '%s'", path)); + } + } + + /** + * @return a ComponentUpload that can validate it's own fields + * @since 3.8 + */ + default ValidatingComponentUpload getValidatingComponentUpload(final ComponentUpload componentUpload) { + return new ValidatingComponentUpload(getDefinition(), componentUpload); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadManager.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadManager.java new file mode 100644 index 0000000000000000000000000000000000000000..e9ed9a0f51da7243fb517a38cc1eaba09695d376 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadManager.java @@ -0,0 +1,48 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.upload; + +import java.io.IOException; +import java.util.Collection; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.storage.Asset; + +/** + * @since 3.7 + */ +public interface UploadManager +{ + /** + * Get the list of {@link UploadDefinition UploadDefinitions} available in this instance. + */ + Collection getAvailableDefinitions(); + + /** + * Get the {@link UploadDefinition} for the repository format. + */ + @Nullable + UploadDefinition getByFormat(String format); + + /** + * Adds a component to the repository at the appropriate location. Will fail if the repository format does not have an + * available handler. + * + * @param repository the {@link Repository} to add the component to + * @param upload the upload + * @return the {@link Asset Assets} created by the operation + */ + Collection handle(Repository repository, ComponentUpload upload) throws IOException; +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadRegexMap.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadRegexMap.java new file mode 100644 index 0000000000000000000000000000000000000000..d8ec41e97d205f64956ad3efbe97270c5e7f929f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/UploadRegexMap.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.upload; + +import java.util.Arrays; +import java.util.List; + +/** + * + * @since 3.8 + */ +public class UploadRegexMap +{ + private final String regex; + + private final List fieldList; + + public UploadRegexMap(final String regex, final String... fieldList) { + this.regex = regex; + this.fieldList = Arrays.asList(fieldList); + } + + /** + * Regex to be used to match against the file input. Groups from the regex will be mapped to the field list. + */ + public String getRegex() { + return regex; + } + + /** + * List of fields to populate from regex groups. (Use null for unused regex groups) + */ + public List getFieldList() { + return fieldList; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/ValidatingComponentUpload.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/ValidatingComponentUpload.java new file mode 100644 index 0000000000000000000000000000000000000000..74d86efcdaa3e270ed8c60e7979f49e2eb93c9e2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/ValidatingComponentUpload.java @@ -0,0 +1,143 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.upload; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.sonatype.nexus.rest.ValidationErrorsException; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.String.format; +import static org.sonatype.nexus.common.text.Strings2.isBlank; + +/** + * A holder of {@link ComponentUpload} that's meant to validate it based on provided {@link UploadDefinition} + * + * @since 3.8 + */ +public class ValidatingComponentUpload +{ + private final UploadDefinition uploadDefinition; + + protected final ComponentUpload componentUpload; + + private final ValidationErrorsException validationErrorsException; + + public ValidatingComponentUpload(final UploadDefinition uploadDefinition, final ComponentUpload componentUpload) { + this.uploadDefinition = checkNotNull(uploadDefinition); + this.componentUpload = checkNotNull(componentUpload); + this.validationErrorsException = new ValidationErrorsException(); + } + + public ComponentUpload getComponentUpload() { + validate(); + return componentUpload; + } + + private void validate() { + validateAssetPresent(); + validateAllowedComponentFields(); + validateRequiredComponentFieldPresent(); + validateAssetFields(); + validateDuplicatesAbsent(); + + if (!validationErrorsException.getValidationErrors().isEmpty()) { + throw validationErrorsException; + } + } + + private void validateAssetPresent() { + if (componentUpload.getAssetUploads().isEmpty()) { + validationErrorsException.withError("No assets found in upload"); + } + } + + private void validateAllowedComponentFields() { + collectNotAllowedFields(componentUpload.getFields(), getAllowedComponentFields()).forEach(field -> + validationErrorsException.withError(field, format("Unknown component field '%s'", field))); + } + + protected void validateRequiredComponentFieldPresent() { + uploadDefinition.getComponentFields().stream() + .filter(field -> !field.isOptional()) + .filter(field -> isBlank(componentUpload.getField(field.getName()))) + .forEach(field -> validationErrorsException.withError(field.getName(), + format("Missing required component field '%s'", field.getDisplayName()))); + } + + private void validateAssetFields() { + for (int assetIndex = 1; assetIndex < componentUpload.getAssetUploads().size() + 1; assetIndex++) { + AssetUpload asset = componentUpload.getAssetUploads().get(assetIndex - 1); + validatePayloadPresent(asset, assetIndex); + validateAllowedAssetFields(asset, assetIndex); + validateRequiredAssetFieldPresent(asset, assetIndex); + } + } + + private void validateDuplicatesAbsent() { + List uploads = componentUpload.getAssetUploads(); + Map, Integer> matches = new HashMap<>(); + + IntStream.range(1, uploads.size() + 1).forEach(i -> { + AssetUpload upload = uploads.get(i - 1); + if (matches.containsKey(upload.getFields())) { + validationErrorsException.withError( + String.format("The assets %s and %s have identical coordinates", matches.get(upload.getFields()), i)); + } + matches.put(upload.getFields(), i); + }); + } + + private void validatePayloadPresent(final AssetUpload asset, final int assetIndex) { + if (asset.getPayload() == null) { + validationErrorsException.withError("file", format("Missing file on asset '%s'", assetIndex)); + } + } + + private void validateAllowedAssetFields(final AssetUpload asset, final int assetIndex) { + collectNotAllowedFields(asset.getFields(), getAllowedAssetFields()).forEach(field -> + validationErrorsException.withError(field, format("Unknown field '%s' on asset '%s'", field, assetIndex))); + } + + private void validateRequiredAssetFieldPresent(final AssetUpload asset, final int assetIndex) { + uploadDefinition.getAssetFields().stream() + .filter(field -> !field.isOptional()) + .filter(field -> isBlank(asset.getField(field.getName()))) + .forEach(field -> validationErrorsException.withError(field.getName(), + format("Missing required asset field '%s' on '%s'", field.getDisplayName(), assetIndex))); + } + + private Collection collectNotAllowedFields(final Map fields, + final Collection allowedFields) + { + return fields.keySet().stream().filter(field -> !allowedFields.contains(field)).collect(Collectors.toList()); + } + + private Set getAllowedAssetFields() { + return uploadDefinition.getAssetFields().stream() + .map(UploadFieldDefinition::getName) + .collect(Collectors.toSet()); + } + + private Set getAllowedComponentFields() { + return uploadDefinition.getComponentFields().stream() + .map(UploadFieldDefinition::getName) + .collect(Collectors.toSet()); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/WithUploadField.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/WithUploadField.java new file mode 100644 index 0000000000000000000000000000000000000000..11929cb35153685f048d8b18fe0c5fa5a57e764f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/WithUploadField.java @@ -0,0 +1,24 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.upload; + +import java.util.Map; + +public interface WithUploadField +{ + Map getFields(); + + default String getField(final String fieldName) { + return getFields().get(fieldName); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/internal/UploadManagerImpl.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/internal/UploadManagerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..24ab94aa72956bb286da038093ad0f141307bb21 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/upload/internal/UploadManagerImpl.java @@ -0,0 +1,100 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.upload.internal; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.types.HostedType; +import org.sonatype.nexus.repository.upload.ComponentUpload; +import org.sonatype.nexus.repository.upload.UploadDefinition; +import org.sonatype.nexus.repository.upload.UploadHandler; +import org.sonatype.nexus.repository.upload.UploadManager; +import org.sonatype.nexus.rest.ValidationErrorsException; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.String.format; +import static java.util.stream.Collectors.toList; + +/** + * @since 3.7 + */ +@Named +@Singleton +public class UploadManagerImpl + implements UploadManager +{ + private List uploadDefinitions; + + private Map uploadHandlers; + + @Inject + public UploadManagerImpl(final Map uploadHandlers) + { + this.uploadHandlers = checkNotNull(uploadHandlers); + this.uploadDefinitions = Collections + .unmodifiableList(uploadHandlers.values().stream().map(handler -> handler.getDefinition()).collect(toList())); + } + + @Override + public Collection getAvailableDefinitions() { + return uploadDefinitions; + } + + @Override + public Collection handle(final Repository repository, final ComponentUpload upload) + throws IOException + { + checkNotNull(repository); + checkNotNull(upload); + + UploadHandler uploadHandler = getUploadHandler(repository); + return uploadHandler.handle(repository, uploadHandler.getValidatingComponentUpload(upload).getComponentUpload()); + } + + @Override + public UploadDefinition getByFormat(final String format) { + checkNotNull(format); + + UploadHandler handler = uploadHandlers.get(format); + return handler != null ? handler.getDefinition() : null; + } + + private UploadHandler getUploadHandler(final Repository repository) + { + if (!(repository.getType() instanceof HostedType)) { + throw new ValidationErrorsException( + format("Uploading components to a '%s' type repository is unsupported, must be '%s'", + repository.getType().getValue(), HostedType.NAME)); + } + + String repositoryFormat = repository.getFormat().toString(); + UploadHandler uploadHandler = uploadHandlers.get(repositoryFormat); + + if (uploadHandler == null) { + throw new ValidationErrorsException( + format("Uploading components to '%s' repositories is unsupported", repositoryFormat)); + } + + return uploadHandler; + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/ConfigurableViewFacet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/ConfigurableViewFacet.java new file mode 100644 index 0000000000000000000000000000000000000000..84eef434bad08b742b1ca2bb6c4ae261e8d13ac2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/ConfigurableViewFacet.java @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import javax.annotation.Nullable; +import javax.inject.Named; + +import org.sonatype.nexus.repository.FacetSupport; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * Configurable {@link ViewFacet} implementation. + * + * @since 3.0 + */ +@Named +public class ConfigurableViewFacet + extends FacetSupport + implements ViewFacet +{ + private Router router; + + public void configure(final Router router) { + checkNotNull(router); + checkState(this.router == null, "Router already configured"); + this.router = router; + } + + @Override + public Response dispatch(final Request request) throws Exception { + return dispatch(request, null); + } + + /** + * @since 3.1 + */ + @Override + public Response dispatch(final Request request, @Nullable final Context context) throws Exception { + checkState(router != null, "Router not configured"); + return router.dispatch(getRepository(), request, context); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Content.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Content.java new file mode 100644 index 0000000000000000000000000000000000000000..15e29a529c1a8d95e02569ce1547e4c62c709b7e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Content.java @@ -0,0 +1,213 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.collect.AttributesMap; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.common.hash.HashAlgorithm; +import org.sonatype.nexus.repository.cache.CacheInfo; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.StorageTx; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.hash.HashCode; +import com.google.common.reflect.TypeToken; +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.time.DateHelper.toDate; +import static org.sonatype.nexus.common.time.DateHelper.toDateTime; + +/** + * Content, that is wrapped {@link Payload} with {@link AttributesMap}. + * + * @since 3.0 + */ +public class Content + implements Payload +{ + /** + * Key of {@link Asset} nested map of content related properties. + */ + @VisibleForTesting + static final String CONTENT = "content"; + + /** + * Key of content "Last-Modified" attribute, with type {@link DateTime}. + */ + public static final String CONTENT_LAST_MODIFIED = "lastModified"; + + /** + * Key of content "ETag" attribute, with type of {@link String}. + */ + public static final String CONTENT_ETAG = "etag"; + + /** + * Key of the "hashCodes" attribute, with type of {@link #T_CONTENT_HASH_CODES_MAP}. + * + * Essentially, this is a map of content hashes required by the format from where this content originates. + */ + public static final String CONTENT_HASH_CODES_MAP = "hashCodesMap"; + + public static final TypeToken> T_CONTENT_HASH_CODES_MAP = + new TypeToken>() {}; + + private final Payload payload; + + private final AttributesMap attributes; + + public Content(final Payload payload) { + this.payload = checkNotNull(payload); + this.attributes = new AttributesMap(); + } + + /** + * @since 3.4 + */ + protected Content(final Payload payload, final AttributesMap attributes) { + this.payload = checkNotNull(payload); + this.attributes = checkNotNull(attributes); + } + + @Override + public InputStream openInputStream() throws IOException { + return payload.openInputStream(); + } + + @Override + public long getSize() { + return payload.getSize(); + } + + @Nullable + @Override + public String getContentType() { + return payload.getContentType(); + } + + @Override + public void close() throws IOException { + payload.close(); + } + + @Nonnull + public AttributesMap getAttributes() { + return attributes; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "payload=" + payload + + ", attributes='" + attributes + '\'' + + '}'; + } + + // Persistence of attributes to and from Asset + + /** + * Key of last modified {@link Date}. + */ + @VisibleForTesting + static final String P_LAST_MODIFIED = "last_modified"; + + /** + * Key of etag {@link String}. If present, was extracted from upstream response, and it has quotes removed (by RFC + * {@code ETag} header value is {@code "quoted"}, this attribute stores it without quotes). + */ + @VisibleForTesting + static final String P_ETAG = "etag"; + + /** + * Extracts non-format specific content attributes into the passed in {@link AttributesMap} (usually originating from + * {@link Content#getAttributes()}) from passed in {@link Asset} and format required hashes. + */ + public static void extractFromAsset(final Asset asset, + final Iterable hashAlgorithms, + final AttributesMap contentAttributes) + { + checkNotNull(asset); + checkNotNull(hashAlgorithms); + final NestedAttributesMap assetAttributes = asset.attributes().child(CONTENT); + final DateTime lastModified = toDateTime(assetAttributes.get(P_LAST_MODIFIED, Date.class)); + final String etag = assetAttributes.get(P_ETAG, String.class); + + final Map checksums = asset.getChecksums(hashAlgorithms); + + contentAttributes.set(Asset.class, asset); + contentAttributes.set(Content.CONTENT_LAST_MODIFIED, lastModified); + contentAttributes.set(Content.CONTENT_ETAG, etag); + contentAttributes.set(Content.CONTENT_HASH_CODES_MAP, checksums); + contentAttributes.set(CacheInfo.class, CacheInfo.extractFromAsset(asset)); + } + + /** + * Applies non-format specific content attributes onto passed in {@link Asset} from passed in {@link AttributesMap} + * (usually originating from {@link Content#getAttributes()}). + */ + public static void applyToAsset(final Asset asset, final AttributesMap contentAttributes) { + checkNotNull(asset); + checkNotNull(contentAttributes); + final NestedAttributesMap assetAttributes = asset.attributes().child(CONTENT); + assetAttributes.set(P_LAST_MODIFIED, toDate(contentAttributes.get(Content.CONTENT_LAST_MODIFIED, DateTime.class))); + assetAttributes.set(P_ETAG, contentAttributes.get(Content.CONTENT_ETAG, String.class)); + final CacheInfo cacheInfo = contentAttributes.get(CacheInfo.class); + if (cacheInfo != null) { + CacheInfo.applyToAsset(asset, cacheInfo); + } + } + + /** + * Finds fresh {@link Asset} instance from passed in TX by entity ID of the {@link Asset} used + * to create passed in {@link Content} instance. The passed in {@link Content} must have been created with + * method {@link #extractFromAsset(Asset, Iterable, AttributesMap)} to have proper attributes set for this operation. + * + * @see #extractFromAsset(Asset, Iterable, AttributesMap) + */ + @Nullable + public static Asset findAsset(final StorageTx tx, final Bucket bucket, Content content) { + final Asset contentAsset = content.getAttributes().require(Asset.class); + if (EntityHelper.hasMetadata(contentAsset)) { + return tx.findAsset(EntityHelper.id(contentAsset), bucket); + } + return null; + } + + /** + * Maintains the "last modified" attribute of the content by setting it to "now". It accepts {@code null}s, and the + * returned instance will have changes applied. Never returns {@code null}. + */ + @Nonnull + public static AttributesMap maintainLastModified(final Asset asset, + @Nullable final AttributesMap contentAttributes) + { + AttributesMap ca = contentAttributes; + if (ca == null) { + ca = new AttributesMap(); + } + if (!ca.contains(Content.CONTENT_LAST_MODIFIED)) { + ca.set(Content.CONTENT_LAST_MODIFIED, DateTime.now()); + } + return ca; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/ContentTypes.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/ContentTypes.java new file mode 100644 index 0000000000000000000000000000000000000000..045c727a5d52d8a5f3cbccff81fa46e0af7dab54 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/ContentTypes.java @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +/** + * Common content-type values. + * + * @since 3.0 + */ +public class ContentTypes +{ + + private ContentTypes() { + // empty + } + + public static final String TEXT_PLAIN = "text/plain"; + + public static final String TEXT_HTML = "text/html"; + + public static final String APPLICATION_JSON = "application/json"; + + public static final String APPLICATION_XML = "application/xml"; + + public static final String APPLICATION_GZIP = "application/gzip"; + + public static final String APPLICATION_ZIP = "application/zip"; + + public static final String APPLICATION_OCTET_STREAM = "application/octet-stream"; + + public static final String APPLICATION_TAR = "application/x-tar"; +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Context.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Context.java new file mode 100644 index 0000000000000000000000000000000000000000..b8537de29314fc763744d4a5d53a29ee14e2bada --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Context.java @@ -0,0 +1,116 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import java.util.ArrayList; +import java.util.ListIterator; + +import javax.annotation.Nonnull; + +import org.sonatype.nexus.common.collect.AttributesMap; +import org.sonatype.nexus.repository.Repository; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * View context. + * + * @since 3.0 + */ +public class Context +{ + private static final Logger log = LoggerFactory.getLogger(Context.class); + + private final AttributesMap attributes = new AttributesMap(); + + private final Repository repository; + + private final Request request; + + private ListIterator handlers; + + public Context(final Repository repository, + final Request request) + { + this.repository = checkNotNull(repository); + this.request = checkNotNull(request); + } + + public AttributesMap getAttributes() { + return attributes; + } + + public Repository getRepository() { + return repository; + } + + public Request getRequest() { + return request; + } + + /** + * Invokes the next handler in the handler chain. + * + * Interceptor-style handlers should invoke this from their {@link Handler#handle(Context)} + * method and return the result. + */ + @Nonnull + public Response proceed() throws Exception { + checkState(handlers != null, "Context not started"); + checkState(handlers.hasNext(), "End of handler chain"); + + // Invoke next handler + Handler handler = handlers.next(); + try { + log.debug("Proceeding: {}", handler); + return handler.handle(this); + } + finally { + // retain handler position in-case of re-proceed + if (handlers.hasPrevious()) { + handlers.previous(); + } + } + } + + /** + * Add an additional handler to the context, immediately after the current handler. + */ + public void insertHandler(final Handler handler) { + checkNotNull(handler); + // Insert the handler so that the next proceed() call will encounter it + handlers.add(handler); + handlers.previous(); + } + + // + // Framework internal + // + + /** + * Start route. + */ + @Nonnull + Response start(final Route route) throws Exception { + checkNotNull(route); + checkState(handlers == null, "Already started"); + log.debug("Starting: {}", route); + // Copy the handler list to allow modification + this.handlers = new ArrayList<>(route.getHandlers()).listIterator(); + return proceed(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/DefaultRoute.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/DefaultRoute.java new file mode 100644 index 0000000000000000000000000000000000000000..9ca5d7fbd0b1fc71c01a59cfbd2cd4871ace8f03 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/DefaultRoute.java @@ -0,0 +1,32 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import java.util.List; + +import org.sonatype.nexus.repository.view.matchers.AlwaysMatcher; + +/** + * View default route. + * + * @since 3.0 + */ +public class DefaultRoute + extends Route +{ + private static final Matcher MATCHER = new AlwaysMatcher(); + + public DefaultRoute(final List handlers) { + super(MATCHER, handlers); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Handler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Handler.java new file mode 100644 index 0000000000000000000000000000000000000000..5786d68f010e0dfe4c336bc93019c7af8045018d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Handler.java @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import javax.annotation.Nonnull; + +/** + * View handler. + * + * @since 3.0 + */ +public interface Handler +{ + @Nonnull + Response handle(@Nonnull Context context) throws Exception; +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Headers.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Headers.java new file mode 100644 index 0000000000000000000000000000000000000000..db52fb1efe43b7c1859887741c9308faeac9463a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Headers.java @@ -0,0 +1,34 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import org.sonatype.nexus.common.collect.StringMultimap; + +import com.google.common.collect.ListMultimap; + +/** + * Headers container. + * + * @since 3.0 + */ +public class Headers + extends StringMultimap +{ + public Headers(final ListMultimap entries) { + super(entries); + } + + public Headers() { + super(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Matcher.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Matcher.java new file mode 100644 index 0000000000000000000000000000000000000000..871f87081ffa4caf5a7deea6fae0fd04e5e0bee0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Matcher.java @@ -0,0 +1,23 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +/** + * View matcher. + * + * @since 3.0 + */ +public interface Matcher +{ + boolean matches(Context context); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Parameters.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Parameters.java new file mode 100644 index 0000000000000000000000000000000000000000..89d941541de0eaf36bcb869cf458da277d400c9a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Parameters.java @@ -0,0 +1,34 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import org.sonatype.nexus.common.collect.StringMultimap; + +import com.google.common.collect.ListMultimap; + +/** + * Parameters container. + * + * @since 3.0 + */ +public class Parameters + extends StringMultimap +{ + public Parameters(final ListMultimap entries) { + super(entries); + } + + public Parameters() { + super(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/PartPayload.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/PartPayload.java new file mode 100644 index 0000000000000000000000000000000000000000..892feab6de62eaca0949d7f45440538c0303fd14 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/PartPayload.java @@ -0,0 +1,40 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import javax.annotation.Nullable; + +/** + * Multipart payload. + * + * @since 3.1 + */ +public interface PartPayload + extends Payload +{ + /** + * Returns the original name of this file as provided by the client. + */ + @Nullable + String getName(); + + /** + * Returns the name of the form field. + */ + String getFieldName(); + + /** + * Returns true for form fields and false for files. + */ + boolean isFormField(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Payload.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Payload.java new file mode 100644 index 0000000000000000000000000000000000000000..ce1d4be83ea16b6f877a0a446793dd8814c38af6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Payload.java @@ -0,0 +1,48 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; + +import javax.annotation.Nullable; + +/** + * Payload. + * + * @since 3.0 + */ +public interface Payload + extends Closeable +{ + long UNKNOWN_SIZE = -1; + + InputStream openInputStream() throws IOException; + + long getSize(); + + @Nullable + String getContentType(); + + /** + * Closes this payload, relinquishing any underlying resources. Streams previously handed out by + * {@link #openInputStream()} may not be affected by this method and should be closed separately. + * + * @since 3.1 + */ + @Override + default void close() throws IOException { + // no underlying resources to clean-up by default + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Request.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Request.java new file mode 100644 index 0000000000000000000000000000000000000000..dfe1e11d79716527111f398cadc8fc5a5a440734 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Request.java @@ -0,0 +1,239 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.collect.AttributesMap; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * View request. + * + * @see Request.Builder + * @since 3.0 + */ +public class Request +{ + private final AttributesMap attributes; + + private final Headers headers; + + private final String action; + + private final String path; + + private final Parameters parameters; + + private final Payload payload; + + private final boolean multipart; + + private final Iterable multiPayloads; + + private Request(final AttributesMap attributes, + final Headers headers, + final String action, + final String path, + final Parameters parameters, + @Nullable final Payload payload, + final boolean multipart, + @Nullable final Iterable multiPayloads) + { + this.attributes = checkNotNull(attributes); + this.headers = checkNotNull(headers); + this.action = checkNotNull(action); + this.path = checkNotNull(path); + this.parameters = checkNotNull(parameters); + this.payload = payload; + this.multipart = multipart; + this.multiPayloads = multiPayloads; + } + + public AttributesMap getAttributes() { + return attributes; + } + + public Headers getHeaders() { + return headers; + } + + public String getAction() { + return action; + } + + public String getPath() { + return path; + } + + public Parameters getParameters() { + return parameters; + } + + @Nullable + public Payload getPayload() { + return payload; + } + + public boolean isMultipart() { + return multipart; + } + + @Nullable + public Iterable getMultiparts() { + return multiPayloads; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "action='" + action + '\'' + + ", path='" + path + '\'' + + ", parameters=" + parameters + + ", payload=" + payload + + ", multipart=" + multipart + + '}'; + } + + // + // Builder + // + + /** + * {@link Request} builder. + */ + public static class Builder + { + private AttributesMap attributes; + + private Headers headers; + + private String action; + + private String path; + + private Parameters parameters; + + private Payload payload; + + private boolean multipart; + + private Iterable multiparts; + + public Builder attributes(final AttributesMap attributes) { + this.attributes = attributes; + return this; + } + + @Nonnull + public AttributesMap attributes() { + if (attributes == null) { + attributes = new AttributesMap(); + } + return attributes; + } + + public Builder attribute(final String name, final Object value) { + attributes().set(name, value); + return this; + } + + public Builder headers(final Headers headers) { + this.headers = headers; + return this; + } + + @Nonnull + public Headers headers() { + if (headers == null) { + headers = new Headers(); + } + return headers; + } + + public Builder header(final String name, final String... values) { + headers().set(name, values); + return this; + } + + public Builder action(final String action) { + this.action = action; + return this; + } + + public Builder path(final String path) { + this.path = path; + return this; + } + + public Builder parameters(final Parameters parameters) { + this.parameters = parameters; + return this; + } + + @Nonnull + public Parameters parameters() { + if (parameters == null) { + parameters = new Parameters(); + } + return parameters; + } + + public Builder parameter(final String name, final String... values) { + parameters().set(name, values); + return this; + } + + public Builder payload(final Payload payload) { + this.payload = payload; + return this; + } + + public Builder multipart(final boolean multipart) { + this.multipart = multipart; + return this; + } + + public Builder multiparts(final Iterable multiparts) { + this.multiparts = multiparts; + this.multipart = multiparts != null; + return this; + } + + public Builder copy(final Request request) { + checkNotNull(request); + attributes = request.getAttributes(); + headers = request.getHeaders(); + action = request.getAction(); + path = request.getPath(); + parameters = request.getParameters(); + payload = request.getPayload(); + multipart = request.isMultipart(); + multiparts = request.getMultiparts(); + return this; + } + + /** + * Requires {@link #action} and {@link #path}. + */ + public Request build() { + checkState(action != null, "Missing: action"); + checkState(path != null, "Missing: path"); + + return new Request(attributes(), headers(), action, path, parameters(), payload, multipart, multiparts); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Response.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Response.java new file mode 100644 index 0000000000000000000000000000000000000000..dcb0ec4515430a11c199e8176f17fcc46b83ef57 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Response.java @@ -0,0 +1,156 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.sonatype.nexus.common.collect.AttributesMap; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * View response. + * + * @see Response.Builder + * @since 3.0 + */ +public class Response +{ + private final AttributesMap attributes; + + private final Headers headers; + + private final Status status; + + private final Payload payload; + + private Response(final AttributesMap attributes, + final Headers headers, + final Status status, + @Nullable final Payload payload) + { + this.attributes = attributes; + this.headers = headers; + this.status = status; + this.payload = payload; + } + + public AttributesMap getAttributes() { + return attributes; + } + + public Headers getHeaders() { + return headers; + } + + public Status getStatus() { + return status; + } + + @Nullable + public Payload getPayload() { + return payload; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "status=" + status + + ", payload=" + payload + + '}'; + } + + // + // Builder + // + + /** + * {@link Response} builder. + */ + public static class Builder + { + private AttributesMap attributes; + + private Headers headers; + + private Status status; + + private Payload payload; + + public Builder attributes(final AttributesMap attributes) { + this.attributes = attributes; + return this; + } + + @Nonnull + public AttributesMap attributes() { + if (attributes == null) { + attributes = new AttributesMap(); + } + return attributes; + } + + public Builder attribute(final String name, final Object value) { + attributes().set(name, value); + return this; + } + + public Builder headers(final Headers headers) { + this.headers = headers; + return this; + } + + @Nonnull + public Headers headers() { + if (headers == null) { + headers = new Headers(); + } + return headers; + } + + public Builder header(final String name, final String... values) { + headers().set(name, values); + return this; + } + + public Builder status(final Status status) { + this.status = status; + return this; + } + + public Builder payload(final Payload payload) { + this.payload = payload; + return this; + } + + public Builder copy(final Response response) { + checkNotNull(response); + attributes = response.getAttributes(); + headers = response.getHeaders(); + status = response.getStatus(); + payload = response.getPayload(); + return this; + } + + /** + * Requires {@link #status}. + */ + public Response build() { + checkState(status != null, "Missing: status"); + + return new Response(attributes(), headers(), status, payload); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Route.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Route.java new file mode 100644 index 0000000000000000000000000000000000000000..59256ea459ed9f11bfa7d586b99a381c336382b0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Route.java @@ -0,0 +1,90 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import java.util.List; + +import javax.annotation.Nonnull; + +import com.google.common.collect.Lists; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * View route. + * + * @since 3.0 + */ +public class Route +{ + private final Matcher matcher; + + private final List handlers; + + public Route(final Matcher matcher, final List handlers) { + this.matcher = checkNotNull(matcher, "Missing matcher"); + checkNotNull(handlers, "Missing handlers"); + checkArgument(!handlers.isEmpty(), "At least one handler is required"); + this.handlers = handlers; + } + + @Nonnull + public Matcher getMatcher() { + return matcher; + } + + @Nonnull + public List getHandlers() { + return handlers; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "matcher=" + matcher + + ", handlers=" + handlers + + '}'; + } + + // + // Builder + // + + /** + * View {@link Route} builder. + */ + public static class Builder + { + private Matcher matcher; + + private List handlers = Lists.newArrayList(); + + public Builder matcher(final Matcher matcher) { + checkState(this.matcher == null, "Only one matcher allowed"); + this.matcher = checkNotNull(matcher); + return this; + } + + public Builder handler(final Handler handler) { + checkNotNull(handler); + handlers.add(handler); + return this; + } + + public Route create() { + return new Route(matcher, handlers); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Router.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Router.java new file mode 100644 index 0000000000000000000000000000000000000000..85ffc0429cdb88599fc873247728aabae9dd1738 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Router.java @@ -0,0 +1,186 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.Repository; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * View router. + * + * @since 3.0 + */ +public class Router + extends ComponentSupport +{ + private final List routes; + + private final DefaultRoute defaultRoute; + + public static final String LOCAL_ATTRIBUTE_PREFIX = "local.attribute."; + + public Router(final List routes, final DefaultRoute defaultRoute) { + this.routes = checkNotNull(routes, "Missing routes"); + this.defaultRoute = checkNotNull(defaultRoute, "Missing default route"); + } + + /** + * Dispatch request to matching route, if supplied will pull attributes from the existingContext. + */ + public Response dispatch(final Repository repository, final Request request, @Nullable final Context existingContext) + throws Exception + { + checkNotNull(repository); + checkNotNull(request); + + logRequest(request); + + // Find route and start context + Context context = maybeCopyContextAttributes(repository, request, existingContext); + Route route = findRoute(context); + Response response = context.start(route); + logResponse(response); + return response; + } + + @VisibleForTesting + Context maybeCopyContextAttributes(final Repository repository, + final Request request, + final Context existingContext) + { + Context context = new Context(repository, request); + + if (existingContext != null) { + existingContext.getAttributes().keys().stream() + .filter(key -> !key.startsWith(LOCAL_ATTRIBUTE_PREFIX)) + .forEach(key -> context.getAttributes().set(key, existingContext.getAttributes().get(key))); + } + + return context; + } + + /** + * Log request details. + */ + private void logRequest(final Request request) { + log.debug("Request: {}", request); + + if (log.isTraceEnabled()) { + if (request.getHeaders().isEmpty()) { + log.trace("No request headers"); + } + else { + log.trace("Request headers:"); + for (Map.Entry header : request.getHeaders()) { + log.trace(" {}: {}", header.getKey(), header.getValue()); + } + } + + if (request.getAttributes().isEmpty()) { + log.trace("No request attributes"); + } + else { + log.trace("Request attributes:"); + for (Map.Entry entry : request.getAttributes()) { + log.trace(" {}={}", entry.getKey(), entry.getValue()); + } + } + } + } + + /** + * Log response details. + */ + private void logResponse(final Response response) { + log.debug("Response: {}", response); + + if (log.isTraceEnabled()) { + if (response.getHeaders().isEmpty()) { + log.trace("No response headers"); + } + else { + log.trace("Response headers:"); + for (Map.Entry header : response.getHeaders()) { + log.trace(" {}: {}", header.getKey(), header.getValue()); + } + } + + if (response.getAttributes().isEmpty()) { + log.trace("No response attributes"); + } + else { + log.trace("Response attributes:"); + for (Map.Entry entry : response.getAttributes()) { + log.trace(" {}={}", entry.getKey(), entry.getValue()); + } + } + } + } + + /** + * Find the first matching route for the given context. + */ + private Route findRoute(final Context context) { + for (Route route : routes) { + if (route.getMatcher().matches(context)) { + return route; + } + } + return defaultRoute; + } + + // + // Builder + // + + /** + * View {@link Router} builder. + */ + public static class Builder + { + // TODO: Consider if we want to add route-ids so we can reference defined routes for re-use (maybe builder state only)? + // TODO: Consider a set of default handlers, as here we have timingHandler on each route? + + private List routes = Lists.newArrayList(); + + private DefaultRoute defaultRoute; + + public Builder route(final Route route) { + checkNotNull(route); + routes.add(route); + return this; + } + + public Builder defaultHandlers(final Handler... handlers) { + checkState(this.defaultRoute == null, "Default handlers already configured"); + this.defaultRoute = new DefaultRoute(Arrays.asList(handlers)); + return this; + } + + public Router create() { + return new Router(routes, defaultRoute); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Status.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Status.java new file mode 100644 index 0000000000000000000000000000000000000000..d393395cf36da5480687abe4c1abdf6c209a9d68 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/Status.java @@ -0,0 +1,88 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import java.io.Serializable; + +import javax.annotation.Nullable; + +// TODO: implement Externalizable? This is made Serialzable just for cache usage, may need to revisit to avoid needing +// TODO: ... to have the cache semantics leak into this core api. Negative-cache may want to wrap or we may want to +// TODO: ... consider an interface instead, though that has non-trivial impact to code-base atm? + +/** + * A representation of response status with an optional message. + * + * @since 3.0 + */ +public class Status + implements Serializable +{ + private final boolean successful; + + private final int code; + + private final String message; + + public Status(final boolean successful, final int code, @Nullable final String message) { + this.successful = successful; + this.code = code; + this.message = message; + } + + public Status(final boolean successful, final int code) { + this(successful, code, null); + } + + public boolean isSuccessful() { + return successful; + } + + public int getCode() { + return code; + } + + @Nullable + public String getMessage() { + return message; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "successful=" + successful + + ", code=" + code + + ", message='" + message + '\'' + + '}'; + } + + // + // Helpers + // + + public static Status success(final int code, final String message) { + return new Status(true, code, message); + } + + public static Status success(final int code) { + return new Status(true, code); + } + + public static Status failure(final int code, final String message) { + return new Status(false, code, message); + } + + public static Status failure(final int code) { + return new Status(false, code); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/ViewFacet.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/ViewFacet.java new file mode 100644 index 0000000000000000000000000000000000000000..ba472eeebdce12d0a331d2034818115fbfa500ef --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/ViewFacet.java @@ -0,0 +1,36 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import org.sonatype.nexus.repository.Facet; + +/** + * View facet. + * + * @since 3.0 + */ +@Facet.Exposed +public interface ViewFacet + extends Facet +{ + /** + * Dispatch request to router. + */ + Response dispatch(Request request) throws Exception; + + /** + * Dispatch request to router with an existing context to pull attributes from. + * @since 3.1 + */ + Response dispatch(Request request, Context context) throws Exception; +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/ViewUtils.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/ViewUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..58d68432d802f6c0654fb160550d053e22766e2b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/ViewUtils.java @@ -0,0 +1,55 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map.Entry; + +import org.apache.http.client.utils.URIBuilder; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Utility class containing view layer-related functionality not specific to individual classes. + * + * @since 3.7 + */ +public final class ViewUtils +{ + private ViewUtils() { + // empty + } + + /** + * Builds a url with encoded url parameters, returning the combined url as a string. + * + * @throws IllegalArgumentException if the url or its parameters are syntactically invalid. + */ + public static String buildUrlWithParameters(final String url, final Parameters parameters) { + checkNotNull(url); + checkNotNull(parameters); + try { + URIBuilder builder = new URIBuilder(url); + for (Entry parameter : parameters.entries()) { + builder.addParameter(parameter.getKey(), parameter.getValue()); + } + URI uri = builder.build(); + return uri.toString(); + } + catch (URISyntaxException e) { + throw new IllegalArgumentException("Unable to build url with base url " + url + " and parameters " + parameters, + e); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/BrowseUnsupportedHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/BrowseUnsupportedHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..1a44f17fb925b79be530398b8a6e973d3ce6463e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/BrowseUnsupportedHandler.java @@ -0,0 +1,120 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.handlers; + +import java.net.URL; + +import javax.annotation.Nonnull; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.template.TemplateHelper; +import org.sonatype.nexus.common.template.TemplateParameters; +import org.sonatype.nexus.common.text.Strings2; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; +import org.sonatype.nexus.repository.http.HttpMethods; +import org.sonatype.nexus.repository.http.HttpResponses; +import org.sonatype.nexus.repository.security.SecurityHandler; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Matcher; +import org.sonatype.nexus.repository.view.Response; +import org.sonatype.nexus.repository.view.Route; +import org.sonatype.nexus.repository.view.payloads.StringPayload; + +import com.google.common.collect.ImmutableList; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Renders browse not supported page for requests to "/" or "/index.html" for + * repositories that do not generate this information. + * + * @since 3.0 + */ +@Named +@Singleton +public class BrowseUnsupportedHandler + extends ComponentSupport + implements Handler +{ + private static final String TEMPLATE_RESOURCE = "browseUnsupportedHtml.vm"; + + private final TemplateHelper templateHelper; + + private final URL template; + + private final Route route; + + private final boolean treeEnabled; + + @Inject + public BrowseUnsupportedHandler(final TemplateHelper templateHelper, final BrowseNodeConfiguration configuration, + final SecurityHandler securityHandler) { + this.templateHelper = checkNotNull(templateHelper); + checkNotNull(securityHandler); + this.template = getClass().getResource(TEMPLATE_RESOURCE); + checkNotNull(template); + this.route = new Route(MATCHER, ImmutableList.of(securityHandler, this)); + this.treeEnabled = checkNotNull(configuration).isEnabled(); + } + + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { + TemplateParameters params = templateHelper.parameters(); + params.set("repository", context.getRepository()); + params.set("treeEnabled", treeEnabled); + + String html = templateHelper.render(template, params); + return HttpResponses.ok(new StringPayload(html, "text/html")); + } + + @Nonnull + public Route getRoute() { + return this.route; + } + + // + // Matcher + // + + /** + * Matches GET request with path ending with one of (ignoring case): + * + * "/" + * "/index.html" + * "/index.htm" + */ + private static class MatcherImpl + extends ComponentSupport + implements Matcher + { + @Override + public boolean matches(final Context context) { + checkNotNull(context); + String action = context.getRequest().getAction(); + String path = context.getRequest().getPath(); + log.debug("Matching: {} {}", action, path); + if (HttpMethods.GET.equals(action)) { + path = Strings2.lower(path); + return path.endsWith("/") || path.endsWith("/index.html") || path.endsWith("/index.htm"); + } + return false; + } + } + + private static final Matcher MATCHER = new MatcherImpl(); +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ConditionalRequestHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ConditionalRequestHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..7acd4d723259fbd8b217767e796bb112cfc46a1e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ConditionalRequestHandler.java @@ -0,0 +1,123 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.handlers; + +import javax.annotation.Nonnull; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.repository.view.Response; +import org.sonatype.nexus.repository.view.Status; +import org.sonatype.nexus.repository.view.ViewFacet; + +import com.google.common.base.Predicate; +import com.google.common.net.HttpHeaders; + +import static org.sonatype.nexus.repository.http.HttpConditions.makeConditional; +import static org.sonatype.nexus.repository.http.HttpConditions.makeUnconditional; +import static org.sonatype.nexus.repository.http.HttpConditions.requestPredicate; +import static org.sonatype.nexus.repository.http.HttpMethods.DELETE; +import static org.sonatype.nexus.repository.http.HttpMethods.GET; +import static org.sonatype.nexus.repository.http.HttpMethods.HEAD; +import static org.sonatype.nexus.repository.http.HttpMethods.POST; +import static org.sonatype.nexus.repository.http.HttpMethods.PUT; +import static org.sonatype.nexus.repository.http.HttpStatus.NOT_MODIFIED; +import static org.sonatype.nexus.repository.http.HttpStatus.PRECONDITION_FAILED; + +/** + * A format-neutral handler for conditional requests. It relies on existence of following HTTP entity headers: + *
      + *
    • Last-Modified
    • + *
    • ETag
    • + *
    + * There is a handy {@link ContentHeadersHandler} handler that adds these for you, if you are serving up {@link + * Content} payloads properly decorated. If not, you should have them somehow (format specific way) set on {@link + * Response} instances coming from your {@link Handler}s. + * + * @see ContentHeadersHandler + * @see Content + * @since 3.0 + */ +public class ConditionalRequestHandler + extends ComponentSupport + implements Handler +{ + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { + final Predicate requestPredicate = requestPredicate(context.getRequest()); + if (requestPredicate != null) { + makeUnconditional(context.getRequest()); + try { + return handleConditional(context, requestPredicate); + } + finally { + makeConditional(context.getRequest()); + } + } + + return context.proceed(); + } + + @Nonnull + private Response handleConditional(@Nonnull final Context context, + @Nonnull final Predicate requestPredicate) throws Exception + { + final String action = context.getRequest().getAction(); + log.debug("Conditional request: {} {}: {}", + action, + context.getRequest().getPath(), + requestPredicate); + switch (action) { + case GET: + case HEAD: { + final Response response = context.proceed(); + if (response.getStatus().isSuccessful() && !requestPredicate.apply(response)) { + // copy only ETag header, leave out all other entity headers + final Response.Builder responseBuilder = new Response.Builder().status(Status.success(NOT_MODIFIED)); + if (response.getHeaders().contains(HttpHeaders.ETAG)) { + responseBuilder.header(HttpHeaders.ETAG, response.getHeaders().get(HttpHeaders.ETAG)); + } + return responseBuilder.build(); + } + else { + return response; + } + } + + case POST: + case PUT: + case DELETE: { + final Request getRequest = new Request.Builder().copy(context.getRequest()).action(GET).build(); + final Response response = context.getRepository().facet(ViewFacet.class).dispatch(getRequest); + if (response.getStatus().isSuccessful() && !requestPredicate.apply(response)) { + // keep all response headers like Last-Modified and ETag, etc + return new Response.Builder() + .copy(response) + .status(Status.failure(PRECONDITION_FAILED)) + .payload(null) + .build(); + } + else { + return context.proceed(); + } + } + + default: + return context.proceed(); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..a3cbab20258bc8f27a0b4de88699895132a060d9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandler.java @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.handlers; + +import javax.annotation.Nonnull; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Payload; +import org.sonatype.nexus.repository.view.Response; + +import com.google.common.net.HttpHeaders; +import org.apache.http.client.utils.DateUtils; +import org.joda.time.DateTime; + +/** + * A format-neutral content handler for decorating response using {@link Content#getAttributes()} provided attributes. + * + * @since 3.0 + */ +@Singleton +@Named +public class ContentHeadersHandler + extends ComponentSupport + implements Handler +{ + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { + final Response response = context.proceed(); + Payload payload = response.getPayload(); + + if (response.getStatus().isSuccessful() && payload instanceof Content) { + final Content content = (Content) payload; + final DateTime lastModified = content.getAttributes().get(Content.CONTENT_LAST_MODIFIED, DateTime.class); + if (lastModified != null) { + response.getHeaders().set(HttpHeaders.LAST_MODIFIED, DateUtils.formatDate(lastModified.toDate())); + } + final String etag = content.getAttributes().get(Content.CONTENT_ETAG, String.class); + if (etag != null) { + response.getHeaders().set(HttpHeaders.ETAG, "\"" + etag + "\""); + } + } + return response; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ContributedHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ContributedHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..e9ae4a80090c59addab95faea5b36730902ae13f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ContributedHandler.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.handlers; + +import javax.inject.Named; + +import org.sonatype.nexus.repository.view.Handler; + +/** + * A {@link Handler} provided by an extension to Nexus. {@link Named @Named} implementations will be automatically + * inserted into repository routes. + * + * @since 3.1 + */ +public interface ContributedHandler + extends Handler +{ +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ExceptionHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ExceptionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..3cb99bb1af7b5fa432afe20f68f80e914a17e682 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ExceptionHandler.java @@ -0,0 +1,86 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.handlers; + +import javax.annotation.Nonnull; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.IllegalOperationException; +import org.sonatype.nexus.repository.InvalidContentException; +import org.sonatype.nexus.repository.http.HttpResponses; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Response; + +import com.orientechnologies.common.concur.lock.OModificationOperationProhibitedException; +import com.orientechnologies.orient.server.distributed.ODistributedException; + +import static org.sonatype.nexus.repository.http.HttpMethods.PUT; + +/** + * A format-neutral error handler for some exceptions. These exceptions are meant to signal some response directly + * mappable onto a HTTP response, usually some 4xx error code. + * + * @since 3.0 + */ +public class ExceptionHandler + extends ComponentSupport + implements Handler +{ + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { //NOSONAR + try { + return context.proceed(); + } + catch (IllegalOperationException e) { + log.warn("Illegal operation: {} {}: {}", + context.getRequest().getAction(), + context.getRequest().getPath(), + e.toString()); + return HttpResponses.badRequest(e.getMessage()); + } + catch (InvalidContentException e) { + log.warn("Invalid content: {} {}: {}", + context.getRequest().getAction(), + context.getRequest().getPath(), + e.toString()); + if (PUT.equals(context.getRequest().getAction())) { + return HttpResponses.badRequest(e.getMessage()); + } + else { + return HttpResponses.notFound(e.getMessage()); + } + } + catch (OModificationOperationProhibitedException e) { //NOSONAR + log.warn("Nexus Repository Manager is in read-only mode: {} {}: {}", + context.getRequest().getAction(), + context.getRequest().getPath(), + e.toString()); + return HttpResponses.serviceUnavailable("Nexus Repository Manager is in read-only mode"); + } + catch (ODistributedException e) { + // OWriteOperationNotPermittedException is not exported by orientdb + if ("OWriteOperationNotPermittedException".equals(e.getClass().getSimpleName())) { + log.warn("Nexus Repository Manager is in read-only mode: {} {}: {}", + context.getRequest().getAction(), + context.getRequest().getPath(), + e.toString()); + return HttpResponses.serviceUnavailable("Nexus Repository Manager is in read-only mode"); + } + else { + throw e; + } + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/HandlerContributor.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/HandlerContributor.java new file mode 100644 index 0000000000000000000000000000000000000000..8fa22d32c9a9b1a28694916ecd9bfe89e0f69ff6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/HandlerContributor.java @@ -0,0 +1,77 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.handlers; + +import java.util.List; +import java.util.ListIterator; + +import javax.annotation.Nonnull; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Response; + +import com.google.common.annotations.VisibleForTesting; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.repository.view.Router.LOCAL_ATTRIBUTE_PREFIX; + +/** + * A {@link Handler} which diverts request processing through any {@link ContributedHandler}s that are active. + * + * @since 3.1 + */ +@Named +@Singleton +public class HandlerContributor + implements Handler +{ + private final List contributedHandlers; + + @VisibleForTesting + static final String EXTENDED_MARKER = + LOCAL_ATTRIBUTE_PREFIX + HandlerContributor.class.getName() + ".extended"; + + @Inject + public HandlerContributor(final List contributedHandlers) + { + this.contributedHandlers = checkNotNull(contributedHandlers); + } + + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { + // Ensure the extra handlers are inserted only once, in the case that a handler higher + // on the stack calls proceed() twice for some reason + if (!isMarkedExtended(context)) { + ListIterator handlerIterator = contributedHandlers.listIterator(contributedHandlers.size()); + while (handlerIterator.hasPrevious()) { + context.insertHandler(handlerIterator.previous()); + } + markExtended(context); + } + + return context.proceed(); + } + + private boolean isMarkedExtended(final Context context) { + return context.getAttributes().contains(EXTENDED_MARKER); + } + + private void markExtended(final Context context) { + context.getAttributes().set(EXTENDED_MARKER, Boolean.TRUE); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/IndexHtmlForwardHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/IndexHtmlForwardHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..227b2f88b197bc7a44c31db69501c5e66705d8ac --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/IndexHtmlForwardHandler.java @@ -0,0 +1,79 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.handlers; + +import javax.annotation.Nonnull; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.http.HttpResponses; +import org.sonatype.nexus.repository.http.HttpStatus; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.repository.view.Response; +import org.sonatype.nexus.repository.view.ViewFacet; + +/** + * Handler which will forward current request to {@code {request.path}/index.html} or {@code {request.path}/index.htm}. + * + * @since 3.0 + */ +@Named +@Singleton +public class IndexHtmlForwardHandler + extends ComponentSupport + implements Handler +{ + private static final String[] INDEX_FILES = { + "index.html", + "index.htm" + }; + + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { + String path = context.getRequest().getPath(); + + // sanity, to ensure we don't corrupt the path + if (!path.endsWith("/")) { + path = path + "/"; + } + + for (String file : INDEX_FILES) { + Response response = forward(context, path + file); + // return response if it was successful or an error which was not not found + if (response.getStatus().isSuccessful() || response.getStatus().getCode() != HttpStatus.NOT_FOUND) { + return response; + } + // otherwise try next index file + } + + // or there is no such file, give up not found + return HttpResponses.notFound(context.getRequest().getPath()); + } + + private Response forward(final Context context, final String path) throws Exception { + log.trace("Forwarding request to path: {}", path); + + Request request = new Request.Builder() + .copy(context.getRequest()) + .path(path) + .build(); + + return context.getRepository() + .facet(ViewFacet.class) + .dispatch(request); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/TimingHandler.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/TimingHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..40653c3db32687e016b7be365a72c9eafba6a1a7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/TimingHandler.java @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.handlers; + +import javax.annotation.Nonnull; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.collect.AttributeKey; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Handler; +import org.sonatype.nexus.repository.view.Response; + +import com.google.common.base.Stopwatch; + +/** + * Simple timing handler. + * + * @since 3.0 + */ +@Named +@Singleton +public class TimingHandler + extends ComponentSupport + implements Handler +{ + public static final String ELAPSED_KEY = AttributeKey.get(TimingHandler.class, "elapsed"); + + @Nonnull + @Override + public Response handle(@Nonnull final Context context) throws Exception { + Stopwatch watch = Stopwatch.createStarted(); + + try { + return context.proceed(); + } + finally { + String elapsed = watch.toString(); + context.getAttributes().set(ELAPSED_KEY, elapsed); + log.trace("Timing: {}", elapsed); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..f964089a0bc3174100437927c122ecc4b601b87a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Common view handlers. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.view.handlers; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/ActionMatcher.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/ActionMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..aaff2ed51c8b11a524cbadbf5324cd292fa5007f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/ActionMatcher.java @@ -0,0 +1,48 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers; + +import java.util.List; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Matcher; +import org.sonatype.nexus.repository.view.Request; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Arrays.asList; + +/** + * Matches if the {@link Request#getAction() request action} is in the allowed list. + * + * @since 3.0 + */ +public class ActionMatcher + extends ComponentSupport + implements Matcher +{ + private final List allowedActions; + + public ActionMatcher(final String... allowedActions) { + checkNotNull(allowedActions); + checkArgument(allowedActions.length > 0, "at least one allowed action must be specified"); + this.allowedActions = asList(allowedActions); + } + + @Override + public boolean matches(final Context context) { + final String action = context.getRequest().getAction(); + return allowedActions.contains(action); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/AlwaysMatcher.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/AlwaysMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..eea6c201761467fc6550ecfad438d3e77dc426c2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/AlwaysMatcher.java @@ -0,0 +1,30 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers; + +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Matcher; + +/** + * Always matcher. + * + * @since 3.0 + */ +public class AlwaysMatcher + implements Matcher +{ + @Override + public boolean matches(final Context context) { + return true; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/LiteralMatcher.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/LiteralMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..91a49e00770d40c1a95f776588004e374308ad51 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/LiteralMatcher.java @@ -0,0 +1,63 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Matcher; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Literal string matcher. + * + * @since 3.0 + */ +public class LiteralMatcher + extends ComponentSupport + implements Matcher +{ + private final String literal; + + private final boolean ignoreCase; + + public LiteralMatcher(final String literal, final boolean ignoreCase) { + this.literal = checkNotNull(literal); + this.ignoreCase = ignoreCase; + } + + public LiteralMatcher(final String literal) { + this(literal, true); + } + + @Override + public boolean matches(final Context context) { + checkNotNull(context); + String path = context.getRequest().getPath(); + log.debug("Matching: {}={} ignore-case: {}", path, literal, ignoreCase); + if (ignoreCase) { + return path.equalsIgnoreCase(literal); + } + else { + return path.equals(literal); + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "literal='" + literal + '\'' + + ", ignoreCase=" + ignoreCase + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/NeverMatcher.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/NeverMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..2bfd575e85d64a29825ed9cafa623135b69081c9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/NeverMatcher.java @@ -0,0 +1,30 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers; + +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Matcher; + +/** + * Never matcher. + * + * @since 3.0 + */ +public class NeverMatcher + implements Matcher +{ + @Override + public boolean matches(final Context context) { + return false; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/PrefixMatcher.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/PrefixMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..b356caf88d18f9ab78d1aae0b612f8493358e112 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/PrefixMatcher.java @@ -0,0 +1,71 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.text.Strings2; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Matcher; + +import com.google.common.annotations.VisibleForTesting; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Prefix string matcher. + * + * @since 3.0 + */ +public class PrefixMatcher + extends ComponentSupport + implements Matcher +{ + private final String prefix; + + private final boolean ignoreCase; + + @VisibleForTesting + public PrefixMatcher(final String prefix, final boolean ignoreCase) { + this.prefix = checkNotNull(prefix); + this.ignoreCase = ignoreCase; + } + + public PrefixMatcher(final String prefix) { + this(prefix, false); + } + + public PrefixMatcher ignoreCase(final boolean ignoreCase) { + return new PrefixMatcher(prefix, ignoreCase); + } + + @Override + public boolean matches(final Context context) { + checkNotNull(context); + String path = context.getRequest().getPath(); + log.debug("Matching: {} starts-with={} ignore-case: {}", path, prefix, ignoreCase); + if (ignoreCase) { + return Strings2.lower(path).startsWith(Strings2.lower(prefix)); + } + else { + return path.startsWith(prefix); + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "prefix='" + prefix + '\'' + + ", ignoreCase=" + ignoreCase + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/RegexMatcher.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/RegexMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..4be41861abdc7661500f6d4b00c3de6636ec033e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/RegexMatcher.java @@ -0,0 +1,84 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers; + +import java.util.regex.MatchResult; +import java.util.regex.Pattern; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Matcher; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Regular expression string matcher. + * + * @since 3.0 + */ +public class RegexMatcher + extends ComponentSupport + implements Matcher +{ + public interface State + { + MatchResult getMatchResult(); + } + + private final Pattern pattern; + + public RegexMatcher(final Pattern pattern) { + this.pattern = checkNotNull(pattern); + } + + public RegexMatcher(final String regex, final int flags) { + checkNotNull(regex); + this.pattern = Pattern.compile(regex, flags); + } + + public RegexMatcher(final String regex) { + checkNotNull(regex); + this.pattern = Pattern.compile(regex); + } + + @Override + public boolean matches(final Context context) { + checkNotNull(context); + + String path = context.getRequest().getPath(); + log.debug("Matching: {}~={}", path, pattern); + final java.util.regex.Matcher m = pattern.matcher(path); + + if (m.matches()) { + // expose match result in context + context.getAttributes().set(State.class, new State() + { + @Override + public MatchResult getMatchResult() { + return m; + } + }); + return true; + } + + // no match + return false; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "pattern=" + pattern + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/SuffixMatcher.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/SuffixMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..3d3edc1da8bdb4984e5badb0100da36b467954a1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/SuffixMatcher.java @@ -0,0 +1,71 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.text.Strings2; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Matcher; + +import com.google.common.annotations.VisibleForTesting; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Suffix string matcher. + * + * @since 3.0 + */ +public class SuffixMatcher + extends ComponentSupport + implements Matcher +{ + private final String suffix; + + private final boolean ignoreCase; + + @VisibleForTesting + public SuffixMatcher(final String suffix, final boolean ignoreCase) { + this.suffix = checkNotNull(suffix); + this.ignoreCase = ignoreCase; + } + + public SuffixMatcher(final String suffix) { + this(suffix, false); + } + + public SuffixMatcher ignoreCase(final boolean ignoreCase) { + return new SuffixMatcher(suffix, ignoreCase); + } + + @Override + public boolean matches(final Context context) { + checkNotNull(context); + String path = context.getRequest().getPath(); + log.debug("Matching: {} ends-with={} ignore-case: {}", path, suffix, ignoreCase); + if (ignoreCase) { + return Strings2.lower(path).endsWith(Strings2.lower(suffix)); + } + else { + return path.endsWith(suffix); + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "suffix='" + suffix + '\'' + + ", ignoreCase=" + ignoreCase + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/AndMatcher.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/AndMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..f9c894f6c0f819667673e1ac0852a765cd5cb186 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/AndMatcher.java @@ -0,0 +1,65 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.logic; + +import java.util.List; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Matcher; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Logical AND matcher. + * + * @since 3.0 + * + * @see LogicMatchers + */ +public class AndMatcher + extends ComponentSupport + implements Matcher +{ + private final List matchers; + + @VisibleForTesting + public AndMatcher(final List matchers) { + this.matchers = checkNotNull(matchers); + } + + @Override + public boolean matches(final Context context) { + checkNotNull(context); + if (log.isDebugEnabled()) { + log.debug("Matching: {}", Joiner.on(" AND ").join(matchers)); + } + for (Matcher matcher : matchers) { + boolean matched = matcher.matches(context); + if (!matched) { + return false; + } + } + return true; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "matchers=" + matchers + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/LogicMatchers.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/LogicMatchers.java new file mode 100644 index 0000000000000000000000000000000000000000..ca089e7a156846cf19fdfe093d56d0f5a29d7293 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/LogicMatchers.java @@ -0,0 +1,48 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.logic; + +import java.util.Arrays; + +import org.sonatype.nexus.repository.view.Matcher; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Logic matcher factory. + * + * @since 3.0 + */ +public class LogicMatchers +{ + private LogicMatchers() { + // empty + } + + public static Matcher and(final Matcher... matchers) { + checkNotNull(matchers); + checkArgument(matchers.length > 1, "AND requires 2 or more matchers"); + return new AndMatcher(Arrays.asList(matchers)); + } + + public static Matcher or(final Matcher... matchers) { + checkNotNull(matchers); + checkArgument(matchers.length > 1, "OR requires 2 or more matchers"); + return new OrMatcher(Arrays.asList(matchers)); + } + + public static Matcher not(final Matcher matcher) { + return new NotMatcher(matcher); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/NotMatcher.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/NotMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..79d939dc96e46a94c73eb3cbdabd4e40e78424fd --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/NotMatcher.java @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.logic; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Matcher; + +import com.google.common.annotations.VisibleForTesting; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Logical NOT matcher. + * + * @since 3.0 + * + * @see LogicMatchers + */ +public class NotMatcher + extends ComponentSupport + implements Matcher +{ + private final Matcher matcher; + + @VisibleForTesting + public NotMatcher(final Matcher matcher) { + this.matcher = checkNotNull(matcher); + } + + @Override + public boolean matches(final Context context) { + checkNotNull(context); + log.debug("Matching: NOT {}", matcher); + return !matcher.matches(context); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "matcher=" + matcher + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/OrMatcher.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/OrMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..9ef84aafe52860d7063120c7e616a591149900a0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/OrMatcher.java @@ -0,0 +1,65 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.logic; + +import java.util.List; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Matcher; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Logical OR matcher. + * + * @since 3.0 + * + * @see LogicMatchers + */ +public class OrMatcher + extends ComponentSupport + implements Matcher +{ + private final List matchers; + + @VisibleForTesting + public OrMatcher(final List matchers) { + this.matchers = checkNotNull(matchers); + } + + @Override + public boolean matches(final Context context) { + checkNotNull(context); + if (log.isDebugEnabled()) { + log.debug("Matching: {}", Joiner.on(" OR ").join(matchers)); + } + for (Matcher matcher : matchers) { + boolean matched = matcher.matches(context); + if (matched) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "matchers=" + matchers + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..81882680810444f033661c0133c4ce64fbe6c9d3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/logic/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * View logic matchers. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.view.matchers.logic; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..dee98ec3640c3083c4cf5fddfe006a9641a77545 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Common view matchers. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.view.matchers; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/LiteralToken.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/LiteralToken.java new file mode 100644 index 0000000000000000000000000000000000000000..4363dd2f438e1bbcdd26c22bfd21ede3f4a3c226 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/LiteralToken.java @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.token; + +import java.util.regex.Pattern; + +/** + * A token representing an unchanging portion of a path. + * + * @since 3.0 + */ +public class LiteralToken + extends Token +{ + public LiteralToken(final String value) { + super(value); + } + + @Override + public String toRegexp() { + return Pattern.quote(value); + } + + public String toString() { + return String.format("lit(%s))", value); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/PatternParser.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/PatternParser.java new file mode 100644 index 0000000000000000000000000000000000000000..a8b8b08b35006fa0cc23de185939f5c25438b22b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/PatternParser.java @@ -0,0 +1,152 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.token; + +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.String.format; +import static java.util.Arrays.asList; + +/** + * Parses a template pattern (see {@link TokenParser} for syntax) into a series of {@link Token}s. + * + * @since 3.0 + */ +public class PatternParser +{ + public static final String DEFAULT_VARIABLE_REGEXP = "[^/]+"; + + private static final List ESCAPE_CHARS = asList('\\'); + + private static final List VARNAME_DISALLOWED_CHARS = asList(' ', '}'); + + private static final List VARIABLE_NAME_TERMINATORS = asList(':', '}'); + + private static final List END_OF_LITERAL = asList('{'); + + private static final List END_OF_VARIABLE_DECLARATION = asList('}'); + + private List tokens = new ArrayList<>(); + + private static final List NONE = Collections.emptyList(); + + public PatternParser(final String tokenPattern) { + checkNotNull(tokenPattern); + + parseTemplate(new StringCharacterIterator(tokenPattern)); + } + + public List getTokens() { + return tokens; + } + + private void parseTemplate(final CharacterIterator iterator) { + while (true) { + final char ch = iterator.current(); + switch (ch) { + case CharacterIterator.DONE: + return; + case '{': + parseVariable(iterator); + break; + default: + parseLiteral(iterator); + } + } + } + + private void parseVariable(final CharacterIterator iterator) { + // Consume the starting '{' character + iterator.next(); + + final String varName = readFragment(iterator, VARIABLE_NAME_TERMINATORS, ESCAPE_CHARS, VARNAME_DISALLOWED_CHARS); + + // The iterator is currently pointing to the end character. + if (iterator.current() == ':') { + // Skip the ':' character + iterator.next(); + final String regexp = readFragment(iterator, END_OF_VARIABLE_DECLARATION, ESCAPE_CHARS, NONE); + tokens.add(new VariableToken(varName, regexp)); + } + else { + tokens.add(new VariableToken(varName, DEFAULT_VARIABLE_REGEXP)); + } + + // The iterator should now be pointing to the varname end delimiter. + checkArgument(iterator.current() == '}', "Variable does not end with '}' at position %s", iterator.getIndex()); + // Consume it. + iterator.next(); + } + + private void parseLiteral(final CharacterIterator iterator) { + final String literal = readFragment(iterator, END_OF_LITERAL, ESCAPE_CHARS, NONE); + tokens.add(new LiteralToken(literal)); + } + + /** + * Reads from the character iterator until either a stop character is reached or the iterator is {@link + * CharacterIterator#DONE done}. The stop character is not consumed. Escape characters indicate that the + * following character should not be considered as a stop character. + * + * @throws IllegalArgumentException if the fragment isn't at least one character + */ + static String readFragment(final CharacterIterator iterator, + final List stopChars, + final List escapeChars, + final List disallowed) + { + StringBuilder b = new StringBuilder(); + boolean escaping = false; + while (true) { + final char ch = iterator.current(); + + if (ch == CharacterIterator.DONE) { + checkArgument(!escaping, + format("Unexpected end after escape character '%s' at position %s.", ch, iterator.getIndex())); + + checkArgument(b.length() >= 1, format("Zero-length fragment at position %s.", iterator.getIndex())); + + return b.toString(); + } + + if (escaping) { + checkAllowed(iterator, disallowed); + b.append(ch); + escaping = false; + } + else if (escapeChars.contains(ch)) { + escaping = true; + } + else if (stopChars.contains(ch)) { + return b.toString(); + } + else { + checkAllowed(iterator, disallowed); + b.append(ch); + } + + iterator.next(); + } + } + + private static void checkAllowed(final CharacterIterator iterator, final List disallowed) { + checkArgument(!disallowed.contains(iterator.current()), "Disallowed character %s at position %s", + iterator.current(), iterator.getIndex()); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/Token.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/Token.java new file mode 100644 index 0000000000000000000000000000000000000000..35eb739cd7215b75230b1a715126854eba9d9043 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/Token.java @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.token; + +import java.util.Arrays; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A portion of a parsed pattern template. + * + * @since 3.0 + */ +public abstract class Token +{ + protected final String value; + + protected Token(final String value) { + this.value = checkNotNull(value); + } + + public abstract String toRegexp(); + + private static final List REGEXP_CHARS = Arrays.asList('[', ']', '{', '}', '(', ')', '\\', '.'); + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Token)) { + return false; + } + + Token token = (Token) o; + return value.equals(token.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/TokenMatcher.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/TokenMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..284727290ec1926e6596040b218cb1a3044716d0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/TokenMatcher.java @@ -0,0 +1,79 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.token; + +import java.util.Map; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Matcher; +import org.sonatype.nexus.repository.view.Request; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link Matcher} that examines the {@link Request#getPath() request path} and attempts to parse it using the + * {@link TokenParser}. + * + * If there is a match, the tokens are stored in the context under key {@link TokenMatcher.State}. + * + * @since 3.0 + */ +public class TokenMatcher + extends ComponentSupport + implements Matcher +{ + public interface State + { + String pattern(); + + Map getTokens(); + } + + private final TokenParser parser; + + private final String pattern; + + public TokenMatcher(final String pattern) { + this.pattern = checkNotNull(pattern); + this.parser = new TokenParser(pattern); + } + + @Override + public boolean matches(final Context context) { + checkNotNull(context); + + String path = context.getRequest().getPath(); + log.debug("Matching: {}~={}", path, parser); + final Map tokens = parser.parse(path); + if (tokens == null) { + // There was no match. + return false; + } + + // matched expose tokens in context + context.getAttributes().set(State.class, new State() + { + @Override + public String pattern() { + return pattern; + } + + @Override + public Map getTokens() { + return tokens; + } + }); + return true; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/TokenParser.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/TokenParser.java new file mode 100644 index 0000000000000000000000000000000000000000..b671ef5c368873638b4b7ef3daf63c112ac2bc8f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/TokenParser.java @@ -0,0 +1,124 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.token; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.common.ComponentSupport; + +import static com.google.common.base.Preconditions.checkState; + + +/** + * Parses path-like strings against a template pattern, a literal string with embedded variables. e.g. {@code + * "/{binName}/{bucketName}"} would match {@code "/bin12/overages"}. + * + * By default, variables are matched against the regexp {@link PatternParser#DEFAULT_VARIABLE_REGEXP}, which + * excludes the path separator ({@code '/'}). To override this, specify the regexp explicitly: + * + * e.g. {@code "/{group:.+}/{artifact}/{version}/{name}-{version}.{ext}"} + * + * {@code "{letterCode:[A-Za-z]}"}< + * + * The backslash ({@code '\'}) serves as an escape character, if (say) it is necessary to use curly braces in the + * regular expression. If escape characters appear in regular expressions, these must be double-escaped. + * + * Caveat: the {@link TokenParser} cannot handle groups in variable regexp definitions. This will cause + * parsing errors. + * + * @since 3.0 + */ +public class TokenParser + extends ComponentSupport +{ + private final List variables; + + private final Pattern pattern; + + public TokenParser(final String templatePattern) { + final List tokens = new PatternParser(templatePattern).getTokens(); + pattern = Pattern.compile(regexp(tokens)); + log.trace("Pattern: {}", pattern); + + // Separate the variable tokens + variables = new ArrayList<>(); + for (Token token : tokens) { + if (token instanceof VariableToken) { + variables.add((VariableToken) token); + } + } + } + + /** + * Attempts to parse the provided path against the template pattern. If the pattern matches, the resulting Map + * contains an entry for each variable in the pattern. The variable names are keys, with the matching portions of the + * path as values. Returns {@code null} if the pattern does not match. + */ + @Nullable + public Map parse(final String path) { + final Matcher matcher = pattern.matcher(path); + if (!matcher.matches()) { + return null; + } + + checkState(matcher.groupCount() == variables.size(), + "Mismatch between the number of captured groups (%s) and the number of variables, %s.", matcher.groupCount(), + variables.size()); + + Map values = new HashMap<>(); + for (int i = 0; i < matcher.groupCount(); i++) { + final String name = variables.get(i).getName(); + final String value = matcher.group(i + 1); + if (values.containsKey(name)) { + final String existingValue = values.get(name); + if (!Objects.equals(existingValue, value)) { + log.trace("Variable '{}' values mismatch: '{}' vs '{}'", name, existingValue, value); + return null; + } + } + else { + values.put(name, value); + } + } + + return values; + } + + public String getPattern() { + return pattern.toString(); + } + + private String regexp(final List tokens) { + StringBuilder b = new StringBuilder(); + for (Token token : tokens) { + b.append(token.toRegexp()); + } + return b.toString(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "pattern=" + pattern + + ", variables=" + variables + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/VariableToken.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/VariableToken.java new file mode 100644 index 0000000000000000000000000000000000000000..768978ab6c91ece8a9f1e21cf57be6ed95fa7f8a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/VariableToken.java @@ -0,0 +1,63 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.token; + +import com.google.common.base.Objects; + +/** + * A named variable that matches a regular expression. + * + * @since 3.0 + */ +public class VariableToken + extends Token +{ + private final String name; + + public VariableToken(final String name, final String regexp) { + super(regexp); + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toRegexp() { + return "(" + value + ")"; + } + + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!(obj instanceof VariableToken)) { + return false; + } + final VariableToken other = (VariableToken) obj; + return Objects.equal(getName(), other.getName()) + && super.equals(obj); + } + + @Override + public int hashCode() { + return Objects.hashCode(name, value); + } + + public String toString() { + return String.format("var(%s,%s)", name, value); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..c4b675358eb66fe74b6e0477177a80c4b5979319 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/matchers/token/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * View token matcher. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.view.matchers.token; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..280f5e95fd3484719dfc49f5bc37238215a66e1a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Repository view framework. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.view; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/BlobPayload.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/BlobPayload.java new file mode 100644 index 0000000000000000000000000000000000000000..392511a515279e9a1f1811ec5c19722aaf3de2f5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/BlobPayload.java @@ -0,0 +1,65 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.payloads; + +import java.io.IOException; +import java.io.InputStream; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.repository.view.Payload; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * {@link Blob} backed payload. + * + * @since 3.0 + */ +public class BlobPayload + implements Payload +{ + private final Blob blob; + + private final String contentType; + + public BlobPayload(final Blob blob, @Nullable final String contentType) { + this.blob = checkNotNull(blob); + this.contentType = contentType; + } + + @Override + public InputStream openInputStream() throws IOException { + return blob.getInputStream(); + } + + @Override + public long getSize() { + return blob.getMetrics().getContentSize(); + } + + @Nullable + @Override + public String getContentType() { + return contentType; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "blob=" + blob + + ", contentType='" + contentType + '\'' + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/BytesPayload.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/BytesPayload.java new file mode 100644 index 0000000000000000000000000000000000000000..8ccc8abf280acca59bd472e826962058a5027739 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/BytesPayload.java @@ -0,0 +1,65 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.payloads; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.view.Payload; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Bytes payload. + * + * @since 3.0 + */ +public class BytesPayload + implements Payload +{ + private final byte[] content; + + private final String contentType; + + public BytesPayload(final byte[] content, @Nullable final String contentType) { + this.content = checkNotNull(content); + this.contentType = contentType; + } + + @Override + public InputStream openInputStream() throws IOException { + return new ByteArrayInputStream(content); + } + + @Override + public long getSize() { + return content.length; + } + + @Nullable + @Override + public String getContentType() { + return contentType; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "size=" + getSize() + + ", contentType='" + contentType + '\'' + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/HttpEntityPayload.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/HttpEntityPayload.java new file mode 100644 index 0000000000000000000000000000000000000000..ed3b19166e4b649932d8b5ebc1d123bb6052395a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/HttpEntityPayload.java @@ -0,0 +1,70 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.payloads; + +import java.io.IOException; +import java.io.InputStream; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.view.Payload; + +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.util.EntityUtils; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Adapts {@link HttpEntity} to {@link Payload}. + * + * @since 3.0 + */ +public class HttpEntityPayload + implements Payload +{ + private final HttpResponse response; + + private final HttpEntity entity; + + public HttpEntityPayload(final HttpResponse response, final HttpEntity entity) { + this.response = checkNotNull(response); + this.entity = checkNotNull(entity); + } + + @Override + public InputStream openInputStream() throws IOException { + return entity.getContent(); + } + + @Override + public long getSize() { + return entity.getContentLength(); + } + + @Nullable + @Override + public String getContentType() { + Header header = entity.getContentType(); + if (header != null) { + return header.getValue(); + } + return null; + } + + @Override + public void close() throws IOException { + EntityUtils.consume(entity); + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/StreamPayload.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/StreamPayload.java new file mode 100644 index 0000000000000000000000000000000000000000..4c8b8801f3bff307999df23299111d471345150c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/StreamPayload.java @@ -0,0 +1,87 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.payloads; + +import java.io.IOException; +import java.io.InputStream; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.view.Payload; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Stream payload. + * + * @since 3.0 + */ +public class StreamPayload + implements Payload +{ + private final InputStreamSupplier stream; + + private final long size; + + private final String contentType; + + public StreamPayload(final InputStreamSupplier stream, final long size, @Nullable final String contentType) { + this.stream = checkNotNull(stream); + this.size = size; + this.contentType = contentType; + } + + /** + * Returnes opened stream from configured supplier. + */ + @Override + public InputStream openInputStream() throws IOException { + return stream.get(); + } + + @Override + public long getSize() { + return size; + } + + @Nullable + @Override + public String getContentType() { + return contentType; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "stream=" + stream + + ", size=" + size + + ", contentType='" + contentType + '\'' + + '}'; + } + + // + // Supplier + // + + // NOTE: Not using Guava Supplier, as we need to express throws IOException + + /** + * Supplies an {@link InputStream}. + */ + public interface InputStreamSupplier + { + @Nonnull + InputStream get() throws IOException; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/StringPayload.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/StringPayload.java new file mode 100644 index 0000000000000000000000000000000000000000..dd89e87942bf52450e8ec118a13d4699dc729ccc --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/StringPayload.java @@ -0,0 +1,79 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.payloads; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.view.Payload; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * String payload. + * + * @since 3.0 + */ +public class StringPayload + implements Payload +{ + private final byte[] contentBytes; + + private final Charset charset; + + private final String contentType; + + public StringPayload(final String content, final Charset charset, @Nullable final String contentType) { + this.contentBytes = checkNotNull(content).getBytes(charset); + this.charset = checkNotNull(charset); + this.contentType = contentType; + } + + public StringPayload(final String content, @Nullable final String contentType) { + this(content, StandardCharsets.UTF_8, contentType); + } + + public Charset getCharset() { + return charset; + } + + @Override + public InputStream openInputStream() throws IOException { + return new ByteArrayInputStream(contentBytes); + } + + @Override + public long getSize() { + return contentBytes.length; + } + + @Nullable + @Override + public String getContentType() { + return contentType; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "size=" + contentBytes.length + + ", charset=" + charset + + ", contentType='" + contentType + '\'' + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/TempBlobPartPayload.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/TempBlobPartPayload.java new file mode 100644 index 0000000000000000000000000000000000000000..40649edc678a779a37dc8c3269a4a8cc7fccd9ae --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/TempBlobPartPayload.java @@ -0,0 +1,82 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.payloads; + +import java.io.IOException; +import java.io.InputStream; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.repository.storage.TempBlob; +import org.sonatype.nexus.repository.view.PartPayload; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * PartPayload backed by a TempBlob. Useful for file uploads surrounded by other multipart data. + * + * @since 3.1 + */ +public class TempBlobPartPayload + implements PartPayload +{ + private final PartPayload payload; + + private final TempBlob tempBlob; + + public TempBlobPartPayload(final PartPayload payload, final TempBlob tempBlob) throws IOException { + this.payload = checkNotNull(payload); + this.tempBlob = checkNotNull(tempBlob); + } + + @Nullable + @Override + public String getName() { + return payload.getName(); + } + + @Override + public String getFieldName() { + return payload.getFieldName(); + } + + @Override + public boolean isFormField() { + return payload.isFormField(); + } + + @Override + public InputStream openInputStream() throws IOException { + return tempBlob.get(); + } + + @Override + public long getSize() { + return UNKNOWN_SIZE; + } + + @Nullable + @Override + public String getContentType() { + return payload.getContentType(); + } + + public TempBlob getTempBlob() { + return tempBlob; + } + + @Override + public void close() throws IOException { + tempBlob.close(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/package-info.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..85f5161e8b88f7f02b7c0626f7f6878f5cb7601b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/payloads/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Common view payloads. + * + * @since 3.0 + */ +package org.sonatype.nexus.repository.view.payloads; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/GlobalRepositoryWebhook.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/GlobalRepositoryWebhook.groovy new file mode 100644 index 0000000000000000000000000000000000000000..5cf1cea8cfc3d5d3bafb4efebfbad1013c0103e9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/GlobalRepositoryWebhook.groovy @@ -0,0 +1,115 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.webhooks + +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.nexus.audit.InitiatorProvider +import org.sonatype.nexus.common.node.NodeAccess +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.manager.RepositoryCreatedEvent +import org.sonatype.nexus.repository.manager.RepositoryDeletedEvent +import org.sonatype.nexus.repository.manager.RepositoryUpdatedEvent +import org.sonatype.nexus.webhooks.GlobalWebhook +import org.sonatype.nexus.webhooks.Webhook +import org.sonatype.nexus.webhooks.WebhookPayload + +import com.google.common.eventbus.AllowConcurrentEvents +import com.google.common.eventbus.Subscribe + +/** + * Global repository {@link Webhook}. + * + * @since 3.1 + */ +@Named +@Singleton +class GlobalRepositoryWebhook + extends GlobalWebhook +{ + public static final String NAME = 'repository' + + @Inject + NodeAccess nodeAccess + + @Inject + InitiatorProvider initiatorProvider + + @Override + String getName() { + return NAME + } + + private static enum EventAction + { + CREATED, + UPDATED, + DELETED + } + + @Subscribe + @AllowConcurrentEvents + void on(final RepositoryCreatedEvent event) { + queue(event.repository, EventAction.CREATED) + } + + @Subscribe + @AllowConcurrentEvents + void on(final RepositoryUpdatedEvent event) { + queue(event.repository, EventAction.UPDATED) + } + + @Subscribe + @AllowConcurrentEvents + void on(final RepositoryDeletedEvent event) { + queue(event.repository, EventAction.DELETED) + } + + private void queue(final Repository repository, final EventAction eventAction) { + def payload = new RepositoryWebhookPayload( + nodeId: nodeAccess.getId(), + timestamp: new Date(), + initiator: initiatorProvider.get(), + action: eventAction + ) + + payload.repository = new RepositoryWebhookPayload.Repository( + format: repository.format.value, + name: repository.name, + type: repository.type.value + ) + + subscriptions.each { + queue(it, payload) + } + } + + static class RepositoryWebhookPayload + extends WebhookPayload + { + EventAction action + + Repository repository + + static class Repository + { + String format + + String name + + String type + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/RepositoryAssetWebhook.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/RepositoryAssetWebhook.groovy new file mode 100644 index 0000000000000000000000000000000000000000..1ae828379f0b6b09481fdd3044b8d3fda047cbba --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/RepositoryAssetWebhook.groovy @@ -0,0 +1,130 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.webhooks + +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.nexus.audit.InitiatorProvider +import org.sonatype.nexus.common.node.NodeAccess +import org.sonatype.nexus.repository.storage.Asset +import org.sonatype.nexus.repository.storage.AssetCreatedEvent +import org.sonatype.nexus.repository.storage.AssetDeletedEvent +import org.sonatype.nexus.repository.storage.AssetEvent +import org.sonatype.nexus.repository.storage.AssetUpdatedEvent +import org.sonatype.nexus.webhooks.Webhook +import org.sonatype.nexus.webhooks.WebhookPayload +import org.sonatype.nexus.webhooks.WebhookRequest + +import com.google.common.eventbus.AllowConcurrentEvents +import com.google.common.eventbus.Subscribe + +/** + * Repository {@link Asset} {@link Webhook}. + * + * @since 3.1 + */ +@Named +@Singleton +class RepositoryAssetWebhook + extends RepositoryWebhook +{ + public static final String NAME = 'asset' + + @Inject + NodeAccess nodeAccess + + @Inject + InitiatorProvider initiatorProvider + + @Override + String getName() { + return NAME + } + + private static enum EventAction + { + CREATED, + UPDATED, + DELETED + } + + @Subscribe + @AllowConcurrentEvents + void on(final AssetCreatedEvent event) { + maybeQueue(event, EventAction.CREATED) + } + + @Subscribe + @AllowConcurrentEvents + void on(final AssetUpdatedEvent event) { + maybeQueue(event, EventAction.UPDATED) + } + + @Subscribe + @AllowConcurrentEvents + void on(final AssetDeletedEvent event) { + maybeQueue(event, EventAction.DELETED) + } + + /** + * Maybe queue {@link WebhookRequest} for event matching subscriptions. + */ + private void maybeQueue(final AssetEvent event, final EventAction eventAction) { + if (event.local) { + + Asset asset = event.asset + def payload = new RepositoryAssetWebhookPayload( + nodeId: nodeAccess.getId(), + timestamp: new Date(), + initiator: initiatorProvider.get(), + repositoryName: event.repositoryName, + action: eventAction + ) + + payload.asset = new RepositoryAssetWebhookPayload.RepositoryAsset( + id: asset.entityMetadata.id.value, + format: asset.format(), + name: asset.name() + ) + + subscriptions.each { + def configuration = it.configuration as RepositoryWebhook.Configuration + if (configuration.repository == event.repositoryName) { + // TODO: discriminate on content-selector + queue(it, payload) + } + } + } + } + + static class RepositoryAssetWebhookPayload + extends WebhookPayload + { + String repositoryName + + EventAction action + + RepositoryAsset asset + + static class RepositoryAsset + { + String id + + String format + + String name + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/RepositoryComponentWebhook.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/RepositoryComponentWebhook.groovy new file mode 100644 index 0000000000000000000000000000000000000000..1f8d81cf6ff95bc8d99de9c17596843152b34191 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/RepositoryComponentWebhook.groovy @@ -0,0 +1,136 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.webhooks + +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.nexus.audit.InitiatorProvider +import org.sonatype.nexus.common.node.NodeAccess +import org.sonatype.nexus.repository.storage.Component +import org.sonatype.nexus.repository.storage.ComponentCreatedEvent +import org.sonatype.nexus.repository.storage.ComponentDeletedEvent +import org.sonatype.nexus.repository.storage.ComponentEvent +import org.sonatype.nexus.repository.storage.ComponentUpdatedEvent +import org.sonatype.nexus.webhooks.Webhook +import org.sonatype.nexus.webhooks.WebhookPayload +import org.sonatype.nexus.webhooks.WebhookRequest + +import com.google.common.eventbus.AllowConcurrentEvents +import com.google.common.eventbus.Subscribe + +/** + * Repository {@link Component} {@link Webhook}. + * + * @since 3.1 + */ +@Named +@Singleton +class RepositoryComponentWebhook + extends RepositoryWebhook +{ + public static final String NAME = 'component' + + @Inject + NodeAccess nodeAccess + + @Inject + InitiatorProvider initiatorProvider + + @Override + String getName() { + return NAME + } + + private static enum EventAction + { + CREATED, + UPDATED, + DELETED + } + + @Subscribe + @AllowConcurrentEvents + void on(final ComponentCreatedEvent event) { + maybeQueue(event, EventAction.CREATED) + } + + @Subscribe + @AllowConcurrentEvents + void on(final ComponentUpdatedEvent event) { + maybeQueue(event, EventAction.UPDATED) + } + + @Subscribe + @AllowConcurrentEvents + void on(final ComponentDeletedEvent event) { + maybeQueue(event, EventAction.DELETED) + } + + /** + * Maybe queue {@link WebhookRequest} for event matching subscriptions. + */ + private void maybeQueue(final ComponentEvent event, final EventAction eventAction) { + if (event.local) { + + Component component = event.component + def payload = new RepositoryComponentWebhookPayload( + nodeId: nodeAccess.getId(), + timestamp: new Date(), + initiator: initiatorProvider.get(), + repositoryName: event.repositoryName, + action: eventAction + ) + + payload.component = new RepositoryComponentWebhookPayload.RepositoryComponent( + id: component.entityMetadata.id.value, + format: component.format(), + name: component.name(), + group: component.group(), + version: component.version() + ) + + subscriptions.each { + def configuration = it.configuration as RepositoryWebhook.Configuration + if (configuration.repository == event.repositoryName) { + // TODO: discriminate on content-selector + queue(it, payload) + } + } + } + } + + static class RepositoryComponentWebhookPayload + extends WebhookPayload + { + String repositoryName + + EventAction action + + RepositoryComponent component + + static class RepositoryComponent + { + String id + + String format + + String name + + String group + + String version + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/RepositoryWebhook.java b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/RepositoryWebhook.java new file mode 100644 index 0000000000000000000000000000000000000000..6bc251d77f9345a5dab3f8f17ba05eddb00b00fc --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/RepositoryWebhook.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.webhooks; + +import org.sonatype.nexus.webhooks.Webhook; +import org.sonatype.nexus.webhooks.WebhookConfiguration; +import org.sonatype.nexus.webhooks.WebhookType; + +/** + * Repository {@link Webhook}. + * + * @since 3.1 + */ +public abstract class RepositoryWebhook + extends Webhook +{ + public static final WebhookType TYPE = new WebhookType("repository") {}; + + public final WebhookType getType() { + return TYPE; + } + + /** + * Additional configuration exposed for {@link RepositoryWebhook}. + */ + public interface Configuration + extends WebhookConfiguration + { + String getRepository(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/RepositoryWebhookCapability.groovy b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/RepositoryWebhookCapability.groovy new file mode 100644 index 0000000000000000000000000000000000000000..5e52a670043d2da97bfb97b6259f0c200f6bde15 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/webhooks/RepositoryWebhookCapability.groovy @@ -0,0 +1,292 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.webhooks + +import javax.annotation.Nullable +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton + +import org.sonatype.goodies.i18n.I18N +import org.sonatype.goodies.i18n.MessageBundle +import org.sonatype.goodies.i18n.MessageBundle.DefaultMessage +import org.sonatype.nexus.capability.CapabilityConfigurationSupport +import org.sonatype.nexus.capability.CapabilityDescriptorSupport +import org.sonatype.nexus.capability.CapabilitySupport +import org.sonatype.nexus.capability.CapabilityType +import org.sonatype.nexus.capability.Condition +import org.sonatype.nexus.capability.Tag +import org.sonatype.nexus.capability.Taggable +import org.sonatype.nexus.formfields.FormField +import org.sonatype.nexus.formfields.ItemselectFormField +import org.sonatype.nexus.formfields.PasswordFormField +import org.sonatype.nexus.formfields.RepositoryCombobox +import org.sonatype.nexus.formfields.UrlFormField +import org.sonatype.nexus.repository.capability.RepositoryConditions +import org.sonatype.nexus.repository.types.GroupType +import org.sonatype.nexus.webhooks.WebhookService +import org.sonatype.nexus.webhooks.WebhookSubscription + +import com.google.common.base.Splitter +import com.google.common.base.Strings +import groovy.transform.PackageScope +import groovy.transform.ToString + +import static org.sonatype.nexus.capability.CapabilityType.capabilityType + +/** + * Capability to manage {@link RepositoryWebhook} configuration. + * + * @since 3.1 + */ +@Named(RepositoryWebhookCapability.TYPE_ID) +class RepositoryWebhookCapability + extends CapabilitySupport + implements Taggable +{ + public static final String TYPE_ID = 'webhook.repository' + + public static final CapabilityType TYPE = capabilityType(TYPE_ID) + + private static interface Messages + extends MessageBundle + { + @DefaultMessage('Webhook: Repository') + String name() + + @DefaultMessage('Webhook') + String category() + + @DefaultMessage('Repository') + String repositoryLabel() + + @DefaultMessage('Repository to discriminate events from') + String repositoryHelp() + + @DefaultMessage('Event Types') + String namesLabel() + + @DefaultMessage('Event types which trigger this Webhook') + String namesHelp() + + @DefaultMessage('URL') + String urlLabel() + + @DefaultMessage('Send a HTTP POST request to this URL') + String urlHelp() + + @DefaultMessage('Secret Key') + String secretLabel() + + @DefaultMessage('Key to use for HMAC payload digest') + String secretHelp() + + @DefaultMessage('%s') + String description(String names) + } + + @PackageScope + static final Messages messages = I18N.create(Messages.class) + + @Inject + WebhookService webhookService + + @Inject + RepositoryConditions repositoryConditions + + private final List subscriptions = [] + + @Override + protected Configuration createConfig(final Map properties) { + return new Configuration(properties) + } + + @Override + protected String renderDescription() { + return messages.description(config.names.join(', ')) + } + + @Override + Condition activationCondition() { + return conditions().logical().and( + conditions().capabilities().passivateCapabilityDuringUpdate(), + repositoryConditions.repositoryExists({config.repository}) + ) + } + + /** + * Subscribe to each configured webhook. + */ + @Override + protected void onActivate(final Configuration config) { + webhookService.webhooks.findAll { + it.type == RepositoryWebhook.TYPE && it.name in config.names + }.each { + subscriptions << it.subscribe(config) + } + } + + /** + * Cancel each webhook subscription. + */ + @Override + protected void onPassivate(final Configuration config) { + subscriptions.each {it.cancel()} + subscriptions.clear() + } + + @Override + Set getTags() { + return [ + Tag.categoryTag(messages.category()), + Tag.repositoryTag(config.repository) + ] + } + + // + // Configuration + // + + private static final String P_REPOSITORY = 'repository' + + private static final String P_NAMES = 'names' + + private static final String P_URL = 'url' + + private static final String P_SECRET = 'secret' + + @ToString(includePackage = false, includeNames = true, excludes = ['secret']) + static class Configuration + extends CapabilityConfigurationSupport + implements RepositoryWebhook.Configuration + { + String repository + + List names + + URI url + + @Nullable + String secret + + Configuration(final Map properties) { + repository = properties[P_REPOSITORY] + names = parseList(properties[P_NAMES]) + url = parseUri(properties[P_URL]) + secret = Strings.emptyToNull(properties[P_SECRET]) + } + + private static final Splitter LIST_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings() + + private static List parseList(final String value) { + List result = [] + result.addAll(LIST_SPLITTER.split(value)) + return result + } + } + + // + // Descriptor + // + + @Named(RepositoryWebhookCapability.TYPE_ID) + @Singleton + static public class Descriptor + extends CapabilityDescriptorSupport + implements Taggable + { + private final FormField repository + + // TODO: content-selector + + private final FormField names + + private final FormField url + + private final FormField secret + + Descriptor() { + this.exposed = true + this.hidden = false + + this.repository = new RepositoryCombobox( + P_REPOSITORY, + messages.repositoryLabel(), + messages.repositoryHelp(), + FormField.MANDATORY + ).excludingAnyOfTypes(GroupType.NAME) + + this.names = new ItemselectFormField( + P_NAMES, + messages.namesLabel(), + messages.namesHelp(), + FormField.MANDATORY + ).with { + storeApi = 'coreui_Webhook.listWithTypeRepository' + buttons = ['add', 'remove'] + fromTitle = 'Available' + toTitle = 'Selected' + return it + } + + this.url = new UrlFormField( + P_URL, + messages.urlLabel(), + messages.urlHelp(), + FormField.MANDATORY + ) + + this.secret = new PasswordFormField( + P_SECRET, + messages.secretLabel(), + messages.secretHelp(), + FormField.OPTIONAL + ) + } + + @Override + CapabilityType type() { + return TYPE + } + + @Override + String name() { + return messages.name() + } + + @Override + List formFields() { + return [repository, names, url, secret] + } + + @Override + protected Configuration createConfig(final Map properties) { + return new Configuration(properties) + } + + @Override + protected String renderAbout() { + return render("$TYPE_ID-about.vm") + } + + @Override + Set getTags() { + return [Tag.categoryTag(messages.category())] + } + + @Override + protected Set uniqueProperties() { + return [P_REPOSITORY, P_URL] as Set + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/resources/org/sonatype/nexus/repository/browse/internal/resources/browseContentHtml.vm b/thirdparty-bundles/components/nexus-repository/src/main/resources/org/sonatype/nexus/repository/browse/internal/resources/browseContentHtml.vm new file mode 100644 index 0000000000000000000000000000000000000000..a18d845f0f8e66ebf2c305146c5a16a42998c669 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/resources/org/sonatype/nexus/repository/browse/internal/resources/browseContentHtml.vm @@ -0,0 +1,94 @@ +#* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + *### + + + + + Index of ${requestPath} + + +#* START favicon links and meta + * favicon set for multiple browsers, OS shortcuts, etc. + * Generated at Real Favicon Generator, https://realfavicongenerator.net/ + * + * Associated files not referenced explicitly in link or meta tags: + * mstile-*.png - Win8+ desktop support + * browserconfig.xml - Win8+ desktop support + * apple-touch-icon.png - OSX Safari desktop support +*# +## IE, early versions (comment required) + +## Safari on MacOS and iOS + + +## classic favicon, shown in browser tabs + +## IE, later versions + +## Win8+ + + +## END favicon links + + + + +

    Index of ${requestPath}

    + +#if($showMoreContentWarning) +

    + There may be additional items to display. +

    +

    + If you need to script against the full list of items in the database, try using the rest api. + You may also try adding a ?filter= parameter to the url. +

    +#end + + + + + + + + + #if(! $root) + + + + #end + #foreach( $listItem in $listItems ) + + + + + + + #end +
    NameLast ModifiedSizeDescription
    Parent Directory
    $esc.html($listItem.name) + #if( $listItem.collection ) +   + #else + $listItem.lastModified + #end + + #if( $listItem.collection ) +   + #else + $listItem.size + #end + $listItem.description
    + + diff --git a/thirdparty-bundles/components/nexus-repository/src/main/resources/org/sonatype/nexus/repository/search/elasticsearch-mapping.json b/thirdparty-bundles/components/nexus-repository/src/main/resources/org/sonatype/nexus/repository/search/elasticsearch-mapping.json new file mode 100644 index 0000000000000000000000000000000000000000..1125b23bae1b7768e522b01d15b2ac45626f63d0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/resources/org/sonatype/nexus/repository/search/elasticsearch-mapping.json @@ -0,0 +1,97 @@ +{ + "settings": { + "index" : { + "number_of_shards" : 1, + "number_of_replicas" : 0 + }, + "analysis": { + "analyzer": { + "case_insensitive_sort": { + "tokenizer": "keyword", + "filter": [ + "lowercase" + ] + } + } + } + }, + "mappings": { + "component": { + "properties": { + "assets": { + "properties": { + "name": { + "type": "string", + "index": "not_analyzed" + }, + "attributes": { + "properties": { + "checksum": { + "properties": { + "md5": { + "type": "string", + "index": "not_analyzed" + }, + "sha1": { + "type": "string", + "index": "not_analyzed" + } + } + } + } + }, + "content_type": { + "type": "string", + "index": "not_analyzed" + }, + "last_updated": { + "type": "long" + }, + "id": { + "type": "string", + "index": "not_analyzed" + } + } + }, + "format": { + "type": "string", + "index": "not_analyzed" + }, + "group": { + "type": "string", + "fields": { + "case_insensitive": { + "type": "string", + "analyzer": "case_insensitive_sort" + }, + "raw": { + "type": "string", + "index": "not_analyzed" + } + } + }, + "name": { + "type": "string", + "fields": { + "case_insensitive": { + "type": "string", + "analyzer": "case_insensitive_sort" + }, + "raw": { + "type": "string", + "index": "not_analyzed" + } + } + }, + "repository_name": { + "type": "string", + "index": "not_analyzed" + }, + "version": { + "type": "string", + "index": "not_analyzed" + } + } + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/main/resources/org/sonatype/nexus/repository/view/handlers/browseUnsupportedHtml.vm b/thirdparty-bundles/components/nexus-repository/src/main/resources/org/sonatype/nexus/repository/view/handlers/browseUnsupportedHtml.vm new file mode 100644 index 0000000000000000000000000000000000000000..fe5727f591d3adf6174ffcd142d6efbf04ec4f58 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/resources/org/sonatype/nexus/repository/view/handlers/browseUnsupportedHtml.vm @@ -0,0 +1,93 @@ +#* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + *### + + + + + Repository - Nexus Repository Manager + + +#* START favicon links and meta + * favicon set for multiple browsers, OS shortcuts, etc. + * Generated at Real Favicon Generator, https://realfavicongenerator.net/ + * + * Associated files not referenced explicitly in link or meta tags: + * mstile-*.png - Win8+ desktop support + * browserconfig.xml - Win8+ desktop support + * apple-touch-icon.png - OSX Safari desktop support +*# +## IE, early versions (comment required) + +## Safari on MacOS and iOS + + +## classic favicon, shown in browser tabs + +## IE, later versions + +## Win8+ + + +## END favicon links + + + + + + +
    +
    + + Repository + ${repository.name} +
    + +
    +
    +

    + This ${repository.format} ${repository.type} repository is not directly browseable at this URL. +

    + + #if($treeEnabled) +

    + Please use the browse + or HTML index + views to inspect the contents of this repository. +

    + #else +

    + Please use the component browser + or asset browser + to inspect the contents of this repository. +

    + #end +
    +
    + + diff --git a/thirdparty-bundles/components/nexus-repository/src/main/resources/org/sonatype/nexus/repository/webhooks/webhook.repository-about.vm b/thirdparty-bundles/components/nexus-repository/src/main/resources/org/sonatype/nexus/repository/webhooks/webhook.repository-about.vm new file mode 100644 index 0000000000000000000000000000000000000000..04d63647feaf0fa817082a9ad240895d60273c5c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/main/resources/org/sonatype/nexus/repository/webhooks/webhook.repository-about.vm @@ -0,0 +1,14 @@ +#* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + *### + +Send HTTP POST requests for repository events. \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/test/it-resources/fabric/elasticsearch.yml b/thirdparty-bundles/components/nexus-repository/src/test/it-resources/fabric/elasticsearch.yml new file mode 100644 index 0000000000000000000000000000000000000000..740f509846032ccb01efd5ad4aab1eeaf6d3b42e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/it-resources/fabric/elasticsearch.yml @@ -0,0 +1,20 @@ +cluster.name: nexus + +path: + home: ${testdir} + conf: ${testdir}/etc + data: ${testdir}/data + logs: ${testdir}/log + work: ${testdir}/tmp + +node.data: true +node.master: true + +# Disable groovy script integration +script.groovy.sandbox.enabled: false + +# Disable networking +node.local: true +http.enabled: false +discovery.zen.ping.multicast.enabled: false + diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/RepositoryTaskSupportTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/RepositoryTaskSupportTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..c5abfcba458ef6abfc0aeafefa1d83fda7d0b3b5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/RepositoryTaskSupportTest.groovy @@ -0,0 +1,243 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository + +import org.sonatype.goodies.common.MultipleFailures.MultipleFailuresException +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.repository.manager.RepositoryManager +import org.sonatype.nexus.repository.types.GroupType +import org.sonatype.nexus.scheduling.TaskConfiguration + +import org.junit.Rule +import org.junit.Test +import org.junit.rules.ExpectedException +import org.mockito.Mock + +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.when + +class RepositoryTaskSupportTest + extends TestSupport +{ + @Rule + public final ExpectedException thrown = ExpectedException.none() + + @Mock + private RepositoryManager repositoryManager + + private TaskConfiguration configuration + + private TestTask task + + /** + * Verify that repository field must be present in configuration. + */ + @Test + void 'repository field must be present'() { + configuration = new TaskConfiguration(id: 'test', typeId: 'test') + task = new TestTask() + task.install(repositoryManager, new GroupType()) + task.configure(configuration) + + thrown.expect(IllegalArgumentException) + task.execute() + } + + /** + * Verify that repository exists. + */ + @Test + void 'repository exists'() { + configuration = new TaskConfiguration(id: 'test', typeId: 'test') + configuration.setString(RepositoryTaskSupport.REPOSITORY_NAME_FIELD_ID, 'foo') + task = new TestTask() + task.install(repositoryManager, new GroupType()) + task.configure(configuration) + + thrown.expect(NullPointerException) + task.execute() + } + + /** + * Verify that configured repository satisfies task repository filter (appliesTo). + */ + @Test + void 'repository satisfies filter'() { + configuration = new TaskConfiguration(id: 'test', typeId: 'test') + configuration.setString(RepositoryTaskSupport.REPOSITORY_NAME_FIELD_ID, 'foo') + when(repositoryManager.get('foo')).thenReturn(mock(Repository)) + task = new TestTask() { + @Override + protected boolean appliesTo(final Repository repository) { + return false + } + } + task.install(repositoryManager, new GroupType()) + task.configure(configuration) + + thrown.expect(IllegalStateException) + task.execute() + } + + /** + * Verify that task is executed for repository. + */ + @Test + void 'task is executed for repository'() { + configuration = new TaskConfiguration(id: 'test', typeId: 'test') + configuration.setString(RepositoryTaskSupport.REPOSITORY_NAME_FIELD_ID, 'foo') + def testRepository = mock(Repository) + when(repositoryManager.get('foo')).thenReturn(testRepository) + def actualRepository = null + task = new TestTask() { + @Override + protected void execute(final Repository repository) { + assert testRepository == repository + actualRepository = repository + } + } + task.install(repositoryManager, new GroupType()) + task.configure(configuration) + + task.execute() + assert actualRepository != null + } + + /** + * Verify that task is executed for all repositories. + */ + @Test + void 'task is executed for all repositories'() { + configuration = new TaskConfiguration(id: 'test', typeId: 'test') + configuration.setString(RepositoryTaskSupport.REPOSITORY_NAME_FIELD_ID, '*') + def testRepository1 = mock(Repository) + def testRepository2 = mock(Repository) + when(repositoryManager.browse()).thenReturn([testRepository1, testRepository2]) + def actualRepositories = [] + task = new TestTask() { + @Override + protected void execute(final Repository repository) { + actualRepositories << repository + } + } + task.install(repositoryManager, new GroupType()) + task.configure(configuration) + + task.execute() + assert [testRepository1, testRepository2] == actualRepositories + } + + /** + * Verify that task is executed for repositories that satisfy filter (appliesTo). + */ + @Test + void 'task is executed for filtered repositories'() { + configuration = new TaskConfiguration(id: 'test', typeId: 'test') + configuration.setString(RepositoryTaskSupport.REPOSITORY_NAME_FIELD_ID, '*') + def testRepository1 = mock(Repository) + def testRepository2 = mock(Repository) + when(repositoryManager.browse()).thenReturn([testRepository1, testRepository2]) + def actualRepositories = [] + task = new TestTask() { + @Override + protected void execute(final Repository repository) { + actualRepositories << repository + } + + @Override + protected boolean appliesTo(final Repository repository) { + return repository != testRepository1 + } + } + task.install(repositoryManager, new GroupType()) + task.configure(configuration) + + task.execute() + assert [testRepository2] == actualRepositories + } + + /** + * Verify that task is executed for all repositories regardless exception. + */ + @Test + void 'task is executed for all repositories regardless exception'() { + configuration = new TaskConfiguration(id: 'test', typeId: 'test') + configuration.setString(RepositoryTaskSupport.REPOSITORY_NAME_FIELD_ID, '*') + def testRepository1 = mock(Repository) + def testRepository2 = mock(Repository) + when(repositoryManager.browse()).thenReturn([testRepository1, testRepository2]) + def actualRepositories = [] + task = new TestTask() { + @Override + protected void execute(final Repository repository) { + actualRepositories << repository + if (testRepository1 == repository) { + throw new Exception() + } + } + } + task.install(repositoryManager, new GroupType()) + task.configure(configuration) + + thrown.expect(MultipleFailuresException) + task.execute() + assert [testRepository1, testRepository2] == actualRepositories + } + + /** + * Verify that task stops execution once cancelled. + */ + @Test + void 'task stops if cancelled'() { + configuration = new TaskConfiguration(id: 'test', typeId: 'test') + configuration.setString(RepositoryTaskSupport.REPOSITORY_NAME_FIELD_ID, '*') + def testRepository1 = mock(Repository) + def testRepository2 = mock(Repository) + when(repositoryManager.browse()).thenReturn([testRepository1, testRepository2]) + def actualRepositories = [] + task = new TestTask() { + @Override + protected void execute(final Repository repository) { + actualRepositories << repository + if (testRepository1 == repository) { + cancel() + } + } + } + task.install(repositoryManager, new GroupType()) + task.configure(configuration) + + task.execute() + assert [testRepository1] == actualRepositories + } + + private class TestTask + extends RepositoryTaskSupport + { + + @Override + protected void execute(final Repository repository) { + + } + + @Override + protected boolean appliesTo(final Repository repository) { + return true + } + + @Override + String getMessage() { + return 'Test task' + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadCountContributedHandlerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadCountContributedHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8a7a4786a8e4139cfd92909e40fa03f9819e4251 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadCountContributedHandlerTest.java @@ -0,0 +1,136 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.collect.AttributesMap; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.assetdownloadcount.internal.AssetDownloadCountContributedHandler; +import org.sonatype.nexus.repository.http.HttpMethods; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.repository.view.Response; +import org.sonatype.nexus.repository.view.Status; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class AssetDownloadCountContributedHandlerTest + extends TestSupport +{ + @Mock + private AssetDownloadCountStore assetDownloadCountStore; + + @Mock + private Repository repository; + + @Mock + private Request request; + + @Mock + private Response response; + + @Mock + private Context context; + + @Mock + private Content content; + + @Mock + private AttributesMap attributesMap; + + @Mock + private Asset asset; + + private AssetDownloadCountContributedHandler underTest; + + @Before + public void setUp() throws Exception { + when(context.getRepository()).thenReturn(repository); + when(context.getRequest()).thenReturn(request); + when(context.proceed()).thenReturn(response); + + when(request.getAction()).thenReturn(HttpMethods.GET); + + when(response.getStatus()).thenReturn(Status.success(200)); + when(response.getPayload()).thenReturn(content); + + when(repository.getName()).thenReturn("myreponame"); + + when(content.getAttributes()).thenReturn(attributesMap); + + when(attributesMap.get(Asset.class)).thenReturn(asset); + + when(asset.name()).thenReturn("myassetname"); + + when(assetDownloadCountStore.isEnabled()).thenReturn(true); + + underTest = new AssetDownloadCountContributedHandler(assetDownloadCountStore); + } + + @Test + public void testHandle_nullReponse() throws Exception { + when(context.proceed()).thenReturn(null); + + underTest.handle(context); + + verify(assetDownloadCountStore).isEnabled(); + verifyNoMoreInteractions(assetDownloadCountStore); + } + + @Test + public void testHandle_unsuccessfulReponse() throws Exception { + when(response.getStatus()).thenReturn(Status.failure(404)); + + underTest.handle(context); + + verify(assetDownloadCountStore).isEnabled(); + verifyNoMoreInteractions(assetDownloadCountStore); + } + + @Test + public void testHandle_invalidRequestAction() throws Exception { + when(request.getAction()).thenReturn(HttpMethods.DELETE); + + underTest.handle(context); + + verify(assetDownloadCountStore).isEnabled(); + verifyNoMoreInteractions(assetDownloadCountStore); + } + + @Test + public void testHandle_countIncremented() throws Exception { + underTest.handle(context); + underTest.handle(context); + underTest.handle(context); + + verify(assetDownloadCountStore, times(3)).incrementCount("myreponame", "myassetname"); + } + + @Test + public void testHandle_disabled() throws Exception { + when(assetDownloadCountStore.isEnabled()).thenReturn(false); + + underTest.handle(context); + verify(assetDownloadCountStore).isEnabled(); + verifyNoMoreInteractions(assetDownloadCountStore); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadCountEntityAdapterTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadCountEntityAdapterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fdba9e858f039b03e02e02d1481be07f739877d6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadCountEntityAdapterTest.java @@ -0,0 +1,420 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule; +import org.sonatype.nexus.repository.assetdownloadcount.internal.AssetDownloadCount; +import org.sonatype.nexus.repository.assetdownloadcount.internal.AssetDownloadCountEntityAdapter; +import org.sonatype.nexus.repository.assetdownloadcount.internal.DateUtils; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.metadata.schema.OSchema; +import org.joda.time.DateTime; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.Mockito.when; + +public class AssetDownloadCountEntityAdapterTest + extends TestSupport +{ + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory("test"); + + @Mock + private NodeAccess nodeAccess; + + private AssetDownloadCountEntityAdapter underTest; + + @Before + public void setUp() { + when(nodeAccess.getId()).thenReturn("nodeid"); + underTest = new AssetDownloadCountEntityAdapter(nodeAccess, 1000); + } + + @Test + public void testRegister() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + underTest.register(db); + OSchema schema = db.getMetadata().getSchema(); + assertThat(schema.getClass(underTest.getTypeName()), is(notNullValue())); + } + } + + @Test + public void testIncrementAndGet() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + underTest.register(db); + underTest.incrementCount(db, "myrepo", "myassetname", 10L); + assertThat(underTest.getCount(db, "myrepo", "myassetname"), is(10L)); + underTest.incrementCount(db, "myrepo", "myassetname", 10L); + assertThat(underTest.getCount(db, "myrepo", "myassetname"), is(20L)); + } + } + + @Test + public void testIncrementAndGetByDate() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + underTest.register(db); + underTest.incrementCount(db, "myrepo", "myassetname", 10L); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.DAY, new DateTime()), is(10L)); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.MONTH, new DateTime()), is(10L)); + underTest.incrementCount(db, "myrepo", "myassetname", 10L); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.DAY, new DateTime()), is(20L)); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.MONTH, new DateTime()), is(20L)); + } + } + + @Test + public void testIncrementAndGetByDate_differentRepositories() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + underTest.register(db); + underTest.incrementCount(db, "myrepo", "myassetname", 10L); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.DAY, new DateTime()), is(10L)); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.MONTH, new DateTime()), is(10L)); + underTest.incrementCount(db, "myrepo", "myassetname", 10L); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.DAY, new DateTime()), is(20L)); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.MONTH, new DateTime()), is(20L)); + underTest.incrementCount(db, "myrepo2", "myassetname", 10L); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.DAY, new DateTime()), is(20L)); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.MONTH, new DateTime()), is(20L)); + assertThat(underTest.getCount(db, "myrepo2", "myassetname", DateType.DAY, new DateTime()), is(10L)); + assertThat(underTest.getCount(db, "myrepo2", "myassetname", DateType.MONTH, new DateTime()), is(10L)); + } + } + + @Test + public void testIncrementAndGetByDate_differentAssets() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + underTest.register(db); + underTest.incrementCount(db, "myrepo", "myassetname", 10L); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.DAY, new DateTime()), is(10L)); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.MONTH, new DateTime()), is(10L)); + underTest.incrementCount(db, "myrepo", "myassetname", 10L); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.DAY, new DateTime()), is(20L)); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.MONTH, new DateTime()), is(20L)); + underTest.incrementCount(db, "myrepo", "myassetname2", 10L); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.DAY, new DateTime()), is(20L)); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.MONTH, new DateTime()), is(20L)); + assertThat(underTest.getCount(db, "myrepo", "myassetname2", DateType.DAY, new DateTime()), is(10L)); + assertThat(underTest.getCount(db, "myrepo", "myassetname2", DateType.MONTH, new DateTime()), is(10L)); + } + } + + @Test + public void testIncrementAndGetByDate_differentDates() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + underTest.register(db); + //need to set an older date, so have to manually insert the record + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withAssetName("myassetname") + .withNodeId("nodeid") + .withDateType(DateType.DAY) + .withDate(DateType.DAY.standardizeDate(new DateTime()).minusDays(10)) + .withCount(100)); + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withAssetName("myassetname") + .withNodeId("nodeid") + .withDateType(DateType.MONTH) + .withDate(DateType.DAY.standardizeDate(new DateTime()).minusDays(40)) + .withCount(200)); + underTest.incrementCount(db, "myrepo", "myassetname", 10L); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.DAY, new DateTime()), is(10L)); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.MONTH, new DateTime()), is(10L)); + } + } + + @Test + public void testIncrementAndGetByDate_differentNodeids() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + underTest.register(db); + //need to set a different nodeid, so have manually insert the record + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withAssetName("myassetname") + .withNodeId("nodeid2") + .withDateType(DateType.DAY) + .withDate(DateType.DAY.standardizeDate(new DateTime())) + .withCount(100)); + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withAssetName("myassetname") + .withNodeId("nodeid2") + .withDateType(DateType.MONTH) + .withDate(DateType.MONTH.standardizeDate(new DateTime())) + .withCount(200)); + underTest.incrementCount(db, "myrepo", "myassetname", 10L); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.DAY, new DateTime()), is(110L)); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.MONTH, new DateTime()), is(210L)); + } + } + + @Test + public void testGetForWholeRepository() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + underTest.register(db); + underTest.addEntity(db, + new AssetDownloadCount().withRepositoryName("myrepo").withNodeId("nodeid2").withDateType(DateType.MONTH) + .withDate(DateType.MONTH.standardizeDate(new DateTime())).withCount(200)); + assertThat(underTest.getCount(db, "myrepo", DateType.MONTH, new DateTime()), is(200L)); + } + } + + @Test + public void testGetForWholeRepositoryEntireHistory() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + underTest.register(db); + underTest.addEntity(db, + new AssetDownloadCount().withRepositoryName("myrepo").withNodeId("nodeid1").withDateType(DateType.DAY) + .withDate(DateType.DAY.standardizeDate(new DateTime().minusDays(10))).withCount(100)); + underTest.addEntity(db, + new AssetDownloadCount().withRepositoryName("myrepo").withNodeId("nodeid1").withDateType(DateType.MONTH) + .withDate(DateType.MONTH.standardizeDate(new DateTime().minusMonths(10))).withCount(200)); + underTest.addEntity(db, + new AssetDownloadCount().withRepositoryName("myrepo").withNodeId("nodeid1").withDateType(DateType.DAY) + .withDate(DateType.DAY.standardizeDate(new DateTime())).withCount(100)); + underTest.addEntity(db, + new AssetDownloadCount().withRepositoryName("myrepo").withNodeId("nodeid1").withDateType(DateType.MONTH) + .withDate(DateType.MONTH.standardizeDate(new DateTime())).withCount(200)); + underTest.addEntity(db, + new AssetDownloadCount().withRepositoryName("myrepo").withNodeId("nodeid2").withDateType(DateType.DAY) + .withDate(DateType.DAY.standardizeDate(new DateTime())).withCount(100)); + underTest.addEntity(db, + new AssetDownloadCount().withRepositoryName("myrepo").withNodeId("nodeid2").withDateType(DateType.MONTH) + .withDate(DateType.MONTH.standardizeDate(new DateTime())).withCount(200)); + + long[] dayCounts = underTest.getCounts(db, "myrepo", DateType.DAY); + long[] monthCounts = underTest.getCounts(db, "myrepo", DateType.MONTH); + + assertThat(dayCounts.length, is(30)); + assertThat(monthCounts.length, is(14)); + + assertThat(dayCounts, + is(new long[]{200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})); + assertThat(monthCounts, is(new long[]{400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0})); + } + } + + @Test + public void testGetCounts() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + underTest.register(db); + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withAssetName("myassetname") + .withNodeId("nodeid1") + .withDateType(DateType.DAY) + .withDate(DateType.DAY.standardizeDate(new DateTime().minusDays(10))) + .withCount(100)); + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withAssetName("myassetname") + .withNodeId("nodeid1") + .withDateType(DateType.MONTH) + .withDate(DateType.MONTH.standardizeDate(new DateTime().minusMonths(10))) + .withCount(200)); + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withAssetName("myassetname") + .withNodeId("nodeid1") + .withDateType(DateType.DAY) + .withDate(DateType.DAY.standardizeDate(new DateTime())) + .withCount(100)); + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withAssetName("myassetname") + .withNodeId("nodeid1") + .withDateType(DateType.MONTH) + .withDate(DateType.MONTH.standardizeDate(new DateTime())) + .withCount(200)); + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withAssetName("myassetname") + .withNodeId("nodeid2") + .withDateType(DateType.DAY) + .withDate(DateType.DAY.standardizeDate(new DateTime())) + .withCount(100)); + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withAssetName("myassetname") + .withNodeId("nodeid2") + .withDateType(DateType.MONTH) + .withDate(DateType.MONTH.standardizeDate(new DateTime())) + .withCount(200)); + + long[] dayCounts = underTest.getCounts(db, "myrepo", "myassetname", DateType.DAY); + long[] monthCounts = underTest.getCounts(db, "myrepo", "myassetname", DateType.MONTH); + + assertThat(dayCounts.length, is(30)); + assertThat(monthCounts.length, is(14)); + + assertThat(dayCounts, + is(new long[]{200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})); + assertThat(monthCounts, is(new long[]{400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0})); + } + } + + @Test + public void testGetCounts_noData() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + underTest.register(db); + + long[] dayCounts = underTest.getCounts(db, "myrepo", "myassetname", DateType.DAY); + long[] monthCounts = underTest.getCounts(db, "myrepo", "myassetname", DateType.MONTH); + + assertThat(dayCounts.length, is(30)); + assertThat(monthCounts.length, is(14)); + + assertThat(dayCounts, + is(new long[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})); + assertThat(monthCounts, is(new long[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})); + } + } + + @Test + public void testRemoveOldRecords() { + DateTime dateTime = new DateTime(); + + List dates = new ArrayList<>(); + dates.add(dateTime.minusYears(10)); + dates.add(dateTime.minusYears(2)); + dates.add(dateTime.minusYears(1)); + dates.add(dateTime.minusMonths(6)); + dates.add(dateTime.minusDays(31)); + dates.add(dateTime.minusDays(30)); + dates.add(dateTime.minusDays(10)); + dates.add(dateTime.minusDays(1)); + dates.add(dateTime); + + Set monthsAdded = new HashSet<>(); + + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + underTest.register(db); + for (DateTime date : dates) { + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withAssetName("myassetname") + .withNodeId("nodeid") + .withDateType(DateType.DAY) + .withDate(DateType.DAY.standardizeDate(date)) + .withCount(100)); + DateTime clearedDate = DateUtils.clearDayAndTime(date); + if (!monthsAdded.contains(clearedDate)) { + monthsAdded.add(clearedDate); + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withAssetName("myassetname") + .withNodeId("nodeid") + .withDateType(DateType.MONTH) + .withDate(DateType.MONTH.standardizeDate(date)) + .withCount(100)); + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withNodeId("nodeid") + .withDateType(DateType.MONTH_WHOLE_REPO) + .withDate(DateType.MONTH_WHOLE_REPO.standardizeDate(date)) + .withCount(100)); + underTest.addEntity(db, + new AssetDownloadCount() + .withRepositoryName("myrepo") + .withNodeId("nodeid") + .withDateType(DateType.MONTH_WHOLE_REPO_VULNERABLE) + .withDate(DateType.MONTH_WHOLE_REPO_VULNERABLE.standardizeDate(date)) + .withCount(100)); + } + } + + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.DAY), is(dates.size() * 100L)); + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.MONTH), is(monthsAdded.size() * 100L)); + long total = 0; + for (DateTime monthAdded : monthsAdded) { + total += underTest.getCount(db, "myrepo", DateType.MONTH_WHOLE_REPO, monthAdded); + } + assertThat(total, is(monthsAdded.size() * 100L)); + total = 0; + for (DateTime monthAdded : monthsAdded) { + total += underTest.getCount(db, "myrepo", DateType.MONTH_WHOLE_REPO_VULNERABLE, monthAdded); + } + assertThat(total, is(monthsAdded.size() * 100L)); + + underTest.removeOldRecords(db, DateType.DAY); + underTest.removeOldRecords(db, DateType.MONTH); + underTest.removeOldRecords(db, DateType.MONTH_WHOLE_REPO); + underTest.removeOldRecords(db, DateType.MONTH_WHOLE_REPO_VULNERABLE); + + //5 records are older than 30 days, so will get dumped + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.DAY), is((dates.size() - 5) * 100L)); + //2 records are older than 1 year, so will get dumped + assertThat(underTest.getCount(db, "myrepo", "myassetname", DateType.MONTH), is((monthsAdded.size() - 2) * 100L)); + total = 0; + for (DateTime monthAdded : monthsAdded) { + total += underTest.getCount(db, "myrepo", DateType.MONTH_WHOLE_REPO, monthAdded); + } + assertThat(total, is((monthsAdded.size() - 2) * 100L)); + total = 0; + for (DateTime monthAdded : monthsAdded) { + total += underTest.getCount(db, "myrepo", DateType.MONTH_WHOLE_REPO_VULNERABLE, monthAdded); + } + assertThat(total, is((monthsAdded.size() - 2) * 100L)); + } + } + + @Test + public void testSetCount() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + underTest.register(db); + + underTest.setCount(db, "myrepo", DateType.MONTH_WHOLE_REPO_VULNERABLE, DateTime.now(), 50L); + + assertThat(underTest.getCount(db, "myrepo", DateType.MONTH_WHOLE_REPO_VULNERABLE, DateTime.now()), is(50L)); + + underTest.setCount(db, "myrepo", DateType.MONTH_WHOLE_REPO_VULNERABLE, DateTime.now(), 100L); + + assertThat(underTest.getCount(db, "myrepo", DateType.MONTH_WHOLE_REPO_VULNERABLE, DateTime.now()), is(100L)); + + underTest.setCount(db, "myrepo", DateType.MONTH_WHOLE_REPO, DateTime.now(), 50L); + + assertThat(underTest.getCount(db, "myrepo", DateType.MONTH_WHOLE_REPO, DateTime.now()), is(50L)); + + underTest.setCount(db, "myrepo", DateType.MONTH_WHOLE_REPO, DateTime.now(), 100L); + + assertThat(underTest.getCount(db, "myrepo", DateType.MONTH_WHOLE_REPO, DateTime.now()), is(100L)); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadCountStoreImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadCountStoreImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d05c01dae939f7e8ab7b96e3d89d0d6528cb368e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadCountStoreImplTest.java @@ -0,0 +1,218 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.orient.freeze.DatabaseFreezeChangeEvent; +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule; +import org.sonatype.nexus.repository.assetdownloadcount.internal.AssetDownloadCountEntityAdapter; +import org.sonatype.nexus.repository.assetdownloadcount.internal.AssetDownloadCountStoreImpl; +import org.sonatype.nexus.repository.assetdownloadcount.internal.AssetDownloadHistoricDataCleaner; +import org.sonatype.nexus.repository.assetdownloadcount.internal.CacheRemovalListener; + +import org.apache.shiro.subject.Subject; +import org.apache.shiro.subject.support.SubjectRunnable; +import org.apache.shiro.util.ThreadContext; +import org.joda.time.DateTime; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class AssetDownloadCountStoreImplTest + extends TestSupport +{ + private static final long[] DAILY_COUNTS = new long[] { 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L, 14L, + 15L, 16L, 17L, 18L, 19L, 20L, 21L, 22L, 23L, 24L, 25L, 26L, 27L, 28L, 29L, 30L }; + + private static final long[] MONTHLY_COUNTS = new long[] { 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L, + 14L }; + + private static final String REPO_NAME = "reponame"; + + private static final String ASSET_NAME = "assetname"; + + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory("test"); + + @Mock + private AssetDownloadCountEntityAdapter assetDownloadCountEntityAdapter; + + @Mock + private AssetDownloadHistoricDataCleaner dataCleaner; + + @Mock + private Subject subject; + + private CacheRemovalListener cacheRemovalListener; + + private AssetDownloadCountStoreImpl underTest; + + @Before + public void setUp() throws Exception { + when(subject.getPrincipal()).thenReturn("admin"); + when(subject.associateWith(any(Runnable.class))) + .thenAnswer(invoc -> new SubjectRunnable(subject, (Runnable) invoc.getArguments()[0])); + ThreadContext.bind(subject); + + cacheRemovalListener = new CacheRemovalListener(database.getInstanceProvider(), assetDownloadCountEntityAdapter); + underTest = new AssetDownloadCountStoreImpl(database.getInstanceProvider(), true, 10, 10, + assetDownloadCountEntityAdapter, dataCleaner, cacheRemovalListener); + underTest.start(); + } + + @After + public void cleanup() { + ThreadContext.unbindSubject(); + } + + @Test + public void testGetDailyCount() { + DateTime date = DateTime.now(); + when(assetDownloadCountEntityAdapter.getCount(any(), eq(REPO_NAME), eq(ASSET_NAME), eq(DateType.DAY), eq(date))) + .thenReturn(100L); + assertThat(underTest.getDailyCount(REPO_NAME, ASSET_NAME, date), is(100L)); + } + + @Test + public void testGetMonthlyCount() { + DateTime date = DateTime.now(); + when(assetDownloadCountEntityAdapter.getCount(any(), eq(REPO_NAME), eq(ASSET_NAME), eq(DateType.MONTH), eq(date))) + .thenReturn(100L); + assertThat(underTest.getMonthlyCount(REPO_NAME, ASSET_NAME, date), is(100L)); + } + + @Test + public void testGetDailyCounts() { + DateTime date = DateTime.now(); + when(assetDownloadCountEntityAdapter.getCounts(any(), eq(REPO_NAME), eq(ASSET_NAME), eq(DateType.DAY))) + .thenReturn(DAILY_COUNTS); + assertThat(underTest.getDailyCounts(REPO_NAME, ASSET_NAME), is(DAILY_COUNTS)); + } + + @Test + public void testGetMonthlyCounts() { + DateTime date = DateTime.now(); + when(assetDownloadCountEntityAdapter.getCounts(any(), eq(REPO_NAME), eq(ASSET_NAME), eq(DateType.MONTH))) + .thenReturn(MONTHLY_COUNTS); + assertThat(underTest.getMonthlyCounts(REPO_NAME, ASSET_NAME), is(MONTHLY_COUNTS)); + } + + @Test + public void testIncrementCount() throws Exception { + underTest = new AssetDownloadCountStoreImpl(database.getInstanceProvider(), true, 1, 1, + assetDownloadCountEntityAdapter, dataCleaner, cacheRemovalListener); + underTest.incrementCount(REPO_NAME, ASSET_NAME); + underTest.incrementCount(REPO_NAME, ASSET_NAME + 1); + Thread.sleep(100); + verify(assetDownloadCountEntityAdapter).incrementCount(any(), eq(REPO_NAME), eq(ASSET_NAME), eq(1L)); + } + + @Test + public void testIncrementCount_validateCaching() throws Exception { + underTest = new AssetDownloadCountStoreImpl(database.getInstanceProvider(), true, 10, 1, + assetDownloadCountEntityAdapter, dataCleaner, cacheRemovalListener); + + //increment 10 items, that will fill the queue, but not overflow it + for (int i = 0 ; i < 10 ; i++) { + underTest.incrementCount(REPO_NAME, ASSET_NAME + i); + } + + Thread.sleep(100); + + //should be nothing on disk yet + verify(assetDownloadCountEntityAdapter, never()).incrementCount(any(), anyString(), anyString(), anyLong()); + + //increment 4 more times + for (int i = 0 ; i < 4 ; i++) { + underTest.incrementCount(REPO_NAME, ASSET_NAME + (10 + i)); + } + + Thread.sleep(100); + + //should have stuffed 4 things in db + for (int i = 0 ; i < 4 ; i++) { + verify(assetDownloadCountEntityAdapter).incrementCount(any(), eq(REPO_NAME), eq(ASSET_NAME + i), eq(1L)); + } + } + + @Test + public void testIncrementCount_validateCacheDrops_dbFrozen() throws Exception { + underTest = new AssetDownloadCountStoreImpl(database.getInstanceProvider(), true, 1, 1, + assetDownloadCountEntityAdapter, dataCleaner, cacheRemovalListener); + + cacheRemovalListener.onDatabaseFreezeChangeEvent(new DatabaseFreezeChangeEvent(true)); + + underTest.incrementCount(REPO_NAME, ASSET_NAME + "a"); + underTest.incrementCount(REPO_NAME, ASSET_NAME + "b"); + + Thread.sleep(100); + + // should be nothing on disk + verify(assetDownloadCountEntityAdapter, never()).incrementCount(any(), anyString(), anyString(), anyLong()); + } + + @Test + public void testSetMonthlyVulnerableCount() { + DateTime date = DateTime.now(); + underTest.setMonthlyVulnerableCount(REPO_NAME, date, 50L); + verify(assetDownloadCountEntityAdapter) + .setCount(any(), eq(REPO_NAME), eq(DateType.MONTH_WHOLE_REPO_VULNERABLE), eq(date), eq(50L)); + } + + @Test + public void testSetMonthlyCount() { + DateTime date = DateTime.now(); + underTest.setMonthlyCount(REPO_NAME, date, 50L); + verify(assetDownloadCountEntityAdapter) + .setCount(any(), eq(REPO_NAME), eq(DateType.MONTH_WHOLE_REPO), eq(date), eq(50L)); + } + + @Test + public void testGetMonthlyVulnerableCountsAllTime() { + when(assetDownloadCountEntityAdapter.getCounts(any(), eq(REPO_NAME), eq(DateType.MONTH_WHOLE_REPO_VULNERABLE))) + .thenReturn(MONTHLY_COUNTS); + assertThat(underTest.getMonthlyVulnerableCounts(REPO_NAME), is(MONTHLY_COUNTS)); + } + + @Test + public void testGetMonthlyCountsAllTime() { + when(assetDownloadCountEntityAdapter.getCounts(any(), eq(REPO_NAME), eq(DateType.MONTH_WHOLE_REPO))) + .thenReturn(MONTHLY_COUNTS); + assertThat(underTest.getMonthlyCounts(REPO_NAME), is(MONTHLY_COUNTS)); + } + + @Test + public void testGetLastThirtyDays() { + when(assetDownloadCountEntityAdapter.getCounts(any(), eq(REPO_NAME), eq(ASSET_NAME), eq(DateType.DAY))).thenReturn( + new long[] { 100L, 0L }); + + long count = underTest.getLastThirtyDays(REPO_NAME, ASSET_NAME); + assertThat(count, is(100L)); + + underTest.incrementCount(REPO_NAME, ASSET_NAME); + + count = underTest.getLastThirtyDays(REPO_NAME, ASSET_NAME); + assertThat(count, is(101L)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadHistoricDataCleanerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadHistoricDataCleanerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3ee9c87a4bad3e20a906c49c4e369190ddf2049c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/AssetDownloadHistoricDataCleanerTest.java @@ -0,0 +1,126 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule; +import org.sonatype.nexus.repository.assetdownloadcount.internal.AssetDownloadCountEntityAdapter; +import org.sonatype.nexus.repository.assetdownloadcount.internal.AssetDownloadHistoricDataCleaner; + +import org.apache.shiro.subject.Subject; +import org.apache.shiro.subject.support.SubjectRunnable; +import org.apache.shiro.util.ThreadContext; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class AssetDownloadHistoricDataCleanerTest + extends TestSupport +{ + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory("test"); + + @Mock + private AssetDownloadCountEntityAdapter assetDownloadCountEntityAdapter; + + @Mock + private Subject subject; + + private AssetDownloadHistoricDataCleaner underTest; + + @Before + public void setUp() throws Exception { + when(subject.getPrincipal()).thenReturn("admin"); + when(subject.associateWith(any(Runnable.class))) + .thenAnswer(invoc -> new SubjectRunnable(subject, (Runnable) invoc.getArguments()[0])); + ThreadContext.bind(subject); + + when(assetDownloadCountEntityAdapter.getMaxDeleteSize()).thenReturn(1000); + + underTest = new AssetDownloadHistoricDataCleaner(database.getInstanceProvider(), assetDownloadCountEntityAdapter, + 5000); + } + + @After + public void cleanup() { + ThreadContext.unbindSubject(); + underTest.stop(); + } + + @Test + public void testStart() throws Exception { + startAndWait(); + assertThat(underTest.isRunning(), is(true)); + verify(assetDownloadCountEntityAdapter).removeOldRecords(any(), eq(DateType.DAY)); + verify(assetDownloadCountEntityAdapter).removeOldRecords(any(), eq(DateType.MONTH)); + verify(assetDownloadCountEntityAdapter).removeOldRecords(any(), eq(DateType.MONTH_WHOLE_REPO)); + verify(assetDownloadCountEntityAdapter).removeOldRecords(any(), eq(DateType.MONTH_WHOLE_REPO_VULNERABLE)); + } + + @Test + public void testRestart() throws Exception { + when(assetDownloadCountEntityAdapter.removeOldRecords(any(), any())).thenThrow(new RuntimeException("doht")); + startAndWait(); + assertThat(underTest.isRunning(), is(false)); + reset(assetDownloadCountEntityAdapter); + when(assetDownloadCountEntityAdapter.getMaxDeleteSize()).thenReturn(1000); + startAndWait(); + assertThat(underTest.isRunning(), is(true)); + verify(assetDownloadCountEntityAdapter).removeOldRecords(any(), eq(DateType.DAY)); + } + + @Test + public void testMultipleDeleteRequests() throws Exception { + for (DateType dateType : DateType.values()) { + when(assetDownloadCountEntityAdapter.removeOldRecords(any(), eq(dateType))).thenReturn(1000).thenReturn(1000) + .thenReturn(0); + } + + startAndWait(); + assertThat(underTest.isRunning(), is(true)); + verify(assetDownloadCountEntityAdapter, times(3)).removeOldRecords(any(), eq(DateType.DAY)); + verify(assetDownloadCountEntityAdapter, times(3)).removeOldRecords(any(), + eq(DateType.MONTH)); + verify(assetDownloadCountEntityAdapter, times(3)).removeOldRecords(any(), + eq(DateType.MONTH_WHOLE_REPO)); + verify(assetDownloadCountEntityAdapter, times(3)).removeOldRecords(any(), + eq(DateType.MONTH_WHOLE_REPO_VULNERABLE)); + } + + @Test + public void testMaxOfZeroDoesntGoInfinite() throws Exception { + when(assetDownloadCountEntityAdapter.getMaxDeleteSize()).thenReturn(0); + startAndWait(); + assertThat(underTest.isRunning(), is(true)); + //just make sure each is only called once + verify(assetDownloadCountEntityAdapter).removeOldRecords(any(), eq(DateType.DAY)); + verify(assetDownloadCountEntityAdapter).removeOldRecords(any(), eq(DateType.MONTH)); + verify(assetDownloadCountEntityAdapter).removeOldRecords(any(), eq(DateType.MONTH_WHOLE_REPO)); + verify(assetDownloadCountEntityAdapter).removeOldRecords(any(), eq(DateType.MONTH_WHOLE_REPO_VULNERABLE)); + } + + private void startAndWait() throws Exception { + underTest.start(); + Thread.sleep(100); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/ComponentDatabaseUpgrade_1_6_Test.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/ComponentDatabaseUpgrade_1_6_Test.java new file mode 100644 index 0000000000000000000000000000000000000000..dfd58f41fb0217f8c6704e6f845f14dff3e6b522 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/assetdownloadcount/ComponentDatabaseUpgrade_1_6_Test.java @@ -0,0 +1,85 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.assetdownloadcount; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.orient.OClassNameBuilder; +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule; +import org.sonatype.nexus.repository.assetdownloadcount.internal.ComponentDatabaseUpgrade_1_6; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OSchema; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.core.IsNull.nullValue; +import static org.junit.Assert.assertThat; + +public class ComponentDatabaseUpgrade_1_6_Test + extends TestSupport +{ + static final String DB_CLASS = new OClassNameBuilder() + .type("assetdownloadcount") + .build(); + + public static final String P_ASSET_NAME = "asset_name"; + + @Rule + public DatabaseInstanceRule componentDatabase = DatabaseInstanceRule.inMemory("test_component"); + + private ComponentDatabaseUpgrade_1_6 underTest; + + @Before + public void setUp() { + underTest = new ComponentDatabaseUpgrade_1_6(componentDatabase.getInstanceProvider()); + try (ODatabaseDocumentTx db = componentDatabase.getInstance().connect()) { + OSchema schema = db.getMetadata().getSchema(); + OClass componentType = schema.createClass(DB_CLASS); + componentType.createProperty(P_ASSET_NAME, OType.STRING); + + for (int i = 0 ; i < 1000 ; i ++) { + ODocument document = db.newInstance(DB_CLASS); + document.save(); + } + } + } + + @Test + public void testAssetNamesUpdated() throws Exception { + try (ODatabaseDocumentTx db = componentDatabase.getInstance().connect()) { + Iterable documents = db.browseClass(DB_CLASS); + for (ODocument document : documents) { + assertThat(document.field(P_ASSET_NAME), nullValue()); + } + } + + underTest.apply(); + + try (ODatabaseDocumentTx db = componentDatabase.getInstance().connect()) { + Iterable documents = db.browseClass(DB_CLASS); + for (ODocument document : documents) { + assertThat(document.field(P_ASSET_NAME), is("")); + } + } + } + + @Test + public void testWithoutExistingDb() throws Exception { + underTest.apply(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/AssetPathBrowseNodeGeneratorTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/AssetPathBrowseNodeGeneratorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e2aec025e6696c481920e52026db896ce5d2046b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/AssetPathBrowseNodeGeneratorTest.java @@ -0,0 +1,77 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse; + +import java.util.List; + +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.DefaultComponent; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class AssetPathBrowseNodeGeneratorTest +{ + private BrowseNodeGenerator generator = new AssetPathBrowseNodeGenerator() + { + }; + + @Test + public void computeAssetPathNoComponent() { + Asset asset = createAsset("asset/path/foo"); + + List path = generator.computeAssetPath(asset, null); + + assertThat(path, contains("asset", "path", "foo")); + } + + @Test + public void computeAssetPathWithComponent() { + Component component = createComponent("component", "group", "1.0.0"); + Asset asset = createAsset("asset/path/foo"); + + List path = generator.computeAssetPath(asset, component); + + assertThat(path, contains("asset", "path", "foo")); + } + + @Test + public void computeComponentPathWithComponent() { + Component component = createComponent("component", "group", "1.0.0"); + Asset asset = createAsset("asset/path/foo"); + + List path = generator.computeComponentPath(asset, component); + + assertThat(path, contains("asset", "path", "foo")); + } + + private Asset createAsset(final String assetName) { + Asset asset = mock(Asset.class); + when(asset.name()).thenReturn(assetName); + return asset; + } + + private Component createComponent(final String name, final String group, final String version) { + Component component = new DefaultComponent(); + component.name(name); + component.group(group); + component.version(version); + + return component; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/BrowseResultTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/BrowseResultTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f1a0f68ce2dd4c55ce34610814cd37297450bbd6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/BrowseResultTest.java @@ -0,0 +1,63 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse; + +import java.util.List; + +import org.sonatype.nexus.repository.storage.Component; + +import com.google.common.collect.Lists; +import org.junit.Test; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; + +public class BrowseResultTest +{ + private final List components = Lists.newArrayList( + mock(Component.class), + mock(Component.class), + mock(Component.class) + ); + + @Test + public void testEstimateCount_NoStartOrLimit() { + // no start or limit, expected is just the count of results + runTest(null, null, 3); + } + + @Test + public void testEstimateCount_LessResultsThanPageSize() { + // start at 0 with a limit of 2, expected is the count of results (i.e. no further page expected) + runTest(0, 2, 3); + } + + @Test + public void testEstimateCount_SameResultsAsPageSize() { + // start at 0 with a limit of 3 (same as results count), expect another page + runTest(0, 3, 6); + } + + @Test + public void testEstimateCount_WithSkipExpectThirdPage() { + // start at 3 (2nd page of skip/limit style paging) with a limit of 3 (same as results count), expect a 3rd page of results + runTest(3, 3, 9); + } + + private void runTest(final Integer start, final Integer limit, final long expected) { + QueryOptions queryOptions = new QueryOptions(null, null, null, start, limit, null); + BrowseResult result = new BrowseResult<>(queryOptions, components); + assertThat(result.getTotal(), equalTo(expected)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/ComponentPathBrowseNodeGeneratorTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/ComponentPathBrowseNodeGeneratorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ec0ac8f1e14f1506671bc871f658b18baa9b6020 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/ComponentPathBrowseNodeGeneratorTest.java @@ -0,0 +1,77 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse; + +import java.util.List; + +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.DefaultComponent; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ComponentPathBrowseNodeGeneratorTest +{ + private BrowseNodeGenerator generator = new ComponentPathBrowseNodeGenerator() + { + }; + + @Test + public void computeAssetPathNoComponent() { + Asset asset = createAsset("asset/path/foo"); + + List path = generator.computeAssetPath(asset, null); + + assertThat(path, contains("asset", "path", "foo")); + } + + @Test + public void computeAssetPathWithComponent() { + Component component = createComponent("component", "group", "1.0.0"); + Asset asset = createAsset("asset/path/foo"); + + List path = generator.computeAssetPath(asset, component); + + assertThat(path, contains("asset", "path", "foo")); + } + + @Test + public void computeComponentPathWithComponent() { + Component component = createComponent("component", "group", "1.0.0"); + Asset asset = createAsset("asset/path/foo"); + + List path = generator.computeComponentPath(asset, component); + + assertThat(path, contains("asset", "path")); + } + + private Asset createAsset(final String assetName) { + Asset asset = mock(Asset.class); + when(asset.name()).thenReturn(assetName); + return asset; + } + + private Component createComponent(final String name, final String group, final String version) { + Component component = new DefaultComponent(); + component.name(name); + component.group(group); + component.version(version); + + return component; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/AssetWhereClauseBuilderTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/AssetWhereClauseBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c9c71e751e314e1893e3d0011ba85e18a46c0492 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/AssetWhereClauseBuilderTest.java @@ -0,0 +1,57 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Test; + +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; +import static org.sonatype.nexus.repository.browse.internal.AssetWhereClauseBuilder.whereClause; + +public class AssetWhereClauseBuilderTest + extends TestSupport +{ + @Test + public void content() throws Exception { + assertThat(whereClause("content", false), is(equalTo("content"))); + } + + @Test + public void contentAndFilter() throws Exception { + assertThat(whereClause("content", true), is(equalTo("content AND name LIKE :nameFilter"))); + } + + @Test + public void contentAndLastId() { + assertThat(whereClause("content", true, true), is(equalTo("content AND name LIKE :nameFilter AND @RID > :rid"))); + } + + @Test + public void noContent() { + assertThat(whereClause(null, false), is(nullValue())); + } + + @Test + public void noContentAndFilter() { + assertThat(whereClause("", true), is(equalTo("name LIKE :nameFilter"))); + } + + @Test + public void noContentFilterAndLastId() { + assertThat(whereClause(null, true, true), is(equalTo("name LIKE :nameFilter AND @RID > :rid"))); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseAssetsSqlBuilderTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseAssetsSqlBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f5f0576f8445df123fb16b72cd86ccea3a740fb5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseAssetsSqlBuilderTest.java @@ -0,0 +1,102 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.browse.QueryOptions; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static java.util.Collections.emptyList; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; + +public class BrowseAssetsSqlBuilderTest + extends TestSupport +{ + static final String CONTENT_AUTH_WHERE = "contentAuth(@this, :browsedRepository) == true"; + + static final String FILTER_WHERE = "name LIKE :nameFilter"; + + static final String LAST_ID_WHERE = "@RID > :rid"; + + @Mock + QueryOptions queryOptions; + + @Mock + AssetEntityAdapter assetEntityAdapter; + + BrowseAssetsSqlBuilder underTest; + + @Before + public void setup() throws Exception { + underTest = new BrowseAssetsSqlBuilder(assetEntityAdapter); + when(assetEntityAdapter.getTypeName()).thenReturn("asset"); + when(queryOptions.getContentAuth()).thenReturn(true); + } + + @Test + public void whereWithAuth() throws Exception { + String whereClause = underTest.buildWhereClause(emptyList(), queryOptions); + assertThat(whereClause, is(equalTo(CONTENT_AUTH_WHERE))); + } + + @Test + public void whereWithFilter() throws Exception { + when(queryOptions.getFilter()).thenReturn("filter"); + String whereClause = underTest.buildWhereClause(emptyList(), queryOptions); + assertThat(whereClause, is(equalTo(CONTENT_AUTH_WHERE + " AND " + FILTER_WHERE))); + } + + @Test + public void whereWithLastId() { + when(queryOptions.getLastId()).thenReturn("#45:1"); + String whereClause = underTest.buildWhereClause(emptyList(), queryOptions); + assertThat(whereClause, is(equalTo(CONTENT_AUTH_WHERE + " AND " + LAST_ID_WHERE))); + Map params = underTest.buildSqlParams("repository", queryOptions); + assertThat(params.get("browsedRepository"), is("repository")); + assertThat(params.get("rid"), is("#45:1")); + } + + @Test + public void whereWithNoContentAuth() { + when(queryOptions.getContentAuth()).thenReturn(false); + assertThat(underTest.buildWhereClause(emptyList(), queryOptions), is(nullValue())); + } + + @Test + public void whereWithNoConentAuthAndFilter() throws Exception { + when(queryOptions.getContentAuth()).thenReturn(false); + when(queryOptions.getFilter()).thenReturn("filter"); + assertThat(underTest.buildWhereClause(emptyList(), queryOptions), is(equalTo(FILTER_WHERE))); + } + + @Test + public void whereWithNoConentAuthAndFilterAndLastId() throws Exception { + when(queryOptions.getContentAuth()).thenReturn(false); + when(queryOptions.getFilter()).thenReturn("filter"); + when(queryOptions.getLastId()).thenReturn("#45:1"); + assertThat(underTest.buildWhereClause(emptyList(), queryOptions), is(equalTo(FILTER_WHERE + " AND " + LAST_ID_WHERE))); + Map params = underTest.buildSqlParams("repository", queryOptions); + assertThat(params.get("browsedRepository"), is(nullValue())); + assertThat(params.get("rid"), is("#45:1")); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseComponentsSqlBuilderTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseComponentsSqlBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a6a58a63f43255ba01ebdfb7b5412db70eb4e253 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseComponentsSqlBuilderTest.java @@ -0,0 +1,47 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.browse.QueryOptions; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static java.util.Collections.emptyList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BrowseComponentsSqlBuilderTest + extends TestSupport +{ + @Mock + ComponentEntityAdapter componentEntityAdapter; + + BrowseComponentsSqlBuilder underTest; + + @Before + public void setUp() { + underTest = new BrowseComponentsSqlBuilder(componentEntityAdapter); + when(componentEntityAdapter.getTypeName()).thenReturn("component"); + } + + @Test + public void buildQueryToReturnNothingWhenBucketsIsEmpty() throws Exception { + assertThat(underTest.buildBrowseSql(emptyList(), mock(QueryOptions.class)), is("")); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseNodeEventHandlerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseNodeEventHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..06c069de2dd897422c7b20f78aff019e3a546183 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseNodeEventHandlerTest.java @@ -0,0 +1,115 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; +import org.sonatype.nexus.repository.config.internal.ConfigurationDeletedEvent; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.AssetCreatedEvent; +import org.sonatype.nexus.repository.storage.AssetDeletedEvent; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class BrowseNodeEventHandlerTest + extends TestSupport +{ + private static final String REPOSITORY_NAME = "maven2repsoitory"; + + private BrowseNodeEventHandler handler; + + @Mock + private BrowseNodeManager browseNodeManager; + + @Before + public void setup() { + handler = new BrowseNodeEventHandler(new BrowseNodeConfiguration(), browseNodeManager); + } + + @Test + public void onAssetCreated() { + Asset asset = new Asset(); + + AssetCreatedEvent event = mock(AssetCreatedEvent.class); + when(event.getAsset()).thenReturn(asset); + when(event.getRepositoryName()).thenReturn(REPOSITORY_NAME); + when(event.isLocal()).thenReturn(true); + + handler.on(event); + + verify(browseNodeManager).createFromAsset(REPOSITORY_NAME, asset); + } + + @Test + public void onAssetCreated_remote() { + AssetCreatedEvent event = mock(AssetCreatedEvent.class); + when(event.isLocal()).thenReturn(false); + + handler.on(event); + + verify(browseNodeManager, never()).createFromAsset(any(), any()); + } + + @Test + public void onAssetDeleted() { + EntityId assetId = mock(EntityId.class); + + AssetDeletedEvent event = mock(AssetDeletedEvent.class); + when(event.getAssetId()).thenReturn(assetId); + when(event.isLocal()).thenReturn(true); + + handler.on(event); + + verify(browseNodeManager).deleteAssetNode(assetId); + } + + @Test + public void onAssetDeleted_remote() { + AssetDeletedEvent event = mock(AssetDeletedEvent.class); + when(event.isLocal()).thenReturn(false); + + handler.on(event); + + verify(browseNodeManager, never()).deleteAssetNode(any()); + } + + @Test + public void onConfigurationDeleted() { + ConfigurationDeletedEvent event = mock(ConfigurationDeletedEvent.class); + when(event.isLocal()).thenReturn(true); + when(event.getRepositoryName()).thenReturn(REPOSITORY_NAME); + + handler.on(event); + + verify(browseNodeManager).deleteByRepository(REPOSITORY_NAME); + } + + @Test + public void onConfigurationDeleted_remote() { + ConfigurationDeletedEvent event = mock(ConfigurationDeletedEvent.class); + when(event.isLocal()).thenReturn(false); + + handler.on(event); + + verify(browseNodeManager, never()).deleteByRepository(any()); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseNodeManagerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseNodeManagerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..db6398e15d0aea43e3fc1ef415d4e9fb6fd19e5d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseNodeManagerTest.java @@ -0,0 +1,210 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.entity.DetachedEntityId; +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.BrowseNodeGenerator; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.BrowseNodeStore; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.ComponentStore; +import org.sonatype.nexus.repository.storage.DefaultComponent; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class BrowseNodeManagerTest + extends TestSupport +{ + private static final String DEFAULT = "default"; + + private static final String MAVEN_2 = "maven2"; + + private static final String REPOSITORY_NAME = "repository"; + + private BrowseNodeManager manager; + + @Mock + private BrowseNodeStore browseNodeStore; + + @Mock + private ComponentStore componentStore; + + @Mock + private BrowseNodeGenerator maven2BrowseNodeGenerator; + + @Mock + private DefaultBrowseNodeGenerator defaultBrowseNodeGenerator; + + @Mock + private Repository repository; + + @Before + public void setup() { + Map generators = new HashMap<>(); + generators.put(DEFAULT, defaultBrowseNodeGenerator); + generators.put(MAVEN_2, maven2BrowseNodeGenerator); + + manager = new BrowseNodeManager(browseNodeStore, componentStore, generators); + + when(repository.getName()).thenReturn(REPOSITORY_NAME); + } + + @Test + public void createFromAssetSavesNodesForAssetWithoutComponent() { + List assetPath = asList("asset"); + Component component = null; + Asset asset = createAsset("asset", "assetId", "otherFormat", null); + + when(defaultBrowseNodeGenerator.computeAssetPath(asset, component)).thenReturn(assetPath); + when(defaultBrowseNodeGenerator.computeComponentPath(asset, component)).thenReturn(emptyList()); + + manager.createFromAsset(REPOSITORY_NAME, asset); + + verify(browseNodeStore).createAssetNode(REPOSITORY_NAME, asList(asset.name()), asset); + + verifyNoMoreInteractions(browseNodeStore); + } + + @Test + public void createFromAssetSavesNodesForFormatSpecificAssetWithComponent() { + List assetPath = asList("component", "asset"); + Component component = createComponent("component", null, null, "componentId"); + Asset asset = createAsset("asset", "assetId", MAVEN_2, EntityHelper.id(component)); + + when(maven2BrowseNodeGenerator.computeAssetPath(asset, component)).thenReturn(assetPath); + when(maven2BrowseNodeGenerator.computeComponentPath(asset, component)).thenReturn(assetPath.subList(0, 1)); + + manager.createFromAsset(REPOSITORY_NAME, asset); + + verify(browseNodeStore).createComponentNode(REPOSITORY_NAME, asList(component.name()), component); + verify(browseNodeStore).createAssetNode(REPOSITORY_NAME, asList(component.name(), asset.name()), asset); + + verifyNoMoreInteractions(browseNodeStore); + } + + @Test + public void createFromAssetsSavesNodesWithNoComponents() { + List assets = asList( + createAsset("assetName1", "assetId1", MAVEN_2, null), + createAsset("assetName2", "assetId2", MAVEN_2, null) + ); + + for (Asset asset : assets) { + String name = asset.name(); + when(maven2BrowseNodeGenerator.computeAssetPath(asset, null)).thenReturn(asList(name)); + } + + manager.createFromAssets(repository, assets); + + for (Asset asset : assets) { + verify(browseNodeStore).createAssetNode(REPOSITORY_NAME, asList(asset.name()), asset); + } + + verifyNoMoreInteractions(browseNodeStore); + } + + @Test + public void createFromAssetsSavesNodesWithComponents() { + List components = asList( + createComponent("componentName1", "componentGroup1", "componentVersion1", "componentId1"), + createComponent("componentName2", "componentGroup2", "componentVersion2", "componentId2") + ); + + List assets = asList( + createAsset("assetName1", "assetId1", MAVEN_2, EntityHelper.id(components.get(0))), + createAsset("assetName2", "assetId2", MAVEN_2, EntityHelper.id(components.get(1))) + ); + + for (int i = 0; i < assets.size(); i++) { + Asset asset = assets.get(i); + Component component = components.get(i); + String name = asset.name(); + when(maven2BrowseNodeGenerator.computeAssetPath(asset, component)).thenReturn( + asList(component.group(), component.name(), component.version(), name)); + when(maven2BrowseNodeGenerator.computeComponentPath(asset, component)).thenReturn(asList( + component.group(), component.name(), component.version())); + } + + manager.createFromAssets(repository, assets); + + for (int i = 0; i < assets.size(); i++) { + Asset asset = assets.get(i); + Component component = components.get(i); + verify(browseNodeStore).createComponentNode(REPOSITORY_NAME, + asList(component.group(), component.name(), component.version()), component); + verify(browseNodeStore).createAssetNode(REPOSITORY_NAME, + asList(component.group(), component.name(), component.version(), asset.name()), asset); + } + + verifyNoMoreInteractions(browseNodeStore); + } + + + private Asset createAsset(final String assetName, final String assetId, final String format, final EntityId componentId) { + EntityMetadata entityMetadata = mock(EntityMetadata.class); + when(entityMetadata.getId()).thenReturn(new DetachedEntityId(assetId)); + + Asset asset = mock(Asset.class); + when(asset.getEntityMetadata()).thenReturn(entityMetadata); + when(asset.name()).thenReturn(assetName); + when(asset.format()).thenReturn(format); + + if (componentId != null) { + when(asset.componentId()).thenReturn(componentId); + } + + Format fmt = mock(Format.class); + when(fmt.getValue()).thenReturn(format); + when(repository.getFormat()).thenReturn(fmt); + + return asset; + } + + private Component createComponent(final String name, + final String group, + final String version, + final String componentId) + { + EntityMetadata metadata = mock(EntityMetadata.class); + when(metadata.getId()).thenReturn(new DetachedEntityId(componentId)); + + Component component = new DefaultComponent(); + component.name(name); + component.group(group); + component.version(version); + component.setEntityMetadata(metadata); + + when(componentStore.read(EntityHelper.id(component))).thenReturn(component); + + return component; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseServiceImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseServiceImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..71de75490441118e0d2f927a9409f2ddb3b084ef --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/BrowseServiceImplTest.java @@ -0,0 +1,339 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.entity.DetachedEntityId; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.assetdownloadcount.AssetDownloadCountStore; +import org.sonatype.nexus.repository.browse.BrowseResult; +import org.sonatype.nexus.repository.browse.QueryOptions; +import org.sonatype.nexus.repository.group.GroupFacet; +import org.sonatype.nexus.repository.security.ContentPermissionChecker; +import org.sonatype.nexus.repository.security.VariableResolverAdapterManager; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.BucketStore; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.repository.storage.StorageTx; +import org.sonatype.nexus.repository.types.GroupType; + +import com.google.common.base.Supplier; +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.record.impl.ODocument; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; + +import static java.util.Arrays.asList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertNull; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + + +public class BrowseServiceImplTest + extends TestSupport +{ + @Mock + ComponentEntityAdapter componentEntityAdapter; + + @Mock + Component componentOne; + + @Mock + ODocument componentOneDoc; + + @Mock + ORID componentOneORID; + + @Mock + VariableResolverAdapterManager variableResolverAdapterManager; + + @Mock + ContentPermissionChecker contentPermissionChecker; + + @Mock + Repository mavenReleases; + + @Mock + StorageFacet storageFacet; + + @Mock + Supplier txSupplier; + + @Mock + StorageTx storageTx; + + @Mock + QueryOptions queryOptions; + + @Mock + Asset assetOne; + + @Mock + ORID assetOneORID; + + @Mock + ODocument assetOneDoc; + + @Mock + Asset assetTwo; + + @Mock + AssetEntityAdapter assetEntityAdapter; + + @Mock + AssetDownloadCountStore assetDownloadCountStore; + + @Mock + BucketStore bucketStore; + + BrowseAssetsSqlBuilder browseAssetsSqlBuilder; + + BrowseComponentsSqlBuilder browseComponentsSqlBuilder; + + List results; + + private BrowseServiceImpl underTest; + + @Before + public void setup() { + results = asList(assetOne, assetTwo); + + when(queryOptions.getContentAuth()).thenReturn(true); + + when(assetOneORID.toString()).thenReturn("assetOne"); + when(componentOneORID.toString()).thenReturn("componentOne"); + + when(mavenReleases.facet(StorageFacet.class)).thenReturn(storageFacet); + when(mavenReleases.getName()).thenReturn("releases"); + + when(storageFacet.txSupplier()).thenReturn(txSupplier); + when(txSupplier.get()).thenReturn(storageTx); + + browseAssetsSqlBuilder = new BrowseAssetsSqlBuilder(assetEntityAdapter); + browseComponentsSqlBuilder = new BrowseComponentsSqlBuilder(componentEntityAdapter); + + underTest = spy(new BrowseServiceImpl(new GroupType(), componentEntityAdapter, variableResolverAdapterManager, + contentPermissionChecker, assetDownloadCountStore, assetEntityAdapter, browseAssetsSqlBuilder, browseComponentsSqlBuilder, bucketStore)); + } + + @Test + public void testGetRepoToContainedGroupMap() { + Repository repo1 = mock(Repository.class); + when(repo1.getName()).thenReturn("repo1"); + when(repo1.optionalFacet(GroupFacet.class)).thenReturn(Optional.empty()); + Repository repo2 = mock(Repository.class); + when(repo2.getName()).thenReturn("repo2"); + when(repo2.optionalFacet(GroupFacet.class)).thenReturn(Optional.empty()); + Repository group = mock(Repository.class); + when(group.getName()).thenReturn("group"); + GroupFacet groupFacet = mock(GroupFacet.class); + when(groupFacet.leafMembers()).thenReturn(Arrays.asList(repo1)); + when(group.optionalFacet(GroupFacet.class)).thenReturn(Optional.of(groupFacet)); + + List repositories = Arrays.asList(repo1, repo2, group); + + Map> repoToContainedGroups = underTest.getRepoToContainedGroupMap(repositories); + assertThat(repoToContainedGroups.size(), is(3)); + assertThat(repoToContainedGroups.get("repo1").size(), is(2)); + assertThat(repoToContainedGroups.get("repo1").get(0), is("repo1")); + assertThat(repoToContainedGroups.get("repo1").get(1), is("group")); + assertThat(repoToContainedGroups.get("repo2").size(), is(1)); + assertThat(repoToContainedGroups.get("repo2").get(0), is("repo2")); + assertThat(repoToContainedGroups.get("group").size(), is(1)); + assertThat(repoToContainedGroups.get("group").get(0), is("group")); + } + + @Captor + ArgumentCaptor> sqlParamsCaptor; + + @Test + public void testBrowseAssets() { + List expectedRepositories = asList(mavenReleases); + String expectedQuery1 = "SELECT FROM INDEXVALUES:asset_name_ci_idx WHERE (bucket = b4) SKIP 0 LIMIT 1"; + String expectedQuery2 = "SELECT FROM INDEXVALUES:asset_name_ci_idx WHERE (bucket = b4) AND contentAuth(@this, :browsedRepository) == true SKIP 0 LIMIT 0"; + List bucketIds = Collections.singletonList("b4"); + Iterable resultDocs = asList(mock(ODocument.class), mock(ODocument.class)); + + doReturn(bucketIds).when(underTest).getBucketIds(any(), eq(expectedRepositories)); + doReturn(results).when(underTest).getAssets(resultDocs); + when(storageTx.browse(eq(expectedQuery1), any())).thenReturn(resultDocs); + when(storageTx.browse(eq(expectedQuery2), sqlParamsCaptor.capture())).thenReturn(resultDocs); + + BrowseResult browseResult = underTest.browseAssets(mavenReleases, queryOptions); + assertThat(browseResult.getTotal(), is(2L)); + assertThat(browseResult.getResults(), is(results)); + + sqlParamsCaptor.getAllValues().stream().forEach(params -> { + String repoNames = params.get("browsedRepository").toString(); + List splitNames = Arrays.asList(repoNames.split(",")); + assertThat(splitNames, contains("releases")); + }); + } + + @Captor + ArgumentCaptor> paramsCaptor; + + @Test + public void testGetAssetById() { + String expectedSql = "SELECT * FROM asset WHERE contentAuth(@this, :browsedRepository) == true AND @RID == :rid"; + + when(storageTx.browse(eq(expectedSql), paramsCaptor.capture())).thenReturn(Collections.singletonList(assetOneDoc)); + + when(assetEntityAdapter.readEntity(assetOneDoc)).thenReturn(assetOne); + + assertThat(underTest.getAssetById(assetOneORID, mavenReleases), is(assetOne)); + + Map params = paramsCaptor.getValue(); + + assertThat(params.get("browsedRepository"), is("releases")); + assertThat(params.get("rid"), is("assetOne")); + } + + @Test + public void testGetAssetById_withEntityId() { + EntityId assetId = new DetachedEntityId(assetOneORID.toString()); + EntityId bucketId = mock(EntityId.class); + Bucket bucket = mock(Bucket.class); + + when(storageTx.findAsset(assetId)).thenReturn(assetOne); + when(assetOne.bucketId()).thenReturn(bucketId); + when(bucketStore.getById(bucketId)).thenReturn(bucket); + when(bucket.getRepositoryName()).thenReturn("releases"); + + when(assetEntityAdapter.readEntity(assetOneDoc)).thenReturn(assetOne); + + assertThat(underTest.getAssetById(assetId, mavenReleases), is(assetOne)); + } + + @Test + public void testGetAssetById_withEntityId_groupRepository() { + EntityId assetId = new DetachedEntityId(assetOneORID.toString()); + EntityId bucketId = mock(EntityId.class); + Bucket bucket = mock(Bucket.class); + + when(storageTx.findAsset(assetId)).thenReturn(assetOne); + when(assetOne.bucketId()).thenReturn(bucketId); + when(bucketStore.getById(bucketId)).thenReturn(bucket); + when(bucket.getRepositoryName()).thenReturn("releases"); + + Repository groupRepository = mock(Repository.class); + when(groupRepository.getType()).thenReturn(new GroupType()); + when(groupRepository.getName()).thenReturn("group-repository"); + when(groupRepository.facet(StorageFacet.class)).thenReturn(storageFacet); + GroupFacet groupFacet = mock(GroupFacet.class); + when(groupFacet.allMembers()).thenReturn(Arrays.asList(groupRepository, mavenReleases)); + when(groupRepository.facet(GroupFacet.class)).thenReturn(groupFacet); + + when(assetEntityAdapter.readEntity(assetOneDoc)).thenReturn(assetOne); + + assertThat(underTest.getAssetById(assetId, groupRepository), is(assetOne)); + verify(groupFacet).allMembers(); + } + + @Test + public void testGetAssetById_withEntityId_wrongRepository() { + EntityId assetId = new DetachedEntityId(assetOneORID.toString()); + EntityId bucketId = mock(EntityId.class); + Bucket bucket = mock(Bucket.class); + + when(storageTx.findAsset(assetId)).thenReturn(assetOne); + when(assetOne.bucketId()).thenReturn(bucketId); + when(bucketStore.getById(bucketId)).thenReturn(bucket); + when(bucket.getRepositoryName()).thenReturn("some-other-repository"); + + when(assetEntityAdapter.readEntity(assetOneDoc)).thenReturn(assetOne); + + assertNull(underTest.getAssetById(assetId, mavenReleases)); + } + + @Test + public void testGetAssetById_NoResultsIsNull() { + String expectedSql = "SELECT * FROM asset WHERE contentAuth(@this, :browsedRepository) == true AND @RID == :rid"; + + when(storageTx.browse(eq(expectedSql), paramsCaptor.capture())).thenReturn(Collections.emptyList()); + + assertThat(underTest.getAssetById(assetOneORID, mavenReleases), nullValue()); + + Map params = paramsCaptor.getValue(); + + assertThat(params.get("browsedRepository"), is("releases")); + assertThat(params.get("rid"), is("assetOne")); + } + + @Test + public void testGetComponentById() { + String expectedSql = "SELECT * FROM component WHERE contentAuth(@this, :browsedRepository) == true AND @RID == :rid"; + + when(storageTx.browse(eq(expectedSql), paramsCaptor.capture())) + .thenReturn(Collections.singletonList(componentOneDoc)); + + when(componentEntityAdapter.readEntity(componentOneDoc)).thenReturn(componentOne); + + assertThat(underTest.getComponentById(componentOneORID, mavenReleases), is(componentOne)); + + Map params = paramsCaptor.getValue(); + + assertThat(params.get("browsedRepository"), is("releases")); + assertThat(params.get("rid"), is("componentOne")); + } + + @Test + public void testGetComponentById_NoResultsIsNull() { + String expectedSql = "SELECT * FROM component WHERE contentAuth(@this, :browsedRepository) == true AND @RID == :rid"; + + when(storageTx.browse(eq(expectedSql), paramsCaptor.capture())).thenReturn(Collections.emptyList()); + + assertThat(underTest.getComponentById(componentOneORID, mavenReleases), nullValue()); + + Map params = paramsCaptor.getValue(); + + assertThat(params.get("browsedRepository"), is("releases")); + assertThat(params.get("rid"), is("componentOne")); + } + + @Test + public void testGetLastThirtyDays() { + Bucket bucket = mock(Bucket.class); + EntityId bucketId = mock(EntityId.class); + when(assetOne.name()).thenReturn("/foo/one"); + when(assetOne.bucketId()).thenReturn(bucketId); + when(bucketStore.getById(bucketId)).thenReturn(bucket); + when(bucket.getRepositoryName()).thenReturn("last-thirty-repo"); + when(assetDownloadCountStore.getLastThirtyDays("last-thirty-repo", "/foo/one")).thenReturn(99L); + + assertThat(underTest.getLastThirtyDays(assetOne), is(99L)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/DefaultBrowseNodeGeneratorTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/DefaultBrowseNodeGeneratorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0e663b3463964b90307366b969a7997d83884071 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/DefaultBrowseNodeGeneratorTest.java @@ -0,0 +1,155 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.browse.BrowseNodeGenerator; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.DefaultComponent; + +import org.junit.Test; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultBrowseNodeGeneratorTest + extends TestSupport +{ + private BrowseNodeGenerator generator = new DefaultBrowseNodeGenerator(); + + @Test + public void computeAssetPathWithNoComponent() { + Component component = null; + Asset asset = createAsset("/path/asset"); + + List path = generator.computeAssetPath(asset, component); + + assertThat(path, contains("path", "asset")); + } + + @Test + public void computeAssetPathWithNoComponent_trailingSlash() { + Component component = null; + Asset asset = createAsset("/path/asset/"); + + List path = generator.computeAssetPath(asset, component); + + assertThat(path, contains("path", "asset")); + } + + @Test + public void computeAssetPathWithComponentNameOnly() { + Component component = createComponent("component", null, null); + Asset asset = createAsset("path/assetName"); + + List path = generator.computeAssetPath(asset, component); + + assertThat(path, contains(component.name(), "assetName")); + } + + @Test + public void computeAssetPathWithComponentNoGroup() { + Component component = createComponent("component", null, "1.0.0"); + Asset asset = createAsset("path/assetName"); + + List path = generator.computeAssetPath(asset, component); + + assertThat(path, contains(component.name(), component.version(), "assetName")); + } + + @Test + public void computeAssetPathWithComponent() { + Component component = createComponent("component", "group", "1.0.0"); + Asset asset = createAsset("path/assetName"); + + List path = generator.computeAssetPath(asset, component); + + assertThat(path, contains(component.group(), component.name(), component.version(), "assetName")); + } + + @Test + public void computeComponentPathWithComponentNameOnly() { + Component component = createComponent("component", null, null); + Asset asset = createAsset("path/assetName"); + + List path = generator.computeComponentPath(asset, component); + + assertThat(path, contains(component.name())); + } + + @Test + public void computeComponentPathWithComponentNoGroup() { + Component component = createComponent("component", null, "1.0.0"); + Asset asset = createAsset("path/assetName"); + + List path = generator.computeComponentPath(asset, component); + + assertThat(path, contains(component.name(), component.version())); + } + + @Test + public void computeComponentPathWithComponent() { + Component component = createComponent("component", "group", "1.0.0"); + Asset asset = createAsset("path/assetName"); + + List path = generator.computeComponentPath(asset, component); + + assertThat(path, contains(component.group(), component.name(), component.version())); + } + + @Test + public void lastSegmentBehaviour() { + assertThat(generator.lastSegment(""), is("")); + assertThat(generator.lastSegment("/"), is("")); + assertThat(generator.lastSegment("//"), is("")); + assertThat(generator.lastSegment("///"), is("")); + assertThat(generator.lastSegment("////"), is("")); + assertThat(generator.lastSegment("a"), is("a")); + assertThat(generator.lastSegment("a/"), is("a")); + assertThat(generator.lastSegment("/a"), is("a")); + assertThat(generator.lastSegment("/a/"), is("a")); + assertThat(generator.lastSegment("//a"), is("a")); + assertThat(generator.lastSegment("a//"), is("a")); + assertThat(generator.lastSegment("//a//"), is("a")); + assertThat(generator.lastSegment("a/b"), is("b")); + assertThat(generator.lastSegment("a/b/"), is("b")); + assertThat(generator.lastSegment("/a/b"), is("b")); + assertThat(generator.lastSegment("/a/b/"), is("b")); + assertThat(generator.lastSegment("a/.b"), is(".b")); + assertThat(generator.lastSegment("a/b.c"), is("b.c")); + } + + private Asset createAsset(final String assetName) { + Asset asset = mock(Asset.class); + when(asset.name()).thenReturn(assetName); + return asset; + } + + private Component createComponent(final String name, + final String group, + final String version) + { + Component component = new DefaultComponent(); + component.name(name); + component.group(group); + component.version(version); + + return component; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/PreviewAssetsSqlBuilderTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/PreviewAssetsSqlBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0bf149b156a6608d8e2dc014d2d17385a3eddfe4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/PreviewAssetsSqlBuilderTest.java @@ -0,0 +1,70 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.HashMap; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.QueryOptions; +import org.sonatype.nexus.repository.security.RepositorySelector; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; + +public class PreviewAssetsSqlBuilderTest + extends TestSupport +{ + static final String CONTENT_EXPRESSION = "contentExpression(@this, :jexlExpression, :repositorySelector, " + + ":repoToContainedGroupMap) == true"; + + static final String FILTER_EXPRESSION = "name LIKE :nameFilter"; + + @Mock + RepositorySelector repositorySelector; + + @Mock + QueryOptions queryOptions; + + @Mock + Repository repository; + + PreviewAssetsSqlBuilder underTest; + + @Before + public void setup() throws Exception { + underTest = new PreviewAssetsSqlBuilder(repositorySelector, + "", + queryOptions, + new HashMap<>()); + } + + @Test + public void whereWithContentExpression() throws Exception { + String whereClause = underTest.buildWhereClause(); + assertThat(whereClause, is(equalTo(CONTENT_EXPRESSION))); + } + + @Test + public void whereWithFilter() throws Exception { + when(queryOptions.getFilter()).thenReturn("filter"); + String whereClause = underTest.buildWhereClause(); + assertThat(whereClause, is(equalTo(CONTENT_EXPRESSION + " AND " + FILTER_EXPRESSION))); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesManagerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesManagerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..973e5c9f0b6afb4efce6db537f0c963895721f2c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesManagerTest.java @@ -0,0 +1,289 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.orient.HexRecordIdObfuscator; +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; +import org.sonatype.nexus.repository.storage.BrowseNodeEntityAdapter; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.BucketEntityAdapter; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; +import org.sonatype.nexus.repository.storage.ComponentFactory; +import org.sonatype.nexus.repository.storage.MetadataNode; +import org.sonatype.nexus.scheduling.TaskConfiguration; +import org.sonatype.nexus.scheduling.TaskInfo; +import org.sonatype.nexus.scheduling.TaskInfo.CurrentState; +import org.sonatype.nexus.scheduling.TaskInfo.State; +import org.sonatype.nexus.scheduling.TaskScheduler; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptySet; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.repository.browse.internal.RebuildBrowseNodesTaskDescriptor.REPOSITORY_NAME_FIELD_ID; + +public class RebuildBrowseNodesManagerTest + extends TestSupport +{ + private static final String REPOSITORY_NAME = "repo"; + @Rule + public DatabaseInstanceRule databaseInstanceRule = DatabaseInstanceRule.inMemory("test"); + + private RebuildBrowseNodesManager underTest; + + private BucketEntityAdapter bucketEntityAdapter; + + private AssetEntityAdapter assetEntityAdapter; + + private ComponentEntityAdapter componentEntityAdapter; + + private Bucket bucket; + + private BrowseNodeEntityAdapter browseNodeEntityAdapter; + + @Mock + private ComponentFactory componentFactory; + + @Mock + private RepositoryManager repositoryManager; + + @Mock + private Repository repository; + + @Mock + private TaskScheduler taskScheduler; + + @Mock + private TaskInfo taskInfo; + + @Mock + private TaskInfo taskInfo2; + + @Mock + private CurrentState currentState; + + @Mock + private CurrentState currentState2; + + @Mock + private BrowseNodeConfiguration configuration; + + @Before + public void configure() throws Exception { + when(configuration.isAutomaticRebuildEnabled()).thenReturn(true); + when(repositoryManager.browse()).thenReturn(Collections.singleton(repository)); + when(repository.getName()).thenReturn(REPOSITORY_NAME); + + initializeDatabase(); + + bucket = createBucket(REPOSITORY_NAME); + + bucketEntityAdapter.addEntity(databaseInstanceRule.getInstance().acquire(), bucket); + } + + private void initializeDatabase() throws Exception { + bucketEntityAdapter = new BucketEntityAdapter(); + componentEntityAdapter = new ComponentEntityAdapter(bucketEntityAdapter, componentFactory, emptySet()); + assetEntityAdapter = new AssetEntityAdapter(bucketEntityAdapter, componentEntityAdapter); + browseNodeEntityAdapter = new BrowseNodeEntityAdapter(componentEntityAdapter, assetEntityAdapter, new BrowseNodeConfiguration()); + underTest = new RebuildBrowseNodesManager(databaseInstanceRule.getInstanceProvider(), taskScheduler, configuration, + bucketEntityAdapter); + + bucketEntityAdapter.register(databaseInstanceRule.getInstance().acquire()); + componentEntityAdapter.register(databaseInstanceRule.getInstance().acquire()); + assetEntityAdapter.register(databaseInstanceRule.getInstance().acquire()); + browseNodeEntityAdapter.register(databaseInstanceRule.getInstance().acquire()); + + bucketEntityAdapter.enableObfuscation(new HexRecordIdObfuscator()); + componentEntityAdapter.enableObfuscation(new HexRecordIdObfuscator()); + assetEntityAdapter.enableObfuscation(new HexRecordIdObfuscator()); + browseNodeEntityAdapter.enableObfuscation(new HexRecordIdObfuscator()); + } + + @Test + public void doStartRunsExistingTask() throws Exception { + setupExistingTask(false); + + assetEntityAdapter.addEntity(databaseInstanceRule.getInstance().acquire(), createAsset("asset", "maven2", bucket)); + + underTest.doStart(); + //doesn't match the repo, so shouldn't be called + verify(taskInfo, never()).runNow(); + //matches the repo so should be called + verify(taskInfo2).runNow(); + //only called when no match found, so should not be called + verify(taskScheduler, never()).createTaskConfigurationInstance(RebuildBrowseNodesTaskDescriptor.TYPE_ID); + } + + @Test + public void doStartDoesntRunExistingRunningTask() throws Exception { + setupExistingTask(true); + + assetEntityAdapter.addEntity(databaseInstanceRule.getInstance().acquire(), createAsset("asset", "maven2", bucket)); + + underTest.doStart(); + //as the task should already be running, we shouldn't call runNow on either of these tasks + verify(taskInfo, never()).runNow(); + verify(taskInfo2, never()).runNow(); + verify(taskScheduler, never()).createTaskConfigurationInstance(RebuildBrowseNodesTaskDescriptor.TYPE_ID); + } + + @Test + public void doStartSkipsTaskSchedulingForNoAssets() { + underTest.doStart(); + + verifyNoMoreInteractions(taskScheduler); + } + + @Test + public void doStartSchedulesTaskIfThereAreNoBrowseNodes() throws Exception { + TaskConfiguration taskConfiguration = new TaskConfiguration(); + assetEntityAdapter.addEntity(databaseInstanceRule.getInstance().acquire(), createAsset("asset", "maven2", bucket)); + when(taskScheduler.createTaskConfigurationInstance(RebuildBrowseNodesTaskDescriptor.TYPE_ID)) + .thenReturn(taskConfiguration); + + underTest.doStart(); + assertThat(taskConfiguration.getString(REPOSITORY_NAME_FIELD_ID), is(REPOSITORY_NAME)); + verify(taskScheduler).createTaskConfigurationInstance(RebuildBrowseNodesTaskDescriptor.TYPE_ID); + verify(taskScheduler).submit(taskConfiguration); + } + + @Test + public void doStartSkipsTaskSchedulingIfThereAreBrowseNodes() throws Exception { + TaskConfiguration taskConfiguration = new TaskConfiguration(); + Asset asset = createAsset("asset", "maven2", bucket); + + try (ODatabaseDocumentTx db = databaseInstanceRule.getInstance().acquire()) { + assetEntityAdapter.addEntity(db, asset); + } + try (ODatabaseDocumentTx db = databaseInstanceRule.getInstance().acquire()) { + browseNodeEntityAdapter.createAssetNode(db, REPOSITORY_NAME, asList(asset.name()), asset); + } + + when(taskScheduler.createTaskConfigurationInstance(RebuildBrowseNodesTaskDescriptor.TYPE_ID)) + .thenReturn(taskConfiguration); + + underTest.doStart(); + + verifyNoMoreInteractions(taskScheduler); + } + + @Test + public void doStartIncludesRepositoriesWithZeroAssets() throws Exception { + TaskConfiguration taskConfiguration = new TaskConfiguration(); + Asset asset = createAsset("asset", "maven2", bucket); + + try (ODatabaseDocumentTx db = databaseInstanceRule.getInstance().acquire()) { + assetEntityAdapter.addEntity(db, asset); + } + try (ODatabaseDocumentTx db = databaseInstanceRule.getInstance().acquire()) { + browseNodeEntityAdapter.createAssetNode(db, REPOSITORY_NAME, asList(asset.name()), asset); + } + try (ODatabaseDocumentTx db = databaseInstanceRule.getInstance().acquire()) { + assetEntityAdapter.deleteEntity(db, asset); + } + + when(taskScheduler.createTaskConfigurationInstance(RebuildBrowseNodesTaskDescriptor.TYPE_ID)) + .thenReturn(taskConfiguration); + + underTest.doStart(); + assertThat(taskConfiguration.getString(REPOSITORY_NAME_FIELD_ID), is(REPOSITORY_NAME)); + verify(taskScheduler).createTaskConfigurationInstance(RebuildBrowseNodesTaskDescriptor.TYPE_ID); + verify(taskScheduler).submit(taskConfiguration); + } + + @Test + public void doStart_rebuildDisabled() throws Exception { + when(configuration.isAutomaticRebuildEnabled()).thenReturn(false); + underTest = new RebuildBrowseNodesManager(databaseInstanceRule.getInstanceProvider(), taskScheduler, configuration, + bucketEntityAdapter); + TaskConfiguration taskConfiguration = new TaskConfiguration(); + assetEntityAdapter.addEntity(databaseInstanceRule.getInstance().acquire(), createAsset("asset", "maven2", bucket)); + when(taskScheduler.createTaskConfigurationInstance(RebuildBrowseNodesTaskDescriptor.TYPE_ID)).thenReturn( + taskConfiguration); + + underTest.doStart(); + assertNull(taskConfiguration.getString(REPOSITORY_NAME_FIELD_ID)); + } + + private void setupExistingTask(final boolean running) { + TaskConfiguration matchConfiguration = new TaskConfiguration(); + matchConfiguration.setTypeId(RebuildBrowseNodesTaskDescriptor.TYPE_ID); + matchConfiguration.setString(RebuildBrowseNodesTaskDescriptor.REPOSITORY_NAME_FIELD_ID, REPOSITORY_NAME); + TaskConfiguration noMatchConfiguration = new TaskConfiguration(); + noMatchConfiguration.setTypeId(RebuildBrowseNodesTaskDescriptor.TYPE_ID); + noMatchConfiguration.setString(RebuildBrowseNodesTaskDescriptor.REPOSITORY_NAME_FIELD_ID, "badreponame"); + when(taskInfo.getConfiguration()).thenReturn(noMatchConfiguration); + when(taskInfo.getCurrentState()).thenReturn(currentState); + when(taskInfo2.getConfiguration()).thenReturn(matchConfiguration); + when(taskInfo2.getCurrentState()).thenReturn(currentState2); + when(currentState.getState()).thenReturn(State.WAITING); + when(currentState2.getState()).thenReturn(running ? State.RUNNING : State.WAITING); + when(taskScheduler.listsTasks()).thenReturn(Arrays.asList(taskInfo, taskInfo2)); + } + + private Asset createAsset(final String name, final String format, final Bucket bucket) throws Exception { + Asset asset = new Asset(); + asset.name(name); + + Method m = MetadataNode.class.getDeclaredMethod("format", String.class); + m.setAccessible(true); + m.invoke(asset, format); + + m = MetadataNode.class.getDeclaredMethod("bucketId", EntityId.class); + m.setAccessible(true); + m.invoke(asset, EntityHelper.id(bucket)); + + m = MetadataNode.class.getDeclaredMethod("attributes", NestedAttributesMap.class); + m.setAccessible(true); + m.invoke(asset, new NestedAttributesMap("attributes", new HashMap<>())); + + return asset; + } + + private Bucket createBucket(final String repositoryName) throws Exception { + Bucket bucket = new Bucket(); + bucket.setRepositoryName(repositoryName); + + Method m = Bucket.class.getDeclaredMethod("attributes", NestedAttributesMap.class); + m.setAccessible(true); + m.invoke(bucket, new NestedAttributesMap("attributes", new HashMap<>())); + + return bucket; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesTaskTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesTaskTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d3adf92d23b4495bac990461086220c627cbc31a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/RebuildBrowseNodesTaskTest.java @@ -0,0 +1,208 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import java.util.AbstractMap.SimpleEntry; +import java.util.List; +import java.util.Map.Entry; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.entity.DetachedEntityId; +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.orient.entity.AttachedEntityHelper; +import org.sonatype.nexus.orient.entity.AttachedEntityMetadata; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.AssetStore; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.BucketStore; + +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.id.ORecordId; +import com.orientechnologies.orient.core.index.OCompositeKey; +import com.orientechnologies.orient.core.index.OIndex; +import com.orientechnologies.orient.core.index.OIndexCursor; +import com.orientechnologies.orient.core.record.impl.ODocument; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.sonatype.goodies.common.Time.seconds; +import static org.sonatype.nexus.repository.storage.AssetEntityAdapter.I_BUCKET_COMPONENT_NAME; + +public class RebuildBrowseNodesTaskTest + extends TestSupport +{ + static final String REPOSITORY_NAME = "repository-name"; + + private static final int REBUILD_PAGE_SIZE = 2; + + private RebuildBrowseNodesTask underTest; + + @Mock + private AssetStore assetStore; + + @Mock + private BucketStore bucketStore; + + @Mock + private BrowseNodeManager browseNodeManager; + + @Mock + private Repository repository; + + @Mock + private Configuration config; + + @Mock + private Bucket bucket; + + @Mock + private AttachedEntityMetadata bucketMetadata; + + @Mock + private ODocument bucketDocument; + + private ORID bucketRid = new ORecordId("#2:1"); + + @Mock + private OIndex bucketComponentIndex; + + @Mock + private OIndexCursor cursor; + + @Before + public void setUp() { + when(repository.getName()).thenReturn(REPOSITORY_NAME); + when(repository.getConfiguration()).thenReturn(config); + when(config.isOnline()).thenReturn(true); + when(bucketStore.read(repository.getName())).thenReturn(bucket); + when(bucket.getEntityMetadata()).thenReturn(bucketMetadata); + when(bucketMetadata.getDocument()).thenReturn(bucketDocument); + when(bucketDocument.getIdentity()).thenReturn(bucketRid); + when(assetStore.getIndex(I_BUCKET_COMPONENT_NAME)).thenReturn(bucketComponentIndex); + when(bucketComponentIndex.cursor()).thenReturn(cursor); + + underTest = new RebuildBrowseNodesTask( + assetStore, + bucketStore, + browseNodeManager, + new BrowseNodeConfiguration(true, true, REBUILD_PAGE_SIZE, 1000, 10_000, 10_000, seconds(0)) + ); + } + + @Test + public void executeWithOfflineRepository() { + ORID bucketId = AttachedEntityHelper.id(bucket); + + Asset asset1 = createMockAsset("#1:1"); + Asset asset2 = createMockAsset("#1:2"); + Asset asset3 = createMockAsset("#2:1"); + + List> page = asList( + createIndexEntry(bucketId, EntityHelper.id(asset1)), + createIndexEntry(bucketId, EntityHelper.id(asset2)), + createIndexEntry(bucketId, EntityHelper.id(asset3))); + + when(config.isOnline()).thenReturn(false); + + when(assetStore.countAssets(any())).thenReturn(3L); + when(assetStore.getNextPage(any(), eq(REBUILD_PAGE_SIZE))).thenReturn(page, emptyList()); + when(assetStore.getById(EntityHelper.id(asset1))).thenReturn(asset1); + when(assetStore.getById(EntityHelper.id(asset2))).thenReturn(asset2); + when(assetStore.getById(EntityHelper.id(asset3))).thenReturn(asset3); + + underTest.execute(repository); + + verify(assetStore, times(2)).getNextPage(any(), eq(REBUILD_PAGE_SIZE)); + + verify(browseNodeManager).createFromAssets(eq(repository), eq(asList(asset1, asset2, asset3))); + } + + @Test + public void executeTruncatesNodesForNoAssets() { + when(assetStore.countAssets(any())).thenReturn(0L); + + underTest.execute(repository); + + verify(browseNodeManager).deleteByRepository(repository.getName()); + verify(assetStore).countAssets(any()); + verifyNoMoreInteractions(assetStore); + verifyNoMoreInteractions(browseNodeManager); + } + + @Test + public void executeProcessesAllAssetsInPages() { + ORID bucketId = AttachedEntityHelper.id(bucket); + + Asset asset1 = createMockAsset("#1:1"); + Asset asset2 = createMockAsset("#1:2"); + Asset asset3 = createMockAsset("#2:1"); + + List> page1 = asList( + createIndexEntry(bucketId, EntityHelper.id(asset1)), + createIndexEntry(bucketId, EntityHelper.id(asset2))); + List> page2 = asList( + createIndexEntry(bucketId, EntityHelper.id(asset3))); + List> page3 = emptyList(); + + when(assetStore.countAssets(any())).thenReturn(3L); + when(assetStore.getNextPage(any(), eq(REBUILD_PAGE_SIZE))).thenReturn(page1, page2, page3); + when(assetStore.getById(EntityHelper.id(asset1))).thenReturn(asset1); + when(assetStore.getById(EntityHelper.id(asset2))).thenReturn(asset2); + when(assetStore.getById(EntityHelper.id(asset3))).thenReturn(asset3); + + underTest.execute(repository); + + verify(assetStore, times(3)).getNextPage(any(), eq(REBUILD_PAGE_SIZE)); + + verify(browseNodeManager).createFromAssets(eq(repository), eq(asList(asset1, asset2))); + verify(browseNodeManager).createFromAssets(eq(repository), eq(asList(asset3))); + } + + private Asset createMockAsset(final String id) { + return createMockAsset(id, null); + } + + private Asset createMockAsset(final String id, final String componentId) { + Asset asset = mock(Asset.class); + when(asset.getEntityMetadata()).thenReturn(mock(EntityMetadata.class)); + + EntityId entityId = new DetachedEntityId(id); + when(asset.getEntityMetadata().getId()).thenReturn(entityId); + + if (componentId != null) { + EntityId componentEntityId = new DetachedEntityId(componentId); + when(asset.componentId()).thenReturn(componentEntityId); + } + + return asset; + } + + private Entry createIndexEntry(final ORID bucketId, final EntityId id) { + return new SimpleEntry<>(new OCompositeKey(bucketId), id); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/SuffixSqlBuilderTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/SuffixSqlBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ec019e9d9aa0edf0bb9c08a0a604c0d20fb16fb0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/SuffixSqlBuilderTest.java @@ -0,0 +1,51 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.browse.QueryOptions; + +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; +import static org.sonatype.nexus.repository.browse.internal.SuffixSqlBuilder.buildSuffix; + +public class SuffixSqlBuilderTest + extends TestSupport +{ + QueryOptions queryOptions; + + @Before + public void setup() throws Exception { + queryOptions = new QueryOptions("filter", "name", "asc", 99, 10); + } + + @Test(expected = NullPointerException.class) + public void failOnNullQueryOptions() throws Exception { + buildSuffix(null); + } + + @Test + public void suffix() throws Exception { + assertThat(buildSuffix(queryOptions), is(equalTo(" SKIP 99 LIMIT 10"))); + } + + @Test + public void testId() { + assertThat(buildSuffix(new QueryOptions("filter", "id", "asc", 99, 10)) + , is(equalTo(" ORDER BY @RID asc SKIP 99 LIMIT 10"))); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/resources/RepositoryBrowseResourceTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/resources/RepositoryBrowseResourceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..98ba1a7ea6bde18c323883cdc6260e9add3e5e7a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/browse/internal/resources/RepositoryBrowseResourceTest.java @@ -0,0 +1,444 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.browse.internal.resources; + +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.template.TemplateHelper; +import org.sonatype.nexus.common.template.TemplateParameters; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; +import org.sonatype.nexus.repository.browse.internal.model.BrowseListItem; +import org.sonatype.nexus.repository.group.GroupFacet; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.BrowseNode; +import org.sonatype.nexus.repository.storage.BrowseNodeStore; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.BucketStore; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.repository.storage.StorageTx; +import org.sonatype.nexus.repository.types.GroupType; +import org.sonatype.nexus.repository.types.ProxyType; +import org.sonatype.nexus.security.SecurityHelper; + +import com.google.common.base.Supplier; +import org.joda.time.DateTime; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; + +import static java.util.Arrays.asList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@SuppressWarnings("unchecked") +public class RepositoryBrowseResourceTest + extends TestSupport +{ + private static final String URL_PREFIX = "http://localhost:8888/service/rest/repository/browse/"; + + private static final String REPOSITORY_NAME = "testRepository"; + + private final SimpleDateFormat format = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy"); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Mock + private TemplateHelper templateHelper; + + @Mock + private BrowseNodeStore browseNodeStore; + + @Mock + private RepositoryManager repositoryManager; + + @Mock + private Repository repository; + + @Mock + private SecurityHelper securityHelper; + + @Mock + private UriInfo uriInfo; + + @Mock + private StorageFacet storageFacet; + + @Mock + private Supplier txSupplier; + + @Mock + private StorageTx storageTx; + + @Mock + private Asset asset; + + private BrowseNodeConfiguration configuration = new BrowseNodeConfiguration(); + + @Mock + private BucketStore bucketStore; + + @Mock + private Bucket bucket; + + private RepositoryBrowseResource underTest; + + @Before + public void before() throws Exception { + when(uriInfo.getAbsolutePath()).thenReturn(UriBuilder.fromPath(URL_PREFIX + "central/").build()); + + when(securityHelper.allPermitted(any())).thenReturn(true); + when(templateHelper.parameters()).thenReturn(new TemplateParameters()); + when(templateHelper.render(any(),any())).thenReturn("something"); + when(repository.getName()).thenReturn(REPOSITORY_NAME); + when(repository.getFormat()).thenReturn(new Format("format") {}); + when(repository.getType()).thenReturn(new ProxyType()); + + when(repository.optionalFacet(GroupFacet.class)).thenReturn(Optional.empty()); + + when(repository.facet(StorageFacet.class)).thenReturn(storageFacet); + when(storageFacet.txSupplier()).thenReturn(txSupplier); + when(txSupplier.get()).thenReturn(storageTx); + + when(storageTx.findAsset(any())).thenReturn(asset); + + EntityId bucketId = mock(EntityId.class); + when(asset.bucketId()).thenReturn(bucketId); + when(bucketStore.getById(bucketId)).thenReturn(bucket); + when(bucket.getRepositoryName()).thenReturn(REPOSITORY_NAME); + + + when(browseNodeStore + .getByPath(repository, Collections.emptyList(), configuration.getMaxHtmlNodes(), null)) + .thenReturn(Collections.singleton(browseNode("org"))); + when(browseNodeStore + .getByPath(repository, Collections.singletonList("org"), configuration.getMaxHtmlNodes(), null)) + .thenReturn(Collections.singleton(browseNode("sonatype"))); + when(repositoryManager.get(REPOSITORY_NAME)).thenReturn(repository); + + underTest = new RepositoryBrowseResource(repositoryManager, browseNodeStore, configuration, bucketStore, + templateHelper, securityHelper); + } + + @Test + public void validateRootRequest() throws Exception { + when(uriInfo.getAbsolutePath()).thenReturn(UriBuilder.fromPath(URL_PREFIX + "central/").build()); + + underTest.getHtml(REPOSITORY_NAME, "", null, uriInfo); + + ArgumentCaptor argument = ArgumentCaptor.forClass(TemplateParameters.class); + verify(templateHelper).render(any(), argument.capture()); + assertThat(argument.getValue().get().get("requestPath"), is("/")); + assertThat(((Collection)argument.getValue().get().get("listItems")).size(), is(1)); + assertThat(((Collection) argument.getValue().get().get("listItems")).iterator().next().getName(), + is("org")); + assertThat( + ((Collection) argument.getValue().get().get("listItems")).iterator().next().getResourceUri(), + is("org/")); + assertThat( + ((Collection) argument.getValue().get().get("listItems")).iterator().next().isCollection(), + is(true)); + } + + @Test + public void validateNonRootRequest() throws Exception { + when(uriInfo.getAbsolutePath()).thenReturn(UriBuilder.fromPath(URL_PREFIX + "central/org/").build()); + + underTest.getHtml(REPOSITORY_NAME, "org/", null, uriInfo); + + ArgumentCaptor argument = ArgumentCaptor.forClass(TemplateParameters.class); + verify(templateHelper).render(any(), argument.capture()); + assertThat(argument.getValue().get().get("requestPath"), is("/org/")); + assertThat(((Collection)argument.getValue().get().get("listItems")).size(), is(1)); + assertThat(((Collection) argument.getValue().get().get("listItems")).iterator().next().getName(), + is("sonatype")); + assertThat( + ((Collection) argument.getValue().get().get("listItems")).iterator().next().getResourceUri(), + is("sonatype/")); + assertThat( + ((Collection) argument.getValue().get().get("listItems")).iterator().next().isCollection(), + is(true)); + } + + @Test + public void validateOrderedEntries() throws Exception { + List nodes = asList( + browseNode("a.txt", mock(EntityId.class), true), + browseNode("Org"), + browseNode("com"), + browseNode("B.txt", mock(EntityId.class), true)); + when(browseNodeStore.getByPath(repository, Collections.emptyList(), configuration.getMaxHtmlNodes(), null)) + .thenReturn(nodes); + + underTest.getHtml(REPOSITORY_NAME, "", null, uriInfo); + + ArgumentCaptor argument = ArgumentCaptor.forClass(TemplateParameters.class); + verify(templateHelper).render(any(), argument.capture()); + + List listItems = (List) argument.getValue().get().get("listItems"); + assertThat(listItems.size(), is(4)); + + assertThat(listItems.get(0).getName(), is("com")); + assertThat(listItems.get(1).getName(), is("Org")); + assertThat(listItems.get(2).getName(), is("a.txt")); + assertThat(listItems.get(3).getName(), is("B.txt")); + } + + @Test + public void validateAsset() throws Exception { + List nodes = asList(browseNode("a.txt", mock(EntityId.class), true)); + when(asset.size()).thenReturn(1024L); + when(asset.blobUpdated()).thenReturn(new DateTime(0)); + when(asset.name()).thenReturn("a1.txt"); + when(repository.getUrl()).thenReturn("http://foo/bar"); + when(browseNodeStore.getByPath(repository, Collections.emptyList(), configuration.getMaxHtmlNodes(), null)) + .thenReturn(nodes); + + underTest.getHtml(REPOSITORY_NAME, "", null, uriInfo); + + ArgumentCaptor argument = ArgumentCaptor.forClass(TemplateParameters.class); + verify(templateHelper).render(any(), argument.capture()); + + List listItems = (List) argument.getValue().get().get("listItems"); + assertThat(listItems.size(), is(1)); + + BrowseListItem item = listItems.get(0); + assertThat(item.getName(), is("a.txt")); + assertThat(item.getSize(), is("1024")); + assertThat(item.getLastModified(), is(format.format(asset.blobUpdated().toDate()))); + assertThat(item.getResourceUri(), is("http://foo/bar/a1.txt")); + } + + @Test + public void validateAsset_groupRepository() throws Exception { + String filter = "test"; + Repository groupRepository = mock(Repository.class); + when(groupRepository.getType()).thenReturn(new GroupType()); + when(groupRepository.getName()).thenReturn("group-repository"); + when(groupRepository.getFormat()).thenReturn(new Format("format") {}); + when(groupRepository.facet(StorageFacet.class)).thenReturn(storageFacet); + GroupFacet groupFacet = mock(GroupFacet.class); + when(groupFacet.allMembers()).thenReturn(Arrays.asList(groupRepository, repository)); + when(groupRepository.optionalFacet(GroupFacet.class)).thenReturn(Optional.of(groupFacet)); + when(repositoryManager.get("group-repository")).thenReturn(groupRepository); + + List nodes = asList(browseNode("a.txt", mock(EntityId.class), true)); + when(asset.size()).thenReturn(1024L); + when(asset.blobUpdated()).thenReturn(new DateTime(0)); + when(asset.name()).thenReturn("a1.txt"); + when(groupRepository.getUrl()).thenReturn("http://foo/bar"); + when(browseNodeStore + .getByPath(groupRepository, Collections.emptyList(), configuration.getMaxHtmlNodes(), filter)) + .thenReturn(nodes); + + underTest.getHtml("group-repository", "", filter, uriInfo); + + ArgumentCaptor argument = ArgumentCaptor.forClass(TemplateParameters.class); + verify(templateHelper).render(any(), argument.capture()); + + List listItems = (List) argument.getValue().get().get("listItems"); + assertThat(listItems.size(), is(1)); + + BrowseListItem item = listItems.get(0); + assertThat(item.getName(), is("a.txt")); + assertThat(item.getSize(), is("1024")); + assertThat(item.getLastModified(), is(format.format(asset.blobUpdated().toDate()))); + assertThat(item.getResourceUri(), is("http://foo/bar/a1.txt")); + } + + @Test + public void validateFilterAppliedToNonAssetUrls() throws Exception { + String filter = "test/test"; + Repository groupRepository = mock(Repository.class); + when(groupRepository.getType()).thenReturn(new GroupType()); + when(groupRepository.getName()).thenReturn("group-repository"); + when(groupRepository.getFormat()).thenReturn(new Format("format") { }); + when(groupRepository.facet(StorageFacet.class)).thenReturn(storageFacet); + GroupFacet groupFacet = mock(GroupFacet.class); + when(groupFacet.allMembers()).thenReturn(Arrays.asList(groupRepository, repository)); + when(groupRepository.optionalFacet(GroupFacet.class)).thenReturn(Optional.of(groupFacet)); + when(repositoryManager.get("group-repository")).thenReturn(groupRepository); + + List nodes = asList(browseNode("bar")); + when(groupRepository.getUrl()).thenReturn("http://foo/"); + when(browseNodeStore + .getByPath(groupRepository, Collections.emptyList(), configuration.getMaxHtmlNodes(), filter)) + .thenReturn(nodes); + + underTest.getHtml("group-repository", "", filter, uriInfo); + + ArgumentCaptor argument = ArgumentCaptor.forClass(TemplateParameters.class); + verify(templateHelper).render(any(), argument.capture()); + + List listItems = (List) argument.getValue().get().get("listItems"); + assertThat(listItems.size(), is(1)); + + BrowseListItem item = listItems.get(0); + assertThat(item.getName(), is("bar")); + assertThat(item.getResourceUri(), is("bar/?filter=test%2Ftest")); + } + + @Test + public void validateRootRequestWithoutTrailingSlash() throws Exception { + when(uriInfo.getAbsolutePath()).thenReturn( + UriBuilder.fromPath(URL_PREFIX + "central").build()); + + Response response = underTest.getHtml(REPOSITORY_NAME, "", null, uriInfo); + assertThat(response.getStatus(), is(303)); + assertThat(response.getHeaders().get("location").get(0).toString(), is(URL_PREFIX + "central/")); + } + + @Test + public void validateNonRootRequestWithoutTrailingSlash() throws Exception { + when(uriInfo.getAbsolutePath()).thenReturn( + UriBuilder.fromPath(URL_PREFIX + "central/org").build()); + + Response response = underTest.getHtml(REPOSITORY_NAME, "org", "", uriInfo); + assertThat(response.getStatus(), is(303)); + assertThat(response.getHeaders().get("location").get(0).toString(), is(URL_PREFIX + "central/org/")); + } + + @Test + public void validatePathNotFoundRequest() throws Exception { + expectedException.expect(WebApplicationException.class); + expectedException.expectMessage("Path not found"); + + underTest.getHtml(REPOSITORY_NAME, "missing", "", uriInfo); + } + + @Test + public void validatePathNotFoundRequestNotAuthorized() throws Exception { + when(securityHelper.allPermitted(any())).thenReturn(false); + expectedException.expect(WebApplicationException.class); + expectedException.expectMessage("Repository not found"); + + underTest.getHtml(REPOSITORY_NAME, "missing", null, uriInfo); + } + + @Test + public void validateRepositoryNotFoundRequest() throws Exception { + expectedException.expect(WebApplicationException.class); + expectedException.expectMessage("Repository not found"); + + underTest.getHtml("missing", "org", "", uriInfo); + } + + @Test + public void validateRepositoryNotAuthorizedRequest() throws Exception { + when(browseNodeStore.getByPath(repository, Collections.singletonList("org"), + configuration.getMaxHtmlNodes(), null)) + .thenReturn(Collections.emptyList()); + when(securityHelper.allPermitted(any())).thenReturn(false); + expectedException.expect(WebApplicationException.class); + expectedException.expectMessage("Repository not found"); + + underTest.getHtml(REPOSITORY_NAME, "org", "", uriInfo); + } + + @Test + public void validateRepositoryWithNoBrowseNodesRequest() throws Exception { + when(browseNodeStore.getByPath(repository, Collections.emptyList(), + configuration.getMaxHtmlNodes(), "")) + .thenReturn(Collections.emptyList()); + + underTest.getHtml(REPOSITORY_NAME, "", "", uriInfo); + + ArgumentCaptor argument = ArgumentCaptor.forClass(TemplateParameters.class); + verify(templateHelper).render(any(), argument.capture()); + assertThat(((Collection)argument.getValue().get().get("listItems")).size(), is(0)); + } + + @Test + public void validateRepositoryWithNoBrowseNodesRequest_nullResponseFromGetChildrenByPath() throws Exception { + underTest.getHtml(REPOSITORY_NAME, "", "", uriInfo); + + ArgumentCaptor argument = ArgumentCaptor.forClass(TemplateParameters.class); + verify(templateHelper).render(any(), argument.capture()); + assertThat(((Collection)argument.getValue().get().get("listItems")).size(), is(0)); + } + + @Test + public void validateAssetFoldersAreTreatedLikeFolders() { + BrowseNode folderAsset = browseNode("folderAsset", mock(EntityId.class), false); + when(browseNodeStore.getByPath(repository, Collections.emptyList(), configuration.getMaxHtmlNodes(), null)) + .thenReturn(asList(folderAsset)); + + underTest.getHtml(REPOSITORY_NAME, "", null, uriInfo); + + ArgumentCaptor argument = ArgumentCaptor.forClass(TemplateParameters.class); + verify(templateHelper).render(any(), argument.capture()); + + List listItems = (List) argument.getValue().get().get("listItems"); + assertThat(listItems.size(), is(1)); + + assertThat(listItems.get(0).getName(), is("folderAsset")); + assertThat(listItems.get(0).getResourceUri(), is("folderAsset/")); + assertThat(listItems.get(0).isCollection(), is(true)); + } + + @Test + public void validateAssetUriIsUrlEscapedToPreventXss() { + BrowseNode browseNode = browseNode(""); + when(browseNodeStore.getByPath(repository, Collections.emptyList(), configuration.getMaxHtmlNodes(), null)) + .thenReturn(asList(browseNode)); + + underTest.getHtml(REPOSITORY_NAME, "", null, uriInfo); + + ArgumentCaptor argument = ArgumentCaptor.forClass(TemplateParameters.class); + verify(templateHelper).render(any(), argument.capture()); + + List listItems = (List) argument.getValue().get().get("listItems"); + assertThat(listItems.size(), is(1)); + + assertThat(listItems.get(0).getName(), is("")); + assertThat(listItems.get(0).getResourceUri(), is("%3Cimg+src%3D%22foo%22%3E/")); + } + + private BrowseNode browseNode(final String name) { + BrowseNode node = new BrowseNode(); + node.setRepositoryName(REPOSITORY_NAME); + node.setName(name); + return node; + } + + private BrowseNode browseNode(final String name, final EntityId assetId, final boolean leaf) { + BrowseNode node = new BrowseNode(); + node.setRepositoryName(REPOSITORY_NAME); + node.setName(name); + node.setAssetId(assetId); + node.setLeaf(leaf); + return node; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/CacheControllerHolderTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/CacheControllerHolderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..beff7ab78f336fac21460a3f15961568d2c20047 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/CacheControllerHolderTest.java @@ -0,0 +1,84 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache; + +import org.sonatype.nexus.repository.cache.CacheControllerHolder.CacheType; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; + +public class CacheControllerHolderTest +{ + private static final CacheType TEST = new CacheType("TEST"); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + private CacheController contentCacheController = new CacheController(1000, "content"); + + private CacheController metadataCacheController = new CacheController(1000, "metadata"); + + private CacheControllerHolder underTest; + + @Before + public void setUp() { + this.underTest = new CacheControllerHolder(contentCacheController, metadataCacheController); + } + + @Test + public void testGetContentCacheController() { + assertThat(underTest.getContentCacheController(), is(contentCacheController)); + } + + @Test + public void testGetMetadataCacheController() { + assertThat(underTest.getMetadataCacheController(), is(metadataCacheController)); + } + + @Test + public void testGetContentCacheControllerViaGet() { + assertThat(underTest.get(CacheControllerHolder.CONTENT), is(contentCacheController)); + } + + @Test + public void testGetMetadataCacheControllerViaGet() { + assertThat(underTest.get(CacheControllerHolder.METADATA), is(metadataCacheController)); + } + + @Test + public void testGetUnknownCacheControllerViaGet() { + assertThat(underTest.get(TEST), is(nullValue())); + } + + @Test + public void testGetContentCacheControllerViaRequire() { + assertThat(underTest.require(CacheControllerHolder.CONTENT), is(contentCacheController)); + } + + @Test + public void testGetMetadataCacheControllerViaRequire() { + assertThat(underTest.require(CacheControllerHolder.METADATA), is(metadataCacheController)); + } + + @Test + public void testGetUnknownCacheControllerViaRequire() { + exception.expectMessage(TEST.value()); + underTest.require(TEST); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/CacheInfoTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/CacheInfoTest.java new file mode 100644 index 0000000000000000000000000000000000000000..da37f4ad82b9c1112ba9429eeded9338858cb617 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/CacheInfoTest.java @@ -0,0 +1,100 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache; + +import java.util.Date; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.repository.storage.Asset; + +import com.google.common.collect.Maps; +import org.joda.time.DateTime; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; + +/** + * Tests {@link CacheInfo}. + */ +public class CacheInfoTest + extends TestSupport +{ + @Test + public void nothingToExtract() { + NestedAttributesMap attributes = new NestedAttributesMap(P_ATTRIBUTES, Maps.newHashMap()); + Asset asset = mock(Asset.class); + when(asset.attributes()).thenReturn(attributes); + CacheInfo cacheInfo = CacheInfo.extractFromAsset(asset); + assertThat(cacheInfo, nullValue()); + } + + @Test + public void lastVerifiedOnlyExtract() { + final DateTime now = DateTime.now(); + NestedAttributesMap attributes = new NestedAttributesMap(P_ATTRIBUTES, Maps.newHashMap()); + attributes.child(CacheInfo.CACHE).set(CacheInfo.LAST_VERIFIED, now.toDate()); + Asset asset = mock(Asset.class); + when(asset.attributes()).thenReturn(attributes); + CacheInfo cacheInfo = CacheInfo.extractFromAsset(asset); + assertThat(cacheInfo, notNullValue()); + assertThat(cacheInfo.getLastVerified(), equalTo(now)); + } + + @Test + public void cacheTokenOnlyExtract() { + final String cacheToken = "foo-bar"; + NestedAttributesMap attributes = new NestedAttributesMap(P_ATTRIBUTES, Maps.newHashMap()); + attributes.child(CacheInfo.CACHE).set(CacheInfo.CACHE_TOKEN, cacheToken); + Asset asset = mock(Asset.class); + when(asset.attributes()).thenReturn(attributes); + CacheInfo cacheInfo = CacheInfo.extractFromAsset(asset); + assertThat(cacheInfo, nullValue()); + } + + @Test + public void lastVerifiedAndCacheTokenExtract() { + final DateTime now = DateTime.now(); + final String cacheToken = "foo-bar"; + NestedAttributesMap attributes = new NestedAttributesMap(P_ATTRIBUTES, Maps.newHashMap()); + attributes.child(CacheInfo.CACHE).set(CacheInfo.LAST_VERIFIED, now.toDate()); + attributes.child(CacheInfo.CACHE).set(CacheInfo.CACHE_TOKEN, cacheToken); + Asset asset = mock(Asset.class); + when(asset.attributes()).thenReturn(attributes); + CacheInfo cacheInfo = CacheInfo.extractFromAsset(asset); + assertThat(cacheInfo, notNullValue()); + assertThat(cacheInfo.getLastVerified(), equalTo(now)); + assertThat(cacheInfo.getCacheToken(), equalTo(cacheToken)); + } + + @Test + public void lastVerifiedAndCacheTokenApply() { + final DateTime now = DateTime.now(); + final String cacheToken = "foo-bar"; + NestedAttributesMap attributes = new NestedAttributesMap(P_ATTRIBUTES, Maps.newHashMap()); + Asset asset = mock(Asset.class); + when(asset.attributes()).thenReturn(attributes); + CacheInfo cacheInfo = new CacheInfo(now, cacheToken); + CacheInfo.applyToAsset(asset, cacheInfo); + assertThat(asset.attributes().child(CacheInfo.CACHE).get(CacheInfo.LAST_VERIFIED, Date.class), + equalTo(now.toDate())); + assertThat(asset.attributes().child(CacheInfo.CACHE).get(CacheInfo.CACHE_TOKEN, String.class), + equalTo(cacheToken)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/NegativeCacheHandlerTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/NegativeCacheHandlerTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..fcb03e98c76a38a2c5183ae040944ca7f065fdbe --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/NegativeCacheHandlerTest.groovy @@ -0,0 +1,167 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.http.HttpMethods +import org.sonatype.nexus.repository.http.HttpResponses +import org.sonatype.nexus.repository.http.HttpStatus +import org.sonatype.nexus.repository.view.Context +import org.sonatype.nexus.repository.view.Request +import org.sonatype.nexus.repository.view.Response +import org.sonatype.nexus.repository.view.Status + +import org.junit.Before +import org.junit.Test + +import static org.mockito.Matchers.any +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.never +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when + +/** + * Tests for {@link NegativeCacheHandler}. + */ +class NegativeCacheHandlerTest +extends TestSupport +{ + private NegativeCacheHandler underTest + private NegativeCacheFacet facet + private NegativeCacheKey key + private Context context + private Request request + private Repository repository + + @Before + void setUp() { + underTest = new NegativeCacheHandler() + facet = mock(NegativeCacheFacet) + key = mock(NegativeCacheKey) + context = mock(Context) + request = mock(Request) + repository = mock(Repository) + when(context.getRequest()).thenReturn(request) + when(context.getRepository()).thenReturn(repository) + when(request.getAction()).thenReturn(HttpMethods.GET) + when(repository.facet(NegativeCacheFacet)).thenReturn(facet) + when(facet.getCacheKey(context)).thenReturn(key) + } + + /** + * Given: + * - request is not an GET request + * Then: + * - context is asked to proceed + * - response from context is passed on + * - no other actions (checked by no interactions with repository) + */ + @Test + void 'directly proceed on non GET requests'() { + when(request.getAction()).thenReturn(HttpMethods.PUT) + Response contextResponse = HttpResponses.ok() + when(context.proceed()).thenReturn(contextResponse) + Response response = underTest.handle(context) + assert response == contextResponse + verify(context).proceed() + verify(repository, never()).facet(any(Class)) + } + + /** + * Given: + * - no cached key present + * - a 404 response from context + * Then: + * - context is asked to proceed + * - response from context is passed on + * - key is put in cache + * - key is not invalidated + */ + @Test + void '404 response gets cached'() { + Response contextResponse = HttpResponses.notFound('404') + when(context.proceed()).thenReturn(contextResponse) + when(facet.get(key)).thenReturn(null) + Response response = underTest.handle(context) + assert response == contextResponse + verify(context).proceed() + verify(facet).put(key, response.status) + verify(facet, never()).invalidate(any(NegativeCacheKey)) + } + + /** + * Given: + * - cached key present + * Then: + * - cached status is returned + * - context is not asked to proceed + * - key is not put in cache + * - key is not invalidated + */ + @Test + void 'return cached 404'() { + Status cachedStatus = Status.failure(HttpStatus.NOT_FOUND, '404') + when(facet.get(key)).thenReturn(cachedStatus) + Response response = underTest.handle(context) + assert response.getStatus() == cachedStatus + verify(context, never()).proceed() + verify(facet, never()).put(any(NegativeCacheKey), any(Status)) + verify(facet, never()).invalidate(any(NegativeCacheKey)) + } + + /** + * Given: + * - no cached key present + * - a non 404 response from context + * Then: + * - context is asked to proceed + * - response from context is passed on + * - key is not put in cache + * - key is not invalidated + */ + @Test + void 'non 404 response passes through'() { + Response contextResponse = HttpResponses.serviceUnavailable('503') + when(context.proceed()).thenReturn(contextResponse) + when(facet.get(key)).thenReturn(null) + Response response = underTest.handle(context) + assert response == contextResponse + verify(context).proceed() + verify(facet, never()).put(any(NegativeCacheKey), any(Status)) + verify(facet, never()).invalidate(any(NegativeCacheKey)) + } + + /** + * Given: + * - no cached key present + * - successful response from context + * Then: + * - context is asked to proceed + * - response from context is passed on + * - key is not put in cache + * - key is invalidated + */ + @Test + void 'successful response invalidates cache'() { + Response contextResponse = HttpResponses.ok('200') + when(context.proceed()).thenReturn(contextResponse) + when(facet.get(key)).thenReturn(null) + Response response = underTest.handle(context) + assert response == contextResponse + verify(context).proceed() + verify(facet, never()).put(any(NegativeCacheKey), any(Status)) + verify(facet).invalidate(any(NegativeCacheKey)) + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/internal/NegativeCacheFacetImplTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/internal/NegativeCacheFacetImplTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..eb42139d1f682df639dd22736dab57df9a743796 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/internal/NegativeCacheFacetImplTest.groovy @@ -0,0 +1,318 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache.internal + +import javax.cache.Cache +import javax.cache.configuration.MutableConfiguration + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.cache.CacheHelper +import org.sonatype.nexus.common.event.EventManager +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.cache.NegativeCacheKey +import org.sonatype.nexus.repository.config.Configuration +import org.sonatype.nexus.repository.config.ConfigurationFacet +import org.sonatype.nexus.repository.http.HttpStatus +import org.sonatype.nexus.repository.view.Status + +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentCaptor + +import static org.mockito.Matchers.any +import static org.mockito.Mockito.eq +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.never +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when + +/** + * Tests for {@link NegativeCacheFacetImpl}. + */ +class NegativeCacheFacetImplTest + extends TestSupport +{ + private NegativeCacheFacetImpl underTest + + private NegativeCacheKey key + + private Status status + + private CacheHelper cacheHelper + + private Cache cache + + private Repository repository + + private NegativeCacheFacetImpl.Config config + + @Before + void setUp() { + cacheHelper = mock(CacheHelper) + cache = mock(Cache) + when(cacheHelper.maybeCreateCache(any(String), any(Class), any(Class), any(MutableConfiguration))).thenReturn(cache) + underTest = new NegativeCacheFacetImpl(cacheHelper) + underTest.installDependencies(mock(EventManager)) + key = mock(NegativeCacheKey) + status = Status.failure(HttpStatus.NOT_FOUND, '404') + repository = mock(Repository) + when(repository.name).thenReturn('test') + + config = new NegativeCacheFacetImpl.Config() + config.enabled = false + def configurationFacet = mock(ConfigurationFacet.class) + when(configurationFacet.readSection( + any(Configuration.class), + eq(NegativeCacheFacetImpl.CONFIG_KEY), + eq(NegativeCacheFacetImpl.Config.class))) + .thenReturn(config) + when(repository.facet(ConfigurationFacet.class)).thenReturn(configurationFacet) + } + + /** + * Given: + * - no configuration present + * Then: + * - cache is not created + * - get returns null + * - facet methods skip cache invocations + * - destroy does not remove cache + */ + @Test + void 'no configuration present no cache'() { + underTest.attach(repository) + underTest.init() + underTest.start() + verify(cacheHelper, never()).maybeCreateCache(any(String), any(Class), any(Class), any(MutableConfiguration)) + assert underTest.get(key) == null + underTest.put(key, Status.failure(HttpStatus.NOT_FOUND, '404')) + underTest.invalidate(key) + underTest.invalidate() + underTest.stop() + underTest.destroy() + verify(cacheHelper, never()).maybeDestroyCache(any(String)) + } + + /** + * Given: + * - configuration present + * - enabled = false + * Then: + * - cache is not created + * - get returns null + * - facet methods skip cache invocations + * - cache is not removed + */ + @Test + void 'not enabled no cache'() { + config.enabled = false + underTest.attach(repository) + underTest.init() + underTest.start() + verify(cacheHelper, never()).maybeCreateCache(any(String), any(Class), any(Class), any(MutableConfiguration)) + assert underTest.get(key) == null + underTest.put(key, Status.failure(HttpStatus.NOT_FOUND, '404')) + underTest.invalidate(key) + underTest.invalidate() + underTest.stop() + underTest.destroy() + verify(cacheHelper, never()).maybeDestroyCache(any(String)) + } + + /** + * Given: + * - configuration present + * - enabled = true + * Then: + * - cache is created + * - cache is removed + */ + @Test + void 'cache is created and removed'() { + config.enabled = true + underTest.attach(repository) + underTest.init() + underTest.start() + ArgumentCaptor cacheNameCaptor = ArgumentCaptor.forClass(String) + ArgumentCaptor configCaptor = ArgumentCaptor.forClass(MutableConfiguration) + verify(cacheHelper).maybeCreateCache(cacheNameCaptor.capture(), any(Class), any(Class), configCaptor.capture()) + when(cache.name).thenReturn(cacheNameCaptor.value) + underTest.stop() + underTest.destroy() + verify(cacheHelper, never()).maybeDestroyCache(any(String)) + } + + /** + * Given: + * - configuration present + * - enabled = true + * - cache manager is not active (already being shutdown) + * Then: + * - cache is not removed + */ + @Test + void 'cache is not removed when manager is not active'() { + config.enabled = true + underTest.attach(repository) + underTest.init() + underTest.start() + underTest.stop() + underTest.destroy() + verify(cacheHelper, never()).maybeDestroyCache(any(String)) + } + + /** + * Given: + * - configuration present + * - enabled = true + * Then: + * - put puts key/status into cache + */ + @Test + void 'put caches element'() { + config.enabled = true + underTest.attach(repository) + underTest.init() + underTest.start() + underTest.put(key, status) + ArgumentCaptor keyCaptor = ArgumentCaptor.forClass(NegativeCacheKey); + ArgumentCaptor statusCaptor = ArgumentCaptor.forClass(Status); + verify(cache).put(keyCaptor.capture(), statusCaptor.capture()) + assert key == keyCaptor.value + assert status == statusCaptor.value + } + + /** + * Given: + * - configuration present + * - enabled = true + * - cache returns an element + * Then: + * - get retrieves element from cache and returns status + */ + @Test + void 'get returns status'() { + config.enabled = true + underTest.attach(repository) + underTest.init() + underTest.start() + when(cache.get(key)).thenReturn(status) + Status actualStatus = underTest.get(key) + assert actualStatus == status + } + + /** + * Given: + * - configuration present + * - enabled = true + * - cache does not have an element for key (returns null) + * Then: + * - get retrieves element from cache and returns null + */ + @Test + void 'get returns null when cache returns null'() { + config.enabled = true + underTest.attach(repository) + underTest.init() + underTest.start() + when(cache.get(key)).thenReturn(null) + Status actualStatus = underTest.get(key) + assert actualStatus == null + } + + /** + * Given: + * - configuration present + * - enabled = true + * Then: + * - invalidate removes key from cache + */ + @Test + void 'invalidate removes element'() { + config.enabled = true + underTest.attach(repository) + underTest.init() + underTest.start() + underTest.invalidate(key) + verify(cache).remove(key) + } + + /** + * Given: + * - configuration present + * - enabled = true + * Then: + * - invalidate removes all keys from cache + */ + @Test + void 'invalidate removes all elements'() { + config.enabled = true + underTest.attach(repository) + underTest.init() + underTest.start() + underTest.invalidate() + verify(cache).removeAll() + } + + /** + * Given: + * - configuration present + * - enabled = true + * - cached entries + * Then: + * - invalidate removes all child keys from cache + */ + @Test + void 'invalidate subset removes key and all child keys'() { + NegativeCacheKey key1 = mock(NegativeCacheKey) + NegativeCacheKey key2 = mock(NegativeCacheKey) + Cache.Entry entry1 = mock(Cache.Entry) + Cache.Entry entry2 = mock(Cache.Entry) + when(entry1.key).thenReturn(key1) + when(entry2.key).thenReturn(key2) + mockIterable(cache, entry1, entry2) + when(key.isParentOf(key1)).thenReturn(false) + when(key.isParentOf(key2)).thenReturn(true) + config.enabled = true + underTest.attach(repository) + underTest.init() + underTest.start() + underTest.invalidateSubset(key) + verify(cache).remove(key) + verify(cache, never()).remove(key1) + verify(cache).remove(key2) + } + + static void mockIterable(Cache iterable, Object... values) { + Iterator mockIterator = mock(Iterator) + when(iterable.iterator()).thenReturn(mockIterator) + + if (values.length == 0) { + when(mockIterator.hasNext()).thenReturn(false) + } + else if (values.length == 1) { + when(mockIterator.hasNext()).thenReturn(true, false) + when(mockIterator.next()).thenReturn(values[0]) + } + else { + Boolean[] hasNextResponses = new Boolean[values.length] + for (int i = 0; i < hasNextResponses.length-1; i++) { + hasNextResponses[i] = true + } + hasNextResponses[hasNextResponses.length - 1] = false + when(mockIterator.hasNext()).thenReturn(true, hasNextResponses) + Object[] valuesMinusTheFirst = Arrays.copyOfRange(values, 1, values.length) + when(mockIterator.next()).thenReturn(values[0], valuesMinusTheFirst) + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/internal/PathNegativeCacheKeyTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/internal/PathNegativeCacheKeyTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..d8bf8a55fe14aec4aa1bd281310c4f0f2c0a6436 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/cache/internal/PathNegativeCacheKeyTest.groovy @@ -0,0 +1,70 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.cache.internal + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.repository.cache.NegativeCacheKey + +import org.junit.Test + +import static org.mockito.Mockito.mock + +/** + * Tests for {@link PathNegativeCacheKey}. + */ +class PathNegativeCacheKeyTest +extends TestSupport +{ + + /** + * Given: + * - a parent path cache key + * - a child key that has a path that starts with parent key path + * Then: + * - #isParentOf returns false + */ + @Test + void 'child key should start with same path'() { + def underTest = new PathNegativeCacheKey('/foo/') + assert underTest.isParentOf(new PathNegativeCacheKey('/foo/bar.jar')) + assert underTest.isParentOf(new PathNegativeCacheKey('/foo/and/more/bar.jar')) + } + + /** + * Given: + * - a parent path cache key + * - a child key that has a path that starts with parent key path + * Then: + * - #isParentOf returns false + */ + @Test + void 'path must end with slash to be a parent'() { + def underTest = new PathNegativeCacheKey('/foo') + assert !underTest.isParentOf(new PathNegativeCacheKey('/foo/bar.jar')) + assert !underTest.isParentOf(new PathNegativeCacheKey('/foo/and/more/bar.jar')) + } + + /** + * Given: + * - a parent path cache key + * - child key is not an {@link PathNegativeCacheKey} + * Then: + * - #isParentOf returns false + */ + @Test + void 'child key must be of same class'() { + def underTest = new PathNegativeCacheKey('/foo/bar.jar') + assert !underTest.isParentOf(mock(NegativeCacheKey)) + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/capability/internal/EventManagerTestSupport.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/capability/internal/EventManagerTestSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..056c2530fdeedaf58ace1beff14f322c0766df28 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/capability/internal/EventManagerTestSupport.java @@ -0,0 +1,107 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.capability.internal; + +import java.util.ArrayList; +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.capability.Condition; +import org.sonatype.nexus.capability.ConditionEvent.Satisfied; +import org.sonatype.nexus.capability.ConditionEvent.Unsatisfied; +import org.sonatype.nexus.common.event.EventManager; + +import org.hamcrest.Matcher; +import org.junit.Before; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.instanceOf; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doAnswer; + +/** + * Support for tests using event bus. + * + * @since capabilities 2.0 + */ +public class EventManagerTestSupport + extends TestSupport +{ + + @Mock + protected EventManager eventManager; + + protected List eventManagerEvents; + + @Before + public final void setUpEventManager() + throws Exception + { + eventManagerEvents = new ArrayList(); + + doAnswer(new Answer() + { + + @Override + public Object answer(final InvocationOnMock invocation) + throws Throwable + { + eventManagerEvents.add(invocation.getArguments()[0]); + return null; + } + + }).when(eventManager).post(any()); + } + + protected void verifyEventManagerEvents(final Matcher... matchers) { + assertThat(eventManagerEvents, contains(matchers)); + } + + protected void verifyNoEventManagerEvents() { + assertThat(eventManagerEvents, empty()); + } + + protected static Matcher satisfied(final Condition condition) { + return allOf( + instanceOf(Satisfied.class), + new ArgumentMatcher() + { + @Override + public boolean matches(final Object argument) { + return ((Satisfied) argument).getCondition() == condition; + } + } + ); + } + + protected static Matcher unsatisfied(final Condition condition) { + return allOf( + instanceOf(Unsatisfied.class), + new ArgumentMatcher() + { + @Override + public boolean matches(final Object argument) { + return ((Unsatisfied) argument).getCondition() == condition; + } + } + ); + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/capability/internal/RepositoryConditionsImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/capability/internal/RepositoryConditionsImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9c8ab1a74db938dbc951c82e99ecb7547a8e6a7f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/capability/internal/RepositoryConditionsImplTest.java @@ -0,0 +1,68 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.capability.internal; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.capability.Condition; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.repository.capability.RepositoryConditions; +import org.sonatype.nexus.repository.manager.RepositoryManager; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; + +/** + * {@link RepositoryConditionsImpl} UTs. + * + * @since capabilities 2.0 + */ +public class RepositoryConditionsImplTest + extends TestSupport +{ + + private RepositoryConditions underTest; + + @Before + public void setUpRepositoryConditions() { + final EventManager eventManager = mock(EventManager.class); + underTest = new RepositoryConditionsImpl(eventManager, mock(RepositoryManager.class)); + } + + /** + * repositoryIsInService() factory method returns expected condition. + */ + @Test + public void repositoryIsInService() { + assertThat( + underTest.repositoryIsOnline(() -> "repo-name"), + is(Matchers.instanceOf(RepositoryOnlineCondition.class)) + ); + } + + /** + * repositoryExists() factory method returns expected condition. + */ + @Test + public void repositoryExists() { + assertThat( + underTest.repositoryExists(() -> "repo-name"), + is(Matchers.instanceOf(RepositoryExistsCondition.class)) + ); + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/capability/internal/RepositoryExistsConditionTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/capability/internal/RepositoryExistsConditionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..352dd7c856b1de568068e05058c349e26febc073 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/capability/internal/RepositoryExistsConditionTest.java @@ -0,0 +1,129 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.capability.internal; + +import java.util.Collections; +import java.util.function.Supplier; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.manager.RepositoryCreatedEvent; +import org.sonatype.nexus.repository.manager.RepositoryDeletedEvent; +import org.sonatype.nexus.repository.manager.RepositoryManager; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * {@link RepositoryExistsCondition} UTs. + * + * @since capabilities 2.0 + */ +public class RepositoryExistsConditionTest + extends EventManagerTestSupport +{ + + static final String TEST_REPOSITORY = "test-repository"; + + @Mock + private Repository repository; + + @Mock + private RepositoryManager repositoryManager; + + private RepositoryExistsCondition underTest; + + @Before + public final void setUpRepositoryExistsCondition() + throws Exception + { + when(repositoryManager.browse()).thenReturn(Collections.emptyList()); + + final Supplier repositoryName = () -> TEST_REPOSITORY; + + when(repository.getName()).thenReturn(TEST_REPOSITORY); + + underTest = new RepositoryExistsCondition(eventManager, repositoryManager, repositoryName); + underTest.bind(); + + verify(eventManager).register(underTest); + + assertThat(underTest.isSatisfied(), is(false)); + + underTest.handle(new RepositoryCreatedEvent(repository)); + } + + /** + * Condition should be satisfied initially (because mocking done in setup). + */ + @Test + public void satisfiedWhenRepositoryExists() { + assertThat(underTest.isSatisfied(), is(true)); + } + + /** + * Condition should become satisfied and notification sent when repository is added. + */ + @Test + public void satisfiedWhenRepositoryAdded() { + assertThat(underTest.isSatisfied(), is(true)); + + underTest.handle(new RepositoryDeletedEvent(repository)); + underTest.handle(new RepositoryCreatedEvent(repository)); + assertThat(underTest.isSatisfied(), is(true)); + + verifyEventManagerEvents(satisfied(underTest), unsatisfied(underTest), satisfied(underTest)); + } + + /** + * Condition should become unsatisfied when repository is removed. + */ + @Test + public void repositoryIsRemoved() { + assertThat(underTest.isSatisfied(), is(true)); + + underTest.handle(new RepositoryDeletedEvent(repository)); + assertThat(underTest.isSatisfied(), is(false)); + + verifyEventManagerEvents(satisfied(underTest), unsatisfied(underTest)); + } + + /** + * Condition should remain satisfied when another repository is removed. + */ + @Test + public void noReactionWhenAnotherRepositoryIsRemoved() { + assertThat(underTest.isSatisfied(), is(true)); + final Repository anotherRepository = mock(Repository.class); + when(anotherRepository.getName()).thenReturn("another"); + underTest.handle(new RepositoryDeletedEvent(anotherRepository)); + assertThat(underTest.isSatisfied(), is(true)); + } + + /** + * Event bus handler is removed when releasing. + */ + @Test + public void releaseRemovesItselfAsHandler() { + underTest.release(); + + verify(eventManager).unregister(underTest); + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/capability/internal/RepositoryOnlineConditionTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/capability/internal/RepositoryOnlineConditionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b934b4a47c50e5eac9587f3e72c95c3a32c1ddeb --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/capability/internal/RepositoryOnlineConditionTest.java @@ -0,0 +1,132 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.capability.internal; + +import java.util.Collections; +import java.util.function.Supplier; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.manager.RepositoryCreatedEvent; +import org.sonatype.nexus.repository.manager.RepositoryDeletedEvent; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.manager.RepositoryUpdatedEvent; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * {@link RepositoryOnlineCondition} UTs. + * + * @since capabilities 2.0 + */ +public class RepositoryOnlineConditionTest + extends EventManagerTestSupport +{ + + static final String TEST_REPOSITORY = "test-repository"; + + @Mock + private Repository repository; + + @Mock + private Configuration configuration; + + @Mock + private RepositoryManager repositoryManager; + + private RepositoryOnlineCondition underTest; + + @Before + public final void setUpRepositoryLocalStatusCondition() + throws Exception + { + when(repositoryManager.browse()).thenReturn(Collections.emptyList()); + + final Supplier repositoryName = () -> TEST_REPOSITORY; + + when(repository.getName()).thenReturn(TEST_REPOSITORY); + when(repository.getConfiguration()).thenReturn(configuration); + when(configuration.isOnline()).thenReturn(true); + + underTest = new RepositoryOnlineCondition(eventManager, repositoryManager, repositoryName); + underTest.bind(); + + verify(eventManager).register(underTest); + + assertThat(underTest.isSatisfied(), is(false)); + + underTest.handle(new RepositoryCreatedEvent(repository)); + } + + /** + * Condition should become unsatisfied and notification sent when repository is out of service. + */ + @Test + public void unsatisfiedWhenRepositoryIsOutOfService() { + assertThat(underTest.isSatisfied(), is(true)); + + when(configuration.isOnline()).thenReturn(false); + underTest.handle(new RepositoryUpdatedEvent(repository)); + assertThat(underTest.isSatisfied(), is(false)); + + verifyEventManagerEvents(satisfied(underTest), unsatisfied(underTest)); + } + + /** + * Condition should become satisfied and notification sent when repository is back on service. + */ + @Test + public void satisfiedWhenRepositoryIsBackToService() { + assertThat(underTest.isSatisfied(), is(true)); + + when(configuration.isOnline()).thenReturn(false); + underTest.handle(new RepositoryUpdatedEvent(repository)); + + when(configuration.isOnline()).thenReturn(true); + underTest.handle(new RepositoryUpdatedEvent(repository)); + assertThat(underTest.isSatisfied(), is(true)); + + verifyEventManagerEvents(satisfied(underTest), unsatisfied(underTest), satisfied(underTest)); + } + + /** + * Condition should become unsatisfied when repository is removed. + */ + @Test + public void unsatisfiedWhenRepositoryIsRemoved() { + assertThat(underTest.isSatisfied(), is(true)); + + underTest.handle(new RepositoryDeletedEvent(repository)); + assertThat(underTest.isSatisfied(), is(false)); + + verifyEventManagerEvents(satisfied(underTest), unsatisfied(underTest)); + } + + /** + * Event bus handler is removed when releasing. + */ + @Test + public void releaseRemovesItselfAsHandler() { + underTest.release(); + + verify(eventManager).unregister(underTest); + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/config/ConfigurationTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/config/ConfigurationTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..3a0af5103d26ea06e5ea8266ffdb68f1ded18938 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/config/ConfigurationTest.groovy @@ -0,0 +1,92 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config + +import org.sonatype.nexus.common.entity.EntityMetadata + +import spock.lang.Specification + +/** + * Tests for {@link Configuration} + * @since 3.1 + */ +class ConfigurationTest + extends Specification +{ + + def "Copy copies all properties."() { + Configuration original = new Configuration(repositoryName: "myrepo", recipeName: "someRecipe", online: false, + attributes: [foo: "bar"], entityMetadata: Mock(EntityMetadata)) + + when: + Configuration clone = original.copy() + + then: + original.repositoryName == clone.repositoryName + original.recipeName == clone.recipeName + original.online == clone.online + original.attributes == clone.attributes + original.entityMetadata.is(clone.entityMetadata) // shallow copy + } + + def "Copy makes a copy of attributes."() { + Configuration original = new Configuration(repositoryName: "myrepo", recipeName: "someRecipe", online: false, + attributes: [foo: "bar"]) + + when: + Configuration clone = original.copy() + clone.attributes.blat = "greep" + + then: + original.attributes.containsKey("blat") == false + } + + def "Copy works when attributes is null."() { + Configuration original = new Configuration(repositoryName: "myrepo", recipeName: "someRecipe", online: false, + attributes: null) + + when: + Configuration clone = original.copy() + + then: + clone.attributes == null + } + + def 'Copy performs a deep clone of attributes'() { + Configuration original = new Configuration(repositoryName: 'myrepo', recipeName: 'someRecipe', online: false, + attributes: [foo: 'bar', httpclient: [authorization: [password: 'secret']]], + entityMetadata: Mock(EntityMetadata)) + + when: + Configuration clone = original.copy(); + clone.attributes.httpclient.authorization.password = 'top secret' + + then: + original.attributes.httpclient.authorization.password == 'secret' + } + + def 'Obfuscates sensitive data'() { + String configStr + Configuration config = new Configuration(repositoryName: 'myrepo', recipeName: 'someRecipe', online: false, + attributes: [ + allsecrets: [password: 'mask', secret: 'mask', applicationPassword: 'mask', systemPassword: 'mask'], + httpclient: [authorization: [password: 'mask']] + ]) + + when: 'Configuration toString is invoked' + configStr = config.toString() + + then: 'All sensitive data has been masked' + !configStr.contains('mask') + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/config/UniqueRepositoryNameValidatorTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/config/UniqueRepositoryNameValidatorTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..32de235c5ff0721ccce07991b9a45b5c95ed836b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/config/UniqueRepositoryNameValidatorTest.groovy @@ -0,0 +1,43 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config + +import org.sonatype.nexus.repository.manager.RepositoryManager + +import spock.lang.Specification + +/** + * Tests validity of Repository names validated by {@link UniqueRepositoryNameValidator} + * @since 3.0 + */ +class UniqueRepositoryNameValidatorTest + extends Specification +{ + RepositoryManager repositoryManager = Mock() + + UniqueRepositoryNameValidator validator = new UniqueRepositoryNameValidator(repositoryManager) + + def "Name is valid when the RepositoryManager says it does not exist"(String name, boolean exists) { + when: + def valid = validator.isValid(name, null) + + then: + 1 * repositoryManager.exists(name) >> exists + valid == !exists + + where: + name | exists + 'foo' | true + 'foo' | false + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/config/internal/ConfigurationEntityAdapterTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/config/internal/ConfigurationEntityAdapterTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..87e2aaade4e671d274a5a4c40f21a84ba4009b46 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/config/internal/ConfigurationEntityAdapterTest.groovy @@ -0,0 +1,170 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config.internal + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.orient.HexRecordIdObfuscator +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule +import org.sonatype.nexus.repository.config.Configuration +import org.sonatype.nexus.security.PasswordHelper + +import com.orientechnologies.orient.core.storage.ORecordDuplicatedException +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock + +import static org.mockito.Mockito.when + +/** + * Tests for {@link ConfigurationEntityAdapter}. + */ +class ConfigurationEntityAdapterTest + extends TestSupport +{ + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory('test') + + @Mock + private PasswordHelper passwordHelper + + private ConfigurationEntityAdapter underTest + + @Before + void setUp() { + when(passwordHelper.encrypt('s1mpl3')).thenReturn('******') + when(passwordHelper.tryDecrypt('******')).thenReturn('s1mpl3') + + underTest = new ConfigurationEntityAdapter(passwordHelper) + underTest.enableObfuscation(new HexRecordIdObfuscator()) + } + + @After + void tearDown() { + underTest = null + } + + @Test + void 'register schema'() { + database.instance.connect().withCloseable { db -> + underTest.register(db) + } + } + + @Test + void 'add simple entity'() { + database.instance.connect().withCloseable { db -> + underTest.register(db) + + def config = new Configuration(repositoryName: 'bar', recipeName: 'foo') + config.attributes('baz').set('a', 'b') + + underTest.addEntity(db, config) + } + } + + @Test(expected = ORecordDuplicatedException) + void 'index on name is case-insensitive'() { + database.instance.connect().withCloseable { db -> + underTest.register(db) + + def config = new Configuration(repositoryName: 'bar', recipeName: 'foo', attributes: [:]) + underTest.addEntity(db, config) + def conflictingConfig = new Configuration(repositoryName: config.repositoryName.capitalize(), + recipeName: config.recipeName, attributes: config.attributes) + underTest.addEntity(db, conflictingConfig) + } + } + + @Test + void 'sensitive entity'() { + database.instance.connect().withCloseable { db -> + underTest.register(db) + + def config = new Configuration(repositoryName: 'bar', recipeName: 'foo') + config.attributes('baz').set('password', 's1mpl3') + + def document = underTest.addEntity(db, config) + + assert document.field('attributes.baz.password') == '******' + + def entity = underTest.readEntity(document) + + assert entity.attributes['baz']['password'] == 's1mpl3' + } + } + + // FIXME: Below use protected bits to test, not easy to expose for testing w/o exposing too much api in impls + // FIXME: Groovy may or may not ignore access modifiers in the future so should sort out how to better test + +// @Test +// void 'read simple entity'() { +// def db = database.instance.connect() +// try { +// underTest.register(db) +// +// def config1 = new Configuration() +// config1.recipeName = 'foo' +// config1.repositoryName = 'bar' +// def attr1 = config1.attributes('baz') +// attr1.set('a', 'b') +// +// def doc = underTest.add(db, config1) +// log doc.toJSON() +// +// def config2 = underTest.readEntity(doc) +// assert config2.recipeName == 'foo' +// assert config2.repositoryName == 'bar' +// assert config2.attributes != null +// assert config2.attributes.size() == 1 +// log config2.attributes.getClass() +// +// def attr2 = config2.attributes('baz') +// assert attr2 != null +// assert attr2.get('a') == 'b' +// } +// finally { +// db.close() +// } +// } + +// @Test +// void 'read detached entity'() { +// def detached +// def db = database.instance.connect() +// try { +// underTest.register(db) +// +// def config1 = new Configuration() +// config1.recipeName = 'foo' +// config1.repositoryName = 'bar' +// def attr1 = config1.attributes('baz') +// attr1.set('a', 'b') +// +// def doc = underTest.add(db, config1) +// detached = underTest.readEntity(doc) +// } +// finally { +// db.close() +// } +// +// def attr2 = detached.attributes('baz') +// assert attr2 != null +// assert attr2.get('a') == 'b' +// +// def attr3 = detached.attributes('more') +// assert attr3 != null +// attr3.set('a', 'b') +// } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/config/internal/ConfigurationStoreImplTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/config/internal/ConfigurationStoreImplTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..d00370fff73b1a6fb00bed45e5db94c887854fb7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/config/internal/ConfigurationStoreImplTest.groovy @@ -0,0 +1,151 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.config.internal + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.orient.HexRecordIdObfuscator +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule +import org.sonatype.nexus.repository.config.Configuration +import org.sonatype.nexus.security.PasswordHelper + +import com.orientechnologies.orient.core.exception.OValidationException +import com.orientechnologies.orient.core.storage.ORecordDuplicatedException +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock + +import static org.junit.Assert.fail + +/** + * Tests for {@link ConfigurationStoreImpl}. + */ +class ConfigurationStoreImplTest + extends TestSupport +{ + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory('test') + + @Mock + private PasswordHelper passwordHelper + + private ConfigurationStoreImpl underTest + + @Before + void setUp() { + def entityAdapter = new ConfigurationEntityAdapter(passwordHelper) + entityAdapter.enableObfuscation(new HexRecordIdObfuscator()) + + underTest = new ConfigurationStoreImpl( + database.instanceProvider, + entityAdapter + ) + + underTest.start() + } + + @After + void tearDown() { + if (underTest) { + underTest.stop() + underTest = null + } + } + + @Test + void 'create configuration'() { + def entity = new Configuration( + repositoryName: 'foo', + recipeName: 'bar', + attributes: [ + 'baz': [ + 'a': false + ] + ] + ) + log entity + assert entity.entityMetadata == null + + underTest.create(entity) + log entity + assert entity.entityMetadata != null + log entity.entityMetadata + } + + @Test + void 'create configuration with unique repositoryName'() { + def create = { name -> + underTest.create(new Configuration( + repositoryName: name, + recipeName: 'test' + )) + } + + create 'foo' + + // attempting to create with same repositoryName should fail + try { + create 'foo' + fail() + } + catch (ORecordDuplicatedException e) { + // expected + log e.toString() + } + } + + @Test + void 'create configuration with mandatory fields'() { + try { + underTest.create(new Configuration( + // omit repositoryName + recipeName: 'test' + )) + fail() + } + catch (OValidationException e) { + // expected + log e.toString() + } + + try { + underTest.create(new Configuration( + repositoryName: 'test' + // omit recipeName + )) + fail() + } + catch (OValidationException e) { + // expected + log e.toString() + } + } + + @Test + void 'list configurations'() { + def create = { name -> + underTest.create(new Configuration( + repositoryName: name, + recipeName: 'test' + )) + } + + create 'foo' + create 'bar' + + underTest.list().each { entity -> + log entity + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/group/GroupFacetImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/group/GroupFacetImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c0632b68c34ac9a9de0ecd0ef8d2b55ee0650d62 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/group/GroupFacetImplTest.java @@ -0,0 +1,164 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.group; + +import java.util.Optional; + +import javax.validation.ConstraintViolation; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.config.ConfigurationFacet; +import org.sonatype.nexus.repository.group.GroupFacetImpl.Config; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.types.GroupType; +import org.sonatype.nexus.repository.types.HostedType; +import org.sonatype.nexus.validation.ConstraintViolationFactory; + +import com.google.common.collect.ImmutableSet; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static com.google.common.collect.ImmutableList.copyOf; +import static org.hamcrest.Matchers.contains; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.repository.group.GroupFacetImpl.CONFIG_KEY; + +public class GroupFacetImplTest + extends TestSupport +{ + @Mock + private RepositoryManager repositoryManager; + + @Mock + private ConfigurationFacet configurationFacet; + + @Mock + private Format format = mock(Format.class); + + private GroupType groupType = new GroupType(); + + private GroupFacetImpl underTest; + + @Before + public void setup() throws Exception { + underTest = new GroupFacetImpl(repositoryManager, makeConstraintViolationFactory(), groupType); + underTest.attach(makeRepositoryUnderTest()); + } + + @Test + public void testDoValidate_pass() { + Config config = new Config(); + config.memberNames = ImmutableSet.of("repository1"); + assertNull(underTest.validateGroupDoesNotContainItself("repositoryUnderTest", config)); + } + + @Test + public void testDoValidate_fail_group_contains_itself() { + Config config = new Config(); + config.memberNames = ImmutableSet.of("repositoryUnderTest"); + assertNotNull(underTest.validateGroupDoesNotContainItself("repositoryUnderTest", config)); + } + + @Test + public void testDoValidate_fail_group_contains_a_group_that_contains_itself() { + Config config = new Config(); + config.memberNames = ImmutableSet.of("repository3"); + assertNotNull(underTest.validateGroupDoesNotContainItself("repositoryUnderTest", config)); + } + + @Test + public void testDoValidate_fail_group_contains_a_group_which_contains_a_group_which_contains_itself() { + Config config = new Config(); + config.memberNames = ImmutableSet.of("repository2"); + assertNotNull(underTest.validateGroupDoesNotContainItself("repositoryUnderTest", config)); + } + + @Test + public void testLeafMembers() throws Exception { + Repository hosted1 = hostedRepository("hosted1"); + Repository hosted2 = hostedRepository("hosted2"); + Repository group1 = groupRepository("group1", hosted1); + Config config = new Config(); + config.memberNames = ImmutableSet.of(hosted1.getName(), hosted2.getName(), group1.getName()); + Configuration configuration = new Configuration(); + configuration.attributes(CONFIG_KEY).set("memberNames", config.memberNames); + when(configurationFacet.readSection(configuration, CONFIG_KEY, Config.class)).thenReturn(config); + underTest.doConfigure(configuration); + assertThat(underTest.leafMembers(), contains(hosted1, hosted2)); + } + + @Test + public void testAllMembers() throws Exception { + Repository hosted1 = hostedRepository("hosted1"); + Repository group1 = groupRepository("group1", hosted1); + underTest.attach(group1); + + for (Repository repo : underTest.allMembers()) { + System.out.println(repo.getName()); + } + assertThat(underTest.allMembers(), contains(group1, hosted1)); + } + + private ConstraintViolationFactory makeConstraintViolationFactory() { + ConstraintViolationFactory constraintViolationFactory = mock(ConstraintViolationFactory.class); + when(constraintViolationFactory.createViolation(anyString(), anyString())) + .thenReturn(mock(ConstraintViolation.class)); + return constraintViolationFactory; + } + + private Repository makeRepositoryUnderTest() { + Repository repositoryUnderTest = groupRepository("repositoryUnderTest"); + when(repositoryUnderTest.facet(GroupFacet.class)).thenReturn(underTest); + when(repositoryUnderTest.facet(ConfigurationFacet.class)).thenReturn(configurationFacet); + + groupRepository("repository2", + groupRepository("repository3", + repositoryUnderTest, + groupRepository("repository1") + ) + ); + return repositoryUnderTest; + } + + private Repository hostedRepository(final String name) { + Repository hostedRepository = mock(Repository.class); + when(hostedRepository.getType()).thenReturn(new HostedType()); + when(hostedRepository.getName()).thenReturn(name); + when(hostedRepository.getFormat()).thenReturn(format); + when(hostedRepository.optionalFacet(GroupFacet.class)).thenReturn(Optional.empty()); + when(repositoryManager.get(name)).thenReturn(hostedRepository); + return hostedRepository; + } + + private Repository groupRepository(final String name, final Repository... repositories) { + Repository groupRepository = mock(Repository.class); + when(groupRepository.getType()).thenReturn(groupType); + when(groupRepository.getName()).thenReturn(name); + when(groupRepository.getFormat()).thenReturn(format); + when(repositoryManager.get(name)).thenReturn(groupRepository); + GroupFacet groupFacet = mock(GroupFacet.class); + when(groupRepository.facet(GroupFacet.class)).thenReturn(groupFacet); + when(groupRepository.optionalFacet(GroupFacet.class)).thenReturn(Optional.of(groupFacet)); + when(groupFacet.members()).thenReturn(copyOf(repositories)); + return groupRepository; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/group/GroupHandlerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/group/GroupHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6a5db1bfbe14514d3092eb0a9a5d36a5a667cd1b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/group/GroupHandlerTest.java @@ -0,0 +1,77 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.group; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.group.GroupHandler.DispatchedRepositories; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.when; + +public class GroupHandlerTest + extends TestSupport +{ + private static final String REPOSITORY_1 = "repository1"; + + private static final String REPOSITORY_2 = "repository2"; + + private static final String REPOSITORY_3 = "repository3"; + + @Mock + private Repository repository1; + + @Mock + private Repository repository2; + + @Mock + private Repository repository3; + + private DispatchedRepositories underTest; + + @Before + public void setUp() throws Exception { + underTest= new DispatchedRepositories(); + when(repository1.toString()).thenReturn(REPOSITORY_1); + when(repository1.getName()).thenReturn(REPOSITORY_1); + when(repository2.toString()).thenReturn(REPOSITORY_2); + when(repository2.getName()).thenReturn(REPOSITORY_2); + when(repository3.toString()).thenReturn(REPOSITORY_3); + when(repository3.getName()).thenReturn(REPOSITORY_3); + } + + @Test + public void checkDispatchedRepositoryInsertionWillPreserveOrder() { + underTest.add(repository1); + underTest.add(repository2); + underTest.add(repository3); + + assertThat(underTest.toString(), + containsString(String.format("[%s, %s, %s]", REPOSITORY_1, REPOSITORY_2, REPOSITORY_3))); + } + + @Test + public void checkDispatchedRepositoryInsertionWillPreserveOrderWhenAlternateSequence() { + underTest.add(repository3); + underTest.add(repository1); + underTest.add(repository2); + + assertThat(underTest.toString(), + containsString(String.format("[%s, %s, %s]", REPOSITORY_3, REPOSITORY_1, REPOSITORY_2))); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/http/PartialPayloadTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/http/PartialPayloadTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7d3b11233ca05085d082ed41d2b6c3621b347f8d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/http/PartialPayloadTest.java @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.http; + +import java.io.IOException; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.view.payloads.BytesPayload; + +import com.google.common.collect.Range; +import com.google.common.io.ByteStreams; +import com.google.common.primitives.Bytes; +import org.junit.Test; + +import static java.util.Arrays.asList; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +/** + * Tests {@link PartialPayload}. + */ +public class PartialPayloadTest + extends TestSupport +{ + private final byte[] input = Bytes.toArray(asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); + + private final BytesPayload bytesPayload = new BytesPayload(input, "n/a"); + + @Test + public void oneBytePartial() throws IOException { + final byte[] output = partial(bytesPayload, Range.closed(0L, 0L)); + + assertThat(output, is(Bytes.toArray(asList(0)))); + } + + @Test + public void threeBytePartial() throws IOException { + final byte[] output = partial(bytesPayload, Range.closed(0L, 2L)); + + assertThat(output, is(Bytes.toArray(asList(0, 1, 2)))); + } + + @Test + public void entireStream() throws IOException { + final byte[] output = partial(bytesPayload, Range.closed(0L, 9L)); + + assertThat(output, is(input)); + } + + private byte[] partial(final BytesPayload bytes, final Range closed) throws IOException { + final PartialPayload partial = new PartialPayload(bytes, closed); + return ByteStreams.toByteArray(partial.openInputStream()); + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/http/RangeParserTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/http/RangeParserTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2d35262600e1d4c821f0251cf8f0525cb9d7c70a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/http/RangeParserTest.java @@ -0,0 +1,89 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.http; + +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; + +import com.google.common.collect.Range; +import org.junit.Test; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; + +/** + * Tests {@link RangeParser}. + */ +public class RangeParserTest + extends TestSupport +{ + private RangeParser parser = new RangeParser(); + + @Test + public void first500Bytes() { + final List> ranges = parser.parseRangeSpec("bytes=0-499", 1000L); + assertThat(ranges.size(), is(1)); + assertThat(ranges.get(0), is(Range.closed(0L, 499L))); + } + + @Test + public void second500Bytes() { + final List> ranges = parser.parseRangeSpec("bytes=500-999", 1000L); + assertThat(ranges.size(), is(1)); + assertThat(ranges.get(0), is(Range.closed(500L, 999L))); + } + + @Test + public void last500Bytes() { + final List> ranges = parser.parseRangeSpec("bytes=9500-", 10000L); + assertThat(ranges.size(), is(1)); + assertThat(ranges.get(0), is(Range.closed(9500L, 9999L))); + } + + @Test + public void last500BytesSuffix() { + final List> ranges = parser.parseRangeSpec("bytes=-1", 10L); + assertThat(ranges.size(), is(1)); + assertThat(ranges.get(0), is(Range.closed(9L, 9L))); + } + + @Test + public void completeSuffix() { + final Range fullContent = parser.parseRangeSpec("bytes=0-", 10L).get(0); + final Range fullSuffix = parser.parseRangeSpec("bytes=-10", 10L).get(0); + assertThat(fullSuffix, is(fullContent)); + } + + @Test + public void illegalRange() { + final List> ranges = parser.parseRangeSpec("bytes=10-", 5L); + System.err.println(ranges); + assertThat(ranges, is(nullValue())); + } + + @Test + public void suffixTooLarge() { + final List> ranges = parser.parseRangeSpec("bytes=-10", 5L); + System.err.println(ranges); + assertThat(ranges, is(nullValue())); + } + + // The RFC allows for ranges that exceed the end of the content to be fulfilled + @Test + public void partialOverlapsAreOkay() { + final List> ranges = parser.parseRangeSpec("bytes=5-100", 10L); + assertThat(ranges.get(0), is(Range.closed(5L, 9L))); + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/httpclient/internal/BlockingHttpClientTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/httpclient/internal/BlockingHttpClientTest.java new file mode 100644 index 0000000000000000000000000000000000000000..74d3a879011f90364c09ea85ffca5d6e0c472631 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/httpclient/internal/BlockingHttpClientTest.java @@ -0,0 +1,144 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.httpclient.internal; + +import java.io.IOException; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.httpclient.FilteredHttpClientSupport.Filterable; +import org.sonatype.nexus.repository.httpclient.RemoteConnectionStatus; +import org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusObserver; +import org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType; +import org.sonatype.nexus.repository.httpclient.internal.HttpClientFacetImpl.Config; + +import org.apache.http.HttpHost; +import org.apache.http.client.HttpClient; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.internal.util.reflection.Whitebox.setInternalState; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.AUTO_BLOCKED_UNAVAILABLE; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.AVAILABLE; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.BLOCKED; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.OFFLINE; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.READY; +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.UNAVAILABLE; + +public class BlockingHttpClientTest + extends TestSupport +{ + @Mock + HttpClient httpClient; + + @Mock + RemoteConnectionStatusObserver statusObserver; + + @Mock + Filterable filterable; + + HttpHost httpHost; + + BlockingHttpClient underTest; + + @Before + public void setup() throws Exception { + httpHost = HttpHost.create("localhost"); + underTest = new BlockingHttpClient(httpClient, new Config(), statusObserver, true); + } + + @Test + public void updateStatusWhenBlocked() throws Exception { + Config config = new Config(); + config.blocked = true; + reset(statusObserver); + new BlockingHttpClient(httpClient, config, statusObserver, true); + ArgumentCaptor newStatusCaptor = ArgumentCaptor.forClass(RemoteConnectionStatus.class); + verify(statusObserver).onStatusChanged(any(), newStatusCaptor.capture()); + assertThat(newStatusCaptor.getValue().getType(), is(equalTo(BLOCKED))); + } + + @Test + public void updateStatusWhenAvailable() throws Exception { + filterAndHandleException(); + verifyUpdateStatus(AVAILABLE); + } + + @Test + public void updateStatusWhenUnavailableAndAutoBlocked() throws Exception { + setInternalState(underTest, "autoBlock", true); + when(filterable.call()).thenThrow(new IOException()); + filterAndHandleException(); + verifyUpdateStatus(AUTO_BLOCKED_UNAVAILABLE); + } + + @Test + public void updateStatusWhenUnavailable() throws Exception { + when(filterable.call()).thenThrow(new IOException()); + filterAndHandleException(); + verifyUpdateStatus(UNAVAILABLE); + } + + @Test + public void doNotUpdateStatusWhenStatusHasNotChanged() throws Exception { + filterAndHandleException(); + filterAndHandleException(); + verifyUpdateStatus(AVAILABLE); + } + + @Test + public void updateStatusWhenAutoBlockedTimeHasChanged() throws Exception { + setInternalState(underTest, "autoBlock", true); + when(filterable.call()).thenThrow(new IOException()); + filterAndHandleException(); + setInternalState(underTest, "blockedUntil", (Object) null); + reset(statusObserver); + Thread.sleep(10L); // guarantee a time increase. + filterAndHandleException(); + verify(statusObserver).onStatusChanged(any(), any()); + } + + @Test + public void setStatusToOfflineWhenPassed() throws Exception { + underTest = new BlockingHttpClient(httpClient, new Config(), statusObserver, false); + ArgumentCaptor newStatusCaptor = ArgumentCaptor.forClass(RemoteConnectionStatus.class); + verify(statusObserver, times(2)).onStatusChanged(any(), newStatusCaptor.capture()); + assertThat(newStatusCaptor.getAllValues().get(1).getType(), is(equalTo(OFFLINE))); + } + + private void verifyUpdateStatus(final RemoteConnectionStatusType newType) { + ArgumentCaptor oldStatusCaptor = ArgumentCaptor.forClass(RemoteConnectionStatus.class); + ArgumentCaptor newStatusCaptor = ArgumentCaptor.forClass(RemoteConnectionStatus.class); + verify(statusObserver, times(2)).onStatusChanged(oldStatusCaptor.capture(), newStatusCaptor.capture()); + assertThat(oldStatusCaptor.getAllValues().get(1).getType(), is(equalTo(READY))); + assertThat(newStatusCaptor.getAllValues().get(1).getType(), is(equalTo(newType))); + } + + private void filterAndHandleException() throws IOException { + try { + underTest.filter(httpHost, filterable); + } + catch (IOException e) { + //Intentionally not logged as this exception is expected in the test and doesn't give us any more information. + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/httpclient/internal/HttpClientFacetImplConfigTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/httpclient/internal/HttpClientFacetImplConfigTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..e59ca35c7d360d25a9c62aac605f65f915e7dd04 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/httpclient/internal/HttpClientFacetImplConfigTest.groovy @@ -0,0 +1,96 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.httpclient.internal + +import javax.validation.Validation +import javax.validation.Validator + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.common.event.EventManager +import org.sonatype.nexus.httpclient.HttpClientManager +import org.sonatype.nexus.httpclient.config.UsernameAuthenticationConfiguration +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.httpclient.RemoteConnectionStatus +import org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusEvent + +import org.junit.Before +import org.junit.Test + +import static org.mockito.Matchers.any +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.verify +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.AVAILABLE +import static org.sonatype.nexus.repository.httpclient.RemoteConnectionStatusType.BLOCKED + +/** + * Tests for {@link HttpClientFacetImpl.Config}. + */ +class HttpClientFacetImplConfigTest + extends TestSupport +{ + private Validator validator + + EventManager eventManager = mock(EventManager.class); + + @Before + public void setUp() throws Exception { + validator = Validation.buildDefaultValidatorFactory().validator + } + + @Test + void 'authentication username with null password'() { + def violations = validator.validate(new HttpClientFacetImpl.Config( + authentication: new UsernameAuthenticationConfiguration( + username: 'admin', + password: null + ) + )) + assert violations.size() == 1 + def violation = violations.iterator().next() + assert violation.propertyPath.toString() == 'authentication.password' + } + + @Test + void 'authentication password with null username'() { + def violations = validator.validate(new HttpClientFacetImpl.Config( + authentication: new UsernameAuthenticationConfiguration( + username: null, + password: 'pass' + ) + )) + assert violations.size() == 1 + def violation = violations.iterator().next() + assert violation.propertyPath.toString() == 'authentication.username' + } + + @Test + void 'required fields may not be whitespace only'() { + def violations = validator.validate(new HttpClientFacetImpl.Config( + authentication: new UsernameAuthenticationConfiguration( + username: ' ', + password: ' ' + ) + )) + assert violations.size() == 2 + assert violations.collect { it.propertyPath.toString() }.sort() == ['authentication.password', 'authentication.username'] + } + + @Test + void 'fire event on remote connection status changed'() { + def underTest = new HttpClientFacetImpl(mock(HttpClientManager.class)) + underTest.attach(mock(Repository.class)) + underTest.installDependencies(eventManager) + underTest.onStatusChanged(new RemoteConnectionStatus(AVAILABLE), new RemoteConnectionStatus(BLOCKED)) + verify(eventManager).post(any(RemoteConnectionStatusEvent.class)) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/httpclient/internal/HttpClientFacetImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/httpclient/internal/HttpClientFacetImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7e79cfb2f19fc55d100804e8f6830961cf9df539 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/httpclient/internal/HttpClientFacetImplTest.java @@ -0,0 +1,88 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.httpclient.internal; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.httpclient.HttpClientManager; +import org.sonatype.nexus.httpclient.config.NtlmAuthenticationConfiguration; +import org.sonatype.nexus.httpclient.config.UsernameAuthenticationConfiguration; + +import org.apache.http.Header; +import org.apache.http.HttpHeaders; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests for {@link HttpClientFacetImpl}. + */ +public class HttpClientFacetImplTest + extends TestSupport +{ + private HttpClientFacetImpl underTest; + + @Mock + private HttpClientManager httpClientManager; + + private HttpClientFacetImpl.Config config = new HttpClientFacetImpl.Config(); + + private UsernameAuthenticationConfiguration usernameAuthentication = new UsernameAuthenticationConfiguration(); + + private NtlmAuthenticationConfiguration ntlmAuthentication = new NtlmAuthenticationConfiguration(); + + // Value generated using: http://www.blitter.se/utils/basic-authentication-header-generator/ + private static final String BASIC_AUTH_ENCODED = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="; + + private static final String USERNAME = "username"; + + private static final String PASSWORD = "password"; + + @Before + public void setUp() { + underTest = new HttpClientFacetImpl(httpClientManager, config); + + usernameAuthentication.setUsername(USERNAME); + usernameAuthentication.setPassword(PASSWORD); + } + + @Test + public void createBasicAuthHeaderWithoutAuthConfiguredThrowsException() throws Exception { + Header basicAuth = underTest.createBasicAuthHeader(); + + assertThat(basicAuth, is(nullValue())); + } + + @Test + public void createBasicAuthHeaderWithoutUsernameAuthThrowsException() throws Exception { + config.authentication = ntlmAuthentication; + + Header basicAuth = underTest.createBasicAuthHeader(); + + assertThat(basicAuth, is(nullValue())); + } + + @Test + public void createBasicAuthWithUsernameAuthConfigWorks() throws Exception { + config.authentication = usernameAuthentication; + + Header basicAuth = underTest.createBasicAuthHeader(); + + assertThat(basicAuth.getName(), is(equalTo(HttpHeaders.AUTHORIZATION))); + assertThat(basicAuth.getValue(), is(equalTo(BASIC_AUTH_ENCODED))); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationStoreImplTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationStoreImplTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..cf7a6b86588b88819bf432c0571c26679c64269e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreConfigurationStoreImplTest.groovy @@ -0,0 +1,100 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal.blobstore + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule + +import com.orientechnologies.orient.core.storage.ORecordDuplicatedException +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +import static org.junit.Assert.fail + +/** + * Tests for {@link BlobStoreConfigurationStoreImpl}. + */ +class BlobStoreConfigurationStoreImplTest + extends TestSupport +{ + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory('test') + + private BlobStoreConfigurationStoreImpl underTest + + @Before + void setup() { + underTest = new BlobStoreConfigurationStoreImpl( + database.instanceProvider, + new BlobStoreConfigurationEntityAdapter() + ) + underTest.start() + } + + @After + void tearDown() { + if (underTest) { + underTest.stop() + underTest = null + } + } + + @Test + void 'Can create a new BlobStoreConfiguration'() { + createConfig() + } + + @Test + void 'Can list the persisted configurations'() { + BlobStoreConfiguration entity = createConfig() + List list = underTest.list() + assert list.size() == 1 + assert list[0].name == entity.name + assert list[0].attributes == entity.attributes + } + + @Test + void 'Can delete an existing BlobStoreConfiguration'() { + BlobStoreConfiguration entity = createConfig() + assert underTest.list() + underTest.delete(entity) + assert !underTest.list() + } + + @Test + void 'Names are unique'() { + BlobStoreConfiguration entity = createConfig() + + try { + createConfig(entity.name, 'path2') + fail() + } + catch (ORecordDuplicatedException e) { + // FIXME: This is fragile for refactoring + assert e.toString().contains(BlobStoreConfigurationEntityAdapter.I_NAME) + } + } + + private BlobStoreConfiguration createConfig(name = 'foo', path = 'bar') { + def entity = new BlobStoreConfiguration( + name: name, + type: 'file', + attributes: [file:[path:path]] + ) + underTest.create(entity) + return entity + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreManagerImplTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreManagerImplTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..3aca214db540ae4f2e27cc99d663bb19ec237476 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/internal/blobstore/BlobStoreManagerImplTest.groovy @@ -0,0 +1,228 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.internal.blobstore + +import javax.inject.Provider + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.blobstore.api.BlobStore +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration +import org.sonatype.nexus.common.event.EventManager +import org.sonatype.nexus.common.node.NodeAccess +import org.sonatype.nexus.orient.freeze.DatabaseFreezeService + +import com.google.common.collect.Lists +import org.hamcrest.Matchers +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.mockito.ArgumentCaptor +import org.mockito.Mock + +import static org.hamcrest.MatcherAssert.assertThat +import static org.junit.Assert.fail +import static org.mockito.ArgumentCaptor.forClass +import static org.mockito.Matchers.any +import static org.mockito.Mockito.doReturn +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.spy +import static org.mockito.Mockito.times +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when +import static org.sonatype.nexus.blobstore.api.BlobStoreManager.DEFAULT_BLOBSTORE_NAME + +/** + * Tests for {@link BlobStoreManagerImpl}. + */ +class BlobStoreManagerImplTest + extends TestSupport +{ + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder() + + @Mock + EventManager eventManager + + @Mock + BlobStoreConfigurationStore store + + @Mock + Provider provider + + @Mock + DatabaseFreezeService databaseFreezeService + + @Mock + NodeAccess nodeAccess + + BlobStoreManagerImpl underTest + + @Before + void setup() { + underTest = newBlobStoreManager() + } + + private BlobStoreManagerImpl newBlobStoreManager(boolean provisionDefaults = false) { + spy(new BlobStoreManagerImpl(eventManager, store, [test: provider, File: provider], + databaseFreezeService, nodeAccess, provisionDefaults)) + } + + @Test + void 'Can start with nothing configured'() { + ArgumentCaptor configurationArgumentCaptor = forClass(BlobStoreConfiguration.class) + when(store.list()).thenReturn(Lists.newArrayList()) + underTest.doStart() + assert !underTest.browse() + + verify(store).create(configurationArgumentCaptor.capture()) + assertThat(configurationArgumentCaptor.getValue().name, Matchers.is(DEFAULT_BLOBSTORE_NAME)) + } + + @Test + void 'Can start with nothing configured and does not create default when clustered'() { + + when(nodeAccess.isClustered()).thenReturn(true) + when(store.list()).thenReturn(Lists.newArrayList()) + underTest.doStart() + + verify(store, times(0)).create(any(BlobStoreConfiguration.class)) + } + + @Test + void 'Can start with nothing configured and does create default when clustered if provisionDefaults is true'() { + underTest = newBlobStoreManager(true) + + ArgumentCaptor configurationArgumentCaptor = forClass(BlobStoreConfiguration.class) + when(nodeAccess.isClustered()).thenReturn(true) + + when(store.list()).thenReturn(Lists.newArrayList()) + underTest.doStart() + + verify(store).create(configurationArgumentCaptor.capture()) + assertThat(configurationArgumentCaptor.getValue().name, Matchers.is(DEFAULT_BLOBSTORE_NAME)) + } + + @Test + void 'Can start with existing configuration'() { + BlobStore blobStore = mock(BlobStore) + when(provider.get()).thenReturn(blobStore) + when(store.list()).thenReturn(Lists.newArrayList(createConfig('test'))) + + underTest.doStart() + + assert underTest.browse().toList() == [blobStore] + } + + @Test + void 'Name can be duplicate regardless of case'() { + BlobStore blobStore = mock(BlobStore) + when(provider.get()).thenReturn(blobStore) + when(store.list()).thenReturn(Lists.newArrayList(createConfig('test'))) + + underTest.doStart() + + assert !underTest.exists('unique') + assert underTest.exists('test') + assert underTest.exists('TEST') + } + + @Test + void 'Can create a BlobStore'() { + BlobStore blobStore = mock(BlobStore) + when(provider.get()).thenReturn(blobStore) + BlobStoreConfiguration configuration = createConfig('test') + + BlobStore createdBlobStore = underTest.create(configuration) + + assert createdBlobStore == blobStore + verify(store).create(configuration) + verify(blobStore).start() + assert underTest.browse().toList() == [blobStore] + assert underTest.get('test') == blobStore + } + + @Test + void 'Can delete an existing BlobStore'() { + BlobStoreConfiguration configuration = createConfig('test') + BlobStore blobStore = mock(BlobStore) + doReturn(blobStore).when(underTest).blobStore('test') + when(store.list()).thenReturn([configuration]) + when(blobStore.getBlobStoreConfiguration()).thenReturn(configuration) + + underTest.delete(configuration.getName()) + + verify(blobStore).stop() + verify(store).delete(configuration) + verify(databaseFreezeService).checkUnfrozen("Unable to delete a BlobStore while database is frozen.") + } + + @Test + void 'All BlobStores are stopped with the manager is stopped'() { + BlobStore blobStore = mock(BlobStore) + when(provider.get()).thenReturn(blobStore) + BlobStoreConfiguration configuration = createConfig('test') + underTest.create(configuration) + + underTest.stop() + + verify(blobStore).stop() + } + + @Test + void 'Blob store not created for invalid configuration'() { + when(provider.get()).thenThrow(new IllegalArgumentException()) + + BlobStoreConfiguration configuration = createConfig('test') + + try { + underTest.create(configuration) + fail() + } + catch (Exception e) { + // expected + } + + assert underTest.browse().toList() == [] + } + + @Test + void 'Can successfullly create new blob stores concurrently'() { + // avoid newBlobStoreManager method because it returns a spy that throws NPE accessing the stores field + underTest = new BlobStoreManagerImpl(eventManager, store, [test: provider, File: provider], + databaseFreezeService, nodeAccess, true) + + BlobStore blobStore = mock(BlobStore) + when(provider.get()).thenReturn(blobStore) + + underTest.create(createConfig(name: 'concurrency-test-1')) + underTest.create(createConfig(name: 'concurrency-test-2')) + + // simulate concurrent access by opening an iterator on the internal stores + def storesIterator = underTest.stores.entrySet().iterator() + storesIterator.next() + + underTest.create(createConfig(name: 'concurrency-test-3')) + // this method will throw ConcurrentModificationException if the internal store isn't thread-safe + storesIterator.next() + } + + private BlobStoreConfiguration createConfig(name = 'foo', type = 'test', attributes = [file:[path:'baz']]) { + def entity = new BlobStoreConfiguration( + name: name, + type: type, + attributes: attributes + ) + return entity + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/maintenance/internal/MaintenanceServiceImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/maintenance/internal/MaintenanceServiceImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..904129d4e92c4a527bff318a1a25aca54a219378 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/maintenance/internal/MaintenanceServiceImplTest.java @@ -0,0 +1,194 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.maintenance.internal; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.IllegalOperationException; +import org.sonatype.nexus.repository.MissingFacetException; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.security.ContentPermissionChecker; +import org.sonatype.nexus.repository.security.VariableResolverAdapter; +import org.sonatype.nexus.repository.security.VariableResolverAdapterManager; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.ComponentMaintenance; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.repository.storage.StorageTx; +import org.sonatype.nexus.security.BreadActions; +import org.sonatype.nexus.selector.VariableSource; + +import com.google.common.base.Supplier; +import org.apache.shiro.authz.AuthorizationException; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static java.util.Collections.singletonList; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class MaintenanceServiceImplTest + extends TestSupport +{ + @Mock + ContentPermissionChecker contentPermissionChecker; + + @Mock + VariableResolverAdapterManager variableResolverAdapterManager; + + @Mock + Format format; + + @Mock + Repository mavenReleases; + + @Mock + Repository mavenGroup; + + @Mock + VariableResolverAdapter variableResolverAdapter; + + @Mock + Asset assetOne; + + @Mock + VariableSource variableSource; + + @Mock + EntityMetadata assetEntityMetadata; + + @Mock + EntityId assetEntityId; + + @Mock + ComponentMaintenance componentMaintenance; + + @Mock + Component component; + + @Mock + EntityMetadata componentEntityMetadata; + + @Mock + EntityId componentEntityId; + + @Mock + StorageFacet storageFacet; + + @Mock + Supplier txSupplier; + + @Mock + StorageTx storageTx; + + MaintenanceServiceImpl underTest; + + @Before + public void setUp() throws Exception { + when(format.toString()).thenReturn("maven2"); + + setupRepository(); + setupStorage(); + setupRepositoryGroup(); + setupVariableResolvers(); + setupAssetComponents(); + + underTest = new MaintenanceServiceImpl(contentPermissionChecker, + variableResolverAdapterManager); + } + + private void setupAssetComponents() { + when(assetOne.getEntityMetadata()).thenReturn(assetEntityMetadata); + when(assetEntityMetadata.getId()).thenReturn(assetEntityId); + + when(component.getEntityMetadata()).thenReturn(componentEntityMetadata); + when(componentEntityMetadata.getId()).thenReturn(componentEntityId); + } + + private void setupVariableResolvers() { + when(variableResolverAdapterManager.get("maven2")).thenReturn(variableResolverAdapter); + when(variableResolverAdapter.fromAsset(assetOne)).thenReturn(variableSource); + } + + private void setupRepositoryGroup() { + when(mavenGroup.getName()).thenReturn("maven-group"); + when(mavenGroup.getFormat()).thenReturn(format); + + doThrow(new MissingFacetException(mavenGroup, ComponentMaintenance.class)).when(mavenGroup) + .facet(ComponentMaintenance.class); + } + + private void setupStorage() { + when(storageFacet.txSupplier()).thenReturn(txSupplier); + when(txSupplier.get()).thenReturn(storageTx); + } + + private void setupRepository() { + when(mavenReleases.getName()).thenReturn("maven-releases"); + when(mavenReleases.getFormat()).thenReturn(format); + when(mavenReleases.facet(ComponentMaintenance.class)).thenReturn(componentMaintenance); + when(mavenReleases.facet(StorageFacet.class)).thenReturn(storageFacet); + } + + @Test + public void testDeleteAsset() { + when(contentPermissionChecker.isPermitted("maven-releases", "maven2", BreadActions.DELETE, variableSource)) + .thenReturn(true); + + underTest.deleteAsset(mavenReleases, assetOne); + + verify(componentMaintenance).deleteAsset(assetEntityId); + } + + @Test(expected = IllegalOperationException.class) + public void testDeleteAsset_notSupported() { + when(contentPermissionChecker.isPermitted("maven-group", "maven2", BreadActions.DELETE, variableSource)) + .thenReturn(true); + + underTest.deleteAsset(mavenGroup, assetOne); + } + + @Test(expected = AuthorizationException.class) + public void testDeleteAsset_notPermitted() { + when(contentPermissionChecker.isPermitted("maven-releases", "maven2", BreadActions.DELETE, variableSource)) + .thenReturn(false); + + underTest.deleteAsset(mavenReleases, assetOne); + } + + @Test + public void testDeleteComponent() { + when(storageTx.browseAssets(component)).thenReturn(singletonList(assetOne)); + when(contentPermissionChecker.isPermitted("maven-releases", "maven2", BreadActions.DELETE, variableSource)) + .thenReturn(true); + + underTest.deleteComponent(mavenReleases, component); + + verify(storageTx).begin(); + verify(storageTx).close(); + verify(componentMaintenance).deleteComponent(componentEntityId); + } + + @Test(expected = AuthorizationException.class) + public void testDeleteComponent_NotAuthorized() { + when(storageTx.browseAssets(component)).thenReturn(singletonList(assetOne)); + when(contentPermissionChecker.isPermitted("maven-releases", "maven2", BreadActions.DELETE, variableSource)) + .thenReturn(false); + + underTest.deleteComponent(mavenReleases, component); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/manager/internal/FacetLookupTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/manager/internal/FacetLookupTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..5603a5b1aabc59d3bcc6aeece5270cc88c6d5ba3 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/manager/internal/FacetLookupTest.groovy @@ -0,0 +1,115 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager.internal + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.repository.Facet +import org.sonatype.nexus.repository.FacetSupport + +import org.junit.Before +import org.junit.Test + +import static org.junit.Assert.fail + +/** + * Tests for {@link org.sonatype.nexus.repository.manager.FacetLookup}. + */ +class FacetLookupTest + extends TestSupport +{ + private FacetLookup underTest + + @Before + void setUp() { + underTest = new FacetLookup() + } + + @Facet.Exposed + static interface ExampleFacet + extends Facet + { + // empty + } + + static class ExampleFacetSupport + extends FacetSupport + implements ExampleFacet + { + // empty + } + + @Facet.Exposed + static class MyExampleFacet + extends ExampleFacetSupport + { + // empty + } + + static class FacetNoExposure + extends FacetSupport + { + // empty + } + + @Test + void 'add and get facet'() { + def facet1 = new MyExampleFacet() + underTest.add(facet1) + + def facet2 = underTest.get(MyExampleFacet.class) + assert facet1 == facet2 + } + + @Test + void 'add and get facet exposure'() { + def facet1 = new MyExampleFacet() + underTest.add(facet1) + + // lookup by exposed intf should return same instance + def facet2 = underTest.get(ExampleFacet.class) + assert facet2 == facet1 + + // lookup by exposed concrete should return same instance + def facet3 = underTest.get(MyExampleFacet.class) + assert facet3 == facet1 + + // look up of non-exposed facet type returns null + def facet4 = underTest.get(ExampleFacetSupport.class) + assert facet4 == null + } + + @Test + void 'add facet with nothing exposed'() { + def facet1 = new FacetNoExposure() + try { + underTest.add(facet1) + fail(); + } + catch (Exception e) { + // expected + } + } + + @Test + void 'add duplicate facet exposure disallowed'() { + underTest.add(new MyExampleFacet()) + + try { + underTest.add(new MyExampleFacet()) + fail() + } + catch (Exception e) { + // expected + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/manager/internal/RepositoryManagerImplTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/manager/internal/RepositoryManagerImplTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..21864f6121b78b244e7d9d01bab079a9d3ec70e6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/manager/internal/RepositoryManagerImplTest.groovy @@ -0,0 +1,356 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.manager.internal + +import javax.inject.Provider + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.blobstore.api.BlobStoreManager +import org.sonatype.nexus.common.collect.NestedAttributesMap +import org.sonatype.nexus.common.entity.EntityMetadata +import org.sonatype.nexus.common.event.EventManager +import org.sonatype.nexus.common.node.NodeAccess +import org.sonatype.nexus.orient.freeze.DatabaseFreezeService +import org.sonatype.nexus.repository.Format +import org.sonatype.nexus.repository.Recipe +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.Type +import org.sonatype.nexus.repository.config.Configuration +import org.sonatype.nexus.repository.config.ConfigurationFacet +import org.sonatype.nexus.repository.config.internal.ConfigurationStore +import org.sonatype.nexus.repository.group.GroupFacet +import org.sonatype.nexus.repository.manager.DefaultRepositoriesContributor +import org.sonatype.nexus.repository.manager.RepositoryMetadataUpdatedEvent +import org.sonatype.nexus.repository.storage.internal.BucketUpdatedEvent + +import com.google.common.collect.ImmutableMap +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentCaptor +import org.mockito.Mock + +import static com.google.common.collect.Lists.asList +import static com.google.common.collect.Lists.newArrayList +import static java.util.Collections.emptyList +import static java.util.Collections.singletonList +import static org.fest.assertions.api.Assertions.assertThat +import static org.hamcrest.Matchers.instanceOf +import static org.junit.Assert.assertFalse +import static org.mockito.Matchers.any +import static org.mockito.Matchers.isA +import static org.mockito.Mockito.atLeastOnce +import static org.mockito.Mockito.never +import static org.mockito.Mockito.times +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when +import static org.sonatype.nexus.blobstore.api.BlobStoreManager.DEFAULT_BLOBSTORE_NAME + +class RepositoryManagerImplTest + extends TestSupport +{ + + static final String MAVEN_CENTRAL_NAME = 'maven-central' + + static final String APACHE_SNAPSHOTS_NAME = 'apache-snapshots' + + List groupMembers + + @Mock + EventManager eventManager + + @Mock + ConfigurationStore configurationStore + + @Mock + DatabaseFreezeService databaseFreezeService + + @Mock + RepositoryFactory repositoryFactory + + @Mock + Provider configurationFacetProvider + + @Mock + RepositoryAdminSecurityContributor securityContributor + + List defaultRepositoriesContributorList + + @Mock + DefaultRepositoriesContributor defaultRepositoriesContributor + + @Mock + Configuration mavenCentralConfiguration + + @Mock + Repository mavenCentralRepository + + @Mock + Configuration apacheSnapshotsConfiguration + + @Mock + Repository apacheSnapshotsRepository + + @Mock + Configuration thirdPartyConfiguration + + @Mock + Repository thirdPartyRepository + + @Mock + Configuration groupConfiguration + + @Mock + Repository groupRepository + + //Recipe for creating repositories + @Mock + Recipe recipe + + String recipeName = 'mockRecipe' + + @Mock + Type type + + @Mock + Type groupType + + @Mock + Format format + + @Mock + NodeAccess nodeAccess + + @Mock + BlobStoreManager blobStoreManager + + @Mock + GroupFacet groupFacet + + @Mock + NestedAttributesMap groupAttributesMap + + @Mock + EntityMetadata entityMetadata + + //Subject of the test + RepositoryManagerImpl repositoryManager + + @Before + void setup() { + setupRecipe() + setupRepositories() + blobstoreProvisionDefaults(false, false) + } + + private void setupRecipe() { + when(recipe.getType()).thenReturn(type) + when(recipe.getFormat()).thenReturn(format) + } + + private void setupRepositories() { + when(defaultRepositoriesContributor.getRepositoryConfigurations()). + thenReturn(asList(mavenCentralConfiguration, apacheSnapshotsConfiguration, thirdPartyConfiguration, + groupConfiguration)) + defaultRepositoriesContributorList = singletonList(defaultRepositoriesContributor) + + mockRepository(mavenCentralConfiguration, mavenCentralRepository, MAVEN_CENTRAL_NAME, 'default') + mockRepository(apacheSnapshotsConfiguration, apacheSnapshotsRepository, APACHE_SNAPSHOTS_NAME, 'default') + mockRepository(thirdPartyConfiguration, thirdPartyRepository, 'third-party', 'third-party') + mockRepository(groupConfiguration, groupRepository, 'group', 'group') + + when(repositoryFactory.create(type, format)). + thenReturn(mavenCentralRepository, apacheSnapshotsRepository, thirdPartyRepository, groupRepository) + + setupGroupRepository() + } + + private void setupGroupRepository() { + groupMembers = newArrayList(MAVEN_CENTRAL_NAME, "apache-snapshots") + when(groupRepository.optionalFacet(GroupFacet.class)).thenReturn(Optional.of(groupFacet)) + when(groupFacet.member(mavenCentralRepository)).thenReturn(true) + when(groupFacet.member(apacheSnapshotsRepository)).thenReturn(true) + when(groupRepository.getType()).thenReturn(groupType) + when(groupType.getValue()).thenReturn("group") + when(groupConfiguration.attributes("group")).thenReturn(groupAttributesMap) + when(groupAttributesMap.get("memberNames", Collection.class)).thenReturn(groupMembers) + } + + private void mockRepository(Configuration configuration, Repository repository, String name, String blobstoreName) { + when(configuration.getRecipeName()).thenReturn(recipeName) + when(configuration.getRepositoryName()).thenReturn(name) + when(configuration.getAttributes()).thenReturn([storage: [blobStoreName: blobstoreName]]) + when(repository.getConfiguration()).thenReturn(configuration) + when(repository.getName()).thenReturn(name) + when(repository.optionalFacet(any(Class.class))).thenReturn(Optional.empty()) + } + + private RepositoryManagerImpl buildRepositoryManagerImpl(final boolean defaultsConfigured, + final boolean skipDefaultRepositories) + { + if (defaultsConfigured) { + when(configurationStore.list()). + thenReturn(asList(mavenCentralConfiguration, apacheSnapshotsConfiguration, thirdPartyConfiguration, + groupConfiguration)) + } + + return initializeAndStartRepositoryManager(skipDefaultRepositories) + } + + private RepositoryManagerImpl initializeAndStartRepositoryManager(boolean skipDefaultRepositories) { + repositoryManager = new RepositoryManagerImpl(eventManager, configurationStore, repositoryFactory, + configurationFacetProvider, ImmutableMap.of(recipeName, recipe), securityContributor, + defaultRepositoriesContributorList, databaseFreezeService, skipDefaultRepositories, blobStoreManager) + + repositoryManager.doStart() + return repositoryManager + } + + private RepositoryManagerImpl buildRepositoryManagerImpl(final boolean defaultsConfigured) { + return buildRepositoryManagerImpl(defaultsConfigured, false) + } + + private void blobstoreProvisionDefaults(final boolean provisionDefaults, final boolean clustered) { + when(blobStoreManager.exists(DEFAULT_BLOBSTORE_NAME)).thenReturn(provisionDefaults || !clustered) + } + + @Test + void 'it should correctly load existing configurations on startup'() { + repositoryManager = buildRepositoryManagerImpl(true) + when(configurationStore.list()). + thenReturn(asList(mavenCentralConfiguration, apacheSnapshotsConfiguration, thirdPartyConfiguration)) + + assertThat(repositoryManager.browse()).hasSize(4) + + verify(mavenCentralRepository).init(mavenCentralConfiguration) + verify(mavenCentralRepository).start() + + verify(apacheSnapshotsRepository).init(apacheSnapshotsConfiguration) + verify(apacheSnapshotsRepository).start() + + verify(thirdPartyRepository).init(thirdPartyConfiguration) + verify(thirdPartyRepository).start() + } + + @Test + void 'it should correctly create default repositories if none are configured on startup'() { + repositoryManager = buildRepositoryManagerImpl(false) + + verify(configurationStore).create(mavenCentralConfiguration) + verify(configurationStore).create(apacheSnapshotsConfiguration) + verify(configurationStore).create(thirdPartyConfiguration) + } + + @Test + void 'should not create default repositories even if none are present if skip defaults is true'() { + repositoryManager = buildRepositoryManagerImpl(false, true) + + verify(configurationStore, times(0)).create(any(Configuration.class)) + } + + @Test + void 'should not create default repositories if it is clustered'() { + when(nodeAccess.isClustered()).thenReturn(true) + blobstoreProvisionDefaults(false, true) + repositoryManager = buildRepositoryManagerImpl(false) + + verify(configurationStore, times(0)).create(any(Configuration.class)) + } + + @Test + void 'should still create default repositories if it is clustered and the default blobstore exists'() { + when(nodeAccess.isClustered()).thenReturn(true) + blobstoreProvisionDefaults(true, true) + repositoryManager = buildRepositoryManagerImpl(false) + + verify(configurationStore).create(mavenCentralConfiguration) + verify(configurationStore).create(apacheSnapshotsConfiguration) + verify(configurationStore).create(thirdPartyConfiguration) + } + + @Test + void 'it should not create any repositories if no defaults are provided'() { + when(defaultRepositoriesContributor.getRepositoryConfigurations()).thenReturn(emptyList()) + + repositoryManager = buildRepositoryManagerImpl(false, false) + + verify(configurationStore, times(0)).create(any(Configuration.class)) + } + + @Test + void 'exists checks name, is not case sensitive'() { + repositoryManager = buildRepositoryManagerImpl(true) + assertThat(repositoryManager.exists(MAVEN_CENTRAL_NAME)).isTrue() + assertThat(repositoryManager.exists(MAVEN_CENTRAL_NAME.toUpperCase())).isTrue() + assertThat(repositoryManager.exists('missing-repository')).isFalse() + } + + @Test + void 'blobstoreUsageCount returns number of repositories using a blob store'() { + repositoryManager = buildRepositoryManagerImpl(true) + assertThat(repositoryManager.blobstoreUsageCount("default")).isEqualTo(2) + assertThat(repositoryManager.blobstoreUsageCount("third-party")).isEqualTo(1) + } + + @Test + void 'test delete checks unfrozen'() { + repositoryManager = buildRepositoryManagerImpl(true) + repositoryManager.delete("maven-central") + verify(databaseFreezeService).checkUnfrozen("Unable to delete repository when database is frozen.") + } + + @Test + void 'remove repository from any group repository configurations on delete'() { + buildRepositoryManagerImpl(true).delete(MAVEN_CENTRAL_NAME) + assertFalse(groupMembers.contains(MAVEN_CENTRAL_NAME)) + } + + @Test + void 'update group repository when a member repository is deleted'() { + buildRepositoryManagerImpl(true).delete(MAVEN_CENTRAL_NAME) + verify(configurationStore).update(groupConfiguration) + } + + @Test + void 'creating repositories concurrently should not fail'() { + RepositoryManagerImpl repositoryManager = initializeAndStartRepositoryManager(true) + repositoryManager.create(new Configuration(repositoryName: 'r1', recipeName: 'mockRecipe')) + repositoryManager.create(new Configuration(repositoryName: 'r2', recipeName: 'mockRecipe')) + + // open an iterator to simulate concurrent access to the private repositories map in RepositoryManagerImpl + def iterator = repositoryManager.repositories.iterator() + iterator.next() + repositoryManager.create(new Configuration(repositoryName: 'r3', recipeName: 'mockRecipe')) + // this call will fail with ConcurrentModificationException if the private repositories map is not thread safe + iterator.next() + } + + @Test + void 'post metadata-updated-event when bucket of existing repository is updated'() { + repositoryManager = buildRepositoryManagerImpl(true) + BucketUpdatedEvent bucketEvent = new BucketUpdatedEvent(entityMetadata, MAVEN_CENTRAL_NAME) + repositoryManager.onBucketUpdated(bucketEvent) + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(Object) + verify(eventManager, atLeastOnce()).post(eventCaptor.capture()) + Object repoEvent = eventCaptor.allValues.last() + assertThat(repoEvent, is(instanceOf(RepositoryMetadataUpdatedEvent))) + assertThat(repoEvent.repository, is(mavenCentralRepository)) + } + + @Test + void 'do not post metadata-updated-event when bucket of deleted repository is updated'() { + repositoryManager = buildRepositoryManagerImpl(true) + BucketUpdatedEvent bucketEvent = new BucketUpdatedEvent(entityMetadata, 'some-deleted-repo$uuid') + repositoryManager.onBucketUpdated(bucketEvent) + verify(eventManager, never()).post(isA(RepositoryMetadataUpdatedEvent)) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/proxy/ConcurrentProxyTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/proxy/ConcurrentProxyTest.java new file mode 100644 index 0000000000000000000000000000000000000000..91efe9ceac4b63b362e341b2b0bc64c2aacd89b7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/proxy/ConcurrentProxyTest.java @@ -0,0 +1,322 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.proxy; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.LockSupport; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.sonatype.goodies.common.Time; +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.goodies.testsupport.concurrent.ConcurrentRunner; +import org.sonatype.nexus.common.collect.AttributesMap; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.cache.CacheController; +import org.sonatype.nexus.repository.cache.CacheControllerHolder; +import org.sonatype.nexus.repository.cache.CacheInfo; +import org.sonatype.nexus.repository.proxy.Cooperation.CooperatingFuture; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Request; + +import com.google.common.collect.ConcurrentHashMultiset; +import com.google.common.collect.Multiset; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Spy; + +import static com.google.common.base.Charsets.UTF_8; +import static com.google.common.io.ByteStreams.toByteArray; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.repository.http.HttpMethods.GET; + +/** + * Concurrent {@link ProxyFacetSupport} tests. + */ +public class ConcurrentProxyTest + extends TestSupport +{ + + private static final int NUM_CLIENTS = 100; + + private static final int NUM_PATHS = 10; + + private static final int DOWNLOAD_DELAY_MILLIS = 1000; + + private static final int RANDOM_DELAY_MILLIS = 500; + + private static final Time PASSIVE_TIMEOUT = Time.seconds(60); + + private static final Time ACTIVE_TIMEOUT = Time.millis((DOWNLOAD_DELAY_MILLIS + RANDOM_DELAY_MILLIS) * 2); + + private static final String META_PREFIX = "meta/"; + + private static final String ASSET_PREFIX = "asset/"; + + private static final byte[] META_CONTENT = "META".getBytes(UTF_8); + + private static final byte[] ASSET_CONTENT = "ASSET".getBytes(UTF_8); + + @Mock + Repository repository; + + @Mock + CacheController cacheController; + + @Mock + CacheControllerHolder cacheControllerHolder; + + @Mock + CacheInfo cacheInfo; + + @Mock + AttributesMap attributesMap; + + @Mock + Request metaRequest; + + @Mock + Context metaContext; + + @Mock + Content metaContent; + + @Mock + Content assetContent; + + Random random = new Random(); + + Multiset upstreamRequests = ConcurrentHashMultiset.create(); + + Map storage = new ConcurrentHashMap<>(); + + AtomicInteger cooperationExceptionCount = new AtomicInteger(); + + @Spy + ProxyFacetSupport underTest = new ProxyFacetSupport() + { + @Nullable + @Override + protected Content getCachedContent(final Context context) { + return storage.get(context.getRequest().getPath()); + } + + @Override + protected Content store(final Context context, final Content content) { + storage.put(context.getRequest().getPath(), content); + return content; + } + + @Override + protected void indicateVerified(final Context context, final Content content, final CacheInfo cacheInfo) { + // no-op + } + + @Override + protected String getUrl(@Nonnull final Context context) { + String path = context.getRequest().getPath(); + if (context.equals(metaContext)) { + return META_PREFIX + path; + } + if (path.contains("indirect")) { + // simulate formats which load index files to find URLs + try (InputStream in = get(metaContext).openInputStream()) { + return ASSET_PREFIX + path; // pretend we used the index + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + return ASSET_PREFIX + path; + } + + @Override + protected Content fetch(final String url, final Context context, final Content stale) throws IOException { + upstreamRequests.add(url); + + // mimic I/O time taken to download content + LockSupport.parkNanos(Time.millis(DOWNLOAD_DELAY_MILLIS + random.nextInt(RANDOM_DELAY_MILLIS)).toNanos()); + + if (url.startsWith(META_PREFIX)) { + return metaContent; + } + if (url.contains("broken")) { + throw new IOException("oops"); + } + if (url.startsWith(ASSET_PREFIX)) { + return assetContent; + } + return null; + } + }; + + @Before + public void setUp() throws Exception { + when(metaRequest.getPath()).thenReturn("index.json"); + when(metaContext.getRequest()).thenReturn(metaRequest); + + when(attributesMap.get(CacheInfo.class)).thenReturn(cacheInfo); + + when(metaContent.getAttributes()).thenReturn(attributesMap); + when(assetContent.getAttributes()).thenReturn(attributesMap); + + when(metaContent.openInputStream()).thenAnswer(invocation -> new ByteArrayInputStream(META_CONTENT)); + when(assetContent.openInputStream()).thenAnswer(invocation -> new ByteArrayInputStream(ASSET_CONTENT)); + + when(cacheController.isStale(cacheInfo)).thenReturn(false); + when(cacheControllerHolder.getContentCacheController()).thenReturn(cacheController); + + underTest.cacheControllerHolder = cacheControllerHolder; + underTest.attach(repository); + } + + Request randomRequest(final String path) { + return new Request.Builder().action(GET).path(path + random.nextInt(NUM_PATHS)).build(); + } + + void verifyValidGet() throws IOException { + Content content = underTest.get(new Context(repository, randomRequest("some/valid/indirect/path-"))); + try (InputStream in = content.openInputStream()) { + assertThat(toByteArray(in), is(ASSET_CONTENT)); + } + } + + void verifyBrokenGet() { + try { + underTest.get(new Context(repository, randomRequest("some/broken/indirect/path-"))); + fail("Expected IOException"); + } + catch (IOException e) { + assertThat(e.getMessage(), is("oops")); + } + } + + void verifyThreadLimit(Request request) throws IOException { + try { + underTest.get(new Context(repository, request)); + } + catch (CooperationException e) { + cooperationExceptionCount.incrementAndGet(); + } + } + + @Test + public void noDownloadCooperation() throws Exception { + int iterations = 2; + + underTest.configureCooperation(false, Time.seconds(0), Time.seconds(0), 0); + + ConcurrentRunner runner = new ConcurrentRunner(iterations, 60); + runner.addTask(NUM_CLIENTS, this::verifyValidGet); + runner.addTask(NUM_CLIENTS, this::verifyBrokenGet); + runner.go(); + + assertThat(runner.getRunInvocations(), is(runner.getTaskCount() * runner.getIterations())); + + // there will be a lot of upstream index requests, potentially up to 2 * NUM_CLIENTS + assertThat(upstreamRequests.count("meta/index.json"), is(greaterThan(NUM_CLIENTS))); + } + + @Test + public void downloadCooperation() throws Exception { + int iterations = 2; + + underTest.configureCooperation(true, PASSIVE_TIMEOUT, ACTIVE_TIMEOUT, 2 * NUM_CLIENTS); + + ConcurrentRunner runner = new ConcurrentRunner(iterations, 60); + runner.addTask(NUM_CLIENTS, this::verifyValidGet); + runner.addTask(NUM_CLIENTS, this::verifyBrokenGet); + runner.go(); + + assertThat(runner.getRunInvocations(), is(runner.getTaskCount() * runner.getIterations())); + + // there will only be one upstream index request + assertThat(upstreamRequests.count("meta/index.json"), is(1)); + + upstreamRequests.elementSet().forEach(element -> { + if (element.contains("broken")) { + // each broken request will go upstream 'iterations' times, + // as result is not cached, but is re-used via cooperation + assertThat(upstreamRequests.count(element), is(iterations)); + } + else { + // each valid request should only go upstream once + assertThat(upstreamRequests.count(element), is(1)); + } + }); + } + + @Test + public void limitCooperatingThreads() throws Exception { + int threadLimit = 10; + + underTest.configureCooperation(true, PASSIVE_TIMEOUT, ACTIVE_TIMEOUT, threadLimit); + + Request request = new Request.Builder().action(GET).path("some/fixed/path").build(); + + ConcurrentRunner runner = new ConcurrentRunner(1, 60); + runner.addTask(NUM_CLIENTS, () -> verifyThreadLimit(request)); + runner.go(); + + assertThat(runner.getRunInvocations(), is(runner.getTaskCount() * runner.getIterations())); + + // only one request should have made it upstream + assertThat(upstreamRequests.count(ASSET_PREFIX + "some/fixed/path"), is(1)); + + // majority of requests should have been cancelled to maintain thread limit + assertThat(cooperationExceptionCount.get(), is(NUM_CLIENTS - threadLimit)); + } + + @Test + public void downloadTimeoutsAreStaggered() throws Exception { + CooperatingFuture cooperatingFuture = new CooperatingFuture<>("testKey"); + + long[] downloadTimeMillis = new long[10]; + + long expectedGap = 200; + + downloadTimeMillis[0] = System.currentTimeMillis(); // first download + for (int i = 1; i < downloadTimeMillis.length; i++) { + + // random sleep representing some client-side work + LockSupport.parkNanos(Time.millis(random.nextInt((int) expectedGap)).toNanos()); + + // staggered sleep should bring us close to the expected gap + LockSupport.parkNanos(cooperatingFuture.staggerTimeout(Time.millis(expectedGap)).toNanos()); + + downloadTimeMillis[i] = System.currentTimeMillis(); // next download + } + + for (int i = 1; i < downloadTimeMillis.length; i++) { + long actualGap = downloadTimeMillis[i] - downloadTimeMillis[i - 1]; + assertThat(actualGap, allOf(greaterThanOrEqualTo(expectedGap - 10), lessThanOrEqualTo(expectedGap + 10))); + } + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupportTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupportTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5adcef3b43d2154e84c6aab27bd7ed1218e07651 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupportTest.java @@ -0,0 +1,273 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.proxy; + +import java.io.IOException; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.collect.AttributesMap; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.cache.CacheController; +import org.sonatype.nexus.repository.cache.CacheControllerHolder; +import org.sonatype.nexus.repository.cache.CacheInfo; +import org.sonatype.nexus.repository.httpclient.RemoteBlockedIOException; +import org.sonatype.nexus.repository.storage.MissingBlobException; +import org.sonatype.nexus.repository.storage.RetryDeniedException; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.repository.view.Context; + +import org.apache.http.StatusLine; +import org.apache.http.message.BasicHttpResponse; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Spy; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; + +/** + * Tests for the abstract class {@link ProxyFacetSupport} + */ +public class ProxyFacetSupportTest + extends TestSupport +{ + + @Spy + ProxyFacetSupport underTest = new ProxyFacetSupport() + { + @Nullable + @Override + protected Content getCachedContent(final Context context) throws IOException { + return null; + } + + @Override + protected Content store(final Context context, final Content content) throws IOException { + return null; + } + + @Override + protected void indicateVerified(final Context context, final Content content, final CacheInfo cacheInfo) + throws IOException + { + + } + + @Override + protected String getUrl(@Nonnull final Context context) { + return null; + } + }; + + @Mock + Content content; + + @Mock + StatusLine statusLine; + + @Mock + Content reFetchedContent; + + @Mock + AttributesMap attributesMap; + + @Mock + CacheInfo cacheInfo; + + @Mock + Context cachedContext; + + @Mock + Context missingContext; + + @Mock + CacheControllerHolder cacheControllerHolder; + + @Mock + CacheController cacheController; + + @Mock + Repository repository; + + @Before + public void setUp() throws Exception { + when(content.getAttributes()).thenReturn(attributesMap); + + when(attributesMap.get(CacheInfo.class)).thenReturn(cacheInfo); + + when(cacheControllerHolder.getContentCacheController()).thenReturn(cacheController); + + when(cachedContext.getRepository()).thenReturn(repository); + + when(missingContext.getRepository()).thenReturn(repository); + + underTest.cacheControllerHolder = cacheControllerHolder; + underTest.attach(repository); + } + + @Test + public void testGet() throws IOException { + when(cacheController.isStale(cacheInfo)).thenReturn(false); + doReturn(content).when(underTest).getCachedContent(cachedContext); + + Content foundContent = underTest.get(cachedContext); + + assertThat(foundContent, is(content)); + } + + @Test + public void testGet_stale() throws IOException { + when(cacheController.isStale(cacheInfo)).thenReturn(true); + doReturn(content).when(underTest).getCachedContent(cachedContext); + + doReturn(reFetchedContent).when(underTest).fetch(cachedContext, content); + doReturn(reFetchedContent).when(underTest).store(cachedContext, reFetchedContent); + + Content foundContent = underTest.get(cachedContext); + + assertThat(foundContent, is(reFetchedContent)); + } + + @Test + public void testGet_ProxyServiceException_contentReturnedIfCached() throws IOException { + when(cacheController.isStale(cacheInfo)).thenReturn(true); + doReturn(content).when(underTest).getCachedContent(cachedContext); + + doThrow(new ProxyServiceException(new BasicHttpResponse(null, 503, "Offline"))) + .when(underTest).fetch(cachedContext, content); + + Content foundContent = underTest.get(cachedContext); + + assertThat(foundContent, is(content)); + } + + @Test(expected = ProxyServiceException.class) + public void testGet_ProxyServiceException_thrownIfNotCached() throws IOException { + when(cacheController.isStale(cacheInfo)).thenReturn(true); + doReturn(null).when(underTest).getCachedContent(cachedContext); + + doThrow(new ProxyServiceException(new BasicHttpResponse(null, 503, "Offline"))) + .when(underTest).fetch(missingContext, null); + + underTest.get(missingContext); + } + + @Test + public void testGet_RemoteBlockedException_contentReturnedIfCached() throws IOException { + when(cacheController.isStale(cacheInfo)).thenReturn(true); + doReturn(content).when(underTest).getCachedContent(cachedContext); + + doThrow(new RemoteBlockedIOException("blocked")).when(underTest).fetch(cachedContext, content); + + Content foundContent = underTest.get(cachedContext); + + assertThat(foundContent, is(content)); + } + + @Test(expected = RemoteBlockedIOException.class) + public void testGet_RemoteBlockedException_thrownIfNotCached() throws IOException { + when(cacheController.isStale(cacheInfo)).thenReturn(true); + doReturn(null).when(underTest).getCachedContent(cachedContext); + + doThrow(new RemoteBlockedIOException("blocked")).when(underTest).fetch(missingContext, null); + + underTest.get(missingContext); + } + + @Test + public void testGet_IOException_contentReturnedIfCached() throws IOException { + when(cacheController.isStale(cacheInfo)).thenReturn(true); + doReturn(content).when(underTest).getCachedContent(cachedContext); + + doThrow(new IOException()).when(underTest).fetch(cachedContext, content); + + Content foundContent = underTest.get(cachedContext); + + assertThat(foundContent, is(content)); + } + + @Test(expected = IOException.class) + public void testGet_IOException_thrownIfNotCached() throws IOException { + when(cacheController.isStale(cacheInfo)).thenReturn(true); + doReturn(null).when(underTest).getCachedContent(cachedContext); + + doThrow(new IOException()).when(underTest).fetch(missingContext, null); + + underTest.get(missingContext); + } + + @Test + public void testGet_MissingBlobException() throws IOException { + RetryDeniedException e = new RetryDeniedException("Denied", new MissingBlobException(null)); + doThrow(e).when(underTest).getCachedContent(cachedContext); + + doReturn(reFetchedContent).when(underTest).fetch(cachedContext, null); + doReturn(reFetchedContent).when(underTest).store(cachedContext, reFetchedContent); + + Content foundContent = underTest.get(cachedContext); + + assertThat(foundContent, is(reFetchedContent)); + } + + @Test(expected = RetryDeniedException.class) + public void testGet_differentRetryReason() throws IOException { + RetryDeniedException e = new RetryDeniedException("Denied", new IOException()); + doThrow(e).when(underTest).getCachedContent(cachedContext); + + underTest.get(cachedContext); + } + + @Test + public void testBuildLogMessage_ContentFound_WithStatusLine() { + String message = underTest.buildLogContentMessage(content, statusLine); + + assertThat(message, containsString("Exception {} checking remote for update")); + assertThat(message, containsString("proxy repo {} failed to fetch {} with status line {}")); + assertThat(message, containsString("returning content from cache.")); + } + + @Test + public void testBuildLogMessage_ContentFound_WithoutStatusLine() { + String message = underTest.buildLogContentMessage(content, null); + + assertThat(message, containsString("Exception {} checking remote for update")); + assertThat(message, containsString("proxy repo {} failed to fetch {}")); + assertThat(message, containsString("returning content from cache.")); + } + + @Test + public void testBuildLogMessage_ContentNotFound_WithStatusLine() { + String message = underTest.buildLogContentMessage(null, statusLine); + + assertThat(message, containsString("Exception {} checking remote for update")); + assertThat(message, containsString("proxy repo {} failed to fetch {} with status line {}")); + assertThat(message, containsString("content not in cache.")); + } + + @Test + public void testBuildLogMessage_ContentNotFound_WithoutStatusLine() { + String message = underTest.buildLogContentMessage(null, null); + + assertThat(message, containsString("Exception {} checking remote for update")); + assertThat(message, containsString("proxy repo {} failed to fetch {}")); + assertThat(message, containsString("content not in cache.")); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/proxy/ProxyHandlerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/proxy/ProxyHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3a0e2e4f44ef13542897cf53571aae527e122e62 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/proxy/ProxyHandlerTest.java @@ -0,0 +1,129 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.proxy; + +import java.io.IOException; +import java.io.UncheckedIOException; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.http.HttpMethods; +import org.sonatype.nexus.repository.http.HttpStatus; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.repository.view.Response; + +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; + +public class ProxyHandlerTest + extends TestSupport +{ + @Mock + private HttpResponse httpResponse; + + @Mock + private StatusLine statusLine; + + @Mock + private Context context; + + @Mock + private Repository repository; + + @Mock + private ProxyFacet proxyFacet; + + @Mock + private Content content; + + @Mock + private Request request; + + private final ProxyHandler underTest = new ProxyHandler(); + + @Before + public void setUp() { + when(context.getRequest()).thenReturn(request); + when(context.getRepository()).thenReturn(repository); + when(repository.facet(ProxyFacet.class)).thenReturn(proxyFacet); + when(httpResponse.getStatusLine()).thenReturn(statusLine); + when(statusLine.toString()).thenReturn("status line"); + } + + @Test + public void testMethodNotAllowedReturns405Response() throws Exception { + when(request.getAction()).thenReturn(HttpMethods.LOCK); + assertStatusCode(underTest.handle(context), HttpStatus.METHOD_NOT_ALLOWED); + } + + @Test + public void testPayloadPresentReturns200Response() throws Exception { + when(request.getAction()).thenReturn(HttpMethods.GET); + when(proxyFacet.get(context)).thenReturn(content); + assertStatusCode(underTest.handle(context), HttpStatus.OK); + } + + @Test + public void testPayloaAbsentReturns404Response() throws Exception { + when(request.getAction()).thenReturn(HttpMethods.GET); + assertStatusCode(underTest.handle(context), HttpStatus.NOT_FOUND); + } + + @Test + public void testProxyServiceExceptionReturns503Response() throws Exception { + when(request.getAction()).thenReturn(HttpMethods.GET); + doThrow(new ProxyServiceException(httpResponse)).when(proxyFacet).get(context); + assertStatusCode(underTest.handle(context), HttpStatus.SERVICE_UNAVAILABLE); + } + + @Test + public void testCooperationExceptionReturns503ResponseWithMessage() throws Exception { + when(request.getAction()).thenReturn(HttpMethods.GET); + doThrow(new CooperationException("Cooperation failed")).when(proxyFacet).get(context); + Response response = underTest.handle(context); + assertStatusCode(response, HttpStatus.SERVICE_UNAVAILABLE); + assertStatusMessage(response, "Cooperation failed"); + } + + @Test + public void testIOExceptionReturns502Response() throws Exception { + when(request.getAction()).thenReturn(HttpMethods.GET); + doThrow(new IOException("message")).when(proxyFacet).get(context); + assertStatusCode(underTest.handle(context), HttpStatus.BAD_GATEWAY); + } + + @Test + public void testUncheckedIOExceptionReturns502Response() throws Exception { + when(request.getAction()).thenReturn(HttpMethods.GET); + doThrow(new UncheckedIOException(new IOException("message"))).when(proxyFacet).get(context); + assertStatusCode(underTest.handle(context), HttpStatus.BAD_GATEWAY); + } + + private void assertStatusCode(final Response response, final int statusCode) { + assertThat(response.getStatus().getCode(), is(statusCode)); + } + + private void assertStatusMessage(final Response response, final String statusMessage) { + assertThat(response.getStatus().getMessage(), is(statusMessage)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/purge/PurgeUnusedFacetImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/purge/PurgeUnusedFacetImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..43ce5b48d2c6edc74cfcf71014ffa41b86389f5a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/purge/PurgeUnusedFacetImplTest.java @@ -0,0 +1,160 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.purge; + +import java.util.Arrays; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.orient.entity.AttachedEntityMetadata; +import org.sonatype.nexus.orient.entity.EntityAdapter; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.repository.storage.StorageTx; +import org.sonatype.nexus.scheduling.CancelableHelper; +import org.sonatype.nexus.scheduling.TaskInterruptedException; +import org.sonatype.nexus.transaction.UnitOfWork; + +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.id.ORecordId; +import com.orientechnologies.orient.core.record.impl.ODocument; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runners.model.MultipleFailureException; +import org.mockito.Mock; + +import static com.google.common.collect.Lists.newArrayList; +import static java.lang.Thread.sleep; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class PurgeUnusedFacetImplTest + extends TestSupport +{ + @Mock + private ComponentEntityAdapter componentEntityAdapter; + + @Mock + private Repository repository; + + @Mock + private StorageFacet storageFacet; + + @Mock + private StorageTx tx; + + @Mock + private Iterable componentDocsIterable; + + @Mock + private Iterator componentDocsIterator; + + @Mock + private Iterable assetIterable; + + @Mock + private Iterator assetIterator; + + private List uncaught; + + private PurgeUnusedFacetImpl underTest; + + @Before + public void setup() throws Exception { + Bucket bucket = mockBucket(); + when(tx.findBucket(repository)).thenReturn(bucket); + when(tx.browse(any(), any())).thenReturn(componentDocsIterable); + when(tx.findAssets(any(), any(), any(), any())).thenReturn(assetIterable); + when(componentDocsIterable.iterator()).thenReturn(componentDocsIterator); + when(assetIterable.iterator()).thenReturn(assetIterator); + when(repository.facet(StorageFacet.class)).thenReturn(storageFacet); + when(storageFacet.txSupplier()).thenReturn(() -> tx); + + uncaught = newArrayList(); + + underTest = new PurgeUnusedFacetImpl(componentEntityAdapter); + underTest.attach(repository); + } + + @Test + public void testCancellable() throws Exception { + // infinite item loops + when(componentDocsIterator.hasNext()).thenReturn(true); + when(componentDocsIterator.next()).thenReturn(mock(ODocument.class)); + when(assetIterator.hasNext()).thenReturn(true); + when(assetIterator.next()).thenReturn(mock(Asset.class)); + + List cancelables = Arrays.asList( + () -> underTest.deleteUnusedComponents(new Date()), + () -> underTest.deleteUnusedAssets(new Date()) + ); + + for (Runnable cancelable : cancelables) { + AtomicBoolean canceled = new AtomicBoolean(false); + Thread t = createTaskThread(cancelable, canceled); + t.start(); + + sleep((long) (Math.random() * 1000)); // sleep for up to a second (emulate task running) + canceled.set(true); // cancel the task + t.join(5000); // ensure task thread ends + + if (t.isAlive()) { + fail("Task did not cancel"); + } + + if (uncaught.size() > 0) { + throw new MultipleFailureException(uncaught); + } + } + } + + private Thread createTaskThread(final Runnable action, final AtomicBoolean cancelFlag) { + Thread t = new Thread(() -> { + CancelableHelper.set(cancelFlag); + UnitOfWork.beginBatch(tx); + + action.run(); + }); + + t.setUncaughtExceptionHandler((thread, ex) -> { + if (ex instanceof TaskInterruptedException) { + return; + } + uncaught.add(ex); + }); + + return t; + } + + private Bucket mockBucket() { + EntityAdapter owner = mock(EntityAdapter.class); + ODocument document = mock(ODocument.class); + ORID orID = new ORecordId(1, 1); + when(document.getIdentity()).thenReturn(orID); + EntityMetadata entityMetadata = new AttachedEntityMetadata(owner, document); + + Bucket bucket = mock(Bucket.class); + when(bucket.getEntityMetadata()).thenReturn(entityMetadata); + return bucket; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/purge/PurgeUnusedTaskDescriptorTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/purge/PurgeUnusedTaskDescriptorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f6ce2d2ae1ee27613da02981a3072c1c68779982 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/purge/PurgeUnusedTaskDescriptorTest.java @@ -0,0 +1,55 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.purge; + +import java.util.List; + +import org.sonatype.nexus.formfields.FormField; +import org.sonatype.nexus.formfields.NumberTextFormField; +import org.sonatype.nexus.scheduling.TaskDescriptor; + +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.sonatype.nexus.repository.RepositoryTaskSupport.REPOSITORY_NAME_FIELD_ID; +import static org.sonatype.nexus.repository.purge.PurgeUnusedTaskDescriptor.LAST_USED_INIT_VALUE; +import static org.sonatype.nexus.repository.purge.PurgeUnusedTaskDescriptor.LAST_USED_MIN_VALUE; + +public class PurgeUnusedTaskDescriptorTest +{ + private TaskDescriptor purgeUnusedTaskDescriptor; + + @Before + public void before() { + purgeUnusedTaskDescriptor = new PurgeUnusedTaskDescriptor(); + } + + /** + * Ensures the construction of the descriptor has the appropriate/default values + */ + @Test + public void testDescriptorConfig() { + List formFields = purgeUnusedTaskDescriptor.getFormFields(); + + assertThat(formFields.size(), is(2)); + assertThat(formFields.get(0).getId(), is(REPOSITORY_NAME_FIELD_ID)); + + NumberTextFormField lastUsedField = (NumberTextFormField) formFields.get(1); + + assertThat(lastUsedField.getId(), is(PurgeUnusedTask.LAST_USED_FIELD_ID)); + assertThat(lastUsedField.getMinimumValue(), is(LAST_USED_MIN_VALUE)); + assertThat(lastUsedField.getInitialValue(), is(LAST_USED_INIT_VALUE)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/SearchUtilsTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/SearchUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4e2494e1ff7536b106086b7936f2a968a6fdb984 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/SearchUtilsTest.java @@ -0,0 +1,90 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest; + +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.rest.internal.resources.RepositoryManagerRESTAdapter; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class SearchUtilsTest + extends TestSupport +{ + + static final String VALID_SHA1_ATTRIBUTE_NAME = "assets.attributes.checksum.sha1"; + + static final String INVALID_SHA1_ATTRIBUTE_NAME = "asset.attributes.checksum.sha1"; + + static final String SHA1_ALIAS = "sha1"; + + @Mock + RepositoryManagerRESTAdapter repositoryManagerRESTAdapter; + + SearchUtils underTest; + + @Before + public void setup() { + + Map searchMappings = ImmutableMap.of( + "default", () -> ImmutableList.of( + new SearchMapping(SHA1_ALIAS, VALID_SHA1_ATTRIBUTE_NAME, "") + ) + ); + + underTest = new SearchUtils(repositoryManagerRESTAdapter, searchMappings); + } + + @Test + public void testIsAssetSearchParam_MappedAlias_Sha1() { + assertTrue(underTest.isAssetSearchParam(SHA1_ALIAS)); + } + + @Test + public void testIsAssetSearchParam_UnMapped_FullAssetAttributeName() { + assertTrue(underTest.isAssetSearchParam(VALID_SHA1_ATTRIBUTE_NAME)); + } + + @Test + public void testIsAssetSearchParam_UnMappedAlias_Returns_False() { + assertFalse(underTest.isAssetSearchParam("new.asset")); + } + + @Test + public void testIsAssetSearchParam_Invalid_Full_AssetAttribute() { + assertFalse(underTest.isAssetSearchParam(INVALID_SHA1_ATTRIBUTE_NAME)); + } + + @Test + public void testIsFullAssetAttributeName() { + assertTrue(underTest.isFullAssetAttributeName(VALID_SHA1_ATTRIBUTE_NAME)); + } + + @Test + public void testIsFullAssetAttributeName_Invalid_LongForm_Attribute_ReturnsFalse() { + assertFalse(underTest.isFullAssetAttributeName(INVALID_SHA1_ATTRIBUTE_NAME)); + } + + @Test + public void testIsFullAssetAttributeName_MappedAlias_ReturnsFalse() { + assertFalse(underTest.isFullAssetAttributeName(SHA1_ALIAS)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/api/AssetXOTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/api/AssetXOTest.java new file mode 100644 index 0000000000000000000000000000000000000000..172d68e333079795306670841d5d763478077e0e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/api/AssetXOTest.java @@ -0,0 +1,82 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.api; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.common.hash.HashAlgorithm; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.storage.Asset; + +import com.orientechnologies.orient.core.id.ORID; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import java.util.Map; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.Mockito.when; + +public class AssetXOTest + extends TestSupport +{ + @Mock + Repository repository; + + @Mock + Asset assetOne; + + @Mock + ORID assetOneORID; + + @Mock + EntityMetadata assetOneEntityMetadata; + + @Mock + EntityId assetOneEntityId; + + @Before + public void setup() { + Map checksum = Maps.newHashMap(ImmutableMap.of(HashAlgorithm.SHA1.name(), "87acec17cd9dcd20a716cc2cf67417b71c8a7016")); + + when(assetOne.name()).thenReturn("nameOne"); + when(assetOne.getEntityMetadata()).thenReturn(assetOneEntityMetadata); + when(assetOne.attributes()).thenReturn(new NestedAttributesMap(Asset.CHECKSUM, checksum)); + + when(assetOneORID.toString()).thenReturn("assetOneORID"); + + when(assetOneEntityMetadata.getId()).thenReturn(assetOneEntityId); + when(assetOneEntityId.getValue()).thenReturn("assetOne"); + + when(repository.getName()).thenReturn("maven-releases"); + when(repository.getUrl()).thenReturn("http://localhost:8081/repository/maven-releases"); + when(repository.getFormat()).thenReturn(new Format("maven2") {}); + } + + @Test + public void fromAsset() { + AssetXO assetXO = AssetXO.fromAsset(assetOne, repository); + + assertThat(assetXO.getId(), notNullValue()); + assertThat(assetXO.getPath(), is("nameOne")); + assertThat(assetXO.getDownloadUrl(), is("http://localhost:8081/repository/maven-releases/nameOne")); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/api/ComponentXODeserializerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/api/ComponentXODeserializerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e8b0c9a3d2149d80cef176613515a7f2fcc41b80 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/api/ComponentXODeserializerTest.java @@ -0,0 +1,164 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.api; + +import java.io.IOException; +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static com.google.common.collect.Sets.newHashSet; +import static java.util.Collections.emptyMap; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.common.decorator.DecoratorUtils.getDecoratedEntity; + +public class ComponentXODeserializerTest + extends TestSupport +{ + @Mock + private ComponentXOFactory componentXOFactory; + + @Mock + private DeserializationContext deserializationContext; + + private ObjectMapper objectMapper = new ObjectMapper(); + + private JsonFactory jsonFactory = new JsonFactory(); + + private ComponentXODeserializerExtension foo = new FooComponentXODeserializerExtension(); + + private ComponentXODeserializerExtension bar = new BarComponentXODeserializerExtension(); + + private ComponentXODeserializer underTest; + + @Before + public void setup() { + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + underTest = new ComponentXODeserializer(componentXOFactory, objectMapper, newHashSet(foo, bar)); + } + + @Test + public void deserialize() throws IOException { + ComponentXO componentXO = new FooComponentXO(new BarComponentXO(new DefaultComponentXO())); + when(componentXOFactory.createComponentXO()).thenReturn(componentXO); + + String json = "{\"id\": \"theid\", \"foo\": \"fiz\", \"bar\": \"biz\"}"; + JsonParser jsonParser = jsonFactory.createParser(json); + jsonParser.setCodec(objectMapper); + ComponentXO result = underTest.deserialize(jsonParser, deserializationContext); + + assertThat(result, notNullValue()); + assertThat(result.getId(), equalTo("theid")); + FooComponentXO fooComponentXO = getDecoratedEntity(result, FooComponentXO.class); + assertThat(fooComponentXO, notNullValue()); + assertThat(fooComponentXO.getFoo(), equalTo("fiz")); + BarComponentXO barComponentXO = getDecoratedEntity(result, BarComponentXO.class); + assertThat(barComponentXO, notNullValue()); + assertThat(barComponentXO.getBar(), equalTo("biz")); + } + + private class FooComponentXO + extends DecoratedComponentXO + implements ComponentXO + { + private String foo; + + FooComponentXO(final ComponentXO componentXO) { + super(componentXO); + } + + @Override + public Map getDecoratedExtraJsonAttributes() { + // not part of this test + return emptyMap(); + } + + public String getFoo() { + return foo; + } + + public void setFoo(final String foo) { + this.foo = foo; + } + } + + private class BarComponentXO + extends DecoratedComponentXO + implements ComponentXO + { + private String bar; + + BarComponentXO(final ComponentXO componentXO) { + super(componentXO); + } + + @Override + public Map getDecoratedExtraJsonAttributes() { + // not part of this test + return emptyMap(); + } + + public String getBar() { + return bar; + } + + public void setBar(final String bar) { + this.bar = bar; + } + } + + private class FooComponentXODeserializerExtension + implements ComponentXODeserializerExtension + { + @Override + public ComponentXO updateComponentXO(final ComponentXO componentXO, final JsonNode jsonNode) { + FooComponentXO fooComponentXO = getDecoratedEntity(componentXO, FooComponentXO.class); + if (fooComponentXO == null) { + return componentXO; + } + + JsonNode data = jsonNode.get("foo"); + fooComponentXO.setFoo(data.asText()); + return componentXO; + } + } + + private class BarComponentXODeserializerExtension + implements ComponentXODeserializerExtension + { + @Override + public ComponentXO updateComponentXO(final ComponentXO componentXO, final JsonNode jsonNode) { + BarComponentXO barComponentXO = getDecoratedEntity(componentXO, BarComponentXO.class); + if (barComponentXO == null) { + return componentXO; + } + + JsonNode data = jsonNode.get("bar"); + barComponentXO.setBar(data.asText()); + return componentXO; + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/api/ComponentXOFactoryTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/api/ComponentXOFactoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..183bd211510a96214feb371c613ba63d5bd34c18 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/api/ComponentXOFactoryTest.java @@ -0,0 +1,73 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.api; + +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ComponentXOFactoryTest + extends TestSupport +{ + @Mock + private ComponentXODecorator componentXODecorator; + + private ComponentXOFactory underTest; + + @Before + public void setup() { + underTest = new ComponentXOFactory(ImmutableSet.of(componentXODecorator)); + + ComponentXO componentXO = new DefaultComponentXO(); + when(componentXODecorator.decorate(any(ComponentXO.class))).thenReturn(new TestComponentXO(componentXO)); + } + + @Test + public void testComponentXO() { + ComponentXO componentXO = underTest.createComponentXO(); + assertThat(componentXO, notNullValue()); + assertThat(componentXO, instanceOf(TestComponentXO.class)); + verify(componentXODecorator).decorate(any(ComponentXO.class)); + TestComponentXO testComponentXO = (TestComponentXO) componentXO; + assertThat(testComponentXO.getWrappedObject(), instanceOf(DefaultComponentXO.class)); + assertThat(testComponentXO.getDecoratedExtraJsonAttributes(), hasEntry("foo", "bar")); + } + + private class TestComponentXO + extends DecoratedComponentXO + implements ComponentXO + { + TestComponentXO(final ComponentXO componentXO) { + super(componentXO); + } + + @Override + public Map getDecoratedExtraJsonAttributes() { + return ImmutableMap.of("foo", "bar"); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/api/RepositoryItemIDXOTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/api/RepositoryItemIDXOTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4e1044327c4bd77e5145d34775366df06edc4ddd --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/api/RepositoryItemIDXOTest.java @@ -0,0 +1,71 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.api; + +import javax.ws.rs.NotFoundException; +import javax.ws.rs.WebApplicationException; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.fail; +import static org.sonatype.nexus.repository.http.HttpStatus.NOT_FOUND; +import static org.sonatype.nexus.repository.http.HttpStatus.UNPROCESSABLE_ENTITY; + +public class RepositoryItemIDXOTest + extends TestSupport +{ + + @Test + public void testAssetXOID() { + String id = "testId"; + String repositoryName = "maven-releases"; + + RepositoryItemIDXO repositoryItemIDXO = new RepositoryItemIDXO(repositoryName, id); + + assertThat(repositoryItemIDXO.getValue().contains(id), is(false)); + assertThat(repositoryItemIDXO.getValue().contains(repositoryName), is(false)); + + RepositoryItemIDXO decoded = RepositoryItemIDXO.fromString(repositoryItemIDXO.getValue()); + + assertThat(decoded.getId(), is(id)); + assertThat(decoded.getRepositoryId(), is(repositoryName)); + assertThat(decoded.getValue(), is(repositoryItemIDXO.getValue())); + } + + @Test + public void unprocessableId() { + try { + RepositoryItemIDXO.fromString("test"); + fail("Expected a WebApplicationException."); + } + catch (WebApplicationException e) { + assertThat(e.getResponse().getStatus(), is(UNPROCESSABLE_ENTITY)); + } + + } + + @Test + public void testIllegalArgumentIsNotFound() { + try { + RepositoryItemIDXO.fromString("@"); + fail("Expected a NotFoundException."); + } + catch (NotFoundException e) { + assertThat(e.getResponse().getStatus(), is(NOT_FOUND)); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/AssetMapUtilsTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/AssetMapUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..716cd044362d5f4f50c5e2e155b06c33c6be0cd5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/AssetMapUtilsTest.java @@ -0,0 +1,213 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.rest.SearchUtils; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static com.google.common.collect.ImmutableMap.of; +import static java.util.Collections.emptyMap; +import static java.util.Optional.empty; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.repository.rest.internal.resources.AssetMapUtils.getValueFromAssetMap; +import static org.sonatype.nexus.repository.rest.internal.resources.ResourcesTestUtils.createAsset; + +public class AssetMapUtilsTest + extends TestSupport +{ + private static final String CLASSIFIER_ATTRIBUTE_NAME = "assets.attributes.maven2.classifier"; + + private static final String EXTENSION_ATTRIBUTE_NAME = "assets.attributes.maven2.extension"; + + private static final String ARTIFACT_ID_ATTRIBUTE_NAME = "assets.attributes.maven2.artifactId"; + + private Map assetMap; + + private Map assetMapWithClassifier; + + private AssetMapUtils underTest; + + @Mock + private SearchUtils searchUtils; + + @Before + public void setup() { + assetMap = createAsset("antlr.jar", "maven2", "first-sha1", of("extension", "jar")); + assetMapWithClassifier = createAsset("antlr.jar", "maven2", "first-sha1", of("extension", "jar", "classifier", "sources")); + underTest = new AssetMapUtils(searchUtils); + } + + @Test + public void testGetValueFromAssetMap_sha1() { + runGetValueFromAssetMapTest(assetMap, "assets.attributes.checksum.sha1", "first-sha1"); + } + + @Test + public void testGetValueFromAssetMap_MavenExtension() { + runGetValueFromAssetMapTest(assetMap, "assets.attributes.maven2.extension", "jar"); + } + + @Test + public void testGetValueFromAssetMap_BadQueryParam_ReturnsEmpty() { + runGetValueFromAssetMapTest(assetMap, "junk", null); + } + + @Test + public void testGetValueFromAssetMap_BadQueryParam2_ReturnsEmpty() { + runGetValueFromAssetMapTest(assetMap, "junk.junk", null); + } + + @Test + public void testGetValueFromAssetMap_MissingIdentifier_ReturnsEmpty() { + runGetValueFromAssetMapTest(assetMap, null, null); + } + + @Test + public void testGetValueFromAssetMap_EmptyMap() { + runGetValueFromAssetMapTest(emptyMap(), "assets.attributes.checksum.sha1", null); + } + + @Test + public void testGetValueFromAssetMap_IncludeClassifierFlag_ReturnsOne() { + Optional value = getValueFromAssetMap(assetMapWithClassifier, CLASSIFIER_ATTRIBUTE_NAME); + assertTrue(value.isPresent()); + assertThat(value.get(), equalTo("sources")); + } + + @Test + public void testGetValueFromAssetMap_IncludeClassifierFlag() { + Optional value = getValueFromAssetMap(assetMap, EXTENSION_ATTRIBUTE_NAME); + assertTrue(value.isPresent()); + assertThat(value.get(), equalTo("jar")); + } + + @Test + public void testFilterAsset_AssetMapClassifier_AssetParamClassifier() { + when(searchUtils.getFullAssetAttributeName(any(String.class))).thenReturn(CLASSIFIER_ATTRIBUTE_NAME); + MultivaluedMap assetParams = new MultivaluedHashMap<>(); + assetParams.add(CLASSIFIER_ATTRIBUTE_NAME, "sources"); + + assertTrue(underTest.filterAsset(assetMapWithClassifier, assetParams)); + } + + @Test + public void testFilterAsset_AssetMapNoClassifier_AssetParamNoClassifier() { + when(searchUtils.getFullAssetAttributeName(any(String.class))).thenReturn(EXTENSION_ATTRIBUTE_NAME); + MultivaluedMap assetParams = new MultivaluedHashMap<>(); + assetParams.add(EXTENSION_ATTRIBUTE_NAME, "jar"); + + assertTrue(underTest.filterAsset(assetMap, assetParams)); + } + + @Test + public void testFilterAsset_AssetMapNoClassifier_AssetParamClassifier() { + when(searchUtils.getFullAssetAttributeName(any(String.class))).thenReturn(CLASSIFIER_ATTRIBUTE_NAME); + MultivaluedMap assetParams = new MultivaluedHashMap<>(); + assetParams.add(CLASSIFIER_ATTRIBUTE_NAME, "sources"); + + assertFalse(underTest.filterAsset(assetMap, assetParams)); + } + + @Test + public void testFilterAsset_AssetMapClassifier_AssetParamNoClassifier() { + when(searchUtils.getFullAssetAttributeName(any(String.class))).thenReturn(EXTENSION_ATTRIBUTE_NAME); + MultivaluedMap assetParams = new MultivaluedHashMap<>(); + assetParams.add(EXTENSION_ATTRIBUTE_NAME, "sources"); + + assertFalse(underTest.filterAsset(assetMapWithClassifier, assetParams)); + } + + @Test + public void testFilterAsset_AssetMapNoClassifier_AssetParamEmptyClassifier() { + when(searchUtils.getFullAssetAttributeName(any(String.class))).thenReturn(CLASSIFIER_ATTRIBUTE_NAME); + MultivaluedMap assetParams = new MultivaluedHashMap<>(); + assetParams.add(CLASSIFIER_ATTRIBUTE_NAME, ""); + + assertTrue(underTest.filterAsset(assetMap, assetParams)); + } + + @Test + public void testFilterAsset_AssetMapClassifier_AssetParamEmptyClassifier() { + when(searchUtils.getFullAssetAttributeName(any(String.class))).thenReturn(CLASSIFIER_ATTRIBUTE_NAME); + MultivaluedMap assetParams = new MultivaluedHashMap<>(); + assetParams.add(CLASSIFIER_ATTRIBUTE_NAME, ""); + + assertFalse(underTest.filterAsset(assetMapWithClassifier, assetParams)); + } + + @Test + public void testFilterAsset_AssetMapClassifier_EmptyAssetParam() { + when(searchUtils.getFullAssetAttributeName(any(String.class))).thenReturn(CLASSIFIER_ATTRIBUTE_NAME); + MultivaluedMap assetParams = new MultivaluedHashMap<>(); + + assertTrue(underTest.filterAsset(assetMapWithClassifier, assetParams)); + } + + @Test + public void testFilterAsset_GetEmptyAssetParams() { + when(searchUtils.getFullAssetAttributeName(any(String.class))).thenReturn(any(String.class)); + List emptyAssetParamsList = underTest.getEmptyAssetParams(getPopulatedMultiValueMap()); + + assertThat(emptyAssetParamsList.size(), equalTo(1)); + } + + @Test + public void testFilterAsset_GetNonEmptyAssetParams() { + when(searchUtils.getFullAssetAttributeName(CLASSIFIER_ATTRIBUTE_NAME)).thenReturn(CLASSIFIER_ATTRIBUTE_NAME); + when(searchUtils.getFullAssetAttributeName(EXTENSION_ATTRIBUTE_NAME)).thenReturn(EXTENSION_ATTRIBUTE_NAME); + when(searchUtils.getFullAssetAttributeName(ARTIFACT_ID_ATTRIBUTE_NAME)).thenReturn(ARTIFACT_ID_ATTRIBUTE_NAME); + Map nonEmptyAssetParamsList = underTest.getNonEmptyAssetParams(getPopulatedMultiValueMap()); + + assertThat(nonEmptyAssetParamsList.size(), equalTo(2)); + assertTrue(nonEmptyAssetParamsList.containsKey(ARTIFACT_ID_ATTRIBUTE_NAME)); + assertThat(nonEmptyAssetParamsList.get(ARTIFACT_ID_ATTRIBUTE_NAME), equalTo("foo")); + assertTrue(nonEmptyAssetParamsList.containsKey(EXTENSION_ATTRIBUTE_NAME)); + assertThat(nonEmptyAssetParamsList.get(EXTENSION_ATTRIBUTE_NAME), equalTo("jar")); + assertFalse(nonEmptyAssetParamsList.containsKey(CLASSIFIER_ATTRIBUTE_NAME)); + } + + private MultivaluedMap getPopulatedMultiValueMap() { + MultivaluedMap assetParams = new MultivaluedHashMap<>(); + assetParams.add(CLASSIFIER_ATTRIBUTE_NAME, ""); + assetParams.add(EXTENSION_ATTRIBUTE_NAME, "jar"); + assetParams.add(ARTIFACT_ID_ATTRIBUTE_NAME, "foo"); + return assetParams; + } + + private void runGetValueFromAssetMapTest(final Map assetMap, final String query, final String match) { + Optional value = getValueFromAssetMap(assetMap, query); + if (match == null) { + assertThat(value, equalTo(empty())); + } + else { + assertTrue(value.isPresent()); + assertThat(value.get(), equalTo(match)); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/AssetsResourceTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/AssetsResourceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1450826a3e369b4aafdee7bab67bcf69339a2871 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/AssetsResourceTest.java @@ -0,0 +1,219 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import java.util.Arrays; + +import javax.ws.rs.NotFoundException; + +import org.sonatype.nexus.common.entity.ContinuationTokenHelper; +import org.sonatype.nexus.common.entity.DetachedEntityId; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.repository.browse.BrowseResult; +import org.sonatype.nexus.repository.browse.QueryOptions; +import org.sonatype.nexus.repository.rest.api.AssetXO; +import org.sonatype.nexus.repository.rest.internal.api.RepositoryItemIDXO; +import org.sonatype.nexus.repository.maintenance.MaintenanceService; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.internal.AssetContinuationTokenHelper; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; +import org.sonatype.nexus.rest.Page; + +import com.orientechnologies.orient.core.id.ORID; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyOrNullString; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.repository.http.HttpStatus.NOT_ACCEPTABLE; +import static org.sonatype.nexus.repository.http.HttpStatus.UNPROCESSABLE_ENTITY; + +public class AssetsResourceTest + extends RepositoryResourceTestSupport +{ + AssetsResource underTest; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Mock + BrowseResult browseResult; + + @Mock + AssetEntityAdapter assetEntityAdapter; + + @Mock + MaintenanceService maintenanceService; + + ContinuationTokenHelper continuationTokenHelper; + + @Mock + ORID assetOneORID; + + Asset assetOne; + + Asset assetTwo; + + AssetXO assetOneXO; + + AssetXO assetTwoXO; + + @Before + public void setUp() throws Exception { + assetOne = getMockedAsset("nameOne", "asset"); + when(assetOneORID.toString()).thenReturn("assetORID"); + + assetTwo = getMockedAsset("assetTwo", "asset-two-continuation"); + + assetOneXO = buildAssetXO("asset", "nameOne", "http://localhost:8081/repository/maven-releases/nameOne"); + assetTwoXO = buildAssetXO("assetTwo", "nameTwo", "http://localhost:8081/repository/maven-releases/nameTwo"); + + continuationTokenHelper = new AssetContinuationTokenHelper(assetEntityAdapter); + + underTest = new AssetsResource(browseService, repositoryManagerRESTAdapter, assetEntityAdapter, maintenanceService, + continuationTokenHelper); + } + + AssetXO buildAssetXO(String id, String path, String downloadUrl) { + AssetXO assetXo = new AssetXO(); + assetXo.setId(id); + assetXo.setPath(path); + assetXo.setDownloadUrl(downloadUrl); + return assetXo; + } + + @Captor + ArgumentCaptor queryOptionsCaptor; + + @Test + public void checkPath() { + assertThat(AssetsResource.RESOURCE_URI, is("/beta/assets")); + } + + @Test + public void testGetAssetsFirstPage() throws Exception { + when(browseResult.getTotal()).thenReturn(10L); + when(browseResult.getResults()).thenReturn(Arrays.asList(assetOne, assetTwo)); + + when(browseService.browseAssets(eq(mavenReleases), queryOptionsCaptor.capture())).thenReturn(browseResult); + + Page assetXOPage = underTest.getAssets(null, "maven-releases"); + assertThat(assetXOPage.getContinuationToken(), is("asset-two-continuation")); + assertThat(assetXOPage.getItems(), hasSize(2)); + } + + @Test + public void testGetAssetsLastPage() throws Exception { + when(browseResult.getTotal()).thenReturn(2l); + when(browseResult.getResults()).thenReturn(Arrays.asList(assetOne, assetTwo)); + + when(browseService.browseAssets(eq(mavenReleases), queryOptionsCaptor.capture())).thenReturn(browseResult); + + Page assetXOPage = underTest.getAssets(null, "maven-releases"); + assertThat(assetXOPage.getContinuationToken(), isEmptyOrNullString()); + assertThat(assetXOPage.getItems(), hasSize(2)); + } + + private void validateAssetOne(final AssetXO assetXO) { + assertThat(assetXO.getId(), notNullValue()); + assertThat(assetXO.getPath(), is("nameOne")); + assertThat(assetXO.getDownloadUrl(), is("http://localhost:8081/repository/maven-releases/nameOne")); + } + + @Test + public void testGetAssetById() { + RepositoryItemIDXO repositoryItemIDXO = new RepositoryItemIDXO("maven-releases", + "c0ac2ab6c5e93a4a3909f0830fdadfcd"); + + when(assetEntityAdapter.recordIdentity(new DetachedEntityId(repositoryItemIDXO.getId()))).thenReturn(assetOneORID); + when(browseService.getAssetById(assetOneORID, mavenReleases)).thenReturn(assetOne); + + AssetXO assetXO = underTest.getAssetById(repositoryItemIDXO.getValue()); + + validateAssetOne(assetXO); + } + + @Test + public void testChecksumExists() throws Exception { + RepositoryItemIDXO repositoryItemIDXO = new RepositoryItemIDXO("maven-releases", + "c0ac2ab6c5e93a4a3909f0830fdadfcd"); + + when(assetEntityAdapter.recordIdentity(new DetachedEntityId(repositoryItemIDXO.getId()))).thenReturn(assetOneORID); + when(browseService.getAssetById(assetOneORID, mavenReleases)).thenReturn(assetOne); + + AssetXO assetXO = underTest.getAssetById(repositoryItemIDXO.getValue()); + + assertThat(assetXO.getChecksum().get("sha1"), is("87acec17cd9dcd20a716cc2cf67417b71c8a7016")); + } + + @Test + public void testGetAssetById_illegalArgumentException() { + RepositoryItemIDXO repositoryItemIDXO = new RepositoryItemIDXO("maven-releases", + "c0ac2ab6c5e93a4a3909f0830fdadfcd"); + + //IllegalArgumentException is thrown when an id for a different entity type is supplied + doThrow(new IllegalArgumentException()).when(assetEntityAdapter) + .recordIdentity(new DetachedEntityId(repositoryItemIDXO.getId())); + + thrown.expect(hasProperty("response", hasProperty("status", is(UNPROCESSABLE_ENTITY)))); + underTest.getAssetById(repositoryItemIDXO.getValue()); + } + + @Test + public void testDeleteAsset() { + RepositoryItemIDXO repositoryItemIDXO = new RepositoryItemIDXO("maven-releases", + "c0ac2ab6c5e93a4a3909f0830fdadfcd"); + + DetachedEntityId entityId = new DetachedEntityId(repositoryItemIDXO.getId()); + when(assetEntityAdapter.recordIdentity(entityId)).thenReturn(assetOneORID); + when(browseService.getAssetById(assetOneORID, mavenReleases)).thenReturn(assetOne); + + underTest.deleteAsset(repositoryItemIDXO.getValue()); + verify(maintenanceService).deleteAsset(mavenReleases, assetOne); + + } + + @Test(expected = NotFoundException.class) + public void testGetAssetById_notFound() { + RepositoryItemIDXO repositoryItemIDXO = new RepositoryItemIDXO("maven-releases", + "f10bd0593de3b5e4b377049bcaa80d3e"); + + when(assetEntityAdapter.recordIdentity(new DetachedEntityId(repositoryItemIDXO.getId()))).thenReturn(assetOneORID); + when(browseService.getAssetById(assetOneORID, mavenReleases)).thenReturn(null); + + underTest.getAssetById(repositoryItemIDXO.getValue()); + } + + @Test + public void testBadContinuationTokenThrowsNotAccepted() throws Exception { + doThrow(new IllegalArgumentException()).when(assetEntityAdapter) + .recordIdentity(any(EntityId.class)); + + thrown.expect(hasProperty("response", hasProperty("status", is(NOT_ACCEPTABLE)))); + underTest.getAssets("whatever", "maven-releases"); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/ComponentsResourceTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/ComponentsResourceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..00a74a58c3c9eb64be50c933e91d684d0519ba6c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/ComponentsResourceTest.java @@ -0,0 +1,371 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collections; + +import javax.ws.rs.NotFoundException; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; + +import org.sonatype.nexus.common.entity.ContinuationTokenHelper; +import org.sonatype.nexus.common.entity.DetachedEntityId; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.repository.browse.BrowseResult; +import org.sonatype.nexus.repository.browse.QueryOptions; +import org.sonatype.nexus.repository.maintenance.MaintenanceService; +import org.sonatype.nexus.repository.rest.ComponentsResourceExtension; +import org.sonatype.nexus.repository.rest.api.ComponentXO; +import org.sonatype.nexus.repository.rest.api.ComponentXOFactory; +import org.sonatype.nexus.repository.rest.internal.api.RepositoryItemIDXO; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; +import org.sonatype.nexus.repository.storage.internal.ComponentContinuationTokenHelper; +import org.sonatype.nexus.repository.upload.ComponentUpload; +import org.sonatype.nexus.repository.upload.UploadConfiguration; +import org.sonatype.nexus.repository.upload.UploadManager; +import org.sonatype.nexus.rest.Page; + +import com.google.common.collect.ImmutableSet; +import com.orientechnologies.orient.core.id.ORID; +import org.hamcrest.CoreMatchers; +import org.jboss.resteasy.plugins.providers.multipart.InputPart; +import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Spy; + +import static java.lang.String.format; +import static java.util.Collections.emptySet; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyOrNullString; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.repository.http.HttpStatus.NOT_ACCEPTABLE; +import static org.sonatype.nexus.repository.http.HttpStatus.UNPROCESSABLE_ENTITY; + +public class ComponentsResourceTest + extends RepositoryResourceTestSupport +{ + private ComponentsResource underTest; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Mock + private ComponentEntityAdapter componentEntityAdapter; + + @Mock + private Component componentOne; + + @Mock + private ORID componentOneORID; + + @Mock + private EntityMetadata componentOneEntityMetadata; + + @Mock + private EntityId componentOneEntityId; + + @Mock + private BrowseResult componentOneBrowseResults; + + @Mock + private Component componentTwo; + + @Mock + private ORID componentTwoORID; + + @Mock + private EntityMetadata componentTwoEntityMetadata; + + @Mock + private EntityId componentTwoEntityId; + + @Mock + private BrowseResult componentTwoBrowseResults; + + @Captor + private ArgumentCaptor queryOptionsCaptor; + + @Mock + private BrowseResult componentBrowseResult; + + @Captor + private ArgumentCaptor detachedEntityIdCaptor; + + @Mock + private MaintenanceService maintenanceService; + + @Mock + private UploadManager uploadManager; + + @Mock + private UploadConfiguration uploadConfiguration; + + @Captor + private ArgumentCaptor componentUploadCaptor; + + @Spy + private ComponentsResourceExtension componentsResourceExtension = new TestComponentsResourceExtension(); + + private Asset assetOne; + + private Asset assetTwo; + + private void configureComponent(Component component, String group, String name, String version) { + when(component.group()).thenReturn(group); + when(component.name()).thenReturn(name); + when(component.version()).thenReturn(version); + } + + @Test + public void checkPath() { + assertThat(ComponentsResource.RESOURCE_URI, is("/beta/components")); + } + + @Before + public void setUp() throws Exception { + assetOne = getMockedAsset("assetOne", "assetOneId"); + assetTwo = getMockedAsset("assetTwo", "assetTwoId"); + + configureComponent(componentOne, "component-one-group", "component-one-name", "1.0.0"); + + when(componentOne.getEntityMetadata()).thenReturn(componentOneEntityMetadata); + when(componentOneEntityMetadata.getId()).thenReturn(componentOneEntityId); + when(componentOneEntityId.toString()).thenReturn("componentOneORID"); + when(componentOneEntityId.getValue()).thenReturn("componentOne"); + + when(browseService.browseComponentAssets(mavenReleases, "componentOne")).thenReturn(componentOneBrowseResults); + when(componentOneBrowseResults.getResults()).thenReturn(Collections.singletonList(assetOne)); + + configureComponent(componentTwo, null, "component-two-name", null); + + when(componentTwo.getEntityMetadata()).thenReturn(componentTwoEntityMetadata); + when(componentTwoEntityMetadata.getId()).thenReturn(componentTwoEntityId); + when(componentTwoEntityId.toString()).thenReturn("componentTwoORID"); + when(componentTwoEntityId.getValue()).thenReturn("88491cd1d185dd1318fdba4364e78406"); + + when(browseService.browseComponentAssets(mavenReleases, "88491cd1d185dd1318fdba4364e78406")) + .thenReturn(componentOneBrowseResults); + when(componentTwoBrowseResults.getResults()).thenReturn(Collections.singletonList(assetTwo)); + + ContinuationTokenHelper continuationTokenHelper = new ComponentContinuationTokenHelper(componentEntityAdapter); + + when(uploadConfiguration.isEnabled()).thenReturn(true); + + underTest = new ComponentsResource(repositoryManagerRESTAdapter, browseService, componentEntityAdapter, + maintenanceService, continuationTokenHelper, uploadManager, uploadConfiguration, + new ComponentXOFactory(emptySet()), ImmutableSet.of(componentsResourceExtension)); + } + + @Test + public void getComponents_firstPage() throws Exception { + when(componentBrowseResult.getTotal()).thenReturn(10L); + when(componentBrowseResult.getResults()).thenReturn(Arrays.asList(componentOne, componentTwo)); + + when(browseService.browseComponents(eq(mavenReleases), queryOptionsCaptor.capture())) + .thenReturn(componentBrowseResult); + + Page componentXOPage = underTest.getComponents(null, mavenReleasesId); + + assertThat(componentXOPage.getItems(), hasSize(2)); + assertThat(componentXOPage.getContinuationToken(), is("88491cd1d185dd1318fdba4364e78406")); + assertThat(queryOptionsCaptor.getValue().getLastId(), nullValue()); + verify(componentsResourceExtension, times(2)).updateComponentXO(any(ComponentXO.class), any(Component.class)); + + componentXOPage.getItems().stream().forEach(componentXO -> assertThat(componentXO.getAssets(), hasSize(1))); + } + + @Test(expected = NotFoundException.class) + public void getComponentById_notFound() throws Exception { + RepositoryItemIDXO repositoryItemXOID = getRepositoryItemIdXO(null); + + underTest.getComponentById(repositoryItemXOID.getValue()); + } + + @Test + public void getComponentById_illegalArgumentException() throws Exception { + RepositoryItemIDXO repositoryItemXOID = new RepositoryItemIDXO("maven-releases", + "f10bd0593de3b5e4b377049bcaa80d3e"); + + //IllegalArgumentException is thrown when an id for a different entity type is supplied + doThrow(new IllegalArgumentException()).when(componentEntityAdapter) + .recordIdentity(new DetachedEntityId(repositoryItemXOID.getId())); + + thrown.expect(hasProperty("response", hasProperty("status", is(UNPROCESSABLE_ENTITY)))); + underTest.getComponentById(repositoryItemXOID.getValue()); + } + + @Test + public void deleteComponent() throws Exception { + RepositoryItemIDXO repositoryItemXOID = getRepositoryItemIdXO(componentOne); + + underTest.deleteComponent(repositoryItemXOID.getValue()); + + verify(maintenanceService).deleteComponent(mavenReleases, componentOne); + } + + @Test(expected = NotFoundException.class) + public void deleteComponent_notFound() throws Exception { + RepositoryItemIDXO repositoryItemXOID = getRepositoryItemIdXO(null); + + underTest.deleteComponent(repositoryItemXOID.getValue()); + } + + @Test + public void invalidContinuationTokenReturnsNotAcceptable() { + doThrow(new IllegalArgumentException()).when(componentEntityAdapter) + .recordIdentity(detachedEntityIdCaptor.capture()); + + thrown.expect(hasProperty("response", hasProperty("status", is(NOT_ACCEPTABLE)))); + underTest.getComponents("whatever", mavenReleasesId); + } + + private RepositoryItemIDXO getRepositoryItemIdXO(Component resultComponent) { + RepositoryItemIDXO repositoryItemXOID = new RepositoryItemIDXO("maven-releases", + "f10bd0593de3b5e4b377049bcaa80d3e"); + + when(componentEntityAdapter.recordIdentity(new DetachedEntityId(repositoryItemXOID.getId()))) + .thenReturn(componentOneORID); + when(browseService.getComponentById(componentOneORID, mavenReleases)).thenReturn(resultComponent); + + return repositoryItemXOID; + } + + @Test + @SuppressWarnings("unchecked") + public void uploadComponent() throws Exception { + MultipartInput multipart = mock(MultipartInput.class); + InputPart filePart = mockStreamInputPart("asset", "content"); + MultivaluedMap fileHeaders = mock(MultivaluedMap.class); + when(filePart.getHeaders()).thenReturn(fileHeaders); + when(fileHeaders.getFirst("Content-Disposition")).thenReturn("form-data; name=asset; filename=foo-0.42.jar"); + InputPart groupPart = mockTextInputPart("groupId", "com.example"); + InputPart artifactPart = mockTextInputPart("artifactId", "foo"); + InputPart versionPart = mockTextInputPart("version", "0.42"); + InputPart extensionPart = mockTextInputPart("asset.extension", "jar"); + when(multipart.getParts()).thenReturn(Arrays.asList(filePart, groupPart, artifactPart, versionPart, extensionPart)); + + underTest.uploadComponent(mavenReleasesId, multipart); + + verify(uploadManager).handle(eq(mavenReleases), componentUploadCaptor.capture()); + assertThat(componentUploadCaptor.getValue().getFields().size(), is(3)); + assertThat(componentUploadCaptor.getValue().getAssetUploads().size(), is(1)); + assertThat(componentUploadCaptor.getValue().getAssetUploads().get(0).getFields().size(), is(1)); + } + + @Test + public void uploadComponentIsHiddenWhenFeatureFlagIsNotEnabled() throws Exception { + when(uploadConfiguration.isEnabled()).thenReturn(false); + + try { + underTest.uploadComponent(mavenReleasesId, null); + fail("Expected a WebApplicationException(NOT_FOUND) to be thrown"); + } + catch (WebApplicationException e) { + assertThat(e.getResponse(), is(CoreMatchers.notNullValue())); + assertThat(e.getResponse().getStatus(), is(404)); + } + } + + private static InputPart mockTextInputPart(String name, String value) throws IOException { + final InputPart mock = mock(InputPart.class); + when(mock.getMediaType()).thenReturn(MediaType.TEXT_PLAIN_TYPE); + MultivaluedMap headers = new MultivaluedHashMap<>(); + headers.putSingle(HttpHeaders.CONTENT_DISPOSITION, format("form-data; name=\"%s\"", name)); + when(mock.getHeaders()).thenReturn(headers); + when(mock.getBodyAsString()).thenReturn(value); + return mock; + } + + private static InputPart mockStreamInputPart(String name, String value) throws IOException { + final InputPart mock = mock(InputPart.class); + when(mock.getMediaType()).thenReturn(MediaType.APPLICATION_OCTET_STREAM_TYPE); + MultivaluedMap headers = new MultivaluedHashMap<>(); + headers.putSingle(HttpHeaders.CONTENT_DISPOSITION, format("form-data; name=\"%s\"", name)); + when(mock.getHeaders()).thenReturn(headers); + when(mock.getBody(InputStream.class, null)).thenReturn(new ByteArrayInputStream(value.getBytes())); + return mock; + } + + @Test + public void getComponents_lastPage() throws Exception { + when(componentBrowseResult.getTotal()).thenReturn(2L); + when(componentBrowseResult.getResults()).thenReturn(Arrays.asList(componentOne, componentTwo)); + + when(browseService.browseComponents(eq(mavenReleases), queryOptionsCaptor.capture())) + .thenReturn(componentBrowseResult); + + when(componentEntityAdapter.recordIdentity(detachedEntityIdCaptor.capture())).thenReturn(componentTwoORID); + + Page componentXOPage = underTest.getComponents("88491cd1d185dd1318fdba4364e78406", mavenReleasesId); + + assertThat(componentXOPage.getItems(), hasSize(2)); + assertThat(componentXOPage.getContinuationToken(), isEmptyOrNullString()); + assertThat(queryOptionsCaptor.getValue().getLastId(), notNullValue()); + assertThat(detachedEntityIdCaptor.getValue(), notNullValue()); + verify(componentsResourceExtension, times(2)).updateComponentXO(any(ComponentXO.class), any(Component.class)); + + componentXOPage.getItems().stream().forEach(componentXO -> assertThat(componentXO.getAssets(), hasSize(1))); + } + + @Test + public void getComponentById() throws Exception { + RepositoryItemIDXO repositoryItemXOID = getRepositoryItemIdXO(componentOne); + + ComponentXO componentXO = underTest.getComponentById(repositoryItemXOID.getValue()); + + assertThat(componentXO, notNullValue()); + assertThat(componentXO.getGroup(), is("component-one-group")); + assertThat(componentXO.getName(), is("component-one-name")); + assertThat(componentXO.getVersion(), is("1.0.0")); + assertThat(componentXO.getAssets(), hasSize(1)); + + verify(componentsResourceExtension).updateComponentXO(any(ComponentXO.class), any(Component.class)); + } + + private class TestComponentsResourceExtension + implements ComponentsResourceExtension + { + @Override + public ComponentXO updateComponentXO(final ComponentXO componentXO, final Component component) { + return componentXO; + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b0aac50dc5736b83cd3fe43e4792a8358c22336b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryManagerRESTAdapterImplTest.java @@ -0,0 +1,161 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import javax.ws.rs.NotFoundException; +import javax.ws.rs.WebApplicationException; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.security.RepositoryContentSelectorPermission; +import org.sonatype.nexus.repository.security.RepositoryViewPermission; +import org.sonatype.nexus.security.SecurityHelper; +import org.sonatype.nexus.selector.SelectorConfiguration; +import org.sonatype.nexus.selector.SelectorManager; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; + +import static java.util.Collections.singletonList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.security.BreadActions.BROWSE; +import static org.sonatype.nexus.security.BreadActions.READ; + +public class RepositoryManagerRESTAdapterImplTest + extends TestSupport +{ + @Mock + RepositoryManager repositoryManager; + + @Mock + Repository repository; + + @Mock + SecurityHelper securityHelper; + + @Captor + ArgumentCaptor repositoryViewPermissionArgumentCaptor; + + @Captor + ArgumentCaptor repositoryContentSelectorPermissionArgumentCaptor; + + @Mock + Format repositoryFormat; + + @Mock + SelectorConfiguration selectorConfiguration; + + @Mock + SelectorManager selectorManager; + + final String REPOSITORY_NAME = "test"; + + RepositoryManagerRESTAdapterImpl underTest; + + @Before + public void setUp() throws Exception { + when(repositoryManager.get(REPOSITORY_NAME)).thenReturn(repository); + + when(repository.getFormat()).thenReturn(repositoryFormat); + + when(selectorManager.browse()).thenReturn(singletonList(selectorConfiguration)); + when(selectorConfiguration.getName()).thenReturn("selector"); + + when(repository.getName()).thenReturn(REPOSITORY_NAME); + + when(repositoryFormat.getValue()).thenReturn("m2"); + + underTest = new RepositoryManagerRESTAdapterImpl(repositoryManager, securityHelper, selectorManager); + } + + @Test + public void getRepository_allPermissions() throws Exception { + configurePermissions(true, true, true); + assertThat(underTest.getRepository(REPOSITORY_NAME), is(repository)); + } + + @Test + public void getRepository_browseOnly() throws Exception { + configurePermissions(true, false, false); + assertThat(underTest.getRepository(REPOSITORY_NAME), is(repository)); + } + + @Test + public void getRepository_contentSelectorOnly() throws Exception { + configurePermissions(false, false, true); + assertThat(underTest.getRepository(REPOSITORY_NAME), is(repository)); + } + + @Test + public void getRepository_readOnlyReturnsForbidden() throws Exception { + configurePermissions(false, true, false); + + try { + underTest.getRepository(REPOSITORY_NAME); + fail(); //should have thrown exception + } + catch (WebApplicationException e) { + assertThat(e.getResponse().getStatus(), is(403)); + } + } + + @Test(expected = NotFoundException.class) + public void getRepository_notFoundWithNoPermissions() { + configurePermissions(false, false, false); + underTest.getRepository(REPOSITORY_NAME); + } + + private void configurePermissions(final boolean permitBrowse, + final boolean permitRead, + final boolean permitViaContentSelector) + { + when(securityHelper + .anyPermitted(new RepositoryViewPermission(repository.getFormat().getValue(), repository.getName(), BROWSE))) + .thenReturn(permitBrowse); + + when(securityHelper + .anyPermitted(new RepositoryViewPermission(repository.getFormat().getValue(), repository.getName(), READ))) + .thenReturn(permitRead); + + when(securityHelper.anyPermitted( + new RepositoryContentSelectorPermission(selectorConfiguration.getName(), repository.getFormat().getValue(), + repository.getName(), + singletonList(BROWSE)))) + .thenReturn(permitViaContentSelector); + } + + + @Test(expected = NotFoundException.class) + public void getRepository_notFound() { + underTest.getRepository("notFound"); + } + + @Test + public void getRepository_null() { + try { + underTest.getRepository(null); + fail(); //should have thrown exception + } + catch (WebApplicationException e) { + assertThat(e.getResponse().getStatus(), is(422)); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryResourceTestSupport.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryResourceTestSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..29488c7f4841cdbebe799e306ddfb9c25a3c5339 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/RepositoryResourceTestSupport.java @@ -0,0 +1,134 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityMetadata; +import org.sonatype.nexus.common.hash.HashAlgorithm; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.BrowseService; +import org.sonatype.nexus.repository.rest.SearchMapping; +import org.sonatype.nexus.repository.rest.SearchMappings; +import org.sonatype.nexus.repository.rest.SearchUtils; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.repository.storage.StorageTx; + +import com.google.common.base.Supplier; +import org.junit.Before; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Map; + +import static org.mockito.Mockito.when; + +public abstract class RepositoryResourceTestSupport + extends TestSupport +{ + @Mock + RepositoryManagerRESTAdapter repositoryManagerRESTAdapter; + + @Mock + BrowseService browseService; + + @Mock + Format format; + + @Mock + Repository mavenReleases; + + @Mock + StorageFacet storageFacet; + + @Mock + StorageTx storageTx; + + Supplier storageTxSupplier; + + String mavenReleasesId = "maven-releases"; + + Map searchMappings = ImmutableMap.of( + "default", () -> ImmutableList.of( + new SearchMapping("sha1", "assets.attributes.checksum.sha1", ""), + new SearchMapping("sha256", "assets.attributes.checksum.sha256", ""), + new SearchMapping("maven.extension", "assets.attributes.maven2.extension", ""), + new SearchMapping("maven.classifier", "assets.attributes.maven2.classifier", ""), + new SearchMapping("mvn.extension", "assets.attributes.maven2.extension", "") + ) + ); + + SearchUtils searchUtils; + + AssetMapUtils assetMapUtils; + + @Before + public void init() { + configureMockedRepository(mavenReleases, mavenReleasesId, "http://localhost:8081/repository/maven-releases"); + + when(format.toString()).thenReturn("maven2"); + when(format.getValue()).thenReturn("maven2"); + + storageTxSupplier = () -> storageTx; + when(storageFacet.txSupplier()).thenReturn(storageTxSupplier); + + searchUtils = new SearchUtils(repositoryManagerRESTAdapter, searchMappings); + + assetMapUtils = new AssetMapUtils(searchUtils); + } + + protected void configureMockedRepository(Repository repository, + String name, + String url) + { + when(repositoryManagerRESTAdapter.getRepository(name)).thenReturn(repository); + when(repository.getUrl()).thenReturn(url); + when(repository.getName()).thenReturn(name); + when(repository.getFormat()).thenReturn(format); + when(repository.facet(StorageFacet.class)).thenReturn(storageFacet); + } + + Asset getMockedAsset(String name, String idValue) { + Map checksum = Maps.newHashMap(ImmutableMap.of(HashAlgorithm.SHA1.name(), "87acec17cd9dcd20a716cc2cf67417b71c8a7016")); + + AssetMocks assetMocks = new AssetMocks(); + MockitoAnnotations.initMocks(assetMocks); + + when(assetMocks.asset.name()).thenReturn(name); + when(assetMocks.asset.getEntityMetadata()).thenReturn(assetMocks.assetEntityMetadata); + when(assetMocks.asset.attributes()).thenReturn(new NestedAttributesMap("attributes", ImmutableMap.of(Asset.CHECKSUM, checksum))); + + when(assetMocks.assetEntityMetadata.getId()).thenReturn(assetMocks.assetEntityId); + when(assetMocks.assetEntityId.getValue()).thenReturn(idValue); + + return assetMocks.asset; + } + + class AssetMocks + { + @Mock + Asset asset; + + @Mock + EntityMetadata assetEntityMetadata; + + @Mock + EntityId assetEntityId; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/ResourcesTestUtils.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/ResourcesTestUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..7e72085c4ed06a2e39e92f71883e1006686b60f0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/ResourcesTestUtils.java @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import com.google.common.collect.ImmutableMap; + +import static com.google.common.collect.ImmutableMap.of; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.ASSETS; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.ATTRIBUTES; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.FORMAT; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.GROUP; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.ID; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.NAME; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.REPOSITORY_NAME; +import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.VERSION; + +/** + * Test utility class for API resources + * + * @since 3.6.1 + */ +class ResourcesTestUtils +{ + static Map createComponent(final String name, + final String repository, + final String format, + final String group, + final String version, + final List assets) + { + return ImmutableMap.builder() + .put(NAME, name) + .put(FORMAT, format) + .put(REPOSITORY_NAME, repository) + .put(GROUP, group) + .put(VERSION, version) + .put(ASSETS, assets) + .build(); + } + + static Map createAsset(final String name, + final String format, + final String sha1, + final Map formatAttributes) + { + Map attributes = of("cache", of("last_verified", 1234), "checksum", of("sha1", sha1), format, + formatAttributes); + return of(NAME, name, ID, UUID.randomUUID().toString(), ATTRIBUTES, attributes); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/SearchResourceTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/SearchResourceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..206df2f51eb3f63202af4ecbe60efbc7e9810c2e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/SearchResourceTest.java @@ -0,0 +1,523 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import java.net.URI; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.BrowseResult; +import org.sonatype.nexus.repository.rest.SearchResourceExtension; +import org.sonatype.nexus.repository.rest.api.AssetXO; +import org.sonatype.nexus.repository.rest.api.ComponentXO; +import org.sonatype.nexus.repository.rest.api.ComponentXOFactory; +import org.sonatype.nexus.repository.search.SearchService; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.rest.Page; + +import com.google.common.collect.ImmutableSet; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; +import org.jboss.resteasy.spi.ResteasyUriInfo; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Spy; + +import static com.google.common.collect.ImmutableMap.of; +import static com.google.common.collect.Lists.newArrayList; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; +import static org.elasticsearch.index.query.QueryBuilders.boolQuery; +import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; +import static org.elasticsearch.index.query.QueryBuilders.termQuery; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.collection.IsMapContaining.hasKey; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.repository.rest.internal.resources.ResourcesTestUtils.createAsset; +import static org.sonatype.nexus.repository.rest.internal.resources.ResourcesTestUtils.createComponent; + +public class SearchResourceTest + extends RepositoryResourceTestSupport +{ + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Mock + SearchService searchService; + + @Mock + SearchResponse searchResponse; + + @Mock + SearchHits searchHits; + + @Mock + SearchHit searchHitMaven; + + @Mock + SearchHit searchHitMaven_withMultipleAssets; + + @Mock + SearchHit searchHitNpm; + + @Captor + ArgumentCaptor queryBuilderArgumentCaptor; + + @Mock + Repository repository; + + @Mock + BrowseResult browseResult; + + @Spy + SearchResourceExtension searchResourceExtension = new TestSearchResourceExtension(); + + SearchResource underTest; + + @Before + public void setup() { + configureMockedRepository(repository, "test-repo", "http://localhost:8081/test"); + setupResponse(); + + underTest = new SearchResource(searchUtils, assetMapUtils, browseService, searchService, new TokenEncoder(), + new ComponentXOFactory(emptySet()), ImmutableSet.of(searchResourceExtension)); + } + + private void setupResponse() { + when(searchResponse.getHits()).thenReturn(searchHits); + + List assets = newArrayList( + createAsset("antlr.jar", "maven2", "first-sha1", of("extension", "jar", "classifier", "foo")), + createAsset("antlr.pom", "maven2", "second-sha1", of("extension", "pom")) + ); + Map component = createComponent("foo", "test-repo", "format", "test", "1.0", assets); + + when(searchHitMaven.sourceAsMap()).thenReturn(component); + when(searchHitMaven.getSource()).thenReturn(component); + when(searchHitMaven.getId()).thenReturn("id1"); + + List mulitple_assets = newArrayList( + createAsset("antlr-fooz.jar", "maven2", "first-sha1", of("extension", "jar", "classifier", "fooz")), + createAsset("antlr.jar", "maven2", "first-sha1", of("extension", "jar")), + createAsset("antlr.pom", "maven2", "first-sha1", of("extension", "pom")) + ); + Map component_with_multiple_assets = createComponent("fooz", "test-repo", "maven2", "test", "2.0", mulitple_assets); + + when(searchHitMaven_withMultipleAssets.sourceAsMap()).thenReturn(component_with_multiple_assets); + when(searchHitMaven_withMultipleAssets.getSource()).thenReturn(component_with_multiple_assets); + when(searchHitMaven_withMultipleAssets.getId()).thenReturn("id2"); + + when(browseService.browseComponentAssets(eq(repository), anyString())).thenReturn(browseResult); + Asset mockedAsset = getMockedAsset("first", "one"); + Asset mockedAsset1 = getMockedAsset("second", "two"); + + when(browseResult.getResults()).thenReturn(asList(mockedAsset, + mockedAsset1)); + + List assets2 = newArrayList( + createAsset("bar.one", "npm", "third-sha1", of("extension", "one")), + createAsset("bar.two", "npm", "fourth-sha1", of("extension", "two")), + createAsset("bar.three", "npm", "fifth-sha1", of("extension", "three")) + ); + Map component2 = createComponent("bar", "test-repo", "npm", "group2", "2.0", assets2); + when(searchHitNpm.sourceAsMap()).thenReturn(component2); + when(searchHitNpm.getSource()).thenReturn(component2); + when(searchHitNpm.getId()).thenReturn("id2"); + + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitMaven, searchHitNpm}); + } + + @Test + public void testSearch() { + //the expected query + QueryBuilder expected = boolQuery() + .filter(termQuery("format", "maven2")); + + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + + Page componentPage = underTest.search(null, uriInfo("?format=maven2")); + + List items = componentPage.getItems(); + + assertThat(items, hasSize(2)); + + ComponentXO componentXO = items.stream().filter(item -> item.getName().equals("foo")).findFirst().get(); + assertThat(componentXO.getGroup(), is("test")); + assertThat(componentXO.getVersion(), is("1.0")); + + ComponentXO componentXO1 = items.stream().filter(item -> item.getName().equals("bar")).findFirst().get(); + assertThat(componentXO1.getGroup(), is("group2")); + assertThat(componentXO1.getVersion(), is("2.0")); + assertThat(componentXO1.getAssets().get(0).getChecksum().get("sha1"), is("87acec17cd9dcd20a716cc2cf67417b71c8a7016")); + + assertThat(queryBuilderArgumentCaptor.getValue().toString(), is(expected.toString())); + verify(searchResourceExtension, times(2)).updateComponentXO(any(ComponentXO.class), any(SearchHit.class)); + } + + @Test + public void testSearchWithChecksum() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitNpm}); + + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + + Page assets = underTest.searchAssets(null, uriInfo("?format=npm")); + + List items = assets.getItems(); + + assertThat(items, hasSize(3)); + + AssetXO assetXO = items.stream().filter(item -> item.getPath().equals("bar.one")).findFirst().get(); + assertThat(assetXO.getChecksum().get("sha1"), is("third-sha1")); + } + + @Test + public void testSearch_Multiple_By_Extension_Empty_Classifier_Return_Maven_Asset_Without_Classifier() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitMaven_withMultipleAssets}); + + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + + Page assets = underTest.searchAssets(null, + uriInfo("?assets.attributes.maven2.extension=jar&maven.classifier")); + List items = assets.getItems(); + assertThat(items, hasSize(1)); + assertThat(items.get(0).getPath(), is("antlr.jar")); + } + + @Test + public void testSearch_Multiple_By_AVE_And_Empty_Classifier_Return_Maven_WithOut_Classifier() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitMaven_withMultipleAssets}); + + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + + Page assets = underTest.searchAssets(null, + uriInfo("?maven.artifactId=antlr&maven.version=2.0&maven.extension=jar&maven.classifier")); + List items = assets.getItems(); + assertThat(items, hasSize(1)); + assertThat(items.get(0).getPath(), is("antlr.jar")); + + assets = underTest.searchAssets(null, + uriInfo("?maven.artifactId=antlr&maven.version=2.0&maven.extension=pom&maven.classifier")); + items = assets.getItems(); + assertThat(items, hasSize(1)); + assertThat(items.get(0).getPath(), is("antlr.pom")); + } + + @Test + public void testSearch_Multiple_By_Classifier_Return_Maven_With_Classifier() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitMaven_withMultipleAssets}); + + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + + Page assets = underTest.searchAssets(null, uriInfo("?assets.attributes.maven2.classifier=fooz")); + List items = assets.getItems(); + assertThat(items, hasSize(1)); + assertThat(items.get(0).getPath(), is("antlr-fooz.jar")); + } + + @Test + public void testSearch_Multiple_By_GA_And_Classifier_Return_Maven_Asset_With_Classifier() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitMaven_withMultipleAssets}); + + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + + Page assets = underTest.searchAssets(null, + uriInfo("?maven.artifactId=antlr&maven.version=2.0&assets.attributes.maven2.classifier=fooz")); + List items = assets.getItems(); + assertThat(items, hasSize(1)); + assertThat(items.get(0).getPath(), is("antlr-fooz.jar")); + } + + @Test + public void testSearch_Using_Long_And_Short_AssetParamNames() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitMaven, searchHitNpm}); + + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + + Page assets_longName = underTest.searchAssets(null, uriInfo("?assets.attributes.maven2.extension=jar")); + List items_longName = assets_longName.getItems(); + assertThat(items_longName, hasSize(1)); + + Page assets_shortName = underTest.searchAssets(null, uriInfo("?maven.extension=jar")); + List items_shortName = assets_shortName.getItems(); + assertThat(items_shortName, hasSize(1)); + } + + @Test + public void testSearch_When_Multiple_Aliases_For_An_AssetAttribute() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitMaven, searchHitNpm}); + + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + + Page assets_shortName = underTest.searchAssets(null, uriInfo("?maven.extension=jar")); + List items_shortName = assets_shortName.getItems(); + assertThat(items_shortName, hasSize(1)); + + //Search using alternate alias mapped to the same attribute as maven.extension + Page assets_alternateName = underTest.searchAssets(null, uriInfo("?mvn.extension=jar")); + List items_alternateName = assets_alternateName.getItems(); + assertThat(items_alternateName, hasSize(1)); + } + + @Test + public void testSearch_With_UnMapped_Long_AssetAttribute() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitMaven}); + + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + + //Positive case, 'classifier' is unmapped + Page assets_validAttribute = underTest.searchAssets(null, uriInfo("?assets.attributes.maven2.classifier=foo")); + List items_assets_validAttribute = assets_validAttribute.getItems(); + assertThat(items_assets_validAttribute , hasSize(1)); + + //Negative case + Page assets_inValidAttribute = underTest.searchAssets(null, uriInfo("?assets.attributes.maven3.classifier=foo")); + List items_inValidAttribute = assets_inValidAttribute.getItems(); + assertThat(items_inValidAttribute, hasSize(0)); + } + + @Test + public void testSearchAndDownload_NoAssetParams_WillReturnAll() { + // mock Elastic is only returning npm + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitNpm}); + + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + + Page assets = underTest.searchAssets(null, uriInfo("?format=npm")); + + List items = assets.getItems(); + + assertThat(items, hasSize(3)); + + AssetXO assetXO = items.stream().filter(item -> item.getPath().equals("bar.one")).findFirst().get(); + assertThat(assetXO.getRepository(), is("test-repo")); + assertThat(assetXO.getDownloadUrl(), is("http://localhost:8081/test/bar.one")); + + AssetXO assetXO2 = items.stream().filter(item -> item.getPath().equals("bar.three")).findFirst().get(); + assertThat(assetXO2.getRepository(), is("test-repo")); + assertThat(assetXO2.getDownloadUrl(), is("http://localhost:8081/test/bar.three")); + + //the expected query + QueryBuilder expected = boolQuery() + .filter(termQuery("format", "npm")); + assertThat(queryBuilderArgumentCaptor.getValue().toString(), is(expected.toString())); + } + + @Test + public void testSearchAndDownload_SpecificAssetParam_WillReturnOne() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitNpm}); + + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + + Page assets = underTest.searchAssets(null, uriInfo("?format=npm&sha1=fifth-sha1")); + + List items = assets.getItems(); + + assertThat(items, hasSize(1)); + + AssetXO assetXO = items.get(0); + assertThat(assetXO.getPath(), equalTo("bar.three")); + assertThat(assetXO.getRepository(), is("test-repo")); + assertThat(assetXO.getDownloadUrl(), is("http://localhost:8081/test/bar.three")); + + //the expected query + QueryBuilder expected = boolQuery() + .filter(termQuery("assets.attributes.checksum.sha1", "fifth-sha1")) + .filter(termQuery("format", "npm")); + assertThat(queryBuilderArgumentCaptor.getValue().toString(), is(expected.toString())); + } + + @Test + public void testSearchAndDownload_SpecificAssetParam_NotFound() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitNpm}); + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + + Page assets = underTest.searchAssets(null, uriInfo("?format=npm&sha1=notfound")); + + List items = assets.getItems(); + + assertThat(items, hasSize(0)); + } + + @Test + public void testSearchAndDownload_SpecificAssetParam_NotFound_404_HTTP_RESPONSE() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitNpm}); + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + try { + underTest.searchAndDownloadAssets(uriInfo("?format=npm&sha1=notfound")); + } + catch (WebApplicationException webEx) { + assertThat(webEx.getResponse().getStatus(), equalTo(404)); + } + } + + @Test + public void testSearchAndDownload_SpecificAssetParam_NotFound_400_HTTP_RESPONSE() { + // mock Elastic is only returning npm + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitNpm}); + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + try { + underTest.searchAndDownloadAssets(uriInfo("?format=npm")); + } + catch (WebApplicationException webEx) { + assertThat(webEx.getResponse().getStatus(), equalTo(400)); + } + } + + @Test + public void testSearchAndDownload_SpecificAssetParam_AssetFound_302_REDIRECT() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitNpm}); + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + Response response = underTest.searchAndDownloadAssets(uriInfo("?format=npm&sha1=fifth-sha1")); + assertThat(response.getStatus(), equalTo(302)); + assertThat(response.getHeaderString("Location"), is("http://localhost:8081/test/bar.three")); + } + + @Test + public void testSearchAndDownload_WithLongAssetParam_AssetFound_302_REDIRECT() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitMaven}); + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + Response response = underTest.searchAndDownloadAssets(uriInfo("?assets.attributes.maven2.extension=jar")); + assertThat(response.getStatus(), equalTo(302)); + assertThat(response.getHeaderString("Location"), is("http://localhost:8081/test/antlr.jar")); + } + + @Test + public void testSearchAndDownload_EmptyClassifier_JarAssetFound_302_REDIRECT() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitMaven_withMultipleAssets}); + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + Response response = + underTest.searchAndDownloadAssets(uriInfo("?assets.attributes.maven2.extension=jar&maven.classifier")); + assertThat(response.getStatus(), equalTo(302)); + assertThat(response.getHeaderString("Location"), is("http://localhost:8081/test/antlr.jar")); + } + + @Test + public void testSearchAndDownload_Classifier_JarAssetFound_302_REDIRECT() { + when(searchHits.hits()).thenReturn(new SearchHit[]{searchHitMaven_withMultipleAssets}); + when(searchService.search(queryBuilderArgumentCaptor.capture(), eq(emptyList()), eq(0), eq(50))) + .thenReturn(searchResponse); + Response response = + underTest.searchAndDownloadAssets(uriInfo("?assets.attributes.maven2.version=2.0&maven.classifier=fooz")); + assertThat(response.getStatus(), equalTo(302)); + assertThat(response.getHeaderString("Location"), is("http://localhost:8081/test/antlr-fooz.jar")); + } + + @Test + public void testBuildQuery() { + //the expected query + QueryBuilder expected = boolQuery() + .must(queryStringQuery("someKindOfStringQuery")) + .filter(termQuery("format", "maven2")) + .filter(termQuery("arbitrary.param", "random")); + + String uri = "?format=maven2" + + "&arbitrary.param=random" + + "&sha256=" + //this one should be ignored because it is empty + "&q=someKindOfStringQuery"; + QueryBuilder actual = searchUtils.buildQuery(uriInfo(uri)); + + assertThat(actual.toString(), is(expected.toString())); + } + + @Test + public void testGetAssetParams() { + MultivaluedMap result = underTest.getAssetParams(uriInfo("?sha1=thisisthesha1&name=antlr")); + assertThat(result.size(), equalTo(1)); + assertThat(result, hasKey("sha1")); + + // put every single search param into the pam + StringBuilder sb = new StringBuilder(); + Set allKeys = searchUtils.getSearchParameters().keySet(); + allKeys.forEach(s -> sb.append(s).append("=valueDoesNotMatter&")); + + // asert only assert params remain + result = underTest.getAssetParams(uriInfo("?" + sb.toString())); + assertThat(result.size(), equalTo(searchUtils.getAssetSearchParameters().size())); + assertThat(result.keySet(), equalTo(searchUtils.getAssetSearchParameters().keySet())); + } + + @Test + public void testGetAssetParams_ForLongAssetParamsEntries() { + //Positive case for long asset search param entries + MultivaluedMap longNameResult = underTest.getAssetParams( + uriInfo("?assets.attributes.maven2.extension=jar")); + assertThat(longNameResult.size(), equalTo(1)); + assertThat(longNameResult, hasKey("assets.attributes.maven2.extension")); + + //Verify negative case + MultivaluedMap negativeCaseResult = underTest.getAssetParams( + uriInfo("?attributes.not.asset.jar")); + assertThat(negativeCaseResult.size(), equalTo(0)); + } + + @Test + public void testGetAssetParams_ForParamsNotInSearchMappings() { + //Verify a query string can contain asset attributes not in the search mappings + MultivaluedMap shortNameResult = underTest.getAssetParams( + uriInfo("?assets.attributes.maven2.classifier=jar")); + assertThat(shortNameResult.size(), equalTo(1)); + } + + private UriInfo uriInfo(final String uri) { + return new ResteasyUriInfo(URI.create(uri)); + } + + private class TestSearchResourceExtension + implements SearchResourceExtension + { + @Override + public ComponentXO updateComponentXO(final ComponentXO componentXO, final SearchHit hit) { + return componentXO; + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/TokenEncoderTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/TokenEncoderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ec80aa1bb5797e6c915882758138d390963d58bf --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/rest/internal/resources/TokenEncoderTest.java @@ -0,0 +1,88 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.rest.internal.resources; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.elasticsearch.index.query.QueryBuilders.boolQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.sonatype.nexus.common.io.Hex.encode; + +public class TokenEncoderTest + extends TestSupport +{ + private static final int PAGE_SIZE = 50; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private TokenEncoder underTest; + + @Before + public void setUp() { + underTest = new TokenEncoder(); + } + + @Test + public void testContinuationToken() { + //Start with no token provided, it should return 'from' of 0 + int from = underTest.decode(null, boolQuery()); + assertThat(from, is(0)); + + //Now get a continuation token from that initial value of 0 + String continuationToken = underTest.encode(from, PAGE_SIZE, boolQuery()); + assertThat(continuationToken, notNullValue()); + + //'from' derived from the new continuation should now be 10 + int from2 = underTest.decode(continuationToken, boolQuery()); + assertThat(from2, is(50)); + + //new continuation token should be different + String continuationToken2 = underTest.encode(from2, PAGE_SIZE, boolQuery()); + assertThat(continuationToken2, notNullValue()); + assertThat(continuationToken2, not(continuationToken)); + + //one more check for good measure + int from3 = underTest.decode(continuationToken2, boolQuery()); + assertThat(from3, is(100)); + } + + @Test + public void testBadContinuationToken() { + String badToken = encode("bad".getBytes()); + + thrown.expect(hasProperty("response", hasProperty("status", is(406)))); + + underTest.decode(badToken, boolQuery()); + } + + @Test + public void testChangingQuery() { + String token = underTest.encode(0, PAGE_SIZE, boolQuery()); + + thrown.expect(hasProperty("response", hasProperty("status", is(406)))); + + underTest.decode(token, matchAllQuery()); + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/DefaultComponentMetadataProducerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/DefaultComponentMetadataProducerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e08184f4e3f660ac2e1446e73e6125ffa635757b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/DefaultComponentMetadataProducerTest.java @@ -0,0 +1,93 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.io.IOException; +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.Component; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static com.google.common.collect.Lists.newArrayList; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.verify; +import static org.sonatype.nexus.repository.storage.StorageTestUtil.createBucket; +import static org.sonatype.nexus.repository.storage.StorageTestUtil.createDetachedAsset; +import static org.sonatype.nexus.repository.storage.StorageTestUtil.createDetachedComponent; + +public class DefaultComponentMetadataProducerTest + extends TestSupport +{ + private static final String REPO_NAME = "repo"; + + private static final String GROUP = "group"; + + private static final String NAME = "name"; + + private static final String VERSION = "1.2.3"; + + private final ObjectMapper mapper = new ObjectMapper(); + + @Mock + private ComponentMetadataProducerExtension componentMetadataProducerExtension; + + private DefaultComponentMetadataProducer underTest; + + @Before + public void setup() { + underTest = new DefaultComponentMetadataProducer(ImmutableSet.of(componentMetadataProducerExtension)); + } + + @Test + public void testGetMetadata() throws IOException { + Bucket bucket = createBucket(REPO_NAME); + Component component = createDetachedComponent(bucket, GROUP, NAME, VERSION); + Iterable assets = newArrayList(createDetachedAsset(bucket, NAME, component)); + Map additional = ImmutableMap.of("foo", "bar"); + + String result = underTest.getMetadata(component, assets, additional); + + JsonNode json = mapper.readTree(result); + + assertValue(json, DefaultComponentMetadataProducer.FORMAT, "format-id"); + assertValue(json, DefaultComponentMetadataProducer.NAME, NAME); + assertValue(json, DefaultComponentMetadataProducer.GROUP, GROUP); + assertValue(json, DefaultComponentMetadataProducer.VERSION, VERSION); + assertValue(json, "foo", "bar"); + + JsonNode jsonAssets = json.get(DefaultComponentMetadataProducer.ASSETS); + assertTrue(jsonAssets.isArray()); + assertThat(jsonAssets.size(), equalTo(1)); + JsonNode jsonAsset = jsonAssets.get(0); + assertValue(jsonAsset, DefaultComponentMetadataProducer.NAME, NAME); + + verify(componentMetadataProducerExtension).getComponentMetadata(any(Component.class)); + } + + private void assertValue(final JsonNode json, final String jsonAttribute, final String value) { + assertThat(json.get(jsonAttribute).asText(), equalTo(value)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/IndexRequestProcessorTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/IndexRequestProcessorTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..99a96591a129b8a6e4f9fc01860da85b10495c5f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/IndexRequestProcessorTest.groovy @@ -0,0 +1,187 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.common.entity.DetachedEntityId +import org.sonatype.nexus.common.entity.EntityBatchEvent +import org.sonatype.nexus.common.entity.EntityId +import org.sonatype.nexus.common.event.EventManager +import org.sonatype.nexus.common.stateguard.InvalidStateException +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.manager.RepositoryManager +import org.sonatype.nexus.repository.storage.AssetCreatedEvent +import org.sonatype.nexus.repository.storage.AssetDeletedEvent +import org.sonatype.nexus.repository.storage.AssetUpdatedEvent +import org.sonatype.nexus.repository.storage.ComponentCreatedEvent +import org.sonatype.nexus.repository.storage.ComponentDeletedEvent +import org.sonatype.nexus.repository.storage.ComponentUpdatedEvent +import org.sonatype.nexus.repository.storage.StorageFacet +import org.sonatype.nexus.repository.storage.StorageTx + +import com.google.common.base.Suppliers +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.junit.runners.Parameterized.Parameter +import org.junit.runners.Parameterized.Parameters +import org.mockito.Mock + +import static org.mockito.Mockito.any +import static org.mockito.Mockito.anySet +import static org.mockito.Mockito.doThrow +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.verifyNoMoreInteractions +import static org.mockito.Mockito.when +import static org.sonatype.nexus.repository.FacetSupport.State.DELETED +import static org.sonatype.nexus.repository.FacetSupport.State.DESTROYED +import static org.sonatype.nexus.repository.FacetSupport.State.FAILED +import static org.sonatype.nexus.repository.FacetSupport.State.STARTED +import static org.sonatype.nexus.repository.FacetSupport.State.STOPPED + +@RunWith(Parameterized.class) +class IndexRequestProcessorTest + extends TestSupport +{ + + @Parameters + public static Collection data() { + return [false, true] + } + + @Parameter + public boolean bulkProcessing + + EntityId alphaComponentId = new DetachedEntityId('#3:1') + + EntityId betaComponentId = new DetachedEntityId('#4:2') + + EntityBatchEvent emptyBatchEvent = new EntityBatchEvent([]) + + EntityBatchEvent simpleBatchEvent = new EntityBatchEvent([ + mockEntityEvent(ComponentCreatedEvent, alphaComponentId) + ]) + + @Mock + RepositoryManager repositoryManager + + @Mock + EventManager eventManager + + @Mock + SearchService searchService + + @Mock + Repository repository + + @Mock + SearchFacet searchFacet + + @Mock + StorageFacet storageFacet + + @Mock + StorageTx storageTx + + IndexRequestProcessor indexRequestProcessor + + @Before + public void setup() { + indexRequestProcessor = new IndexRequestProcessor(repositoryManager, eventManager, searchService, bulkProcessing) + when(repositoryManager.get('testRepo')).thenReturn(repository) + when(repository.optionalFacet(SearchFacet)).thenReturn(Optional.of(searchFacet)) + when(repository.facet(StorageFacet)).thenReturn(storageFacet) + when(storageFacet.txSupplier()).thenReturn(Suppliers.ofInstance(storageTx)) + } + + def mockEntityEvent(eventType, componentId) { + def mockedEvent = mock(eventType) + when(mockedEvent.getRepositoryName()).thenReturn('testRepo') + when(mockedEvent.getComponentId()).thenReturn(componentId) + return mockedEvent + } + + @Test + public void entityEventsTriggerSearchUpdates() throws Exception { + + // mix of events; simulates creating new entities, plus updating and/or deleting old ones + ComponentCreatedEvent e1 = mockEntityEvent(ComponentCreatedEvent, alphaComponentId) + AssetCreatedEvent e2 = mockEntityEvent(AssetCreatedEvent, betaComponentId) + AssetCreatedEvent e3 = mockEntityEvent(AssetCreatedEvent, alphaComponentId) + ComponentUpdatedEvent e4 = mockEntityEvent(ComponentUpdatedEvent, betaComponentId) + AssetUpdatedEvent e5 = mockEntityEvent(AssetUpdatedEvent, alphaComponentId) + AssetDeletedEvent e6 = mockEntityEvent(AssetDeletedEvent, betaComponentId) + ComponentDeletedEvent e7 = mockEntityEvent(ComponentDeletedEvent, alphaComponentId) + + indexRequestProcessor.on(new EntityBatchEvent([e1, e2, e3, e4, e5, e6, e7])) + + // only alpha component should be deleted, beta events should be coalesced into a single put + + if (bulkProcessing) { + verify(searchFacet).bulkDelete([alphaComponentId] as Set) + verify(searchFacet).bulkPut([betaComponentId] as Set) + } + else { + verify(searchFacet).delete(alphaComponentId) + verify(searchFacet).put(betaComponentId) + } + + verifyNoMoreInteractions(searchFacet) + } + + @Test + public void noEntityEventsTriggerNoSearchUpdates() throws Exception { + indexRequestProcessor.on(emptyBatchEvent) + + verifyNoMoreInteractions(searchFacet) + } + + @Test + public void entityEventsOnStoppedRepositoryDoesNotThrowException() throws Exception { + throwOnPut(new InvalidStateException(STOPPED, STARTED)) + + indexRequestProcessor.on(simpleBatchEvent) + } + + @Test + public void entityEventsOnDeletedRepositoryDoesNotThrowException() throws Exception { + throwOnPut(new InvalidStateException(DELETED, STARTED)) + + indexRequestProcessor.on(simpleBatchEvent) + } + + @Test + public void entityEventsOnDestroyedRepositoryDoesNotThrowException() throws Exception { + throwOnPut(new InvalidStateException(DESTROYED, STARTED)) + + indexRequestProcessor.on(simpleBatchEvent) + } + + @Test(expected = InvalidStateException) + public void entityEventsOnFailedRepositoryThrowsException() throws Exception { + throwOnPut(new InvalidStateException(FAILED, STARTED)) + + indexRequestProcessor.on(simpleBatchEvent) + } + + void throwOnPut(final Throwable throwable) { + if (bulkProcessing) { + doThrow(throwable).when(searchFacet).bulkPut(anySet()) + } + else { + doThrow(throwable).when(searchFacet).put(any()) + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/IndexSyncServiceTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/IndexSyncServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..11d592ffba9469fc9d125b682be1d146bf9cf0ed --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/IndexSyncServiceTest.java @@ -0,0 +1,104 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; + +import javax.inject.Provider; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.orient.entity.EntityLog.UnknownDeltaException; +import org.sonatype.nexus.repository.storage.AssetEntityAdapter; +import org.sonatype.nexus.repository.storage.ComponentEntityAdapter; +import org.sonatype.nexus.scheduling.TaskScheduler; + +import com.orientechnologies.orient.core.storage.impl.local.paginated.wal.OLogSequenceNumber; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.Mock; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.when; + +public class IndexSyncServiceTest + extends TestSupport +{ + @Mock + Provider componentDatabase; + + @Mock + ComponentEntityAdapter componentEntityAdapter; + + @Mock + AssetEntityAdapter assetEntityAdapter; + + @Mock + ApplicationDirectories directories; + + @Mock + NodeAccess nodeAccess; + + @Mock + IndexRequestProcessor indexRequestProcessor; + + @Mock + public TaskScheduler taskScheduler; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private File nexusLsn; + + private IndexSyncService underTest; + + private long MARKER_POSITION = 0L; + + private long MARKER_SEGMENT = 35L; + + @Before + public void setUp() throws Exception { + File elasticsearchDir = temporaryFolder.newFolder(); + when(directories.getWorkDirectory("elasticsearch")).thenReturn(elasticsearchDir); + underTest = new IndexSyncService(componentDatabase, componentEntityAdapter, + assetEntityAdapter, directories, nodeAccess, + indexRequestProcessor, taskScheduler); + nexusLsn = new File(elasticsearchDir, "nexus.lsn"); + } + + @Test (expected = UnknownDeltaException.class) + public void testLoadCheckpointFile_withUpgradeMarker() throws Exception { + byte[] upgradeMarkerBytes = IndexSyncService.INDEX_UPGRADE_MARKER.getBytes(); + try (DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(nexusLsn))) { + outputStream.write(upgradeMarkerBytes); + } + underTest.loadCheckpoint(); + } + + @Test + public void testLoadCheckpointFile_withoutUpgradeMarker() throws Exception { + try (DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(nexusLsn))) { + new OLogSequenceNumber(MARKER_SEGMENT, MARKER_POSITION).toStream(outputStream); + } + OLogSequenceNumber oLogSequenceNumber = underTest.loadCheckpoint(); + assertThat(oLogSequenceNumber.getPosition(), is(MARKER_POSITION)); + assertThat(oLogSequenceNumber.getSegment(), is(MARKER_SEGMENT)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/SearchServiceImplIT.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/SearchServiceImplIT.groovy new file mode 100644 index 0000000000000000000000000000000000000000..772754d886bb0f67e1a275337129b9c3c77053d9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/SearchServiceImplIT.groovy @@ -0,0 +1,201 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search + +import java.nio.file.Paths +import java.security.SecureRandom + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.goodies.testsupport.junit.TestDataRule +import org.sonatype.nexus.common.app.ApplicationDirectories +import org.sonatype.nexus.common.node.NodeAccess +import org.sonatype.nexus.elasticsearch.internal.ClientProvider +import org.sonatype.nexus.elasticsearch.internal.NodeProvider +import org.sonatype.nexus.repository.Format +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.config.Configuration +import org.sonatype.nexus.repository.manager.RepositoryManager +import org.sonatype.nexus.repository.storage.Component +import org.sonatype.nexus.repository.storage.DefaultComponent +import org.sonatype.nexus.security.SecurityHelper + +import com.google.common.collect.ContiguousSet +import com.google.common.collect.ImmutableList +import com.google.common.collect.Iterables +import com.google.common.collect.Multimap +import com.google.common.collect.Multimaps +import com.google.common.collect.Range +import org.elasticsearch.index.query.BoolQueryBuilder +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock + +import static com.google.common.collect.DiscreteDomain.integers +import static com.jayway.awaitility.Awaitility.await +import static java.util.concurrent.TimeUnit.MINUTES +import static org.elasticsearch.index.query.QueryBuilders.boolQuery +import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery +import static org.hamcrest.Matchers.is +import static org.junit.Assert.assertThat +import static org.mockito.Mockito.any +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.when + +class SearchServiceImplIT + extends TestSupport +{ + static final String BASEDIR = new File(System.getProperty('basedir', '')).absolutePath + + @Rule + public TestDataRule testData = new TestDataRule(Paths.get(BASEDIR, 'src/test/it-resources').toFile()) + + static final int TEST_REPOSITORY_COUNT = 10 + + static final int TEST_COMPONENT_COUNT = 3000 + + @Mock + ApplicationDirectories directories + + @Mock + NodeAccess nodeAccess + + @Mock + RepositoryManager repositoryManager + + @Mock + Configuration repositoryConfig + + @Mock + SearchFacet searchFacet + + @Mock + Format testFormat + + @Mock + SecurityHelper securityHelper + + @Mock + SearchSubjectHelper searchSubjectHelper + + SearchServiceImpl searchService + + def repositories = [] + + def components = [] + + Multimap componentsByRepository = Multimaps.newListMultimap([:], {[]}) + + BoolQueryBuilder exampleQuery = boolQuery().must(queryStringQuery('example')) + + @Before + public void setup() { + when(directories.getConfigDirectory('fabric')).thenReturn(testData.resolveFile('fabric')) + when(nodeAccess.getId()).thenReturn('test-node') + + System.setProperty('testdir', new File(BASEDIR, 'target/test-node').path) + + NodeProvider nodeProvider = new NodeProvider(directories, nodeAccess, null, null) + ClientProvider clientProvider = new ClientProvider(nodeProvider) + + searchService = new SearchServiceImpl(clientProvider, repositoryManager, securityHelper, searchSubjectHelper, + ImmutableList.of(), false, 1000, 1, 0) + + when(repositoryConfig.isOnline()).thenReturn(true) + when(testFormat.getValue()).thenReturn('test-format') + + for (int i = 0; i < TEST_REPOSITORY_COUNT; i++) { + Repository repository = mock(Repository.class) + when(repository.getName()).thenReturn("test-$i" as String) + when(repository.getConfiguration()).thenReturn(repositoryConfig) + when(repository.optionalFacet(SearchFacet.class)).thenReturn(Optional.of(searchFacet)) + when(repository.getFormat()).thenReturn(testFormat) + searchService.createIndex(repository) + repositories.add(repository) + } + + when(repositoryManager.browse()).thenReturn(repositories as List) + when(securityHelper.allPermitted(any())).thenReturn(true) + + for (int i = 0; i < TEST_COMPONENT_COUNT; i++) { + Component component = new DefaultComponent() + component.format('test-format') + component.group('example') + component.name("$i") + component.version('1.0') + components.add(component) + } + } + + @After + public void teardown() { + repositories.forEach(searchService.&deleteIndex) + } + + @Test + public void testBulkDelete() throws Exception { + + seedComponentIndex() + + // attempt to bulk-delete indexed documents under each repository + repositories.forEach({ repo -> + searchService.bulkDelete(repo, componentsByRepository.get(repo)*.name()) }) + + // wait for all documents to be removed + await().atMost(1, MINUTES).until({ + assertThat(Iterables.size(searchService.browseUnrestricted(exampleQuery)), is(0)) }) + } + + @Test + public void testBulkDeleteByIdentifierOnly() throws Exception { + + seedComponentIndex() + + // attempt to bulk-delete indexed documents by identifier only, without knowing the owning repository + searchService.bulkDelete(null, ContiguousSet.create(Range.closed(0, TEST_COMPONENT_COUNT), integers()).asList()) + + // wait for all documents to be removed + await().atMost(1, MINUTES).until({ + assertThat(Iterables.size(searchService.browseUnrestricted(exampleQuery)), is(0)) }) + } + + private seedComponentIndex() { + Random random = new SecureRandom() + + // distribute components across repositories + components.forEach({ component -> + componentsByRepository.put(repositories[random.nextInt(TEST_REPOSITORY_COUNT)], component) }) + + // populate each index using bulk operations + componentsByRepository.keys().forEach({ repository -> + searchService.bulkPut( + repository, + componentsByRepository.get(repository), + { component -> component.name() }, + { component -> + """ + { "format":"${component.format()}", + "group":"${component.group()}", + "name":"${component.name()}", + "version":"${component.version()}" + } + """ as String + }) + }) + + // wait for all documents to be indexed + await().atMost(1, MINUTES).until({ + assertThat(Iterables.size(searchService.browseUnrestricted(exampleQuery)), is(TEST_COMPONENT_COUNT)) }) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/SearchServiceImplTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/SearchServiceImplTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..cc6f738edf58633f55d95eae578ff4d390a568c6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/SearchServiceImplTest.groovy @@ -0,0 +1,227 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search + +import javax.inject.Provider + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.common.event.EventManager +import org.sonatype.nexus.repository.Format +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.config.Configuration +import org.sonatype.nexus.repository.manager.RepositoryManager +import org.sonatype.nexus.repository.manager.internal.RepositoryImpl +import org.sonatype.nexus.repository.storage.Component +import org.sonatype.nexus.repository.storage.DefaultComponent +import org.sonatype.nexus.repository.types.HostedType +import org.sonatype.nexus.security.SecurityHelper + +import com.google.common.base.Function +import com.google.common.collect.BiMap +import com.google.common.collect.HashBiMap +import org.elasticsearch.action.ListenableActionFuture +import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequestBuilder +import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse +import org.elasticsearch.action.bulk.BulkProcessor +import org.elasticsearch.action.index.IndexRequestBuilder +import org.elasticsearch.client.AdminClient +import org.elasticsearch.client.Client +import org.elasticsearch.client.IndicesAdminClient +import org.elasticsearch.common.settings.Settings +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentCaptor +import org.mockito.Mock + +import static org.hamcrest.MatcherAssert.assertThat +import static org.hamcrest.Matchers.arrayWithSize +import static org.hamcrest.Matchers.contains +import static org.mockito.Mockito.eq +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when +import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA1 + +class SearchServiceImplTest + extends TestSupport +{ + + @Mock + Provider clientProvider + + @Mock + Client client + + @Mock + AdminClient adminClient + + @Mock + IndicesAdminClient indicesAdminClient + + @Mock + IndicesExistsRequestBuilder indicesExistsRequestBuilder + + @Mock + ListenableActionFuture actionFuture + + @Mock + IndicesExistsResponse indicesExistsResponse + + @Mock + RepositoryManager repositoryManager + + @Mock + SecurityHelper securityHelper + + @Mock + SearchSubjectHelper searchSubjectHelper + + @Mock + List indexSettingsContributors + + @Mock + EventManager eventManager + + @Mock + BulkProcessor bulkProcessor + + Settings settings = Settings.EMPTY + + SearchServiceImpl searchService + + @Before + public void setup() { + when(clientProvider.get()).thenReturn(client) + when(client.admin()).thenReturn(adminClient) + when(adminClient.indices()).thenReturn(indicesAdminClient) + when(client.settings()).thenReturn(settings) + + searchService = new SearchServiceImpl(clientProvider, repositoryManager, securityHelper, searchSubjectHelper, + indexSettingsContributors, false, 1000, 0, 0) + searchService.bulkProcessor = bulkProcessor + } + + @Test + public void testCreateIndexAlreadyExists() throws Exception { + ArgumentCaptor varArgs = captureRepoNameArg() + + searchService.createIndex(repository('test')) + + assertThat(varArgs.getAllValues(), contains(SHA1.function().hashUnencodedChars('test').toString())) + } + + /** + * Search indices identifiers are {@link Repository#getName()} passed thru SHA1 hasher to normalize them and + * make them suit ES index name requirements (lower case, max len 255, etc). + */ + @Test + public void testCreateIndexRepositoryNameMapping() throws Exception { + ArgumentCaptor varArgs = captureRepoNameArg() + + searchService.createIndex(repository('UPPERCASE')) + + assertThat(varArgs.getAllValues(), contains(SHA1.function().hashUnencodedChars('UPPERCASE').toString())) + } + + /** + * Verify successful execution path for {@link SearchServiceImpl#bulkPut(Repository, Iterable, Function, Function)}. + */ + @Test + void testBulkPut() { + int requestCount = 50 + BiMap components = HashBiMap.create() + for (int i = 0; i < requestCount; i++) { + components.put(UUID.randomUUID().toString(), new DefaultComponent()) + } + + BiMap inverse = components.inverse() + String json = '{ "a": "b" }' + + Repository repository = repository(SearchServiceImpl.TYPE) + ArgumentCaptor indexName = captureRepoNameArg() + searchService.createIndex(repository) + + components.entrySet().forEach({ entry -> + IndexRequestBuilder builder = mock(IndexRequestBuilder.class) + when( + client.prepareIndex(indexName.capture(), eq(SearchServiceImpl.TYPE), eq(entry.getKey())) + ).thenReturn(builder) + when(builder.setSource(json)).thenReturn(builder) + org.elasticsearch.action.index.IndexRequest request = mock(org.elasticsearch.action.index.IndexRequest.class) + when(builder.request()).thenReturn(request) + }) + + searchService.bulkPut(repository, + components.values(), + { component -> inverse.get(component) }, + { component -> json } + ) + + // Note: can't do a 'verify(bulkProcessor, times(requestCount)).add(any())' here, + // because BulkProcessor#add is overloaded, each variant taking a single argument of the same supertype + // mockito is unable to resolve the invoked method (fails with 'wanted, but not invoked error') + verify(bulkProcessor).flush() + } + + @Test + void testGetSearchableIndexes() { + ArgumentCaptor captureA = captureRepoNameArg() + def repositoryA = repository('a') + searchService.createIndex(repositoryA) + def encodedA = captureA.allValues[0] + + ArgumentCaptor captureB = captureRepoNameArg() + def repositoryB = repository('b') + searchService.createIndex(repositoryB) + def encodedB = captureB.allValues[0] + + when(repositoryManager.browse()).thenReturn([repositoryA, repositoryB]) + def searchable = searchService.getSearchableIndexes(true, ['a']) + assertThat(searchable as List, contains(encodedA)) + assertThat(searchable, arrayWithSize(1)) + + when(repositoryManager.browse()).thenReturn([repositoryA, repositoryB]) + searchable = searchService.getSearchableIndexes(true, ['a', 'b']) + assertThat(searchable as List, contains(encodedA, encodedB)) + assertThat(searchable, arrayWithSize(2)) + } + + protected Repository repository(String name) { + Repository repository = new RepositoryImpl(eventManager, new HostedType(), new TestFormat('test')) + repository.name = name + def configuration = new Configuration() + configuration.online = true + repository.configuration = configuration + SearchFacet searchFacet = mock(SearchFacet) + repository.attach(searchFacet) + return repository + } + + private ArgumentCaptor captureRepoNameArg() { + ArgumentCaptor varArgs = ArgumentCaptor.forClass(String.class) + when(indicesAdminClient.prepareExists(varArgs.capture())).thenReturn(indicesExistsRequestBuilder) + when(indicesExistsRequestBuilder.execute()).thenReturn(actionFuture) + when(actionFuture.actionGet()).thenReturn(indicesExistsResponse) + when(indicesExistsResponse.isExists()).thenReturn(true) + varArgs + } + + class TestFormat + extends Format + { + + TestFormat(final String value) { + super(value) + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/SearchSubjectHelperTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/SearchSubjectHelperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5c95dec3e59c1dc16583f0fffe01316aa85770ad --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/SearchSubjectHelperTest.java @@ -0,0 +1,53 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.search.SearchSubjectHelper.SubjectRegistration; + +import org.apache.shiro.subject.Subject; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +public class SearchSubjectHelperTest + extends TestSupport +{ + @Mock + Subject subject; + + SearchSubjectHelper helper; + + @Before + public void setup() { + helper = new SearchSubjectHelper(); + } + + @Test + public void testRegistration() { + assertThat(helper.subjects.size(), is(0)); + try (SubjectRegistration registration = helper.register(subject)) { + assertThat(helper.subjects.size(), is(1)); + assertThat(helper.getSubject(registration.getId()), is(subject)); + } + assertThat(helper.subjects.size(), is(0)); + } + + @Test(expected = NullPointerException.class) + public void testMissingSubject() { + helper.getSubject(""); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/internal/ElasticSearchIndexUpgradeTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/internal/ElasticSearchIndexUpgradeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3cc192f273226d982e26f2fcbab456d065461d0a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/search/internal/ElasticSearchIndexUpgradeTest.java @@ -0,0 +1,62 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.search.internal; + +import java.io.File; +import java.io.FileInputStream; +import java.nio.charset.StandardCharsets; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.app.ApplicationDirectories; + +import org.apache.commons.io.IOUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.Mock; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.repository.search.IndexSyncService.INDEX_UPGRADE_MARKER; + +public class ElasticSearchIndexUpgradeTest + extends TestSupport +{ + + @Mock + private ApplicationDirectories appDirs; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private File elasticsearchDir; + + private ElasticSearchIndexUpgrade underTest; + + @Before + public void setUp() throws Exception { + elasticsearchDir = temporaryFolder.newFolder(); + when(appDirs.getWorkDirectory("elasticsearch")).thenReturn(elasticsearchDir); + underTest = new ElasticSearchIndexUpgrade(appDirs); + } + + @Test + public void testApply() throws Exception { + underTest.apply(); + try (FileInputStream in = new FileInputStream(new File(elasticsearchDir, "nexus.lsn"))) { + assertThat(IOUtils.toByteArray(in), is(INDEX_UPGRADE_MARKER.getBytes(StandardCharsets.UTF_8))); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/ContentPermissionCheckerImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/ContentPermissionCheckerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6c3c9cb1ba7fd322989916095f76322fa4ada85a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/ContentPermissionCheckerImplTest.java @@ -0,0 +1,155 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import java.util.Arrays; +import java.util.Collections; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.security.internal.ContentPermissionCheckerImpl; +import org.sonatype.nexus.security.BreadActions; +import org.sonatype.nexus.security.SecurityHelper; +import org.sonatype.nexus.selector.JexlSelector; +import org.sonatype.nexus.selector.SelectorConfiguration; +import org.sonatype.nexus.selector.SelectorManager; +import org.sonatype.nexus.selector.VariableSource; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ContentPermissionCheckerImplTest + extends TestSupport +{ + @Mock + SecurityHelper securityHelper; + + @Mock + SelectorManager selectorManager; + + @Mock + VariableSource variableSource; + + SelectorConfiguration config; + + ContentPermissionCheckerImpl impl; + + @Before + public void setup() { + impl = new ContentPermissionCheckerImpl(securityHelper, selectorManager); + + config = new SelectorConfiguration(); + config.setName("selector"); + config.setDescription("selector"); + config.setType(JexlSelector.TYPE); + config.setAttributes(Collections.singletonMap("expression", "true")); + } + + @Test + public void testIsViewPermitted_permitted() throws Exception { + when(securityHelper + .anyPermitted(eq(new RepositoryViewPermission("repoFormat", "repoName", Arrays.asList(BreadActions.READ))))) + .thenReturn(true); + + assertThat(impl.isViewPermitted("repoName", "repoFormat", BreadActions.READ), is(true)); + } + + @Test + public void testIsViewPermitted_notPermitted() throws Exception { + assertThat(impl.isViewPermitted("repoName", "repoFormat", BreadActions.READ), is(false)); + + //just to make sure it was actually called, since returning false is the default behaviour + verify(securityHelper) + .anyPermitted(eq(new RepositoryViewPermission("repoFormat", "repoName", Arrays.asList(BreadActions.READ)))); + } + + @Test + public void testIsContentPermitted_permitted() throws Exception { + when(selectorManager.evaluate(any(), any())).thenReturn(true); + + when(securityHelper.anyPermitted(eq(new RepositoryContentSelectorPermission("selector", "repoFormat", "repoName", + Arrays.asList(BreadActions.READ))))).thenReturn(true); + + assertThat(impl.isContentPermitted("repoName", "repoFormat", BreadActions.READ, config, variableSource), is(true)); + } + + @Test + public void testIsContentPermitted_notPermitted() throws Exception { + when(selectorManager.evaluate(any(), any())).thenReturn(true); + + assertThat(impl.isContentPermitted("repoName", "repoFormat", BreadActions.READ, config, variableSource), is(false)); + + //just to make sure it was actually called, since returning false is the default behaviour + verify(securityHelper).anyPermitted(eq(new RepositoryContentSelectorPermission("selector", "repoFormat", "repoName", + Arrays.asList(BreadActions.READ)))); + } + + @Test + public void testIsPermitted_viewPermittedContentPermitted() throws Exception { + when(securityHelper + .anyPermitted(eq(new RepositoryViewPermission("repoFormat", "repoName", Arrays.asList(BreadActions.READ))))) + .thenReturn(true); + + when(selectorManager.browse()).thenReturn(Arrays.asList(config)); + + when(selectorManager.evaluate(any(), any())).thenReturn(true); + + assertThat(impl.isPermitted("repoName", "repoFormat", BreadActions.READ, variableSource), is(true)); + } + + @Test + public void testIsPermitted_viewPermittedContentNotPermitted() throws Exception { + when(securityHelper + .anyPermitted(eq(new RepositoryViewPermission("repoFormat", "repoName", Arrays.asList(BreadActions.READ))))) + .thenReturn(true); + + when(selectorManager.browse()).thenReturn(Arrays.asList(config)); + + when(selectorManager.evaluate(any(), any())).thenReturn(false); + + assertThat(impl.isPermitted("repoName", "repoFormat", BreadActions.READ, variableSource), is(true)); + } + + @Test + public void testIsPermitted_viewNotPermittedContentPermitted() throws Exception { + when(securityHelper + .anyPermitted(eq(new RepositoryViewPermission("repoFormat", "repoName", Arrays.asList(BreadActions.READ))))) + .thenReturn(false); + + when(selectorManager.browse()).thenReturn(Arrays.asList(config)); + + when(selectorManager.evaluate(any(), any())).thenReturn(true); + + assertThat(impl.isPermitted("repoName", "repoFormat", BreadActions.READ, variableSource), is(false)); + } + + @Test + public void testIsPermitted_viewNotPermittedContentNotPermitted() throws Exception { + when(securityHelper + .anyPermitted(eq(new RepositoryViewPermission("repoFormat", "repoName", Arrays.asList(BreadActions.READ))))) + .thenReturn(false); + + when(selectorManager.browse()).thenReturn(Arrays.asList(config)); + + when(selectorManager.evaluate(any(), any())).thenReturn(false); + + assertThat(impl.isPermitted("repoName", "repoFormat", BreadActions.READ, variableSource), is(false)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/SecurityFacetSupportTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/SecurityFacetSupportTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c076a7ec4683fd9df63dcf406e5ae6fcea1c4aad --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/SecurityFacetSupportTest.java @@ -0,0 +1,100 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.http.HttpMethods; +import org.sonatype.nexus.repository.view.Request; + +import org.apache.shiro.authz.AuthorizationException; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.security.BreadActions.READ; + +public class SecurityFacetSupportTest + extends TestSupport +{ + private static final class TestSecurityFacetSupport + extends SecurityFacetSupport + { + public TestSecurityFacetSupport(final RepositoryFormatSecurityContributor securityContributor, + final VariableResolverAdapter variableResolverAdapter, + final ContentPermissionChecker contentPermissionChecker) + { + super(securityContributor, variableResolverAdapter, contentPermissionChecker); + } + } + + @Mock + Request request; + + @Mock + Repository repository; + + @Mock + ContentPermissionChecker contentPermissionChecker; + + @Mock + VariableResolverAdapter variableResolverAdapter; + + @Mock + RepositoryFormatSecurityContributor securityContributor; + + TestSecurityFacetSupport testSecurityFacetSupport; + + @Before + public void setupConfig() throws Exception { + when(request.getPath()).thenReturn("/some/path.txt"); + when(request.getAction()).thenReturn(HttpMethods.GET); + + when(repository.getFormat()).thenReturn(new Format("test") { }); + when(repository.getName()).thenReturn("SecurityFacetSupportTest"); + + testSecurityFacetSupport = new TestSecurityFacetSupport(securityContributor, + variableResolverAdapter, contentPermissionChecker); + + testSecurityFacetSupport.attach(repository); + } + + @Test + public void testEnsurePermitted_permitted() throws Exception { + when(contentPermissionChecker.isPermitted(eq("SecurityFacetSupportTest"), eq("test"), eq(READ), any())) + .thenReturn(true); + testSecurityFacetSupport.ensurePermitted(request); + } + + @Test + public void testEnsurePermitted_notPermitted() throws Exception { + when(contentPermissionChecker.isPermitted(eq("SecurityFacetSupportTest"), eq("test"), eq(READ), any())) + .thenReturn(false); + + try { + testSecurityFacetSupport.ensurePermitted(request); + fail("AuthorizationException should have been thrown"); + } + catch (AuthorizationException e) { + //expected + } + + verify(contentPermissionChecker).isPermitted(eq("SecurityFacetSupportTest"), eq("test"), eq(READ), any()); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/SecurityHandlerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/SecurityHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..749fb9248a39927b002e9c81693c00b4d99c2245 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/SecurityHandlerTest.java @@ -0,0 +1,67 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.collect.AttributesMap; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.view.Context; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class SecurityHandlerTest + extends TestSupport +{ + private SecurityHandler underTest; + + @Mock + private Context context; + + @Mock + private Repository repository; + + @Mock + private SecurityFacet securityFacet; + + private AttributesMap attributesMap; + + @Before + public void setup() { + underTest = new SecurityHandler(); + + attributesMap = new AttributesMap(); + when(repository.facet(SecurityFacet.class)).thenReturn(securityFacet); + when(context.getRepository()).thenReturn(repository); + when(context.getAttributes()).thenReturn(attributesMap); + } + + @Test + public void testHandle() throws Exception { + underTest.handle(context); + verify(securityFacet).ensurePermitted(any()); + } + + @Test + public void testHandle_alreadyAuthorized() throws Exception { + attributesMap.set(SecurityHandler.AUTHORIZED_KEY, true); + underTest.handle(context); + verify(securityFacet, never()).ensurePermitted(any()); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/SecurityTrial.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/SecurityTrial.groovy new file mode 100644 index 0000000000000000000000000000000000000000..c215277ed7ac6e5c746604e79a2bea8eb6dc5e4d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/SecurityTrial.groovy @@ -0,0 +1,59 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security + +import org.sonatype.goodies.testsupport.TestSupport + +import org.apache.shiro.authz.permission.DomainPermission +import org.apache.shiro.authz.permission.WildcardPermission +import org.junit.Test + +/** + * Security trials. + */ +class SecurityTrial + extends TestSupport +{ + @Test + void 'wildcard permission string'() { + def p = new WildcardPermission('foo:bar:*:baz') + log p + } + + @Test + void 'domain permission string'() { + def p = new DomainPermission('foo,bar', 'read,wrote') + log p + } + + private static class CustomPermission + extends DomainPermission + { + CustomPermission(final String actions, final String targets) { + super(actions, targets) + } + } + + @Test + void 'custom domain permission string'() { + def p = new CustomPermission('foo,bar', 'read,wrote') + log p + } + + @Test + void 'implied permission'() { + def granted = new WildcardPermission('test:*') + def permission = new WildcardPermission('test:foo') + log granted.implies(permission) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/SimpleVariableResolverAdapterTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/SimpleVariableResolverAdapterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4cad03b05ada7b2882c1e0cfca4db473bef78081 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/SimpleVariableResolverAdapterTest.java @@ -0,0 +1,117 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security; + +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.security.internal.SimpleVariableResolverAdapter; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.selector.VariableSource; + +import com.orientechnologies.orient.core.record.impl.ODocument; +import org.elasticsearch.search.lookup.SourceLookup; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.when; + +public class SimpleVariableResolverAdapterTest + extends TestSupport +{ + private static final String FORMAT_VARIABLE = "format"; + + private static final String PATH_VARIABLE = "path"; + + private static final String TEST_PATH_WITHOUT_SLASH = "some/path.txt"; + + private static final String TEST_PATH_WITH_SLASH = '/' + TEST_PATH_WITHOUT_SLASH; + + private static final String TEST_FORMAT = "test"; + + @Mock + Request request; + + @Mock + SourceLookup sourceLookup; + + @Mock + Map sourceLookupAsset; + + @Mock + ODocument document; + + @Mock + Asset asset; + + @Mock + Repository repository; + + @Test + public void testFromRequest() throws Exception { + when(request.getPath()).thenReturn(TEST_PATH_WITH_SLASH); + when(repository.getName()).thenReturn("SimpleVariableResolverAdapterTest"); + when(repository.getFormat()).thenReturn(new Format(TEST_FORMAT) { }); + SimpleVariableResolverAdapter simpleVariableResolverAdapter = new SimpleVariableResolverAdapter(); + VariableSource source = simpleVariableResolverAdapter.fromRequest(request, repository); + + assertThat(source.getVariableSet(), containsInAnyOrder(FORMAT_VARIABLE, PATH_VARIABLE)); + assertThat(source.get(FORMAT_VARIABLE).get(), is(TEST_FORMAT)); + assertThat(source.get(PATH_VARIABLE).get(), is(TEST_PATH_WITH_SLASH)); + } + + @Test + public void testFromDocument() throws Exception { + when(document.field("name", String.class)).thenReturn(TEST_PATH_WITHOUT_SLASH); + when(document.field(FORMAT_VARIABLE, String.class)).thenReturn(TEST_FORMAT); + + SimpleVariableResolverAdapter simpleVariableResolverAdapter = new SimpleVariableResolverAdapter(); + VariableSource source = simpleVariableResolverAdapter.fromDocument(document); + + assertThat(source.getVariableSet(), containsInAnyOrder(FORMAT_VARIABLE, PATH_VARIABLE)); + assertThat(source.get(FORMAT_VARIABLE).get(), is(TEST_FORMAT)); + assertThat(source.get(PATH_VARIABLE).get(), is(TEST_PATH_WITH_SLASH)); + } + + @Test + public void testFromAsset() throws Exception { + when(asset.name()).thenReturn(TEST_PATH_WITHOUT_SLASH); + when(asset.format()).thenReturn(TEST_FORMAT); + + SimpleVariableResolverAdapter simpleVariableResolverAdapter = new SimpleVariableResolverAdapter(); + VariableSource source = simpleVariableResolverAdapter.fromAsset(asset); + + assertThat(source.getVariableSet(), containsInAnyOrder(FORMAT_VARIABLE, PATH_VARIABLE)); + assertThat(source.get(FORMAT_VARIABLE).get(), is(TEST_FORMAT)); + assertThat(source.get(PATH_VARIABLE).get(), is(TEST_PATH_WITH_SLASH)); + } + + @Test + public void testFromSourceLookup() throws Exception { + when(sourceLookupAsset.get("name")).thenReturn(TEST_PATH_WITHOUT_SLASH); + when(sourceLookup.get("format")).thenReturn(TEST_FORMAT); + + SimpleVariableResolverAdapter simpleVariableResolverAdapter = new SimpleVariableResolverAdapter(); + VariableSource source = simpleVariableResolverAdapter.fromSourceLookup(sourceLookup, sourceLookupAsset); + + assertThat(source.getVariableSet(), containsInAnyOrder(FORMAT_VARIABLE, PATH_VARIABLE)); + assertThat(source.get(FORMAT_VARIABLE).get(), is(TEST_FORMAT)); + assertThat(source.get(PATH_VARIABLE).get(), is(TEST_PATH_WITH_SLASH)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/internal/VariableResolverAdapterManagerImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/internal/VariableResolverAdapterManagerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..046051a42c4126fc9f19cd0396f2467fed7cd488 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/security/internal/VariableResolverAdapterManagerImplTest.java @@ -0,0 +1,56 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.security.internal; + +import java.util.HashMap; +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.security.VariableResolverAdapter; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class VariableResolverAdapterManagerImplTest + extends TestSupport +{ + @Mock + private VariableResolverAdapter specializedAdapter; + + @Mock + private VariableResolverAdapter defaultAdapter; + + private VariableResolverAdapterManagerImpl manager; + + @Before + public void setUp() { + Map adaptersByFormat = new HashMap<>(); + adaptersByFormat.put("special", specializedAdapter); + adaptersByFormat.put(SimpleVariableResolverAdapter.NAME, defaultAdapter); + manager = new VariableResolverAdapterManagerImpl(adaptersByFormat); + } + + @Test + public void testGet_SpecializedAdapter() { + assertThat(manager.get("special"), is(specializedAdapter)); + } + + @Test + public void testGet_DefaultAdapter() { + assertThat(manager.get("other-format"), is(defaultAdapter)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPluginScriptTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPluginScriptTest.java new file mode 100644 index 0000000000000000000000000000000000000000..378868ee96575f2987f9a8b5104ec7726bd42e69 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/selector/internal/ContentAuthPluginScriptTest.java @@ -0,0 +1,118 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.selector.internal; + +import java.util.Collections; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.security.ContentPermissionChecker; +import org.sonatype.nexus.repository.security.VariableResolverAdapter; +import org.sonatype.nexus.repository.security.VariableResolverAdapterManager; +import org.sonatype.nexus.selector.VariableSource; + +import com.google.common.collect.ImmutableMap; +import org.apache.shiro.subject.Subject; +import org.elasticsearch.search.lookup.SourceLookup; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.anyMap; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.security.BreadActions.BROWSE; + +/** + * Tests for {@link ContentAuthPluginScript}. + */ +public class ContentAuthPluginScriptTest + extends TestSupport +{ + private static final String REPOSITORY_NAME = "repository-name"; + + private static final String PATH = "path"; + + private static final String FORMAT = "format"; + + @Mock + Subject subject; + + @Mock + VariableSource variableSource; + + @Mock + ContentPermissionChecker contentPermissionChecker; + + @Mock + VariableResolverAdapterManager variableResolverAdapterManager; + + @Mock + VariableResolverAdapter variableResolverAdapter; + + SourceLookup sourceLookup; + + ContentAuthPluginScript underTest; + + @Before + public void setup() { + sourceLookup = new SourceLookup(); + when(variableResolverAdapterManager.get(FORMAT)).thenReturn(variableResolverAdapter); + when(variableResolverAdapter.fromSourceLookup(eq(sourceLookup), anyMap())).thenReturn(variableSource); + underTest = new ContentAuthPluginScript(subject, contentPermissionChecker, + variableResolverAdapterManager) { + @Override + protected SourceLookup getSourceLookup() { + return sourceLookup; + } + }; + } + + @Test + public void testPermitted() { + sourceLookup.setSource(new ImmutableMap.Builder() + .put("format", FORMAT) + .put("repository_name", REPOSITORY_NAME) + .put("assets", Collections.singletonList(Collections.singletonMap("name", PATH))) + .build()); + when(contentPermissionChecker.isPermitted(REPOSITORY_NAME, FORMAT, BROWSE, variableSource)).thenReturn(true); + assertThat(underTest.run(), is(true)); + verify(contentPermissionChecker, times(1)).isPermitted(REPOSITORY_NAME, FORMAT, BROWSE, variableSource); + } + + @Test + public void testNotPermitted() { + sourceLookup.setSource(new ImmutableMap.Builder() + .put("format", FORMAT) + .put("repository_name", REPOSITORY_NAME) + .put("assets", Collections.singletonList(Collections.singletonMap("name", PATH))) + .build()); + when(contentPermissionChecker.isPermitted(REPOSITORY_NAME, FORMAT, BROWSE, variableSource)).thenReturn(false); + assertThat(underTest.run(), is(false)); + verify(contentPermissionChecker, times(1)).isPermitted(REPOSITORY_NAME, FORMAT, BROWSE, variableSource); + } + + @Test + public void testWithoutAssets() { + sourceLookup.setSource(new ImmutableMap.Builder() + .put("format", FORMAT) + .put("repository_name", REPOSITORY_NAME) + .build()); + assertThat(underTest.run(), is(false)); + verifyZeroInteractions(contentPermissionChecker); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/selector/internal/ContentAuthTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/selector/internal/ContentAuthTest.java new file mode 100644 index 0000000000000000000000000000000000000000..579db9a9f880d77587cd7a0bc0f516e084726e40 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/selector/internal/ContentAuthTest.java @@ -0,0 +1,192 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.selector.internal; + +import java.util.Collections; +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; + +import com.orientechnologies.orient.core.command.OCommandRequest; +import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal; +import com.orientechnologies.orient.core.db.record.OIdentifiable; +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.record.impl.ODocument; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link ContentAuth}. + */ +public class ContentAuthTest + extends TestSupport +{ + private static final String REPOSITORY_NAME = "repository-name"; + + private static final String PATH = "path"; + + private static final String FORMAT = "format"; + + @Mock + ContentAuthHelper contentAuthHelper; + + @Mock + ODocument assetDocument; + + @Mock + ODocument componentDocument; + + @Mock + ODocument bucketDocument; + + @Mock + OCommandRequest commandRequest; + + @Mock + ODatabaseDocumentInternal database; + + ContentAuth underTest; + + @Before + public void setup() { + when(bucketDocument.getRecord()).thenReturn(bucketDocument); + when(bucketDocument.field("repository_name", String.class)).thenReturn(REPOSITORY_NAME); + when(bucketDocument.getIdentity()).thenReturn(mock(ORID.class)); + + when(assetDocument.getClassName()).thenReturn("asset"); + when(assetDocument.getRecord()).thenReturn(assetDocument); + when(assetDocument.field("bucket", OIdentifiable.class)).thenReturn(bucketDocument); + when(assetDocument.field("name", String.class)).thenReturn(PATH); + when(assetDocument.field("format", String.class)).thenReturn(FORMAT); + + when(componentDocument.getClassName()).thenReturn("component"); + when(componentDocument.getRecord()).thenReturn(componentDocument); + when(componentDocument.field("bucket", OIdentifiable.class)).thenReturn(bucketDocument); + when(componentDocument.getDatabase()).thenReturn(database); + when(componentDocument.getIdentity()).thenReturn(mock(ORID.class)); + + when(commandRequest.execute(any(Map.class))).thenReturn(Collections.singletonList(assetDocument)); + when(database.command(any(OCommandRequest.class))).thenReturn(commandRequest); + + underTest = new ContentAuth(contentAuthHelper); + } + + @Test + public void testAssetPermitted() { + when(contentAuthHelper.checkAssetPermissions(assetDocument, new String[]{REPOSITORY_NAME})).thenReturn(true); + assertThat(underTest.execute(underTest, null, null, new Object[] { assetDocument, REPOSITORY_NAME }, null), is(true)); + verify(contentAuthHelper, times(1)).checkAssetPermissions(assetDocument, new String[]{REPOSITORY_NAME}); + } + + @Test + public void testAssetNotPermitted() { + when(contentAuthHelper.checkAssetPermissions(assetDocument, new String[]{REPOSITORY_NAME})).thenReturn(false); + assertThat(underTest.execute(underTest, null, null, new Object[] { assetDocument, REPOSITORY_NAME }, null), is(false)); + verify(contentAuthHelper, times(1)).checkAssetPermissions(assetDocument, new String[]{REPOSITORY_NAME}); + } + + @Test + public void testComponentPermitted() { + when(contentAuthHelper.checkAssetPermissions(assetDocument, new String[]{REPOSITORY_NAME})).thenReturn(true); + assertThat(underTest.execute(underTest, null, null, new Object[] { componentDocument, REPOSITORY_NAME }, null), is(true)); + verify(contentAuthHelper, times(1)).checkAssetPermissions(assetDocument, new String[]{REPOSITORY_NAME}); + } + + @Test + public void testComponentNotPermitted() { + when(contentAuthHelper.checkAssetPermissions(assetDocument, new String[]{REPOSITORY_NAME})).thenReturn(false); + assertThat(underTest.execute(underTest, null, null, new Object[] { componentDocument, REPOSITORY_NAME }, null), is(false)); + verify(contentAuthHelper, times(1)).checkAssetPermissions(assetDocument, new String[]{REPOSITORY_NAME}); + } + + @Test + public void testComponentPermitted_withGroupRepo() { + when(contentAuthHelper.checkAssetPermissions(assetDocument, new String[]{"group_repo"})).thenReturn(true); + assertThat(underTest.execute(underTest, null, null, new Object[] { componentDocument, "group_repo" }, null), is(true)); + verify(contentAuthHelper).checkAssetPermissions(assetDocument, new String[]{"group_repo"}); + verify(contentAuthHelper, never()).checkAssetPermissions(assetDocument, new String[]{REPOSITORY_NAME}); + } + + @Test + public void testComponentNotPermitted_withGroupRepo() { + assertThat(underTest.execute(underTest, null, null, new Object[] { componentDocument, "group_repo" }, null), is(false)); + verify(contentAuthHelper).checkAssetPermissions(assetDocument, new String[]{"group_repo"}); + verify(contentAuthHelper, never()).checkAssetPermissions(assetDocument, new String[]{REPOSITORY_NAME}); + } + + @Test + public void testAssetPermitted_jexlOnly() { + when(contentAuthHelper.checkAssetPermissionsJexlOnly(assetDocument, new String[] { REPOSITORY_NAME })).thenReturn( + true); + assertThat(underTest.execute(underTest, null, null, new Object[] { assetDocument, REPOSITORY_NAME, true }, null), + is(true)); + verify(contentAuthHelper, times(1)).checkAssetPermissionsJexlOnly(assetDocument, new String[] { REPOSITORY_NAME }); + } + + @Test + public void testAssetNotPermitted_jexlOnly() { + when(contentAuthHelper.checkAssetPermissionsJexlOnly(assetDocument, new String[] { REPOSITORY_NAME })).thenReturn( + false); + assertThat(underTest.execute(underTest, null, null, new Object[] { assetDocument, REPOSITORY_NAME, true }, null), + is(false)); + verify(contentAuthHelper, times(1)).checkAssetPermissionsJexlOnly(assetDocument, new String[] { REPOSITORY_NAME }); + } + + @Test + public void testComponentPermitted_jexlOnly() { + when(contentAuthHelper.checkAssetPermissionsJexlOnly(assetDocument, new String[] { REPOSITORY_NAME })).thenReturn( + true); + assertThat( + underTest.execute(underTest, null, null, new Object[] { componentDocument, REPOSITORY_NAME, true }, null), + is(true)); + verify(contentAuthHelper, times(1)).checkAssetPermissionsJexlOnly(assetDocument, new String[] { REPOSITORY_NAME }); + } + + @Test + public void testComponentNotPermitted_jexlOnly() { + when(contentAuthHelper.checkAssetPermissionsJexlOnly(assetDocument, new String[] { REPOSITORY_NAME })).thenReturn( + false); + assertThat( + underTest.execute(underTest, null, null, new Object[] { componentDocument, REPOSITORY_NAME, true }, null), + is(false)); + verify(contentAuthHelper, times(1)).checkAssetPermissionsJexlOnly(assetDocument, new String[] { REPOSITORY_NAME }); + } + + @Test + public void testComponentPermitted_withGroupRepo_jexlOnly() { + when(contentAuthHelper.checkAssetPermissionsJexlOnly(assetDocument, new String[] { "group_repo" })) + .thenReturn(true); + assertThat(underTest.execute(underTest, null, null, new Object[] { componentDocument, "group_repo", true }, null), + is(true)); + verify(contentAuthHelper).checkAssetPermissionsJexlOnly(assetDocument, new String[] { "group_repo" }); + verify(contentAuthHelper, never()).checkAssetPermissionsJexlOnly(assetDocument, new String[] { REPOSITORY_NAME }); + } + + @Test + public void testComponentNotPermitted_withGroupRepo_jexlOnly() { + assertThat(underTest.execute(underTest, null, null, new Object[] { componentDocument, "group_repo", true }, null), + is(false)); + verify(contentAuthHelper).checkAssetPermissionsJexlOnly(assetDocument, new String[] { "group_repo" }); + verify(contentAuthHelper, never()).checkAssetPermissionsJexlOnly(assetDocument, new String[] { REPOSITORY_NAME }); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/selector/internal/ContentExpressionFunctionTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/selector/internal/ContentExpressionFunctionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8960a89f8ff0a91d0af242deaec5c566ec2ad10d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/selector/internal/ContentExpressionFunctionTest.java @@ -0,0 +1,163 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.selector.internal; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.security.VariableResolverAdapter; +import org.sonatype.nexus.repository.security.VariableResolverAdapterManager; +import org.sonatype.nexus.selector.SelectorManager; +import org.sonatype.nexus.selector.VariableSource; + +import com.orientechnologies.orient.core.command.OCommandRequest; +import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal; +import com.orientechnologies.orient.core.db.record.OIdentifiable; +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.record.impl.ODocument; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link ContentExpressionFunction}. + */ +public class ContentExpressionFunctionTest + extends TestSupport +{ + private static final String REPOSITORY_NAME = "repository-name"; + + private static final String PATH = "path"; + + private static final String FORMAT = "format"; + + @Mock + VariableSource variableSource; + + @Mock + VariableResolverAdapter variableResolverAdapter; + + @Mock + VariableResolverAdapterManager variableResolverAdapterManager; + + @Mock + ODocument assetDocument; + + @Mock + ODocument bucketDocument; + + @Mock + OCommandRequest commandRequest; + + @Mock + ODatabaseDocumentInternal database; + + @Mock + private SelectorManager selectorManager; + + @Mock + private ContentAuthHelper contentAuthHelper; + + ContentExpressionFunction underTest; + + @Before + public void setup() { + when(variableResolverAdapterManager.get(FORMAT)).thenReturn(variableResolverAdapter); + when(variableResolverAdapter.fromDocument(assetDocument)).thenReturn(variableSource); + + when(bucketDocument.getRecord()).thenReturn(bucketDocument); + when(bucketDocument.field("repository_name", String.class)).thenReturn(REPOSITORY_NAME); + when(bucketDocument.getIdentity()).thenReturn(mock(ORID.class)); + + when(assetDocument.getClassName()).thenReturn("asset"); + when(assetDocument.getRecord()).thenReturn(assetDocument); + when(assetDocument.field("bucket", OIdentifiable.class)).thenReturn(bucketDocument); + when(assetDocument.field("name", String.class)).thenReturn(PATH); + when(assetDocument.field("format", String.class)).thenReturn(FORMAT); + + when(commandRequest.execute(any(Map.class))).thenReturn(Collections.singletonList(assetDocument)); + when(database.command(any(OCommandRequest.class))).thenReturn(commandRequest); + + underTest = new ContentExpressionFunction(variableResolverAdapterManager, selectorManager, contentAuthHelper); + } + + @Test + public void testMatchingAsset_SingleRepository() throws Exception { + when(selectorManager.evaluate(any(), any())).thenReturn(true); + when(contentAuthHelper.checkAssetPermissions(any(), eq(REPOSITORY_NAME))).thenReturn(true); + assertThat(underTest + .execute(underTest, null, null, new Object[]{assetDocument, "jexlexpression", REPOSITORY_NAME, ""}, + null), is(true)); + } + + @Test + public void testMatchingAsset_AllRepositories() throws Exception { + when(selectorManager.evaluate(any(), any())).thenReturn(true); + when(contentAuthHelper.checkAssetPermissions(any(), eq(REPOSITORY_NAME))).thenReturn(true); + Map> repoToContainedGroupMap = new HashMap<>(); + repoToContainedGroupMap.put(REPOSITORY_NAME, Arrays.asList(REPOSITORY_NAME)); + assertThat(underTest.execute(underTest, null, null, new Object[]{ + assetDocument, "jexlexpression", "*", repoToContainedGroupMap + }, null), is(true)); + } + + @Test + public void testMatchingAsset_AllRepositories_inGroup() throws Exception { + when(selectorManager.evaluate(any(), any())).thenReturn(true); + when(contentAuthHelper.checkAssetPermissions(any(), eq(REPOSITORY_NAME), eq("groupRepo"))).thenReturn(true); + Map> repoToContainedGroupMap = new HashMap<>(); + repoToContainedGroupMap.put(REPOSITORY_NAME, Arrays.asList(REPOSITORY_NAME, "groupRepo")); + assertThat(underTest.execute(underTest, null, null, new Object[]{ + assetDocument, "jexlexpression", "*", repoToContainedGroupMap + }, null), is(true)); + } + + @Test + public void testNonMatchingAsset_SingleRepository() throws Exception { + when(selectorManager.evaluate(any(), any())).thenReturn(true); + assertThat(underTest + .execute(underTest, null, null, new Object[]{assetDocument, "jexlexpression", REPOSITORY_NAME, ""}, + null), is(false)); + } + + @Test + public void testNonMatchingAsset_AllRepositories() throws Exception { + when(selectorManager.evaluate(any(), any())).thenReturn(true); + Map> repoToContainedGroupMap = new HashMap<>(); + repoToContainedGroupMap.put(REPOSITORY_NAME, Collections.emptyList()); + assertThat(underTest.execute(underTest, null, null, new Object[]{ + assetDocument, "jexlexpression", "*", repoToContainedGroupMap + }, null), is(false)); + } + + @Test + public void testNonMatchingAsset_AllRepositories_inGroup() throws Exception { + when(selectorManager.evaluate(any(), any())).thenReturn(true); + Map> repoToContainedGroupMap = new HashMap<>(); + repoToContainedGroupMap.put(REPOSITORY_NAME, Arrays.asList("groupRepo")); + assertThat(underTest.execute(underTest, null, null, new Object[]{ + assetDocument, "jexlexpression", "*", repoToContainedGroupMap + }, null), is(false)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/AssetEntityAdapterTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/AssetEntityAdapterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..43fdd5dbef711874f08b709017d2287f54cff49c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/AssetEntityAdapterTest.java @@ -0,0 +1,116 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.HashMap; + +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule; + +import com.google.common.collect.Iterables; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import static java.util.Collections.emptySet; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; +import static org.sonatype.nexus.repository.storage.StorageTestUtil.createAsset; +import static org.sonatype.nexus.repository.storage.StorageTestUtil.createComponent; + +public class AssetEntityAdapterTest +{ + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory("test"); + + private ComponentEntityAdapter componentEntityAdapter; + + private AssetEntityAdapter assetEntityAdapter; + + private Bucket bucket; + + @Before + public void setUp() { + BucketEntityAdapter bucketEntityAdapter = new BucketEntityAdapter(); + ComponentFactory componentFactory = new ComponentFactory(emptySet()); + componentEntityAdapter = new ComponentEntityAdapter(bucketEntityAdapter, componentFactory, emptySet()); + assetEntityAdapter = new AssetEntityAdapter(bucketEntityAdapter, componentEntityAdapter); + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + bucketEntityAdapter.register(db); + componentEntityAdapter.register(db); + assetEntityAdapter.register(db); + bucket = new Bucket(); + bucket.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + bucket.setRepositoryName("test-repo"); + bucketEntityAdapter.addEntity(db, bucket); + } + } + + @Test + public void testModifyAssetAfterBrowsingByComponent() { + + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + db.begin(); + + Component component = createComponent(bucket, "some-group", "some-component", "1.0"); + componentEntityAdapter.addEntity(db, component); + + Asset asset = createAsset(bucket, "some-asset", component); + assetEntityAdapter.addEntity(db, asset); + + db.commit(); + } + + Asset asset; + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + db.begin(); + + Component component = Iterables.getFirst(componentEntityAdapter.browseByQuery( + db, "name = :name", singletonMap("name", "some-component"), singletonList(bucket), null), null); + + asset = Iterables.getFirst(assetEntityAdapter.browseByComponent(db, component), null); + asset.attributes().child("mandatory").set("test-key", "test-value"); + assetEntityAdapter.editEntity(db, asset); + + db.commit(); + } + + assertThat(asset, is(notNullValue())); + + /* + * Check the asset's attributes can be modified outside of the transaction. + * + * This used to fail because the attributes were backed by an OTrackedMap + * which expected to find a live DB context whenever the map was mutated. + * We now wrap this tracking map so it detaches when this isn't the case. + * + * It also wasn't obvious when you were modifying attributes. For example + * just accessing a non-existent child section implicitly added it to the + * map, causing it to mutate... + */ + + // mandatory section already exists, so this won't mutate the attributes + assertTrue(asset.attributes().child("mandatory").contains("test-key")); + + // optional section doesn't exist yet, so this will mutate the attributes + assertFalse(asset.attributes().child("optional").contains("test-key")); + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/AssetStoreImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/AssetStoreImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..79468268950945bde89bd72c91fe039d2771ed9d --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/AssetStoreImplTest.java @@ -0,0 +1,122 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.index.OCompositeKey; +import com.orientechnologies.orient.core.index.OIndexCursor; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import static java.util.Collections.emptySet; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; +import static org.sonatype.nexus.repository.storage.StorageTestUtil.createAsset; +import static org.sonatype.nexus.repository.storage.StorageTestUtil.createComponent; + +@SuppressWarnings({ "unchecked", "rawtypes" }) +public class AssetStoreImplTest + extends TestSupport +{ + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory("test"); + + private AssetEntityAdapter assetEntityAdapter; + + private AssetStoreImpl underTest; + + private Bucket bucket; + + private Component component; + + @Before + public void setUp() { + BucketEntityAdapter bucketEntityAdapter = new BucketEntityAdapter(); + ComponentFactory componentFactory = new ComponentFactory(emptySet()); + ComponentEntityAdapter componentEntityAdapter = new ComponentEntityAdapter(bucketEntityAdapter, componentFactory, + emptySet()); + assetEntityAdapter = new AssetEntityAdapter(bucketEntityAdapter, componentEntityAdapter); + + underTest = new AssetStoreImpl(database.getInstanceProvider(), assetEntityAdapter); + + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + bucketEntityAdapter.register(db); + componentEntityAdapter.register(db); + assetEntityAdapter.register(db); + + bucket = new Bucket(); + bucket.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + bucket.setRepositoryName("test-repo"); + bucketEntityAdapter.addEntity(db, bucket); + + component = createComponent(bucket, "group", "name", "1.0"); + componentEntityAdapter.addEntity(db, component); + } + } + + @Test + public void getNextPageReturnsEntriesByPages() { + int limit = 2; + + Asset asset1 = createAsset(bucket, "asset1", component); + Asset asset2 = createAsset(bucket, "asset2", component); + Asset asset3 = createAsset(bucket, "asset3", component); + + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + assetEntityAdapter.addEntity(db, asset1); + assetEntityAdapter.addEntity(db, asset2); + assetEntityAdapter.addEntity(db, asset3); + } + + OIndexCursor cursor = underTest.getIndex(AssetEntityAdapter.I_BUCKET_COMPONENT_NAME).cursor(); + + List> assetPage1 = underTest.getNextPage(cursor, limit); + List> assetPage2 = underTest.getNextPage(cursor, limit); + assertThat(assetPage1.size(), is(2)); + assertThat(assetPage1.get(0).getValue(), is(EntityHelper.id(asset1))); + assertThat(assetPage1.get(1).getValue(), is(EntityHelper.id(asset2))); + assertThat(assetPage2.size(), is(1)); + assertThat(assetPage2.get(0).getValue(), is(EntityHelper.id(asset3))); + } + + @Test + public void getNextPageStopsAtNullEntry() { + int limit = 2; + + Asset asset1 = createAsset(bucket, "asset1", component); + + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + assetEntityAdapter.addEntity(db, asset1); + } + + OIndexCursor cursor = underTest.getIndex(AssetEntityAdapter.I_BUCKET_COMPONENT_NAME).cursor(); + + List> assetPage1 = underTest.getNextPage(cursor, limit); + List> assetPage2 = underTest.getNextPage(cursor, limit); + + assertThat(assetPage1.size(), is(1)); + assertThat(assetPage1.get(0).getValue(), is(EntityHelper.id(asset1))); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BlobTxTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BlobTxTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..01fc462cf854ada817f9872deeceeefab6390fa1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BlobTxTest.groovy @@ -0,0 +1,156 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage + +import java.nio.file.Path + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.blobstore.api.Blob +import org.sonatype.nexus.blobstore.api.BlobId +import org.sonatype.nexus.blobstore.api.BlobMetrics +import org.sonatype.nexus.blobstore.api.BlobStore +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration +import org.sonatype.nexus.common.hash.HashAlgorithm +import org.sonatype.nexus.common.node.NodeAccess + +import com.google.common.hash.HashCode +import org.junit.Test + +import static org.hamcrest.MatcherAssert.assertThat +import static org.hamcrest.Matchers.equalTo +import static org.hamcrest.Matchers.hasEntry +import static org.hamcrest.Matchers.is +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.when +import static org.sonatype.nexus.blobstore.api.BlobStore.CONTENT_TYPE_HEADER +import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA1 + +/** + * Tests for {@link BlobTx}. + */ +class BlobTxTest + extends TestSupport +{ + @Test + void 'create blob with verified hashes'() { + + final long blobSize = 10L + final String contentType = 'text/plain' + final InputStream inputStream = new ByteArrayInputStream('helloworld'.getBytes('UTF-8')) + + final NodeAccess nodeAccess = mock(NodeAccess.class) + when(nodeAccess.getId()).thenReturn('id') + + final BlobStoreConfiguration blobStoreConfiguration = mock(BlobStoreConfiguration.class) + when(blobStoreConfiguration.getName()).thenReturn('blobStoreConfiguration') + + final BlobMetrics blobMetrics = mock(BlobMetrics.class) + when(blobMetrics.getContentSize()).thenReturn(blobSize) + + final BlobId blobId = new BlobId('blobid') + final Blob blob = mock(Blob.class) + when(blob.getMetrics()).thenReturn(blobMetrics) + when(blob.getId()).thenReturn(blobId) + + final BlobStore blobStore = [ + getBlobStoreConfiguration: { blobStoreConfiguration }, + create : { InputStream is, Map map -> is.text; blob } + ] as BlobStore + + final BlobTx testSubject = new BlobTx(nodeAccess, blobStore) + final AssetBlob assetBlob = testSubject.create(inputStream, [:], [SHA1], contentType) + + assertThat(assetBlob.contentType, is(equalTo(contentType))) + assertThat(assetBlob.size, is(equalTo(blobSize))) + assertThat(assetBlob.hashesVerified, is(true)) + assertThat(assetBlob.hashes, hasEntry(SHA1, HashCode.fromString('6adfb183a4a2c94a2f92dab5ade762a47889a5a1'))) + assertThat(assetBlob.ingestedBlob, is(blob)) + } + + @Test + void 'create blob from hard link without verified hashes'() { + + final Path path = mock(Path.class) + final long blobSize = 1L + final String contentType = 'text/plain' + final Map headers = [:] + final Map hashes = [(SHA1): HashCode.fromString('356a192b7913b04c54574d18c28d46e6395428ab')] + + final NodeAccess nodeAccess = mock(NodeAccess.class) + when(nodeAccess.getId()).thenReturn('id') + + final BlobStoreConfiguration blobStoreConfiguration = mock(BlobStoreConfiguration.class) + when(blobStoreConfiguration.getName()).thenReturn('blobStoreConfiguration') + + final BlobMetrics blobMetrics = mock(BlobMetrics.class) + when(blobMetrics.getContentSize()).thenReturn(blobSize) + + final BlobId blobId = new BlobId('blobid') + final Blob blob = mock(Blob.class) + when(blob.getMetrics()).thenReturn(blobMetrics) + when(blob.getId()).thenReturn(blobId) + + final BlobStore blobStore = [ + getBlobStoreConfiguration: { blobStoreConfiguration }, + create : { Path p, Map map, long size, HashCode sha1 -> blob } + ] as BlobStore + + final BlobTx testSubject = new BlobTx(nodeAccess, blobStore) + final AssetBlob assetBlob = testSubject.createByHardLinking(path, headers, hashes, contentType, blobSize) + + assertThat(assetBlob.contentType, is(equalTo(contentType))) + assertThat(assetBlob.size, is(equalTo(blobSize))) + assertThat(assetBlob.hashesVerified, is(false)) + assertThat(assetBlob.hashes, hasEntry(SHA1, HashCode.fromString('356a192b7913b04c54574d18c28d46e6395428ab'))) + assertThat(assetBlob.ingestedBlob, is(blob)) + } + + @Test + void 'create blob from existing blob'() { + + long blobSize = 1L + String contentType = 'text/plain' + Map headers = [(CONTENT_TYPE_HEADER): contentType] + Map hashes = [(SHA1): HashCode.fromString('356a192b7913b04c54574d18c28d46e6395428ab')] + + NodeAccess nodeAccess = mock(NodeAccess.class) + when(nodeAccess.getId()).thenReturn('id') + + BlobStoreConfiguration blobStoreConfiguration = mock(BlobStoreConfiguration.class) + when(blobStoreConfiguration.getName()).thenReturn('blobStoreConfiguration') + + BlobMetrics blobMetrics = mock(BlobMetrics.class) + when(blobMetrics.getContentSize()).thenReturn(blobSize) + when(blobMetrics.getSha1Hash()).thenReturn('356a192b7913b04c54574d18c28d46e6395428ab') + + BlobId blobId = new BlobId('blobid') + Blob blob = mock(Blob.class) + when(blob.getMetrics()).thenReturn(blobMetrics) + when(blob.getId()).thenReturn(blobId) + + BlobStore blobStore = [ + getBlobStoreConfiguration: { blobStoreConfiguration }, + get : { BlobId id -> blob }, + copy : { BlobId id, Map map -> blob } + ] as BlobStore + + BlobTx testSubject = new BlobTx(nodeAccess, blobStore) + AssetBlob assetBlob = testSubject.createByCopying(blobId, headers, hashes, true) + + assertThat(assetBlob.contentType, is(equalTo(contentType))) + assertThat(assetBlob.size, is(equalTo(blobSize))) + assertThat(assetBlob.hashesVerified, is(true)) + assertThat(assetBlob.hashes, hasEntry(SHA1, HashCode.fromString('356a192b7913b04c54574d18c28d46e6395428ab'))) + assertThat(assetBlob.ingestedBlob, is(blob)) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BrowseNodeEntityAdapterTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BrowseNodeEntityAdapterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1702032284632f366f577660a334b7cce6a602fe --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BrowseNodeEntityAdapterTest.java @@ -0,0 +1,665 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.HashMap; +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.common.text.Strings2; +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; + +import com.google.common.base.Splitter; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.emptyIterable; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; + +public class BrowseNodeEntityAdapterTest + extends TestSupport +{ + private static final String REPOSITORY_NAME = "test-repo"; + + private static final String FORMAT_NAME = "test-format"; + + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inFilesystem("test"); + + private BucketEntityAdapter bucketEntityAdapter; + + private ComponentEntityAdapter componentEntityAdapter; + + private AssetEntityAdapter assetEntityAdapter; + + private BrowseNodeEntityAdapter underTest; + + private Bucket bucket; + + private Component component; + + private Asset asset; + + @Before + public void setUp() throws Exception { + + bucketEntityAdapter = new BucketEntityAdapter(); + ComponentFactory componentFactory = new ComponentFactory(emptySet()); + componentEntityAdapter = new ComponentEntityAdapter(bucketEntityAdapter, componentFactory, emptySet()); + assetEntityAdapter = new AssetEntityAdapter(bucketEntityAdapter, componentEntityAdapter); + + underTest = new BrowseNodeEntityAdapter(componentEntityAdapter, assetEntityAdapter, new BrowseNodeConfiguration()); + + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + bucketEntityAdapter.register(db); + componentEntityAdapter.register(db); + assetEntityAdapter.register(db); + underTest.register(db); + + createEntities(db); + } + } + + @Test + public void manageAssetBelowComponent() throws Exception { + List path = Splitter.on('/').omitEmptyStrings().splitToList(asset.name()); + + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + underTest.createComponentNode(db, REPOSITORY_NAME, path.subList(0, 3), component); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/")), + hasProperty("name", is("1.0")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + underTest.createAssetNode(db, REPOSITORY_NAME, path, asset); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/")), + hasProperty("name", is("1.0")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())), + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", nullValue()), + hasProperty("assetId", is(EntityHelper.id(asset))), + hasProperty("assetNameLowercase", is(Strings2.lower(asset.name())))) + )); + + underTest.deleteAssetNode(db, EntityHelper.id(asset)); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/")), + hasProperty("name", is("1.0")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + underTest.deleteComponentNode(db, EntityHelper.id(component)); + + assertThat(underTest.browse(db), is(emptyIterable())); + } + } + + @Test + public void manageAssetSameLevelAsComponent() throws Exception { + List path = Splitter.on('/').omitEmptyStrings().splitToList(asset.name()); + + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + underTest.createComponentNode(db, REPOSITORY_NAME, path, component); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + underTest.createAssetNode(db, REPOSITORY_NAME, path, asset); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", is(EntityHelper.id(asset))), + hasProperty("assetNameLowercase", is(Strings2.lower(asset.name())))) + )); + + underTest.deleteAssetNode(db, EntityHelper.id(asset)); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + underTest.deleteComponentNode(db, EntityHelper.id(component)); + + assertThat(underTest.browse(db), is(emptyIterable())); + } + + // now try out-of-order to check partial delete still works + + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + underTest.createAssetNode(db, REPOSITORY_NAME, path, asset); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", nullValue()), + hasProperty("assetId", is(EntityHelper.id(asset))), + hasProperty("assetNameLowercase", is(Strings2.lower(asset.name())))) + )); + + underTest.createComponentNode(db, REPOSITORY_NAME, path, component); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", is(EntityHelper.id(asset))), + hasProperty("assetNameLowercase", is(Strings2.lower(asset.name())))) + )); + + underTest.deleteComponentNode(db, EntityHelper.id(component)); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", nullValue()), + hasProperty("assetId", is(EntityHelper.id(asset))), + hasProperty("assetNameLowercase", is(Strings2.lower(asset.name())))) + )); + + underTest.deleteAssetNode(db, EntityHelper.id(asset)); + + assertThat(underTest.browse(db), is(emptyIterable())); + } + } + + @Test + public void manageComponentRepeatedInTree() throws Exception { + + // test that assets can have the same component at different paths in the tree if they want + + Asset patchAsset = new Asset(); + patchAsset.bucketId(EntityHelper.id(bucket)); + patchAsset.componentId(EntityHelper.id(component)); + patchAsset.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + patchAsset.format(FORMAT_NAME); + patchAsset.name("/org/foo/1.0/foo-1.0-1.jar"); + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + assetEntityAdapter.addEntity(db, patchAsset); + } + + List path = Splitter.on('/').omitEmptyStrings().splitToList(asset.name()); + List patchPath = Splitter.on('/').omitEmptyStrings().splitToList(patchAsset.name()); + + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + underTest.createComponentNode(db, REPOSITORY_NAME, path, component); + underTest.createComponentNode(db, REPOSITORY_NAME, patchPath, component); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + , + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0-1.jar")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + underTest.createAssetNode(db, REPOSITORY_NAME, path, asset); + underTest.createAssetNode(db, REPOSITORY_NAME, patchPath, patchAsset); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", is(EntityHelper.id(asset))), + hasProperty("assetNameLowercase", is(Strings2.lower(asset.name())))) + , + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0-1.jar")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", is(EntityHelper.id(patchAsset))), + hasProperty("assetNameLowercase", is(Strings2.lower(patchAsset.name())))) + )); + + underTest.deleteAssetNode(db, EntityHelper.id(asset)); + underTest.deleteAssetNode(db, EntityHelper.id(patchAsset)); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + , + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0-1.jar")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + underTest.deleteComponentNode(db, EntityHelper.id(component)); + + assertThat(underTest.browse(db), is(emptyIterable())); + } + + // now try out-of-order to check partial delete still works + + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + underTest.createAssetNode(db, REPOSITORY_NAME, path, asset); + underTest.createAssetNode(db, REPOSITORY_NAME, patchPath, patchAsset); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", nullValue()), + hasProperty("assetId", is(EntityHelper.id(asset))), + hasProperty("assetNameLowercase", is(Strings2.lower(asset.name())))) + , + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0-1.jar")), + hasProperty("componentId", nullValue()), + hasProperty("assetId", is(EntityHelper.id(patchAsset))), + hasProperty("assetNameLowercase", is(Strings2.lower(patchAsset.name())))) + )); + + underTest.createComponentNode(db, REPOSITORY_NAME, path, component); + underTest.createComponentNode(db, REPOSITORY_NAME, patchPath, component); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", is(EntityHelper.id(asset))), + hasProperty("assetNameLowercase", is(Strings2.lower(asset.name())))) + , + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0-1.jar")), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", is(EntityHelper.id(patchAsset))), + hasProperty("assetNameLowercase", is(Strings2.lower(patchAsset.name())))) + )); + + underTest.deleteComponentNode(db, EntityHelper.id(component)); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", nullValue()), + hasProperty("assetId", is(EntityHelper.id(asset))), + hasProperty("assetNameLowercase", is(Strings2.lower(asset.name())))) + , + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0-1.jar")), + hasProperty("componentId", nullValue()), + hasProperty("assetId", is(EntityHelper.id(patchAsset))), + hasProperty("assetNameLowercase", is(Strings2.lower(patchAsset.name())))) + )); + + underTest.deleteAssetNode(db, EntityHelper.id(asset)); + underTest.deleteAssetNode(db, EntityHelper.id(patchAsset)); + + assertThat(underTest.browse(db), is(emptyIterable())); + } + } + + @Test + public void duplicateRequestsAreMerged() throws Exception { + List path = Splitter.on('/').omitEmptyStrings().splitToList(asset.name()); + + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + underTest.createAssetNode(db, REPOSITORY_NAME, path, asset); + underTest.createAssetNode(db, REPOSITORY_NAME, path, asset); + underTest.createAssetNode(db, REPOSITORY_NAME, path, asset); + + assertThat(underTest.browse(db), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("componentId", nullValue()), + hasProperty("assetId", is(EntityHelper.id(asset))), + hasProperty("assetNameLowercase", is(Strings2.lower(asset.name())))) + )); + } + } + + @Test + public void pagedDeletes() throws Exception { + List path = Splitter.on('/').omitEmptyStrings().splitToList(asset.name()); + + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + underTest.createComponentNode(db, REPOSITORY_NAME, path.subList(0, 3), component); + underTest.createAssetNode(db, REPOSITORY_NAME, path, asset); + + assertThat(underTest.count(db), is(2L)); + + int deleteCount = underTest.deleteByRepository(db, REPOSITORY_NAME, 1); + + assertThat(deleteCount, is(1)); + assertThat(underTest.count(db), is(1L)); + + deleteCount = underTest.deleteByRepository(db, REPOSITORY_NAME, 1); + + assertThat(deleteCount, is(1)); + assertThat(underTest.count(db), is(0L)); + + deleteCount = underTest.deleteByRepository(db, REPOSITORY_NAME, 1); + + assertThat(deleteCount, is(0)); + assertThat(underTest.count(db), is(0L)); + } + } + + @Test + public void browseAssetBelowComponent() throws Exception { + List path = Splitter.on('/').omitEmptyStrings().splitToList(asset.name()); + + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + underTest.createComponentNode(db, REPOSITORY_NAME, path.subList(0, 3), component); + underTest.createAssetNode(db, REPOSITORY_NAME, path, asset); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path.subList(0, 0), 1, "", emptyMap()), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/")), + hasProperty("name", is("org")), + hasProperty("leaf", is(false)), + hasProperty("componentId", nullValue()), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path.subList(0, 1), 1, "", emptyMap()), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/")), + hasProperty("name", is("foo")), + hasProperty("leaf", is(false)), + hasProperty("componentId", nullValue()), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path.subList(0, 2), 1, "", emptyMap()), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/")), + hasProperty("name", is("1.0")), + hasProperty("leaf", is(false)), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path.subList(0, 3), 1, "", emptyMap()), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("leaf", is(true)), + hasProperty("componentId", nullValue()), + hasProperty("assetId", is(EntityHelper.id(asset))), + hasProperty("assetNameLowercase", is(Strings2.lower(asset.name())))) + )); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path, 1, "", emptyMap()), is(empty())); + } + } + + @Test + public void browseAssetSameLevelAsComponent() throws Exception { + List path = Splitter.on('/').omitEmptyStrings().splitToList(asset.name()); + + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + underTest.createComponentNode(db, REPOSITORY_NAME, path, component); + underTest.createAssetNode(db, REPOSITORY_NAME, path, asset); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path.subList(0, 0), 1, "", emptyMap()), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/")), + hasProperty("name", is("org")), + hasProperty("leaf", is(false)), + hasProperty("componentId", nullValue()), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path.subList(0, 1), 1, "", emptyMap()), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/")), + hasProperty("name", is("foo")), + hasProperty("leaf", is(false)), + hasProperty("componentId", nullValue()), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path.subList(0, 2), 1, "", emptyMap()), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/")), + hasProperty("name", is("1.0")), + hasProperty("leaf", is(false)), + hasProperty("componentId", nullValue()), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path.subList(0, 3), 1, "", emptyMap()), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("leaf", is(true)), + hasProperty("componentId", is(EntityHelper.id(component))), + hasProperty("assetId", is(EntityHelper.id(asset))), + hasProperty("assetNameLowercase", is(Strings2.lower(asset.name())))) + )); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path, 1, "", emptyMap()), is(empty())); + } + } + + @Test + public void browseNestedAssets() throws Exception { + List path = Splitter.on('/').omitEmptyStrings().splitToList(asset.name()); + + try (ODatabaseDocumentTx db = database.getInstance().acquire()) { + + Asset parentAsset = new Asset(); + parentAsset.bucketId(EntityHelper.id(bucket)); + parentAsset.componentId(EntityHelper.id(component)); + parentAsset.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + parentAsset.format(FORMAT_NAME); + parentAsset.name("/org/foo"); + assetEntityAdapter.addEntity(db, parentAsset); + + underTest.createAssetNode(db, REPOSITORY_NAME, path.subList(0, 2), parentAsset); + underTest.createAssetNode(db, REPOSITORY_NAME, path, asset); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path.subList(0, 0), 1, "", emptyMap()), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/")), + hasProperty("name", is("org")), + hasProperty("leaf", is(false)), + hasProperty("componentId", nullValue()), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path.subList(0, 1), 1, "", emptyMap()), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/")), + hasProperty("name", is("foo")), + hasProperty("leaf", is(false)), + hasProperty("componentId", nullValue()), + hasProperty("assetId", is(EntityHelper.id(parentAsset))), + hasProperty("assetNameLowercase", is(Strings2.lower(parentAsset.name())))) + )); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path.subList(0, 2), 1, "", emptyMap()), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/")), + hasProperty("name", is("1.0")), + hasProperty("leaf", is(false)), + hasProperty("componentId", nullValue()), + hasProperty("assetId", nullValue()), + hasProperty("assetNameLowercase", nullValue())) + )); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path.subList(0, 3), 1, "", emptyMap()), + contains( + allOf( + hasProperty("repositoryName", is(REPOSITORY_NAME)), + hasProperty("parentPath", is("/org/foo/1.0/")), + hasProperty("name", is("foo-1.0.jar")), + hasProperty("leaf", is(true)), + hasProperty("componentId", nullValue()), + hasProperty("assetId", is(EntityHelper.id(asset))), + hasProperty("assetNameLowercase", is(Strings2.lower(asset.name())))) + )); + + assertThat(underTest.getByPath(db, REPOSITORY_NAME, path, 1, "", emptyMap()), is(empty())); + } + } + + private void createEntities(final ODatabaseDocumentTx db) { + bucket = new Bucket(); + bucket.setRepositoryName(REPOSITORY_NAME); + bucket.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + bucketEntityAdapter.addEntity(db, bucket); + + component = new DefaultComponent(); + component.bucketId(EntityHelper.id(bucket)); + component.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + component.format(FORMAT_NAME); + component.group("org").name("foo").version("1.0"); + componentEntityAdapter.addEntity(db, component); + + asset = new Asset(); + asset.bucketId(EntityHelper.id(bucket)); + asset.componentId(EntityHelper.id(component)); + asset.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + asset.format(FORMAT_NAME); + asset.name("/org/foo/1.0/foo-1.0.jar"); + assetEntityAdapter.addEntity(db, asset); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BrowseNodeStoreImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BrowseNodeStoreImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bb62b90f3a99965fa9a67b72fafb9d17a558da79 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BrowseNodeStoreImplTest.java @@ -0,0 +1,458 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; +import org.sonatype.nexus.repository.group.GroupFacet; +import org.sonatype.nexus.repository.security.RepositoryViewPermission; +import org.sonatype.nexus.repository.types.GroupType; +import org.sonatype.nexus.security.SecurityHelper; +import org.sonatype.nexus.selector.CselAssetSqlBuilder; +import org.sonatype.nexus.selector.CselSelector; +import org.sonatype.nexus.selector.JexlSelector; +import org.sonatype.nexus.selector.SelectorConfiguration; +import org.sonatype.nexus.selector.SelectorManager; + +import com.google.common.collect.ImmutableMap; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.is; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.sonatype.goodies.common.Time.seconds; + +public class BrowseNodeStoreImplTest + extends TestSupport +{ + private static final String REPOSITORY_NAME = "test-repo"; + + private static final String MEMBER_A = "test-repo-a"; + + private static final String MEMBER_B = "test-repo-b"; + + private static final String MEMBER_C = "test-repo-c"; + + private static final String FORMAT_NAME = "test-format"; + + private static final int MAX_NODES = 100; + + private static final int DELETE_PAGE_SIZE = 80; + + @Mock + private DatabaseInstance databaseInstance; + + @Mock + private ODatabaseDocumentTx db; + + @Mock + private BrowseNodeEntityAdapter browseNodeEntityAdapter; + + @Mock + private SecurityHelper securityHelper; + + @Mock + private SelectorManager selectorManager; + + @Mock + private SelectorConfiguration byGroup; + + @Mock + private SelectorConfiguration byVersion; + + @Mock + private SelectorConfiguration jexl; + + @Mock + private Repository repository; + + @Mock + private Repository memberA; + + @Mock + private Repository memberB; + + @Mock + private Repository memberC; + + @Mock + private GroupFacet groupFacet; + + @Mock + private Format format; + + @Mock + private Component component; + + @Mock + private EntityId componentId; + + @Mock + private Asset asset; + + @Mock + private EntityId assetId; + + private BrowseNodeStoreImpl underTest; + + @Before + public void setUp() throws Exception { + when(databaseInstance.acquire()).thenReturn(db); + when(databaseInstance.connect()).thenReturn(db); + + when(format.getValue()).thenReturn(FORMAT_NAME); + when(repository.getName()).thenReturn(REPOSITORY_NAME); + when(repository.getFormat()).thenReturn(format); + when(memberA.getName()).thenReturn(MEMBER_A); + when(memberA.getFormat()).thenReturn(format); + when(memberB.getName()).thenReturn(MEMBER_B); + when(memberB.getFormat()).thenReturn(format); + when(memberC.getName()).thenReturn(MEMBER_C); + when(memberC.getFormat()).thenReturn(format); + + when(byGroup.getType()).thenReturn(CselSelector.TYPE); + when(byGroup.getAttributes()).thenReturn(ImmutableMap.of("expression", "coordinate.groupId == \"org.sonatype\"")); + when(byVersion.getType()).thenReturn(CselSelector.TYPE); + when(byVersion.getAttributes()).thenReturn(ImmutableMap.of("expression", "coordinate.version == \"2.1\"")); + when(jexl.getType()).thenReturn(JexlSelector.TYPE); + + underTest = new BrowseNodeStoreImpl( + () -> databaseInstance, + browseNodeEntityAdapter, + securityHelper, + selectorManager, + new CselAssetSqlBuilder(), + new BrowseNodeConfiguration(true, true, 1000, DELETE_PAGE_SIZE, 10_000, 10_000, seconds(0))); + + underTest.start(); + + verify(browseNodeEntityAdapter).register(db); + } + + @After + public void tearDown() throws Exception { + underTest.stop(); + } + + @Test + public void storeOperations() throws Exception { + List componentPath = asList("org", "foo", "1.0"); + List assetPath = asList("org", "foo", "1.0", "foo-1.0.jar"); + + underTest.createComponentNode(REPOSITORY_NAME, componentPath, component); + underTest.createAssetNode(REPOSITORY_NAME, assetPath, asset); + underTest.deleteAssetNode(assetId); + underTest.deleteComponentNode(componentId); + + verify(browseNodeEntityAdapter).createComponentNode(db, REPOSITORY_NAME, componentPath, component); + verify(browseNodeEntityAdapter).createAssetNode(db, REPOSITORY_NAME, assetPath, asset); + verify(browseNodeEntityAdapter).deleteAssetNode(db, assetId); + verify(browseNodeEntityAdapter).deleteComponentNode(db, componentId); + + // check truncation stops when results drop the below the expected page size + when(browseNodeEntityAdapter.deleteByRepository(db, REPOSITORY_NAME, DELETE_PAGE_SIZE)).thenReturn( + DELETE_PAGE_SIZE, DELETE_PAGE_SIZE, DELETE_PAGE_SIZE - 1); + + underTest.deleteByRepository(REPOSITORY_NAME); + + verify(browseNodeEntityAdapter, times(3)).deleteByRepository(db, REPOSITORY_NAME, DELETE_PAGE_SIZE); + + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void simpleQueryWithBrowsePermission() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(true); + + underTest.getByPath(repository, queryPath, MAX_NODES, null); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + verify(browseNodeEntityAdapter).getByPath(db, REPOSITORY_NAME, queryPath, MAX_NODES, "", emptyMap()); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void keywordQueryWithBrowsePermission() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(true); + + underTest.getByPath(repository, queryPath, MAX_NODES, "wibble"); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + verify(browseNodeEntityAdapter).getByPath(db, REPOSITORY_NAME, queryPath, MAX_NODES, + "asset_name_lowercase like '%wibble%'", emptyMap()); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void simpleQueryWithoutBrowsePermissionOrSelectors() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(false); + when(selectorManager.browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME))).thenReturn(emptyList()); + + underTest.getByPath(repository, queryPath, MAX_NODES, null); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + verify(selectorManager).browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME)); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void keywordQueryWithoutBrowsePermissionOrSelectors() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(false); + when(selectorManager.browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME))).thenReturn(emptyList()); + + underTest.getByPath(repository, queryPath, MAX_NODES, "wibble"); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + verify(selectorManager).browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME)); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void simpleQueryWithContentSelector() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(false); + when(selectorManager.browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME))).thenReturn(asList(byGroup)); + + underTest.getByPath(repository, queryPath, MAX_NODES, null); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + verify(selectorManager).browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME)); + verify(browseNodeEntityAdapter).getByPath(db, REPOSITORY_NAME, queryPath, MAX_NODES, + "(asset_id.attributes.test-format.groupId = :s0p0)", ImmutableMap.of("s0p0", "org.sonatype")); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void keywordQueryWithContentSelector() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(false); + when(selectorManager.browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME))).thenReturn(asList(byGroup)); + + underTest.getByPath(repository, queryPath, MAX_NODES, "wibble"); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + verify(selectorManager).browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME)); + verify(browseNodeEntityAdapter).getByPath(db, REPOSITORY_NAME, queryPath, MAX_NODES, + "asset_name_lowercase like '%wibble%' and (asset_id.attributes.test-format.groupId = :s0p0)", + ImmutableMap.of("s0p0", "org.sonatype")); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void simpleQueryWithContentSelectors() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(false); + when(selectorManager.browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME))) + .thenReturn(asList(byGroup, byVersion)); + + underTest.getByPath(repository, queryPath, MAX_NODES, null); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + verify(selectorManager).browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME)); + verify(browseNodeEntityAdapter).getByPath(db, REPOSITORY_NAME, queryPath, MAX_NODES, + "((asset_id.attributes.test-format.groupId = :s0p0) or (asset_id.attributes.test-format.version = :s1p0))", + ImmutableMap.of("s0p0", "org.sonatype", "s1p0", "2.1")); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void keywordQueryWithContentSelectors() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(false); + when(selectorManager.browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME))) + .thenReturn(asList(byGroup, byVersion)); + + underTest.getByPath(repository, queryPath, MAX_NODES, "wibble"); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + verify(selectorManager).browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME)); + verify(browseNodeEntityAdapter).getByPath(db, REPOSITORY_NAME, queryPath, MAX_NODES, + "asset_name_lowercase like '%wibble%' and" + + " ((asset_id.attributes.test-format.groupId = :s0p0) or (asset_id.attributes.test-format.version = :s1p0))", + ImmutableMap.of("s0p0", "org.sonatype", "s1p0", "2.1")); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void simpleQueryWithContentSelectorMix() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(false); + when(selectorManager.browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME))) + .thenReturn(asList(byGroup, jexl, byVersion)); + + underTest.getByPath(repository, queryPath, MAX_NODES, null); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + verify(selectorManager).browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME)); + verify(browseNodeEntityAdapter).getByPath(db, REPOSITORY_NAME, queryPath, MAX_NODES, + "((asset_id.attributes.test-format.groupId = :s0p0) or (asset_id.attributes.test-format.version = :s1p0)" + + " or contentAuth(@this.asset_id, :authz_repository_name, true) = true)", + ImmutableMap.of("s0p0", "org.sonatype", "s1p0", "2.1", "authz_repository_name", "test-repo")); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void keywordQueryWithContentSelectorMix() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(false); + when(selectorManager.browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME))) + .thenReturn(asList(byGroup, jexl, byVersion)); + + underTest.getByPath(repository, queryPath, MAX_NODES, "wibble"); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + verify(selectorManager).browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME)); + verify(browseNodeEntityAdapter).getByPath(db, REPOSITORY_NAME, queryPath, MAX_NODES, + "asset_name_lowercase like '%wibble%' and" + + " ((asset_id.attributes.test-format.groupId = :s0p0) or (asset_id.attributes.test-format.version = :s1p0)" + + " or contentAuth(@this.asset_id, :authz_repository_name, true) = true)", + ImmutableMap.of("s0p0", "org.sonatype", "s1p0", "2.1", "authz_repository_name", "test-repo")); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void simpleQueryWithJEXL() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(false); + when(selectorManager.browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME))).thenReturn(asList(jexl)); + + underTest.getByPath(repository, queryPath, MAX_NODES, null); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + verify(selectorManager).browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME)); + verify(browseNodeEntityAdapter).getByPath(db, REPOSITORY_NAME, queryPath, MAX_NODES, + "contentAuth(@this.asset_id, :authz_repository_name, true) = true", + ImmutableMap.of("authz_repository_name", REPOSITORY_NAME)); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void keywordQueryWithJEXL() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(false); + when(selectorManager.browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME))).thenReturn(asList(jexl)); + + underTest.getByPath(repository, queryPath, MAX_NODES, "wibble"); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + verify(selectorManager).browseActive(asList(REPOSITORY_NAME), asList(FORMAT_NAME)); + verify(browseNodeEntityAdapter).getByPath(db, REPOSITORY_NAME, queryPath, MAX_NODES, + "asset_name_lowercase like '%wibble%' and contentAuth(@this.asset_id, :authz_repository_name, true) = true", + ImmutableMap.of("authz_repository_name", REPOSITORY_NAME)); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void groupQuery() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(true); + when(repository.getType()).thenReturn(new GroupType()); + when(repository.facet(GroupFacet.class)).thenReturn(groupFacet); + when(groupFacet.leafMembers()).thenReturn(asList(memberA, memberB, memberC)); + + when(browseNodeEntityAdapter.getByPath(db, MEMBER_A, queryPath, MAX_NODES, "", emptyMap())) + .thenReturn(asList(node(MEMBER_A, "com"), node(MEMBER_A, "org"))); + when(browseNodeEntityAdapter.getByPath(db, MEMBER_B, queryPath, MAX_NODES, "", emptyMap())) + .thenReturn(asList(node(MEMBER_B, "biz"), node(MEMBER_B, "org"))); + when(browseNodeEntityAdapter.getByPath(db, MEMBER_C, queryPath, MAX_NODES, "", emptyMap())) + .thenReturn(asList(node(MEMBER_C, "com"), node(MEMBER_C, "javax"))); + + Iterable nodes = underTest.getByPath(repository, queryPath, MAX_NODES, null); + + // check that duplicate nodes were removed, should follow a 'first-one-wins' approach + assertThat(nodes, containsInAnyOrder( + allOf(hasProperty("repositoryName", is(MEMBER_A)), hasProperty("name", is("com"))), + allOf(hasProperty("repositoryName", is(MEMBER_A)), hasProperty("name", is("org"))), + allOf(hasProperty("repositoryName", is(MEMBER_B)), hasProperty("name", is("biz"))), + allOf(hasProperty("repositoryName", is(MEMBER_C)), hasProperty("name", is("javax"))))); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + verify(browseNodeEntityAdapter).getByPath(db, MEMBER_A, queryPath, MAX_NODES, "", emptyMap()); + verify(browseNodeEntityAdapter).getByPath(db, MEMBER_B, queryPath, MAX_NODES, "", emptyMap()); + verify(browseNodeEntityAdapter).getByPath(db, MEMBER_C, queryPath, MAX_NODES, "", emptyMap()); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + @Test + public void groupQueryWithLimit() throws Exception { + List queryPath = asList("org", "foo"); + + when(securityHelper.anyPermitted(any())).thenReturn(true); + when(repository.getType()).thenReturn(new GroupType()); + when(repository.facet(GroupFacet.class)).thenReturn(groupFacet); + when(groupFacet.leafMembers()).thenReturn(asList(memberA, memberB, memberC)); + + when(browseNodeEntityAdapter.getByPath(db, MEMBER_A, queryPath, 1, "", emptyMap())) + .thenReturn(asList(node(MEMBER_A, "com"))); + when(browseNodeEntityAdapter.getByPath(db, MEMBER_B, queryPath, 1, "", emptyMap())) + .thenReturn(asList(node(MEMBER_B, "com"))); + when(browseNodeEntityAdapter.getByPath(db, MEMBER_C, queryPath, 1, "", emptyMap())) + .thenReturn(asList(node(MEMBER_C, "com"))); + + Iterable nodes = underTest.getByPath(repository, queryPath, 1, null); + + // check that the limit was correctly applied to the merged results + assertThat(nodes, containsInAnyOrder( + allOf(hasProperty("repositoryName", is(MEMBER_A)), hasProperty("name", is("com"))))); + + verify(securityHelper).anyPermitted(any(RepositoryViewPermission.class)); + // merging of results should be lazy: only the first member should have been consulted + verify(browseNodeEntityAdapter).getByPath(db, MEMBER_A, queryPath, 1, "", emptyMap()); + verifyNoMoreInteractions(browseNodeEntityAdapter, securityHelper, selectorManager); + } + + private static BrowseNode node(final String repositoryName, final String name) { + BrowseNode node = new BrowseNode(); + node.setRepositoryName(repositoryName); + node.setParentPath("/"); + node.setName(name); + return node; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BrowseNodeStoreLoadTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BrowseNodeStoreLoadTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3b9374ae29962788945e75cf12e85878d0ef6e5e --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BrowseNodeStoreLoadTest.java @@ -0,0 +1,161 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.goodies.testsupport.concurrent.ConcurrentRunner; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule; +import org.sonatype.nexus.repository.browse.BrowseNodeConfiguration; +import org.sonatype.nexus.security.SecurityHelper; +import org.sonatype.nexus.selector.CselAssetSqlBuilder; +import org.sonatype.nexus.selector.SelectorManager; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; + +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Lists.newArrayList; +import static java.util.Arrays.asList; +import static java.util.Collections.emptySet; +import static org.sonatype.goodies.common.Time.seconds; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; + +public class BrowseNodeStoreLoadTest + extends TestSupport +{ + private static final String REPOSITORY_NAME = "test-repo"; + + private static final int DELETE_PAGE_SIZE = 80; + + private static final int ASSET_COUNT = 50; + + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory("test"); + + @Mock + private SecurityHelper securityHelper; + + @Mock + private SelectorManager selectorManager; + + private Bucket bucket; + + private Component component; + + private List assets = new ArrayList<>(); + + private BrowseNodeStoreImpl underTest; + + @Before + public void setUp() throws Exception { + BrowseNodeConfiguration configuration = new BrowseNodeConfiguration(true, true, 1000, DELETE_PAGE_SIZE, 10_000, 10_000, seconds(0)); + + BucketEntityAdapter bucketEntityAdapter = new BucketEntityAdapter(); + ComponentFactory componentFactory = new ComponentFactory(emptySet()); + ComponentEntityAdapter componentEntityAdapter = new ComponentEntityAdapter(bucketEntityAdapter, componentFactory, + emptySet()); + AssetEntityAdapter assetEntityAdapter = new AssetEntityAdapter(bucketEntityAdapter, componentEntityAdapter); + + BrowseNodeEntityAdapter browseNodeEntityAdapter = new BrowseNodeEntityAdapter(componentEntityAdapter, assetEntityAdapter, configuration); + + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + bucketEntityAdapter.register(db); + bucket = new Bucket(); + bucket.setRepositoryName(REPOSITORY_NAME); + bucket.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + bucketEntityAdapter.addEntity(db, bucket); + + componentEntityAdapter.register(db); + component = new DefaultComponent() + .bucketId(EntityHelper.id(bucket)) + .format("test") + .group("test-group") + .name("test-component") + .version("1.0") + .attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + componentEntityAdapter.addEntity(db, component); + + assetEntityAdapter.register(db); + for (int i = 0; i < ASSET_COUNT; i++) { + assets.add(new Asset() + .bucketId(EntityHelper.id(bucket)) + .format("test") + .name("test-asset-" + i) + .attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>()))); + assetEntityAdapter.addEntity(db, assets.get(i)); + } + + bucketEntityAdapter.register(db); + } + + underTest = new BrowseNodeStoreImpl( + database.getInstanceProvider(), + browseNodeEntityAdapter, + securityHelper, + selectorManager, + new CselAssetSqlBuilder(), + configuration); + + underTest.start(); + } + + @After + public void tearDown() throws Exception { + underTest.stop(); + } + + @Test + public void exercisePathContentionBetweenAssets() throws Exception { + ConcurrentRunner runner = new ConcurrentRunner(1, 30); + + List componentPath = asList("some", "kind", "of", "path"); + + for (int i = 0; i < ASSET_COUNT; i++) { + int assetIndex = i; + runner.addTask(1, () -> { + underTest.createComponentNode(REPOSITORY_NAME, componentPath, component); + List assetPath = newArrayList(concat(componentPath, asList("to", "asset" + assetIndex))); + underTest.createAssetNode(REPOSITORY_NAME, assetPath, assets.get(assetIndex)); + }); + } + + runner.go(); + } + + @Test + public void exercisePathContentionBetweenAssetAndComponent() throws Exception { + ConcurrentRunner runner = new ConcurrentRunner(1, 30); + + List commonPath = asList("some", "kind", "of", "path"); + + runner.addTask(1, () -> { + underTest.createComponentNode(REPOSITORY_NAME, commonPath, component); + }); + + runner.addTask(1, () -> { + underTest.createAssetNode(REPOSITORY_NAME, commonPath, assets.get(0)); + }); + + runner.go(); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BucketDeleterTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BucketDeleterTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..a0a458463f06ef4724498a06665005b5b5705588 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/BucketDeleterTest.groovy @@ -0,0 +1,192 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.blobstore.api.BlobId +import org.sonatype.nexus.blobstore.api.BlobRef +import org.sonatype.nexus.blobstore.api.BlobStore +import org.sonatype.nexus.blobstore.api.BlobStoreManager +import org.sonatype.nexus.common.stateguard.InvalidStateException +import org.sonatype.nexus.orient.DatabaseInstance + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito + +import static org.mockito.Matchers.any +import static org.mockito.Mockito.doThrow +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.never +import static org.mockito.Mockito.times +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when +import static org.sonatype.nexus.repository.FacetSupport.State.STARTED +import static org.sonatype.nexus.repository.FacetSupport.State.STOPPED + +class BucketDeleterTest + extends TestSupport +{ + static final String BLOB_STORE_NAME = 'test-blob-store' + + @Mock + private DatabaseInstance databaseInstance + + @Mock + private BucketEntityAdapter bucketEntityAdapter + + @Mock + private ComponentEntityAdapter componentEntityAdapter + + @Mock + private AssetEntityAdapter assetEntityAdapter + + @Mock + private BlobStoreManager blobStoreManager + + @Mock + private BlobStore blobStore + + @Mock + private Bucket bucket + + @Mock + private ODatabaseDocumentTx db + + private BucketDeleter underTest + + @Before + void setUp() { + when(databaseInstance.acquire()).thenReturn(db) + underTest = new BucketDeleter({ databaseInstance }, bucketEntityAdapter, componentEntityAdapter, assetEntityAdapter, + blobStoreManager) + } + + @Test + void 'components and assets are deleted in batches'() { + List allBlobIds = [] + List allBlobRefs = [] + List allAssets = [] + + List componentsWithAssets = (1..201).collect { + BlobId blobId = mock(BlobId) + allBlobIds << blobId + + BlobRef blobRef = mockBlobRef(blobId) + allBlobRefs << blobRef + + Asset asset = mockAsset(blobRef) + allAssets << asset + + Component component = mock(Component) + when(assetEntityAdapter.browseByComponent(db, component)).thenReturn([asset]) + return component + } + + List assetsWithoutComponents = (1..201).collect { + BlobId blobId = mock(BlobId) + allBlobIds << blobId + + BlobRef blobRef = mockBlobRef(blobId) + allBlobRefs << blobRef + + Asset asset = mockAsset(blobRef) + allAssets << asset + + return asset + } + + when(blobStoreManager.get(BLOB_STORE_NAME)).thenReturn(blobStore) + when(componentEntityAdapter.browseByBucket(db, bucket)).thenReturn(componentsWithAssets) + when(assetEntityAdapter.browseByBucket(db, bucket)).thenReturn(assetsWithoutComponents) + + underTest.deleteBucket(bucket) + + componentsWithAssets.each { component -> verify(componentEntityAdapter).deleteEntity(db, component) } + allAssets.each { asset -> verify(assetEntityAdapter).deleteEntity(db, asset) } + allBlobIds.each { blobId -> verify(blobStore).delete(blobId, 'Deleting Bucket') } + verify(bucketEntityAdapter).deleteEntity(db, bucket) + verify(db, times(8)).commit() + } + + @Test + void 'blob deletion correctly ignores subsequent deletes from a blob store that no longer exists'() { + List blobIds = (1..50).collect { mock(BlobId) } + List blobRefs = blobIds.collect { blobId -> mockBlobRef(blobId) } + List assets = blobRefs.collect { blobRef -> mockAsset(blobRef) } + + when(blobStoreManager.get(BLOB_STORE_NAME)).thenReturn(null) + when(componentEntityAdapter.browseByBucket(db, bucket)).thenReturn([]) + when(assetEntityAdapter.browseByBucket(db, bucket)).thenReturn(assets) + + underTest.deleteBucket(bucket) + + assets.each { asset -> verify(assetEntityAdapter).deleteEntity(db, asset) } + verify(blobStore, never()).delete(any(BlobId), any(String)) + verify(bucketEntityAdapter).deleteEntity(db, bucket) + verify(db, times(4)).commit() + } + + @Test + void 'blob deletion correctly ignores subsequent deletes of a stopped blob store'() { + List blobIds = (1..50).collect { mock(BlobId) } + List blobRefs = blobIds.collect { blobId -> mockBlobRef(blobId) } + List assets = blobRefs.collect { blobRef -> mockAsset(blobRef) } + + when(blobStoreManager.get(BLOB_STORE_NAME)).thenReturn(blobStore) + doThrow(new InvalidStateException(STOPPED, STARTED)).when(blobStore).delete(any(BlobId), any(String)) + when(componentEntityAdapter.browseByBucket(db, bucket)).thenReturn([]) + when(assetEntityAdapter.browseByBucket(db, bucket)).thenReturn(assets) + + underTest.deleteBucket(bucket) + + assets.each { asset -> verify(assetEntityAdapter).deleteEntity(db, asset) } + verify(blobStore, times(1)).delete(any(BlobId), any(String)) + verify(bucketEntityAdapter).deleteEntity(db, bucket) + verify(db, times(4)).commit() + } + + @Test + void 'blob deletion will survive exceptions thrown by individual failed blob deletions'() { + List blobIds = (1..50).collect { mock(BlobId) } + List blobRefs = blobIds.collect { blobId -> mockBlobRef(blobId) } + List assets = blobRefs.collect { blobRef -> mockAsset(blobRef) } + + when(blobStoreManager.get(BLOB_STORE_NAME)).thenReturn(blobStore) + doThrow(new RuntimeException()).when(blobStore).delete(any(BlobId), any(String)) + when(componentEntityAdapter.browseByBucket(db, bucket)).thenReturn([]) + when(assetEntityAdapter.browseByBucket(db, bucket)).thenReturn(assets) + + underTest.deleteBucket(bucket) + + blobIds.each { blobId -> verify(blobStore).delete(blobId, 'Deleting Bucket') } + assets.each { asset -> verify(assetEntityAdapter).deleteEntity(db, asset) } + verify(bucketEntityAdapter).deleteEntity(db, bucket) + verify(db, times(4)).commit() + } + + private BlobRef mockBlobRef(final BlobId blobId) { + BlobRef blobRef = mock(BlobRef) + when(blobRef.getBlobId()).thenReturn(blobId) + when(blobRef.getStore()).thenReturn(BLOB_STORE_NAME) + return blobRef + } + + private Asset mockAsset(final BlobRef blobRef) { + Asset asset = mock(Asset) + when(asset.blobRef()).thenReturn(blobRef) + return asset + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/ComponentEntityAdapterTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/ComponentEntityAdapterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e0d964b5afec462196c5c6a097531edbd6b5d4f2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/ComponentEntityAdapterTest.java @@ -0,0 +1,192 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OSchema; +import com.orientechnologies.orient.core.record.impl.ODocument; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; + +import static java.util.Collections.emptySet; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; +import static org.sonatype.nexus.repository.storage.StorageTestUtil.createComponent; + +public class ComponentEntityAdapterTest + extends TestSupport +{ + private final static String GROUP = "group"; + + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory("test"); + + private ComponentEntityAdapter entityAdapter; + + private Bucket bucket; + + @Mock + private ComponentEntityAdapterExtension componentEntityAdapterExtension; + + @Before + public void setUp() { + BucketEntityAdapter bucketEntityAdapter = new BucketEntityAdapter(); + ComponentFactory componentFactory = new ComponentFactory(emptySet()); + entityAdapter = new ComponentEntityAdapter(bucketEntityAdapter, componentFactory, + ImmutableSet.of(componentEntityAdapterExtension)); + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + bucketEntityAdapter.register(db); + bucket = new Bucket(); + bucket.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + bucket.setRepositoryName("test-repo"); + bucketEntityAdapter.addEntity(db, bucket); + } + } + + @Test + public void testRegister() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + entityAdapter.register(db); + OSchema schema = db.getMetadata().getSchema(); + assertThat(schema.getClass(entityAdapter.getTypeName()), is(notNullValue())); + verify(componentEntityAdapterExtension).defineType(any(ODatabaseDocumentTx.class), any(OClass.class)); + } + } + + @Test + public void testBrowseByQuery_SpecialCharacters() { + String special = ",;.:-_#'+*~<>|!\"§$%&/()=?{\\}"; + String group = "g-" + special; + String name = "n-" + special; + String version = "v-" + special; + + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + entityAdapter.register(db); + + Component component = createComponent(bucket, group, name, version); + entityAdapter.addEntity(db, component); + verify(componentEntityAdapterExtension).writeFields(any(ODocument.class), any(Component.class)); + + Query query = Query.builder().where("group").eq(group).and("name").eq(name).and("version").eq(version).build(); + List components = Lists.newArrayList(entityAdapter.browseByQuery(db, query.getWhere(), + query.getParameters(), Collections.singleton(bucket), query.getQuerySuffix())); + + assertThat(components, hasSize(1)); + assertThat(components.get(0).group(), is(group)); + assertThat(components.get(0).name(), is(name)); + assertThat(components.get(0).version(), is(version)); + + verify(componentEntityAdapterExtension).readFields(any(ODocument.class), any(Component.class)); + } + } + + @Test + public void testBrowseByNameCaseInsensitive() { + String name = "CamelCase"; + String version = "version"; + + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + entityAdapter.register(db); + + Component component = createComponent(bucket, GROUP, name, version); + entityAdapter.addEntity(db, component); + verify(componentEntityAdapterExtension).writeFields(any(ODocument.class), any(Component.class)); + + List components1 = + Lists.newArrayList(entityAdapter.browseByNameCaseInsensitive(db, "camelcase", + Collections.singleton(bucket), null)); + + assertThat(components1, hasSize(1)); + assertThat(components1.get(0).name(), is(name)); + assertThat(components1.get(0).version(), is(version)); + + List components2 = + Lists.newArrayList(entityAdapter.browseByNameCaseInsensitive(db, "CAMELCASE", + Collections.singleton(bucket), null)); + + assertThat(components2, hasSize(1)); + assertThat(components2.get(0).name(), is(name)); + assertThat(components2.get(0).version(), is(version)); + + verify(componentEntityAdapterExtension, times(2)).readFields(any(ODocument.class), any(Component.class)); + } + } + + @Test + public void testBrowseByNameCaseInsensitiveWithLimit() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + entityAdapter.register(db); + + Component component1 = createComponent(bucket, GROUP, "name", "version1"); + entityAdapter.addEntity(db, component1); + + Component component2 = createComponent(bucket, GROUP, "name", "version2"); + entityAdapter.addEntity(db, component2); + + verify(componentEntityAdapterExtension, times(2)).writeFields(any(ODocument.class), any(Component.class)); + + List allComponents = + Lists.newArrayList(entityAdapter.browseByNameCaseInsensitive(db, "name", + Collections.singleton(bucket), "limit 2")); + + assertThat(allComponents, hasSize(2)); + verify(componentEntityAdapterExtension, times(2)).readFields(any(ODocument.class), any(Component.class)); + + List firstComponent = + Lists.newArrayList(entityAdapter.browseByNameCaseInsensitive(db, "name", + Collections.singleton(bucket), "limit 1")); + + assertThat(firstComponent, hasSize(1)); + verify(componentEntityAdapterExtension, times(3)).readFields(any(ODocument.class), any(Component.class)); + } + } + + @Test + public void testExists() { + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + entityAdapter.register(db); + + entityAdapter.addEntity(db, createComponent(bucket, "com.example", "testapp", "1.0")); + entityAdapter.addEntity(db, createComponent(bucket, null, "foo", "2.0")); + entityAdapter.addEntity(db, createComponent(bucket, null, "bar", null)); + + assertThat(entityAdapter.exists(db, "com.example", "testapp", "1.0", bucket), is(true)); + assertThat(entityAdapter.exists(db, null, "foo", "2.0", bucket), is(true)); + assertThat(entityAdapter.exists(db, null, "bar", null, bucket), is(true)); + + assertThat(entityAdapter.exists(db, "com.example", "foo", "1.0", bucket), is(false)); + assertThat(entityAdapter.exists(db, null, "testapp", "1.0", bucket), is(false)); + assertThat(entityAdapter.exists(db, "com.example", "bar", null, bucket), is(false)); + assertThat(entityAdapter.exists(db, null, "foo", null, bucket), is(false)); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/ComponentFactoryTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/ComponentFactoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ff71180782db0068a7e418b26084e356a41e4d1b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/ComponentFactoryTest.java @@ -0,0 +1,63 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import org.sonatype.goodies.testsupport.TestSupport; + +import com.google.common.collect.ImmutableSet; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ComponentFactoryTest + extends TestSupport +{ + @Mock + private ComponentDecorator componentDecorator; + + private ComponentFactory underTest; + + @Before + public void setup() { + underTest = new ComponentFactory(ImmutableSet.of(componentDecorator)); + + Component component = new DefaultComponent(); + when(componentDecorator.decorate(any(Component.class))).thenReturn(new TestComponent(component)); + } + + @Test + public void testComponent() { + Component component = underTest.createComponent(); + assertThat(component, notNullValue()); + assertThat(component, instanceOf(TestComponent.class)); + verify(componentDecorator).decorate(any(Component.class)); + TestComponent testComponent = (TestComponent) component; + assertThat(testComponent.getWrappedObject(), instanceOf(DefaultComponent.class)); + } + + private class TestComponent + extends DecoratedComponent + implements Component + { + TestComponent(final Component component) { + super(component); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/ContentSelectorUpgradeManagerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/ContentSelectorUpgradeManagerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3a2124e7287f90be475874924c34bbb825ae39a8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/ContentSelectorUpgradeManagerTest.java @@ -0,0 +1,98 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.selector.CselSelector; +import org.sonatype.nexus.selector.CselValidator; +import org.sonatype.nexus.selector.JexlSelector; +import org.sonatype.nexus.selector.SelectorConfiguration; +import org.sonatype.nexus.selector.SelectorManager; + +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import static java.util.Arrays.asList; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class ContentSelectorUpgradeManagerTest + extends TestSupport +{ + @InjectMocks + private ContentSelectorUpgradeManager manager; + + @Mock + private CselValidator cselValidator; + + @Mock + private SelectorManager selectorManager; + + @Test + public void doStartConvertsValidJexlToCsel() throws Exception { + SelectorConfiguration jexlSelector = createSelector("valid", JexlSelector.TYPE); + + when(selectorManager.browseJexl()).thenReturn(asList(jexlSelector)); + when(cselValidator.validate((String) jexlSelector.getAttributes().get("expression"))).thenReturn(true); + + manager.doStart(); + + verify(selectorManager).browseJexl(); + assertThat(jexlSelector.getType(), is(CselSelector.TYPE)); + verify(selectorManager).update(jexlSelector); + verifyNoMoreInteractions(selectorManager); + } + + @Test + public void doStartSkipsInvalidCsel() throws Exception { + SelectorConfiguration jexlSelector = createSelector("invalid", JexlSelector.TYPE); + + when(selectorManager.browseJexl()).thenReturn(asList(jexlSelector)); + when(cselValidator.validate((String) jexlSelector.getAttributes().get("expression"))).thenReturn(false); + + manager.doStart(); + + verify(selectorManager).browseJexl(); + assertThat(jexlSelector.getType(), is(JexlSelector.TYPE)); + verifyNoMoreInteractions(selectorManager); + } + + @Test + public void doStartDoesNothing() throws Exception { + when(selectorManager.browseJexl()).thenReturn(Collections.emptyList()); + + manager.doStart(); + + verify(selectorManager).browseJexl(); + verifyNoMoreInteractions(selectorManager); + } + + private SelectorConfiguration createSelector(String expression, String type) { + Map attributes = new HashMap<>(); + attributes.put("expression", expression); + + SelectorConfiguration config = new SelectorConfiguration(); + config.setType(type); + config.setAttributes(attributes); + + return config; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/DefaultContentValidatorTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/DefaultContentValidatorTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..5924f335239c691d35deb88fe7acab55635747d9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/DefaultContentValidatorTest.groovy @@ -0,0 +1,257 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.mime.MimeRulesSource +import org.sonatype.nexus.mime.internal.DefaultMimeSupport +import org.sonatype.nexus.repository.InvalidContentException +import org.sonatype.nexus.repository.view.ContentTypes + +import com.google.common.base.Supplier +import org.junit.Test + +import static org.hamcrest.CoreMatchers.equalTo +import static org.hamcrest.MatcherAssert.assertThat + +/** + * Tests for {@link DefaultContentValidator}. + */ +class DefaultContentValidatorTest + extends TestSupport +{ + private DefaultContentValidator testSubject = new DefaultContentValidator(new DefaultMimeSupport()) + + private byte[] emptyZip = [80, 75, 05, 06, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00] + + Supplier supplier(byte[] bytes) { + ByteArrayInputStream bis = new ByteArrayInputStream(bytes) + return new Supplier() { + @Override + InputStream get() { + return bis + } + } + } + + @Test + void 'simple text non-strict with declared'() { + def type = testSubject.determineContentType( + false, + supplier('simple text'.bytes), + MimeRulesSource.NOOP, + 'test.txt', + ContentTypes.TEXT_PLAIN + ) + assertThat(type, equalTo(ContentTypes.TEXT_PLAIN)) + } + + @Test + void 'simple text non-strict with undeclared'() { + def type = testSubject.determineContentType( + false, + supplier('simple text'.bytes), + MimeRulesSource.NOOP, + 'test.txt', + null + ) + assertThat(type, equalTo(ContentTypes.TEXT_PLAIN)) + } + + @Test + void 'simple text non-strict with wrong declared'() { + def type = testSubject.determineContentType( + false, + supplier('simple text'.bytes), + MimeRulesSource.NOOP, + 'test.txt', + 'application/zip' + ) + assertThat(type, equalTo(ContentTypes.TEXT_PLAIN)) + } + + @Test + void 'simple text strict with wrong declared'() { + def type = testSubject.determineContentType( + true, + supplier('simple text'.bytes), + MimeRulesSource.NOOP, + 'test.txt', + 'application/zip' + ) + assertThat(type, equalTo(ContentTypes.TEXT_PLAIN)) + } + + @Test + void 'simple zip non-strict with undeclared'() { + def type = testSubject.determineContentType( + false, + supplier(emptyZip), + MimeRulesSource.NOOP, + 'test.zip', + null + ) + assertThat(type, equalTo('application/zip')) + } + + @Test + void 'simple zip non-strict with declared'() { + def type = testSubject.determineContentType( + false, + supplier(emptyZip), + MimeRulesSource.NOOP, + 'test.zip', + 'application/zip' + ) + assertThat(type, equalTo('application/zip')) + } + + @Test + void 'simple zip strict with declared'() { + def type = testSubject.determineContentType( + true, + supplier(emptyZip), + MimeRulesSource.NOOP, + 'test.zip', + 'application/zip' + ) + assertThat(type, equalTo('application/zip')) + } + + @Test + void 'simple zip strict with wrong declared'() { + def type = testSubject.determineContentType( + true, + supplier(emptyZip), + MimeRulesSource.NOOP, + 'test.zip', + ContentTypes.TEXT_PLAIN + ) + assertThat(type, equalTo('application/zip')) + } + + @Test(expected = InvalidContentException) + void 'strict wrong zip content as text'() { + testSubject.determineContentType( + true, + supplier(emptyZip), + MimeRulesSource.NOOP, + 'test.txt', + ContentTypes.TEXT_PLAIN + ) + } + + @Test(expected = InvalidContentException) + void 'strict wrong text content as zip'() { + testSubject.determineContentType( + true, + supplier('simple text'.bytes), + MimeRulesSource.NOOP, + 'test.zip', + 'application/zip' + ) + } + + @Test + void 'non-strict wrong zip content as text'() { + def type = testSubject.determineContentType( + false, + supplier(emptyZip), + MimeRulesSource.NOOP, + 'test.txt', + ContentTypes.TEXT_PLAIN + ) + assertThat(type, equalTo(ContentTypes.TEXT_PLAIN)) + } + + @Test + void 'non-strict wrong text content as zip'() { + def type = testSubject.determineContentType( + false, + supplier('simple text'.bytes), + MimeRulesSource.NOOP, + 'test.zip', + 'application/zip' + ) + assertThat(type, equalTo('application/zip')) + } + + @Test(expected = InvalidContentException) + void 'strict wrong zip content as text undeclared'() { + testSubject.determineContentType( + true, + supplier(emptyZip), + MimeRulesSource.NOOP, + 'test.txt', + null + ) + } + + @Test(expected = InvalidContentException) + void 'strict wrong text content as zip undeclared'() { + testSubject.determineContentType( + true, + supplier('simple text'.bytes), + MimeRulesSource.NOOP, + 'test.zip', + null + ) + } + + @Test + void 'non-strict wrong zip content as text undeclared'() { + def type = testSubject.determineContentType( + false, + supplier(emptyZip), + MimeRulesSource.NOOP, + 'test.txt', + null + ) + assertThat(type, equalTo(ContentTypes.TEXT_PLAIN)) + } + + @Test + void 'non-strict wrong text content as zip undeclared'() { + def type = testSubject.determineContentType( + false, + supplier('simple text'.bytes), + MimeRulesSource.NOOP, + 'test.zip', + null + ) + assertThat(type, equalTo('application/zip')) + } + + @Test + void 'declared charset missing'() { + def type = testSubject.determineContentType( + true, + supplier('simple text'.bytes), + MimeRulesSource.NOOP, + 'test.txt', + ContentTypes.TEXT_PLAIN + '; charset=' + ) + assertThat(type, equalTo(ContentTypes.TEXT_PLAIN)) + } + + @Test(expected = InvalidContentException) + void 'completely invalid'() { + def type = testSubject.determineContentType( + true, + supplier('simple text'.bytes), + MimeRulesSource.NOOP, + 'test.txt', + '@#$*(#&%$*(%)k;lasj;klfjsdfas' + ) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/MetadataNodeEntityAdapterTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/MetadataNodeEntityAdapterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5d8075abe7ea2a7add5caf981b3c297ff7a8d1eb --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/MetadataNodeEntityAdapterTest.java @@ -0,0 +1,115 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.ArrayList; +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import com.orientechnologies.orient.core.id.ORID; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsEmptyIterable.emptyIterable; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MetadataNodeEntityAdapterTest + extends TestSupport +{ + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory("test"); + + List buckets; + + @Mock + BucketEntityAdapter bucketEntityAdapter; + + MetadataNodeEntityAdapter underTest; + + @Before + public void setup() throws Exception { + buckets = new ArrayList<>(); + buckets.add(makeBucket("orid")); + underTest = new TestableMetadataNodeEntityAdapter("type", bucketEntityAdapter); + } + + @Test + public void addBucketConstraints() throws Exception { + StringBuilder query = new StringBuilder(); + underTest.addBucketConstraints("where clause", buckets, query); + assertThat(query.toString(), is(equalTo(" and (bucket=orid)"))); + } + + @Test + public void addBucketConstraintsWithWhere() throws Exception { + StringBuilder query = new StringBuilder(); + underTest.addBucketConstraints(null, buckets, query); + assertThat(query.toString(), is(equalTo(" where (bucket=orid)"))); + } + + @Test + public void addBucketConstraintsWithWhereForMultipleBuckets() throws Exception { + buckets.add(makeBucket("orid2")); + StringBuilder query = new StringBuilder(); + underTest.addBucketConstraints(null, buckets, query); + assertThat(query.toString(), is(equalTo(" where (bucket=orid or bucket=orid2)"))); + } + + @Test + public void emptyBucketsBrowseByQuery() throws Exception { + buckets.clear(); + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + assertThat(underTest.browseByQuery(db, null, null, buckets, null), is(emptyIterable())); + } + } + + @Test + public void emptyBucketsCountByQuery() throws Exception { + buckets.clear(); + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + assertThat(underTest.countByQuery(db, null, null, buckets, null), is(0L)); + } + } + + private Bucket makeBucket(final String id) { + Bucket bucket = mock(Bucket.class); + ORID orid = mock(ORID.class); + when(bucketEntityAdapter.recordIdentity(bucket)).thenReturn(orid); + when(orid.toString()).thenReturn(id); + return bucket; + } + + private static class TestableMetadataNodeEntityAdapter + extends MetadataNodeEntityAdapter + { + public TestableMetadataNodeEntityAdapter(final String typeName, + final BucketEntityAdapter bucketEntityAdapter) + { + super(typeName, bucketEntityAdapter); + } + + @Override + protected Asset newEntity() { + return null; + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/OrientAsyncHelperTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/OrientAsyncHelperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9d76cbf88275ebb18d38e0eb7210c2ff5969db7b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/OrientAsyncHelperTest.java @@ -0,0 +1,98 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.NoSuchElementException; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.storage.OrientAsyncHelper.QueueConsumingIterable; +import org.sonatype.nexus.repository.storage.OrientAsyncHelper.QueueFeedingResultListener; + +import com.orientechnologies.orient.core.record.impl.ODocument; +import org.junit.Test; +import org.mockito.Mock; + +import static java.util.Arrays.asList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class OrientAsyncHelperTest + extends TestSupport +{ + @Mock + private ODocument result; + + private BlockingQueue newQueue(ODocument... elements) { + return new ArrayBlockingQueue<>(Math.max(1, elements.length), false, asList(elements)); + } + + @Test + public void testIteratorHasNext_NonEmptyQueue() { + QueueConsumingIterable underTest = new QueueConsumingIterable(1, newQueue(result)); + assertThat(underTest.hasNext(), is(true)); + } + + @Test + public void testIteratorHasNext_EmptyQueue() { + QueueConsumingIterable underTest = new QueueConsumingIterable(1, newQueue(OrientAsyncHelper.SENTINEL)); + assertThat(underTest.hasNext(), is(false)); + } + + @Test(expected = IllegalStateException.class) + public void testIteratorHasNext_Timeout() { + QueueConsumingIterable underTest = new QueueConsumingIterable(1, newQueue()); + underTest.hasNext(); + } + + @Test + public void testIteratorNext_NonEmptyQueue() { + QueueConsumingIterable underTest = new QueueConsumingIterable(1, newQueue(result)); + assertThat(underTest.next(), is(result)); + } + + @Test(expected = NoSuchElementException.class) + public void testIteratorNext_EmptyQueue() { + QueueConsumingIterable underTest = new QueueConsumingIterable(1, newQueue(OrientAsyncHelper.SENTINEL)); + underTest.next(); + } + + @Test(expected = IllegalStateException.class) + public void testIteratorNext_Timeout() { + QueueConsumingIterable underTest = new QueueConsumingIterable(1, newQueue()); + underTest.next(); + } + + @Test + public void testCommandResultListenerResult_NonFullQueue() { + BlockingQueue queue = newQueue(); + QueueFeedingResultListener underTest = new QueueFeedingResultListener(1, queue); + assertThat(underTest.result(result), is(true)); + assertThat(queue.poll(), is(result)); + } + + @Test + public void testCommandResultListenerResult_FullQueue() { + QueueFeedingResultListener underTest = new QueueFeedingResultListener(1, newQueue(result)); + assertThat(underTest.result(result), is(false)); + } + + @Test + public void testCommandResultListenerEnd() { + BlockingQueue queue = newQueue(); + QueueFeedingResultListener underTest = new QueueFeedingResultListener(1, queue); + underTest.end(); + assertThat(queue.poll(), is(OrientAsyncHelper.SENTINEL)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/QueryTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/QueryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..011d02989f67e1ff5fa26a71185356a235759139 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/QueryTest.java @@ -0,0 +1,86 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.storage.Query.Builder; + +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +public class QueryTest + extends TestSupport +{ + private Builder builder; + + @Before + public void setup() { + builder = Query.builder(); + } + + @Test + public void testHasWhere() { + assertThat(builder.hasWhere(), is(equalTo(false))); + + builder.where(" "); // this will get trimmed + assertThat(builder.hasWhere(), is(equalTo(false))); + + builder.where("placebo"); + assertThat(builder.hasWhere(), is(equalTo(true))); + } + + @Test + public void testAnonymousParameter() { + builder.where("x = ").param("placebo"); + + final Query query = builder.build(); + + assertThat(query.getWhere(), is(equalTo("x = :p0"))); + final Map parameters = query.getParameters(); + + assertThat(parameters.size(), is(equalTo(1))); + assertThat((String) parameters.get("p0"), is(equalTo("placebo"))); + } + + @Test(expected = IllegalStateException.class) + public void testEqMissingWhere() { + builder.eq("any"); + } + + @Test + public void testEq() { + Query query = builder.where("x").eq("placebo").build(); + assertThat(query.getWhere(), is("x = :p0")); + + final Map parameters = query.getParameters(); + assertThat(parameters.size(), is(equalTo(1))); + assertThat((String) parameters.get("p0"), is(equalTo("placebo"))); + } + + @Test(expected = IllegalStateException.class) + public void testAndMissingWhere() { + builder.and("any"); + } + + @Test + public void testAnd() { + assertThat(builder.where("x").and("y").build().getWhere(), is("x AND y")); + } +} + diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskManagerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskManagerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..82ff2158a7e449fba7a83cf4ca339252ac224abe --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskManagerTest.java @@ -0,0 +1,123 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Date; +import java.util.concurrent.Future; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.scheduling.TaskConfiguration; +import org.sonatype.nexus.scheduling.TaskInfo; +import org.sonatype.nexus.scheduling.TaskInfo.CurrentState; +import org.sonatype.nexus.scheduling.TaskInfo.RunState; +import org.sonatype.nexus.scheduling.TaskInfo.State; +import org.sonatype.nexus.scheduling.TaskScheduler; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static java.util.Collections.emptyList; +import static org.codehaus.groovy.runtime.InvokerHelper.asList; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class RebuildAssetUploadMetadataTaskManagerTest + extends TestSupport +{ + private RebuildAssetUploadMetadataTaskManager manager; + + @Mock + private TaskScheduler taskScheduler; + + @Before + public void injectMocks() { + manager = new RebuildAssetUploadMetadataTaskManager(taskScheduler); + } + + @Test + public void doStartWithNoOldRebuildTasks() throws Exception { + when(taskScheduler.listsTasks()).thenReturn(emptyList()); + + manager.doStart(); + + verify(taskScheduler, never()).scheduleTask(any(), any()); + } + + @Test + public void doStartRemovedOldTasks() throws Exception { + TaskInfo existingTask = createMockTaskInfo(RebuildAssetUploadMetadataTaskDescriptor.TYPE_ID); + + when(taskScheduler.listsTasks()).thenReturn(asList(existingTask)); + + manager.doStart(); + + verify(existingTask).remove(); + } + + private TaskConfiguration createTaskConfiguration() { + TaskConfiguration configuration = new TaskConfiguration(); + configuration.setMessage("message"); + return configuration; + } + + private TaskInfo createMockTaskInfo(String typeId) { + TaskConfiguration taskConfiguration = createTaskConfiguration(); + taskConfiguration.setTypeId(typeId); + + TaskInfo taskInfo = mock(TaskInfo.class); + when(taskInfo.getConfiguration()).thenReturn(taskConfiguration); + return taskInfo; + } + + private CurrentState createCurrentState(State state) { + CurrentState currentState = new CurrentState() + { + @Override + public State getState() { + return state; + } + + @Nullable + @Override + public Date getNextRun() { + return null; + } + + @Nullable + @Override + public Date getRunStarted() { + return null; + } + + @Nullable + @Override + public RunState getRunState() { + return null; + } + + @Nullable + @Override + public Future getFuture() { + return null; + } + }; + + return currentState; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskTest.java new file mode 100644 index 0000000000000000000000000000000000000000..59f54711a3292ca7e97664c9e21b85a454c8269c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/RebuildAssetUploadMetadataTaskTest.java @@ -0,0 +1,210 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobId; +import org.sonatype.nexus.blobstore.api.BlobMetrics; +import org.sonatype.nexus.blobstore.api.BlobRef; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.blobstore.api.BlobStoreManager; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule; +import org.sonatype.nexus.scheduling.TaskInterruptedException; + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; +import org.joda.time.DateTime; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; + +import static java.util.Collections.emptySet; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.blobstore.api.BlobStore.CREATED_BY_HEADER; +import static org.sonatype.nexus.blobstore.api.BlobStore.CREATED_BY_IP_HEADER; +import static org.sonatype.nexus.common.entity.EntityHelper.id; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; + +public class RebuildAssetUploadMetadataTaskTest + extends TestSupport +{ + private static final int PAGE_SIZE = 1; + private static final String REPOSITORY_NAME = "repositoryName"; + + private RebuildAssetUploadMetadataTask task; + + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inMemory("RebuildAssetUploadMetadataTaskTest"); + + private RebuildAssetUploadMetadataConfiguration configuration = new RebuildAssetUploadMetadataConfiguration(true, + PAGE_SIZE); + + private AssetStore assetStore; + + private AssetEntityAdapter assetEntityAdapter; + + private Bucket bucket; + + @Mock + private BlobStoreManager blobStoreManager; + + @Mock + private BlobStore blobStore; + + private int assets = 0; + + @Before + public void setUp() { + BucketEntityAdapter bucketEntityAdapter = new BucketEntityAdapter(); + ComponentFactory componentFactory = new ComponentFactory(emptySet()); + ComponentEntityAdapter componentEntityAdapter = new ComponentEntityAdapter(bucketEntityAdapter, componentFactory, + emptySet()); + assetEntityAdapter = new AssetEntityAdapter(bucketEntityAdapter, componentEntityAdapter); + + try (ODatabaseDocumentTx db = database.getInstance().connect()) { + bucketEntityAdapter.register(db); + componentEntityAdapter.register(db); + assetEntityAdapter.register(db); + + bucket = bucketEntityAdapter.newEntity(); + bucket.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + bucket.setRepositoryName(REPOSITORY_NAME); + bucketEntityAdapter.addEntity(db, bucket); + } + + assetStore = new AssetStoreImpl(database.getInstanceProvider(), assetEntityAdapter); + + task = new RebuildAssetUploadMetadataTask(assetStore, blobStoreManager, configuration); + when(blobStoreManager.get(any())).thenReturn(blobStore); + } + + @Test(expected = TaskInterruptedException.class) + public void executeCancelledTaskThrowsTaskInterruptedException() { + task.cancel(); + + Blob blob = createMockBlob("blobId"); + Asset asset = createAsset(blob); + assetStore.save(asset); + + task.execute(); + } + + @Test + public void executeUpdatesCandidateAssets() { + Blob blob = createMockBlob("blobId"); + Asset asset = createAsset(blob); + assetStore.save(asset); + + task.execute(); + + Asset updatedAsset = assetStore.getById(id(asset)); + assertThat(updatedAsset.name(), is(asset.name())); + assertThat(updatedAsset.createdBy(), is(blob.getHeaders().get(CREATED_BY_HEADER))); + assertThat(updatedAsset.createdByIp(), is(blob.getHeaders().get(CREATED_BY_IP_HEADER))); + assertThat(updatedAsset.blobCreated(), is(blob.getMetrics().getCreationTime())); + } + + @Test + public void executeSkipsAssetsWithANonEmptyCreatedBy() { + Blob blob = createMockBlob("blobId"); + Asset asset = createAsset(blob); + asset.createdBy("somebody"); + assetStore.save(asset); + + task.execute(); + + Asset updatedAsset = assetStore.getById(id(asset)); + assertThat(updatedAsset.name(), is(asset.name())); + assertThat(updatedAsset.createdBy(), is("somebody")); + assertThat(updatedAsset.createdByIp(), is(nullValue())); + } + + @Test + public void executeSkipsAssetsWithANoBlobRef() { + Asset assetWithNoBlobRef = createAsset(null); + assetWithNoBlobRef.createdBy("somebody"); + assetStore.save(assetWithNoBlobRef); + + task.execute(); + + Asset updatedAssetWithNoBlobRef = assetStore.getById(id(assetWithNoBlobRef)); + assertThat(updatedAssetWithNoBlobRef.name(), is(assetWithNoBlobRef.name())); + assertThat(updatedAssetWithNoBlobRef.createdBy(), is("somebody")); + assertThat(updatedAssetWithNoBlobRef.createdByIp(), is(nullValue())); + } + + @Test + public void executeSkipsFirstAssetWithAnEmptyCreatedBy() { + Blob blob = createMockBlob("blobId"); + when(blob.getHeaders()).thenReturn(Collections.singletonMap(CREATED_BY_IP_HEADER, "192.168.0.1")); + Asset asset = createAsset(blob); + asset.createdBy("alreadySet"); + assetStore.save(asset); + + Blob blob2 = createMockBlob("blobId2"); + when(blob2.getHeaders()).thenReturn(Collections.singletonMap(CREATED_BY_IP_HEADER, "192.168.0.2")); + Asset asset2 = createAsset(blob2); + asset2.createdBy(null); + assetStore.save(asset2); + + + task.execute(); + + assertThat(assetStore.getById(id(asset)).createdByIp(), is(nullValue())); + assertThat(assetStore.getById(id(asset2)).createdByIp(), is(nullValue())); + } + + private Asset createAsset(Blob blob) { + Asset asset = new Asset(); + asset.bucketId(id(bucket)); + asset.attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + if (blob != null) { + asset.blobRef(new BlobRef("node", "store", blob.getId().asUniqueString())); + } + asset.format("format"); + asset.name("asset" + (assets++)); + return asset; + } + + private Blob createMockBlob(String id) { + String createdBy = "createdBy"; + String createdByIp = "createdByIp"; + DateTime creationTime = new DateTime(); + Blob blob = mock(Blob.class); + + Map headers = new HashMap<>(); + headers.put(CREATED_BY_HEADER, createdBy); + headers.put(CREATED_BY_IP_HEADER, createdByIp); + when(blob.getHeaders()).thenReturn(headers); + + BlobMetrics metrics = new BlobMetrics(creationTime, "hash", 1L); + when(blob.getMetrics()).thenReturn(metrics); + + BlobId blobId = new BlobId(id); + when(blob.getId()).thenReturn(blobId); + when(blobStore.get(blobId)).thenReturn(blob); + + return blob; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/StorageFacetImplIT.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/StorageFacetImplIT.java new file mode 100644 index 0000000000000000000000000000000000000000..01859deb653d0657cc4c8fbf05d4196c92049ab1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/StorageFacetImplIT.java @@ -0,0 +1,875 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.blobstore.api.BlobStoreManager; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.EntityHelper; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.common.entity.EntityVersion; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.mime.internal.DefaultMimeSupport; +import org.sonatype.nexus.orient.HexRecordIdObfuscator; +import org.sonatype.nexus.orient.entity.AttachedEntityId; +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.attributes.internal.AttributesFacetImpl; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.config.ConfigurationFacet; +import org.sonatype.nexus.repository.search.SearchFacet; +import org.sonatype.nexus.repository.storage.internal.ComponentSchemaRegistration; +import org.sonatype.nexus.security.ClientInfoProvider; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.orientechnologies.orient.core.exception.OConcurrentModificationException; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.sql.OCommandSQL; +import com.orientechnologies.orient.core.storage.ORecordDuplicatedException; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Collections.emptySet; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.common.entity.EntityHelper.id; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_NAME; + +/** + * Integration tests for {@link StorageFacetImpl}. + */ +public class StorageFacetImplIT + extends TestSupport +{ + @Rule + public DatabaseInstanceRule database = DatabaseInstanceRule.inFilesystem("test"); + + protected StorageFacetImpl underTest; + + protected Repository testRepository1 = mock(Repository.class); + + protected Repository testRepository2 = mock(Repository.class); + + protected TestFormat testFormat = new TestFormat(); + + private ComponentSchemaRegistration schemaRegistration; + + private AssetEntityAdapter assetEntityAdapter; + + private class TestFormat + extends Format + { + public TestFormat() { + super("test"); + } + } + + @Before + public void setUp() throws Exception { + BucketEntityAdapter bucketEntityAdapter = new BucketEntityAdapter(); + ComponentFactory componentFactory = new ComponentFactory(emptySet()); + HexRecordIdObfuscator recordIdObfuscator = new HexRecordIdObfuscator(); + bucketEntityAdapter.enableObfuscation(recordIdObfuscator); + ComponentEntityAdapter componentEntityAdapter = new ComponentEntityAdapter(bucketEntityAdapter, componentFactory, + emptySet()); + componentEntityAdapter.enableObfuscation(recordIdObfuscator); + assetEntityAdapter = new AssetEntityAdapter(bucketEntityAdapter, componentEntityAdapter); + assetEntityAdapter.enableObfuscation(recordIdObfuscator); + + schemaRegistration = new ComponentSchemaRegistration( + database.getInstanceProvider(), + bucketEntityAdapter, + componentEntityAdapter, + assetEntityAdapter); + + schemaRegistration.start(); + + StorageFacetImpl.Config config = new StorageFacetImpl.Config(); + ConfigurationFacet configurationFacet = mock(ConfigurationFacet.class); + when(configurationFacet.readSection( + any(Configuration.class), + eq(StorageFacetImpl.CONFIG_KEY), + eq(StorageFacetImpl.Config.class))) + .thenReturn(config); + + when(testRepository1.getName()).thenReturn("test-repository-1"); + when(testRepository1.getFormat()).thenReturn(testFormat); + when(testRepository1.facet(ConfigurationFacet.class)).thenReturn(configurationFacet); + when(testRepository1.facet(SearchFacet.class)).thenReturn(mock(SearchFacet.class)); + + when(testRepository2.getName()).thenReturn("test-repository-2"); + when(testRepository2.getFormat()).thenReturn(testFormat); + when(testRepository2.facet(ConfigurationFacet.class)).thenReturn(configurationFacet); + when(testRepository2.facet(SearchFacet.class)).thenReturn(mock(SearchFacet.class)); + + underTest = storageFacetImpl("testNodeId", bucketEntityAdapter, componentEntityAdapter, assetEntityAdapter, testRepository1); + } + + @After + public void tearDown() throws Exception { + underTest.stop(); + + schemaRegistration.stop(); + } + + @Test + public void initialState() { + try (StorageTx tx = beginTX()) { + // We should have one bucket, which was auto-created for the repository during initialization + checkSize(tx.browseBuckets(), 1); + } + } + + @Test + public void startWithEmptyAttributes() { + try (StorageTx tx = beginTX()) { + Asset asset = tx.createAsset(tx.findBucket(testRepository1), testFormat); + Component component = tx.createComponent(tx.findBucket(testRepository1), testFormat); + + NestedAttributesMap assetAttributes = asset.attributes(); + assertThat(assetAttributes, is(notNullValue())); + assertThat(assetAttributes.isEmpty(), is(true)); + + NestedAttributesMap componentAttributes = component.attributes(); + assertThat(componentAttributes, is(notNullValue())); + assertThat(componentAttributes.isEmpty(), is(true)); + } + } + + @Test + public void getAndSetAttributes() { + EntityId docId; + try (StorageTx tx = beginTX()) { + Asset asset = tx.createAsset(tx.findBucket(testRepository1), testFormat); + asset.name("asset"); + NestedAttributesMap map = asset.attributes(); + + assertThat(map.isEmpty(), is(true)); + + map.child("bag1").set("foo", "bar"); + map.child("bag2").set("baz", "qux"); + + assertThat(map.isEmpty(), is(false)); + + tx.saveAsset(asset); + + tx.commit(); + docId = EntityHelper.id(asset); + } + + try (StorageTx tx = beginTX()) { + NestedAttributesMap map = tx.findAsset(docId, tx.findBucket(testRepository1)).attributes(); + + assertThat(map.size(), is(2)); + assertThat(map.child("bag1").size(), is(1)); + assertThat((String) map.child("bag1").get("foo"), is("bar")); + assertThat(map.child("bag2").size(), is(1)); + assertThat((String) map.child("bag2").get("baz"), is("qux")); + } + } + + @Test + public void findAssets() throws Exception { + // Setup: add an asset in both repositories + try (StorageTx tx = beginTX()) { + Asset asset1 = tx.createAsset(tx.findBucket(testRepository1), testFormat); + asset1.name("asset1"); + asset1.size(42L); + tx.saveAsset(asset1); + tx.commit(); + } + + underTest.attach(testRepository2); + underTest.init(); + try (StorageTx tx = beginTX()) { + Asset asset2 = tx.createAsset(tx.findBucket(testRepository2), testFormat); + asset2.name("asset2"); + asset2.size(42L); + tx.saveAsset(asset2); + tx.commit(); + } + + // Queries + try (StorageTx tx = beginTX()) { + + // Find assets with name = "asset1" + + // ..in testRepository1, should yield 1 match + checkSize(tx.findAssets("name = :name", ImmutableMap.of("name", (Object) "asset1"), + ImmutableSet.of(testRepository1), null), 1); + assertThat(tx.countAssets("name = :name", ImmutableMap.of("name", (Object) "asset1"), + ImmutableSet.of(testRepository1), null), is(1L)); + // ...in testRepository2, should yield 0 matches + checkSize(tx.findAssets("name = :name", ImmutableMap.of("name", (Object) "asset1"), + ImmutableSet.of(testRepository2), null), 0); + assertThat(tx.countAssets("name = :name", ImmutableMap.of("name", (Object) "asset1"), + ImmutableSet.of(testRepository2), null), is(0L)); + // ..in testRepository1 or testRepository2, should yeild 1 match + checkSize(tx.findAssets("name = :name", ImmutableMap.of("name", (Object) "asset1"), + ImmutableSet.of(testRepository1, testRepository2), null), 1); + assertThat(tx.countAssets("name = :name", ImmutableMap.of("name", (Object) "asset1"), + ImmutableSet.of(testRepository1, testRepository2), null), is(1L)); + // ..in any repository should yeild 2 matches + checkSize(tx.findAssets("name = :name", ImmutableMap.of("name", (Object) "asset1"), null, null), 1); + assertThat(tx.countAssets("name = :name", ImmutableMap.of("name", (Object) "asset1"), null, null), is(1L)); + + // Find assets with number = 42 + + // ..in testRepository1, should yield 1 match + checkSize(tx.findAssets("size = :number", ImmutableMap.of("number", (Object) 42), + ImmutableSet.of(testRepository1), null), 1); + assertThat(tx.countAssets("size = :number", ImmutableMap.of("number", (Object) 42), + ImmutableSet.of(testRepository1), null), is(1L)); + // ..in testRepository2, should yield 1 match + checkSize(tx.findAssets("size = :number", ImmutableMap.of("number", (Object) 42), + ImmutableSet.of(testRepository2), null), 1); + assertThat(tx.countAssets("size = :number", ImmutableMap.of("number", (Object) 42), + ImmutableSet.of(testRepository2), null), is(1L)); + // ..in testRepository1 or testRepository2, should yield 2 matches + checkSize(tx.findAssets("size = :number", ImmutableMap.of("number", (Object) 42), + ImmutableSet.of(testRepository1, testRepository2), null), 2); + assertThat(tx.countAssets("size = :number", ImmutableMap.of("number", (Object) 42), + ImmutableSet.of(testRepository1, testRepository2), null), is(2L)); + // ..in any repository, should yield 2 matches + checkSize(tx.findAssets("size = :number", ImmutableMap.of("number", (Object) 42), + ImmutableSet.of(testRepository1, testRepository2), null), 2); + assertThat(tx.countAssets("size = :number", ImmutableMap.of("number", (Object) 42), + ImmutableSet.of(testRepository1, testRepository2), null), is(2L)); + + // Find assets in any repository with name = "foo" or number = 42 + String whereClause = "name = :name or size = :number"; + Map parameters = ImmutableMap.of("name", (Object) "foo", "number", 42); + + // ..in ascending order by name with limit 1, should return asset1 + String suffix = "order by name limit 1"; + List results = Lists.newArrayList(tx.findAssets(whereClause, parameters, null, suffix)); + checkSize(results, 1); + assertThat((String) results.get(0).name(), is("asset1")); + + // ..in descending order by name with limit 1, should return asset2 + suffix = "order by name desc limit 1"; + results = Lists.newArrayList(tx.findAssets(whereClause, parameters, null, suffix)); + checkSize(results, 1); + assertThat((String) results.get(0).name(), is("asset2")); + } + } + + @Test + public void mapOfMaps() { + Map bag2 = ImmutableMap.of(); + + // Transaction 1: + // Create a new asset with property "attributes" that's a map of maps (stored as an embeddedmap) + EntityId docId; + try (StorageTx tx = beginTX()) { + Bucket bucket = tx.findBucket(testRepository1); + Asset asset = tx.createAsset(bucket, testFormat); + asset.name("asset"); + asset.attributes().child("bag1").set("foo", "bar"); + asset.attributes().child("bag2").set("baz", "qux"); + tx.saveAsset(asset); + tx.commit(); + docId = EntityHelper.id(asset); + } + + // Transaction 2: + // Get the asset and make sure it contains what we expect + try (StorageTx tx = beginTX()) { + Bucket bucket = tx.findBucket(testRepository1); + Asset asset = tx.findAsset(docId, bucket); + assert asset != null; + + NestedAttributesMap outputMap = asset.attributes(); + + assertThat(outputMap.size(), is(2)); + + Map outputBag1 = (Map) outputMap.get("bag1"); + assertNotNull(outputBag1); + assertThat(outputBag1.keySet().size(), is(1)); + assertThat(outputBag1.get("foo"), is("bar")); + + Map outputBag2 = (Map) outputMap.get("bag2"); + assertNotNull(outputBag2); + assertThat(outputBag2.keySet().size(), is(1)); + assertThat(outputBag2.get("baz"), is("qux")); + } + + // Transaction 3: + // Make sure we can use dot notation to query for the asset by some aspect of the attributes + try (StorageTx tx = beginTX()) { + Map parameters = ImmutableMap.of("fooValue", "bar"); + String query = String.format("select from %s where attributes.bag1.foo = :fooValue", assetEntityAdapter.getTypeName()); + + Iterable docs = tx.getDb().command(new OCommandSQL(query)).execute(parameters); + List list = Lists.newArrayList(docs); + + assertThat(list.size(), is(1)); + assertThat(new AttachedEntityId(assetEntityAdapter, list.get(0).getIdentity()), is(docId)); + } + } + + @Test + public void roundTripTest() { + EntityId asset1Id = null; + EntityId asset2Id = null; + EntityId componentId = null; + + try (StorageTx tx = beginTX()) { + // Verify initial state with browse + Bucket bucket = tx.findBucket(testRepository1); + + checkSize(tx.browseBuckets(), 1); + checkSize(tx.browseAssets(bucket), 0); + checkSize(tx.browseComponents(bucket), 0); + + // Create an asset and component and verify state with browse and find + Asset asset1 = tx.createAsset(bucket, testFormat); + asset1.name("foo"); + tx.saveAsset(asset1); + + Component component = tx.createComponent(bucket, testFormat); + component.name("bar"); + tx.saveComponent(component); + + Asset asset2 = tx.createAsset(bucket, component); + asset2.name("asset2"); + tx.saveAsset(asset2); + + tx.commit(); + + // In transaction mode, ORIDs are placeholders until commit, so IDs should be collected after commit + asset1Id = id(asset1); + asset2Id = id(asset2); + componentId = id(component); + } + + try (StorageTx tx = beginTX()) { + Bucket bucket = tx.findBucket(testRepository1); + + checkSize(tx.browseAssets(bucket), 2); + checkSize(tx.browseComponents(bucket), 1); + + assertNotNull(tx.findAsset(asset1Id, bucket)); + assertNotNull(tx.findComponentInBucket(componentId, bucket)); + + checkSize(tx.browseAssets(tx.findComponentInBucket(componentId, bucket)), 1); + assertNotNull(tx.firstAsset(tx.findComponentInBucket(componentId, bucket))); + assertNull(tx.findAsset(asset1Id, bucket).componentId()); + assertNotNull(tx.findAsset(asset2Id, bucket).componentId()); + + assertNull(tx.findAssetWithProperty(P_NAME, "nomatch", bucket)); + assertNotNull(tx.findAssetWithProperty(P_NAME, "foo", bucket)); + + assertNull(tx.findComponentWithProperty(P_NAME, "nomatch", bucket)); + assertNotNull(tx.findComponentWithProperty(P_NAME, "bar", bucket)); + + // Delete both and make sure browse and find behave as expected + tx.deleteAsset(tx.findAsset(asset1Id, bucket)); + tx.deleteComponent(tx.findComponentInBucket(componentId, bucket)); + + tx.commit(); + tx.begin(); + + checkSize(tx.browseAssets(bucket), 0); + checkSize(tx.browseComponents(bucket), 0); + assertNull(tx.findAsset(asset1Id, bucket)); + assertNull(tx.findComponentInBucket(componentId, bucket)); + + // NOTE: It doesn't matter for this test, but you should commit when finished with one or more writes + // If you don't, your changes will be automatically rolled back. + tx.commit(); + } + } + + @Test + public void componentAssetLinksAreDurable() { + try (StorageTx tx = beginTX()) { + Bucket bucket = tx.findBucket(testRepository1); + final Component component = tx.createComponent(bucket, testFormat).name("component"); + tx.saveComponent(component); + + final Asset asset = tx.createAsset(bucket, component).name("asset"); + tx.saveAsset(asset); + + tx.commit(); + } + + try (StorageTx tx = beginTX()) { + final Asset asset = tx.findAssetWithProperty("name", "asset", tx.findBucket(testRepository1)); + assertThat(asset, is(notNullValue())); + + final Component component = tx.findComponentInBucket(asset.componentId(), tx.findBucket(testRepository1)); + assertThat(component, is(notNullValue())); + assertThat(component.name(), is("component")); + + final Asset tmpAsset = tx.findAssetWithProperty("name", "asset", component); + assertThat(tmpAsset.toString(), is(equalTo(asset.toString()))); + } + } + + @Test + public void concurrentTransactionWithoutConflictTest() throws Exception { + doConcurrentTransactionTest(false); + } + + @Test + public void concurrentTransactionWithConflictTest() throws Exception { + doConcurrentTransactionTest(true); + } + + private void doConcurrentTransactionTest(boolean simulateConflict) throws Exception { + // setup: + // main thread: create a new asset and commit it. + // test: + // main thread: start new transaction, and if simulating a conflict, read the asset + // aux thread: start new transaction, modify asset, and commit + // main thread: if not simulating a conflict, read the asset. then modify the asset, then commit it + // expectation: + // if simulating a conflict: commit on main thread fails with OConcurrentModificationException + // if not simulating a conflict: modification made in main thread is persisted after the modification on aux + + // setup + final EntityId assetId; + EntityVersion firstVersion; + try (StorageTx tx = beginTX()) { + Bucket bucket = tx.findBucket(testRepository1); + Asset asset = tx.createAsset(bucket, testFormat); + asset.name("asset"); + tx.saveAsset(asset); + assetId = EntityHelper.id(asset); + tx.commit(); + firstVersion = EntityHelper.version(asset); + } + + // test + // 1. start a tx (mainTx) in the main thread + try (StorageTx mainTx = beginTX()) { + Bucket bucket = mainTx.findBucket(testRepository1); + Asset asset = null; + + if (simulateConflict) { + // cause a conflict to occur later by reading the asset before the other tx starts + // (this causes the MVCC version comparison at commit-time to fail) + asset = checkNotNull(mainTx.findAsset(assetId, bucket)); + } + + // 2. modify and commit the asset in a separate tx (auxTx) in another thread + Thread auxThread = new Thread() + { + @Override + public void run() { + try (StorageTx auxTx = beginTX()) { + Bucket bucket = auxTx.findBucket(testRepository1); + Asset asset = checkNotNull(auxTx.findAsset(assetId, bucket)); + asset.name("firstValue"); + auxTx.saveAsset(asset); + auxTx.commit(); + } + } + }; + auxThread.start(); + auxThread.join(); + + // 3. modify and commit the asset in mainTx, in the main thread + if (!simulateConflict) { + // only read the asset we propose to change *after* the other transaction completes + asset = checkNotNull(mainTx.findAsset(assetId, bucket)); + } + asset.name("secondValue"); + mainTx.saveAsset(asset); + mainTx.commit(); // if we're simulating a conflict, this call should throw OConcurrentModificationException + assertThat(simulateConflict, is(false)); + } + catch (OConcurrentModificationException e) { + assertThat(simulateConflict, is(true)); + return; + } + + // not simulating a conflict; verify the expected state + try (StorageTx tx = beginTX()) { + Bucket bucket = tx.findBucket(testRepository1); + Asset asset = checkNotNull(tx.findAsset(assetId, bucket)); + + String name = asset.name(); + EntityVersion finalVersion = EntityHelper.version(asset); + + assertThat(name, is("secondValue")); + assertThat(Integer.valueOf(finalVersion.getValue()), greaterThan(Integer.valueOf(firstVersion.getValue()))); + } + } + + @Test + public void noDuplicateComponent() throws Exception { + createComponent(null, "name", null); + createComponent("group", "name", null); + createComponent(null, "name", "1"); + createComponent("group", "name", "1"); + } + + @Test(expected = ORecordDuplicatedException.class) + public void duplicateComponentName() throws Exception { + createComponent(null, "name", null); + createComponent(null, "name", null); + } + + @Test(expected = ORecordDuplicatedException.class) + public void duplicateComponentGroupName() throws Exception { + createComponent("group", "name", null); + createComponent("group", "name", null); + } + + @Test(expected = ORecordDuplicatedException.class) + public void duplicateComponentNameVersion() throws Exception { + createComponent(null, "name", "1"); + createComponent(null, "name", "1"); + } + + @Test(expected = ORecordDuplicatedException.class) + public void duplicateComponentGroupNameVersion() throws Exception { + createComponent("group", "name", "1"); + createComponent("group", "name", "1"); + } + + private Component createComponent(final String group, final String name, final String version) throws Exception { + try (StorageTx tx = beginTX()) { + Bucket bucket = tx.findBucket(testRepository1); + Component component = tx.createComponent(bucket, testFormat) + .group(group) + .name(name) + .version(version); + tx.saveComponent(component); + tx.commit(); + return component; + } + } + + private Asset createAsset(final Component component, final String name) throws Exception { + try (StorageTx tx = beginTX()) { + Bucket bucket = tx.findBucket(testRepository1); + Asset asset; + if (component != null) { + asset = tx.createAsset(bucket, component); + } + else { + asset = tx.createAsset(bucket, testFormat); + } + asset.name(name); + tx.saveAsset(asset); + tx.commit(); + return asset; + } + } + + @Test + public void noDuplicateAsset() throws Exception { + Component component = createComponent("group", "name", "1"); + createAsset(component, "name"); + createAsset(null, "name"); + } + + @Test(expected = ORecordDuplicatedException.class) + public void duplicateAssetComponentName() throws Exception { + Component component = createComponent("group", "name", "1"); + createAsset(component, "name"); + createAsset(component, "name"); + } + + @Test(expected = ORecordDuplicatedException.class) + public void duplicateAssetName() throws Exception { + createAsset(null, "name"); + createAsset(null, "name"); + } + + private void checkSize(Iterable iterable, int expectedSize) { + assertThat(Iterators.size(iterable.iterator()), is(expectedSize)); + } + + @Test + public void repeatedAssetModificationsAreSaved() throws Exception { + createComponent("testGroup", "testName", "testVersion"); + try (StorageTx tx = beginTX()) { + final Component component = tx.findComponentWithProperty("version", "testVersion", tx.findBucket(testRepository1)); + final Asset asset = tx.createAsset(tx.findBucket(testRepository1), component).name("asset"); + + final NestedAttributesMap attributes = asset.formatAttributes(); + attributes.set("attribute1", "original"); + tx.saveAsset(asset); + + final Iterable assets = tx.browseAssets(component); + final Asset reloadedAsset = assets.iterator().next(); + + reloadedAsset.formatAttributes().set("attribute2", "alternate"); + tx.saveAsset(reloadedAsset); + + tx.commit(); + } + + try (StorageTx tx = beginTX()) { + final Component component = tx.findComponentWithProperty("version", "testVersion", tx.findBucket(testRepository1)); + final Iterable assets = tx.browseAssets(component); + final Asset asset = assets.iterator().next(); + + assertThat(asset.formatAttributes().get("attribute1", String.class), equalTo("original")); + assertThat(asset.formatAttributes().get("attribute2", String.class), equalTo("alternate")); + } + } + + @Test + public void transactionsRollBackWhenRequired() throws Exception { + try (StorageTx tx = beginTX()) { + final Component component = tx.createComponent(tx.findBucket(testRepository1), testFormat) + .group("myGroup") + .version("0.9") + .name("myComponent"); + tx.saveComponent(component); + tx.rollback(); + } + + try (StorageTx tx = beginTX()) { + final Component component = tx.findComponentWithProperty("group", "myGroup", tx.findBucket(testRepository1)); + assertThat(component, is(nullValue())); + } + } + + @Test + public void transactionsRollBackOnException() throws Exception { + try { + try (StorageTx tx = beginTX()) { + final Component component = tx.createComponent(tx.findBucket(testRepository1), testFormat) + .group("myGroup") + .version("0.9") + .name("myComponent"); + tx.saveComponent(component); + throw new IllegalStateException(); + } + } + catch (IllegalStateException ignored) { + } + + try (StorageTx tx = beginTX()) { + final Component component = tx.findComponentWithProperty("group", "myGroup", tx.findBucket(testRepository1)); + assertThat(component, is(nullValue())); + } + } + + @Test + public void transactionContentIsSaved() throws Exception { + try (StorageTx tx = beginTX()) { + final Component component = tx.createComponent(tx.findBucket(testRepository1), testFormat) + .group("myGroup") + .version("0.9") + .name("myComponent"); + tx.saveComponent(component); + tx.commit(); + } + + try (StorageTx tx = beginTX()) { + final Iterable components = tx.browseComponents(tx.findBucket(testRepository1)); + final Component component = tx.findComponentWithProperty("group", "myGroup", tx.findBucket(testRepository1)); + assertThat(component, is(notNullValue())); + assertThat(component.group(), is("myGroup")); + } + } + + @Test + public void entityIdCanBeUsedInLaterTransactions() throws Exception { + EntityId componentId; + + try (StorageTx tx = beginTX()) { + final Component component = tx.createComponent(tx.findBucket(testRepository1), testFormat).name("component"); + tx.saveComponent(component); + + componentId = id(component); + + tx.commit(); + } + + try (StorageTx tx = beginTX()) { + final Component component = tx.findComponentInBucket(componentId, tx.findBucket(testRepository1)); + assertThat("component", component, is(notNullValue())); + assertThat(component.name(), is("component")); + } + } + + @Test + public void entityIdCanBeReferencedBeforeCommit() throws Exception { + EntityId componentId; + EntityId assetId; + + try (StorageTx tx = beginTX()) { + final Component component = tx.createComponent(tx.findBucket(testRepository1), testFormat).name("component"); + tx.saveComponent(component); + + // Implicitly reference the component's entity id + final Asset asset = tx.createAsset(tx.findBucket(testRepository1), component).name("hello"); + tx.saveAsset(asset); + + tx.commit(); + componentId = id(component); + assetId = id(asset); + } + + try (StorageTx tx = beginTX()) { + final Component component = tx.findComponentInBucket(componentId, tx.findBucket(testRepository1)); + assertThat("component", component, is(notNullValue())); + assertThat(component.name(), is("component")); + + final Asset asset = tx.findAsset(assetId, tx.findBucket(testRepository1)); + assertThat("asset", asset, is(notNullValue())); + assertThat(asset.name(), is("hello")); + } + } + + @Test + public void dependentQueryFromUncommittedComponentDoesNotThrowException() throws Exception { + try (StorageTx tx = beginTX()) { + final Component component = tx.createComponent(tx.findBucket(testRepository1), testFormat).name("component"); + tx.saveComponent(component); + + // Correct use of attached entity ids prevent an exception being thrown by this line + tx.browseAssets(component); + } + } + + @Test + public void assetLastDownloaded() throws Exception { + final String ASSET_NAME = "assetLastDownloaded"; + try (StorageTx tx = beginTX()) { + Bucket bucket = tx.findBucket(testRepository1); + Asset asset = tx.createAsset(bucket, testFormat).name(ASSET_NAME); + tx.saveAsset(asset); + tx.commit(); + } + + try (StorageTx tx = beginTX()) { + Bucket bucket = tx.findBucket(testRepository1); + Asset asset = tx.findAssetWithProperty(P_NAME, ASSET_NAME, bucket); + assertThat(asset, notNullValue()); + assertThat(asset.lastDownloaded(), nullValue()); + assertThat(asset.markAsDownloaded(), is(true)); + tx.saveAsset(asset); + tx.commit(); + } + + try (StorageTx tx = beginTX()) { + Bucket bucket = tx.findBucket(testRepository1); + Asset asset = tx.findAssetWithProperty(P_NAME, ASSET_NAME, bucket); + assertThat(asset, notNullValue()); + assertThat(asset.lastDownloaded(), notNullValue()); + assertThat(asset.markAsDownloaded(), is(false)); + } + } + + @Test + public void bucketAttributesConsistentOnConcurrentAccess() throws Exception { + // in an HA environment another node's StorageFacetImpl can make + // changes to the underlying database + BucketEntityAdapter otherBucketEntityAdapter = new BucketEntityAdapter(); + ComponentFactory componentFactory = new ComponentFactory(emptySet()); + ComponentEntityAdapter otherComponentEntityAdapter = new ComponentEntityAdapter(otherBucketEntityAdapter, + componentFactory, emptySet()); + AssetEntityAdapter otherAssetEntityAdapter = + new AssetEntityAdapter(otherBucketEntityAdapter, otherComponentEntityAdapter); + StorageFacetImpl otherNodeStorageFacetImpl = storageFacetImpl("otherNodeId", + otherBucketEntityAdapter, otherComponentEntityAdapter, otherAssetEntityAdapter, testRepository1); + + AttributesFacetImpl attributesFacet = attributesFacetImpl(testRepository1); + + // access attributes through underTest + when(testRepository1.facet(StorageFacet.class)).thenReturn(underTest); + attributesFacet.modifyAttributes(attributes -> attributes.set("foo", "original")); + assertThat(attributesFacet.getAttributes().require("foo", String.class), equalTo("original")); + + // update attributes through otherNodeStorageFacetImpl + when(testRepository1.facet(StorageFacet.class)).thenReturn(otherNodeStorageFacetImpl); + attributesFacet.modifyAttributes(attributes -> attributes.set("foo", "updated")); + + // retrieve updated attributes through underTest + when(testRepository1.facet(StorageFacet.class)).thenReturn(underTest); + assertThat(attributesFacet.getAttributes().require("foo", String.class), equalTo("updated")); + } + + private StorageFacetImpl storageFacetImpl(final String nodeId, + final BucketEntityAdapter bucketEntityAdapter, + final ComponentEntityAdapter componentEntityAdapter, + final AssetEntityAdapter assetEntityAdapter, + final Repository repository) throws Exception { + NodeAccess mockNodeAccess = mock(NodeAccess.class); + when(mockNodeAccess.getId()).thenReturn(nodeId); + BlobStoreManager mockBlobStoreManager = mock(BlobStoreManager.class); + when(mockBlobStoreManager.get(anyString())).thenReturn(mock(BlobStore.class)); + ContentValidatorSelector contentValidatorSelector = + new ContentValidatorSelector(Collections.emptyMap(), new DefaultContentValidator(new DefaultMimeSupport())); + MimeRulesSourceSelector mimeRulesSourceSelector = new MimeRulesSourceSelector(Collections.emptyMap()); + StorageFacetManager storageFacetManager = mock(StorageFacetManager.class); + ComponentFactory componentFactory = new ComponentFactory(emptySet()); + StorageFacetImpl storageFacetImpl = new StorageFacetImpl( + mockNodeAccess, + mockBlobStoreManager, + database.getInstanceProvider(), + bucketEntityAdapter, + componentEntityAdapter, + assetEntityAdapter, + mock(ClientInfoProvider.class), + contentValidatorSelector, + mimeRulesSourceSelector, + storageFacetManager, + componentFactory); + storageFacetImpl.installDependencies(mock(EventManager.class)); + + storageFacetImpl.attach(repository); + storageFacetImpl.init(); + storageFacetImpl.start(); + + return storageFacetImpl; + } + + private AttributesFacetImpl attributesFacetImpl(Repository repository) throws Exception { + AttributesFacetImpl attributesFacetImpl = new AttributesFacetImpl(); + attributesFacetImpl.installDependencies(mock(EventManager.class)); + attributesFacetImpl.attach(repository); + attributesFacetImpl.init(); + attributesFacetImpl.start(); + return attributesFacetImpl; + } + + private StorageTx beginTX() { + final StorageTx tx = underTest.txSupplier().get(); + tx.begin(); + return tx; + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/StorageFacetImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/StorageFacetImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1df44ffd6b64e344d4d2e2b5b3afc4422aa6a747 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/StorageFacetImplTest.java @@ -0,0 +1,229 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobId; +import org.sonatype.nexus.blobstore.api.BlobMetrics; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration; +import org.sonatype.nexus.blobstore.api.BlobStoreManager; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.orient.DatabaseInstance; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.config.ConfigurationFacet; +import org.sonatype.nexus.repository.storage.StorageFacetImpl.Config; +import org.sonatype.nexus.repository.view.Payload; +import org.sonatype.nexus.security.ClientInfo; +import org.sonatype.nexus.security.ClientInfoProvider; + +import com.google.common.io.ByteStreams; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Matchers; +import org.mockito.Mock; + +import static com.google.common.hash.HashCode.fromString; +import static java.util.Collections.singletonList; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA1; + +/** + * Unit tests for {@link StorageFacetImpl}. + */ +public class StorageFacetImplTest + extends TestSupport +{ + private static final String NODE_ID = "testNodeId"; + + private static final String BLOB_STORE_NAME = "testBlobStore"; + + private static final String BLOB_ID = "testBlobId"; + + @Mock + private NodeAccess nodeAccess; + + @Mock + private BlobStoreManager blobStoreManager; + + @Mock + private BlobStoreConfiguration blobStoreConfiguration; + + @Mock + private BlobStore blobStore; + + @Mock + private BlobId blobId; + + @Mock + private BlobMetrics blobMetrics; + + @Mock + private Blob blob; + + @Mock + private DatabaseInstance databaseInstance; + + @Mock + private BucketEntityAdapter bucketEntityAdapter; + + @Mock + private ComponentEntityAdapter componentEntityAdapter; + + @Mock + private AssetEntityAdapter assetEntityAdapter; + + @Mock + private ClientInfoProvider clientInfoProvider; + + @Mock + private ClientInfo clientInfo; + + @Mock + private ContentValidatorSelector contentValidatorSelector; + + @Mock + private MimeRulesSourceSelector mimeRulesSourceSelector; + + @Mock + private StorageFacetManager storageFacetManager; + + @Mock + private Configuration configuration; + + @Mock + private ConfigurationFacet configurationFacet; + + @Mock + private Repository repository; + + @Mock + private Payload payload; + + @Mock + private ComponentFactory componentFactory; + + @Captor + private ArgumentCaptor> mapArgumentCaptor; + + private StorageFacetImpl underTest; + + @Before + public void setUp() throws Exception { + Config config = new Config(); + config.blobStoreName = BLOB_STORE_NAME; + when(blobStore.getBlobStoreConfiguration()).thenReturn(blobStoreConfiguration); + when(blobStoreConfiguration.getName()).thenReturn(BLOB_STORE_NAME); + when(repository.facet(ConfigurationFacet.class)).thenReturn(configurationFacet); + when(configurationFacet + .readSection(any(Configuration.class), eq(StorageFacetImpl.CONFIG_KEY), eq(StorageFacetImpl.Config.class))) + .thenReturn(config); + when(nodeAccess.getId()).thenReturn(NODE_ID); + when(blob.getId()).thenReturn(blobId); + when(blob.getMetrics()).thenReturn(blobMetrics); + when(blobId.asUniqueString()).thenReturn(BLOB_ID); + underTest = new StorageFacetImpl( + nodeAccess, + blobStoreManager, + () -> databaseInstance, + bucketEntityAdapter, + componentEntityAdapter, + assetEntityAdapter, + clientInfoProvider, + contentValidatorSelector, + mimeRulesSourceSelector, + storageFacetManager, + componentFactory); + underTest.attach(repository); + } + + @Test + public void createTempBlobFromInputStream() throws Exception { + byte[] contents = "hello, world".getBytes(StandardCharsets.UTF_8); + underTest.doConfigure(configuration); + when(blobStoreManager.get(BLOB_STORE_NAME)).thenReturn(blobStore); + when(blobStore.create(any(InputStream.class), Matchers.>any())).thenAnswer( + invocationOnMock -> { + ByteStreams.toByteArray((InputStream) invocationOnMock.getArguments()[0]); + return blob; + }); + when(blobMetrics.getContentSize()).thenReturn((long) contents.length); + try (ByteArrayInputStream in = new ByteArrayInputStream(contents)) { + try (TempBlob tempBlob = underTest.createTempBlob(in, singletonList(SHA1))) { + assertThat(tempBlob.getHashes(), hasEntry(SHA1, fromString("b7e23ec29af22b0b4e41da31e868d57226121c84"))); + assertThat(tempBlob.getHashesVerified(), is(true)); + assertThat(tempBlob.getBlob(), is(blob)); + } + verify(blobStore).deleteHard(blobId); + } + } + + @Test + public void createTempBlobFromPayload() throws Exception { + byte[] contents = "hello, world".getBytes(StandardCharsets.UTF_8); + underTest.doConfigure(configuration); + when(blobStoreManager.get(BLOB_STORE_NAME)).thenReturn(blobStore); + when(blobStore.create(any(InputStream.class), Matchers.>any())).thenAnswer( + invocationOnMock -> { + ByteStreams.toByteArray((InputStream) invocationOnMock.getArguments()[0]); + return blob; + }); + when(blobMetrics.getContentSize()).thenReturn((long) contents.length); + when(payload.openInputStream()).thenAnswer(invocationOnMock -> new ByteArrayInputStream(contents)); + try (TempBlob tempBlob = underTest.createTempBlob(payload, singletonList(SHA1))) { + assertThat(tempBlob.getHashes(), hasEntry(SHA1, fromString("b7e23ec29af22b0b4e41da31e868d57226121c84"))); + assertThat(tempBlob.getHashesVerified(), is(true)); + assertThat(tempBlob.getBlob(), is(blob)); + } + verify(blobStore).deleteHard(blobId); + } + + @Test + public void createTempBlob_uploader() throws Exception { + byte[] contents = "hello, world".getBytes(StandardCharsets.UTF_8); + underTest.doConfigure(configuration); + when(blobStoreManager.get(BLOB_STORE_NAME)).thenReturn(blobStore); + when(blobStore.create(any(InputStream.class), Matchers.> any())) + .thenAnswer(invocationOnMock -> { + ByteStreams.toByteArray((InputStream) invocationOnMock.getArguments()[0]); + return blob; + }); + when(blobMetrics.getContentSize()).thenReturn((long) contents.length); + when(clientInfoProvider.getCurrentThreadClientInfo()).thenReturn(clientInfo); + when(clientInfo.getRemoteIP()).thenReturn("10.1.1.1"); + when(clientInfo.getUserid()).thenReturn("jpicard"); + when(payload.openInputStream()).thenAnswer(invocationOnMock -> new ByteArrayInputStream(contents)); + try (TempBlob tempBlob = underTest.createTempBlob(payload, singletonList(SHA1))) { + verify(blobStore).create(any(InputStream.class), mapArgumentCaptor.capture()); + + assertThat(mapArgumentCaptor.getValue(), hasEntry(BlobStore.CREATED_BY_IP_HEADER, "10.1.1.1")); + assertThat(mapArgumentCaptor.getValue(), hasEntry(BlobStore.CREATED_BY_HEADER, "jpicard")); + } + verify(blobStore).deleteHard(blobId); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/StorageTestUtil.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/StorageTestUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..2338d8da89b8bfdf31ea1c62dc316ce548a8ceb4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/StorageTestUtil.java @@ -0,0 +1,89 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage; + +import java.util.HashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.DetachedEntityId; +import org.sonatype.nexus.common.entity.DetachedEntityMetadata; +import org.sonatype.nexus.common.entity.DetachedEntityVersion; + +import static org.sonatype.nexus.common.entity.EntityHelper.id; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; + +/** + * Test utils for storage classes + * + * @since 3.6.1 + */ +public class StorageTestUtil +{ + private static AtomicInteger VERSION_INCREMENTER = new AtomicInteger(1); + + public static Bucket createBucket(final String repositoryName) + { + Bucket bucket = new Bucket() + .attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + bucket.setEntityMetadata( + new DetachedEntityMetadata(new DetachedEntityId("a"), new DetachedEntityVersion(getNextVersion()))); + bucket.setRepositoryName(repositoryName); + return bucket; + } + + public static Component createComponent(final Bucket bucket, + final String group, + final String name, + final String version) + { + return new DefaultComponent() + .bucketId(id(bucket)) + .format("format-id") + .group(group) + .name(name) + .version(version) + .attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + } + + public static Component createDetachedComponent(final Bucket bucket, + final String group, + final String name, + final String version) + { + Component component = createComponent(bucket, group, name, version); + component.setEntityMetadata( + new DetachedEntityMetadata(new DetachedEntityId("b"), new DetachedEntityVersion(getNextVersion()))); + return component; + } + + public static Asset createAsset(final Bucket bucket, final String name, final Component component) { + return new Asset() + .bucketId(id(bucket)) + .format("format-id") + .name(name) + .componentId(id(component)) + .attributes(new NestedAttributesMap(P_ATTRIBUTES, new HashMap<>())); + } + + public static Asset createDetachedAsset(final Bucket bucket, final String name, final Component component) { + Asset asset = createAsset(bucket, name, component); + asset.setEntityMetadata( + new DetachedEntityMetadata(new DetachedEntityId("c"), new DetachedEntityVersion(getNextVersion()))); + return asset; + } + + private static String getNextVersion() { + return Integer.toString(VERSION_INCREMENTER.addAndGet(1)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/StorageTxImplTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/StorageTxImplTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..2e28a188ea8e3c49cff1f6087c66753b5bf6d23c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/StorageTxImplTest.groovy @@ -0,0 +1,662 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.blobstore.api.Blob +import org.sonatype.nexus.blobstore.api.BlobMetrics +import org.sonatype.nexus.blobstore.api.BlobRef +import org.sonatype.nexus.blobstore.api.BlobStore +import org.sonatype.nexus.common.collect.NestedAttributesMap +import org.sonatype.nexus.common.entity.EntityId +import org.sonatype.nexus.common.entity.EntityMetadata +import org.sonatype.nexus.common.hash.HashAlgorithm +import org.sonatype.nexus.mime.MimeRulesSource +import org.sonatype.nexus.repository.IllegalOperationException +import org.sonatype.nexus.repository.view.ContentTypes + +import com.google.common.base.Supplier +import com.google.common.hash.HashCode +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx +import com.orientechnologies.orient.core.tx.OTransaction +import org.joda.time.DateTime +import org.junit.Before +import org.junit.Test +import org.mockito.Mock + +import static org.hamcrest.MatcherAssert.assertThat +import static org.hamcrest.Matchers.is +import static org.mockito.Matchers.any +import static org.mockito.Matchers.anyBoolean +import static org.mockito.Matchers.anyString +import static org.mockito.Matchers.eq +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.never +import static org.mockito.Mockito.times +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when +import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA1 +import static org.sonatype.nexus.repository.storage.Asset.CHECKSUM +import static org.sonatype.nexus.repository.storage.Asset.HASHES_NOT_VERIFIED +import static org.sonatype.nexus.repository.storage.Asset.PROVENANCE + +/** + * Tests for {@link StorageTxImpl}. + */ +class StorageTxImplTest +extends TestSupport +{ + @Mock + private BlobTx blobTx + @Mock + private ODatabaseDocumentTx db + @Mock + private OTransaction tx + @Mock + private BucketEntityAdapter bucketEntityAdapter + @Mock + private ComponentEntityAdapter componentEntityAdapter + @Mock + private AssetEntityAdapter assetEntityAdapter + @Mock + private ComponentFactory componentFactory + @Mock + private Asset asset + @Mock + private EntityMetadata entityMetadata; + @Mock + private EntityId entityId; + + private Supplier supplier = new Supplier(){ + @Override + InputStream get() { + return new ByteArrayInputStream('testContent'.bytes) + } + } + + private DefaultContentValidator defaultContentValidator = mock(DefaultContentValidator) + private Map headers = [:] + private Map expectedHeaders = [(Bucket.REPO_NAME_HEADER) : 'testRepo', (BlobStore.BLOB_NAME_HEADER) : 'testBlob.txt', (BlobStore.CREATED_BY_HEADER) : 'test', (BlobStore.CREATED_BY_IP_HEADER) : '127.0.0.1', (BlobStore.CONTENT_TYPE_HEADER) : 'text/plain'] + private Iterable hashAlgorithms = [] + + @Before + void prepare() { + when(asset.getEntityMetadata()).thenReturn(entityMetadata); + when(entityMetadata.getId()).thenReturn(entityId); + + when(defaultContentValidator.determineContentType(anyBoolean(), any(Supplier), eq(MimeRulesSource.NOOP), anyString(), anyString())).thenReturn("text/plain") + when(db.getTransaction()).thenReturn(tx) + } + + /** + * Given: + * - an asset with a blob + * - DENY write policy + * When: + * - asset is removed + * Then: + * - exception is thrown + * - blob is not removed from db + * - asset is not removed from db + */ + @Test + void 'deleting assets fails when DENY write policy'() { + when(asset.blobRef()).thenReturn(mock(BlobRef)) + def underTest = new StorageTxImpl('test', '127.0.0.1', blobTx, db, 'testRepo', WritePolicy.DENY, + WritePolicySelector.DEFAULT, bucketEntityAdapter, componentEntityAdapter, assetEntityAdapter, false, + defaultContentValidator, MimeRulesSource.NOOP, componentFactory) + try { + underTest.deleteAsset(asset) + assertThat 'Expected IllegalOperationException', false + } + catch (IllegalOperationException e) {} + verify(blobTx, never()).delete(any(BlobRef), any(String)) + verify(assetEntityAdapter, never()).deleteEntity(db, asset) + } + + /** + * Given: + * - an asset without a blob + * - DENY write policy + * When: + * - asset is removed + * Then: + * - asset is removed from db + */ + @Test + void 'deleting assets pass when DENY write policy without blob'() { + new StorageTxImpl('test', '127.0.0.1', blobTx, db, 'testRepo', WritePolicy.DENY, WritePolicySelector.DEFAULT, + bucketEntityAdapter, componentEntityAdapter, assetEntityAdapter, false, defaultContentValidator, + MimeRulesSource.NOOP, componentFactory).deleteAsset(asset) + verify(assetEntityAdapter, times(1)).deleteEntity(db, asset) + } + + /** + * Given: + * - an asset with a blob + * - ALLOW write policy + * When: + * - asset is removed + * Then: + * - blob is removed from db + * - asset is removed from db + */ + @Test + void 'deleting assets pass when ALLOW write policy'() { + deleteAssetWhenWritePolicy(WritePolicy.ALLOW) + } + + /** + * Given: + * - an asset with a blob + * - ALLOW_ONCE write policy + * When: + * - asset is removed + * Then: + * - blob is removed from db + * - asset is removed from db + */ + @Test + void 'deleting assets pass when ALLOW_ONCE write policy'() { + deleteAssetWhenWritePolicy(WritePolicy.ALLOW_ONCE) + } + + void deleteAssetWhenWritePolicy(final WritePolicy writePolicy) { + def blobRef = mock(BlobRef) + when(asset.blobRef()).thenReturn(blobRef) + new StorageTxImpl('test', '127.0.0.1', blobTx, db, 'testRepo', writePolicy, WritePolicySelector.DEFAULT, + bucketEntityAdapter, componentEntityAdapter, assetEntityAdapter, false, defaultContentValidator, + MimeRulesSource.NOOP, componentFactory).deleteAsset(asset) + verify(blobTx, times(1)).delete(eq(blobRef), any(String)) + verify(assetEntityAdapter, times(1)).deleteEntity(db, asset) + } + + /** + * Given: + * - an asset with a blob + * - DENY write policy + * When: + * - setting a blob on the asset + * Then: + * - exception is thrown + * - existing blob is not removed from db + * - new blob is not created in db + * - asset blob reference is not changed + */ + @Test + void 'setting blob fails on asset with blob when DENY write policy'() { + def blobRef = mock(BlobRef) + def asssetBlob = mock(AssetBlob) + when(asssetBlob.getBlobRef()).thenReturn(blobRef) + when(asset.blobRef()).thenReturn(blobRef) + def underTest = new StorageTxImpl('test', '127.0.0.1', blobTx, db, 'testRepo', WritePolicy.DENY, + WritePolicySelector.DEFAULT, bucketEntityAdapter, componentEntityAdapter, assetEntityAdapter, false, + defaultContentValidator, MimeRulesSource.NOOP, componentFactory) + try { + underTest.setBlob(asset, 'testBlob.txt', supplier, hashAlgorithms, headers, 'text/plain', false) + assertThat 'Expected IllegalOperationException', false + } + catch (IllegalOperationException e) {} + verify(blobTx, never()).delete(any(BlobRef), any(String)) + verify(blobTx, never()).create(any(InputStream), any(Map), any(Iterable), anyString()) + verify(asset, never()).blobRef(any(BlobRef)) + } + + /** + * Given: + * - an asset without a blob + * - DENY write policy + * When: + * - setting a blob on the asset + * Then: + * - exception is thrown + * - new blob is not created in db + * - asset blob reference is not changed + */ + @Test + void 'setting blob fails on asset without blob when DENY write policy'() { + def underTest = new StorageTxImpl('test', '127.0.0.1', blobTx, db, 'testRepo', WritePolicy.DENY, + WritePolicySelector.DEFAULT, bucketEntityAdapter, componentEntityAdapter, assetEntityAdapter, false, + defaultContentValidator, MimeRulesSource.NOOP, componentFactory) + try { + underTest.setBlob(asset, 'testBlob.txt', supplier, hashAlgorithms, headers, 'text/plain', false) + assertThat 'Expected IllegalOperationException', false + } + catch (IllegalOperationException e) {} + verify(blobTx, never()).delete(any(BlobRef), any(String)) + verify(blobTx, never()).create(any(InputStream), any(Map), any(Iterable), anyString()) + verify(asset, never()).blobRef(any(BlobRef)) + } + + /** + * Given: + * - an asset with a blob + * - ALLOW_ONCE write policy + * When: + * - setting a blob on the asset + * Then: + * - exception is thrown + * - existing blob is not removed from db + * - new blob is not created in db + * - asset blob reference is not changed + */ + @Test + void 'setting blob fails on asset with blob when ALLOW_ONCE write policy'() { + def blobRef = mock(BlobRef) + def assetBlob = mock(AssetBlob) + when(assetBlob.getBlobRef()).thenReturn(blobRef) + when(asset.blobRef()).thenReturn(blobRef) + when(blobTx.create(any(InputStream), any(Map), any(Iterable), anyString())).thenReturn(assetBlob) + def underTest = new StorageTxImpl('test', '127.0.0.1', blobTx, db, 'testRepo', WritePolicy.ALLOW_ONCE, + WritePolicySelector.DEFAULT, bucketEntityAdapter, componentEntityAdapter, assetEntityAdapter, false, + defaultContentValidator, MimeRulesSource.NOOP, componentFactory) + try { + underTest.setBlob(asset, 'testBlob.txt', supplier, hashAlgorithms, headers, 'text/plain', false) + assertThat 'Expected IllegalOperationException', false + } + catch (IllegalOperationException e) {} + verify(blobTx, never()).delete(any(BlobRef), any(String)) + verify(blobTx, never()).create(any(InputStream), any(Map), any(Iterable), anyString()) + verify(asset, never()).blobRef(any(BlobRef)) + } + + /** + * Given: + * - an asset without a blob + * - ALLOW_ONCE write policy + * When: + * - setting a blob on the asset + * Then: + * - new blob is created in db + * - asset blob reference is changed + */ + @Test + void 'setting blob pass on asset without blob when ALLOW_ONCE write policy'() { + when(asset.attributes()).thenReturn(new NestedAttributesMap('attributes', [:])) + def newBlobRef = mock(BlobRef) + def assetBlob = mock(AssetBlob) + def blob = mock(Blob) + def headerMap = [(BlobStore.CREATED_BY_IP_HEADER) : '127.0.0.1', (BlobStore.CREATED_BY_HEADER) : 'anonymous'] + when(blob.getHeaders()).thenReturn(headerMap) + when(assetBlob.getBlob()).thenReturn(blob) + when(assetBlob.getBlobRef()).thenReturn(newBlobRef) + when(blobTx.create(any(InputStream), any(Map), any(Iterable), anyString())).thenReturn(assetBlob) + def underTest = new StorageTxImpl('test', '127.0.0.1', blobTx, db, 'testRepo', WritePolicy.ALLOW_ONCE, + WritePolicySelector.DEFAULT, bucketEntityAdapter, componentEntityAdapter, assetEntityAdapter, false, + defaultContentValidator, MimeRulesSource.NOOP, componentFactory) + underTest.setBlob(asset, 'testBlob.txt', supplier, hashAlgorithms, headers, "text/plain", false) + verify(blobTx, times(1)).create(any(InputStream), eq(expectedHeaders), eq(hashAlgorithms), eq('text/plain')) + verify(asset, times(1)).blobRef(newBlobRef) + } + + /** + * Given: + * - an asset with a blob + * - ALLOW write policy + * When: + * - setting a blob on the asset + * Then: + * - existing blob is removed from db + * - new blob is created in db + * - asset blob reference is changed + */ + @Test + void 'setting blob pass on asset with blob when ALLOW write policy'() { + when(asset.attributes()).thenReturn(new NestedAttributesMap('attributes', [:])) + def blobRef = mock(BlobRef) + def assetBlob = mock(AssetBlob) + def blob = mock(Blob) + def headerMap = [(BlobStore.CREATED_BY_IP_HEADER) : '127.0.0.1', (BlobStore.CREATED_BY_HEADER) : 'anonymous'] + when(blob.getHeaders()).thenReturn(headerMap) + when(assetBlob.getBlob()).thenReturn(blob) + when(assetBlob.getBlobRef()).thenReturn(blobRef); + when(asset.blobRef()).thenReturn(blobRef) + def newBlobRef = mock(BlobRef) + def newAssetBlob = mock(AssetBlob) + when(newAssetBlob.getBlob()).thenReturn(blob) + when(newAssetBlob.getBlobRef()).thenReturn(newBlobRef) + when(blobTx.create(any(InputStream), any(Map), any(Iterable), eq(ContentTypes.TEXT_PLAIN))).thenReturn(newAssetBlob) + def underTest = new StorageTxImpl('test', '127.0.0.1', blobTx, db, 'testRepo', WritePolicy.ALLOW, + WritePolicySelector.DEFAULT, bucketEntityAdapter, componentEntityAdapter, assetEntityAdapter, false, + defaultContentValidator, MimeRulesSource.NOOP, componentFactory) + underTest.setBlob(asset, 'testBlob.txt', supplier, hashAlgorithms, headers, 'text/plain', false) + verify(blobTx, times(1)).delete(eq(blobRef), any(String)) + verify(blobTx, times(1)).create(any(InputStream), eq(expectedHeaders), any(Iterable), eq(ContentTypes.TEXT_PLAIN)) + verify(asset, times(1)).blobRef(newBlobRef) + } + + /** + * Given: + * - an asset without a blob + * - ALLOW write policy + * When: + * - setting a blob on the asset + * Then: + * - new blob is created in db + * - asset blob reference is changed + */ + @Test + void 'setting blob pass on asset without blob when ALLOW write policy'() { + when(asset.attributes()).thenReturn(new NestedAttributesMap('attributes', [:])) + def newBlobRef = mock(BlobRef) + def assetBlob = mock(AssetBlob) + def blob = mock(Blob) + def headerMap = [(BlobStore.CREATED_BY_IP_HEADER) : '127.0.0.1', (BlobStore.CREATED_BY_HEADER) : 'anonymous'] + when(blob.getHeaders()).thenReturn(headerMap) + when(assetBlob.getBlob()).thenReturn(blob) + when(assetBlob.getBlobRef()).thenReturn(newBlobRef) + when(blobTx.create(any(InputStream), any(Map), any(Iterable), anyString())).thenReturn(assetBlob) + def underTest = new StorageTxImpl('test', '127.0.0.1', blobTx, db, 'testRepo', WritePolicy.ALLOW, + WritePolicySelector.DEFAULT, bucketEntityAdapter, componentEntityAdapter, assetEntityAdapter, false, + defaultContentValidator, MimeRulesSource.NOOP, componentFactory) + underTest.setBlob(asset, 'testBlob.txt', supplier, hashAlgorithms, headers, 'text/plain', false) + verify(blobTx, times(1)).create(any(InputStream), eq(expectedHeaders), eq(hashAlgorithms), eq('text/plain')) + verify(asset, times(1)).blobRef(newBlobRef) + } + + @Test(expected = IllegalArgumentException) + void 'invoking createBlob with verified by no contentType supplied'() { + when(asset.attributes()).thenReturn(new NestedAttributesMap('attributes', [:])) + def newBlobRef = mock(BlobRef) + def assetBlob = mock(AssetBlob) + when(assetBlob.getBlobRef()).thenReturn(newBlobRef) + when(blobTx.create(any(InputStream), any(Map), any(Iterable), anyString())).thenReturn(assetBlob) + def underTest = new StorageTxImpl('test', '127.0.0.1', blobTx, db, 'testRepo', WritePolicy.ALLOW, + WritePolicySelector.DEFAULT, bucketEntityAdapter, componentEntityAdapter, assetEntityAdapter, false, + defaultContentValidator, MimeRulesSource.NOOP, componentFactory) + underTest.setBlob(asset, 'testBlob.txt', supplier, hashAlgorithms, headers, null, true) + } + + /** + * Given: + * - an asset without a blob + * - an unattached asset blob without verified checksums + * When: + * - attaching the blob to the asset + * Then: + * - the asset checksum attributes will contain the checksum + * - the asset provenance attributes will indicate the hashes were not verified + */ + @Test + void 'attaching blob without verified hashes to asset'() { + attachBlobWithHashes(false) + } + + /** + * Given: + * - an asset without a blob + * - an unattached asset blob with verified checksums + * When: + * - attaching the blob to the asset + * Then: + * - the asset checksum attributes will contain the checksum + * - the asset provenance attributes will indicate the hashes were verified + */ + @Test + void 'attaching blob with verified hashes to asset'() { + attachBlobWithHashes(true) + } + + void attachBlobWithHashes(boolean hashesVerified) { + def hashCode = '6adfb183a4a2c94a2f92dab5ade762a47889a5a1' + def hashes = [(SHA1): HashCode.fromString(hashCode)] as Map + def attributesMap = mock(NestedAttributesMap.class) + def checksum = mock(NestedAttributesMap.class) + def provenance = mock(NestedAttributesMap.class) + when(attributesMap.child(CHECKSUM)).thenReturn(checksum) + when(attributesMap.child(PROVENANCE)).thenReturn(provenance) + when(asset.attributes()).thenReturn(attributesMap) + def newBlobRef = mock(BlobRef) + def assetBlob = mock(AssetBlob) + def blob = mock(Blob) + def headerMap = [(BlobStore.CREATED_BY_IP_HEADER) : '127.0.0.1', (BlobStore.CREATED_BY_HEADER) : 'anonymous'] + when(blob.getHeaders()).thenReturn(headerMap) + when(assetBlob.getBlob()).thenReturn(blob) + when(assetBlob.getBlobRef()).thenReturn(newBlobRef) + when(assetBlob.getHashes()).thenReturn(hashes) + when(assetBlob.getHashesVerified()).thenReturn(hashesVerified) + def underTest = new StorageTxImpl('test', + '127.0.0.1', + blobTx, + db, + 'testRepo', + WritePolicy.ALLOW, + WritePolicySelector.DEFAULT, + bucketEntityAdapter, + componentEntityAdapter, + assetEntityAdapter, + false, + defaultContentValidator, + MimeRulesSource.NOOP, + componentFactory) + underTest.attachBlob(asset, assetBlob) + verify(checksum, times(1)).set(SHA1.name(), hashCode) + verify(provenance, times(1)).set(HASHES_NOT_VERIFIED, !hashesVerified) + } + + /** + * Given: + * - a blob which is missing from the blobstore + * When: + * - requiring the blob + * Then: + * - exception is thrown + */ + @Test(expected = MissingBlobException.class) + void 'requiring blob fails when blob is missing from blobstore'() { + def blobRef = mock(BlobRef) + when(blobTx.get(blobRef)).thenReturn(null) + def underTest = new StorageTxImpl( + 'test', + '127.0.0.1', + blobTx, + db, + 'testRepo', + WritePolicy.ALLOW_ONCE, + WritePolicySelector.DEFAULT, + bucketEntityAdapter, + componentEntityAdapter, + assetEntityAdapter, + false, + defaultContentValidator, + MimeRulesSource.NOOP, componentFactory) + underTest.requireBlob(blobRef) + } + + /** + * Given: + * - an asset with no attached blob + * When: + * - attaching a blob + * Then: + * - the blob created timestamp will be updated + * - the blob updated timestamp will be updated + */ + @Test + void 'attaching a blob to an asset without a blob sets the blob created and blob updated timestamps'() { + when(asset.attributes()).thenReturn(new NestedAttributesMap('key', [key: [:]])) + def blobMetrics = mock(BlobMetrics) + when(blobMetrics.getSha1Hash()).thenReturn('sha1') + def blob = mock(Blob) + when(blob.getMetrics()).thenReturn(blobMetrics) + def assetBlob = mock(AssetBlob) + when(assetBlob.getBlob()).thenReturn(blob) + def underTest = new StorageTxImpl( + 'test', + '127.0.0.1', + blobTx, + db, + 'testRepo', + WritePolicy.ALLOW_ONCE, + WritePolicySelector.DEFAULT, + bucketEntityAdapter, + componentEntityAdapter, + assetEntityAdapter, + false, + defaultContentValidator, + MimeRulesSource.NOOP, componentFactory) + underTest.attachBlob(asset, assetBlob) + verify(asset).blobCreated(any(DateTime)) + verify(asset).blobUpdated(any(DateTime)) + } + + /** + * Given: + * - an asset with an attached blob + * When: + * - attaching a blob with a different hash + * Then: + * - the blob created timestamp will NOT be updated + * - the blob updated timestamp will be updated + */ + @Test + void 'attaching a blob with different hash to an asset with a blob only updates the blob created timestamp'() { + def oldBlobMetrics = mock(BlobMetrics) + when(oldBlobMetrics.getSha1Hash()).thenReturn('old-sha1') + def oldBlob = mock(Blob) + when(oldBlob.getMetrics()).thenReturn(oldBlobMetrics) + def blobRef = mock(BlobRef) + when(blobTx.get(blobRef)).thenReturn(oldBlob) + when(asset.attributes()).thenReturn(new NestedAttributesMap('key', [key: [:]])) + when(asset.blobRef()).thenReturn(blobRef) + def newBlobMetrics = mock(BlobMetrics) + when(newBlobMetrics.getSha1Hash()).thenReturn('new-sha1') + def newBlob = mock(Blob) + when(newBlob.getMetrics()).thenReturn(newBlobMetrics) + def assetBlob = mock(AssetBlob) + when(assetBlob.getBlob()).thenReturn(newBlob) + def underTest = new StorageTxImpl( + 'test', + '127.0.0.1', + blobTx, + db, + 'testRepo', + WritePolicy.ALLOW, + WritePolicySelector.DEFAULT, + bucketEntityAdapter, + componentEntityAdapter, + assetEntityAdapter, + false, + defaultContentValidator, + MimeRulesSource.NOOP, componentFactory) + underTest.attachBlob(asset, assetBlob) + verify(asset, never()).blobCreated(any(DateTime)) + verify(asset).blobUpdated(any(DateTime)) + } + + /** + * Given: + * - an asset with an attached blob + * When: + * - attaching a blob with the same hash + * Then: + * - the blob created timestamp will NOT be updated + * - the blob updated timestamp will NOT be updated + */ + void 'attaching a blob with the same hash to an asset with an existing blob does not update any timestamps'() { + def oldBlobMetrics = mock(BlobMetrics) + when(oldBlobMetrics.getSha1Hash()).thenReturn('sha1') + def oldBlob = mock(Blob) + when(oldBlob.getMetrics()).thenReturn(oldBlobMetrics) + def blobRef = mock(BlobRef) + when(blobTx.get(blobRef)).thenReturn(oldBlob) + when(asset.attributes()).thenReturn(new NestedAttributesMap('key', [key: [:]])) + when(asset.blobRef()).thenReturn(blobRef) + def newBlobMetrics = mock(BlobMetrics) + when(newBlobMetrics.getSha1Hash()).thenReturn('sha1') + def newBlob = mock(Blob) + when(newBlob.getMetrics()).thenReturn(newBlobMetrics) + def assetBlob = mock(AssetBlob) + when(assetBlob.getBlob()).thenReturn(newBlob) + def underTest = new StorageTxImpl( + 'test', + '127.0.0.1', + blobTx, + db, + 'testRepo', + WritePolicy.ALLOW, + WritePolicySelector.DEFAULT, + bucketEntityAdapter, + componentEntityAdapter, + assetEntityAdapter, + false, + defaultContentValidator, + MimeRulesSource.NOOP, componentFactory) + underTest.attachBlob(asset, assetBlob) + verify(asset, never()).blobCreated(any(DateTime)) + verify(asset, never()).blobUpdated(any(DateTime)) + } + + /** + * Given: + * - an asset with an attached blob that has disappeared from the blob store + * When: + * - attaching a blob with any hash + * Then: + * - the blob created timestamp will NOT be updated + * - the blob updated timestamp will be updated + */ + @Test + void 'attaching a blob to an asset with a missing blob only updates the blob created timestamp'() { + def blobRef = mock(BlobRef) + when(blobTx.get(blobRef)).thenReturn(null) + when(asset.attributes()).thenReturn(new NestedAttributesMap('key', [key: [:]])) + when(asset.blobRef()).thenReturn(blobRef) + def newBlobMetrics = mock(BlobMetrics) + when(newBlobMetrics.getSha1Hash()).thenReturn('new-sha1') + def newBlob = mock(Blob) + when(newBlob.getMetrics()).thenReturn(newBlobMetrics) + def assetBlob = mock(AssetBlob) + when(assetBlob.getBlob()).thenReturn(newBlob) + def underTest = new StorageTxImpl( + 'test', + '127.0.0.1', + blobTx, + db, + 'testRepo', + WritePolicy.ALLOW, + WritePolicySelector.DEFAULT, + bucketEntityAdapter, + componentEntityAdapter, + assetEntityAdapter, + false, + defaultContentValidator, + MimeRulesSource.NOOP, componentFactory) + underTest.attachBlob(asset, assetBlob) + verify(asset, never()).blobCreated(any(DateTime)) + verify(asset).blobUpdated(any(DateTime)) + } + + @Test + void 'verifying asset lookup by id'() { + def assetId = mock(EntityId) + def asset = mock(Asset) + when(assetEntityAdapter.read(db, assetId)).thenReturn(asset); + def underTest = new StorageTxImpl( + 'test', + '127.0.0.1', + blobTx, + db, + 'testRepo', + WritePolicy.ALLOW, + WritePolicySelector.DEFAULT, + bucketEntityAdapter, + componentEntityAdapter, + assetEntityAdapter, + false, + defaultContentValidator, + MimeRulesSource.NOOP, componentFactory) + + assertThat underTest.findAsset(assetId), is(asset) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_3_Test.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_3_Test.groovy new file mode 100644 index 0000000000000000000000000000000000000000..b7343421b324be92bba600f2c5a7431e3932b76b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_3_Test.groovy @@ -0,0 +1,151 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.orient.OClassNameBuilder +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule + +import com.orientechnologies.orient.core.metadata.schema.OClass +import com.orientechnologies.orient.core.metadata.schema.OSchema +import com.orientechnologies.orient.core.metadata.schema.OType +import com.orientechnologies.orient.core.record.impl.ODocument +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +import static org.hamcrest.Matchers.is +import static org.junit.Assert.assertThat + +class ComponentDatabaseUpgrade_1_3_Test + extends TestSupport +{ + static final String ASSET_CLASS = new OClassNameBuilder() + .type('asset') + .build() + + static final String P_NAME = 'name' + + static final String P_FORMAT = 'format' + + static final String P_ATTRIBUTES = 'attributes' + + static final String P_LAST_ACCESSED = 'last_accessed' + + static final String P_LAST_DOWNLOADED = 'last_downloaded' + + static final String P_BLOB_CREATED = 'blob_created' + + static final String P_BLOB_UPDATED = 'blob_updated' + + static final Date LAST_ACCESSED_TIMESTAMP = new Date(0) + + @Rule + public DatabaseInstanceRule componentDatabase = DatabaseInstanceRule.inMemory("test_component") + + ComponentDatabaseUpgrade_1_3 underTest + + @Before + void setUp() { + underTest = new ComponentDatabaseUpgrade_1_3(componentDatabase.getInstanceProvider()) + } + + @Test + void 'upgrade step creates blob_created and blob_updated properties when absent'() { + populateComponentDatabase() + + underTest.apply() + + componentDatabase.instance.connect().withCloseable { db -> + OClass assetType = db.metadata.schema.getClass(ASSET_CLASS) + assertThat(assetType.existsProperty(P_BLOB_CREATED), is(true)) + assertThat(assetType.existsProperty(P_BLOB_UPDATED), is(true)) + assertThat(assetType.getProperty(P_BLOB_CREATED).type, is(OType.DATETIME)) + assertThat(assetType.getProperty(P_BLOB_UPDATED).type, is(OType.DATETIME)) + } + } + + @Test + void 'upgrade step works even if blob_created and blob_updated properties are present'() { + populateComponentDatabase() + + componentDatabase.instance.connect().withCloseable { db -> + OClass assetType = db.metadata.schema.getClass(ASSET_CLASS) + assetType.createProperty(P_BLOB_CREATED, OType.DATETIME) + assetType.createProperty(P_BLOB_UPDATED, OType.DATETIME) + } + + underTest.apply() + + componentDatabase.instance.connect().withCloseable { db -> + OClass assetType = db.metadata.schema.getClass(ASSET_CLASS) + assertThat(assetType.existsProperty(P_BLOB_CREATED), is(true)) + assertThat(assetType.existsProperty(P_BLOB_UPDATED), is(true)) + assertThat(assetType.getProperty(P_BLOB_CREATED).type, is(OType.DATETIME)) + assertThat(assetType.getProperty(P_BLOB_UPDATED).type, is(OType.DATETIME)) + } + } + + @Test + void 'upgrade step replaces last_accessed with last_downloaded'() { + populateComponentDatabase() + + underTest.apply() + + componentDatabase.instance.connect().withCloseable { db -> + OClass assetType = db.metadata.schema.getClass(ASSET_CLASS) + assertThat(assetType.existsProperty(P_LAST_ACCESSED), is(false)) + assertThat(assetType.existsProperty(P_LAST_DOWNLOADED), is(true)) + assertThat(assetType.getProperty(P_LAST_DOWNLOADED).type, is(OType.DATETIME)) + db.browseClass(ASSET_CLASS).each { asset -> + assertThat(asset.field(P_LAST_DOWNLOADED, OType.DATETIME), is(LAST_ACCESSED_TIMESTAMP)) + } + } + } + + @Test + void 'upgrade step does not throw exceptions if asset class is not found in schema'() { + underTest.apply() + } + + @Test + void 'upgrade step does not throw exceptions if last_accessed property is not found on asset'() { + populateComponentDatabase(false) + underTest.apply() + } + + private void populateComponentDatabase(boolean includeLastAccessedField = true) { + componentDatabase.instance.connect().withCloseable { db -> + + OSchema schema = db.getMetadata().getSchema(); + OClass assetType = schema.createClass(ASSET_CLASS) + + assetType.createProperty(P_NAME, OType.STRING) + .setMandatory(true) + .setNotNull(true) + assetType.createProperty(P_FORMAT, OType.STRING) + .setMandatory(true) + .setNotNull(true) + if (includeLastAccessedField) { + assetType.createProperty(P_LAST_ACCESSED, OType.DATETIME) + } + assetType.createProperty(P_ATTRIBUTES, OType.EMBEDDEDMAP) + + ODocument document = db.newInstance(ASSET_CLASS) + document.field(P_NAME, 'name') + document.field(P_FORMAT, 'format') + document.field(P_LAST_ACCESSED, LAST_ACCESSED_TIMESTAMP) + document.save() + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_4_Test.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_4_Test.groovy new file mode 100644 index 0000000000000000000000000000000000000000..fe05a6ce637de7d4ff447740972d712163b16f3a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_4_Test.groovy @@ -0,0 +1,100 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx +import com.orientechnologies.orient.core.metadata.schema.OClass +import com.orientechnologies.orient.core.metadata.schema.OSchema +import com.orientechnologies.orient.core.metadata.schema.OType +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +import static org.hamcrest.Matchers.containsInAnyOrder +import static org.junit.Assert.assertThat +import static org.sonatype.nexus.repository.storage.AssetEntityAdapter.P_COMPONENT +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_NAME +import static org.sonatype.nexus.repository.storage.internal.ComponentDatabaseUpgrade_1_4.ASSET_CLASS +import static org.sonatype.nexus.repository.storage.internal.ComponentDatabaseUpgrade_1_4.COMPONENT_CLASS +import static org.sonatype.nexus.repository.storage.internal.ComponentDatabaseUpgrade_1_4.I_ASSET_NAME +import static org.sonatype.nexus.repository.storage.internal.ComponentDatabaseUpgrade_1_4.I_COMPONENT +import static org.sonatype.nexus.repository.storage.internal.ComponentDatabaseUpgrade_1_4.I_COMPONENT_GROUP_NAME_VERSION +import static org.sonatype.nexus.repository.storage.internal.ComponentDatabaseUpgrade_1_4.I_NAME_CASE_INSENSITIVE + +class ComponentDatabaseUpgrade_1_4_Test + extends TestSupport +{ + + @Rule + public DatabaseInstanceRule componentDatabase = DatabaseInstanceRule.inMemory("test_component") + + ComponentDatabaseUpgrade_1_4 underTest + + @Before + void setUp() { + underTest = new ComponentDatabaseUpgrade_1_4(componentDatabase.getInstanceProvider()) + } + + @Test + void 'upgrade step creates indices when absent'() { + populateComponentDatabase() + + underTest.apply() + + componentDatabase.instance.connect().withCloseable { db -> + assertThat(indicesFor(db, ASSET_CLASS), containsInAnyOrder(I_COMPONENT, I_ASSET_NAME)) + assertThat(indicesFor(db, COMPONENT_CLASS), + containsInAnyOrder(I_NAME_CASE_INSENSITIVE, I_COMPONENT_GROUP_NAME_VERSION)) + } + } + + @Test + void 'upgrade step works even if indices are present'() { + populateComponentDatabase() + + underTest.apply() + + underTest.apply() + + componentDatabase.instance.connect().withCloseable { db -> + assertThat(indicesFor(db, ASSET_CLASS), containsInAnyOrder(I_COMPONENT, I_ASSET_NAME)) + assertThat(indicesFor(db, COMPONENT_CLASS), + containsInAnyOrder(I_NAME_CASE_INSENSITIVE, I_COMPONENT_GROUP_NAME_VERSION)) + } + } + + @Test + void 'upgrade step does not throw exceptions if asset class and component class are not found in schema'() { + underTest.apply() + } + + private static indicesFor(final ODatabaseDocumentTx db, final String type) { + db.metadata.schema.getClass(type).indexes.collect { it.name } + } + + private void populateComponentDatabase() { + componentDatabase.instance.connect().withCloseable { db -> + + OSchema schema = db.getMetadata().getSchema() + + OClass componentType = schema.createClass(COMPONENT_CLASS) + componentType.createProperty(P_NAME, OType.STRING) + + OClass assetType = schema.createClass(ASSET_CLASS) + assetType.createProperty(P_COMPONENT, OType.LINK, componentType) + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_5_Test.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_5_Test.groovy new file mode 100644 index 0000000000000000000000000000000000000000..a1f703ce6014d27bb7a7a041b3545f7ee204ac50 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/ComponentDatabaseUpgrade_1_5_Test.groovy @@ -0,0 +1,174 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.orient.OClassNameBuilder +import org.sonatype.nexus.orient.OIndexBuilder +import org.sonatype.nexus.orient.OIndexNameBuilder +import org.sonatype.nexus.orient.testsupport.DatabaseInstanceRule + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx +import com.orientechnologies.orient.core.metadata.schema.OClass +import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE +import com.orientechnologies.orient.core.metadata.schema.OSchema +import com.orientechnologies.orient.core.metadata.schema.OType +import com.orientechnologies.orient.core.record.impl.ODocument +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +import static org.hamcrest.Matchers.is +import static org.hamcrest.Matchers.not +import static org.hamcrest.core.IsNull.nullValue +import static org.junit.Assert.assertThat + +class ComponentDatabaseUpgrade_1_5_Test + extends TestSupport +{ + static final String COMPONENT_CLASS = new OClassNameBuilder() + .type('component') + .build() + + static final String I_CI_NAME_CASE_INSENSITIVE = new OIndexNameBuilder() + .type(COMPONENT_CLASS) + .property(P_CI_NAME) + .caseInsensitive() + .build(); + + static final String I_NAME_CASE_INSENSITIVE = new OIndexNameBuilder() + .type(COMPONENT_CLASS) + .property(P_NAME) + .caseInsensitive() + .build(); + + static final String P_NAME = 'name' + + static final String P_FORMAT = 'format' + + static final String P_CI_NAME = 'ci_name' + + @Rule + public DatabaseInstanceRule componentDatabase = DatabaseInstanceRule.inMemory("test_component") + + ComponentDatabaseUpgrade_1_5 underTest + + @Before + void setUp() { + underTest = new ComponentDatabaseUpgrade_1_5(componentDatabase.getInstanceProvider()) + } + + @Test + void 'upgrade step creates and populates ci_name property'() { + componentDatabase.instance.connect().withCloseable { db -> + createComponentType(db) + createComponentRecord(db) + } + + underTest.apply() + + componentDatabase.instance.connect().withCloseable { db -> + assertComponentType(db) + assertDocuments(db) + } + + underTest.apply() + + componentDatabase.instance.connect().withCloseable { db -> + assertComponentType(db) + assertDocuments(db) + } + } + + @Test + void 'upgrade step creates ci_name case-insensitive index'() { + componentDatabase.instance.connect().withCloseable { db -> + createComponentType(db) + assertThat(db.metadata.indexManager.getIndex(I_CI_NAME_CASE_INSENSITIVE), is(nullValue())) + } + + underTest.apply() + + componentDatabase.instance.connect().withCloseable { db -> + assertThat(db.metadata.indexManager.getIndex(I_CI_NAME_CASE_INSENSITIVE), not(nullValue())) + } + + underTest.apply() + + componentDatabase.instance.connect().withCloseable { db -> + assertThat(db.metadata.indexManager.getIndex(I_CI_NAME_CASE_INSENSITIVE), not(nullValue())) + } + } + + @Test + void 'upgrade step drops existing case-insensitive index on name field'() { + componentDatabase.instance.connect().withCloseable { db -> + createComponentType(db) + OSchema schema = db.metadata.schema + OClass componentType = schema.getClass(COMPONENT_CLASS) + new OIndexBuilder(componentType, I_NAME_CASE_INSENSITIVE, INDEX_TYPE.NOTUNIQUE) + .property(P_NAME, OType.STRING) + .caseInsensitive() + .build(db) + } + + componentDatabase.instance.connect().withCloseable { db -> + assertThat(db.metadata.indexManager.getIndex(I_NAME_CASE_INSENSITIVE), not(nullValue())) + } + + underTest.apply() + + componentDatabase.instance.connect().withCloseable { db -> + assertThat(db.metadata.indexManager.getIndex(I_NAME_CASE_INSENSITIVE), is(nullValue())) + } + + underTest.apply() + + componentDatabase.instance.connect().withCloseable { db -> + assertThat(db.metadata.indexManager.getIndex(I_NAME_CASE_INSENSITIVE), is(nullValue())) + } + } + + private void createComponentRecord(ODatabaseDocumentTx db) { + ODocument document = db.newInstance(COMPONENT_CLASS) + document.field(P_NAME, 'TestName') + document.field(P_FORMAT, 'format') + document.save() + } + + private void createComponentType(ODatabaseDocumentTx db) { + OSchema schema = db.metadata.schema + OClass componentType = schema.createClass(COMPONENT_CLASS) + componentType.createProperty(P_NAME, OType.STRING) + .setMandatory(true) + .setNotNull(true) + componentType.createProperty(P_FORMAT, OType.STRING) + .setMandatory(true) + .setNotNull(true) + } + + private void assertDocuments(ODatabaseDocumentTx db) { + db.browseClass(COMPONENT_CLASS).forEach { document -> + assertThat(document.field(P_NAME), is('TestName')) + assertThat(document.field(P_CI_NAME), is('testname')) // has to be lowercase for now by convention (workaround) + } + } + + private void assertComponentType(ODatabaseDocumentTx db) { + OClass componentType = db.metadata.schema.getClass(COMPONENT_CLASS) + assertThat(componentType.existsProperty(P_CI_NAME), is(true)) + assertThat(componentType.getProperty(P_CI_NAME).mandatory, is(true)) + assertThat(componentType.getProperty(P_CI_NAME).notNull, is(true)) + assertThat(componentType.getProperty(P_CI_NAME).collate.name, is('ci')) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTaskManagerTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTaskManagerTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..6dce45fda092a8d3c308d6746fff3b4719a19358 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTaskManagerTest.groovy @@ -0,0 +1,89 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.scheduling.TaskConfiguration +import org.sonatype.nexus.scheduling.TaskInfo +import org.sonatype.nexus.scheduling.TaskScheduler +import org.sonatype.nexus.scheduling.schedule.Cron +import org.sonatype.nexus.scheduling.schedule.Schedule +import org.sonatype.nexus.scheduling.schedule.ScheduleFactory + +import org.junit.Before +import org.junit.Test +import org.mockito.Mock + +import static org.mockito.Matchers.any +import static org.mockito.Matchers.eq +import static org.mockito.Mockito.never +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when + +class StorageFacetCleanupTaskManagerTest + extends TestSupport +{ + static final String TASK_TYPE_ID = 'repository.storage-facet-cleanup' + + static final String CRON_EXPRESSION = '0 * * * * ?' + + @Mock + private TaskScheduler taskScheduler + + @Mock + private ScheduleFactory scheduleFactory + + @Mock + private TaskInfo taskInfo + + @Mock + private Cron cron + + private StorageFacetCleanupTaskManager underTest + + @Before + public void setUp() { + underTest = new StorageFacetCleanupTaskManager( + taskScheduler, + CRON_EXPRESSION + ) + } + + @Test + void 'will create a new cleanup task if one does not exist on startup'() { + TaskConfiguration taskConfiguration = new TaskConfiguration() + taskConfiguration.setTypeId(TASK_TYPE_ID) + + when(taskScheduler.listsTasks()).thenReturn([]) + when(taskScheduler.getScheduleFactory()).thenReturn(scheduleFactory) + when(taskScheduler.createTaskConfigurationInstance(TASK_TYPE_ID)).thenReturn(taskConfiguration) + when(scheduleFactory.cron(any(Date), eq(CRON_EXPRESSION))).thenReturn(cron) + + underTest.doStart() + + verify(taskScheduler).scheduleTask(taskConfiguration, cron) + } + + @Test + void 'will not create a duplicate cleanup task if one exists on startup'() { + TaskConfiguration taskConfiguration = new TaskConfiguration() + taskConfiguration.setTypeId(TASK_TYPE_ID) + + when(taskInfo.getConfiguration()).thenReturn(taskConfiguration) + when(taskScheduler.listsTasks()).thenReturn([taskInfo]) + + underTest.doStart() + + verify(taskScheduler, never()).scheduleTask(any(TaskConfiguration), any(Schedule)) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTaskTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTaskTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..dc69fb040dad54bff14ac4b7fd2cdb00b1c6203f --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/StorageFacetCleanupTaskTest.groovy @@ -0,0 +1,45 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.repository.storage.StorageFacetManager + +import org.junit.Before +import org.junit.Test +import org.mockito.Mock + +import static org.mockito.Mockito.times +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when + +class StorageFacetCleanupTaskTest + extends TestSupport +{ + @Mock + StorageFacetManager storageFacetManager + + private StorageFacetCleanupTask underTest + + @Before + public void setUp() { + underTest = new StorageFacetCleanupTask(storageFacetManager) + } + + @Test + void 'task attempts deletions until no more entries were successfully deleted'() { + when(storageFacetManager.performDeletions()).thenReturn(2L, 1L, 0L) + underTest.execute() + verify(storageFacetManager, times(3)).performDeletions() + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/StorageFacetManagerImplTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/StorageFacetManagerImplTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..b8428d833dfe15605d25a138192755844fa57da4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/storage/internal/StorageFacetManagerImplTest.groovy @@ -0,0 +1,116 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.storage.internal + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.blobstore.api.BlobStore +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration +import org.sonatype.nexus.common.collect.NestedAttributesMap +import org.sonatype.nexus.orient.DatabaseInstance +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.storage.Bucket +import org.sonatype.nexus.repository.storage.BucketDeleter +import org.sonatype.nexus.repository.storage.BucketEntityAdapter + +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock + +import static org.hamcrest.Matchers.is +import static org.hamcrest.Matchers.startsWith +import static org.junit.Assert.assertThat +import static org.mockito.Matchers.same +import static org.mockito.Mockito.doThrow +import static org.mockito.Mockito.never +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when + +class StorageFacetManagerImplTest + extends TestSupport +{ + static final String TEST_REPOSITORY_NAME = 'testRepository' + + static final String TEST_BLOB_STORE_NAME = 'testBlobStore' + + @Mock + private BlobStore blobStore + + @Mock + private BlobStoreConfiguration blobStoreConfiguration + + @Mock + private DatabaseInstance databaseInstance + + @Mock + private BucketEntityAdapter bucketEntityAdapter + + @Mock + private BucketDeleter bucketDeleter + + @Mock + private Repository repository + + @Mock + private ODatabaseDocumentTx db + + @Captor + ArgumentCaptor bucketCaptor + + private StorageFacetManagerImpl underTest + + @Before + public void setUp() { + when(databaseInstance.acquire()).thenReturn(db) + underTest = new StorageFacetManagerImpl( + { databaseInstance }, + bucketEntityAdapter, + bucketDeleter + ) + } + + @Test + void 'correctly marks a bucket for deletion'() { + Bucket bucket = new Bucket(attributes: new NestedAttributesMap("attributes", [:])) + + when(blobStoreConfiguration.getName()).thenReturn(TEST_BLOB_STORE_NAME) + when(repository.getName()).thenReturn(TEST_REPOSITORY_NAME) + when(blobStore.getBlobStoreConfiguration()).thenReturn(blobStoreConfiguration) + + underTest.enqueueDeletion(repository, blobStore, bucket) + verify(bucketEntityAdapter).editEntity(same(db), bucketCaptor.capture()) + + NestedAttributesMap bucketAttributes = bucketCaptor.value.attributes() + assertThat(bucketCaptor.value.repositoryName, startsWith(TEST_REPOSITORY_NAME + '$')) + assertThat(bucketAttributes.contains('pendingDeletion'), is(true)) + } + + @Test + void 'only buckets marked for deletion will be deleted, and deletion will be attempted at least once for each'() { + Bucket normalBucket = new Bucket(attributes: new NestedAttributesMap('attributes', [:])) + Bucket deleteBucket = new Bucket(attributes: new NestedAttributesMap('attributes', [pendingDeletion: [:]])) + Bucket failedBucket = new Bucket(attributes: new NestedAttributesMap('attributes', [pendingDeletion: [:]])) + + when(bucketEntityAdapter.browse(db)).thenReturn([normalBucket, failedBucket, deleteBucket]) + doThrow(new RuntimeException()).when(bucketDeleter).deleteBucket(failedBucket) + + long count = underTest.performDeletions() + + assertThat(count, is(1L)) + verify(bucketDeleter, never()).deleteBucket(normalBucket) + verify(bucketDeleter).deleteBucket(failedBucket) + verify(bucketDeleter).deleteBucket(deleteBucket) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/tools/DeadBlobFinderTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/tools/DeadBlobFinderTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..ccfb746780e46637668914792198a8709eb9a134 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/tools/DeadBlobFinderTest.groovy @@ -0,0 +1,284 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.tools + +import org.sonatype.nexus.blobstore.api.Blob +import org.sonatype.nexus.blobstore.api.BlobId +import org.sonatype.nexus.blobstore.api.BlobMetrics +import org.sonatype.nexus.blobstore.api.BlobRef +import org.sonatype.nexus.blobstore.api.BlobStoreException +import org.sonatype.nexus.common.collect.NestedAttributesMap +import org.sonatype.nexus.common.entity.DetachedEntityId +import org.sonatype.nexus.common.entity.DetachedEntityMetadata +import org.sonatype.nexus.common.entity.DetachedEntityVersion +import org.sonatype.nexus.common.hash.HashAlgorithm +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.storage.Asset +import org.sonatype.nexus.repository.storage.AssetEntityAdapter +import org.sonatype.nexus.repository.storage.StorageFacet +import org.sonatype.nexus.repository.storage.StorageTx + +import com.google.common.base.Supplier +import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx +import org.apache.tika.io.IOUtils +import org.joda.time.DateTime +import spock.lang.Shared +import spock.lang.Specification +import spock.lang.Subject + +import static org.sonatype.nexus.repository.tools.ResultState.ASSET_DELETED +import static org.sonatype.nexus.repository.tools.ResultState.DELETED +import static org.sonatype.nexus.repository.tools.ResultState.MISSING_BLOB_REF +import static org.sonatype.nexus.repository.tools.ResultState.SHA1_DISAGREEMENT +import static org.sonatype.nexus.repository.tools.ResultState.UNAVAILABLE_BLOB + +class DeadBlobFinderTest + extends Specification +{ + Repository repository = Mock() + + StorageFacet storageFacet = Mock() + + StorageTx tx = Mock() + + NestedAttributesMap attributes = Mock() + + Blob blob = Mock() + + BlobRef blobRef = Mock() + + AssetEntityAdapter assetEntityAdapter = Mock() + + ODatabaseDocumentTx db = Mock() + + @Shared + InputStream blobStream = IOUtils.toInputStream('foo') + + @Shared + Asset asset + + @Shared + BlobMetrics blobMetrics = new BlobMetrics(DateTime.now(), '1234', 1234) + + @Subject + DeadBlobFinder deadBlobFinder = new DeadBlobFinder(assetEntityAdapter) + + + def setup() { + asset = createAsset() + repository.name >> 'bar' + } + + Asset createAsset() { + Asset asset = new Asset() + asset.attributes(attributes) + asset.name('foo') + asset.blobRef(blobRef) + asset.entityMetadata = new DetachedEntityMetadata(new DetachedEntityId('foo'), new DetachedEntityVersion('1')) + return asset + } + + def 'no errors if blob exists and matches checksum'() { + when: 'On the happy path' + def result = deadBlobFinder.find(repository) + + then: 'no errors are surfaced' + interaction { + mockTxState() + mockAssetBrowse() + commonBlobMock() + } + result.isEmpty() + } + + def 'errors if blob sha1 disagrees with db'() { + when: 'asset hash is in disagreement with blob' + def result = deadBlobFinder.find(repository) + + then: 'an error is returned' + interaction { + mockTxState() + mockAssetBrowse() + mockAssetReload() + } + 2 * attributes.child('checksum') >> attributes + 2 * attributes.get(HashAlgorithm.SHA1.name(), String) >> '1235' + 2 * tx.requireBlob(_) >> blob + 2 * blob.metrics >> blobMetrics + !result.isEmpty() + result[0].asset.name() == 'foo' + result[0].resultState == SHA1_DISAGREEMENT + } + + def 'errors if blob InputStream is not available'() { + given: 'An inputstream which is not available' + InputStream is = Mock() + + when: + def result = deadBlobFinder.find(repository) + + then: + interaction { + mockTxState() + mockAssetBrowse() + commonBlobMock(2, is) + mockAssetReload() + } + 2 * is.available() >> 0 + !result.isEmpty() + result[0].asset.name() == 'foo' + result[0].resultState == UNAVAILABLE_BLOB + } + + def 'do not check availability of InputStream if content length is zero'() { + when: 'Our metrics indicate that a Blob has zero content length' + def result = deadBlobFinder.find(repository) + + then: 'we do not bother to check availability' + interaction { + mockTxState() + mockAssetBrowse() + commonBlobMock(1, blobStream, new BlobMetrics(DateTime.now(), '1234', 0)) + } + 0 * blobStream.available() // never called as zero length blob would return 0 correctly + result.isEmpty() + } + + def 'missing Asset.blobRef is an error if not ignored'() { + given: 'An asset with missing BlobRef' + asset.blobRef(null) + + when: 'we elect not to ignore missing blobs' + def result = deadBlobFinder.find(repository, false) + + then: 'an error result is returned' + interaction { + mockTxState() + mockAssetBrowse() + mockAssetReload() + } + !result.isEmpty() + result[0].asset.name() == 'foo' + result[0].resultState == MISSING_BLOB_REF + result[0].errorMessage == 'Missing property: blob_ref' + } + + def 'missing Asset.blobRef is not an error if ignored (nuget case)'() { + given: 'An asset with missing BlobRef' + asset.blobRef(null) + + when: 'we elect to ignore missing blobs' + def result = deadBlobFinder.find(repository, true) + + then: 'no error result is returned' + interaction { + mockTxState() + mockAssetBrowse() + } + result.isEmpty() + } + + def 'passing in a null repository results in an error'() { + when: 'A null repository is passed in ' + deadBlobFinder.find(null) + + then: 'an exception is thrown before interacting with the persistence layer at all' + thrown(NullPointerException) + 0 * _ + } + + def 'an asset can be deleted while the system is inspected'() { + given: 'An InputStream with a problem reading' + InputStream is = Mock() + + when: 'We encounter an Asset with a problem on the first pass' + def result = deadBlobFinder.find(repository) + + then: 'The Asset is reported as deleted' + interaction { + mockTxState() + mockAssetBrowse() + commonBlobMock(1, is) + mockAssetReload(null) + } + is.available() >> { throw new IOException('cannot read inputstream') } + !result.isEmpty() + result[0].asset == null + result[0].resultState == ASSET_DELETED + } + + def 'an asset blob can be deleted while the system is inspected'() { + given: 'A missing blobRef on the first pass and a missing file on the second' + asset.blobRef(null) // first pass we have a missing blobRef + Asset reloadedAsset = createAsset() + Blob reloadedBlob = Mock() // second pass the blobRef is there but file does not exist + + when: 'We encounter an Asset with a missing blob file on the second pass' + def result = deadBlobFinder.find(repository, false) + + then: 'The Asset is reported as having the associated Blob deleted' + interaction { + mockTxState() + mockAssetBrowse() + mockAssetReload(reloadedAsset) + } + 1 * tx.requireBlob(_) >> reloadedBlob + 1 * reloadedBlob.metrics >> blobMetrics + 1 * attributes.child('checksum') >> attributes + 1 * attributes.get(HashAlgorithm.SHA1.name(), String) >> '1234' + 1 * reloadedBlob.inputStream >> { throw new BlobStoreException('Blob has been deleted', new BlobId('foo')) } + !result.isEmpty() + result[0].asset.name() == 'foo' + result[0].resultState == DELETED + } + + /** + * Verify that begin/close are always called. + */ + private void mockTxState() { + 1 * tx.begin() + 1 * tx.close() + } + + /** + * Verify one pass over all Assets. + * @param assets + */ + private void mockAssetBrowse(List assets = [asset]) { + 1 * repository.facet(StorageFacet) >> storageFacet + 1 * storageFacet.txSupplier() >> ({ -> tx } as Supplier) + 1 * tx.browseAssets(_) >> assets + } + + /** + * Verify the reload of one particular Asset. + * @param reloadedAsset the Asset to be individually loaded + */ + private void mockAssetReload(Asset reloadedAsset = asset) { + 1 * tx.db >> db + 1 * assetEntityAdapter.read(_, _) >> reloadedAsset + } + + /** + * Helper to parameterize mock configuration of Blob access. + */ + private void commonBlobMock(int passes = 1, stream = blobStream, BlobMetrics metrics = blobMetrics, + String sha1 = '1234') + { + passes * tx.requireBlob(_) >> blob + passes * attributes.child('checksum') >> attributes + passes * attributes.get(HashAlgorithm.SHA1.name(), String) >> sha1 + passes * blob.metrics >> metrics + passes * blob.inputStream >> stream + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/upload/ValidatingComponentUploadTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/upload/ValidatingComponentUploadTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e43d21e2937fefad77d2bcec873f932e8ec02e4a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/upload/ValidatingComponentUploadTest.java @@ -0,0 +1,165 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.upload; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.view.PartPayload; +import org.sonatype.nexus.rest.ValidationErrorXO; +import org.sonatype.nexus.rest.ValidationErrorsException; + +import com.google.common.collect.ImmutableMap; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static java.lang.String.format; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonMap; +import static java.util.stream.Collectors.toList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.repository.upload.UploadFieldDefinition.Type.STRING; + +public class ValidatingComponentUploadTest + extends TestSupport +{ + @Mock + private UploadDefinition uploadDefinition; + + @Mock + private ComponentUpload componentUpload; + + @Mock + private AssetUpload assetUpload; + + @Before + public void setup() { + when(uploadDefinition.getComponentFields()).thenReturn(emptyList()); + when(uploadDefinition.getAssetFields()).thenReturn(emptyList()); + } + + @Test + public void testValidate_missingAssets() { + expectExceptionOnValidate(componentUpload, "No assets found in upload"); + } + + @Test + public void testValidate_missingAssetField() { + when(uploadDefinition.getComponentFields()).thenReturn(emptyList()); + when(uploadDefinition.getAssetFields()).thenReturn( + Collections.singletonList(new UploadFieldDefinition("foo", false, STRING))); + + when(assetUpload.getPayload()).thenReturn(mock(PartPayload.class)); + when(componentUpload.getAssetUploads()).thenReturn(Collections.singletonList(assetUpload)); + + expectExceptionOnValidate(componentUpload, "Missing required asset field 'Foo' on '1'"); + } + + @Test + public void testValidate_missingComponentField() { + when(uploadDefinition.getAssetFields()).thenReturn(emptyList()); + when(uploadDefinition.getComponentFields()).thenReturn( + Collections.singletonList(new UploadFieldDefinition("bar", false, STRING))); + + when(assetUpload.getPayload()).thenReturn(mock(PartPayload.class)); + when(componentUpload.getAssetUploads()).thenReturn(Collections.singletonList(assetUpload)); + + expectExceptionOnValidate(componentUpload, "Missing required component field 'Bar'"); + } + + @Test + public void testValidate_noViolations() { + when(uploadDefinition.getAssetFields()).thenReturn( + Collections.singletonList(new UploadFieldDefinition("foo", false, STRING))); + when(uploadDefinition.getComponentFields()).thenReturn( + Collections.singletonList(new UploadFieldDefinition("bar", false, STRING))); + + when(assetUpload.getPayload()).thenReturn(mock(PartPayload.class)); + when(assetUpload.getFields()).thenReturn(singletonMap("foo", "fooValue")); + when(assetUpload.getField("foo")).thenReturn("fooValue"); + + when(componentUpload.getAssetUploads()).thenReturn(Collections.singletonList(assetUpload)); + when(componentUpload.getFields()).thenReturn(singletonMap("bar", "barValue")); + when(componentUpload.getField("bar")).thenReturn("barValue"); + + try { + ValidatingComponentUpload validated = new ValidatingComponentUpload(uploadDefinition, componentUpload); + validated.getComponentUpload(); + } + catch (ValidationErrorsException e) { + fail(format("Unexpected validation exception thrown '%s'", e)); + } + } + + @Test + public void testValidate_duplicates() { + when(uploadDefinition.getAssetFields()).thenReturn(Arrays.asList(new UploadFieldDefinition("field1", true, STRING), + new UploadFieldDefinition("field2", true, STRING))); + + AssetUpload assetUploadOne = new AssetUpload(); + assetUploadOne.getFields().putAll(ImmutableMap.of("field1", "x", "field2", "y")); + assetUploadOne.setPayload(mock(PartPayload.class)); + + AssetUpload assetUploadTwo = new AssetUpload(); + assetUploadTwo.getFields().putAll(ImmutableMap.of("field1", "x", "field2", "y")); + assetUploadTwo.setPayload(mock(PartPayload.class)); + + AssetUpload assetUploadThree = new AssetUpload(); + assetUploadThree.getFields().putAll(ImmutableMap.of("field1", "x")); + assetUploadThree.setPayload(mock(PartPayload.class)); + + ComponentUpload componentUpload = new ComponentUpload(); + componentUpload.getAssetUploads().addAll(Arrays.asList(assetUploadOne, assetUploadTwo, assetUploadThree)); + + expectExceptionOnValidate(componentUpload, "The assets 1 and 2 have identical coordinates"); + } + + @Test + public void testValidate_unknownField() { + when(uploadDefinition.getAssetFields()).thenReturn(emptyList()); + when(uploadDefinition.getComponentFields()).thenReturn(emptyList()); + + AssetUpload assetUpload = new AssetUpload(); + assetUpload.getFields().put("foo", "foo"); + assetUpload.setPayload(mock(PartPayload.class)); + + ComponentUpload componentUpload = new ComponentUpload(); + componentUpload.getFields().put("bar", "bar"); + componentUpload.getAssetUploads().addAll(Collections.singletonList(assetUpload)); + + expectExceptionOnValidate(componentUpload, + "Unknown component field 'bar'", "Unknown field 'foo' on asset '1'"); + } + + private void expectExceptionOnValidate(final ComponentUpload component, final String... message) + { + try { + ValidatingComponentUpload validated = new ValidatingComponentUpload(uploadDefinition, component); + validated.getComponentUpload(); + fail("Expected exception to be thrown"); + } + catch (ValidationErrorsException exception) { + List messages = exception.getValidationErrors().stream() + .map(ValidationErrorXO::getMessage) + .collect(toList()); + assertThat(messages, contains(message)); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/upload/internal/UploadManagerImplTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/upload/internal/UploadManagerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2a5307ca13f5ddefb34386adc2b83c88deee4a2b --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/upload/internal/UploadManagerImplTest.java @@ -0,0 +1,178 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.upload.internal; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.Format; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.types.GroupType; +import org.sonatype.nexus.repository.types.HostedType; +import org.sonatype.nexus.repository.types.ProxyType; +import org.sonatype.nexus.repository.types.VirtualType; +import org.sonatype.nexus.repository.upload.AssetUpload; +import org.sonatype.nexus.repository.upload.ComponentUpload; +import org.sonatype.nexus.repository.upload.UploadDefinition; +import org.sonatype.nexus.repository.upload.UploadHandler; +import org.sonatype.nexus.repository.upload.ValidatingComponentUpload; +import org.sonatype.nexus.rest.ValidationErrorXO; +import org.sonatype.nexus.rest.ValidationErrorsException; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class UploadManagerImplTest + extends TestSupport +{ + private UploadManagerImpl underTest; + + @Mock + UploadHandler handlerA; + + @Mock + UploadDefinition uploadA; + + @Mock + UploadHandler handlerB; + + @Mock + UploadDefinition uploadB; + + @Mock + Repository repository; + + @Mock + ValidatingComponentUpload validatingComponentUpload; + + @Before + public void setup() { + when(handlerA.getDefinition()).thenReturn(uploadA); + when(handlerB.getDefinition()).thenReturn(uploadB); + when(handlerA.getValidatingComponentUpload(anyObject())).thenReturn(validatingComponentUpload); + when(handlerB.getValidatingComponentUpload(anyObject())).thenReturn(validatingComponentUpload); + + when(repository.getFormat()).thenReturn(new Format("a") + { + }); + when(repository.getType()).thenReturn(new HostedType()); + + Map handlers = new HashMap<>(); + handlers.put("a", handlerA); + handlers.put("b", handlerB); + + underTest = new UploadManagerImpl(handlers); + } + + @Test + public void testGetAvailable() { + assertThat(underTest.getAvailableDefinitions(), contains(uploadA, uploadB)); + } + + @Test + public void testGetByFormat() { + assertThat(underTest.getByFormat("a"), is(uploadA)); + assertThat(underTest.getByFormat("b"), is(uploadB)); + } + + @Test + public void testHandle() throws IOException { + ComponentUpload component = mock(ComponentUpload.class); + when(component.getAssetUploads()).thenReturn(Collections.singletonList(new AssetUpload())); + when(validatingComponentUpload.getComponentUpload()).thenReturn(component); + underTest.handle(repository, component); + + verify(handlerA, times(1)).handle(repository, component); + verify(handlerB, never()).handle(repository, component); + + // Try the other, to be sure! + reset(handlerA, handlerB); + when(handlerB.getDefinition()).thenReturn(uploadB); + when(handlerB.getValidatingComponentUpload(anyObject())).thenReturn(validatingComponentUpload); + + when(repository.getFormat()).thenReturn(new Format("b") + { + }); + + underTest.handle(repository, component); + + verify(handlerB, times(1)).handle(repository, component); + verify(handlerA, never()).handle(repository, component); + } + + @Test + public void testHandle_unsupportedRepositoryFormat() throws IOException { + ComponentUpload component = mock(ComponentUpload.class); + when(repository.getFormat()).thenReturn(new Format("c") + { + }); + + expectExceptionOnUpload(repository, component, "Uploading components to 'c' repositories is unsupported"); + } + + @Test + public void testHandle_unsupportedRepositoryGroupType() throws IOException { + when(repository.getType()).thenReturn(new GroupType()); + expectExceptionOnUpload(repository, mock(ComponentUpload.class), + "Uploading components to a 'group' type repository is unsupported, must be 'hosted'"); + } + + @Test + public void testHandle_unsupportedRepositoryProxyType() throws IOException { + when(repository.getType()).thenReturn(new ProxyType()); + expectExceptionOnUpload(repository, mock(ComponentUpload.class), + "Uploading components to a 'proxy' type repository is unsupported, must be 'hosted'"); + } + + @Test + public void testHandle_unsupportedRepositoryVirtualType() throws IOException { + when(repository.getType()).thenReturn(new VirtualType()); + expectExceptionOnUpload(repository, mock(ComponentUpload.class), + "Uploading components to a 'virtual' type repository is unsupported, must be 'hosted'"); + } + + + private void expectExceptionOnUpload(final Repository repository, + final ComponentUpload component, + final String message) throws IOException + { + try { + underTest.handle(repository, component); + fail("Expected exception to be thrown"); + } + catch (ValidationErrorsException exception) { + List messages = exception.getValidationErrors().stream() + .map(ValidationErrorXO::getMessage) + .collect(Collectors.toList()); + assertThat(messages, contains(message)); + } + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/ContentTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/ContentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..667aa0aae2f3e480342814c4d785e29d9556bcb7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/ContentTest.java @@ -0,0 +1,129 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.collect.AttributesMap; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.hash.HashAlgorithm; +import org.sonatype.nexus.repository.storage.Asset; + +import com.google.common.collect.Maps; +import org.joda.time.DateTime; +import org.junit.Test; + +import static java.util.Collections.singletonList; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.repository.storage.Asset.CHECKSUM; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; + +/** + * Tests {@link Content}. + */ +public class ContentTest + extends TestSupport +{ + private static final List ALGORITHMS = singletonList(HashAlgorithm.SHA1); + + private NestedAttributesMap nestedAttributesMap() { + NestedAttributesMap attributes = new NestedAttributesMap(P_ATTRIBUTES, Maps.newHashMap()); + attributes.child(CHECKSUM).set( + HashAlgorithm.SHA1.name(), + HashAlgorithm.SHA1.function().hashString("foobar", StandardCharsets.UTF_8).toString() + ); + return attributes; + } + + @Test + public void nothingToExtract() { + final NestedAttributesMap attributes = nestedAttributesMap(); + Asset asset = mock(Asset.class); + when(asset.attributes()).thenReturn(attributes); + AttributesMap contentAttributes = new AttributesMap(); + Content.extractFromAsset(asset, ALGORITHMS, contentAttributes); + assertThat(contentAttributes.contains(Content.CONTENT_LAST_MODIFIED), is(false)); + assertThat(contentAttributes.contains(Content.CONTENT_ETAG), is(false)); + assertThat(contentAttributes.contains(Content.CONTENT_HASH_CODES_MAP), is(true)); + } + + @Test + public void lastModifiedOnlyExtract() { + final DateTime now = DateTime.now(); + final NestedAttributesMap attributes = nestedAttributesMap(); + attributes.child(Content.CONTENT).set(Content.P_LAST_MODIFIED, now.toDate()); + Asset asset = mock(Asset.class); + when(asset.attributes()).thenReturn(attributes); + AttributesMap contentAttributes = new AttributesMap(); + Content.extractFromAsset(asset, ALGORITHMS, contentAttributes); + assertThat(contentAttributes.get(Content.CONTENT_LAST_MODIFIED, DateTime.class), equalTo(now)); + assertThat(contentAttributes.contains(Content.CONTENT_ETAG), is(false)); + assertThat(contentAttributes.contains(Content.CONTENT_HASH_CODES_MAP), is(true)); + } + + @Test + public void etagOnlyExtract() { + final String etag = "foo-bar"; + final NestedAttributesMap attributes = nestedAttributesMap(); + attributes.child(Content.CONTENT).set(Content.P_ETAG, etag); + Asset asset = mock(Asset.class); + when(asset.attributes()).thenReturn(attributes); + AttributesMap contentAttributes = new AttributesMap(); + Content.extractFromAsset(asset, ALGORITHMS, contentAttributes); + assertThat(contentAttributes.contains(Content.CONTENT_LAST_MODIFIED), is(false)); + assertThat(contentAttributes.get(Content.CONTENT_ETAG, String.class), equalTo(etag)); + assertThat(contentAttributes.contains(Content.CONTENT_HASH_CODES_MAP), is(true)); + } + + @Test + public void lastModifiedAndEtagExtract() { + final DateTime now = DateTime.now(); + final String etag = "foo-bar"; + final NestedAttributesMap attributes = nestedAttributesMap(); + attributes.child(Content.CONTENT).set(Content.P_LAST_MODIFIED, now.toDate()); + attributes.child(Content.CONTENT).set(Content.P_ETAG, etag); + Asset asset = mock(Asset.class); + when(asset.attributes()).thenReturn(attributes); + AttributesMap contentAttributes = new AttributesMap(); + Content.extractFromAsset(asset, ALGORITHMS, contentAttributes); + assertThat(contentAttributes.get(Content.CONTENT_LAST_MODIFIED, DateTime.class), equalTo(now)); + assertThat(contentAttributes.get(Content.CONTENT_ETAG, String.class), equalTo(etag)); + assertThat(contentAttributes.contains(Content.CONTENT_HASH_CODES_MAP), is(true)); + } + + @Test + public void lastModifiedAndEtagApply() { + final DateTime now = DateTime.now(); + final String etag = "foo-bar"; + final NestedAttributesMap attributes = nestedAttributesMap(); + attributes.child(Content.CONTENT).set(Content.P_LAST_MODIFIED, now.toDate()); + attributes.child(Content.CONTENT).set(Content.P_ETAG, etag); + Asset asset = mock(Asset.class); + when(asset.attributes()).thenReturn(attributes); + AttributesMap contentAttributes = new AttributesMap(); + contentAttributes.set(Content.CONTENT_LAST_MODIFIED, now); + contentAttributes.set(Content.CONTENT_ETAG, etag); + Content.applyToAsset(asset, contentAttributes); + assertThat(asset.attributes().child(Content.CONTENT).get(Content.P_LAST_MODIFIED, Date.class), + equalTo(now.toDate())); + assertThat(asset.attributes().child(Content.CONTENT).get(Content.P_ETAG, String.class), equalTo( + etag)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/RouterTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/RouterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..104ecceb812b5911eb54d5ac48529fe268f7c208 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/RouterTest.java @@ -0,0 +1,61 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import java.util.Collections; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.Repository; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.sonatype.nexus.repository.view.Router.LOCAL_ATTRIBUTE_PREFIX; + +public class RouterTest + extends TestSupport +{ + private Router underTest; + + @Mock + Repository repository; + + @Mock + Request request; + + @Mock + Route route; + + @Mock + DefaultRoute defaultRoute; + + + @Before + public void setup() throws Exception { + underTest = new Router(Collections.singletonList(route), defaultRoute); + } + + @Test + public void testMaybeCopyContextAttributes() throws Exception { + Context existingContext = new Context(repository, request); + existingContext.getAttributes().set("somekey", "somevalue"); + existingContext.getAttributes().set(LOCAL_ATTRIBUTE_PREFIX + "anotherkey", "anothervalue"); + Context newContext = underTest.maybeCopyContextAttributes(repository, request, existingContext); + assertThat(newContext.getAttributes().get("somekey"), is("somevalue")); + assertThat(newContext.getAttributes().get(LOCAL_ATTRIBUTE_PREFIX + "anotherkey"), nullValue()); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/ViewUtilsTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/ViewUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d14edfc05acaf9f43c2dff9e250f59e2e46fc47a --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/ViewUtilsTest.java @@ -0,0 +1,112 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view; + +import java.net.URISyntaxException; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.hamcrest.Matchers.any; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.sonatype.nexus.repository.view.ViewUtils.buildUrlWithParameters; + +public class ViewUtilsTest + extends TestSupport +{ + @Rule + public final ExpectedException exception = ExpectedException.none(); + + @Test + public void buildUrlsWithoutParameters() { + Parameters parameters = new Parameters(); + + assertThat(buildUrlWithParameters("http://www.example.com", parameters), + is("http://www.example.com")); + assertThat(buildUrlWithParameters("https://www.example.com", parameters), + is("https://www.example.com")); + } + + @Test + public void buildUrlsWithSubpathsWithoutParameters() { + Parameters parameters = new Parameters(); + + assertThat(buildUrlWithParameters("http://www.example.com/foo", parameters), + is("http://www.example.com/foo")); + assertThat(buildUrlWithParameters("https://www.example.com/foo", parameters), + is("https://www.example.com/foo")); + } + + @Test + public void buildUrlsWithSingleParameter() { + Parameters parameters = new Parameters(); + parameters.set("a", "1"); + + assertThat(buildUrlWithParameters("http://www.example.com", parameters), + is("http://www.example.com?a=1")); + assertThat(buildUrlWithParameters("http://www.example.com/foo", parameters), + is("http://www.example.com/foo?a=1")); + } + + @Test + public void buildUrlsWithMultipleParameters() { + Parameters parameters = new Parameters(); + parameters.set("a", "1"); + parameters.set("b", "2"); + + assertThat(buildUrlWithParameters("http://www.example.com", parameters), + is("http://www.example.com?a=1&b=2")); + assertThat(buildUrlWithParameters("http://www.example.com/foo", parameters), + is("http://www.example.com/foo?a=1&b=2")); + } + + @Test + public void buildUrlsWithDuplicateParameters() { + Parameters parameters = new Parameters(); + parameters.set("a", "1", "2"); + + assertThat(buildUrlWithParameters("http://www.example.com", parameters), + is("http://www.example.com?a=1&a=2")); + assertThat(buildUrlWithParameters("http://www.example.com/foo", parameters), + is("http://www.example.com/foo?a=1&a=2")); + } + + @Test + public void buildUrlsWithCorrectlyEncodedParameters() { + Parameters parameters = new Parameters(); + parameters.set("a", "what?"); + parameters.set("b", "F=ma"); + parameters.set("c", "hello world"); + parameters.set("d", "1+2"); + + assertThat(buildUrlWithParameters("http://www.example.com", parameters), + is("http://www.example.com?a=what%3F&b=F%3Dma&c=hello+world&d=1%2B2")); + } + + @Test + public void exceptionIsThrownOnInvalidUrl() { + exception.expect(IllegalArgumentException.class); + exception.expectCause(any(URISyntaxException.class)); + exception.expectMessage("http://www.test hostname.com"); + exception.expectMessage("test-parameter"); + exception.expectMessage("test-value"); + + Parameters parameters = new Parameters(); + parameters.set("test-parameter", "test-value"); + buildUrlWithParameters("http://www.test hostname.com", parameters); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/ConditionalRequestHandlerTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/ConditionalRequestHandlerTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..c5562b874adc9e592b3d0533e7f847181cb8ef95 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/ConditionalRequestHandlerTest.groovy @@ -0,0 +1,132 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.handlers + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.http.HttpMethods +import org.sonatype.nexus.repository.view.ContentTypes +import org.sonatype.nexus.repository.view.Context +import org.sonatype.nexus.repository.view.Request +import org.sonatype.nexus.repository.view.Response +import org.sonatype.nexus.repository.view.Status +import org.sonatype.nexus.repository.view.ViewFacet +import org.sonatype.nexus.repository.view.payloads.StringPayload + +import com.google.common.net.HttpHeaders +import org.junit.Before +import org.junit.Test +import org.mockito.Matchers +import org.mockito.Mock + +import static org.hamcrest.MatcherAssert.assertThat +import static org.hamcrest.Matchers.equalTo +import static org.mockito.Matchers.eq +import static org.mockito.Mockito.times +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when + +/** + * Tests for {@link ConditionalRequestHandler}. + */ +class ConditionalRequestHandlerTest + extends TestSupport +{ + @Mock + Context context + + @Mock + Repository repository + + @Mock + ViewFacet viewFacet + + Response response200 = new Response.Builder() + .status(Status.success(200)) + .payload(new StringPayload('payload', ContentTypes.TEXT_PLAIN)) + .header(HttpHeaders.LAST_MODIFIED, 'Tue, 16 Jun 2015 11:30:00 GMT') + .build() + + Response response201 = new Response.Builder() + .status(Status.success(201)) + .build() + + ConditionalRequestHandler conditionalRequestHandler = new ConditionalRequestHandler() + + @Before + void prepare() { + when(context.getRepository()).thenReturn(repository) + when(repository.facet(eq(ViewFacet.class))).thenReturn(viewFacet) + when(viewFacet.dispatch(Matchers.any(Request))).thenReturn(response200); + } + + @Test + void 'basic GET'() { + Request request = new Request.Builder().action(HttpMethods.GET).path('/foo').build() + when(context.getRequest()).thenReturn(request) + when(context.proceed()).thenReturn(response200) + Response response = conditionalRequestHandler.handle(context) + verify(context, times(1)).proceed() + assertThat(response.status.code, equalTo(200)) + } + + @Test + void 'conditional GET false'() { + Request request = new Request.Builder().action(HttpMethods.GET).path('/foo').header(HttpHeaders.IF_MODIFIED_SINCE, 'Tue, 16 Jun 2015 11:30:00 GMT').build() + when(context.getRequest()).thenReturn(request) + when(context.proceed()).thenReturn(response200) + Response response = conditionalRequestHandler.handle(context) + verify(context, times(1)).proceed() + assertThat(response.status.code, equalTo(304)) + } + + @Test + void 'conditional GET true'() { + Request request = new Request.Builder().action(HttpMethods.GET).path('/foo').header(HttpHeaders.IF_MODIFIED_SINCE, 'Tue, 16 Jun 2014 11:30:00 GMT').build() + when(context.getRequest()).thenReturn(request) + when(context.proceed()).thenReturn(response200) + Response response = conditionalRequestHandler.handle(context) + verify(context, times(1)).proceed() + assertThat(response.status.code, equalTo(200)) + } + + @Test + void 'basic PUT'() { + Request request = new Request.Builder().action(HttpMethods.PUT).path('/foo').payload(new StringPayload('payload', ContentTypes.TEXT_PLAIN)).build() + when(context.getRequest()).thenReturn(request) + when(context.proceed()).thenReturn(response201) + Response response = conditionalRequestHandler.handle(context) + verify(context, times(1)).proceed() + assertThat(response.status.code, equalTo(201)) + } + + @Test + void 'conditional PUT false'() { + Request request = new Request.Builder().action(HttpMethods.PUT).path('/foo').payload(new StringPayload('payload', ContentTypes.TEXT_PLAIN)).header(HttpHeaders.IF_UNMODIFIED_SINCE, 'Tue, 16 Jun 2014 11:30:00 GMT').build() + when(context.getRequest()).thenReturn(request) + when(context.proceed()).thenReturn(response201) + Response response = conditionalRequestHandler.handle(context) + verify(context, times(0)).proceed() + assertThat(response.status.code, equalTo(412)) + } + + @Test + void 'conditional PUT true'() { + Request request = new Request.Builder().action(HttpMethods.PUT).path('/foo').payload(new StringPayload('payload', ContentTypes.TEXT_PLAIN)).header(HttpHeaders.IF_UNMODIFIED_SINCE, 'Tue, 16 Jun 2015 11:30:00 GMT').build() + when(context.getRequest()).thenReturn(request) + when(context.proceed()).thenReturn(response201) + Response response = conditionalRequestHandler.handle(context) + verify(context, times(1)).proceed() + assertThat(response.status.code, equalTo(201)) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandlerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..572ee56ede3b297cb3424cb63dd6ba24be7df367 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandlerTest.java @@ -0,0 +1,101 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.handlers; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.http.HttpResponses; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.repository.view.Response; +import org.sonatype.nexus.repository.view.payloads.StringPayload; + +import com.google.common.net.HttpHeaders; +import org.apache.http.client.utils.DateUtils; +import org.joda.time.DateTime; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Mockito.when; + +/** + * UT for {@link ContentHeadersHandler}. + * + * @since 3.0 + */ +public class ContentHeadersHandlerTest + extends TestSupport +{ + final DateTime now = DateTime.now(); + + final ContentHeadersHandler subject = new ContentHeadersHandler(); + + final String payloadString = "testPayload"; + + @Mock + Context context; + + @Mock + Request request; + + @Before + public void before() throws Exception { + when(context.getRequest()).thenReturn(request); + } + + @Test + public void okResponse() throws Exception { + final Content content = new Content(new StringPayload(payloadString, "text/plain")); + content.getAttributes().set(Content.CONTENT_LAST_MODIFIED, now); + content.getAttributes().set(Content.CONTENT_ETAG, "etag"); + when(context.proceed()).thenReturn(HttpResponses.ok(content)); + final Response r = subject.handle(context); + assertThat(r.getStatus().isSuccessful(), is(true)); + assertThat(r.getHeaders().get(HttpHeaders.LAST_MODIFIED), equalTo(DateUtils.formatDate(now.toDate()))); + assertThat(r.getHeaders().get(HttpHeaders.ETAG), equalTo("\"etag\"")); + } + + @Test + public void okResponseNoExtraData() throws Exception { + when(context.proceed()).thenReturn( + HttpResponses.ok(new Content(new StringPayload(payloadString, "text/plain")))); + final Response r = subject.handle(context); + assertThat(r.getStatus().isSuccessful(), is(true)); + assertThat(r.getHeaders().get(HttpHeaders.LAST_MODIFIED), nullValue()); + assertThat(r.getHeaders().get(HttpHeaders.ETAG), nullValue()); + } + + @Test + public void okResponseNotContent() throws Exception { + when(context.proceed()).thenReturn(HttpResponses.ok(new StringPayload("test", "text/plain"))); + final Response r = subject.handle(context); + assertThat(r.getStatus().isSuccessful(), is(true)); + assertThat(r.getHeaders().get(HttpHeaders.LAST_MODIFIED), nullValue()); + assertThat(r.getHeaders().get(HttpHeaders.ETAG), nullValue()); + } + + @Test + public void notOkResponse() throws Exception { + when(context.proceed()).thenReturn(HttpResponses.notFound()); + final Response r = subject.handle(context); + assertThat(r.getStatus().isSuccessful(), is(false)); + assertThat(r.getHeaders().get(HttpHeaders.LAST_MODIFIED), nullValue()); + assertThat(r.getHeaders().get(HttpHeaders.ETAG), nullValue()); + } + +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/ExceptionHandlerTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/ExceptionHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..115f80748bdd5f179b4048f4ca20f71c62fc8f6c --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/ExceptionHandlerTest.java @@ -0,0 +1,80 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.handlers; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.IllegalOperationException; +import org.sonatype.nexus.repository.InvalidContentException; +import org.sonatype.nexus.repository.http.HttpStatus; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Request; + +import com.orientechnologies.common.concur.lock.OModificationOperationProhibitedException; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.orient.testsupport.OrientExceptionMocker.mockOrientException; + +public class ExceptionHandlerTest + extends TestSupport +{ + ExceptionHandler underTest; + + Exception frozenException; + + @Mock + Context context; + + @Mock + Request request; + + @Before + public void setUp() throws Exception { + when(context.getRequest()).thenReturn(request); + when(request.getAction()).thenReturn("GET"); + when(request.getPath()).thenReturn("/test"); + + underTest = new ExceptionHandler(); + + frozenException = mockOrientException(OModificationOperationProhibitedException.class); + } + + @Test + public void handleIllegalOperation() throws Exception { + when(context.proceed()).thenThrow(new IllegalOperationException("That operation was illegal.")); + assertThat(underTest.handle(context).getStatus().getCode(), is(HttpStatus.BAD_REQUEST)); + } + + @Test + public void handleInvalidContent() throws Exception { + when(context.proceed()).thenThrow(new InvalidContentException("That content was invalid")); + assertThat(underTest.handle(context).getStatus().getCode(), is(HttpStatus.NOT_FOUND)); + } + + @Test + public void handleInvalidContentPut() throws Exception { + when(request.getAction()).thenReturn("PUT"); + when(context.proceed()).thenThrow(new InvalidContentException("That content was invalid")); + assertThat(underTest.handle(context).getStatus().getCode(), is(HttpStatus.BAD_REQUEST)); + } + + @Test + public void handleOModificationOperationProhibited() throws Exception { + when(context.proceed()).thenThrow(frozenException); + assertThat(underTest.handle(context).getStatus().getCode(), is(HttpStatus.SERVICE_UNAVAILABLE)); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/HandlerContributorTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/HandlerContributorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4bbd7c05c33be8158e90c4bbf5cb35e3fef552c6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/HandlerContributorTest.java @@ -0,0 +1,67 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.handlers; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.collect.AttributesMap; +import org.sonatype.nexus.repository.http.HttpResponses; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.repository.view.Context; +import org.sonatype.nexus.repository.view.Request; +import org.sonatype.nexus.repository.view.Response; +import org.sonatype.nexus.repository.view.payloads.StringPayload; + +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import static java.util.Arrays.asList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * UT for {@link HandlerContributor}. + */ +public class HandlerContributorTest + extends TestSupport +{ + @Test + public void addContributedHandlers() throws Exception { + final AttributesMap attributes = new AttributesMap(); + + final Request request = mock(Request.class); + final Context context = mock(Context.class); + + when(context.getRequest()).thenReturn(request); + when(context.getAttributes()).thenReturn(attributes); + when(context.proceed()).thenReturn(HttpResponses.ok(new Content(new StringPayload("test", "text/plain")))); + + final ContributedHandler handlerA = mock(ContributedHandler.class); + final ContributedHandler handlerB = mock(ContributedHandler.class); + final HandlerContributor underTest = new HandlerContributor(asList(handlerA, handlerB)); + final Response response = underTest.handle(context); + + assertThat(response.getStatus().isSuccessful(), is(true)); + assertThat(attributes.get(HandlerContributor.EXTENDED_MARKER), is(Boolean.TRUE)); + + // Handle a second time to ensure the contributed handlers aren't injected twice + underTest.handle(context); + + ArgumentCaptor handlerCaptor = ArgumentCaptor.forClass(ContributedHandler.class); + verify(context, times(2)).insertHandler(handlerCaptor.capture()); + assertThat(handlerCaptor.getAllValues(), is(asList(handlerB, handlerA))); // Intentionally added in "reverse" order + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/LiteralMatcherTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/LiteralMatcherTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..8df8837337074c97987426e0932b15b746caf711 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/LiteralMatcherTest.groovy @@ -0,0 +1,40 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.view.Context +import org.sonatype.nexus.repository.view.Request + +import org.junit.Test + +import static org.mockito.Mockito.mock + +/** + * Tests for {@link LiteralMatcher}. + */ +class LiteralMatcherTest + extends TestSupport +{ + private Context context(String path) { + return new Context(mock(Repository.class), new Request.Builder().action('GET').path(path).build()) + } + + @Test + void 'basic'() { + def underTest = new LiteralMatcher('foo') + assert underTest.matches(context('foo')) + assert !underTest.matches(context('bar')) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/RegexMatcherTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/RegexMatcherTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..601ef8ef2ae3f4d2715b638dc91181d157509c84 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/RegexMatcherTest.groovy @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.view.Context +import org.sonatype.nexus.repository.view.Request + +import org.junit.Test + +import static org.mockito.Mockito.mock + +/** + * Tests for {@link RegexMatcher}. + */ +class RegexMatcherTest + extends TestSupport +{ + private Context context(String path) { + return new Context(mock(Repository.class), new Request.Builder().action('GET').path(path).build()) + } + + @Test + void 'basic'() { + def underTest = new RegexMatcher('foo.*bar') + assert underTest.matches(context('foobar')) + assert underTest.matches(context('fooooooobar')) + assert !underTest.matches(context('foobarbaz')) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/SuffixMatcherTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/SuffixMatcherTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..8c560ecb9452a558e987a7dd59237a952887b8f0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/SuffixMatcherTest.groovy @@ -0,0 +1,40 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.view.Context +import org.sonatype.nexus.repository.view.Request + +import org.junit.Test + +import static org.mockito.Mockito.mock + +/** + * Tests for {@link SuffixMatcher}. + */ +class SuffixMatcherTest + extends TestSupport +{ + private Context context(String path) { + return new Context(mock(Repository.class), new Request.Builder().action('GET').path(path).build()) + } + + @Test + void 'basic'() { + def underTest = new SuffixMatcher('/') + assert underTest.matches(context('foo/')) + assert !underTest.matches(context('foo/index.html')) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/logic/AndMatcherTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/logic/AndMatcherTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..c7e6953a6dd37cd8475d68b33113f4b12fcc58e2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/logic/AndMatcherTest.groovy @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.logic + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.view.Context +import org.sonatype.nexus.repository.view.Request +import org.sonatype.nexus.repository.view.matchers.RegexMatcher + +import org.junit.Test + +import static org.mockito.Mockito.mock + +/** + * Tests for {@link AndMatcher}. + */ +class AndMatcherTest + extends TestSupport +{ + private Context context(String path) { + return new Context(mock(Repository.class), new Request.Builder().action('GET').path(path).build()) + } + + @Test + void 'AND 2 matchers'() { + def underTest = new AndMatcher([ + new RegexMatcher('foo.*'), + new RegexMatcher('.*bar') + ]) + + assert underTest.matches(context('foobar')) + assert underTest.matches(context('fooooooobar')) + assert !underTest.matches(context('foobarbaz')) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/logic/NotMatcherTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/logic/NotMatcherTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..1af258646accfde4f1228d9e4aeb1f00be60c1ff --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/logic/NotMatcherTest.groovy @@ -0,0 +1,44 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.logic + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.view.Context +import org.sonatype.nexus.repository.view.Request +import org.sonatype.nexus.repository.view.matchers.LiteralMatcher + +import org.junit.Test + +import static org.mockito.Mockito.mock + +/** + * Tests for {@link NotMatcher}. + */ +class NotMatcherTest + extends TestSupport +{ + private Context context(String path) { + return new Context(mock(Repository.class), new Request.Builder().action('GET').path(path).build()) + } + + @Test + void 'NOT 2 matchers'() { + def underTest = new NotMatcher( + new LiteralMatcher('foo') + ) + + assert !underTest.matches(context('foo')) + assert underTest.matches(context('bar')) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/logic/OrMatcherTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/logic/OrMatcherTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..c9cf551ffabb1fb8eb624e6c82a18e0f06b8c0c5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/logic/OrMatcherTest.groovy @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.logic + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.view.Context +import org.sonatype.nexus.repository.view.Request +import org.sonatype.nexus.repository.view.matchers.LiteralMatcher + +import org.junit.Test + +import static org.mockito.Mockito.mock + +/** + * Tests for {@link OrMatcher}. + */ +class OrMatcherTest + extends TestSupport +{ + private Context context(String path) { + return new Context(mock(Repository.class), new Request.Builder().action('GET').path(path).build()) + } + + @Test + void 'OR 2 matchers'() { + def underTest = new OrMatcher([ + new LiteralMatcher('foo'), + new LiteralMatcher('bar') + ]) + + assert underTest.matches(context('foo')) + assert underTest.matches(context('bar')) + assert !underTest.matches(context('baz')) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/token/PatternParserFragmentReadingTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/token/PatternParserFragmentReadingTest.java new file mode 100644 index 0000000000000000000000000000000000000000..86f495a44929646ff767c257803ebfb7060ccdce --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/token/PatternParserFragmentReadingTest.java @@ -0,0 +1,70 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.token; + +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.Collections; +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Test; + +import static java.util.Arrays.asList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.sonatype.nexus.repository.view.matchers.token.PatternParser.readFragment; + +public class PatternParserFragmentReadingTest + extends TestSupport +{ + public static final String WILD = ".+"; + + public static final List NONE = Collections.emptyList(); + + @Test + public void readLiteralFragment() { + final String s = readFragment(it("abcdefg"), asList('-'), asList('-'), NONE); + + assertThat(s, is(equalTo("abcdefg"))); + } + + @Test + public void readTerminatedFragment() { + final String s = readFragment(it("abc-defg"), asList('-'), asList('\\'), NONE); + assertThat(s, is(equalTo("abc"))); + } + + @Test + public void readEscapedFragment() { + final String s = readFragment(it("abc\\-d-efg"), asList('-'), asList('\\'), NONE); + assertThat(s, is(equalTo("abc-d"))); + } + + @Test(expected = IllegalArgumentException.class) + public void readDisallowedCharacter() { + readFragment(it("s*fs"), NONE, NONE, asList('*')); + } + + @Test(expected = IllegalArgumentException.class) + public void escapingDoesNotProtectDisallowedCharacters() { + + readFragment(it("s\\*fs"), NONE, asList('\\'), asList('*')); + } + + private CharacterIterator it(final String sdfs) { + return new StringCharacterIterator(sdfs); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/token/PatternParserTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/token/PatternParserTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4a0ce6ce2b2ff498f9065bd596f66166df72c527 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/token/PatternParserTest.java @@ -0,0 +1,77 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.token; + +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Test; + +import static java.util.Arrays.asList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +/** + * Tests for {@link PatternParser}. + */ +public class PatternParserTest + extends TestSupport +{ + public static final String DEFAULT = PatternParser.DEFAULT_VARIABLE_REGEXP; + + @Test + public void parseSimplePatterns() { + checkTokenDetection("asdfasdf", lit("asdfasdf")); + checkTokenDetection("/{a}", lit("/"), var("a", DEFAULT)); + checkTokenDetection("/{a}/{b}", lit("/"), var("a", DEFAULT), lit("/"), var("b", DEFAULT)); + checkTokenDetection("{chocolate}/{build}-{version}.{extension}", var("chocolate", DEFAULT), lit("/"), + var("build", DEFAULT), lit("-"), var("version", DEFAULT), lit("."), var("extension", DEFAULT)); + } + + @Test + public void parseRegexp() { + // A simple regexp variable that matches everything + checkTokenDetection("{name:.*}", var("name", ".*")); + + // A variable that matches only letters + checkTokenDetection("{letters:[A-Za-z]*}", var("letters", "[A-Za-z]*")); + } + + @Test(expected = IllegalArgumentException.class) + public void parsingUnfinishedVariableFails() { + parse("{"); + } + + public void parseEscapedVariableStartAsLiteral() { + checkTokenDetection("\\{hi}", lit("\\{hi}")); + } + + private void checkTokenDetection(final String pattern, Token... expectedTokens) { + final PatternParser parser = parse(pattern); + final List tokens = parser.getTokens(); + + assertThat(tokens, is(equalTo(asList(expectedTokens)))); + } + + private PatternParser parse(final String pattern) {return new PatternParser(pattern);} + + private static Token var(String name, String regexp) { + return new VariableToken(name, regexp); + } + + private static Token lit(String value) { + return new LiteralToken(value); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/token/TokenParserTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/token/TokenParserTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e0ab62b2e337ad4891942194446667282791af89 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/token/TokenParserTest.java @@ -0,0 +1,173 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.token; + +import java.util.Map; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; + +/** + * Test for {@link TokenParser}. + */ +public class TokenParserTest + extends TestSupport +{ + @Test + public void simplePattern() { + String pattern = "/{a}/{b}/{c}"; + + final TokenParser parser = new TokenParser(pattern); + log(parser); + + final Map tokens = parser.parse("/yes/no/false"); + + assertThat(tokens, is(notNullValue())); + assertThat(tokens.entrySet(), hasSize(3)); + + assertThat(tokens.get("a"), is(equalTo("yes"))); + assertThat(tokens.get("b"), is(equalTo("no"))); + assertThat(tokens.get("c"), is(equalTo("false"))); + } + + @Test + public void mavenLikeTemplate() { + final String pattern = "/{group}/{module}/{version}/{name}-{version}.{ext}"; + final TokenParser parser = new TokenParser(pattern); + log(parser); + + final Map tokens = parser.parse("/foo/bar/1234/bar-1234.zip"); + + assertThat(tokens, is(notNullValue())); + assertThat(tokens.entrySet(), hasSize(5)); + + assertThat(tokens.get("group"), is(equalTo("foo"))); + assertThat(tokens.get("module"), is(equalTo("bar"))); + assertThat(tokens.get("version"), is(equalTo("1234"))); + assertThat(tokens.get("name"), is(equalTo("bar"))); + assertThat(tokens.get("ext"), is(equalTo("zip"))); + } + + @Test + public void mavenGroupsHaveMultiplePathSegments() { + final String pattern = "/{group:.+}/{module}/{version}/{name}-{version}.{ext}"; + final TokenParser parser = new TokenParser(pattern); + log(parser); + + final Map tokens = parser.parse("/org/sonatype/nexus/components/1234/bar-1234.zip"); + + assertThat(tokens, is(notNullValue())); + assertThat(tokens.entrySet(), hasSize(5)); + + assertThat(tokens.get("group"), is(equalTo("org/sonatype/nexus"))); + assertThat(tokens.get("module"), is(equalTo("components"))); + assertThat(tokens.get("version"), is(equalTo("1234"))); + assertThat(tokens.get("name"), is(equalTo("bar"))); + assertThat(tokens.get("ext"), is(equalTo("zip"))); + } + + @Test + public void testNugetOperations() { + final String pattern = "/{operation}({paramString:.*})"; + final TokenParser parser = new TokenParser(pattern); + log(parser); + + final Map tokens = parser.parse("/Packages()"); + + assertThat(tokens, is(notNullValue())); + assertThat(tokens.entrySet(), hasSize(2)); + + assertThat(tokens.get("operation"), is(equalTo("Packages"))); + assertThat(tokens.get("paramString"), is(equalTo(""))); + } + + @Test + public void testOptionalParens() { + final String pattern = "/{operation:[^/()]+}{parens:\\\\Q()\\\\E|}"; + final TokenParser parser = new TokenParser(pattern); + log(parser); + + { + final Map tokens = parser.parse("/Packages"); + + assertThat(tokens, notNullValue()); + assertThat(tokens.entrySet(), hasSize(2)); + + assertThat(tokens.get("operation"), equalTo("Packages")); + assertThat(tokens.get("parens"), equalTo("")); + } + { + final Map tokens = parser.parse("/Packages()"); + + assertThat(tokens, notNullValue()); + assertThat(tokens.entrySet(), hasSize(2)); + + assertThat(tokens.get("operation"), equalTo("Packages")); + assertThat(tokens.get("parens"), equalTo("()")); + } + + assertThat(parser.parse("/Packages("), nullValue()); + + assertThat(parser.parse("/Packages)"), nullValue()); + + assertThat(parser.parse("/Packages(foobar)"), nullValue()); + } + + @Test + public void slashesInTheSecondGroup() { + final String pattern = "/{singleSegment}/{manySegments:.+}"; + final TokenParser parser = new TokenParser(pattern); + log(parser); + + final Map tokens = parser.parse("/1/2/3/4/5"); + + assertThat(tokens, is(notNullValue())); + assertThat(tokens.entrySet(), hasSize(2)); + + assertThat(tokens.get("singleSegment"), is(equalTo("1"))); + assertThat(tokens.get("manySegments"), is(equalTo("2/3/4/5"))); + } + + @Test + public void sameNamedVariables() { + final String pattern = "/{group:.+}/{name}/{version}/{name}-{version}.{ext}"; + final TokenParser parser = new TokenParser(pattern); + log(parser); + + Map tokens; + + // matching regexp but groups are unrelated + tokens = parser.parse("/org/eclipse/jetty/jetty-io/maven-metadata.xml"); + assertThat(tokens, is(nullValue())); + + // matching regexp but jetty-io1 vs jetty-io2 + tokens = parser.parse("/org/eclipse/jetty/jetty-io1/8.1.16.v20140903/jetty-io2-8.1.16.v20140903.pom"); + assertThat(tokens, is(nullValue())); + + tokens = parser.parse("/org/eclipse/jetty/jetty-io/8.1.16.v20140903/jetty-io-8.1.16.v20140903.pom"); + assertThat(tokens, is(notNullValue())); + assertThat(tokens.entrySet(), hasSize(4)); + assertThat(tokens.get("group"), is(equalTo("org/eclipse/jetty"))); + assertThat(tokens.get("name"), is(equalTo("jetty-io"))); + assertThat(tokens.get("version"), is(equalTo("8.1.16.v20140903"))); + assertThat(tokens.get("ext"), is(equalTo("pom"))); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/token/TokenTest.java b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/token/TokenTest.java new file mode 100644 index 0000000000000000000000000000000000000000..18e30652f4b62a912fbe3a5d2871d7db752668ef --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/matchers/token/TokenTest.java @@ -0,0 +1,39 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.view.matchers.token; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link Token}. + */ +public class TokenTest + extends TestSupport +{ + @Test + public void literalToken() { + checkRegexpForm("/", "\\Q/\\E"); + checkRegexpForm("/--a", "\\Q/--a\\E"); + checkRegexpForm("[]", "\\Q[]\\E"); + } + + private void checkRegexpForm(final String token, final String regexp) { + assertThat(new LiteralToken(token).toRegexp(), is(equalTo(regexp))); + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/webhooks/GlobalRepositoryWebhookTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/webhooks/GlobalRepositoryWebhookTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..4f12b412d8144dc6d7cbbfa24eade36aec15f762 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/webhooks/GlobalRepositoryWebhookTest.groovy @@ -0,0 +1,129 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.webhooks + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.audit.InitiatorProvider +import org.sonatype.nexus.common.event.EventManager +import org.sonatype.nexus.common.node.NodeAccess +import org.sonatype.nexus.repository.Format +import org.sonatype.nexus.repository.Repository +import org.sonatype.nexus.repository.RepositoryEvent +import org.sonatype.nexus.repository.manager.RepositoryCreatedEvent +import org.sonatype.nexus.repository.manager.RepositoryDeletedEvent +import org.sonatype.nexus.repository.manager.RepositoryUpdatedEvent +import org.sonatype.nexus.repository.types.ProxyType +import org.sonatype.nexus.repository.webhooks.GlobalRepositoryWebhook.RepositoryWebhookPayload +import org.sonatype.nexus.webhooks.WebhookRequestSendEvent + +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentCaptor +import org.mockito.Mock + +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when + +class GlobalRepositoryWebhookTest + extends TestSupport +{ + private GlobalRepositoryWebhook globalRepositoryWebhook + + @Mock + private EventManager eventManager + + @Mock + private InitiatorProvider initiatorProvider + + @Mock + private NodeAccess nodeAccess + + @Mock + private RepositoryCreatedEvent repositoryCreatedEvent + + @Mock + private RepositoryUpdatedEvent repositoryUpdatedEvent + + @Mock + private RepositoryDeletedEvent repositoryDeletedEvent + + public class TestFormat + extends Format + { + public TestFormat() { + super('format') + } + } + + @Before + public void before() { + globalRepositoryWebhook = new GlobalRepositoryWebhook( + eventManager: eventManager, + initiatorProvider: initiatorProvider, + nodeAccess: nodeAccess + ) + + def configuration = mock(RepositoryWebhook.Configuration.class) + when(configuration.repository).thenReturn('repoName') + globalRepositoryWebhook.subscribe(configuration) + + when(initiatorProvider.get()).thenReturn('initiator') + when(nodeAccess.id).thenReturn('nodeId') + + for (RepositoryEvent repositoryEvent : [repositoryCreatedEvent, repositoryUpdatedEvent, repositoryDeletedEvent]) { + def repository = mock(Repository.class) + + when(repositoryEvent.getRepository()).thenReturn(repository) + when(repository.name).thenReturn('name') + when(repository.format).thenReturn(new TestFormat()) + when(repository.type).thenReturn(new ProxyType()) + } + } + + @Test + public void 'has the correct event id'() { + assert globalRepositoryWebhook.id == "rm:global:repository" + } + + @Test + public void 'queues repository created events'() { + testRepositoryEvent(repositoryCreatedEvent, 'CREATED') + } + + @Test + public void 'queues repository updated events'() { + testRepositoryEvent(repositoryUpdatedEvent, 'UPDATED') + } + + @Test + public void 'queues repository deleted events'() { + testRepositoryEvent(repositoryDeletedEvent, 'DELETED') + } + + private void testRepositoryEvent(RepositoryEvent repositoryEvent, String type) { + globalRepositoryWebhook.on(repositoryEvent) + + def assetArgumentCaptor = new ArgumentCaptor() + verify(eventManager).post(assetArgumentCaptor.capture()) + + def repositoryPayload = (RepositoryWebhookPayload) assetArgumentCaptor.value.request.payload + + assert repositoryPayload.initiator == 'initiator' + assert repositoryPayload.nodeId == 'nodeId' + assert repositoryPayload.action.toString() == type + assert repositoryPayload.repository.name == 'name' + assert repositoryPayload.repository.format == 'format' + assert repositoryPayload.repository.type == 'proxy' + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/webhooks/RepositoryAssetWebhookTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/webhooks/RepositoryAssetWebhookTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..5d0bb63301e9a1a357d0ff72d00c1334b3cddcbe --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/webhooks/RepositoryAssetWebhookTest.groovy @@ -0,0 +1,154 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.webhooks + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.audit.InitiatorProvider +import org.sonatype.nexus.common.entity.EntityId +import org.sonatype.nexus.common.entity.EntityMetadata +import org.sonatype.nexus.common.event.EventManager +import org.sonatype.nexus.common.node.NodeAccess +import org.sonatype.nexus.repository.storage.Asset +import org.sonatype.nexus.repository.storage.AssetCreatedEvent +import org.sonatype.nexus.repository.storage.AssetDeletedEvent +import org.sonatype.nexus.repository.storage.AssetEvent +import org.sonatype.nexus.repository.storage.AssetUpdatedEvent +import org.sonatype.nexus.repository.webhooks.RepositoryAssetWebhook.RepositoryAssetWebhookPayload +import org.sonatype.nexus.webhooks.WebhookRequestSendEvent + +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentCaptor +import org.mockito.Mock + +import static org.mockito.Matchers.any +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.times +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when + +class RepositoryAssetWebhookTest + extends TestSupport +{ + private RepositoryAssetWebhook repositoryAssetWebhook + + @Mock + private EventManager eventManager + + @Mock + private InitiatorProvider initiatorProvider + + @Mock + private NodeAccess nodeAccess + + @Mock + private AssetCreatedEvent assetCreatedEvent + + @Mock + private AssetUpdatedEvent assetUpdatedEvent + + @Mock + private AssetDeletedEvent assetDeletedEvent + + @Before + public void before() { + repositoryAssetWebhook = new RepositoryAssetWebhook( + eventManager: eventManager, + initiatorProvider: initiatorProvider, + nodeAccess: nodeAccess + ) + + def configuration = mock(RepositoryWebhook.Configuration.class) + when(configuration.repository).thenReturn('repoName') + repositoryAssetWebhook.subscribe(configuration) + + when(initiatorProvider.get()).thenReturn('initiator') + when(nodeAccess.id).thenReturn('nodeId') + + for (AssetEvent assetEvent : [assetCreatedEvent, assetUpdatedEvent, assetDeletedEvent]) { + def asset = mock(Asset.class) + def entityMetadata = mock(EntityMetadata.class) + def entityId = mock(EntityId.class) + + when(assetEvent.getRepositoryName()).thenReturn('repoName') + when(assetEvent.getAsset()).thenReturn(asset) + when(asset.name()).thenReturn('name') + when(asset.format()).thenReturn('format') + when(asset.entityMetadata).thenReturn(entityMetadata) + when(entityMetadata.id).thenReturn(entityId) + when(entityId.value).thenReturn('id') + } + } + + @Test + public void 'has the correct event id'() { + assert repositoryAssetWebhook.id == "rm:repository:asset" + } + + @Test + public void 'queues local asset created events'() { + testLocalAssetEvent(assetCreatedEvent, 'CREATED') + } + + @Test + public void 'queues local asset updated events'() { + testLocalAssetEvent(assetUpdatedEvent, 'UPDATED') + } + + @Test + public void 'queues local asset deleted events'() { + testLocalAssetEvent(assetDeletedEvent, 'DELETED') + } + + @Test + public void 'ignores non local asset created events'() { + testNonLocalAssetEvent(assetCreatedEvent) + } + + @Test + public void 'ignores non local asset updated events'() { + testNonLocalAssetEvent(assetUpdatedEvent) + } + + @Test + public void 'ignores non local asset deleted events'() { + testNonLocalAssetEvent(assetDeletedEvent) + } + + private void testLocalAssetEvent(AssetEvent assetEvent, String type) { + when(assetEvent.isLocal()).thenReturn(true) + + repositoryAssetWebhook.on(assetEvent) + + def assetArgumentCaptor = new ArgumentCaptor() + verify(eventManager).post(assetArgumentCaptor.capture()) + + def assetPayload = (RepositoryAssetWebhookPayload) assetArgumentCaptor.value.request.payload + + assert assetPayload.initiator == 'initiator' + assert assetPayload.nodeId == 'nodeId' + assert assetPayload.action.toString() == type + assert assetPayload.repositoryName == 'repoName' + assert assetPayload.asset.name == 'name' + assert assetPayload.asset.format == 'format' + assert assetPayload.asset.id == 'id' + } + + private void testNonLocalAssetEvent(AssetEvent assetEvent) { + when(assetEvent.isLocal()).thenReturn(false) + + repositoryAssetWebhook.on(assetEvent) + + verify(eventManager, times(0)).post(any(WebhookRequestSendEvent.class)) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/webhooks/RepositoryComponentWebhookTest.groovy b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/webhooks/RepositoryComponentWebhookTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..5c9eeb1a5761dd225e036080455232838c8ec4c5 --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/webhooks/RepositoryComponentWebhookTest.groovy @@ -0,0 +1,158 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.webhooks + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.audit.InitiatorProvider +import org.sonatype.nexus.common.entity.EntityId +import org.sonatype.nexus.common.entity.EntityMetadata +import org.sonatype.nexus.common.event.EventManager +import org.sonatype.nexus.common.node.NodeAccess +import org.sonatype.nexus.repository.storage.Component +import org.sonatype.nexus.repository.storage.ComponentCreatedEvent +import org.sonatype.nexus.repository.storage.ComponentDeletedEvent +import org.sonatype.nexus.repository.storage.ComponentEvent +import org.sonatype.nexus.repository.storage.ComponentUpdatedEvent +import org.sonatype.nexus.repository.webhooks.RepositoryComponentWebhook.RepositoryComponentWebhookPayload +import org.sonatype.nexus.webhooks.WebhookRequestSendEvent + +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentCaptor +import org.mockito.Mock + +import static org.mockito.Matchers.any +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.times +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when + +class RepositoryComponentWebhookTest + extends TestSupport +{ + private RepositoryComponentWebhook repositoryComponentWebhook + + @Mock + private EventManager eventManager + + @Mock + private InitiatorProvider initiatorProvider + + @Mock + private NodeAccess nodeAccess + + @Mock + private ComponentCreatedEvent componentCreatedEvent + + @Mock + private ComponentUpdatedEvent componentUpdatedEvent + + @Mock + private ComponentDeletedEvent componentDeletedEvent + + @Before + public void before() { + repositoryComponentWebhook = new RepositoryComponentWebhook( + eventManager: eventManager, + initiatorProvider: initiatorProvider, + nodeAccess: nodeAccess + ) + + def configuration = mock(RepositoryWebhook.Configuration.class) + when(configuration.repository).thenReturn('repoName') + repositoryComponentWebhook.subscribe(configuration) + + when(initiatorProvider.get()).thenReturn('initiator') + when(nodeAccess.id).thenReturn('nodeId') + + for (ComponentEvent componentEvent : [componentCreatedEvent, componentUpdatedEvent, componentDeletedEvent]) { + def component = mock(Component.class) + def entityMetadata = mock(EntityMetadata.class) + def entityId = mock(EntityId.class) + + when(componentEvent.getRepositoryName()).thenReturn('repoName') + when(componentEvent.getComponent()).thenReturn(component) + when(component.name()).thenReturn('name') + when(component.format()).thenReturn('format') + when(component.group()).thenReturn('group') + when(component.version()).thenReturn('version') + when(component.entityMetadata).thenReturn(entityMetadata) + when(entityMetadata.id).thenReturn(entityId) + when(entityId.value).thenReturn('id') + } + } + + @Test + public void 'has the correct event id'() { + assert repositoryComponentWebhook.id == "rm:repository:component" + } + + @Test + public void 'queues local component created events'() { + testLocalComponentEvent(componentCreatedEvent, 'CREATED') + } + + @Test + public void 'queues local component updated events'() { + testLocalComponentEvent(componentUpdatedEvent, 'UPDATED') + } + + @Test + public void 'queues local component deleted events'() { + testLocalComponentEvent(componentDeletedEvent, 'DELETED') + } + + @Test + public void 'ignores non local component created events'() { + testNonLocalAssetEvent(componentCreatedEvent) + } + + @Test + public void 'ignores non local component updated events'() { + testNonLocalAssetEvent(componentUpdatedEvent) + } + + @Test + public void 'ignores non local component deleted events'() { + testNonLocalAssetEvent(componentDeletedEvent) + } + + private void testLocalComponentEvent(ComponentEvent componentEvent, String type) { + when(componentEvent.isLocal()).thenReturn(true) + + repositoryComponentWebhook.on(componentEvent) + + def argumentCaptor = new ArgumentCaptor() + verify(eventManager).post(argumentCaptor.capture()) + + def componentPayload = (RepositoryComponentWebhookPayload) argumentCaptor.value.request.payload + + assert componentPayload.initiator == 'initiator' + assert componentPayload.nodeId == 'nodeId' + assert componentPayload.action.toString() == type + assert componentPayload.repositoryName == 'repoName' + assert componentPayload.component.name == 'name' + assert componentPayload.component.format == 'format' + assert componentPayload.component.group == 'group' + assert componentPayload.component.version == 'version' + assert componentPayload.component.id == 'id' + } + + private void testNonLocalAssetEvent(ComponentEvent componentEvent) { + when(componentEvent.isLocal()).thenReturn(false) + + repositoryComponentWebhook.on(componentEvent) + + verify(eventManager, times(0)).post(any(WebhookRequestSendEvent.class)) + } +} diff --git a/thirdparty-bundles/components/nexus-repository/src/test/resources/logback-test.xml b/thirdparty-bundles/components/nexus-repository/src/test/resources/logback-test.xml new file mode 100644 index 0000000000000000000000000000000000000000..baf8a0c98193eb7260b1d827eaf1caf8e34bf1da --- /dev/null +++ b/thirdparty-bundles/components/nexus-repository/src/test/resources/logback-test.xml @@ -0,0 +1,17 @@ + + + + + System.out + + %date %level [%thread%X{DC}] %logger - %msg%n + + + + + + + + + + diff --git a/thirdparty-bundles/components/nexus-rest-client/pom.xml b/thirdparty-bundles/components/nexus-rest-client/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..bd17b42c95713bf6d98a9b366237c68b8a5a954b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest-client/pom.xml @@ -0,0 +1,52 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.8.0-02 + + + nexus-rest-client + ${project.groupId}:${project.artifactId} + bundle + + + + org.sonatype.nexus + nexus-common + + + + org.sonatype.nexus + nexus-httpclient + + + + org.jboss.resteasy + resteasy-client + + + + org.jboss.resteasy + resteasy-multipart-provider + + + + diff --git a/thirdparty-bundles/components/nexus-rest-client/src/main/java/org/sonatype/nexus/rest/client/RestClientConfiguration.java b/thirdparty-bundles/components/nexus-rest-client/src/main/java/org/sonatype/nexus/rest/client/RestClientConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..4bc14c70a987b9d79749193581f1b4b42f8d969f --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest-client/src/main/java/org/sonatype/nexus/rest/client/RestClientConfiguration.java @@ -0,0 +1,78 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest.client; + +import java.util.function.Supplier; + +import javax.annotation.Nullable; +import javax.ws.rs.core.Configurable; + +import org.apache.http.client.HttpClient; + +/** + * REST client configuration. + * + * @since 3.1 + */ +public class RestClientConfiguration +{ + // NOTE: Exposing JAX-RS api here to simplify advanced client semantics + + public interface Customizer + { + void apply(Configurable builder); + } + + public static final RestClientConfiguration DEFAULTS = new RestClientConfiguration(null, null, false); + + private final Supplier httpClient; + + private final Customizer customizer; + + private final boolean useTrustStore; + + private RestClientConfiguration(@Nullable final Supplier httpClient, + @Nullable final Customizer customizer, + final boolean useTrustStore) + { + this.httpClient = httpClient; + this.customizer = customizer; + this.useTrustStore = useTrustStore; + } + + public RestClientConfiguration withHttpClient(@Nullable final Supplier httpClient) { + return new RestClientConfiguration(httpClient, customizer, useTrustStore); + } + + @Nullable + public Supplier getHttpClient() { + return httpClient; + } + + public RestClientConfiguration withCustomizer(@Nullable final Customizer customizer) { + return new RestClientConfiguration(httpClient, customizer, useTrustStore); + } + + @Nullable + public Customizer getCustomizer() { + return customizer; + } + + public RestClientConfiguration withUseTrustStore(final boolean useTrustStore) { + return new RestClientConfiguration(httpClient, customizer, useTrustStore); + } + + public boolean getUseTrustStore() { + return useTrustStore; + } +} diff --git a/thirdparty-bundles/components/nexus-rest-client/src/main/java/org/sonatype/nexus/rest/client/RestClientFactory.java b/thirdparty-bundles/components/nexus-rest-client/src/main/java/org/sonatype/nexus/rest/client/RestClientFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3c27655a83ab2dc34478bdfc493196d369c48fb0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest-client/src/main/java/org/sonatype/nexus/rest/client/RestClientFactory.java @@ -0,0 +1,39 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest.client; + +import java.net.URI; + +import javax.ws.rs.client.Client; + +/** + * REST client factory. + * + * @since 3.0 + */ +public interface RestClientFactory +{ + + Client create(RestClientConfiguration configuration); + + default Client create() { + return create(RestClientConfiguration.DEFAULTS); + } + + /** + * Returns a proxy backed by the given client and URI. + * + * @since 3.2.1 + */ + T proxy(Class api, Client client, URI baseUri); +} diff --git a/thirdparty-bundles/components/nexus-rest-client/src/main/java/org/sonatype/nexus/rest/client/internal/BridgeClassLoader.java b/thirdparty-bundles/components/nexus-rest-client/src/main/java/org/sonatype/nexus/rest/client/internal/BridgeClassLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..fa837c0bd88e5639a304b2f4af289a673571d138 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest-client/src/main/java/org/sonatype/nexus/rest/client/internal/BridgeClassLoader.java @@ -0,0 +1,36 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest.client.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * ClassLoader which bridges two classloaders into a single classloader view. + * + * @since 3.2.1 + */ +class BridgeClassLoader + extends ClassLoader +{ + private final ClassLoader secondary; + + public BridgeClassLoader(final ClassLoader primary, final ClassLoader secondary) { + super(checkNotNull(primary)); + this.secondary = checkNotNull(secondary); + } + + @Override + protected Class findClass(final String name) throws ClassNotFoundException { + return secondary.loadClass(name); + } +} diff --git a/thirdparty-bundles/components/nexus-rest-client/src/main/java/org/sonatype/nexus/rest/client/internal/RestClientFactoryImpl.java b/thirdparty-bundles/components/nexus-rest-client/src/main/java/org/sonatype/nexus/rest/client/internal/RestClientFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..47e94facb57847102c3c1a447c5385e7ee60bb35 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest-client/src/main/java/org/sonatype/nexus/rest/client/internal/RestClientFactoryImpl.java @@ -0,0 +1,101 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest.client.internal; + +import java.net.URI; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.WebTarget; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.thread.TcclBlock; +import org.sonatype.nexus.httpclient.SSLContextSelector; +import org.sonatype.nexus.rest.client.RestClientConfiguration; +import org.sonatype.nexus.rest.client.RestClientFactory; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.LoadingCache; +import org.apache.http.client.HttpClient; +import org.apache.http.protocol.BasicHttpContext; +import org.apache.http.protocol.HttpContext; +import org.jboss.resteasy.client.jaxrs.ClientHttpEngine; +import org.jboss.resteasy.client.jaxrs.ProxyBuilder; +import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; +import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.cache.CacheLoader.from; + +/** + * REST client factory. + * + * @since 3.0 + */ +@Named("default") +@Singleton +public class RestClientFactoryImpl + extends ComponentSupport + implements RestClientFactory +{ + private final LoadingCache bridgeClassLoaderCache = + CacheBuilder.newBuilder().build(from( + (loader) -> new BridgeClassLoader(loader, ProxyBuilder.class.getClassLoader()))); + + private final Provider httpClient; + + @Inject + public RestClientFactoryImpl(final Provider httpClient) { + this.httpClient = checkNotNull(httpClient); + } + + @Override + public Client create(final RestClientConfiguration configuration) { + checkNotNull(configuration); + + try (TcclBlock tccl = TcclBlock.begin(ResteasyClientBuilder.class)) { + HttpContext httpContext = new BasicHttpContext(); + if (configuration.getUseTrustStore()) { + httpContext.setAttribute(SSLContextSelector.USE_TRUST_STORE, true); + } + HttpClient client; + if (configuration.getHttpClient() != null) { + client = checkNotNull(configuration.getHttpClient().get()); + } + else { + client = httpClient.get(); + } + ClientHttpEngine httpEngine = new ApacheHttpClient4Engine(client, httpContext); + + ResteasyClientBuilder builder = new ResteasyClientBuilder().httpEngine(httpEngine); + + if (configuration.getCustomizer() != null) { + configuration.getCustomizer().apply(builder); + } + + return builder.build(); + } + } + + @Override + public T proxy(final Class api, final Client client, final URI baseUri) { + WebTarget target = client.target(baseUri); + + return ProxyBuilder.builder(api, target) + .classloader(bridgeClassLoaderCache.getUnchecked(api.getClassLoader())) + .build(); + } +} diff --git a/thirdparty-bundles/components/nexus-rest-jackson2/pom.xml b/thirdparty-bundles/components/nexus-rest-jackson2/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..d2d8130558c3256c571169347ab9281699699488 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest-jackson2/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.8.0-02 + + + nexus-rest-jackson2 + ${project.groupId}:${project.artifactId} + bundle + + + + org.sonatype.nexus + nexus-common + + + + org.sonatype.nexus + nexus-rest + + + + org.jboss.resteasy + resteasy-jackson2-provider + + + + diff --git a/thirdparty-bundles/components/nexus-rest-jackson2/src/main/java/org/sonatype/nexus/rest/jackson2/internal/ObjectMapperProvider.java b/thirdparty-bundles/components/nexus-rest-jackson2/src/main/java/org/sonatype/nexus/rest/jackson2/internal/ObjectMapperProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..a23b7050db6061ef61a66c0edc6ddfab53ad6e09 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest-jackson2/src/main/java/org/sonatype/nexus/rest/jackson2/internal/ObjectMapperProvider.java @@ -0,0 +1,49 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest.jackson2.internal; + +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +/** + * Jackson {@link ObjectMapper} provider for use with Siesta. + * + * @since 3.0 + */ +@Named("siesta") +@Singleton +public class ObjectMapperProvider + extends ComponentSupport + implements Provider +{ + private final ObjectMapper mapper; + + public ObjectMapperProvider() { + this.mapper = new ObjectMapper() + .enable(SerializationFeature.INDENT_OUTPUT) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + } + + @Override + public ObjectMapper get() { + return mapper; + } +} diff --git a/thirdparty-bundles/components/nexus-rest-jackson2/src/main/java/org/sonatype/nexus/rest/jackson2/internal/ObjectMapperResolver.java b/thirdparty-bundles/components/nexus-rest-jackson2/src/main/java/org/sonatype/nexus/rest/jackson2/internal/ObjectMapperResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..5dfa920b5621489dec481df6fea62a1d59c8460a --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest-jackson2/src/main/java/org/sonatype/nexus/rest/jackson2/internal/ObjectMapperResolver.java @@ -0,0 +1,57 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest.jackson2.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.ext.ContextResolver; +import javax.ws.rs.ext.Provider; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.rest.Component; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Jackson {@link ObjectMapper} resolver. + * + * This will use the mapper bound to the name "siesta". + * + * @since 3.0 + * @see ObjectMapperProvider + */ +@Named +@Singleton +@Provider +@Produces(MediaType.APPLICATION_JSON) +public class ObjectMapperResolver + extends ComponentSupport + implements ContextResolver, Component +{ + private final javax.inject.Provider mapperProvider; + + @Inject + public ObjectMapperResolver(@Named("siesta") final javax.inject.Provider mapperProvider) { + this.mapperProvider = checkNotNull(mapperProvider); + } + + @Override + public ObjectMapper getContext(final Class type) { + return mapperProvider.get(); + } +} diff --git a/thirdparty-bundles/components/nexus-rest-jackson2/src/main/java/org/sonatype/nexus/rest/jackson2/internal/package-info.java b/thirdparty-bundles/components/nexus-rest-jackson2/src/main/java/org/sonatype/nexus/rest/jackson2/internal/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..2d2769c5b1dd75f5695f6ad06e1fb4d19bf3a41a --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest-jackson2/src/main/java/org/sonatype/nexus/rest/jackson2/internal/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * REST Jackson v2 integration. + * + * @since 3.0 + */ +package org.sonatype.nexus.rest.jackson2.internal; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rest/pom.xml b/thirdparty-bundles/components/nexus-rest/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..3e79f303160a98c4ec289a13e3bf6c9f377c2b84 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest/pom.xml @@ -0,0 +1,53 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.8.0-02 + + + nexus-rest + ${project.groupId}:${project.artifactId} + bundle + + + + org.sonatype.nexus + nexus-common + + + + org.sonatype.nexus + nexus-validation + + + + javax.ws.rs + javax.ws.rs-api + + + + com.fasterxml.jackson.core + jackson-annotations + true + + + + diff --git a/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/APIConstants.java b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/APIConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..2cd5b92f995967a4b3535553b7230b0c0690b469 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/APIConstants.java @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest; + +/** + * Constants relating to REST APIs. + * + * @since 3.4 + */ +public class APIConstants +{ + private APIConstants() { + } + + public static final String BETA_API_PREFIX = "/beta"; +} diff --git a/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/Component.java b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/Component.java new file mode 100644 index 0000000000000000000000000000000000000000..1934e62f51d07fd563000bf1730f3feee01f44bc --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/Component.java @@ -0,0 +1,23 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest; + +/** + * Marker for Siesta JAX-RS components. + * + * @since 3.0 + */ +public interface Component +{ + // empty +} diff --git a/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/ExceptionMapperSupport.java b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/ExceptionMapperSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..796a61f5d2fed807bbd4685ab74c0a391666429d --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/ExceptionMapperSupport.java @@ -0,0 +1,99 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest; + +import java.util.UUID; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN; + +/** + * Support for {@link ExceptionMapper} implementations. + * + * @since 3.0 + */ +public abstract class ExceptionMapperSupport + implements ExceptionMapper, Component +{ + public static final String X_SIESTA_FAULT_ID = "X-Siesta-FaultId"; + + protected final Logger log = LoggerFactory.getLogger(getClass()); + + public Response toResponse(final E exception) { + checkNotNull(exception); + + // Generate unique identifier + final String id = generateFaultId(); + + // debug/trace log exception details + if (log.isTraceEnabled()) { + log.trace("(ID {}) Mapping exception: " + exception, id, exception); + } + else { + log.debug("(ID {}) Mapping exception: " + exception, id); + } + + // Prepare the response + Response response; + try { + response = convert(exception, id); + } + catch (Exception e) { + log.warn("(ID {}) Failed to map exception", id, e); + response = Response.serverError().entity(new FaultXO(id, e)).build(); + } + + // Add fault-id to the response as header + response.getHeaders().putSingle(X_SIESTA_FAULT_ID, id); + + // Log terse (unless debug enabled) warning with fault details + final Object entity = response.getEntity(); + log.warn("(ID {}) Response: [{}] {}; mapped from: {}", + id, + response.getStatus(), + entity == null ? "(no entity/body)" : String.format("'%s'", entity), + exception, + log.isDebugEnabled() ? exception : null + ); + + return response; + } + + private static String generateFaultId() { + return UUID.randomUUID().toString(); + } + + /** + * Convert the given exception into a response. + * + * @param exception The exception to convert. + * @param id The unique identifier generated for this fault. + */ + protected abstract Response convert(final E exception, final String id); + + protected Response unexpectedResponse(final Throwable exception, final String id) { + // always log unexpected exception with stack + log.warn("(ID {}) Unexpected exception: {}", id, exception.toString(), exception); + + return Response.serverError() + .entity(String.format("ERROR: (ID %s) %s", id, exception)) + .type(TEXT_PLAIN) + .build(); + } +} diff --git a/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/FaultXO.java b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/FaultXO.java new file mode 100644 index 0000000000000000000000000000000000000000..78f6e1bf1c8509718500e9c9d77313c8cf0d6a7a --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/FaultXO.java @@ -0,0 +1,69 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest; + +import javax.xml.bind.annotation.XmlRootElement; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Fault exchange object. + * + * @since 3.0 + */ +@XmlRootElement(name = "fault") +public class FaultXO +{ + @JsonProperty + private String id; + + @JsonProperty + private String message; + + public FaultXO() { + // empty + } + + public FaultXO(final String id, final String message) { + this.id = id; + this.message = message; + } + + public FaultXO(final String id, final Throwable cause) { + this(id, cause.toString()); + } + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } + + public String getMessage() { + return message; + } + + public void setMessage(final String message) { + this.message = message; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "id='" + id + '\'' + + ", message='" + message + '\'' + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/MediaTypes.java b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/MediaTypes.java new file mode 100644 index 0000000000000000000000000000000000000000..3d23258390fba49ab22e0fc516a513dc91160673 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/MediaTypes.java @@ -0,0 +1,39 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest; + +import javax.ws.rs.core.MediaType; + +/** + * Siesta specific media types. + * + * @since 3.0 + */ +public class MediaTypes +{ + private MediaTypes() { + // empty + } + + // application/vnd.siesta-validation-errors-v1+xml + + public static final String VND_VALIDATION_ERRORS_V1_XML = "application/vnd.siesta-validation-errors-v1+xml"; + + public static final MediaType VND_VALIDATION_ERRORS_V1_XML_TYPE = new MediaType("application", "vnd.siesta-validation-errors-v1+xml"); + + // application/vnd.siesta-validation-errors-v1+json + + public static final String VND_VALIDATION_ERRORS_V1_JSON = "application/vnd.siesta-validation-errors-v1+json"; + + public static final MediaType VND_VALIDATION_ERRORS_V1_JSON_TYPE = new MediaType("application", "vnd.siesta-validation-errors-v1+json"); +} diff --git a/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/Page.groovy b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/Page.groovy new file mode 100644 index 0000000000000000000000000000000000000000..79eee1134edb8c1b5c7d2e17cf9c1a3a7d6a7627 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/Page.groovy @@ -0,0 +1,39 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest + +import com.fasterxml.jackson.annotation.JsonProperty +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.builder.Builder + +/** + * An object to hold pages of XOs for use in the REST API. + * + * @since 3.3 + */ +@CompileStatic +@Builder +@EqualsAndHashCode +class Page +{ + List items; + + String continuationToken; + + Page(@JsonProperty('items') final List items, + @JsonProperty('continuationToken') final String continuationToken) { + this.items = items + this.continuationToken = continuationToken + } +} diff --git a/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/Resource.java b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/Resource.java new file mode 100644 index 0000000000000000000000000000000000000000..7e8a112c0e3af0662726bd4a90a854e142e68ede --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/Resource.java @@ -0,0 +1,24 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest; + +/** + * Marker for Siesta JAX-RS resources. + * + * @since 3.0 + */ +public interface Resource + extends Component +{ + // empty +} diff --git a/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/ValidationErrorXO.java b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/ValidationErrorXO.java new file mode 100644 index 0000000000000000000000000000000000000000..5f2995a4a423ec61cbbbfccf63031d942b45541c --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/ValidationErrorXO.java @@ -0,0 +1,127 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest; + +import javax.xml.bind.annotation.XmlRootElement; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Validation error exchange object. + * + * @since 3.0 + */ +@XmlRootElement(name = "validationError") +public class ValidationErrorXO +{ + /** + * Denotes that validation does not applies to a specific value. + */ + public static final String GENERIC = "*"; + + /** + * Identifies the value value that is failing validation. A value of "*" denotes that validation + * does not applies to a specific value. + * + * E.g. "name". + */ + @JsonProperty + private String id; + + /** + * Description of failing validation. + * + * E.g. "Name cannot be null". + */ + @JsonProperty + private String message; + + public ValidationErrorXO() { + this.id = GENERIC; + } + + /** + * Creates a validation error that does not applies to a specific value. + * + * @param message validation description + */ + public ValidationErrorXO(final String message) { + this(GENERIC, message); + } + + /** + * Creates a validation error for a specific value. + * + * @param id identifier of value failing validation. + * @param message validation description + */ + public ValidationErrorXO(final String id, final String message) { + this.id = id == null ? GENERIC : id; + this.message = message; + } + + /** + * @return identifier of value failing validation (never null). A value of "*" denotes that validation does + * not applies to a specific value. + */ + public String getId() { + return id; + } + + /** + * @param id of value failing validation + */ + public void setId(final String id) { + this.id = id == null ? GENERIC : id; + } + + /** + * @param id of value failing validation + * @return itself, for fluent api usage + */ + public ValidationErrorXO withId(final String id) { + setId(id); + return this; + } + + /** + * @return validation description + */ + public String getMessage() { + return message; + } + + /** + * @param message validation description + */ + public void setMessage(final String message) { + this.message = message; + } + + /** + * @param message validation description + * @return itself, for fluent api usage + */ + public ValidationErrorXO withMessage(final String message) { + this.message = message; + return this; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "id='" + id + '\'' + + ", message='" + message + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/ValidationErrorsException.java b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/ValidationErrorsException.java new file mode 100644 index 0000000000000000000000000000000000000000..d775af47654b4e0d89a3583a6042aff42b8f5402 --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/ValidationErrorsException.java @@ -0,0 +1,85 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Thrown when there are request validation errors. + * + * @since 3.0 + * @see ValidationErrorXO + */ +public class ValidationErrorsException + extends RuntimeException +{ + private final List errors = new ArrayList(); + + public ValidationErrorsException() { + super(); + } + + public ValidationErrorsException(final String message) { + errors.add(new ValidationErrorXO(message)); + } + + public ValidationErrorsException(final String id, final String message) { + errors.add(new ValidationErrorXO(id, message)); + } + + public ValidationErrorsException withError(final String message) { + errors.add(new ValidationErrorXO(message)); + return this; + } + + public ValidationErrorsException withError(final String id, final String message) { + errors.add(new ValidationErrorXO(id, message)); + return this; + } + + public ValidationErrorsException withErrors(final ValidationErrorXO... validationErrors) { + checkNotNull(validationErrors); + errors.addAll(Arrays.asList(validationErrors)); + return this; + } + + public ValidationErrorsException withErrors(final List validationErrors) { + checkNotNull(validationErrors); + errors.addAll(validationErrors); + return this; + } + + public List getValidationErrors() { + return errors; + } + + public boolean hasValidationErrors() { + return !errors.isEmpty(); + } + + @Override + public String getMessage() { + final StringBuilder sb = new StringBuilder(); + for (final ValidationErrorXO error : errors) { + if (sb.length() > 0) { + sb.append(", "); + } + sb.append(error.getMessage()); + } + return sb.length() == 0 ? "(No validation errors)" : sb.toString(); + } +} diff --git a/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/WebApplicationMessageException.java b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/WebApplicationMessageException.java new file mode 100644 index 0000000000000000000000000000000000000000..2df60b5bf5c49234ab15a06004da767fd1016d2b --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/WebApplicationMessageException.java @@ -0,0 +1,33 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.rest; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import static com.google.common.base.Preconditions.checkNotNull; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN; + +/** + * {@link WebApplicationException} with {@link Status} and a text message. + * + * @since 3.8 + */ +public class WebApplicationMessageException + extends WebApplicationException +{ + public WebApplicationMessageException(final Status status, final String message) { + super(Response.status(checkNotNull(status)).entity(checkNotNull(message)).type(TEXT_PLAIN).build()); + } +} diff --git a/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/package-info.java b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..fcad0acaea87eb8791ecc99a2c4effc94c71e5ed --- /dev/null +++ b/thirdparty-bundles/components/nexus-rest/src/main/java/org/sonatype/nexus/rest/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * REST framework. + * + * @since 3.0 + */ +package org.sonatype.nexus.rest; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/pom.xml b/thirdparty-bundles/components/nexus-scheduling/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..4ea0ac8399a2e293481bfdfd304b821d725312ed --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/pom.xml @@ -0,0 +1,85 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.8.0-02 + + + nexus-scheduling + ${project.groupId}:${project.artifactId} + bundle + + + + org.sonatype.nexus + nexus-common + + + + org.sonatype.nexus + nexus-formfields + + + + org.sonatype.nexus + nexus-validation + + + + org.sonatype.nexus + nexus-orient + + + + org.sonatype.nexus + nexus-security + + + + org.jboss.resteasy + resteasy-jaxrs + + + + io.swagger + swagger-annotations + + + + org.sonatype.nexus + nexus-rest + + + + org.sonatype.goodies + goodies-testsupport + test + + + + org.spockframework + spock-core + test + + + + + diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/Cancelable.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/Cancelable.java new file mode 100644 index 0000000000000000000000000000000000000000..73965f0e5d8f17fc7219ba8c33f6efc2559f9d3e --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/Cancelable.java @@ -0,0 +1,31 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +/** + * Cancelable interface, that should be implemented by tasks supports cancellation. + * + * @since 3.0 + */ +public interface Cancelable +{ + /** + * Cancels the task. + */ + void cancel(); + + /** + * Returns {@code true} if canceled. + */ + boolean isCanceled(); +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/CancelableHelper.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/CancelableHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..ebdd94e56ab9c06201120b8aeb3bc9689c1fccec --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/CancelableHelper.java @@ -0,0 +1,59 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +import java.util.concurrent.atomic.AtomicBoolean; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Support for per-thread storing of cancelable flag. + * + * Periodically checking the {@link #checkCancellation()} is the preferred way to detect cancellation in components + * outside the tasks. Within task, you have the {@link TaskSupport#isCanceled()} method. + * + * @since 3.0 + */ +public class CancelableHelper +{ + private CancelableHelper() { + // empty + } + + // FIXME: its not clear why we have to install/remove a flag here, vs managing this per-thread. + + private static final ThreadLocal currentFlagHolder = new ThreadLocal<>(); + + public static void set(final AtomicBoolean flag) { + checkNotNull(flag); + currentFlagHolder.set(flag); + } + + public static void remove() { + currentFlagHolder.remove(); + } + + /** + * Throws {@link TaskInterruptedException} if current task is canceled or interrupted. + */ + public static void checkCancellation() { + Thread.yield(); + AtomicBoolean current = currentFlagHolder.get(); + if (current != null && current.get()) { + throw new TaskInterruptedException("Thread '" + Thread.currentThread().getName() + "' is canceled", true); + } + if (Thread.interrupted()) { + throw new TaskInterruptedException("Thread '" + Thread.currentThread().getName() + "' is interrupted", false); + } + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/ClusteredTaskState.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/ClusteredTaskState.java new file mode 100644 index 0000000000000000000000000000000000000000..690dd920ceb5c17383668fab3a101cbe2c4ad561 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/ClusteredTaskState.java @@ -0,0 +1,93 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +import java.util.Date; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.scheduling.TaskInfo.EndState; +import org.sonatype.nexus.scheduling.TaskInfo.RunState; +import org.sonatype.nexus.scheduling.TaskInfo.State; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Describes the basic state of a task on a node within a clustered environment. + * + * @since 3.1 + */ +public class ClusteredTaskState +{ + private final String nodeId; + + private final State state; + + private final RunState runState; + + private final EndState lastEndState; + + private final Date lastRunStarted; + + private final Long lastRunDuration; + + public ClusteredTaskState(String nodeId, + State state, + @Nullable RunState runState, + @Nullable EndState lastEndState, + @Nullable Date lastRunStarted, + @Nullable Long lastRunDuration) + { + this.nodeId = checkNotNull(nodeId); + this.state = checkNotNull(state); + this.runState = runState; + this.lastEndState = lastEndState; + this.lastRunStarted = lastRunStarted; // NOSONAR + this.lastRunDuration = lastRunDuration; + } + + public String getNodeId() { + return nodeId; + } + + public State getState() { + return state; + } + + @Nullable + public RunState getRunState() { + return runState; + } + + @Nullable + public EndState getLastEndState() { + return lastEndState; + } + + @Nullable + public Date getLastRunStarted() { + return lastRunStarted; // NOSONAR + } + + @Nullable + public Long getLastRunDuration() { + return lastRunDuration; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{nodeId=" + nodeId + ", state=" + state + ", runState=" + runState + + ", lastEndState=" + lastEndState + ", lastRunStarted=" + lastRunStarted + ", lastRunDuration=" + + lastRunDuration + "}"; + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/ClusteredTaskStateStore.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/ClusteredTaskStateStore.java new file mode 100644 index 0000000000000000000000000000000000000000..878e0f53443921c1f2600d3770340fb172b107fe --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/ClusteredTaskStateStore.java @@ -0,0 +1,43 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +import java.util.List; + +import javax.annotation.Nullable; + +/** + * Keeps track of task states within a clustered environment. Each node advertises local task states which the store + * distributes across the cluster for all nodes to query. + * + * @since 3.1 + */ +public interface ClusteredTaskStateStore +{ + /** + * Updates the store with the current state of the specified task. + */ + void setLocalState(TaskInfo taskInfo); + + /** + * Removes the state of the specified task from the store. + */ + void removeClusteredState(String taskId); + + /** + * Returns the state of a given task across the nodes in a clustered environment or {@code null} if clustering isn't + * enabled. + */ + @Nullable + List getClusteredState(String taskId); +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/Task.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/Task.java new file mode 100644 index 0000000000000000000000000000000000000000..f0b8cb791be8f43ae13fa49482fa832df9730033 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/Task.java @@ -0,0 +1,62 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +import java.util.concurrent.Callable; + +/** + * The main interface for all Tasks used in Nexus. + * + * All implementations should keep their configuration (if any) in the corresponding {@link TaskConfiguration} object, + * due to persistence implications. Or, the task might source it's configuration from other (injected) component. + * Implementations must not be marked as singletons, container must provide new instance for each execution. + * + * @since 3.0 + */ +public interface Task + extends Callable +{ + /** + * Returns the copy of the task configuration. + * + * Modifying this map has no effect on effective configuration of the task. + */ + TaskConfiguration taskConfiguration(); + + /** + * Configures the task. + * + * @throws IllegalArgumentException if passed in configuration is invalid. + */ + void configure(TaskConfiguration taskConfiguration); + + /** + * Returns a unique ID of the task instance. + * + * Shorthand method for {@link TaskConfiguration#getId()}. + */ + String getId(); + + /** + * Returns a descriptive name of the task instance, usually set by user. + */ + String getName(); + + /** + * Returns short generated message of current task's instance work. + * + * This message is based on task configuration and same typed tasks might emit different messages depending + * on configuration. + */ + String getMessage(); +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskConfiguration.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..999fb9a7e1662fae0648db578a30a6d53a2802d4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskConfiguration.java @@ -0,0 +1,319 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.logging.task.TaskLogInfo; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +// FIXME: Revisit this overly complex configuration container class + +/** + * The task configuration backed by plain map. + * + * The configuration is persisted by actual underlying scheduler, so it MUST contain strings only + * (and string encoded primitives). Still, you can circumvent this primitive configuration by storing some custom + * string as key here, and using that key fetch some custom configuration for your task via some injected component. + * + * As this configuration may get persisted, for simplicity's sake there are some HARD requirements against the + * contents: + * + * For keys: only {@link String}s are accepted, {@code null} keys are NOT accepted. + * + * For values: only {@link String}s are accepted, {@code null} keys are NOT accepted. If you must have {@code null} for + * value, you can use some sentinel value to mark "undefined" state. Still, the best is to not set the mapping at all, + * as that also might be interpret as "unset". + * + * Many of the methods does this: set the key-value is value is non-null, otherwise REMOVE it. + * Also, many getter method accept "default value", that are returned in case mapping of key is not present in the map. + * + * This class is not thread safe. + * + * @since 3.0 + */ +public final class TaskConfiguration + implements TaskLogInfo +{ + // TODO: keys which start with "." are considered "private" for some strange reason + + private static final String ID_KEY = ".id"; + + private static final String NAME_KEY = ".name"; + + private static final String TYPE_ID_KEY = ".typeId"; + + private static final String TYPE_NAME_KEY = ".typeName"; + + private static final String ENABLED_KEY = ".enabled"; + + private static final String VISIBLE_KEY = ".visible"; + + private static final String ALERT_EMAIL_KEY = ".alertEmail"; + + private static final String CREATED_KEY = ".created"; + + private static final String UPDATED_KEY = ".updated"; + + private static final String MESSAGE_KEY = ".message"; + + private final Map configuration; + + public TaskConfiguration() { + this.configuration = new HashMap<>(); + } + + public TaskConfiguration(final TaskConfiguration configuration) { + checkNotNull(configuration); + this.configuration = new HashMap<>(configuration.configuration); + validate(); + } + + public void validate() { + // FIXME: These are state-checks not argument checks! + checkArgument(!Strings.isNullOrEmpty(getId()), "Incomplete task configuration: id"); + checkArgument(!Strings.isNullOrEmpty(getTypeId()), "Incomplete task configuration: typeId"); + for (Entry entry : configuration.entrySet()) { + checkArgument(entry.getKey() instanceof String && entry.getValue() instanceof String, + "Invalid entry in map: %s", configuration); + } + } + + public String getTaskLogName() { + final String name = Strings.isNullOrEmpty(getName()) ? getTypeName() : getName(); + return String.format("'%s' [%s]", name, getTypeId()); + } + + /** + * Copy configuration from given to self. + */ + public TaskConfiguration apply(final TaskConfiguration from) { + checkNotNull(from); + from.validate(); + configuration.putAll(from.configuration); + return this; + } + + public Map asMap() { + return ImmutableMap.copyOf(configuration); + } + + public String toString() { + return configuration.toString(); + } + + // + // Core properties + // + + // FIXME: Some of this screams out for a builer pattern, as we expect things like id to be non-null + // FIXME: and this correctness is only enforced via validate helper + + public String getId() { + return getString(ID_KEY); + } + + public void setId(final String id) { + checkNotNull(id); + configuration.put(ID_KEY, id); + } + + public String getName() { + return getString(NAME_KEY); + } + + public void setName(final String name) { + checkNotNull(name); + configuration.put(NAME_KEY, name); + } + + public String getTypeId() { + return getString(TYPE_ID_KEY); + } + + public void setTypeId(final String typeId) { + checkNotNull(typeId); + configuration.put(TYPE_ID_KEY, typeId); + } + + public String getTypeName() { + return getString(TYPE_NAME_KEY); + } + + public void setTypeName(final String typeName) { + checkNotNull(typeName); + configuration.put(TYPE_NAME_KEY, typeName); + } + + public boolean isEnabled() { + return getBoolean(ENABLED_KEY, true); + } + + public void setEnabled(final boolean enabled) { + configuration.put(ENABLED_KEY, Boolean.toString(enabled)); + } + + public boolean isVisible() { + return getBoolean(VISIBLE_KEY, true); + } + + + public void setVisible(final boolean visible) { + configuration.put(VISIBLE_KEY, Boolean.toString(visible)); + } + + @Nullable + public String getAlertEmail() { + return getString(ALERT_EMAIL_KEY); + } + + public void setAlertEmail(final String email) { + if (Strings.isNullOrEmpty(email)) { + configuration.remove(ALERT_EMAIL_KEY); + } + else { + configuration.put(ALERT_EMAIL_KEY, email); + } + } + + @Nullable + public Date getCreated() { + return getDate(CREATED_KEY, null); + } + + public void setCreated(final Date date) { + checkNotNull(date); + setDate(CREATED_KEY, date); + } + + @Nullable + public Date getUpdated() { + return getDate(UPDATED_KEY, null); + } + + public void setUpdated(final Date date) { + checkNotNull(date); + setDate(UPDATED_KEY, date); + } + + @Nullable + public String getMessage() { + return getString(MESSAGE_KEY); + } + + public void setMessage(final String message) { + if (Strings.isNullOrEmpty(message)) { + configuration.remove(MESSAGE_KEY); + } + else { + configuration.put(MESSAGE_KEY, message); + } + } + + // + // Typed configuration helpers + // + + // FIXME: Consider changing set null to remove sematics, this could lead to confusing results + + public Date getDate(final String key, final Date defaultValue) { + if (configuration.containsKey(key)) { + // TODO: will NPE if value is null + return new DateTime(getString(key)).toDate(); + } + else { + return defaultValue; + } + } + + public void setDate(final String key, final Date date) { + checkNotNull(key); + if (date == null) { + configuration.remove(key); + } + else { + configuration.put(key, new DateTime(date).toString()); + } + } + + public boolean getBoolean(final String key, final boolean defaultValue) { + return Boolean.parseBoolean(getString(key, String.valueOf(defaultValue))); + } + + public void setBoolean(final String key, final boolean value) { + checkNotNull(key); + configuration.put(key, String.valueOf(value)); + } + + public int getInteger(final String key, final int defaultValue) { + return Integer.parseInt(getString(key, String.valueOf(defaultValue))); + } + + public void setInteger(final String key, final int value) { + checkNotNull(key); + configuration.put(key, String.valueOf(value)); + } + + public long getLong(final String key, final long defaultValue) { + return Long.parseLong(getString(key, String.valueOf(defaultValue))); + } + + public void setLong(final String key, final long value) { + checkNotNull(key); + configuration.put(key, String.valueOf(value)); + } + + @Nullable + public String getString(final String key) { + return getString(key, null); + } + + public String getString(final String key, final String defaultValue) { + checkNotNull(key); + if (configuration.containsKey(key)) { + return configuration.get(key); + } + else { + return defaultValue; + } + } + + public void setString(final String key, final String value) { + checkNotNull(key); + if (value == null) { + configuration.remove(key); + } + else { + configuration.put(key, value); + } + } + + /** + * @since 3.2 + */ + public boolean containsKey(final String key) { + checkNotNull(key); + return configuration.containsKey(key); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskDescriptor.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..960899fb45f9f0fb48a23a9743e6e3a121ff8adb --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskDescriptor.java @@ -0,0 +1,73 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +import java.util.List; + +import org.sonatype.nexus.formfields.FormField; + +/** + * Descriptor for a {@link Task}. + * + * Descriptors should be named singleton components. + * + * @since 3.0 + */ +public interface TaskDescriptor +{ + /** + * Unique identifier for descriptor. + */ + String getId(); + + /** + * Short descriptive name of task. + */ + String getName(); + + /** + * The type of task. + */ + Class getType(); + + /** + * Task configuration fields. + */ + List getFormFields(); + + /** + * Directly manipulate task configuration before storing + * + * @since 3.2 + * @param configuration task's configuration + */ + void initializeConfiguration(TaskConfiguration configuration); + + // TODO: Figure out some clearer terms to use for the following state flags: + + /** + * Returns task visibility. + * + * Visible tasks are shown to users. + * + * May be overridden by {@link TaskConfiguration#setVisible(boolean)}. + */ + boolean isVisible(); + + /** + * Returns task exposure. + * + * Exposed tasks are allowed to be created by users. + */ + boolean isExposed(); +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskDescriptorSupport.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskDescriptorSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..772161fc01d0a5767a16ddba3682a40c058eca9b --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskDescriptorSupport.java @@ -0,0 +1,152 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import org.sonatype.nexus.formfields.CheckboxFormField; +import org.sonatype.nexus.formfields.ComboboxFormField; +import org.sonatype.nexus.formfields.FormField; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.stream.Collectors.toList; + +/** + * Support for {@link TaskDescriptor} implementations. + * + * @since 3.0 + */ +public abstract class TaskDescriptorSupport + implements TaskDescriptor +{ + // Common task form-field identifiers/labels + + public static final String MULTINODE_KEY = "multinode"; + + public static final String MULTINODE_LABEL = "Multi node"; + + public static final String MULTINODE_HELP = "Run task on all nodes in the cluster."; + + public static final String LIMIT_NODE_KEY = "limitnode"; + + public static final String LIMIT_NODE_LABEL = "Select node"; + + public static final String LIMIT_NODE_HELP = "Run task on this node in the cluster."; + + // Constants to help document configuration, since these are final and we have no fluent builder ATM + + protected static final boolean VISIBLE = true; + + protected static final boolean NOT_VISIBLE = false; + + protected static final boolean EXPOSED = true; + + protected static final boolean NOT_EXPOSED = false; + + private final String id; + + private final String name; + + private final Class type; + + private final boolean visible; + + private final boolean exposed; + + private final List formFields; + + public TaskDescriptorSupport(final String id, + final Class type, + final String name, + final boolean visible, + final boolean exposed, + final FormField... formFields) + { + + this.id = checkNotNull(id); + this.type = checkNotNull(type); + this.name = checkNotNull(name); + this.visible = visible; + this.exposed = exposed; + + checkNotNull(formFields); + this.formFields = Arrays.stream(formFields).filter(Objects::nonNull).collect(toList()); + } + + @Override + public final String getId() { + return id; + } + + @Override + public final String getName() { + return name; + } + + @Override + public final Class getType() { + return type; + } + + @Override + public final boolean isVisible() { + return visible; + } + + @Override + public final boolean isExposed() { + return exposed; + } + + @Override + public final List getFormFields() { + return formFields; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", type=" + type + + ", visible=" + visible + + ", exposed=" + exposed + + '}'; + } + + @Override + public void initializeConfiguration(final TaskConfiguration configuration) { + // no-op + } + + /** + * Creates a new {@link FormField} for tasks which can run on multiple-nodes at once. + * + * @since 3.1 + */ + protected static CheckboxFormField newMultinodeFormField() { + return new CheckboxFormField(MULTINODE_KEY, MULTINODE_LABEL, MULTINODE_HELP, false); + } + + /** + * Creates a new {@link FormField} for tasks which are limited to a specific node. + * + * @since 3.2 + */ + protected static ComboboxFormField newLimitNodeFormField() { + return new ComboboxFormField(LIMIT_NODE_KEY, LIMIT_NODE_LABEL, LIMIT_NODE_HELP, true) + .withStoreApi("node_NodeAccess.nodes").withIdMapping("name").withNameMapping("displayName"); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskFactory.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..31573f838f2b1da3e9ca94cd576884ca1e9580d7 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskFactory.java @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +import java.util.List; + +import javax.annotation.Nullable; + +/** + * Factory for creating {@link Task} instances. + * + * @see TaskDescriptor + * @see TaskConfiguration + */ +public interface TaskFactory +{ + /** + * Returns an immutable list of all detected descriptors. + */ + List getDescriptors(); + + /** + * Find a descriptor by its type-id; null if not found. + */ + @Nullable + TaskDescriptor findDescriptor(String typeId); + + /** + * Create a task from given configuration. + */ + Task create(TaskConfiguration config); +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskInfo.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..a1bf173f37d4f8dee55d8a0ae7311faf6186ffef --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskInfo.java @@ -0,0 +1,233 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +import java.util.Date; +import java.util.concurrent.Future; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.scheduling.schedule.Now; +import org.sonatype.nexus.scheduling.schedule.Schedule; + +/** + * The class holding information about task at the moment the instance of task info was created. + * + * This is the "handle" of a scheduled task. + * + * The handle might become "stale" if the task this instance is handle of is removed from scheduler + * (ie. by some other thread). In that case, some of the methods will throw {@link TaskRemovedException} on invocation + * to signal that state. + * + * For task entering {@link State#DONE}, this class will behave a bit differently: + * they will never throw {@link TaskRemovedException}, and upon they are done, the task info will cache + * task configuration, schedule, current and last run state forever. + * + * @since 3.0 + */ +public interface TaskInfo +{ + /** + * Returns a unique ID of the task instance. + * + * Shorthand method for {@link #getConfiguration()#getId()} + */ + String getId(); + + /** + * Returns a name of the task instance. + * + * Shorthand method for {@link #getConfiguration()#getName()} + */ + String getName(); + + /** + * Returns a type id of the task instance. + * + * Shorthand method for {@link #getConfiguration()#getTypeId()} + * + * @since 3.8 + */ + String getTypeId(); + + /** + * Returns a message of the task instance. + * + * Shorthand method for {@link #getConfiguration()#getMessage()} + */ + String getMessage(); + + /** + * Returns a COPY of the task configuration map from the moment this instance was created or any state change on task + * happened. + * + * Modifications to this configuration are possible, but does not affect currently executing task, nor is being + * persisted. + * + * Generally, this configuration is only for inspection, or, to be used to re-schedule existing task with changed + * configuration. + */ + TaskConfiguration getConfiguration(); + + /** + * Returns the task's schedule from the moment this instance was created or any state change on task happened. + */ + Schedule getSchedule(); + + /** + * Task instance might be waiting (to be run, either by schedule or manually), or might be running, or might be + * done (will never run again, is "done"). The "done" state is ending state for task, it will according to it's + * {@link Schedule} not execute anymore. + * + * Scheduler will never give out "fresh" task info instances with state "done" as done task is also removed. + * These states might be get into only by having a "single shot" task ended. Instances in + * this "ending" state, while still holding valid configuration and schedule, might be used to reschedule a + * NEW task instance, but the reference to this instance should be dropped and let for GC to collect it, and + * continue with the newly returned task info. + * + * Transitions: + * {@link #WAITING} -> {@link #RUNNING} + * {@link #RUNNING} -> {@link #WAITING} + * {@link #RUNNING} -> {@link #DONE} + */ + enum State + { + WAITING, RUNNING, DONE + } + + /** + * Running task instance might be running okay, being blocked (by other tasks), or might be canceled but the + * cancellation was not yet detected or some cleanup is being done. + * + * Possible transitions: currentRunState.ordinal <= newRunState.ordinal + * Ending states are {@link #RUNNING} and {@link #CANCELED}. + */ + enum RunState + { + STARTING, BLOCKED, RUNNING, CANCELED + } + + interface CurrentState + { + /** + * Returns the state of task, never {@code null}. + */ + State getState(); + + /** + * Returns the date of next run, if applicable, or {@code null}. + */ + @Nullable + Date getNextRun(); + + /** + * If task is running, returns it's run state, otherwise {@code null}. + */ + @Nullable + Date getRunStarted(); + + /** + * If task is running, returns it's run state, otherwise {@code null}. + */ + @Nullable + RunState getRunState(); + + /** + * If task is in states {@link State#RUNNING} or {@link State#DONE}, returns it's future, otherwise {@code null}. + * In case of {@link State#DONE} the future is done too. + */ + @Nullable + Future getFuture(); + } + + enum EndState + { + OK, FAILED, CANCELED + } + + interface LastRunState + { + /** + * Returns the last end state. + */ + EndState getEndState(); + + /** + * Returns the date of last run start. + */ + Date getRunStarted(); + + /** + * Returns the last run duration. + */ + long getRunDuration(); + } + + /** + * Returns the task current state, never {@code null}. + * + * For tasks scheduled with {@link Now} schedule, or having manually started with {@link #runNow()} method, + * the invocation of this method will block until underlying scheduler actually starts the task, hence, + * caller might get the task result. + */ + CurrentState getCurrentState(); + + /** + * Returns the task last run state, if there was any, otherwise {@code null}. + */ + @Nullable + LastRunState getLastRunState(); + + /** + * Removes (with canceling if runs) the task. + * + * Returns {@code true} if it's guaranteed that the task is removed from scheduler, and no future executions of + * this task will happen. Still, task might be executing in this very moment, until detects cancellation. + * + * Returns {@code false} if task executes and is not cancelable. + */ + boolean remove(); + + /** + * Executes the scheduled task now, unrelated to it's actual schedule. Already running task or disabled task + * cannot have this method executed, will throw {@link IllegalStateException}. + * + * This also implies that this method will NOT change the state of tasks "original" schedule! + * + * For example: hourly schedule, has 30 minutes to run, and task is getting run now. When the task is done, + * there will be still 30 minutes for it's original schedule to run (minus the task execution time). + * + * @param triggerSource the source that triggered this task + * + * @throws TaskRemovedException if task with this ID has been removed from scheduler. + * @throws IllegalStateException if task is already running, or, if is disabled. + * + * @since 3.1 + */ + TaskInfo runNow(@Nullable String triggerSource) throws TaskRemovedException; + + /** + * @see #runNow(String) + */ + default TaskInfo runNow() throws TaskRemovedException { + return runNow(null); + } + + /** + * Returns the source that triggered the current task execution, if known. + * + * @since 3.1 + */ + @Nullable + String getTriggerSource(); +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskInterruptedException.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskInterruptedException.java new file mode 100644 index 0000000000000000000000000000000000000000..668b625fa74773327bf4184ceee9732658cb4556 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskInterruptedException.java @@ -0,0 +1,36 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +/** + * Runtime exception thrown in cases when task is interrupted. + * + * Semantically meaning is almost same as {@link InterruptedException} except + * this one is unchecked exception and may carry some cause. + * + * @since 3.0 + */ +public class TaskInterruptedException + extends RuntimeException +{ + private final boolean canceled; + + public TaskInterruptedException(final String message, final boolean canceled) { + super(message); + this.canceled = canceled; + } + + public boolean isCanceled() { + return canceled; + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskRemovedException.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskRemovedException.java new file mode 100644 index 0000000000000000000000000000000000000000..b6d55d5968423873a627b20b8c9697b6211c783d --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskRemovedException.java @@ -0,0 +1,32 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +import javax.annotation.Nullable; + +/** + * Checked exception thrown in cases when task is removed by some other party, but the caller is unaware of it. + * + * @since 3.0 + */ +public class TaskRemovedException + extends Exception +{ + public TaskRemovedException(final String taskId) { + this(taskId, null); + } + + public TaskRemovedException(final String taskId, @Nullable final Throwable cause) { + super(String.format("Task '%s' does not exists", taskId), cause); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskScheduler.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskScheduler.java new file mode 100644 index 0000000000000000000000000000000000000000..9f9fc3b6af0f604129400dd539331f6c1512fab1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskScheduler.java @@ -0,0 +1,93 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +import java.util.List; +import java.util.concurrent.Future; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.scheduling.schedule.Schedule; +import org.sonatype.nexus.scheduling.schedule.ScheduleFactory; +import org.sonatype.nexus.scheduling.spi.SchedulerSPI; + +/** + * Application task scheduling facade. + * + * Provides a high-level API around {@link SchedulerSPI}. + */ +public interface TaskScheduler +{ + /** + * Returns the factory to create tasks. + */ + TaskFactory getTaskFactory(); + + /** + * Returns the factory to create schedules. + */ + ScheduleFactory getScheduleFactory(); + + /** + * Create a configuration for the given type-id. + */ + TaskConfiguration createTaskConfigurationInstance(String typeId); + + /** + * Issues a task for immediate execution, giving control over it with returned {@link Future} instance. + * + * Tasks executed via this method are executed as soon as possible, and are not persisted. + */ + TaskInfo submit(TaskConfiguration configuration); + + /** + * Returns the {@link TaskInfo} of a task by it's ID, if present, otherwise {@code null}. + */ + @Nullable + TaskInfo getTaskById(String id); + + /** + * List existing tasks. + */ + List listsTasks(); + + /** + * Schedules a task for execution based on given schedule. + * + * If existing task with ID exists, it will be replaced. + * + * Task must not be running. + */ + TaskInfo scheduleTask(TaskConfiguration configuration, Schedule schedule); + + /** + * Returns the count of currently running tasks. + */ + int getRunningTaskCount(); + + /** + * Returns the count of tasks executed so far. + * + * @since 3.7 + */ + int getExecutedTaskCount(); + + /** + * Returns the state of a given task across the nodes in a clustered environment or {@code null} if clustering isn't + * enabled. + * + * @since 3.1 + */ + @Nullable + List getClusteredTaskStateById(String taskId); +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskSupport.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..e218c6bba2a49cd5700b7794e861b341a2e16b44 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/TaskSupport.java @@ -0,0 +1,140 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling; + +import java.util.concurrent.atomic.AtomicBoolean; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.logging.task.TaskLoggerFactory; +import org.sonatype.nexus.logging.task.TaskLoggerHelper; + +import com.google.common.base.Strings; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.logging.task.TaskLoggingMarkers.TASK_LOG_ONLY; + +/** + * Support for {@link Task} implementations. + * + * Subclasses may implement {@link Cancelable} interface if they are implemented to periodically check for + * {@link #isCanceled()} or {@link CancelableHelper#checkCancellation()} methods. + * + * Task implementations should be {@code @Named} components but must not be {@code @Singletons}. + * + * @since 3.0 + */ +public abstract class TaskSupport + extends ComponentSupport + implements Task +{ + private final TaskConfiguration configuration; + + private final AtomicBoolean canceledFlag; + + public TaskSupport() { + this.configuration = createTaskConfiguration(); + this.canceledFlag = new AtomicBoolean(false); + } + + protected TaskConfiguration createTaskConfiguration() { + return new TaskConfiguration(); + } + + protected TaskConfiguration getConfiguration() { + return configuration; + } + + @Override + public TaskConfiguration taskConfiguration() { + return new TaskConfiguration(configuration); + } + + @Override + public void configure(final TaskConfiguration configuration) { + checkNotNull(configuration); + + configuration.validate(); + this.configuration.apply(configuration); + + String message = getMessage(); + if (!Strings.isNullOrEmpty(message)) { + this.configuration.setMessage(message); + } + } + + @Override + public String getId() { + return getConfiguration().getId(); + } + + @Override + public String getName() { + return getConfiguration().getName(); + } + + /** + * Install canceled flag and {@link #execute()}. + */ + @Override + public final Object call() throws Exception { + startTaskLogging(); + CancelableHelper.set(canceledFlag); + try { + return execute(); + } + catch (Exception e) { + log.error(TASK_LOG_ONLY, "Failed to run task '{}'", getMessage(), e); + throw e; + } + finally { + CancelableHelper.remove(); + finishTaskLogging(); + } + } + + private void startTaskLogging() { + TaskLoggerHelper.start(TaskLoggerFactory.create(this, log, configuration)); + } + + private void finishTaskLogging() { + TaskLoggerHelper.finish(); + } + + /** + * Execute task logic. + */ + protected abstract Object execute() throws Exception; + + // + // Cancelable; not directly implemented but here allow Cancelable to be used as a marker and provide impl + // + + /** + * Cancel this task. + */ + public void cancel() { + canceledFlag.set(true); + } + + /** + * Check if this task is canceled. + */ + public boolean isCanceled() { + return canceledFlag.get(); + } + + @Override + public String toString() { + return String.format("%s(id=%s, name=%s)", getClass().getSimpleName(), getId(), getName()); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/api/TaskXO.groovy b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/api/TaskXO.groovy new file mode 100644 index 0000000000000000000000000000000000000000..4d75e698847d872011a940b2af5c0704e76c3634 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/api/TaskXO.groovy @@ -0,0 +1,62 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.api + +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString + +import org.sonatype.nexus.scheduling.TaskInfo + +/** + * Task transfer object for REST APIs. + * + * @since 3.6 + */ +@CompileStatic +@ToString(includePackage = false, includeNames = true) +@EqualsAndHashCode(includes = ['id']) +class TaskXO +{ + String id + + String name + + String type + + String message + + String currentState + + String lastRunResult + + Date nextRun + + Date lastRun + + static TaskXO fromTaskInfo(final TaskInfo taskInfo) { + TaskXO taskXO = new TaskXO() + + taskXO.id = taskInfo.id + taskXO.name = taskInfo.name + taskXO.type = taskInfo.typeId + taskXO.message = taskInfo.message + taskXO.currentState = taskInfo.currentState.state.toString() + taskXO.nextRun = taskInfo.currentState.nextRun + if (taskInfo.lastRunState != null) { + taskXO.lastRunResult = taskInfo.lastRunState.endState?.toString() + taskXO.lastRun = taskInfo.lastRunState.runStarted + } + return taskXO + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/constraints/CronExpression.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/constraints/CronExpression.java new file mode 100644 index 0000000000000000000000000000000000000000..66e53ffafa238a2eee7eb6f9eff910f35c5779aa --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/constraints/CronExpression.java @@ -0,0 +1,45 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.constraints; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Validate string is a valid CRON expression. + * + * @since 3.0 + */ +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) +@Retention(RUNTIME) +@Constraint(validatedBy = CronExpressionValidator.class) +@Documented +public @interface CronExpression +{ + String message() default "Invalid Cron expression"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/constraints/CronExpressionValidator.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/constraints/CronExpressionValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..c479437fcb4da885730921770fd3678a2d7663f4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/constraints/CronExpressionValidator.java @@ -0,0 +1,63 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.constraints; + +import java.util.Date; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.validation.ConstraintValidatorContext; + +import org.sonatype.nexus.scheduling.TaskScheduler; +import org.sonatype.nexus.scheduling.schedule.ScheduleFactory; +import org.sonatype.nexus.validation.ConstraintValidatorSupport; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * {@link CronExpression} validator. + * + * @see ScheduleFactory#cron(Date, String) + * @since 3.0 + */ +@Named +public class CronExpressionValidator + extends ConstraintValidatorSupport +{ + private final ScheduleFactory scheduleFactory; + + @Inject + public CronExpressionValidator(final TaskScheduler taskScheduler) { + checkNotNull(taskScheduler); + this.scheduleFactory = taskScheduler.getScheduleFactory(); + } + + @Override + public void initialize(final CronExpression annotation) { + // nop + } + + @Override + public boolean isValid(final String value, final ConstraintValidatorContext context) { + try { + scheduleFactory.cron(new Date(), value); + } + catch (IllegalArgumentException e) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(e.getMessage()) + .addConstraintViolation(); + return false; + } + return true; + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/constraints/package-info.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/constraints/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..a0e4ccc999844eca706171a4dbe214cbbb9dffef --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/constraints/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Scheduling related bean-validation constraints. + * + * @since 3.0 + */ +package org.sonatype.nexus.scheduling.constraints; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskBlockedEvent.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskBlockedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..262884045d55ac83e35b40844c11c174cb822aad --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskBlockedEvent.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.events; + +import org.sonatype.nexus.scheduling.TaskInfo; + +/** + * Emitted when a task has been blocked waiting for other tasks to finish. + * + * @since 3.1 + */ +public class TaskBlockedEvent + extends TaskEvent +{ + public TaskBlockedEvent(final TaskInfo taskInfo) { + super(taskInfo); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskDeletedEvent.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskDeletedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..5e524223c4f7b73e717c2060725692de24d51e6f --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskDeletedEvent.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.events; + +import org.sonatype.nexus.scheduling.TaskInfo; + +/** + * Emitted when a task has been deleted. + * + * @since 3.1 + */ +public class TaskDeletedEvent + extends TaskEvent +{ + public TaskDeletedEvent(final TaskInfo taskInfo) { + super(taskInfo); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEvent.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..99b47b534768b83c444891317c015bb7ba90ccf9 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEvent.java @@ -0,0 +1,85 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.events; + +import java.util.Date; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.scheduling.TaskInfo; +import org.sonatype.nexus.scheduling.TaskInfo.CurrentState; +import org.sonatype.nexus.scheduling.TaskInfo.LastRunState; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Abstract super class for task related events. + * + * @since 2.0 + */ +public abstract class TaskEvent +{ + private final Date eventDate; + + private final TaskInfo taskInfo; + + private final CurrentState currentState; + + private final LastRunState lastRunState; + + public TaskEvent(final TaskInfo taskInfo) { + this.eventDate = new Date(); + this.taskInfo = checkNotNull(taskInfo); + this.currentState = taskInfo.getCurrentState(); + this.lastRunState = taskInfo.getLastRunState(); + } + + /** + * Returns the timestamp of event creation. + */ + public Date getEventDate() { + return eventDate; + } + + /** + * Returns the "handle" of the task. Please note that this is "live" object, so states returned by this + * instance may change as task progresses or even finishes it's work while the event handler gets this event. + */ + public TaskInfo getTaskInfo() { + return taskInfo; + } + + /** + * Gets the "current state" of the task in the moment this event was fired, never returns {@code null}. + */ + public CurrentState getCurrentState() { + return currentState; + } + + /** + * Gets the "last run state" in the moment this event was fired, may return {@code null}. + */ + @Nullable + public LastRunState getLastRunState() { + return lastRunState; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "nexusTask=" + getTaskInfo().getConfiguration() + + ", currentState=" + currentState + + ", lastRunState=" + lastRunState + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventCanceled.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventCanceled.java new file mode 100644 index 0000000000000000000000000000000000000000..9b84ee205d2c1c3a5cd4d873f7d7209af37f5a0c --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventCanceled.java @@ -0,0 +1,34 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.events; + +import org.sonatype.nexus.scheduling.Cancelable; +import org.sonatype.nexus.scheduling.TaskInfo; + +/** + * Event fired when {@link Cancelable} NX Task is running and has been canceled. + * + * Fired in the moment cancellation was applied, the task is probably still running and will stop when it detects + * request for cancellation. + * + * If this event was emitted, the last event sent in this run for the task is {@link TaskEventStoppedCanceled}. + * + * @since 2.0 + */ +public class TaskEventCanceled + extends TaskEvent +{ + public TaskEventCanceled(final TaskInfo taskInfo) { + super(taskInfo); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStarted.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStarted.java new file mode 100644 index 0000000000000000000000000000000000000000..a9ceb169ed9d2eb9b125030a385667d3be715064 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStarted.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.events; + +import org.sonatype.nexus.scheduling.TaskInfo; + +/** + * Event fired when a task is started (might be running or sleeping if blocked). + * + * @since 2.0 + */ +public class TaskEventStarted + extends TaskEvent +{ + public TaskEventStarted(final TaskInfo taskInfo) { + super(taskInfo); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStopped.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStopped.java new file mode 100644 index 0000000000000000000000000000000000000000..4254e8b9b4dc043d211c0ac2d5daa7f485b3fd0b --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStopped.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.events; + +import org.sonatype.nexus.scheduling.TaskInfo; + +/** + * Base class for events fired when a task is stopped, done (whatever the outcome is: finished, cancelled or failed). + * + * @since 2.0 + */ +public abstract class TaskEventStopped + extends TaskEvent +{ + public TaskEventStopped(final TaskInfo taskInfo) { + super(taskInfo); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStoppedCanceled.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStoppedCanceled.java new file mode 100644 index 0000000000000000000000000000000000000000..5af54999beffc49cc701b920e7b3fe20af1652a6 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStoppedCanceled.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.events; + +import org.sonatype.nexus.scheduling.TaskInfo; + +/** + * Event fired when a task is cancelled (more precisely, not WHEN is cancelled, but when task detected that is being + * canceled and gives up the work). + * + * @since 2.0 + */ +public class TaskEventStoppedCanceled + extends TaskEventStopped +{ + public TaskEventStoppedCanceled(final TaskInfo taskInfo) { + super(taskInfo); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStoppedDone.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStoppedDone.java new file mode 100644 index 0000000000000000000000000000000000000000..49910ed0e1ad73ea33d58ccf647c560810bc8bbc --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStoppedDone.java @@ -0,0 +1,29 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.events; + +import org.sonatype.nexus.scheduling.TaskInfo; + +/** + * Event fired when a task is stopped, is cleanly done what was it doing (whatever the ending resolution is: finished, + * cancelled or failed). + * + * @since 2.0 + */ +public class TaskEventStoppedDone + extends TaskEventStopped +{ + public TaskEventStoppedDone(final TaskInfo taskInfo) { + super(taskInfo); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStoppedFailed.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStoppedFailed.java new file mode 100644 index 0000000000000000000000000000000000000000..3b5ab6abaed8e6ea2dccb8e4dc725e0b6a3f900c --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskEventStoppedFailed.java @@ -0,0 +1,43 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.events; + +import org.sonatype.nexus.scheduling.TaskInfo; + +/** + * Event fired when a task failed with some error. + * + * @since 2.0 + */ +public class TaskEventStoppedFailed + extends TaskEventStopped +{ + /** + * Failure cause. + */ + private final Throwable throwable; + + public TaskEventStoppedFailed(final TaskInfo taskInfo, + final Throwable throwable) + { + super(taskInfo); + this.throwable = throwable; + } + + /** + * Returns the failure cause. + */ + public Throwable getFailureCause() { + return throwable; + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskScheduledEvent.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskScheduledEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..0514aa88e6124b51fc30c33121b77d0c4f078b7e --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskScheduledEvent.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.events; + +import org.sonatype.nexus.scheduling.TaskInfo; + +/** + * Emitted when a task has been scheduled. + * + * @since 3.1 + */ +public class TaskScheduledEvent + extends TaskEvent +{ + public TaskScheduledEvent(final TaskInfo taskInfo) { + super(taskInfo); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskStartedRunningEvent.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskStartedRunningEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..d4b5d3ab8fadbed7d43d63ce70129584910641d2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/TaskStartedRunningEvent.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.events; + +import org.sonatype.nexus.scheduling.TaskInfo; + +/** + * Emitted when a task has started running (not to be confused with {@link TaskEventStarted}). + * + * @since 3.1 + */ +public class TaskStartedRunningEvent + extends TaskEvent +{ + public TaskStartedRunningEvent(final TaskInfo taskInfo) { + super(taskInfo); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/package-info.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..206653b56cf8c45234d0baf8841dd85a0fab3db4 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/events/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Scheduling events. + * + * @since 3.0 + */ +package org.sonatype.nexus.scheduling.events; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/LocalTaskStateTracker.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/LocalTaskStateTracker.java new file mode 100644 index 0000000000000000000000000000000000000000..7df04ceef1cd2a2822e446b54554ee80e645183a --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/LocalTaskStateTracker.java @@ -0,0 +1,74 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.internal; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.app.ManagedLifecycle.Phase; +import org.sonatype.nexus.common.event.EventAware; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.scheduling.ClusteredTaskStateStore; +import org.sonatype.nexus.scheduling.TaskScheduler; +import org.sonatype.nexus.scheduling.events.TaskDeletedEvent; +import org.sonatype.nexus.scheduling.events.TaskEvent; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.Subscribe; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Updates {@link ClusteredTaskStateStore} with local task states. + * + * @since 3.1 + */ +@Named +@Singleton +@ManagedLifecycle(phase = Phase.TASKS) +public class LocalTaskStateTracker + extends StateGuardLifecycleSupport + implements EventAware +{ + private final ClusteredTaskStateStore clusteredTaskStateStore; + + private final TaskScheduler taskScheduler; + + @Inject + public LocalTaskStateTracker(final ClusteredTaskStateStore clusteredTaskStateStore, + final TaskScheduler taskScheduler) + { + this.clusteredTaskStateStore = checkNotNull(clusteredTaskStateStore); + this.taskScheduler = checkNotNull(taskScheduler); + } + + @Override + protected void doStart() throws Exception { + log.debug("Recording initial task states"); + taskScheduler.listsTasks().stream().forEach(clusteredTaskStateStore::setLocalState); + } + + @Subscribe + @AllowConcurrentEvents + public void on(TaskEvent event) { + log.debug("Updating task state for event {}", event); + if (event instanceof TaskDeletedEvent) { + clusteredTaskStateStore.removeClusteredState(event.getTaskInfo().getId()); + } + else { + clusteredTaskStateStore.setLocalState(event.getTaskInfo()); + } + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/NonClusteredTaskStateStore.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/NonClusteredTaskStateStore.java new file mode 100644 index 0000000000000000000000000000000000000000..7d18573f559a276569686e245756e1f1b90fa38e --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/NonClusteredTaskStateStore.java @@ -0,0 +1,51 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.internal; + +import java.util.List; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.scheduling.ClusteredTaskState; +import org.sonatype.nexus.scheduling.ClusteredTaskStateStore; +import org.sonatype.nexus.scheduling.TaskInfo; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Implementation of {@link ClusteredTaskStateStore} for non-clustered environments. + * + * @since 3.1 + */ +@Named +@Singleton +public class NonClusteredTaskStateStore + implements ClusteredTaskStateStore +{ + @Override + public void setLocalState(TaskInfo taskInfo) { + checkNotNull(taskInfo); + } + + @Override + public void removeClusteredState(String taskId) { + checkNotNull(taskId); + } + + @Override + public List getClusteredState(String taskId) { + checkNotNull(taskId); + return null; // NOSONAR + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/TaskActivation.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/TaskActivation.java new file mode 100644 index 0000000000000000000000000000000000000000..f9968af60e98f78acd8a38b6760c3b59278a2244 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/TaskActivation.java @@ -0,0 +1,90 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.internal; + +import java.util.concurrent.Future; + +import javax.annotation.Priority; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; +import org.sonatype.nexus.common.event.EventAware; +import org.sonatype.nexus.orient.freeze.DatabaseFreezeChangeEvent; +import org.sonatype.nexus.orient.freeze.DatabaseFreezeService; +import org.sonatype.nexus.scheduling.TaskInfo; +import org.sonatype.nexus.scheduling.spi.SchedulerSPI; + +import com.google.common.eventbus.Subscribe; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.TASKS; + +/** + * Manages activation/passivation of the scheduler. + * + * @since 3.0 + */ +@Named +@ManagedLifecycle(phase = TASKS) +@Priority(Integer.MIN_VALUE) // start scheduler at the end of this phase +@Singleton +public class TaskActivation + extends StateGuardLifecycleSupport + implements EventAware +{ + private final SchedulerSPI scheduler; + + private final DatabaseFreezeService databaseFreezeService; + + @Inject + public TaskActivation(final SchedulerSPI scheduler, final DatabaseFreezeService databaseFreezeService) { + this.scheduler = checkNotNull(scheduler); + this.databaseFreezeService = checkNotNull(databaseFreezeService); + } + + @Override + protected void doStart() throws Exception { + if (!databaseFreezeService.isFrozen()) { + scheduler.resume(); + } + } + + @Override + protected void doStop() throws Exception { + scheduler.pause(); + } + + /** + * @since 3.2.1 + */ + @Subscribe + public void onDatabaseFreezeChangeEvent(final DatabaseFreezeChangeEvent databaseFreezeChangeEvent) { + if (databaseFreezeChangeEvent.isFrozen()) { + scheduler.pause(); + scheduler.listsTasks().stream() + .filter(taskInfo -> !maybeCancel(taskInfo)) + .forEach(taskInfo -> log.warn("Unable to cancel task: {}", taskInfo.getName())); + } + else { + scheduler.resume(); + } + } + + private boolean maybeCancel(final TaskInfo taskInfo) { + Future future = taskInfo.getCurrentState().getFuture(); + return future == null || future.cancel(false); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/TaskFactoryImpl.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/TaskFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..db98d00f9e60963f99fea574806f7de9272195ca --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/TaskFactoryImpl.java @@ -0,0 +1,198 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.internal; + +import java.lang.annotation.Annotation; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.scheduling.Task; +import org.sonatype.nexus.scheduling.TaskConfiguration; +import org.sonatype.nexus.scheduling.TaskDescriptor; +import org.sonatype.nexus.scheduling.TaskFactory; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Maps; +import com.google.inject.Key; +import org.eclipse.sisu.BeanEntry; +import org.eclipse.sisu.Mediator; +import org.eclipse.sisu.inject.BeanLocator; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default {@link TaskFactory} implementation. + * + * Resolves {@link TaskDescriptor} components via {@link BeanLocator} singleton components. + * + * Resolves {@link Task} components via {@link BeanLocator} lookup by {@link TaskDescriptor#getType()}. + * + * @since 3.0 + */ +@Named +@Singleton +public class TaskFactoryImpl + extends ComponentSupport + implements TaskFactory +{ + private final BeanLocator beanLocator; + + /** + * Map of descriptor-id to descriptor instance. + */ + private final Map taskDefinitions = Maps.newConcurrentMap(); + + @Inject + public TaskFactoryImpl(final BeanLocator beanLocator) { + this.beanLocator = checkNotNull(beanLocator); + + // watch for TaskDescriptor components + beanLocator.watch(Key.get(TaskDescriptor.class, Named.class), new TaskDescriptorMediator(), this); + } + + /** + * Simple struct to hold descriptor and bean entry together. + */ + private static class TaskDefinition + { + private final TaskDescriptor descriptor; + + private final BeanEntry beanEntry; + + private TaskDefinition(final TaskDescriptor descriptor, + final BeanEntry beanEntry) + { + this.descriptor = checkNotNull(descriptor); + this.beanEntry = checkNotNull(beanEntry); + } + } + + /** + * Sisu {@link Mediator} to maintain mapping of type-id to descriptor instance as they come and go. + */ + private static class TaskDescriptorMediator + implements Mediator + { + @Override + public void add(final BeanEntry entry, final TaskFactoryImpl watcher) throws Exception { + watcher.addDescriptor(entry.getValue()); + } + + @Override + public void remove(final BeanEntry entry, final TaskFactoryImpl watcher) throws Exception { + watcher.removeDescriptor(entry.getValue().getId()); + } + } + + /** + * Registers a Task implementation: based on passed in descriptor the task's {@link BeanEntry} is looked up too, + * validated and cached, keyed by {@link TaskDescriptor#getId()}. + */ + @VisibleForTesting + void addDescriptor(final TaskDescriptor descriptor) { + String typeId = descriptor.getId(); + log.debug("Adding task type-id: {}", typeId); + + // resolve task component + Class type = descriptor.getType(); + log.debug("Resolving task bean-entry for type-id {} of type: {}", typeId, type.getName()); + Iterator> entries = beanLocator.locate(Key.get(type)).iterator(); + if (!entries.hasNext()) { + log.warn("Missing task-component for type-id: {}; ignoring it", typeId); + return; + } + + BeanEntry entry = entries.next(); + if (entry.getImplementationClass().getAnnotation(Singleton.class) != null) { + log.warn( + "Task type-id {} implementation {} is singleton; ignoring it", + typeId, + entry.getImplementationClass().getName()); + return; + } + log.debug("Adding task type-id: {} -> {}", typeId, entry.getImplementationClass().getName()); + TaskDefinition prevTaskDefinition = taskDefinitions.put(typeId, new TaskDefinition(descriptor, entry)); + if (prevTaskDefinition != null) { + log.warn( + "Duplicate task type-id {} implementations: {} replaced by {}", + typeId, + prevTaskDefinition.descriptor.getType().getName(), + descriptor.getType().getName()); + } + } + + /** + * Unregisters a Task implementation by it's type-id ({@link TaskDescriptor#getId()}). + */ + @VisibleForTesting + void removeDescriptor(final String typeId) { + log.debug("Removing task type-id: {}", typeId); + taskDefinitions.remove(typeId); + } + + /** + * Creates a new instance of Task having provided type-id, by using {@link BeanEntry#getProvider()}, hence + * new instance is created every time (tasks are enforced to not be singletons, see {@link + * #addDescriptor(TaskDescriptor)}. + */ + @VisibleForTesting + Task newInstance(final String typeId) { + TaskDefinition taskDefinition = taskDefinitions.get(typeId); + checkArgument(taskDefinition != null, "Unknown task type-id: %s", typeId); + + Class type = taskDefinition.descriptor.getType(); + return type.cast(taskDefinition.beanEntry.getProvider().get()); + } + + @Override + public List getDescriptors() { + return Collections.unmodifiableList( + taskDefinitions.values().stream().map(d -> d.descriptor).collect(Collectors.toList()) + ); + } + + @Override + @Nullable + public TaskDescriptor findDescriptor(final String typeId) { + TaskDefinition taskDefinition = taskDefinitions.get(typeId); + if (taskDefinition != null) { + return taskDefinition.descriptor; + } + return null; + } + + @Override + public Task create(final TaskConfiguration config) { + checkNotNull(config); + log.debug("Creating task instance: {}", config); + + // ensure configuration is sane + config.validate(); + + // create and configure the task + Task task = newInstance(config.getTypeId()); + task.configure(config); + + return task; + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/TaskSchedulerImpl.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/TaskSchedulerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..ea43eec1ebf6d56600a87fdf48ee4dc77a49fd22 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/TaskSchedulerImpl.java @@ -0,0 +1,168 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.internal; + +import java.util.Date; +import java.util.List; +import java.util.UUID; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.event.EventManager; +import org.sonatype.nexus.scheduling.ClusteredTaskState; +import org.sonatype.nexus.scheduling.ClusteredTaskStateStore; +import org.sonatype.nexus.scheduling.TaskConfiguration; +import org.sonatype.nexus.scheduling.TaskDescriptor; +import org.sonatype.nexus.scheduling.TaskFactory; +import org.sonatype.nexus.scheduling.TaskInfo; +import org.sonatype.nexus.scheduling.TaskScheduler; +import org.sonatype.nexus.scheduling.events.TaskScheduledEvent; +import org.sonatype.nexus.scheduling.schedule.Schedule; +import org.sonatype.nexus.scheduling.schedule.ScheduleFactory; +import org.sonatype.nexus.scheduling.spi.SchedulerSPI; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * Default {@link TaskScheduler} implementation. + * + * @since 3.0 + */ +@Singleton +@Named +public class TaskSchedulerImpl + extends ComponentSupport + implements TaskScheduler +{ + private final EventManager eventManager; + + private final TaskFactory taskFactory; + + private final ClusteredTaskStateStore clusteredTaskStateStore; + + private final Provider scheduler; + + @Inject + public TaskSchedulerImpl(final EventManager eventManager, + final TaskFactory taskFactory, + final ClusteredTaskStateStore clusteredTaskStateStore, + final Provider scheduler) + { + this.eventManager = checkNotNull(eventManager); + this.taskFactory = checkNotNull(taskFactory); + this.clusteredTaskStateStore = checkNotNull(clusteredTaskStateStore); + this.scheduler = checkNotNull(scheduler); + } + + @Override + public TaskFactory getTaskFactory() { + return taskFactory; + } + + /** + * Helper to ensure provided scheduler is non-null. + */ + private SchedulerSPI getScheduler() { + SchedulerSPI result = scheduler.get(); + checkState(result != null); + return result; + } + + @Override + public ScheduleFactory getScheduleFactory() { + ScheduleFactory result = getScheduler().scheduleFactory(); + checkState(result != null); + return result; + } + + @Override + public int getRunningTaskCount() { + return getScheduler().getRunningTaskCount(); + } + + @Override + public int getExecutedTaskCount() { + return getScheduler().getExecutedTaskCount(); + } + + @Override + public TaskConfiguration createTaskConfigurationInstance(final String typeId) { + checkNotNull(typeId); + + TaskDescriptor descriptor = taskFactory.findDescriptor(typeId); + checkArgument(descriptor != null, "Missing descriptor for task with type-id: %s", typeId); + + TaskConfiguration config = new TaskConfiguration(); + descriptor.initializeConfiguration(config); // in case any hardcode values need to be inserted + config.setId(UUID.randomUUID().toString()); + config.setTypeId(descriptor.getId()); + config.setTypeName(descriptor.getName()); + config.setName(descriptor.getName()); + config.setVisible(descriptor.isVisible()); + + return config; + } + + @Override + public TaskInfo submit(final TaskConfiguration config) { + return scheduleTask(config, getScheduleFactory().now()); + } + + @Override + public TaskInfo getTaskById(final String id) { + checkNotNull(id); + return getScheduler().getTaskById(id); + } + + @Override + public List listsTasks() { + return getScheduler().listsTasks(); + } + + @Override + public TaskInfo scheduleTask(final TaskConfiguration config, final Schedule schedule) { + checkNotNull(config); + checkNotNull(schedule); + + config.validate(); + + Date now = new Date(); + if (config.getCreated() == null) { + config.setCreated(now); + } + config.setUpdated(now); + + TaskInfo taskInfo = getScheduler().scheduleTask(config, schedule); + + log.info("Task {} scheduled: {}", + taskInfo.getConfiguration().getTaskLogName(), + taskInfo.getSchedule().getType() + ); + + eventManager.post(new TaskScheduledEvent(taskInfo)); + + return taskInfo; + } + + @Override + public List getClusteredTaskStateById(String taskId) { + checkNotNull(taskId); + return clusteredTaskStateStore.getClusteredState(taskId); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/resources/TasksResource.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/resources/TasksResource.java new file mode 100644 index 0000000000000000000000000000000000000000..01dfeed4b380878213b1d125b13443472c0a07c2 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/resources/TasksResource.java @@ -0,0 +1,146 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.internal.resources; + +import java.util.List; +import java.util.concurrent.Future; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.rest.Page; +import org.sonatype.nexus.rest.Resource; +import org.sonatype.nexus.scheduling.TaskScheduler; +import org.sonatype.nexus.scheduling.TaskInfo; +import org.sonatype.nexus.scheduling.api.TaskXO; +import org.sonatype.nexus.scheduling.internal.resources.doc.TasksResourceDoc; + +import org.apache.shiro.authz.annotation.RequiresAuthentication; +import org.apache.shiro.authz.annotation.RequiresPermissions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.String.format; +import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.toList; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static javax.ws.rs.core.Response.Status.CONFLICT; +import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; +import static org.sonatype.nexus.scheduling.internal.resources.TasksResource.RESOURCE_URI; +import static org.sonatype.nexus.rest.APIConstants.BETA_API_PREFIX; + +/** + * @since 3.6 + */ +@Named +@Singleton +@Path(RESOURCE_URI) +@Produces(APPLICATION_JSON) +@Consumes(APPLICATION_JSON) +public class TasksResource + extends ComponentSupport + implements Resource, TasksResourceDoc +{ + public static final String RESOURCE_URI = BETA_API_PREFIX + "/tasks"; + + private static final String TRIGGER_SOURCE = "REST API"; + + private final TaskScheduler taskScheduler; + + @Inject + public TasksResource(final TaskScheduler taskScheduler) { + this.taskScheduler = checkNotNull(taskScheduler); + } + + @GET + @RequiresAuthentication + @RequiresPermissions("nexus:tasks:read") + public Page getTasks(@QueryParam("type") final String type) { + List taskXOs = taskScheduler.listsTasks().stream() + .filter(taskInfo -> taskInfo.getConfiguration().isVisible()) + .filter(taskInfo -> typeParameterMatches(type, taskInfo)) + .map(TaskXO::fromTaskInfo) + .collect(toList()); + + return new Page<>(taskXOs, null); + } + + @GET + @Path("/{id}") + @RequiresAuthentication + @RequiresPermissions("nexus:tasks:read") + public TaskXO getTaskById(@PathParam("id") final String id) { + return TaskXO.fromTaskInfo(getTaskInfo(id)); + } + + @POST + @Path("/{id}/run") + @RequiresAuthentication + @RequiresPermissions("nexus:tasks:start") + public void run(@PathParam("id") final String id) { + try { + getTaskInfo(id).runNow(TRIGGER_SOURCE); + } + catch (NotFoundException notFoundException) { + throw notFoundException; + } + catch (Exception e) { + log.error("error running task with id {}", id, e); + throw new WebApplicationException(format("Error running task %s", id), INTERNAL_SERVER_ERROR); + } + } + + @POST + @Path("/{id}/stop") + @RequiresAuthentication + @RequiresPermissions("nexus:tasks:stop") + public void stop(@PathParam("id") final String id) { + try { + TaskInfo taskInfo = getTaskInfo(id); + Future taskFuture = taskInfo.getCurrentState().getFuture(); + if (taskFuture == null) { + throw new WebApplicationException(format("Task %s is not running", id), CONFLICT); + } + if (!taskFuture.cancel(false)) { + throw new WebApplicationException(format("Unable to stop task %s", id), CONFLICT); + } + } + catch (WebApplicationException webApplicationException) { + throw webApplicationException; + } + catch (Exception e) { + log.error("error stopping task with id {}", id, e); + throw new WebApplicationException(format("Error running task %s", id), INTERNAL_SERVER_ERROR); + } + } + + private TaskInfo getTaskInfo(final String id) { + return ofNullable(taskScheduler.getTaskById(id)) + .filter(taskInfo -> taskInfo.getConfiguration().isVisible()) + .orElseThrow(() -> new NotFoundException("Unable to locate task with id " + id)); + } + + private static boolean typeParameterMatches(final String type, final TaskInfo taskInfo) { + return type == null || type.isEmpty() || type.equals(taskInfo.getTypeId()); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/resources/doc/TasksResourceDoc.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/resources/doc/TasksResourceDoc.java new file mode 100644 index 0000000000000000000000000000000000000000..4b05b0f55fac539140f8c80654b2255c8ede0922 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/internal/resources/doc/TasksResourceDoc.java @@ -0,0 +1,55 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.internal.resources.doc; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import org.sonatype.nexus.scheduling.api.TaskXO; +import org.sonatype.nexus.rest.Page; + +/** + * Swagger documentation for {@link TasksResource} + * + * @since 3.6 + */ +@Api(value = "tasks") +public interface TasksResourceDoc +{ + @ApiOperation("List tasks") + Page getTasks(@ApiParam(value = "Type of the tasks to get") final String type); + + @ApiOperation("Get a single task by id") + @ApiResponses(value = { + @ApiResponse(code = 404, message = "Task not found") + }) + TaskXO getTaskById(@ApiParam(value = "Id of the task to get") final String id); + + @ApiOperation("Run task") + @ApiResponses(value = { + @ApiResponse(code = 204, message = "Task was run"), + @ApiResponse(code = 404, message = "Task not found") + }) + void run(@ApiParam(value = "Id of the task to run") final String id); + + @ApiOperation("Stop task") + @ApiResponses(value = { + @ApiResponse(code = 204, message = "Task was stopped"), + @ApiResponse(code = 409, message = "Unable to stop task"), + @ApiResponse(code = 404, message = "Task not found") + }) + void stop(@ApiParam(value = "Id of the task to stop") final String id); +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/package-info.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..bb25a57e5fc7ab2d4d804ed7812d5424f91acc6a --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Task scheduling framework. + * + * @since 3.0 + */ +package org.sonatype.nexus.scheduling; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Cron.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Cron.java new file mode 100644 index 0000000000000000000000000000000000000000..83d78f8044fc3ce732eb630da73213f472254e9c --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Cron.java @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.schedule; + +import java.util.Date; + +/** + * Schedule that accepts cron expression. + * + * @see ScheduleFactory#cron(Date, String) + */ +public class Cron + extends Schedule +{ + public static final String TYPE = "cron"; + + public static final String SCHEDULE_CRON_EXPRESSION = "schedule.cronExpression"; + + public Cron(final Date startAt, final String cronExpression) { + super(TYPE); + set(SCHEDULE_START_AT, dateToString(startAt)); + set(SCHEDULE_CRON_EXPRESSION, cronExpression); + } + + public Date getStartAt() { + return stringToDate(get(SCHEDULE_START_AT)); + } + + public String getCronExpression() { + return get(SCHEDULE_CRON_EXPRESSION); + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Daily.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Daily.java new file mode 100644 index 0000000000000000000000000000000000000000..c68b26eb94586d6f0919a9ede9ff3d5e383095fc --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Daily.java @@ -0,0 +1,35 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.schedule; + +import java.util.Date; + +/** + * Schedule that repeats daily at same time. + * + * @see ScheduleFactory#daily(Date) + */ +public class Daily + extends Schedule +{ + public static final String TYPE = "daily"; + + public Daily(final Date startAt) { + super(TYPE); + set(SCHEDULE_START_AT, dateToString(startAt)); + } + + public Date getStartAt() { + return stringToDate(get(SCHEDULE_START_AT)); + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Hourly.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Hourly.java new file mode 100644 index 0000000000000000000000000000000000000000..ae3bce351ecfaa93d1581b6d64ce86879f2d8fac --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Hourly.java @@ -0,0 +1,35 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.schedule; + +import java.util.Date; + +/** + * Schedule that repeats every hour at same minute of hour. + * + * @see ScheduleFactory#hourly(Date) + */ +public class Hourly + extends Schedule +{ + public static final String TYPE = "hourly"; + + public Hourly(final Date startAt) { + super(TYPE); + set(SCHEDULE_START_AT, dateToString(startAt)); + } + + public Date getStartAt() { + return stringToDate(get(SCHEDULE_START_AT)); + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Manual.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Manual.java new file mode 100644 index 0000000000000000000000000000000000000000..553a0dd15ac379af7b47573443023619c8248dd1 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Manual.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.schedule; + +/** + * Schedule that never executes, is meant for manual execution (from code or via UI by user). + * + * @see ScheduleFactory#manual() + */ +public class Manual + extends Schedule +{ + public static final String TYPE = "manual"; + + public Manual() { + super(TYPE); + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Monthly.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Monthly.java new file mode 100644 index 0000000000000000000000000000000000000000..a4dc692d9efa56edee69b19a7c42d42f7af0ac47 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Monthly.java @@ -0,0 +1,134 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.schedule; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * Schedule that repeats on same days of a month repeatedly. + * + * @see ScheduleFactory#monthly(Date, Set) + */ +public class Monthly + extends Schedule +{ + public static final String TYPE = "monthly"; + + /** + * Representation of a calender day. + */ + public static final class CalendarDay + implements Comparable + { + /** + * A special "sentinel" value for a CalendarDay that marks the "last day in the month". + * + * Normal days would not be suited for that, nor 30, nor 31, nor 28 nor 29 would work. + */ + private static final int LAST_DAY_OF_MONTH = 999; + + private static final CalendarDay LAST_DAY = new CalendarDay(LAST_DAY_OF_MONTH); + + public static CalendarDay day(final int day) { + return new CalendarDay(day); + } + + public static Set days(final int... days) { + Set result = new HashSet<>(); + for (int day : days) { + result.add(day(day)); + } + return result; + } + + public static CalendarDay lastDay() { + return LAST_DAY; + } + + private final int day; + + private CalendarDay(final int day) { + checkArgument((day >= 1 && day <= 31) || day == LAST_DAY_OF_MONTH, "Invalid calendar day %s", day); + this.day = day; + } + + public int getDay() { + return day; + } + + public boolean isLastDayOfMonth() { + return day == LAST_DAY_OF_MONTH; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "day=" + (day == LAST_DAY_OF_MONTH ? "last" : day) + + '}'; + } + + @Override + public int compareTo(final CalendarDay other) { + return this.day - other.day; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CalendarDay that = (CalendarDay) o; + if (day != that.day) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return day; + } + + /** + * Function to convert {@link CalendarDay} to string. + */ + public static final Function dayToString = input -> Integer.toString(input.getDay()); + + /** + * Function to convert string to {@link CalendarDay}. + */ + public static final Function stringToDay = input -> new CalendarDay(Integer.parseInt(input)); + } + + public Monthly(final Date startAt, final Set daysToRun) { + super(TYPE); + set(SCHEDULE_START_AT, dateToString(startAt)); + set(SCHEDULE_DAYS_TO_RUN, setToCsv(daysToRun, CalendarDay.dayToString)); + } + + public Date getStartAt() { + return stringToDate(get(SCHEDULE_START_AT)); + } + + public Set getDaysToRun() { + return csvToSet(get(SCHEDULE_DAYS_TO_RUN), CalendarDay.stringToDay); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Now.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Now.java new file mode 100644 index 0000000000000000000000000000000000000000..39df051c61047272c40c72b7e1e0a5ffb08022dc --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Now.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.schedule; + +/** + * Schedule that executes once at the moment task is being scheduled with. + * + * @see ScheduleFactory#now() + */ +public class Now + extends Schedule +{ + public static final String TYPE = "now"; + + public Now() { + super(TYPE); + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Once.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Once.java new file mode 100644 index 0000000000000000000000000000000000000000..1ef182594b83f5d6950df5da9bcdaada25ef7ff0 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Once.java @@ -0,0 +1,38 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.schedule; + +import java.util.Date; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Schedule that executes once at given time. + * + * @see ScheduleFactory#once(Date) + */ +public class Once + extends Schedule +{ + public static final String TYPE = "once"; + + public Once(final Date startAt) { + super(TYPE); + checkNotNull(startAt); + set(SCHEDULE_START_AT, dateToString(startAt)); + } + + public Date getStartAt() { + return stringToDate(get(SCHEDULE_START_AT)); + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Schedule.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Schedule.java new file mode 100644 index 0000000000000000000000000000000000000000..7d7080732e9d956f050695bdec9608245b1f0a9a --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Schedule.java @@ -0,0 +1,115 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.schedule; + +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.function.Function; +import java.util.stream.Collectors; + +import com.google.common.base.Splitter; +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Schedule. + */ +public abstract class Schedule +{ + public static final String SCHEDULE_TYPE = "schedule.type"; + + public static final String SCHEDULE_START_AT = "schedule.startAt"; + + public static final String SCHEDULE_DAYS_TO_RUN = "schedule.daysToRun"; + + private final Map properties = new HashMap<>(); + + protected Schedule(final String type) { + checkNotNull(type); + set(SCHEDULE_TYPE, type); + } + + public String getType() { + return properties.get(SCHEDULE_TYPE); + } + + /** + * Get a schedule property. + */ + protected String get(final String name) { + return properties.get(name); + } + + /** + * Set a schedule property. + */ + protected void set(final String name, final String value) { + properties.put(name, value); + } + + public Map asMap() { + return Collections.unmodifiableMap(properties); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "properties=" + properties + + '}'; + } + + // + // Helpers + // + + /** + * Convert a date into a string. + */ + public static String dateToString(final Date date) { + return new DateTime(date).toString(); + } + + /** + * Convert a string into a date. + */ + public static Date stringToDate(final String string) { + return DateTime.parse(string).toDate(); + } + + /** + * Convert a set into a CSV formatted string using given mapping function. + * + * Ordering of set elements in produced CSV string is enforced to their natural ordering. + */ + public static > String setToCsv(final Set set, final Function function) { + return new TreeSet<>(set).stream() + .map(function) + .collect(Collectors.joining(",")); + } + + /** + * Convert a CSV formatted string into a set using given mapping function. + * + * Ordering of returned set element does not reflect CSV ordering, elements are (re)sorted by their natural ordering. + */ + public static > Set csvToSet(final String csv, final Function function) { + return Splitter.on(',').splitToList(csv).stream() + .map(function) + .collect(Collectors.toCollection(TreeSet::new)); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/ScheduleFactory.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/ScheduleFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..bf78d4989a2fe32ad1387854b3ebf3c7ae4885bb --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/ScheduleFactory.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.schedule; + +import java.util.Date; +import java.util.Set; + +import org.sonatype.nexus.scheduling.schedule.Monthly.CalendarDay; +import org.sonatype.nexus.scheduling.schedule.Weekly.Weekday; + +/** + * Constructs {@link Schedule} instances. + */ +public interface ScheduleFactory +{ + Manual manual(); + + Now now(); + + Once once(Date startAt); + + Hourly hourly(Date startAt); + + Daily daily(Date startAt); + + Weekly weekly(Date startAt, Set daysToRun); + + Monthly monthly(Date startAt, Set daysToRun); + + Cron cron(Date startAt, String cronExpression); +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/ScheduleFactoryImpl.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/ScheduleFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..f9c38302a4ee237a077117dcee8614a9993d0916 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/ScheduleFactoryImpl.java @@ -0,0 +1,84 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.schedule; + +import java.util.Date; +import java.util.Set; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.scheduling.schedule.Monthly.CalendarDay; +import org.sonatype.nexus.scheduling.schedule.Weekly.Weekday; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Support for {@link ScheduleFactory} implementations. + * + * @since 3.0 + */ +public class ScheduleFactoryImpl + extends ComponentSupport + implements ScheduleFactory +{ + @Override + public Manual manual() { + return new Manual(); + } + + @Override + public Now now() { + return new Now(); + } + + @Override + public Once once(final Date startAt) { + checkNotNull(startAt); + return new Once(startAt); + } + + @Override + public Hourly hourly(final Date startAt) { + checkNotNull(startAt); + return new Hourly(startAt); + } + + @Override + public Daily daily(final Date startAt) { + checkNotNull(startAt); + return new Daily(startAt); + } + + @Override + public Weekly weekly(final Date startAt, final Set daysToRun) { + checkNotNull(startAt); + checkNotNull(daysToRun); + checkArgument(!daysToRun.isEmpty(), "No days of week set to run"); + return new Weekly(startAt, daysToRun); + } + + @Override + public Monthly monthly(final Date startAt, final Set daysToRun) { + checkNotNull(startAt); + checkNotNull(daysToRun); + checkArgument(!daysToRun.isEmpty(), "No days of month set to run"); + return new Monthly(startAt, daysToRun); + } + + @Override + public Cron cron(final Date startAt, final String cronExpression) { + checkNotNull(startAt); + checkNotNull(cronExpression); + return new Cron(startAt, cronExpression); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Weekly.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Weekly.java new file mode 100644 index 0000000000000000000000000000000000000000..6416c289cce59b34f01b64d22da92172054d55ad --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/Weekly.java @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.schedule; + +import java.util.Date; +import java.util.Set; +import java.util.function.Function; + +/** + * Schedule that repeats on same days of a week repeatedly. + * + * @see ScheduleFactory#weekly(Date, Set) + */ +public class Weekly + extends Schedule +{ + public static final String TYPE = "weekly"; + + /** + * Representation of weekday. + */ + public enum Weekday + { + SUN, MON, TUE, WED, THU, FRI, SAT; + + /** + * Function to convert {@link Weekday} to strings. + */ + public static final Function dayToString = Enum::name; + + /** + * Function to convert string to {@link Weekday}. + */ + public static final Function stringToDay = Weekday::valueOf; + } + + public Weekly(final Date startAt, final Set daysToRun) { + super(TYPE); + set(SCHEDULE_START_AT, dateToString(startAt)); + set(SCHEDULE_DAYS_TO_RUN, setToCsv(daysToRun, Weekday.dayToString)); + } + + public Date getStartAt() { + return stringToDate(get(SCHEDULE_START_AT)); + } + + public Set getDaysToRun() { + return csvToSet(get(SCHEDULE_DAYS_TO_RUN), Weekday.stringToDay); + } +} \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/package-info.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..f0f47e69e03eae325ba3ffb8f8cc0eac408c2499 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/schedule/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Schedules. + * + * @since 3.0 + */ +package org.sonatype.nexus.scheduling.schedule; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/spi/SchedulerSPI.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/spi/SchedulerSPI.java new file mode 100644 index 0000000000000000000000000000000000000000..4201dffbbfec28c87c7be33438f9c149705f4238 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/spi/SchedulerSPI.java @@ -0,0 +1,89 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.spi; + +import java.util.List; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.lifecycle.Lifecycle; +import org.sonatype.nexus.scheduling.TaskConfiguration; +import org.sonatype.nexus.scheduling.TaskInfo; +import org.sonatype.nexus.scheduling.schedule.Schedule; +import org.sonatype.nexus.scheduling.schedule.ScheduleFactory; + +/** + * The underlying scheduler that provides scheduling. + * + * @since 3.0 + */ +public interface SchedulerSPI + extends Lifecycle +{ + /** + * Returns the SPI specific {@link ScheduleFactory}. + */ + ScheduleFactory scheduleFactory(); + + /** + * Returns status message. + */ + String renderStatusMessage(); + + /** + * Returns verbose detail message. + */ + String renderDetailMessage(); + + /** + * Pause the scheduler. + */ + void pause(); + + /** + * Resume the scheduler. + */ + void resume(); + + /** + * Returns the task for the given identifier; or null if missing. + */ + @Nullable + TaskInfo getTaskById(String id); + + /** + * Returns a list of all tasks which have been scheduled. + */ + List listsTasks(); + + /** + * Schedule a task with the given scheduler. + * + * If a task already exists with the same task identifier, the task will be updated. + * + * Task must not be running. + */ + TaskInfo scheduleTask(TaskConfiguration config, Schedule schedule); + + /** + * Returns the count of currently running tasks. + */ + int getRunningTaskCount(); + + /** + * Returns the count of tasks executed so far. + * + * @since 3.7 + */ + int getExecutedTaskCount(); +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/spi/package-info.java b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/spi/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..ebf928bc7ce99ef48d20cd0e81d6120f3e697f00 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/main/java/org/sonatype/nexus/scheduling/spi/package-info.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +/** + * Scheduling service provider support. + * + * @since 3.0 + */ +package org.sonatype.nexus.scheduling.spi; \ No newline at end of file diff --git a/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/LocalTaskStateTrackerTest.java b/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/LocalTaskStateTrackerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..934ed5f890bc9a7f516ec3ddb51c95a703e684f8 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/LocalTaskStateTrackerTest.java @@ -0,0 +1,107 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.internal; + +import java.util.Arrays; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.scheduling.ClusteredTaskStateStore; +import org.sonatype.nexus.scheduling.TaskInfo; +import org.sonatype.nexus.scheduling.TaskScheduler; +import org.sonatype.nexus.scheduling.events.TaskBlockedEvent; +import org.sonatype.nexus.scheduling.events.TaskDeletedEvent; +import org.sonatype.nexus.scheduling.events.TaskEventCanceled; +import org.sonatype.nexus.scheduling.events.TaskEventStarted; +import org.sonatype.nexus.scheduling.events.TaskEventStoppedDone; +import org.sonatype.nexus.scheduling.events.TaskScheduledEvent; +import org.sonatype.nexus.scheduling.events.TaskStartedRunningEvent; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Tests {@link LocalTaskStateTracker}. + */ +public class LocalTaskStateTrackerTest + extends TestSupport +{ + @Mock + private ClusteredTaskStateStore store; + + @Mock + private TaskScheduler scheduler; + + @Mock + private TaskInfo taskInfo; + + private LocalTaskStateTracker tracker; + + @Before + public void setUp() { + tracker = new LocalTaskStateTracker(store, scheduler); + when(taskInfo.getId()).thenReturn("task-id"); + } + + @Test + public void testStart() throws Exception { + when(scheduler.listsTasks()).thenReturn(Arrays.asList(taskInfo)); + tracker.start(); + verify(store).setLocalState(taskInfo); + } + + @Test + public void testOn_Deleted() { + tracker.on(new TaskDeletedEvent(taskInfo)); + verify(store).removeClusteredState("task-id"); + } + + @Test + public void testOn_Scheduled() { + tracker.on(new TaskScheduledEvent(taskInfo)); + verify(store).setLocalState(taskInfo); + } + + @Test + public void testOn_Started() { + tracker.on(new TaskEventStarted(taskInfo)); + verify(store).setLocalState(taskInfo); + } + + @Test + public void testOn_Stopped() { + tracker.on(new TaskEventStoppedDone(taskInfo)); + verify(store).setLocalState(taskInfo); + } + + @Test + public void testOn_Canceled() { + tracker.on(new TaskEventCanceled(taskInfo)); + verify(store).setLocalState(taskInfo); + } + + @Test + public void testOn_Blocked() { + tracker.on(new TaskBlockedEvent(taskInfo)); + verify(store).setLocalState(taskInfo); + } + + @Test + public void testOn_StartedRunning() { + tracker.on(new TaskStartedRunningEvent(taskInfo)); + verify(store).setLocalState(taskInfo); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/SimpleTask.java b/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/SimpleTask.java new file mode 100644 index 0000000000000000000000000000000000000000..3de549c249b9fada5cf760f24fe8f10b2d717d24 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/SimpleTask.java @@ -0,0 +1,34 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.internal; + +import org.sonatype.nexus.scheduling.TaskSupport; + +/** + * Simple task. + * + * @see SimpleTaskDescriptor + */ +public class SimpleTask + extends TaskSupport +{ + @Override + protected Object execute() throws Exception { + return "simple-result"; + } + + @Override + public String getMessage() { + return "simple-message"; + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/SimpleTaskDescriptor.java b/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/SimpleTaskDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..e9e0e7ce139006c617f08f348c07bc83f6d7eb76 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/SimpleTaskDescriptor.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.internal; + +import org.sonatype.nexus.scheduling.TaskDescriptorSupport; + +/** + * {@link SimpleTask} descriptor. + */ +public class SimpleTaskDescriptor + extends TaskDescriptorSupport +{ + public static final String TYPE_ID = "simple"; + + public SimpleTaskDescriptor() { + super(TYPE_ID, SimpleTask.class, "simple-name", VISIBLE, EXPOSED); + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/TaskActivationTest.groovy b/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/TaskActivationTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..84ca29b77b65263664fe69c3e7274b512ac7ef46 --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/TaskActivationTest.groovy @@ -0,0 +1,95 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.internal + +import java.util.concurrent.Future + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.orient.freeze.DatabaseFreezeChangeEvent +import org.sonatype.nexus.orient.freeze.DatabaseFreezeService +import org.sonatype.nexus.scheduling.TaskInfo +import org.sonatype.nexus.scheduling.TaskInfo.CurrentState +import org.sonatype.nexus.scheduling.spi.SchedulerSPI + +import org.junit.Before +import org.junit.Test +import org.mockito.Mock + +import static java.util.Arrays.asList +import static org.mockito.Mockito.doReturn +import static org.mockito.Mockito.times +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when +import static org.sonatype.nexus.scheduling.TaskInfo.State.RUNNING +import static org.sonatype.nexus.scheduling.TaskInfo.State.WAITING + +class TaskActivationTest + extends TestSupport +{ + @Mock + SchedulerSPI schedulerSpi + + @Mock + DatabaseFreezeService databaseFreezeService + + @Mock + TaskInfo runningTask + + @Mock + TaskInfo waitingTask + + @Mock + CurrentState runningTaskCurrentState + + @Mock + CurrentState waitingTaskCurrentState + + @Mock + Future runningTaskFuture + + private TaskActivation underTest + + @Before + void setUp() { + when(runningTask.getCurrentState()).thenReturn(runningTaskCurrentState) + when(runningTaskCurrentState.getState()).thenReturn(RUNNING) + doReturn(runningTaskFuture).when(runningTaskCurrentState).getFuture() + + when(waitingTask.getCurrentState()).thenReturn(waitingTaskCurrentState) + when(waitingTaskCurrentState.getState()).thenReturn(WAITING) + doReturn(null).when(waitingTaskCurrentState).getFuture() + + when(schedulerSpi.listsTasks()).thenReturn(asList(runningTask, waitingTask)) + underTest = new TaskActivation(schedulerSpi, databaseFreezeService) + } + + @Test + void 'pause scheduler and cancel tasks when database is frozen'() { + underTest.onDatabaseFreezeChangeEvent(new DatabaseFreezeChangeEvent(true)) + verify(schedulerSpi).pause() + verify(runningTaskFuture).cancel(false) + } + + @Test + void 'restart scheduler when database is unfrozen'() { + underTest.onDatabaseFreezeChangeEvent(new DatabaseFreezeChangeEvent(false)) + verify(schedulerSpi).resume() + } + + @Test + void 'scheduler not resumed on startup when database is frozen'() { + when(databaseFreezeService.isFrozen()).thenReturn(true) + underTest.start() + verify(schedulerSpi, times(0)).resume() + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/TaskFactoryImplTest.groovy b/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/TaskFactoryImplTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..11357452d037af5bcf599286519a092cbb03d70a --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/TaskFactoryImplTest.groovy @@ -0,0 +1,117 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.internal + +import java.lang.annotation.Annotation + +import org.sonatype.goodies.testsupport.TestSupport +import org.sonatype.nexus.scheduling.TaskConfiguration + +import com.google.inject.Key +import com.google.inject.util.Providers +import org.eclipse.sisu.BeanEntry +import org.eclipse.sisu.inject.BeanLocator +import org.junit.Before +import org.junit.Test +import org.mockito.Mock + +import static org.junit.Assert.fail +import static org.mockito.Matchers.any +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.when + +/** + * Tests for {@link TaskFactoryImpl}. + */ +class TaskFactoryImplTest + extends TestSupport +{ + @Mock + private BeanLocator beanLocator + + private TaskFactoryImpl underTest + + @Before + void setUp() { + BeanEntry simpleTaskBeanEntry = mock(BeanEntry.class) + when(simpleTaskBeanEntry.getImplementationClass()).thenReturn(SimpleTask) + when(simpleTaskBeanEntry.getProvider()).thenReturn(Providers.of(new SimpleTask())) + when(beanLocator.locate(any(Key))).thenReturn(Collections.singletonList(simpleTaskBeanEntry)) + underTest = new TaskFactoryImpl(beanLocator) + } + + @Test + void 'register and find descriptor'() { + assert underTest.descriptors.isEmpty() + + underTest.addDescriptor(new SimpleTaskDescriptor()) + assert underTest.descriptors.size() == 1 + + def descriptor1 = underTest.findDescriptor(SimpleTaskDescriptor.TYPE_ID) + assert descriptor1 != null + + def descriptor2 = underTest.findDescriptor('no-such-type-id') + assert descriptor2 == null + } + + @Test + void 'descriptor list is immutable'() { + // can not add directly to the list + try { + underTest.descriptors.add(new SimpleTaskDescriptor()) + fail() + } + catch (UnsupportedOperationException e) { + // expected + } + + // can not remove from list + underTest.addDescriptor(new SimpleTaskDescriptor()) + try { + underTest.descriptors.iterator().remove() + fail() + } + catch (UnsupportedOperationException e) { + // expected + } + } + + @Test + void 'create missing descriptor'() { + def config = new TaskConfiguration( + id: UUID.randomUUID().toString(), + typeId: 'no-such-type-id' + ) + try { + underTest.create(config) + fail() + } + catch (IllegalArgumentException e) { + // expected + } + } + + @Test + void 'create task'() { + underTest.addDescriptor(new SimpleTaskDescriptor()) + + def config = new TaskConfiguration( + id: UUID.randomUUID().toString(), + typeId: SimpleTaskDescriptor.TYPE_ID + ) + def task = underTest.create(config) + + assert task != null + assert task instanceof SimpleTask + } +} diff --git a/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/resources/TasksResourceTest.groovy b/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/resources/TasksResourceTest.groovy new file mode 100644 index 0000000000000000000000000000000000000000..9f054ee38dbe09c71a4877f41bb2a4f4c84d583b --- /dev/null +++ b/thirdparty-bundles/components/nexus-scheduling/src/test/java/org/sonatype/nexus/scheduling/internal/resources/TasksResourceTest.groovy @@ -0,0 +1,199 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.scheduling.internal.resources + +import java.util.concurrent.CompletableFuture +import java.util.concurrent.Future +import javax.ws.rs.WebApplicationException +import javax.ws.rs.core.Response.Status + +import org.sonatype.nexus.scheduling.TaskConfiguration +import org.sonatype.nexus.scheduling.TaskInfo +import org.sonatype.nexus.scheduling.TaskScheduler +import org.sonatype.nexus.scheduling.schedule.Schedule + +import spock.lang.Specification + +import static TaskInfo.State.* +import static javax.ws.rs.core.Response.Status.CONFLICT +import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR +import static javax.ws.rs.core.Response.Status.NOT_FOUND + +public class TasksResourceTest + extends Specification { + def tasksResource + + def taskScheduler = Mock(TaskScheduler) + + def visibleConfig = new TaskConfiguration(visible: true) + def invisibleConfig = new TaskConfiguration(visible: false) + + def testTasks = [ + new TestTaskInfo(id: 'task0', name: 'Invisible task', typeId: 'aType', + currentState: new TestCurrentState(state: WAITING), configuration: invisibleConfig), + new TestTaskInfo(id: 'task1', name: 'Task 1', typeId: 'anotherType', + currentState: new TestCurrentState(state: WAITING), configuration: visibleConfig), + new TestTaskInfo(id: 'task2', name: 'Task 2', typeId: 'aType', currentState: new TestCurrentState(state: RUNNING, + runStarted: new Date(), future: new CompletableFuture()), configuration: visibleConfig), + new TestTaskInfo(id: 'task3', name: 'Task 3', typeId: 'anotherType', + currentState: new TestCurrentState(state: DONE, runStarted: new Date(), + future: CompletableFuture.completedFuture(null)), configuration: visibleConfig) + ] + + def setup() { + tasksResource = new TasksResource(taskScheduler) + } + + def 'resource path is the expected value'() { + expect: + TasksResource.RESOURCE_URI == '/beta/tasks' + } + + def 'getTasks gets list of scheduled tasks'() { + when: + def page = tasksResource.getTasks() + + then: + 1 * taskScheduler.listsTasks() >> testTasks + page.items.size() == 3 + page.items*.id == ['task1', 'task2', 'task3'] + page.items*.name == ['Task 1', 'Task 2', 'Task 3'] + page.items*.type == ['anotherType', 'aType', 'anotherType'] + page.items*.currentState == [WAITING.toString(), RUNNING.toString(), DONE.toString()] + } + + def 'getTasks filters on task type'() { + when: + def page = tasksResource.getTasks('anotherType') + + then: + 1 * taskScheduler.listsTasks() >> testTasks + page.items.size() == 2 + page.items*.id == ['task1', 'task3'] + } + + def 'getTaskById gets tasks by id'() { + when: 'getTaskById called with valid id' + def validTaskXO = tasksResource.getTaskById('task1') + + then: 'expected task is returned' + 1 * taskScheduler.getTaskById(_) >> { String id -> testTasks.find { it.id == id } } + validTaskXO.id == 'task1' + validTaskXO.name == 'Task 1' + validTaskXO.type == 'anotherType' + validTaskXO.currentState == WAITING.toString() + + when: 'getTaskById called with invalid id' + def invalidTaskXO = tasksResource.getTaskById('nosuchtask') + + then: 'a 404 response is generated' + 1 * taskScheduler.getTaskById(_) >> { String id -> testTasks.find { it.id == id } } + invalidTaskXO == null + WebApplicationException exception = thrown() + Status.fromStatusCode(exception.response.status) == NOT_FOUND + } + + def 'run invokes runNow on tasks'() { + when: 'run called with valid id' + tasksResource.run('task1') + + then: 'task found and invoked once with runNow()' + 1 * taskScheduler.getTaskById(_) >> { String id -> testTasks.find { it.id == id } } + testTasks.find { it.id == 'task1' }.runs == ['REST API'] + + when: 'run called with invalid id' + tasksResource.run('nosuchtask') + + then: 'a 404 response is generated' + 1 * taskScheduler.getTaskById(_) >> { String id -> testTasks.find { it.id == id } } + WebApplicationException exception1 = thrown() + Status.fromStatusCode(exception1.response.status) == NOT_FOUND + + when: 'an error occurs' + tasksResource.run('error') + + then: 'a 500 response is generated' + 1 * taskScheduler.getTaskById(_) >> { String id -> throw new RuntimeException("error") } + WebApplicationException exception2 = thrown() + Status.fromStatusCode(exception2.response.status) == INTERNAL_SERVER_ERROR + } + + def 'stop cancels running tasks'() { + when: 'stop called with valid id for a running task' + tasksResource.stop('task2') + + then: 'task found and cancelled' + 1 * taskScheduler.getTaskById(_) >> { String id -> testTasks.find { it.id == id } } + testTasks.find { it.id == 'task2' }.currentState.future.isCancelled() + + when: 'stop called with valid id for a non-running task' + tasksResource.stop('task1') + + then: 'task found, a 409 is generated' + 1 * taskScheduler.getTaskById(_) >> { String id -> testTasks.find { it.id == id } } + WebApplicationException exception1 = thrown() + Status.fromStatusCode(exception1.response.status) == CONFLICT + + when: 'stop called with completed task' + tasksResource.stop('task3') + + then: 'a 409 response is generated' + 1 * taskScheduler.getTaskById(_) >> { String id -> testTasks.find { it.id == id } } + WebApplicationException exception2 = thrown() + Status.fromStatusCode(exception2.response.status) == CONFLICT + + when: 'stop called with invalid id' + tasksResource.stop('nosuchtask') + + then: 'a 404 response is generated' + 1 * taskScheduler.getTaskById(_) >> { String id -> testTasks.find { it.id == id } } + WebApplicationException exception3 = thrown() + Status.fromStatusCode(exception3.response.status) == NOT_FOUND + + when: 'an error occurs' + tasksResource.stop('error') + + then: 'a 500 response is generated' + 1 * taskScheduler.getTaskById(_) >> { String id -> throw new RuntimeException("error") } + WebApplicationException exception4 = thrown() + Status.fromStatusCode(exception4.response.status) == INTERNAL_SERVER_ERROR + } + + class TestTaskInfo implements TaskInfo { + String id + String name + String typeId + String message + String triggerSource + TaskInfo.CurrentState currentState + TaskInfo.LastRunState lastRunState + TaskConfiguration configuration + Schedule schedule + + boolean remove() {} + TaskInfo runNow(String s) { + runs << s + return this + } + + def runs = [] + } + + class TestCurrentState implements TaskInfo.CurrentState { + TaskInfo.State state + Date nextRun + Date runStarted + TaskInfo.RunState runState + Future future + } +} diff --git a/thirdparty-bundles/components/nexus-script/pom.xml b/thirdparty-bundles/components/nexus-script/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..5b0f66ba3f8325d5dfb827fb302c48547a2ab940 --- /dev/null +++ b/thirdparty-bundles/components/nexus-script/pom.xml @@ -0,0 +1,78 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.8.0-02 + + + nexus-script + ${project.groupId}:${project.artifactId} + bundle + + + + org.sonatype.nexus + nexus-common + + + + org.sonatype.nexus + nexus-validation + + + + javax.ws.rs + javax.ws.rs-api + + + + org.apache.ivy + ivy + 2.3.0 + + + + com.fasterxml.jackson.core + jackson-annotations + true + + + + org.sonatype.goodies + goodies-testsupport + test + + + + org.spockframework + spock-core + test + + + + org.spockframework + spock-guice + test + + + + + diff --git a/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/Script.groovy b/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/Script.groovy new file mode 100644 index 0000000000000000000000000000000000000000..cfa51f3e55854d4b233b677a510b27b53141437f --- /dev/null +++ b/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/Script.groovy @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.script + +import org.sonatype.nexus.common.entity.AbstractEntity + +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import groovy.transform.TupleConstructor + +/** + * Script entity. + * + * @since 3.0 + */ +@ToString +@TupleConstructor +@EqualsAndHashCode +public class Script + extends AbstractEntity +{ + String name + + String content + + String type = ScriptManager.DEFAULT_TYPE +} diff --git a/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptClient.groovy b/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptClient.groovy new file mode 100644 index 0000000000000000000000000000000000000000..d47e0b2d845a2050fa7d45e9574e6a0efab8362f --- /dev/null +++ b/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptClient.groovy @@ -0,0 +1,86 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.script + +import javax.validation.Valid +import javax.validation.constraints.NotNull +import javax.ws.rs.Consumes +import javax.ws.rs.DELETE +import javax.ws.rs.GET +import javax.ws.rs.POST +import javax.ws.rs.PUT +import javax.ws.rs.Path +import javax.ws.rs.PathParam +import javax.ws.rs.Produces + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON +import static javax.ws.rs.core.MediaType.TEXT_PLAIN + +/** + * Public API for managing Scripts. Provides BREAD capabilities. + * + * @since 3.0 + */ +@Path(ScriptClient.RESOURCE_URI) +@Produces([APPLICATION_JSON]) +@Consumes([APPLICATION_JSON]) +interface ScriptClient +{ + public static final String RESOURCE_URI = '/v1/script' + + public static final String RUN_ACTION = 'run' + + /** + * Browse all {@link Script}. + */ + @GET + List browse() + + /** + * Get a specific {@link Script} by name. + */ + @GET + @Path('{name}') + ScriptXO read(@PathParam('name') String name) + + /** + * Edit an existing {@link Script}. + */ + @PUT + @Path('{name}') + void edit(@PathParam('name') String name, @NotNull @Valid ScriptXO scriptXO) + + /** + * Add a new {@link Script}. + */ + @POST + void add(@NotNull @Valid ScriptXO scriptXO) + + /** + * Delete an existing {@link Script}. + */ + @DELETE + @Path('{name}') + void delete(@PathParam('name') String name) + + /** + * Run an existing {@link Script}. + * @param name the name of the Script to execute + * @param args the arguments to pass to the Script + */ + @POST + @Path('{name}/run') + @Consumes([TEXT_PLAIN]) + ScriptResultXO run(@PathParam('name') String name, String args) + +} diff --git a/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptCreatedEvent.java b/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptCreatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..4a713be68e62bd97a402363872f48148c1829879 --- /dev/null +++ b/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptCreatedEvent.java @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.script; + +/** + * Emitted when a {@link Script} is created. + * + * @since 3.1 + */ +public class ScriptCreatedEvent + extends ScriptEvent +{ + public ScriptCreatedEvent(final Script script) { + super(script); + } +} diff --git a/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptDeletedEvent.java b/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptDeletedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..c0bb6d1d96c1ef321fb97f207543f38edcf8b18b --- /dev/null +++ b/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptDeletedEvent.java @@ -0,0 +1,26 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.script; + +/** + * Emitted when a {@link Script} is deleted. + * + * @since 3.1 + */ +public class ScriptDeletedEvent + extends ScriptEvent +{ + public ScriptDeletedEvent(final Script script) { + super(script); + } +} diff --git a/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptEvent.java b/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..8964dda25f3c5ba3933b4cc25187e382d2790f62 --- /dev/null +++ b/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptEvent.java @@ -0,0 +1,38 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.script; + +/** + * {@link Script} event. + * + * @since 3.1 + */ +public class ScriptEvent +{ + private final Script script; + + public ScriptEvent(final Script script) { + this.script = script; + } + + public Script getScript() { + return script; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "script=" + script + + '}'; + } +} diff --git a/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptManager.groovy b/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptManager.groovy new file mode 100644 index 0000000000000000000000000000000000000000..9588312b32a8bfeb05f4ea9f0c392c5989f92248 --- /dev/null +++ b/thirdparty-bundles/components/nexus-script/src/main/java/org/sonatype/nexus/script/ScriptManager.groovy @@ -0,0 +1,40 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.script + +import javax.annotation.Nullable + +import org.sonatype.goodies.lifecycle.Lifecycle + +/** + * Script manager. + * + * @since 3.0 + */ +interface ScriptManager + extends Lifecycle +{ + static final String DEFAULT_TYPE = 'groovy' + + Iterable